1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2015 by Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edinterface
22 */
23
24 #include "DNA_screen_types.h"
25 #include "DNA_userdef_types.h"
26
27 #include "BLI_listbase.h"
28 #include "BLI_math.h"
29 #include "BLI_rect.h"
30
31 #include "UI_interface.h"
32
33 #include "interface_intern.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #ifdef USE_UIBUT_SPATIAL_ALIGN
38
39 /**
40 * This struct stores a (simplified) 2D representation of all buttons of a same align group,
41 * with their immediate neighbors (if found),
42 * and needed value to compute 'stitching' of aligned buttons.
43 *
44 * \note This simplistic struct cannot fully represent complex layouts where buttons share some
45 * 'align space' with several others (see schema below), we'd need linked list and more
46 * complex code to handle that. However, looks like we can do without that for now,
47 * which is rather lucky!
48 *
49 * <pre>
50 * +--------+-------+
51 * | BUT 1 | BUT 2 | BUT 3 has two 'top' neighbors...
52 * |----------------| => In practice, we only store one of BUT 1 or 2 (which ones is not
53 * | BUT 3 | really deterministic), and assume the other stores a ref to BUT 3.
54 * +----------------+
55 * </pre>
56 *
57 * This will probably not work in all possible cases,
58 * but not sure we want to support such exotic cases anyway.
59 */
60 typedef struct ButAlign {
61 uiBut *but;
62
63 /* Neighbor buttons */
64 struct ButAlign *neighbors[4];
65
66 /* Pointers to coordinates (rctf values) of the button. */
67 float *borders[4];
68
69 /* Distances to the neighbors. */
70 float dists[4];
71
72 /* Flags, used to mark whether we should 'stitch'
73 * the corners of this button with its neighbors' ones. */
74 char flags[4];
75 } ButAlign;
76
77 /* Side-related enums and flags. */
78 enum {
79 /* Sides (used as indices, order is **crucial**,
80 * this allows us to factorize code in a loop over the four sides). */
81 LEFT = 0,
82 TOP = 1,
83 RIGHT = 2,
84 DOWN = 3,
85 TOTSIDES = 4,
86
87 /* Stitch flags, built from sides values. */
88 STITCH_LEFT = 1 << LEFT,
89 STITCH_TOP = 1 << TOP,
90 STITCH_RIGHT = 1 << RIGHT,
91 STITCH_DOWN = 1 << DOWN,
92 };
93
94 /* Mapping between 'our' sides and 'public' UI_BUT_ALIGN flags, order must match enum above. */
95 # define SIDE_TO_UI_BUT_ALIGN \
96 { \
97 UI_BUT_ALIGN_LEFT, UI_BUT_ALIGN_TOP, UI_BUT_ALIGN_RIGHT, UI_BUT_ALIGN_DOWN \
98 }
99
100 /* Given one side, compute the three other ones */
101 # define SIDE1(_s) (((_s) + 1) % TOTSIDES)
102 # define OPPOSITE(_s) (((_s) + 2) % TOTSIDES)
103 # define SIDE2(_s) (((_s) + 3) % TOTSIDES)
104
105 /* 0: LEFT/RIGHT sides; 1 = TOP/DOWN sides. */
106 # define IS_COLUMN(_s) ((_s) % 2)
107
108 /* Stitch flag from side value. */
109 # define STITCH(_s) (1 << (_s))
110
111 /* Max distance between to buttons for them to be 'mergeable'. */
112 # define MAX_DELTA 0.45f * max_ii(UI_UNIT_Y, UI_UNIT_X)
113
ui_but_can_align(const uiBut * but)114 bool ui_but_can_align(const uiBut *but)
115 {
116 const bool btype_can_align = !ELEM(but->type,
117 UI_BTYPE_LABEL,
118 UI_BTYPE_CHECKBOX,
119 UI_BTYPE_CHECKBOX_N,
120 UI_BTYPE_TAB,
121 UI_BTYPE_SEPR,
122 UI_BTYPE_SEPR_LINE,
123 UI_BTYPE_SEPR_SPACER);
124 return (btype_can_align && (BLI_rctf_size_x(&but->rect) > 0.0f) &&
125 (BLI_rctf_size_y(&but->rect) > 0.0f));
126 }
127
128 /**
129 * This function checks a pair of buttons (assumed in a same align group),
130 * and if they are neighbors, set needed data accordingly.
131 *
132 * \note It is designed to be called in total random order of buttons.
133 * Order-based optimizations are done by caller.
134 */
block_align_proximity_compute(ButAlign * butal,ButAlign * butal_other)135 static void block_align_proximity_compute(ButAlign *butal, ButAlign *butal_other)
136 {
137 /* That's the biggest gap between two borders to consider them 'alignable'. */
138 const float max_delta = MAX_DELTA;
139 float delta, delta_side_opp;
140 int side, side_opp;
141
142 const bool butal_can_align = ui_but_can_align(butal->but);
143 const bool butal_other_can_align = ui_but_can_align(butal_other->but);
144
145 const bool buts_share[2] = {
146 /* Sharing same line? */
147 !((*butal->borders[DOWN] >= *butal_other->borders[TOP]) ||
148 (*butal->borders[TOP] <= *butal_other->borders[DOWN])),
149 /* Sharing same column? */
150 !((*butal->borders[LEFT] >= *butal_other->borders[RIGHT]) ||
151 (*butal->borders[RIGHT] <= *butal_other->borders[LEFT])),
152 };
153
154 /* Early out in case buttons share no column or line, or if none can align... */
155 if (!(buts_share[0] || buts_share[1]) || !(butal_can_align || butal_other_can_align)) {
156 return;
157 }
158
159 for (side = 0; side < RIGHT; side++) {
160 /* We are only interested in buttons which share a same line
161 * (LEFT/RIGHT sides) or column (TOP/DOWN sides). */
162 if (buts_share[IS_COLUMN(side)]) {
163 side_opp = OPPOSITE(side);
164
165 /* We check both opposite sides at once, because with very small buttons,
166 * delta could be below max_delta for the wrong side
167 * (that is, in horizontal case, the total width of two buttons can be below max_delta).
168 * We rely on exact zero value here as an 'already processed' flag,
169 * so ensure we never actually set a zero value at this stage.
170 * FLT_MIN is zero-enough for UI position computing. ;) */
171 delta = max_ff(fabsf(*butal->borders[side] - *butal_other->borders[side_opp]), FLT_MIN);
172 delta_side_opp = max_ff(fabsf(*butal->borders[side_opp] - *butal_other->borders[side]),
173 FLT_MIN);
174 if (delta_side_opp < delta) {
175 SWAP(int, side, side_opp);
176 delta = delta_side_opp;
177 }
178
179 if (delta < max_delta) {
180 /* We are only interested in neighbors that are
181 * at least as close as already found ones. */
182 if (delta <= butal->dists[side]) {
183 {
184 /* We found an as close or closer neighbor.
185 * If both buttons are alignable, we set them as each other neighbors.
186 * Else, we have an unalignable one, we need to reset the others matching
187 * neighbor to NULL if its 'proximity distance'
188 * is really lower with current one.
189 *
190 * NOTE: We cannot only execute that piece of code in case we found a
191 * **closer** neighbor, due to the limited way we represent neighbors
192 * (buttons only know **one** neighbor on each side, when they can
193 * actually have several ones), it would prevent some buttons to be
194 * properly 'neighborly-initialized'. */
195 if (butal_can_align && butal_other_can_align) {
196 butal->neighbors[side] = butal_other;
197 butal_other->neighbors[side_opp] = butal;
198 }
199 else if (butal_can_align && (delta < butal->dists[side])) {
200 butal->neighbors[side] = NULL;
201 }
202 else if (butal_other_can_align && (delta < butal_other->dists[side_opp])) {
203 butal_other->neighbors[side_opp] = NULL;
204 }
205 butal->dists[side] = butal_other->dists[side_opp] = delta;
206 }
207
208 if (butal_can_align && butal_other_can_align) {
209 const int side_s1 = SIDE1(side);
210 const int side_s2 = SIDE2(side);
211
212 const int stitch = STITCH(side);
213 const int stitch_opp = STITCH(side_opp);
214
215 if (butal->neighbors[side] == NULL) {
216 butal->neighbors[side] = butal_other;
217 }
218 if (butal_other->neighbors[side_opp] == NULL) {
219 butal_other->neighbors[side_opp] = butal;
220 }
221
222 /* We have a pair of neighbors, we have to check whether we
223 * can stitch their matching corners.
224 * E.g. if butal_other is on the left of butal (that is, side == LEFT),
225 * if both TOP (side_s1) coordinates of buttons are close enough,
226 * we can stitch their upper matching corners,
227 * and same for DOWN (side_s2) side. */
228 delta = fabsf(*butal->borders[side_s1] - *butal_other->borders[side_s1]);
229 if (delta < max_delta) {
230 butal->flags[side_s1] |= stitch;
231 butal_other->flags[side_s1] |= stitch_opp;
232 }
233 delta = fabsf(*butal->borders[side_s2] - *butal_other->borders[side_s2]);
234 if (delta < max_delta) {
235 butal->flags[side_s2] |= stitch;
236 butal_other->flags[side_s2] |= stitch_opp;
237 }
238 }
239 }
240 /* We assume two buttons can only share one side at most - for until
241 * we have spherical UI. */
242 return;
243 }
244 }
245 }
246 }
247
248 /**
249 * This function takes care of case described in this schema:
250 *
251 * <pre>
252 * +-----------+-----------+
253 * | BUT 1 | BUT 2 |
254 * |-----------------------+
255 * | BUT 3 |
256 * +-----------+
257 * </pre>
258 *
259 * Here, BUT 3 RIGHT side would not get 'dragged' to align with BUT 1 RIGHT side,
260 * since BUT 3 has not RIGHT neighbor.
261 * So, this function, when called with BUT 1, will 'walk' the whole column in \a side_s1 direction
262 * (TOP or DOWN when called for RIGHT side), and force buttons like BUT 3 to align as needed,
263 * if BUT 1 and BUT 3 were detected as needing top-right corner stitching in
264 * #block_align_proximity_compute() step.
265 *
266 * \note To avoid doing this twice, some stitching flags are cleared to break the
267 * 'stitching connection' between neighbors.
268 */
block_align_stitch_neighbors(ButAlign * butal,const int side,const int side_opp,const int side_s1,const int side_s2,const int align,const int align_opp,const float co)269 static void block_align_stitch_neighbors(ButAlign *butal,
270 const int side,
271 const int side_opp,
272 const int side_s1,
273 const int side_s2,
274 const int align,
275 const int align_opp,
276 const float co)
277 {
278 ButAlign *butal_neighbor;
279
280 const int stitch_s1 = STITCH(side_s1);
281 const int stitch_s2 = STITCH(side_s2);
282
283 /* We have to check stitching flags on both sides of the stitching,
284 * since we only clear one of them flags to break any future loop on same 'columns/side' case.
285 * Also, if butal is spanning over several rows or columns of neighbors,
286 * it may have both of its stitching flags
287 * set, but would not be the case of its immediate neighbor! */
288 while ((butal->flags[side] & stitch_s1) && (butal = butal->neighbors[side_s1]) &&
289 (butal->flags[side] & stitch_s2)) {
290 butal_neighbor = butal->neighbors[side];
291
292 /* If we actually do have a neighbor, we directly set its values accordingly,
293 * and clear its matching 'dist' to prevent it being set again later... */
294 if (butal_neighbor) {
295 butal->but->drawflag |= align;
296 butal_neighbor->but->drawflag |= align_opp;
297 *butal_neighbor->borders[side_opp] = co;
298 butal_neighbor->dists[side_opp] = 0.0f;
299 }
300 /* See definition of UI_BUT_ALIGN_STITCH_LEFT/TOP for reason of this... */
301 else if (side == LEFT) {
302 butal->but->drawflag |= UI_BUT_ALIGN_STITCH_LEFT;
303 }
304 else if (side == TOP) {
305 butal->but->drawflag |= UI_BUT_ALIGN_STITCH_TOP;
306 }
307 *butal->borders[side] = co;
308 butal->dists[side] = 0.0f;
309 /* Clearing one of the 'flags pair' here is enough to prevent this loop running on
310 * the same column, side and direction again. */
311 butal->flags[side] &= ~stitch_s2;
312 }
313 }
314
315 /**
316 * Helper to sort ButAlign items by:
317 * - Their align group.
318 * - Their vertical position.
319 * - Their horizontal position.
320 */
ui_block_align_butal_cmp(const void * a,const void * b)321 static int ui_block_align_butal_cmp(const void *a, const void *b)
322 {
323 const ButAlign *butal = a;
324 const ButAlign *butal_other = b;
325
326 /* Sort by align group. */
327 if (butal->but->alignnr != butal_other->but->alignnr) {
328 return butal->but->alignnr - butal_other->but->alignnr;
329 }
330
331 /* Sort vertically.
332 * Note that Y of buttons is decreasing (first buttons have higher Y value than later ones). */
333 if (*butal->borders[TOP] != *butal_other->borders[TOP]) {
334 return (*butal_other->borders[TOP] > *butal->borders[TOP]) ? 1 : -1;
335 }
336
337 /* Sort horizontally. */
338 if (*butal->borders[LEFT] != *butal_other->borders[LEFT]) {
339 return (*butal->borders[LEFT] > *butal_other->borders[LEFT]) ? 1 : -1;
340 }
341
342 /* XXX We cannot actually assert here, since in some very compressed space cases,
343 * stupid UI code produces widgets which have the same TOP and LEFT positions...
344 * We do not care really,
345 * because this happens when UI is way too small to be usable anyway. */
346 /* BLI_assert(0); */
347 return 0;
348 }
349
ui_block_align_but_to_region(uiBut * but,const ARegion * region)350 static void ui_block_align_but_to_region(uiBut *but, const ARegion *region)
351 {
352 rctf *rect = &but->rect;
353 const float but_width = BLI_rctf_size_x(rect);
354 const float but_height = BLI_rctf_size_y(rect);
355 const float outline_px = U.pixelsize; /* This may have to be made more variable. */
356
357 switch (but->drawflag & UI_BUT_ALIGN) {
358 case UI_BUT_ALIGN_TOP:
359 rect->ymax = region->winy + outline_px;
360 rect->ymin = but->rect.ymax - but_height;
361 break;
362 case UI_BUT_ALIGN_DOWN:
363 rect->ymin = -outline_px;
364 rect->ymax = rect->ymin + but_height;
365 break;
366 case UI_BUT_ALIGN_LEFT:
367 rect->xmin = -outline_px;
368 rect->xmax = rect->xmin + but_width;
369 break;
370 case UI_BUT_ALIGN_RIGHT:
371 rect->xmax = region->winx + outline_px;
372 rect->xmin = rect->xmax - but_width;
373 break;
374 default:
375 /* Tabs may be shown in unaligned regions too, they just appear as regular buttons then. */
376 break;
377 }
378 }
379
380 /**
381 * Compute the alignment of all 'align groups' of buttons in given block.
382 *
383 * This is using an order-independent algorithm,
384 * i.e. alignment of buttons should be OK regardless of order in which
385 * they are added to the block.
386 */
ui_block_align_calc(uiBlock * block,const ARegion * region)387 void ui_block_align_calc(uiBlock *block, const ARegion *region)
388 {
389 int num_buttons = 0;
390
391 const int sides_to_ui_but_align_flags[4] = SIDE_TO_UI_BUT_ALIGN;
392
393 ButAlign *butal_array;
394 ButAlign *butal, *butal_other;
395 int side;
396
397 /* First loop: we count number of buttons belonging to an align group,
398 * and clear their align flag.
399 * Tabs get some special treatment here, they get aligned to region border. */
400 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
401 /* special case: tabs need to be aligned to a region border, drawflag tells which one */
402 if (but->type == UI_BTYPE_TAB) {
403 ui_block_align_but_to_region(but, region);
404 }
405 else {
406 /* Clear old align flags. */
407 but->drawflag &= ~UI_BUT_ALIGN_ALL;
408 }
409
410 if (but->alignnr != 0) {
411 num_buttons++;
412 }
413 }
414
415 if (num_buttons < 2) {
416 /* No need to go further if we have nothing to align... */
417 return;
418 }
419
420 /* Note that this is typically less than ~20, and almost always under ~100.
421 * Even so, we can't ensure this value won't exceed available stack memory.
422 * Fallback to allocation instead of using #alloca, see: T78636. */
423 ButAlign butal_array_buf[256];
424 if (num_buttons <= ARRAY_SIZE(butal_array_buf)) {
425 butal_array = butal_array_buf;
426 }
427 else {
428 butal_array = MEM_mallocN(sizeof(*butal_array) * num_buttons, __func__);
429 }
430 memset(butal_array, 0, sizeof(*butal_array) * (size_t)num_buttons);
431
432 /* Second loop: we initialize our ButAlign data for each button. */
433 butal = butal_array;
434 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
435 if (but->alignnr != 0) {
436 butal->but = but;
437 butal->borders[LEFT] = &but->rect.xmin;
438 butal->borders[RIGHT] = &but->rect.xmax;
439 butal->borders[DOWN] = &but->rect.ymin;
440 butal->borders[TOP] = &but->rect.ymax;
441 copy_v4_fl(butal->dists, FLT_MAX);
442 butal++;
443 }
444 }
445
446 /* This will give us ButAlign items regrouped by align group, vertical and horizontal location.
447 * Note that, given how buttons are defined in UI code,
448 * butal_array shall already be "nearly sorted"... */
449 qsort(butal_array, (size_t)num_buttons, sizeof(*butal_array), ui_block_align_butal_cmp);
450
451 /* Third loop: for each pair of buttons in the same align group,
452 * we compute their potential proximity. Note that each pair is checked only once, and that we
453 * break early in case we know all remaining pairs will always be too far away. */
454 int i;
455 for (i = 0, butal = butal_array; i < num_buttons; i++, butal++) {
456 const short alignnr = butal->but->alignnr;
457
458 int j;
459 for (j = i + 1, butal_other = &butal_array[i + 1]; j < num_buttons; j++, butal_other++) {
460 const float max_delta = MAX_DELTA;
461
462 /* Since they are sorted, buttons after current butal can only be of same or higher
463 * group, and once they are not of same group, we know we can break this sub-loop and
464 * start checking with next butal. */
465 if (butal_other->but->alignnr != alignnr) {
466 break;
467 }
468
469 /* Since they are sorted vertically first, buttons after current butal can only be at
470 * same or lower height, and once they are lower than a given threshold, we know we can
471 * break this sub-loop and start checking with next butal. */
472 if ((*butal->borders[DOWN] - *butal_other->borders[TOP]) > max_delta) {
473 break;
474 }
475
476 block_align_proximity_compute(butal, butal_other);
477 }
478 }
479
480 /* Fourth loop: we have all our 'aligned' buttons as a 'map' in butal_array. We need to:
481 * - update their relevant coordinates to stitch them.
482 * - assign them valid flags.
483 */
484 for (i = 0; i < num_buttons; i++) {
485 butal = &butal_array[i];
486
487 for (side = 0; side < TOTSIDES; side++) {
488 butal_other = butal->neighbors[side];
489
490 if (butal_other) {
491 const int side_opp = OPPOSITE(side);
492 const int side_s1 = SIDE1(side);
493 const int side_s2 = SIDE2(side);
494
495 const int align = sides_to_ui_but_align_flags[side];
496 const int align_opp = sides_to_ui_but_align_flags[side_opp];
497
498 float co;
499
500 butal->but->drawflag |= align;
501 butal_other->but->drawflag |= align_opp;
502 if (!IS_EQF(butal->dists[side], 0.0f)) {
503 float *delta = &butal->dists[side];
504
505 if (*butal->borders[side] < *butal_other->borders[side_opp]) {
506 *delta *= 0.5f;
507 }
508 else {
509 *delta *= -0.5f;
510 }
511 co = (*butal->borders[side] += *delta);
512
513 if (!IS_EQF(butal_other->dists[side_opp], 0.0f)) {
514 BLI_assert(butal_other->dists[side_opp] * 0.5f == fabsf(*delta));
515 *butal_other->borders[side_opp] = co;
516 butal_other->dists[side_opp] = 0.0f;
517 }
518 *delta = 0.0f;
519 }
520 else {
521 co = *butal->borders[side];
522 }
523
524 block_align_stitch_neighbors(
525 butal, side, side_opp, side_s1, side_s2, align, align_opp, co);
526 block_align_stitch_neighbors(
527 butal, side, side_opp, side_s2, side_s1, align, align_opp, co);
528 }
529 }
530 }
531 if (butal_array_buf != butal_array) {
532 MEM_freeN(butal_array);
533 }
534 }
535
536 # undef SIDE_TO_UI_BUT_ALIGN
537 # undef SIDE1
538 # undef OPPOSITE
539 # undef SIDE2
540 # undef IS_COLUMN
541 # undef STITCH
542 # undef MAX_DELTA
543
544 #else /* !USE_UIBUT_SPATIAL_ALIGN */
545
ui_but_can_align(const uiBut * but)546 bool ui_but_can_align(const uiBut *but)
547 {
548 return !ELEM(but->type,
549 UI_BTYPE_LABEL,
550 UI_BTYPE_CHECKBOX,
551 UI_BTYPE_CHECKBOX_N,
552 UI_BTYPE_SEPR,
553 UI_BTYPE_SEPR_LINE,
554 UI_BTYPE_SEPR_SPACER);
555 }
556
buts_are_horiz(uiBut * but1,uiBut * but2)557 static bool buts_are_horiz(uiBut *but1, uiBut *but2)
558 {
559 float dx, dy;
560
561 /* simple case which can fail if buttons shift apart
562 * with proportional layouts, see: T38602. */
563 if ((but1->rect.ymin == but2->rect.ymin) && (but1->rect.xmin != but2->rect.xmin)) {
564 return true;
565 }
566
567 dx = fabsf(but1->rect.xmax - but2->rect.xmin);
568 dy = fabsf(but1->rect.ymin - but2->rect.ymax);
569
570 return (dx <= dy);
571 }
572
ui_block_align_calc_but(uiBut * first,short nr)573 static void ui_block_align_calc_but(uiBut *first, short nr)
574 {
575 uiBut *prev, *but = NULL, *next;
576 int flag = 0, cols = 0, rows = 0;
577
578 /* auto align */
579
580 for (but = first; but && but->alignnr == nr; but = but->next) {
581 if (but->next && but->next->alignnr == nr) {
582 if (buts_are_horiz(but, but->next)) {
583 cols++;
584 }
585 else {
586 rows++;
587 }
588 }
589 }
590
591 /* rows == 0: 1 row, cols == 0: 1 column */
592
593 /* note; how it uses 'flag' in loop below (either set it, or OR it) is confusing */
594 for (but = first, prev = NULL; but && but->alignnr == nr; prev = but, but = but->next) {
595 next = but->next;
596 if (next && next->alignnr != nr) {
597 next = NULL;
598 }
599
600 /* clear old flag */
601 but->drawflag &= ~UI_BUT_ALIGN;
602
603 if (flag == 0) { /* first case */
604 if (next) {
605 if (buts_are_horiz(but, next)) {
606 if (rows == 0) {
607 flag = UI_BUT_ALIGN_RIGHT;
608 }
609 else {
610 flag = UI_BUT_ALIGN_DOWN | UI_BUT_ALIGN_RIGHT;
611 }
612 }
613 else {
614 flag = UI_BUT_ALIGN_DOWN;
615 }
616 }
617 }
618 else if (next == NULL) { /* last case */
619 if (prev) {
620 if (buts_are_horiz(prev, but)) {
621 if (rows == 0) {
622 flag = UI_BUT_ALIGN_LEFT;
623 }
624 else {
625 flag = UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_LEFT;
626 }
627 }
628 else {
629 flag = UI_BUT_ALIGN_TOP;
630 }
631 }
632 }
633 else if (buts_are_horiz(but, next)) {
634 /* check if this is already second row */
635 if (prev && buts_are_horiz(prev, but) == 0) {
636 flag &= ~UI_BUT_ALIGN_LEFT;
637 flag |= UI_BUT_ALIGN_TOP;
638 /* exception case: bottom row */
639 if (rows > 0) {
640 uiBut *bt = but;
641 while (bt && bt->alignnr == nr) {
642 if (bt->next && bt->next->alignnr == nr && buts_are_horiz(bt, bt->next) == 0) {
643 break;
644 }
645 bt = bt->next;
646 }
647 if (bt == NULL || bt->alignnr != nr) {
648 flag = UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_RIGHT;
649 }
650 }
651 }
652 else {
653 flag |= UI_BUT_ALIGN_LEFT;
654 }
655 }
656 else {
657 if (cols == 0) {
658 flag |= UI_BUT_ALIGN_TOP;
659 }
660 else { /* next button switches to new row */
661
662 if (prev && buts_are_horiz(prev, but)) {
663 flag |= UI_BUT_ALIGN_LEFT;
664 }
665 else {
666 flag &= ~UI_BUT_ALIGN_LEFT;
667 flag |= UI_BUT_ALIGN_TOP;
668 }
669
670 if ((flag & UI_BUT_ALIGN_TOP) == 0) { /* still top row */
671 if (prev) {
672 if (next && buts_are_horiz(but, next)) {
673 flag = UI_BUT_ALIGN_DOWN | UI_BUT_ALIGN_LEFT | UI_BUT_ALIGN_RIGHT;
674 }
675 else {
676 /* last button in top row */
677 flag = UI_BUT_ALIGN_DOWN | UI_BUT_ALIGN_LEFT;
678 }
679 }
680 else {
681 flag |= UI_BUT_ALIGN_DOWN;
682 }
683 }
684 else {
685 flag |= UI_BUT_ALIGN_TOP;
686 }
687 }
688 }
689
690 but->drawflag |= flag;
691
692 /* merge coordinates */
693 if (prev) {
694 /* simple cases */
695 if (rows == 0) {
696 but->rect.xmin = (prev->rect.xmax + but->rect.xmin) / 2.0f;
697 prev->rect.xmax = but->rect.xmin;
698 }
699 else if (cols == 0) {
700 but->rect.ymax = (prev->rect.ymin + but->rect.ymax) / 2.0f;
701 prev->rect.ymin = but->rect.ymax;
702 }
703 else {
704 if (buts_are_horiz(prev, but)) {
705 but->rect.xmin = (prev->rect.xmax + but->rect.xmin) / 2.0f;
706 prev->rect.xmax = but->rect.xmin;
707 /* copy height too */
708 but->rect.ymax = prev->rect.ymax;
709 }
710 else if (prev->prev && buts_are_horiz(prev->prev, prev) == 0) {
711 /* the previous button is a single one in its row */
712 but->rect.ymax = (prev->rect.ymin + but->rect.ymax) / 2.0f;
713 prev->rect.ymin = but->rect.ymax;
714
715 but->rect.xmin = prev->rect.xmin;
716 if (next && buts_are_horiz(but, next) == 0) {
717 but->rect.xmax = prev->rect.xmax;
718 }
719 }
720 else {
721 /* the previous button is not a single one in its row */
722 but->rect.ymax = prev->rect.ymin;
723 }
724 }
725 }
726 }
727 }
728
ui_block_align_calc(uiBlock * block,const struct ARegion * UNUSED (region))729 void ui_block_align_calc(uiBlock *block, const struct ARegion *UNUSED(region))
730 {
731 short nr;
732
733 /* align buttons with same align nr */
734 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
735 if (but->alignnr) {
736 nr = but->alignnr;
737 ui_block_align_calc_but(but, nr);
738
739 /* skip with same number */
740 for (; but && but->alignnr == nr; but = but->next) {
741 /* pass */
742 }
743
744 if (!but) {
745 break;
746 }
747 }
748 else {
749 but = but->next;
750 }
751 }
752 }
753
754 #endif /* !USE_UIBUT_SPATIAL_ALIGN */
755
ui_but_align_opposite_to_area_align_get(const ARegion * region)756 int ui_but_align_opposite_to_area_align_get(const ARegion *region)
757 {
758 const ARegion *align_region = (region->alignment & RGN_SPLIT_PREV && region->prev) ?
759 region->prev :
760 region;
761
762 switch (RGN_ALIGN_ENUM_FROM_MASK(align_region->alignment)) {
763 case RGN_ALIGN_TOP:
764 return UI_BUT_ALIGN_DOWN;
765 case RGN_ALIGN_BOTTOM:
766 return UI_BUT_ALIGN_TOP;
767 case RGN_ALIGN_LEFT:
768 return UI_BUT_ALIGN_RIGHT;
769 case RGN_ALIGN_RIGHT:
770 return UI_BUT_ALIGN_LEFT;
771 }
772
773 return 0;
774 }
775