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