Edge Slide: Fix multi-object for loop early exit
[blender.git] / source / blender / editors / screen / area_query.c
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
17 /** \file
18  * \ingroup edscr
19  *
20  * Query functions for area/region.
21  */
22
23 #include "DNA_userdef_types.h"
24
25 #include "BLI_blenlib.h"
26 #include "BLI_utildefines.h"
27 #include "BLI_math_base.h"
28
29 #include "RNA_types.h"
30
31 #include "WM_api.h"
32
33 #include "ED_screen.h"
34
35 #include "UI_interface.h"
36 #include "UI_view2d.h"
37
38 bool ED_region_overlap_isect_x(const ARegion *ar, const int event_x)
39 {
40   BLI_assert(ar->overlap);
41   /* No contents, skip it. */
42   if (ar->v2d.mask.xmin == ar->v2d.mask.xmax) {
43     return false;
44   }
45   return BLI_rctf_isect_x(&ar->v2d.tot,
46                           UI_view2d_region_to_view_x(&ar->v2d, event_x - ar->winrct.xmin));
47 }
48
49 bool ED_region_overlap_isect_y(const ARegion *ar, const int event_y)
50 {
51   BLI_assert(ar->overlap);
52   /* No contents, skip it. */
53   if (ar->v2d.mask.ymin == ar->v2d.mask.ymax) {
54     return false;
55   }
56   return BLI_rctf_isect_y(&ar->v2d.tot,
57                           UI_view2d_region_to_view_y(&ar->v2d, event_y - ar->winrct.ymin));
58 }
59
60 bool ED_region_overlap_isect_xy(const ARegion *ar, const int event_xy[2])
61 {
62   return (ED_region_overlap_isect_x(ar, event_xy[0]) &&
63           ED_region_overlap_isect_y(ar, event_xy[1]));
64 }
65
66 bool ED_region_panel_category_gutter_calc_rect(const ARegion *ar, rcti *r_ar_gutter)
67 {
68   *r_ar_gutter = ar->winrct;
69   if (UI_panel_category_is_visible(ar)) {
70     const int category_tabs_width = round_fl_to_int(UI_view2d_scale_get_x(&ar->v2d) *
71                                                     UI_PANEL_CATEGORY_MARGIN_WIDTH);
72     if (ar->alignment == RGN_ALIGN_LEFT) {
73       r_ar_gutter->xmax = r_ar_gutter->xmin + category_tabs_width;
74     }
75     else if (ar->alignment == RGN_ALIGN_RIGHT) {
76       r_ar_gutter->xmin = r_ar_gutter->xmax - category_tabs_width;
77     }
78     else {
79       BLI_assert(!"Unsupported alignment");
80     }
81     return true;
82   }
83   return false;
84 }
85
86 bool ED_region_panel_category_gutter_isect_xy(const ARegion *ar, const int event_xy[2])
87 {
88   rcti ar_gutter;
89   if (ED_region_panel_category_gutter_calc_rect(ar, &ar_gutter)) {
90     return BLI_rcti_isect_pt_v(&ar_gutter, event_xy);
91   }
92   return false;
93 }
94
95 bool ED_region_overlap_isect_x_with_margin(const ARegion *ar, const int event_x, const int margin)
96 {
97   BLI_assert(ar->overlap);
98   /* No contents, skip it. */
99   if (ar->v2d.mask.xmin == ar->v2d.mask.xmax) {
100     return false;
101   }
102   int region_x = event_x - ar->winrct.xmin;
103   return ((ar->v2d.tot.xmin <= UI_view2d_region_to_view_x(&ar->v2d, region_x + margin)) &&
104           (ar->v2d.tot.xmax >= UI_view2d_region_to_view_x(&ar->v2d, region_x - margin)));
105 }
106
107 bool ED_region_overlap_isect_y_with_margin(const ARegion *ar, const int event_y, const int margin)
108 {
109   BLI_assert(ar->overlap);
110   /* No contents, skip it. */
111   if (ar->v2d.mask.ymin == ar->v2d.mask.ymax) {
112     return false;
113   }
114   int region_y = event_y - ar->winrct.ymin;
115   return ((ar->v2d.tot.ymin <= UI_view2d_region_to_view_y(&ar->v2d, region_y + margin)) &&
116           (ar->v2d.tot.ymax >= UI_view2d_region_to_view_y(&ar->v2d, region_y - margin)));
117 }
118
119 bool ED_region_overlap_isect_xy_with_margin(const ARegion *ar,
120                                             const int event_xy[2],
121                                             const int margin)
122 {
123   return (ED_region_overlap_isect_x_with_margin(ar, event_xy[0], margin) &&
124           ED_region_overlap_isect_y_with_margin(ar, event_xy[1], margin));
125 }
126
127 bool ED_region_contains_xy(const ARegion *ar, const int event_xy[2])
128 {
129   /* Only use the margin when inside the region. */
130   if (BLI_rcti_isect_pt_v(&ar->winrct, event_xy)) {
131     if (ar->overlap) {
132       const int overlap_margin = UI_REGION_OVERLAP_MARGIN;
133       /* Note the View2D.tot isn't reliable for headers with spacers otherwise
134        * we'd check #ED_region_overlap_isect_xy_with_margin for both bases. */
135       if (ar->v2d.keeptot == V2D_KEEPTOT_STRICT) {
136         /* Header. */
137         rcti rect;
138         BLI_rcti_init_pt_radius(&rect, event_xy, overlap_margin);
139         if (UI_region_but_find_rect_over(ar, &rect) == NULL) {
140           return false;
141         }
142       }
143       else {
144         /* Side-bar & any other kind of overlapping region. */
145
146         /* Check alignment to avoid region tabs being clipped out
147          * by only clipping a single axis for aligned regions. */
148         if (ELEM(ar->alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
149           if (!ED_region_overlap_isect_x_with_margin(ar, event_xy[0], overlap_margin)) {
150             return false;
151           }
152         }
153         else if (ELEM(ar->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
154           if (ED_region_panel_category_gutter_isect_xy(ar, event_xy)) {
155             /* pass */
156           }
157           else if (!ED_region_overlap_isect_y_with_margin(ar, event_xy[1], overlap_margin)) {
158             return false;
159           }
160         }
161         else {
162           /* No panel categories for horizontal regions currently. */
163           if (!ED_region_overlap_isect_xy_with_margin(ar, event_xy, overlap_margin)) {
164             return false;
165           }
166         }
167       }
168     }
169     return true;
170   }
171   return false;
172 }