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 edinterface
19  *
20  * Utilities to inspect the interface, extract information.
21  */
22 
23 #include "BLI_listbase.h"
24 #include "BLI_math.h"
25 #include "BLI_rect.h"
26 #include "BLI_utildefines.h"
27 
28 #include "DNA_screen_types.h"
29 
30 #include "UI_interface.h"
31 #include "UI_view2d.h"
32 
33 #include "RNA_access.h"
34 
35 #include "interface_intern.h"
36 
37 #include "WM_api.h"
38 #include "WM_types.h"
39 
40 /* -------------------------------------------------------------------- */
41 /** \name Button (#uiBut) State
42  * \{ */
43 
ui_but_is_editable(const uiBut * but)44 bool ui_but_is_editable(const uiBut *but)
45 {
46   return !ELEM(but->type,
47                UI_BTYPE_LABEL,
48                UI_BTYPE_SEPR,
49                UI_BTYPE_SEPR_LINE,
50                UI_BTYPE_ROUNDBOX,
51                UI_BTYPE_LISTBOX,
52                UI_BTYPE_PROGRESS_BAR);
53 }
54 
ui_but_is_editable_as_text(const uiBut * but)55 bool ui_but_is_editable_as_text(const uiBut *but)
56 {
57   return ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_SEARCH_MENU);
58 }
59 
ui_but_is_toggle(const uiBut * but)60 bool ui_but_is_toggle(const uiBut *but)
61 {
62   return ELEM(but->type,
63               UI_BTYPE_BUT_TOGGLE,
64               UI_BTYPE_TOGGLE,
65               UI_BTYPE_ICON_TOGGLE,
66               UI_BTYPE_ICON_TOGGLE_N,
67               UI_BTYPE_TOGGLE_N,
68               UI_BTYPE_CHECKBOX,
69               UI_BTYPE_CHECKBOX_N,
70               UI_BTYPE_ROW);
71 }
72 
73 /**
74  * Can we mouse over the button or is it hidden/disabled/layout.
75  * \note ctrl is kind of a hack currently,
76  * so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed.
77  */
ui_but_is_interactive(const uiBut * but,const bool labeledit)78 bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
79 {
80   /* note, UI_BTYPE_LABEL is included for highlights, this allows drags */
81   if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == NULL) {
82     return false;
83   }
84   if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) {
85     return false;
86   }
87   if (but->flag & UI_HIDDEN) {
88     return false;
89   }
90   if (but->flag & UI_SCROLLED) {
91     return false;
92   }
93   if ((but->type == UI_BTYPE_TEXT) && (but->emboss == UI_EMBOSS_NONE) && !labeledit) {
94     return false;
95   }
96   if ((but->type == UI_BTYPE_LISTROW) && labeledit) {
97     return false;
98   }
99 
100   return true;
101 }
102 
103 /* file selectors are exempt from utf-8 checks */
UI_but_is_utf8(const uiBut * but)104 bool UI_but_is_utf8(const uiBut *but)
105 {
106   if (but->rnaprop) {
107     const int subtype = RNA_property_subtype(but->rnaprop);
108     return !(ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING));
109   }
110   return !(but->flag & UI_BUT_NO_UTF8);
111 }
112 
113 #ifdef USE_UI_POPOVER_ONCE
ui_but_is_popover_once_compat(const uiBut * but)114 bool ui_but_is_popover_once_compat(const uiBut *but)
115 {
116   return (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR) || ui_but_is_toggle(but));
117 }
118 #endif
119 
ui_but_has_array_value(const uiBut * but)120 bool ui_but_has_array_value(const uiBut *but)
121 {
122   return (but->rnapoin.data && but->rnaprop &&
123           ELEM(RNA_property_subtype(but->rnaprop),
124                PROP_COLOR,
125                PROP_TRANSLATION,
126                PROP_DIRECTION,
127                PROP_VELOCITY,
128                PROP_ACCELERATION,
129                PROP_MATRIX,
130                PROP_EULER,
131                PROP_QUATERNION,
132                PROP_AXISANGLE,
133                PROP_XYZ,
134                PROP_XYZ_LENGTH,
135                PROP_COLOR_GAMMA,
136                PROP_COORDS));
137 }
138 
139 static wmOperatorType *g_ot_tool_set_by_id = NULL;
UI_but_is_tool(const uiBut * but)140 bool UI_but_is_tool(const uiBut *but)
141 {
142   /* very evil! */
143   if (but->optype != NULL) {
144     if (g_ot_tool_set_by_id == NULL) {
145       g_ot_tool_set_by_id = WM_operatortype_find("WM_OT_tool_set_by_id", false);
146     }
147     if (but->optype == g_ot_tool_set_by_id) {
148       return true;
149     }
150   }
151   return false;
152 }
153 
UI_but_has_tooltip_label(const uiBut * but)154 bool UI_but_has_tooltip_label(const uiBut *but)
155 {
156   if ((but->drawstr[0] == '\0') && !ui_block_is_popover(but->block)) {
157     return UI_but_is_tool(but);
158   }
159   return false;
160 }
161 
ui_but_icon(const uiBut * but)162 int ui_but_icon(const uiBut *but)
163 {
164   if (!(but->flag & UI_HAS_ICON)) {
165     return ICON_NONE;
166   }
167 
168   /* Consecutive icons can be toggle between. */
169   if (but->drawflag & UI_BUT_ICON_REVERSE) {
170     return but->icon - but->iconadd;
171   }
172   return but->icon + but->iconadd;
173 }
174 
175 /** \} */
176 
177 /* -------------------------------------------------------------------- */
178 /** \name Button (#uiBut) Spatial
179  * \{ */
180 
ui_but_pie_dir(RadialDirection dir,float vec[2])181 void ui_but_pie_dir(RadialDirection dir, float vec[2])
182 {
183   float angle;
184 
185   BLI_assert(dir != UI_RADIAL_NONE);
186 
187   angle = DEG2RADF((float)ui_radial_dir_to_angle[dir]);
188   vec[0] = cosf(angle);
189   vec[1] = sinf(angle);
190 }
191 
ui_but_isect_pie_seg(const uiBlock * block,const uiBut * but)192 static bool ui_but_isect_pie_seg(const uiBlock *block, const uiBut *but)
193 {
194   const float angle_range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_4 :
195                                                                                    M_PI_4 / 2.0;
196   float vec[2];
197 
198   if (block->pie_data.flags & UI_PIE_INVALID_DIR) {
199     return false;
200   }
201 
202   ui_but_pie_dir(but->pie_dir, vec);
203 
204   if (saacos(dot_v2v2(vec, block->pie_data.pie_dir)) < angle_range) {
205     return true;
206   }
207 
208   return false;
209 }
210 
ui_but_contains_pt(const uiBut * but,float mx,float my)211 bool ui_but_contains_pt(const uiBut *but, float mx, float my)
212 {
213   return BLI_rctf_isect_pt(&but->rect, mx, my);
214 }
215 
ui_but_contains_rect(const uiBut * but,const rctf * rect)216 bool ui_but_contains_rect(const uiBut *but, const rctf *rect)
217 {
218   return BLI_rctf_isect(&but->rect, rect, NULL);
219 }
220 
ui_but_contains_point_px(const uiBut * but,const ARegion * region,int x,int y)221 bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, int x, int y)
222 {
223   uiBlock *block = but->block;
224   if (!ui_region_contains_point_px(region, x, y)) {
225     return false;
226   }
227 
228   float mx = x, my = y;
229   ui_window_to_block_fl(region, block, &mx, &my);
230 
231   if (but->pie_dir != UI_RADIAL_NONE) {
232     if (!ui_but_isect_pie_seg(block, but)) {
233       return false;
234     }
235   }
236   else if (!ui_but_contains_pt(but, mx, my)) {
237     return false;
238   }
239 
240   return true;
241 }
242 
ui_but_contains_point_px_icon(const uiBut * but,ARegion * region,const wmEvent * event)243 bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEvent *event)
244 {
245   rcti rect;
246   int x = event->x, y = event->y;
247 
248   ui_window_to_block(region, but->block, &x, &y);
249 
250   BLI_rcti_rctf_copy(&rect, &but->rect);
251 
252   if (but->imb || but->type == UI_BTYPE_COLOR) {
253     /* use button size itself */
254   }
255   else if (but->drawflag & UI_BUT_ICON_LEFT) {
256     rect.xmax = rect.xmin + (BLI_rcti_size_y(&rect));
257   }
258   else {
259     const int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect);
260     rect.xmin += delta / 2;
261     rect.xmax -= delta / 2;
262   }
263 
264   return BLI_rcti_isect_pt(&rect, x, y);
265 }
266 
267 /* x and y are only used in case event is NULL... */
ui_but_find_mouse_over_ex(ARegion * region,const int x,const int y,const bool labeledit)268 uiBut *ui_but_find_mouse_over_ex(ARegion *region, const int x, const int y, const bool labeledit)
269 {
270   uiBut *butover = NULL;
271 
272   if (!ui_region_contains_point_px(region, x, y)) {
273     return NULL;
274   }
275   LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
276     float mx = x, my = y;
277     ui_window_to_block_fl(region, block, &mx, &my);
278 
279     LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
280       if (ui_but_is_interactive(but, labeledit)) {
281         if (but->pie_dir != UI_RADIAL_NONE) {
282           if (ui_but_isect_pie_seg(block, but)) {
283             butover = but;
284             break;
285           }
286         }
287         else if (ui_but_contains_pt(but, mx, my)) {
288           butover = but;
289           break;
290         }
291       }
292     }
293 
294     /* CLIP_EVENTS prevents the event from reaching other blocks */
295     if (block->flag & UI_BLOCK_CLIP_EVENTS) {
296       /* check if mouse is inside block */
297       if (BLI_rctf_isect_pt(&block->rect, mx, my)) {
298         break;
299       }
300     }
301   }
302 
303   return butover;
304 }
305 
ui_but_find_mouse_over(ARegion * region,const wmEvent * event)306 uiBut *ui_but_find_mouse_over(ARegion *region, const wmEvent *event)
307 {
308   return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0);
309 }
310 
ui_but_find_rect_over(const struct ARegion * region,const rcti * rect_px)311 uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
312 {
313   if (!ui_region_contains_rect_px(region, rect_px)) {
314     return NULL;
315   }
316 
317   /* Currently no need to expose this at the moment. */
318   const bool labeledit = true;
319   rctf rect_px_fl;
320   BLI_rctf_rcti_copy(&rect_px_fl, rect_px);
321   uiBut *butover = NULL;
322 
323   LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
324     rctf rect_block;
325     ui_window_to_block_rctf(region, block, &rect_block, &rect_px_fl);
326 
327     LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
328       if (ui_but_is_interactive(but, labeledit)) {
329         /* No pie menu support. */
330         BLI_assert(but->pie_dir == UI_RADIAL_NONE);
331         if (ui_but_contains_rect(but, &rect_block)) {
332           butover = but;
333           break;
334         }
335       }
336     }
337 
338     /* CLIP_EVENTS prevents the event from reaching other blocks */
339     if (block->flag & UI_BLOCK_CLIP_EVENTS) {
340       /* check if mouse is inside block */
341       if (BLI_rctf_isect(&block->rect, &rect_block, NULL)) {
342         break;
343       }
344     }
345   }
346   return butover;
347 }
348 
ui_list_find_mouse_over_ex(ARegion * region,int x,int y)349 uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
350 {
351   if (!ui_region_contains_point_px(region, x, y)) {
352     return NULL;
353   }
354   LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
355     float mx = x, my = y;
356     ui_window_to_block_fl(region, block, &mx, &my);
357     LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
358       if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) {
359         return but;
360       }
361     }
362   }
363 
364   return NULL;
365 }
366 
ui_list_find_mouse_over(ARegion * region,const wmEvent * event)367 uiBut *ui_list_find_mouse_over(ARegion *region, const wmEvent *event)
368 {
369   return ui_list_find_mouse_over_ex(region, event->x, event->y);
370 }
371 
372 /** \} */
373 
374 /* -------------------------------------------------------------------- */
375 /** \name Button (#uiBut) Relations
376  * \{ */
377 
ui_but_prev(uiBut * but)378 uiBut *ui_but_prev(uiBut *but)
379 {
380   while (but->prev) {
381     but = but->prev;
382     if (ui_but_is_editable(but)) {
383       return but;
384     }
385   }
386   return NULL;
387 }
388 
ui_but_next(uiBut * but)389 uiBut *ui_but_next(uiBut *but)
390 {
391   while (but->next) {
392     but = but->next;
393     if (ui_but_is_editable(but)) {
394       return but;
395     }
396   }
397   return NULL;
398 }
399 
ui_but_first(uiBlock * block)400 uiBut *ui_but_first(uiBlock *block)
401 {
402   LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
403     if (ui_but_is_editable(but)) {
404       return but;
405     }
406   }
407   return NULL;
408 }
409 
ui_but_last(uiBlock * block)410 uiBut *ui_but_last(uiBlock *block)
411 {
412   uiBut *but;
413 
414   but = block->buttons.last;
415   while (but) {
416     if (ui_but_is_editable(but)) {
417       return but;
418     }
419     but = but->prev;
420   }
421   return NULL;
422 }
423 
ui_but_is_cursor_warp(const uiBut * but)424 bool ui_but_is_cursor_warp(const uiBut *but)
425 {
426   if (U.uiflag & USER_CONTINUOUS_MOUSE) {
427     if (ELEM(but->type,
428              UI_BTYPE_NUM,
429              UI_BTYPE_NUM_SLIDER,
430              UI_BTYPE_TRACK_PREVIEW,
431              UI_BTYPE_HSVCUBE,
432              UI_BTYPE_HSVCIRCLE,
433              UI_BTYPE_CURVE,
434              UI_BTYPE_CURVEPROFILE)) {
435       return true;
436     }
437   }
438 
439   return false;
440 }
441 
ui_but_contains_password(const uiBut * but)442 bool ui_but_contains_password(const uiBut *but)
443 {
444   return but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD);
445 }
446 
447 /** \} */
448 
449 /* -------------------------------------------------------------------- */
450 /** \name Button (#uiBut) Text
451  * \{ */
452 
ui_but_drawstr_len_without_sep_char(const uiBut * but)453 size_t ui_but_drawstr_len_without_sep_char(const uiBut *but)
454 {
455   if (but->flag & UI_BUT_HAS_SEP_CHAR) {
456     const char *str_sep = strrchr(but->drawstr, UI_SEP_CHAR);
457     if (str_sep != NULL) {
458       return (str_sep - but->drawstr);
459     }
460   }
461   return strlen(but->drawstr);
462 }
463 
ui_but_tip_len_only_first_line(const uiBut * but)464 size_t ui_but_tip_len_only_first_line(const uiBut *but)
465 {
466   if (but->tip == NULL) {
467     return 0;
468   }
469 
470   const char *str_sep = strchr(but->tip, '\n');
471   if (str_sep != NULL) {
472     return (str_sep - but->tip);
473   }
474   return strlen(but->tip);
475 }
476 
477 /** \} */
478 
479 /* -------------------------------------------------------------------- */
480 /** \name Block (#uiBlock) State
481  * \{ */
482 
ui_block_is_menu(const uiBlock * block)483 bool ui_block_is_menu(const uiBlock *block)
484 {
485   return (((block->flag & UI_BLOCK_LOOP) != 0) &&
486           /* non-menu popups use keep-open, so check this is off */
487           ((block->flag & UI_BLOCK_KEEP_OPEN) == 0));
488 }
489 
ui_block_is_popover(const uiBlock * block)490 bool ui_block_is_popover(const uiBlock *block)
491 {
492   return (block->flag & UI_BLOCK_POPOVER) != 0;
493 }
494 
ui_block_is_pie_menu(const uiBlock * block)495 bool ui_block_is_pie_menu(const uiBlock *block)
496 {
497   return ((block->flag & UI_BLOCK_RADIAL) != 0);
498 }
499 
ui_block_is_popup_any(const uiBlock * block)500 bool ui_block_is_popup_any(const uiBlock *block)
501 {
502   return (ui_block_is_menu(block) || ui_block_is_popover(block) || ui_block_is_pie_menu(block));
503 }
504 
ui_but_next_non_separator(const uiBut * but)505 static const uiBut *ui_but_next_non_separator(const uiBut *but)
506 {
507   for (; but; but = but->next) {
508     if (!ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
509       return but;
510     }
511   }
512   return NULL;
513 }
514 
UI_block_is_empty_ex(const uiBlock * block,const bool skip_title)515 bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title)
516 {
517   const uiBut *but = block->buttons.first;
518   if (skip_title) {
519     /* Skip the first label, since popups often have a title,
520      * we may want to consider the block empty in this case. */
521     but = ui_but_next_non_separator(but);
522     if (but && but->type == UI_BTYPE_LABEL) {
523       but = but->next;
524     }
525   }
526   return (ui_but_next_non_separator(but) == NULL);
527 }
528 
UI_block_is_empty(const uiBlock * block)529 bool UI_block_is_empty(const uiBlock *block)
530 {
531   return UI_block_is_empty_ex(block, false);
532 }
533 
UI_block_can_add_separator(const uiBlock * block)534 bool UI_block_can_add_separator(const uiBlock *block)
535 {
536   if (ui_block_is_menu(block) && !ui_block_is_pie_menu(block)) {
537     const uiBut *but = block->buttons.last;
538     return (but && !ELEM(but->type, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR));
539   }
540   return true;
541 }
542 
543 /** \} */
544 
545 /* -------------------------------------------------------------------- */
546 /** \name Block (#uiBlock) Spatial
547  * \{ */
548 
ui_block_find_mouse_over_ex(const ARegion * region,const int x,const int y,bool only_clip)549 uiBlock *ui_block_find_mouse_over_ex(const ARegion *region,
550                                      const int x,
551                                      const int y,
552                                      bool only_clip)
553 {
554   if (!ui_region_contains_point_px(region, x, y)) {
555     return NULL;
556   }
557   LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
558     if (only_clip) {
559       if ((block->flag & UI_BLOCK_CLIP_EVENTS) == 0) {
560         continue;
561       }
562     }
563     float mx = x, my = y;
564     ui_window_to_block_fl(region, block, &mx, &my);
565     if (BLI_rctf_isect_pt(&block->rect, mx, my)) {
566       return block;
567     }
568   }
569   return NULL;
570 }
571 
ui_block_find_mouse_over(const ARegion * region,const wmEvent * event,bool only_clip)572 uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, bool only_clip)
573 {
574   return ui_block_find_mouse_over_ex(region, event->x, event->y, only_clip);
575 }
576 
577 /** \} */
578 
579 /* -------------------------------------------------------------------- */
580 /** \name Region (#ARegion) State
581  * \{ */
582 
ui_region_find_active_but(ARegion * region)583 uiBut *ui_region_find_active_but(ARegion *region)
584 {
585   LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
586     LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
587       if (but->active) {
588         return but;
589       }
590     }
591   }
592 
593   return NULL;
594 }
595 
ui_region_find_first_but_test_flag(ARegion * region,int flag_include,int flag_exclude)596 uiBut *ui_region_find_first_but_test_flag(ARegion *region, int flag_include, int flag_exclude)
597 {
598   LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
599     LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
600       if (((but->flag & flag_include) == flag_include) && ((but->flag & flag_exclude) == 0)) {
601         return but;
602       }
603     }
604   }
605 
606   return NULL;
607 }
608 
609 /** \} */
610 
611 /* -------------------------------------------------------------------- */
612 /** \name Region (#ARegion) Spatial
613  * \{ */
614 
ui_region_contains_point_px(const ARegion * region,int x,int y)615 bool ui_region_contains_point_px(const ARegion *region, int x, int y)
616 {
617   rcti winrct;
618   ui_region_winrct_get_no_margin(region, &winrct);
619   if (!BLI_rcti_isect_pt(&winrct, x, y)) {
620     return false;
621   }
622 
623   /* also, check that with view2d, that the mouse is not over the scroll-bars
624    * NOTE: care is needed here, since the mask rect may include the scroll-bars
625    * even when they are not visible, so we need to make a copy of the mask to
626    * use to check
627    */
628   if (region->v2d.mask.xmin != region->v2d.mask.xmax) {
629     const View2D *v2d = &region->v2d;
630     int mx = x, my = y;
631 
632     ui_window_to_region(region, &mx, &my);
633     if (!BLI_rcti_isect_pt(&v2d->mask, mx, my) ||
634         UI_view2d_mouse_in_scrollers(region, &region->v2d, x, y)) {
635       return false;
636     }
637   }
638 
639   return true;
640 }
641 
ui_region_contains_rect_px(const ARegion * region,const rcti * rect_px)642 bool ui_region_contains_rect_px(const ARegion *region, const rcti *rect_px)
643 {
644   rcti winrct;
645   ui_region_winrct_get_no_margin(region, &winrct);
646   if (!BLI_rcti_isect(&winrct, rect_px, NULL)) {
647     return false;
648   }
649 
650   /* See comment in 'ui_region_contains_point_px' */
651   if (region->v2d.mask.xmin != region->v2d.mask.xmax) {
652     const View2D *v2d = &region->v2d;
653     rcti rect_region;
654     ui_window_to_region_rcti(region, &rect_region, rect_px);
655     if (!BLI_rcti_isect(&v2d->mask, &rect_region, NULL) ||
656         UI_view2d_rect_in_scrollers(region, &region->v2d, rect_px)) {
657       return false;
658     }
659   }
660 
661   return true;
662 }
663 
664 /** \} */
665 
666 /* -------------------------------------------------------------------- */
667 /** \name Screen (#bScreen) Spatial
668  * \{ */
669 
670 /** Check if the cursor is over any popups. */
ui_screen_region_find_mouse_over_ex(bScreen * screen,int x,int y)671 ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y)
672 {
673   LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
674     rcti winrct;
675 
676     ui_region_winrct_get_no_margin(region, &winrct);
677 
678     if (BLI_rcti_isect_pt(&winrct, x, y)) {
679       return region;
680     }
681   }
682   return NULL;
683 }
684 
ui_screen_region_find_mouse_over(bScreen * screen,const wmEvent * event)685 ARegion *ui_screen_region_find_mouse_over(bScreen *screen, const wmEvent *event)
686 {
687   return ui_screen_region_find_mouse_over_ex(screen, event->x, event->y);
688 }
689 
690 /** \} */
691 
692 /* -------------------------------------------------------------------- */
693 /** \name Manage Internal State
694  * \{ */
695 
ui_interface_tag_script_reload_queries(void)696 void ui_interface_tag_script_reload_queries(void)
697 {
698   g_ot_tool_set_by_id = NULL;
699 }
700 
701 /** \} */
702