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