Blender V4.3
interface_align.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2015 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "DNA_screen_types.h"
10#include "DNA_userdef_types.h"
11
12#include "BLI_listbase.h"
13#include "BLI_math_vector.h"
14#include "BLI_rect.h"
15
16#include "UI_interface.hh"
17
18#include "interface_intern.hh"
19
20#include "MEM_guardedalloc.h"
21
22#ifdef USE_UIBUT_SPATIAL_ALIGN
23
45struct ButAlign {
47
48 /* Neighbor buttons */
50
51 /* Pointers to coordinates (rctf values) of the button. */
52 float *borders[4];
53
54 /* Distances to the neighbors. */
55 float dists[4];
56
57 /* Flags, used to mark whether we should 'stitch'
58 * the corners of this button with its neighbors' ones. */
59 char flags[4];
60};
61
62/* Side-related enums and flags. */
63enum {
64 /* Sides (used as indices, order is **crucial**,
65 * this allows us to factorize code in a loop over the four sides). */
66 LEFT = 0,
67 TOP = 1,
68 RIGHT = 2,
69 DOWN = 3,
71
72 /* Stitch flags, built from sides values. */
77};
78
79/* Mapping between 'our' sides and 'public' UI_BUT_ALIGN flags, order must match enum above. */
80# define SIDE_TO_UI_BUT_ALIGN \
81 { \
82 UI_BUT_ALIGN_LEFT, UI_BUT_ALIGN_TOP, UI_BUT_ALIGN_RIGHT, UI_BUT_ALIGN_DOWN \
83 }
84
85/* Given one side, compute the three other ones */
86# define SIDE1(_s) (((_s) + 1) % TOTSIDES)
87# define OPPOSITE(_s) (((_s) + 2) % TOTSIDES)
88# define SIDE2(_s) (((_s) + 3) % TOTSIDES)
89
90/* 0: LEFT/RIGHT sides; 1 = TOP/DOWN sides. */
91# define IS_COLUMN(_s) ((_s) % 2)
92
93/* Stitch flag from side value. */
94# define STITCH(_s) (1 << (_s))
95
96/* Max distance between to buttons for them to be 'mergeable'. */
97# define MAX_DELTA 0.45f * max_ii(UI_UNIT_Y, UI_UNIT_X)
98
99bool ui_but_can_align(const uiBut *but)
100{
101 const bool btype_can_align = !ELEM(but->type,
109 return (btype_can_align && (BLI_rctf_size_x(&but->rect) > 0.0f) &&
110 (BLI_rctf_size_y(&but->rect) > 0.0f));
111}
112
120static void block_align_proximity_compute(ButAlign *butal, ButAlign *butal_other)
121{
122 /* That's the biggest gap between two borders to consider them 'alignable'. */
123 const float max_delta = MAX_DELTA;
124 float delta, delta_side_opp;
125 int side, side_opp;
126
127 const bool butal_can_align = ui_but_can_align(butal->but);
128 const bool butal_other_can_align = ui_but_can_align(butal_other->but);
129
130 const bool buts_share[2] = {
131 /* Sharing same line? */
132 !((*butal->borders[DOWN] >= *butal_other->borders[TOP]) ||
133 (*butal->borders[TOP] <= *butal_other->borders[DOWN])),
134 /* Sharing same column? */
135 !((*butal->borders[LEFT] >= *butal_other->borders[RIGHT]) ||
136 (*butal->borders[RIGHT] <= *butal_other->borders[LEFT])),
137 };
138
139 /* Early out in case buttons share no column or line, or if none can align... */
140 if (!(buts_share[0] || buts_share[1]) || !(butal_can_align || butal_other_can_align)) {
141 return;
142 }
143
144 for (side = 0; side < RIGHT; side++) {
145 /* We are only interested in buttons which share a same line
146 * (LEFT/RIGHT sides) or column (TOP/DOWN sides). */
147 if (buts_share[IS_COLUMN(side)]) {
148 side_opp = OPPOSITE(side);
149
150 /* We check both opposite sides at once, because with very small buttons,
151 * delta could be below max_delta for the wrong side
152 * (that is, in horizontal case, the total width of two buttons can be below max_delta).
153 * We rely on exact zero value here as an 'already processed' flag,
154 * so ensure we never actually set a zero value at this stage.
155 * FLT_MIN is zero-enough for UI position computing. ;) */
156 delta = max_ff(fabsf(*butal->borders[side] - *butal_other->borders[side_opp]), FLT_MIN);
157 delta_side_opp = max_ff(fabsf(*butal->borders[side_opp] - *butal_other->borders[side]),
158 FLT_MIN);
159 if (delta_side_opp < delta) {
160 std::swap(side, side_opp);
161 delta = delta_side_opp;
162 }
163
164 if (delta < max_delta) {
165 /* We are only interested in neighbors that are
166 * at least as close as already found ones. */
167 if (delta <= butal->dists[side]) {
168 {
169 /* We found an as close or closer neighbor.
170 * If both buttons are alignable, we set them as each other neighbors.
171 * Else, we have an unalignable one, we need to reset the others matching
172 * neighbor to nullptr if its 'proximity distance'
173 * is really lower with current one.
174 *
175 * NOTE: We cannot only execute that piece of code in case we found a
176 * **closer** neighbor, due to the limited way we represent neighbors
177 * (buttons only know **one** neighbor on each side, when they can
178 * actually have several ones), it would prevent some buttons to be
179 * properly 'neighborly-initialized'. */
180 if (butal_can_align && butal_other_can_align) {
181 butal->neighbors[side] = butal_other;
182 butal_other->neighbors[side_opp] = butal;
183 }
184 else if (butal_can_align && (delta < butal->dists[side])) {
185 butal->neighbors[side] = nullptr;
186 }
187 else if (butal_other_can_align && (delta < butal_other->dists[side_opp])) {
188 butal_other->neighbors[side_opp] = nullptr;
189 }
190 butal->dists[side] = butal_other->dists[side_opp] = delta;
191 }
192
193 if (butal_can_align && butal_other_can_align) {
194 const int side_s1 = SIDE1(side);
195 const int side_s2 = SIDE2(side);
196
197 const int stitch = STITCH(side);
198 const int stitch_opp = STITCH(side_opp);
199
200 if (butal->neighbors[side] == nullptr) {
201 butal->neighbors[side] = butal_other;
202 }
203 if (butal_other->neighbors[side_opp] == nullptr) {
204 butal_other->neighbors[side_opp] = butal;
205 }
206
207 /* We have a pair of neighbors, we have to check whether we
208 * can stitch their matching corners.
209 * E.g. if butal_other is on the left of butal (that is, side == LEFT),
210 * if both TOP (side_s1) coordinates of buttons are close enough,
211 * we can stitch their upper matching corners,
212 * and same for DOWN (side_s2) side. */
213 delta = fabsf(*butal->borders[side_s1] - *butal_other->borders[side_s1]);
214 if (delta < max_delta) {
215 butal->flags[side_s1] |= stitch;
216 butal_other->flags[side_s1] |= stitch_opp;
217 }
218 delta = fabsf(*butal->borders[side_s2] - *butal_other->borders[side_s2]);
219 if (delta < max_delta) {
220 butal->flags[side_s2] |= stitch;
221 butal_other->flags[side_s2] |= stitch_opp;
222 }
223 }
224 }
225 /* We assume two buttons can only share one side at most - for until
226 * we have spherical UI. */
227 return;
228 }
229 }
230 }
231}
232
255 const int side,
256 const int side_opp,
257 const int side_s1,
258 const int side_s2,
259 const int align,
260 const int align_opp,
261 const float co)
262{
263 ButAlign *butal_neighbor;
264
265 const int stitch_s1 = STITCH(side_s1);
266 const int stitch_s2 = STITCH(side_s2);
267
268 /* We have to check stitching flags on both sides of the stitching,
269 * since we only clear one of them flags to break any future loop on same 'columns/side' case.
270 * Also, if butal is spanning over several rows or columns of neighbors,
271 * it may have both of its stitching flags
272 * set, but would not be the case of its immediate neighbor! */
273 while ((butal->flags[side] & stitch_s1) && (butal = butal->neighbors[side_s1]) &&
274 (butal->flags[side] & stitch_s2))
275 {
276 butal_neighbor = butal->neighbors[side];
277
278 /* If we actually do have a neighbor, we directly set its values accordingly,
279 * and clear its matching 'dist' to prevent it being set again later... */
280 if (butal_neighbor) {
281 butal->but->drawflag |= align;
282 butal_neighbor->but->drawflag |= align_opp;
283 *butal_neighbor->borders[side_opp] = co;
284 butal_neighbor->dists[side_opp] = 0.0f;
285 }
286 /* See definition of UI_BUT_ALIGN_STITCH_LEFT/TOP for reason of this... */
287 else if (side == LEFT) {
289 }
290 else if (side == TOP) {
292 }
293 *butal->borders[side] = co;
294 butal->dists[side] = 0.0f;
295 /* Clearing one of the 'flags pair' here is enough to prevent this loop running on
296 * the same column, side and direction again. */
297 butal->flags[side] &= ~stitch_s2;
298 }
299}
300
307static int ui_block_align_butal_cmp(const void *a, const void *b)
308{
309 const ButAlign *butal = static_cast<const ButAlign *>(a);
310 const ButAlign *butal_other = static_cast<const ButAlign *>(b);
311
312 /* Sort by align group. */
313 if (butal->but->alignnr != butal_other->but->alignnr) {
314 return butal->but->alignnr - butal_other->but->alignnr;
315 }
316
317 /* Sort vertically.
318 * Note that Y of buttons is decreasing (first buttons have higher Y value than later ones). */
319 if (*butal->borders[TOP] != *butal_other->borders[TOP]) {
320 return (*butal_other->borders[TOP] > *butal->borders[TOP]) ? 1 : -1;
321 }
322
323 /* Sort horizontally. */
324 if (*butal->borders[LEFT] != *butal_other->borders[LEFT]) {
325 return (*butal->borders[LEFT] > *butal_other->borders[LEFT]) ? 1 : -1;
326 }
327
328 /* XXX We cannot actually assert here, since in some very compressed space cases,
329 * stupid UI code produces widgets which have the same TOP and LEFT positions...
330 * We do not care really,
331 * because this happens when UI is way too small to be usable anyway. */
332 // BLI_assert(0);
333 return 0;
334}
335
336static void ui_block_align_but_to_region(uiBut *but, const ARegion *region)
337{
338 rctf *rect = &but->rect;
339 const float but_width = BLI_rctf_size_x(rect);
340 const float but_height = BLI_rctf_size_y(rect);
341 const float outline_px = U.pixelsize; /* This may have to be made more variable. */
342
343 switch (but->drawflag & UI_BUT_ALIGN) {
344 case UI_BUT_ALIGN_TOP:
345 rect->ymax = region->winy + outline_px;
346 rect->ymin = but->rect.ymax - but_height;
347 break;
349 rect->ymin = -outline_px;
350 rect->ymax = rect->ymin + but_height;
351 break;
353 rect->xmin = -outline_px;
354 rect->xmax = rect->xmin + but_width;
355 break;
357 rect->xmax = region->winx + outline_px;
358 rect->xmin = rect->xmax - but_width;
359 break;
360 default:
361 /* Tabs may be shown in unaligned regions too, they just appear as regular buttons then. */
362 break;
363 }
364}
365
366void ui_block_align_calc(uiBlock *block, const ARegion *region)
367{
368 int num_buttons = 0;
369
370 const int sides_to_ui_but_align_flags[4] = SIDE_TO_UI_BUT_ALIGN;
371
372 ButAlign *butal_array;
373 ButAlign *butal, *butal_other;
374 int side;
375
376 /* First loop: we count number of buttons belonging to an align group,
377 * and clear their align flag.
378 * Tabs get some special treatment here, they get aligned to region border. */
379 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
380 /* special case: tabs need to be aligned to a region border, drawflag tells which one */
381 if (but->type == UI_BTYPE_TAB) {
382 ui_block_align_but_to_region(but, region);
383 }
384 else {
385 /* Clear old align flags. */
386 but->drawflag &= ~UI_BUT_ALIGN_ALL;
387 }
388
389 if (but->alignnr != 0) {
390 num_buttons++;
391 }
392 }
393
394 if (num_buttons < 2) {
395 /* No need to go further if we have nothing to align... */
396 return;
397 }
398
399 /* Note that this is typically less than ~20, and almost always under ~100.
400 * Even so, we can't ensure this value won't exceed available stack memory.
401 * Fallback to allocation instead of using #alloca, see: #78636. */
402 ButAlign butal_array_buf[256];
403 if (num_buttons <= ARRAY_SIZE(butal_array_buf)) {
404 butal_array = butal_array_buf;
405 }
406 else {
407 butal_array = static_cast<ButAlign *>(
408 MEM_mallocN(sizeof(*butal_array) * num_buttons, __func__));
409 }
410 memset(butal_array, 0, sizeof(*butal_array) * size_t(num_buttons));
411
412 /* Second loop: we initialize our ButAlign data for each button. */
413 butal = butal_array;
414 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
415 if (but->alignnr != 0) {
416 butal->but = but;
417 butal->borders[LEFT] = &but->rect.xmin;
418 butal->borders[RIGHT] = &but->rect.xmax;
419 butal->borders[DOWN] = &but->rect.ymin;
420 butal->borders[TOP] = &but->rect.ymax;
421 copy_v4_fl(butal->dists, FLT_MAX);
422 butal++;
423 }
424 }
425
426 /* This will give us ButAlign items regrouped by align group, vertical and horizontal location.
427 * Note that, given how buttons are defined in UI code,
428 * butal_array shall already be "nearly sorted"... */
429 qsort(butal_array, size_t(num_buttons), sizeof(*butal_array), ui_block_align_butal_cmp);
430
431 /* Third loop: for each pair of buttons in the same align group,
432 * we compute their potential proximity. Note that each pair is checked only once, and that we
433 * break early in case we know all remaining pairs will always be too far away. */
434 int i;
435 for (i = 0, butal = butal_array; i < num_buttons; i++, butal++) {
436 const short alignnr = butal->but->alignnr;
437
438 int j;
439 for (j = i + 1, butal_other = &butal_array[i + 1]; j < num_buttons; j++, butal_other++) {
440 const float max_delta = MAX_DELTA;
441
442 /* Since they are sorted, buttons after current butal can only be of same or higher
443 * group, and once they are not of same group, we know we can break this sub-loop and
444 * start checking with next butal. */
445 if (butal_other->but->alignnr != alignnr) {
446 break;
447 }
448
449 /* Since they are sorted vertically first, buttons after current butal can only be at
450 * same or lower height, and once they are lower than a given threshold, we know we can
451 * break this sub-loop and start checking with next butal. */
452 if ((*butal->borders[DOWN] - *butal_other->borders[TOP]) > max_delta) {
453 break;
454 }
455
456 block_align_proximity_compute(butal, butal_other);
457 }
458 }
459
460 /* Fourth loop: we have all our 'aligned' buttons as a 'map' in butal_array. We need to:
461 * - update their relevant coordinates to stitch them.
462 * - assign them valid flags.
463 */
464 for (i = 0; i < num_buttons; i++) {
465 butal = &butal_array[i];
466
467 for (side = 0; side < TOTSIDES; side++) {
468 butal_other = butal->neighbors[side];
469
470 if (butal_other) {
471 const int side_opp = OPPOSITE(side);
472 const int side_s1 = SIDE1(side);
473 const int side_s2 = SIDE2(side);
474
475 const int align = sides_to_ui_but_align_flags[side];
476 const int align_opp = sides_to_ui_but_align_flags[side_opp];
477
478 float co;
479
480 butal->but->drawflag |= align;
481 butal_other->but->drawflag |= align_opp;
482 if (!IS_EQF(butal->dists[side], 0.0f)) {
483 float *delta = &butal->dists[side];
484
485 if (*butal->borders[side] < *butal_other->borders[side_opp]) {
486 *delta *= 0.5f;
487 }
488 else {
489 *delta *= -0.5f;
490 }
491 co = (*butal->borders[side] += *delta);
492
493 if (!IS_EQF(butal_other->dists[side_opp], 0.0f)) {
494 BLI_assert(butal_other->dists[side_opp] * 0.5f == fabsf(*delta));
495 *butal_other->borders[side_opp] = co;
496 butal_other->dists[side_opp] = 0.0f;
497 }
498 *delta = 0.0f;
499 }
500 else {
501 co = *butal->borders[side];
502 }
503
505 butal, side, side_opp, side_s1, side_s2, align, align_opp, co);
507 butal, side, side_opp, side_s2, side_s1, align, align_opp, co);
508 }
509 }
510 }
511 if (butal_array_buf != butal_array) {
512 MEM_freeN(butal_array);
513 }
514}
515
516# undef SIDE_TO_UI_BUT_ALIGN
517# undef SIDE1
518# undef OPPOSITE
519# undef SIDE2
520# undef IS_COLUMN
521# undef STITCH
522# undef MAX_DELTA
523
524#else /* !USE_UIBUT_SPATIAL_ALIGN */
525
526bool ui_but_can_align(const uiBut *but)
527{
528 return !ELEM(but->type,
535}
536
537static bool buts_are_horiz(uiBut *but1, uiBut *but2)
538{
539 float dx, dy;
540
541 /* simple case which can fail if buttons shift apart
542 * with proportional layouts, see: #38602. */
543 if ((but1->rect.ymin == but2->rect.ymin) && (but1->rect.xmin != but2->rect.xmin)) {
544 return true;
545 }
546
547 dx = fabsf(but1->rect.xmax - but2->rect.xmin);
548 dy = fabsf(but1->rect.ymin - but2->rect.ymax);
549
550 return (dx <= dy);
551}
552
553static void ui_block_align_calc_but(uiBut *first, short nr)
554{
555 uiBut *prev, *but = nullptr, *next;
556 int flag = 0, cols = 0, rows = 0;
557
558 /* auto align */
559
560 for (but = first; but && but->alignnr == nr; but = but->next) {
561 if (but->next && but->next->alignnr == nr) {
562 if (buts_are_horiz(but, but->next)) {
563 cols++;
564 }
565 else {
566 rows++;
567 }
568 }
569 }
570
571 /* rows == 0: 1 row, cols == 0: 1 column */
572
573 /* NOTE: manipulation of 'flag' in the loop below is confusing.
574 * In some cases it's assigned, other times OR is used. */
575 for (but = first, prev = nullptr; but && but->alignnr == nr; prev = but, but = but->next) {
576 next = but->next;
577 if (next && next->alignnr != nr) {
578 next = nullptr;
579 }
580
581 /* clear old flag */
582 but->drawflag &= ~UI_BUT_ALIGN;
583
584 if (flag == 0) { /* first case */
585 if (next) {
586 if (buts_are_horiz(but, next)) {
587 if (rows == 0) {
589 }
590 else {
592 }
593 }
594 else {
596 }
597 }
598 }
599 else if (next == nullptr) { /* last case */
600 if (prev) {
601 if (buts_are_horiz(prev, but)) {
602 if (rows == 0) {
604 }
605 else {
607 }
608 }
609 else {
611 }
612 }
613 }
614 else if (buts_are_horiz(but, next)) {
615 /* check if this is already second row */
616 if (prev && buts_are_horiz(prev, but) == 0) {
617 flag &= ~UI_BUT_ALIGN_LEFT;
619 /* exception case: bottom row */
620 if (rows > 0) {
621 uiBut *bt = but;
622 while (bt && bt->alignnr == nr) {
623 if (bt->next && bt->next->alignnr == nr && buts_are_horiz(bt, bt->next) == 0) {
624 break;
625 }
626 bt = bt->next;
627 }
628 if (bt == nullptr || bt->alignnr != nr) {
630 }
631 }
632 }
633 else {
635 }
636 }
637 else {
638 if (cols == 0) {
640 }
641 else { /* next button switches to new row */
642
643 if (prev && buts_are_horiz(prev, but)) {
645 }
646 else {
647 flag &= ~UI_BUT_ALIGN_LEFT;
649 }
650
651 if ((flag & UI_BUT_ALIGN_TOP) == 0) { /* still top row */
652 if (prev) {
653 if (next && buts_are_horiz(but, next)) {
655 }
656 else {
657 /* last button in top row */
659 }
660 }
661 else {
663 }
664 }
665 else {
667 }
668 }
669 }
670
671 but->drawflag |= flag;
672
673 /* merge coordinates */
674 if (prev) {
675 /* simple cases */
676 if (rows == 0) {
677 but->rect.xmin = (prev->rect.xmax + but->rect.xmin) / 2.0f;
678 prev->rect.xmax = but->rect.xmin;
679 }
680 else if (cols == 0) {
681 but->rect.ymax = (prev->rect.ymin + but->rect.ymax) / 2.0f;
682 prev->rect.ymin = but->rect.ymax;
683 }
684 else {
685 if (buts_are_horiz(prev, but)) {
686 but->rect.xmin = (prev->rect.xmax + but->rect.xmin) / 2.0f;
687 prev->rect.xmax = but->rect.xmin;
688 /* copy height too */
689 but->rect.ymax = prev->rect.ymax;
690 }
691 else if (prev->prev && buts_are_horiz(prev->prev, prev) == 0) {
692 /* the previous button is a single one in its row */
693 but->rect.ymax = (prev->rect.ymin + but->rect.ymax) / 2.0f;
694 prev->rect.ymin = but->rect.ymax;
695
696 but->rect.xmin = prev->rect.xmin;
697 if (next && buts_are_horiz(but, next) == 0) {
698 but->rect.xmax = prev->rect.xmax;
699 }
700 }
701 else {
702 /* the previous button is not a single one in its row */
703 but->rect.ymax = prev->rect.ymin;
704 }
705 }
706 }
707 }
708}
709
710void ui_block_align_calc(uiBlock *block, const struct ARegion *(region))
711{
712 short nr;
713
714 /* align buttons with same align nr */
715 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
716 if (but->alignnr) {
717 nr = but->alignnr;
718 ui_block_align_calc_but(but, nr);
719
720 /* skip with same number */
721 for (; but && but->alignnr == nr; but = but->next) {
722 /* pass */
723 }
724
725 if (!but) {
726 break;
727 }
728 }
729 else {
730 but = but->next;
731 }
732 }
733}
734
735#endif /* !USE_UIBUT_SPATIAL_ALIGN */
736
738{
739 const ARegion *align_region = (region->alignment & RGN_SPLIT_PREV && region->prev) ?
740 region->prev :
741 region;
742
743 switch (RGN_ALIGN_ENUM_FROM_MASK(align_region->alignment)) {
744 case RGN_ALIGN_TOP:
745 return UI_BUT_ALIGN_DOWN;
746 case RGN_ALIGN_BOTTOM:
747 return UI_BUT_ALIGN_TOP;
748 case RGN_ALIGN_LEFT:
749 return UI_BUT_ALIGN_RIGHT;
750 case RGN_ALIGN_RIGHT:
751 return UI_BUT_ALIGN_LEFT;
752 }
753
754 return 0;
755}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE void copy_v4_fl(float r[4], float f)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
#define ARRAY_SIZE(arr)
#define ELEM(...)
#define IS_EQF(a, b)
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_LEFT
@ RGN_ALIGN_TOP
@ RGN_ALIGN_RIGHT
@ RGN_SPLIT_PREV
Read Guarded memory(de)allocation.
@ UI_BTYPE_TAB
@ UI_BTYPE_SEPR_SPACER
@ UI_BTYPE_LABEL
@ UI_BTYPE_SEPR_LINE
@ UI_BTYPE_CHECKBOX_N
@ UI_BTYPE_SEPR
@ UI_BTYPE_CHECKBOX
@ UI_BUT_ALIGN_DOWN
@ UI_BUT_ALIGN_TOP
@ UI_BUT_ALIGN
@ UI_BUT_ALIGN_STITCH_TOP
@ UI_BUT_ALIGN_STITCH_LEFT
@ UI_BUT_ALIGN_RIGHT
@ UI_BUT_ALIGN_LEFT
unsigned int U
Definition btGjkEpa3.h:78
local_group_size(16, 16) .push_constant(Type b
#define fabsf(x)
#define IS_COLUMN(_s)
#define MAX_DELTA
bool ui_but_can_align(const uiBut *but)
static void 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)
static void ui_block_align_but_to_region(uiBut *but, const ARegion *region)
int ui_but_align_opposite_to_area_align_get(const ARegion *region)
static void block_align_proximity_compute(ButAlign *butal, ButAlign *butal_other)
static int ui_block_align_butal_cmp(const void *a, const void *b)
#define SIDE_TO_UI_BUT_ALIGN
#define SIDE2(_s)
#define STITCH(_s)
#define SIDE1(_s)
#define OPPOSITE(_s)
@ TOP
@ STITCH_DOWN
@ STITCH_RIGHT
@ STITCH_LEFT
@ STITCH_TOP
@ DOWN
@ TOTSIDES
@ LEFT
@ RIGHT
void ui_block_align_calc(uiBlock *block, const ARegion *region)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static ulong * next
SymEdge< T > * prev(const SymEdge< T > *se)
#define FLT_MAX
Definition stdcycles.h:14
struct ARegion * prev
ButAlign * neighbors[4]
float dists[4]
char flags[4]
float * borders[4]
float xmax
float xmin
float ymax
float ymin
ListBase buttons
eButType type
uiBut * next
uint8_t flag
Definition wm_window.cc:138