diff options
author | Hans Goudey <h.goudey@me.com> | 2021-04-29 07:32:55 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-04-29 07:32:55 +0300 |
commit | 2038250c62fa3753fcc31129f8dd5f4fdf08e2e7 (patch) | |
tree | 013d5ca0f535ebae01f3b03731cc507520a414c7 /source/blender/editors/interface/interface_query.cc | |
parent | 38ebac86cc28e53d7640dcc8ce3bc4edbb8f2a1b (diff) |
WIP changes to compiling interface directory as C++ codetemp-interface-cpp
Diffstat (limited to 'source/blender/editors/interface/interface_query.cc')
-rw-r--r-- | source/blender/editors/interface/interface_query.cc | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc new file mode 100644 index 00000000000..f3aeae2464f --- /dev/null +++ b/source/blender/editors/interface/interface_query.cc @@ -0,0 +1,702 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + * + * Utilities to inspect the interface, extract information. + */ + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rect.h" +#include "BLI_utildefines.h" + +#include "DNA_screen_types.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "RNA_access.h" + +#include "interface_intern.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* -------------------------------------------------------------------- */ +/** \name Button (#uiBut) State + * \{ */ + +bool ui_but_is_editable(const uiBut *but) +{ + return !ELEM(but->type, + UI_BTYPE_LABEL, + UI_BTYPE_SEPR, + UI_BTYPE_SEPR_LINE, + UI_BTYPE_ROUNDBOX, + UI_BTYPE_LISTBOX, + UI_BTYPE_PROGRESS_BAR); +} + +bool ui_but_is_editable_as_text(const uiBut *but) +{ + return ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_SEARCH_MENU); +} + +bool ui_but_is_toggle(const uiBut *but) +{ + return ELEM(but->type, + UI_BTYPE_BUT_TOGGLE, + UI_BTYPE_TOGGLE, + UI_BTYPE_ICON_TOGGLE, + UI_BTYPE_ICON_TOGGLE_N, + UI_BTYPE_TOGGLE_N, + UI_BTYPE_CHECKBOX, + UI_BTYPE_CHECKBOX_N, + UI_BTYPE_ROW); +} + +/** + * Can we mouse over the button or is it hidden/disabled/layout. + * \note ctrl is kind of a hack currently, + * so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed. + */ +bool ui_but_is_interactive(const uiBut *but, const bool labeledit) +{ + /* note, UI_BTYPE_LABEL is included for highlights, this allows drags */ + if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == NULL) { + return false; + } + if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) { + return false; + } + if (but->flag & UI_HIDDEN) { + return false; + } + if (but->flag & UI_SCROLLED) { + return false; + } + if ((but->type == UI_BTYPE_TEXT) && (but->emboss == UI_EMBOSS_NONE) && !labeledit) { + return false; + } + if ((but->type == UI_BTYPE_LISTROW) && labeledit) { + return false; + } + + return true; +} + +/* file selectors are exempt from utf-8 checks */ +bool UI_but_is_utf8(const uiBut *but) +{ + if (but->rnaprop) { + const int subtype = RNA_property_subtype(but->rnaprop); + return !(ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING)); + } + return !(but->flag & UI_BUT_NO_UTF8); +} + +#ifdef USE_UI_POPOVER_ONCE +bool ui_but_is_popover_once_compat(const uiBut *but) +{ + return (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR) || ui_but_is_toggle(but)); +} +#endif + +bool ui_but_has_array_value(const uiBut *but) +{ + return (but->rnapoin.data && but->rnaprop && + ELEM(RNA_property_subtype(but->rnaprop), + PROP_COLOR, + PROP_TRANSLATION, + PROP_DIRECTION, + PROP_VELOCITY, + PROP_ACCELERATION, + PROP_MATRIX, + PROP_EULER, + PROP_QUATERNION, + PROP_AXISANGLE, + PROP_XYZ, + PROP_XYZ_LENGTH, + PROP_COLOR_GAMMA, + PROP_COORDS)); +} + +static wmOperatorType *g_ot_tool_set_by_id = NULL; +bool UI_but_is_tool(const uiBut *but) +{ + /* very evil! */ + if (but->optype != NULL) { + if (g_ot_tool_set_by_id == NULL) { + g_ot_tool_set_by_id = WM_operatortype_find("WM_OT_tool_set_by_id", false); + } + if (but->optype == g_ot_tool_set_by_id) { + return true; + } + } + return false; +} + +bool UI_but_has_tooltip_label(const uiBut *but) +{ + if ((but->drawstr[0] == '\0') && !ui_block_is_popover(but->block)) { + return UI_but_is_tool(but); + } + return false; +} + +int ui_but_icon(const uiBut *but) +{ + if (!(but->flag & UI_HAS_ICON)) { + return ICON_NONE; + } + + /* Consecutive icons can be toggle between. */ + if (but->drawflag & UI_BUT_ICON_REVERSE) { + return but->icon - but->iconadd; + } + return but->icon + but->iconadd; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Button (#uiBut) Spatial + * \{ */ + +void ui_but_pie_dir(RadialDirection dir, float vec[2]) +{ + float angle; + + BLI_assert(dir != UI_RADIAL_NONE); + + angle = DEG2RADF((float)ui_radial_dir_to_angle[dir]); + vec[0] = cosf(angle); + vec[1] = sinf(angle); +} + +static bool ui_but_isect_pie_seg(const uiBlock *block, const uiBut *but) +{ + const float angle_range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_4 : + M_PI_4 / 2.0; + float vec[2]; + + if (block->pie_data.flags & UI_PIE_INVALID_DIR) { + return false; + } + + ui_but_pie_dir((RadialDirection)but->pie_dir, vec); + + if (saacos(dot_v2v2(vec, block->pie_data.pie_dir)) < angle_range) { + return true; + } + + return false; +} + +bool ui_but_contains_pt(const uiBut *but, float mx, float my) +{ + return BLI_rctf_isect_pt(&but->rect, mx, my); +} + +bool ui_but_contains_rect(const uiBut *but, const rctf *rect) +{ + return BLI_rctf_isect(&but->rect, rect, NULL); +} + +bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, int x, int y) +{ + uiBlock *block = but->block; + if (!ui_region_contains_point_px(region, x, y)) { + return false; + } + + float mx = x, my = y; + ui_window_to_block_fl(region, block, &mx, &my); + + if (but->pie_dir != UI_RADIAL_NONE) { + if (!ui_but_isect_pie_seg(block, but)) { + return false; + } + } + else if (!ui_but_contains_pt(but, mx, my)) { + return false; + } + + return true; +} + +bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEvent *event) +{ + rcti rect; + int x = event->x, y = event->y; + + ui_window_to_block(region, but->block, &x, &y); + + BLI_rcti_rctf_copy(&rect, &but->rect); + + if (but->imb || but->type == UI_BTYPE_COLOR) { + /* use button size itself */ + } + else if (but->drawflag & UI_BUT_ICON_LEFT) { + rect.xmax = rect.xmin + (BLI_rcti_size_y(&rect)); + } + else { + const int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect); + rect.xmin += delta / 2; + rect.xmax -= delta / 2; + } + + return BLI_rcti_isect_pt(&rect, x, y); +} + +/* x and y are only used in case event is NULL... */ +uiBut *ui_but_find_mouse_over_ex(const ARegion *region, + const int x, + const int y, + const bool labeledit) +{ + uiBut *butover = NULL; + + if (!ui_region_contains_point_px(region, x, y)) { + return NULL; + } + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + float mx = x, my = y; + ui_window_to_block_fl(region, block, &mx, &my); + + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (ui_but_is_interactive(but, labeledit)) { + if (but->pie_dir != UI_RADIAL_NONE) { + if (ui_but_isect_pie_seg(block, but)) { + butover = but; + break; + } + } + else if (ui_but_contains_pt(but, mx, my)) { + butover = but; + break; + } + } + } + + /* CLIP_EVENTS prevents the event from reaching other blocks */ + if (block->flag & UI_BLOCK_CLIP_EVENTS) { + /* check if mouse is inside block */ + if (BLI_rctf_isect_pt(&block->rect, mx, my)) { + break; + } + } + } + + return butover; +} + +uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) +{ + return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0); +} + +uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) +{ + if (!ui_region_contains_rect_px(region, rect_px)) { + return NULL; + } + + /* Currently no need to expose this at the moment. */ + const bool labeledit = true; + rctf rect_px_fl; + BLI_rctf_rcti_copy(&rect_px_fl, rect_px); + uiBut *butover = NULL; + + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + rctf rect_block; + ui_window_to_block_rctf(region, block, &rect_block, &rect_px_fl); + + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (ui_but_is_interactive(but, labeledit)) { + /* No pie menu support. */ + BLI_assert(but->pie_dir == UI_RADIAL_NONE); + if (ui_but_contains_rect(but, &rect_block)) { + butover = but; + break; + } + } + } + + /* CLIP_EVENTS prevents the event from reaching other blocks */ + if (block->flag & UI_BLOCK_CLIP_EVENTS) { + /* check if mouse is inside block */ + if (BLI_rctf_isect(&block->rect, &rect_block, NULL)) { + break; + } + } + } + return butover; +} + +uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y) +{ + if (!ui_region_contains_point_px(region, x, y)) { + return NULL; + } + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + float mx = x, my = y; + ui_window_to_block_fl(region, block, &mx, &my); + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) { + return but; + } + } + } + + return NULL; +} + +uiBut *ui_list_find_mouse_over(ARegion *region, const wmEvent *event) +{ + return ui_list_find_mouse_over_ex(region, event->x, event->y); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Button (#uiBut) Relations + * \{ */ + +uiBut *ui_but_prev(uiBut *but) +{ + while (but->prev) { + but = but->prev; + if (ui_but_is_editable(but)) { + return but; + } + } + return NULL; +} + +uiBut *ui_but_next(uiBut *but) +{ + while (but->next) { + but = but->next; + if (ui_but_is_editable(but)) { + return but; + } + } + return NULL; +} + +uiBut *ui_but_first(uiBlock *block) +{ + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (ui_but_is_editable(but)) { + return but; + } + } + return NULL; +} + +uiBut *ui_but_last(uiBlock *block) +{ + uiBut *but = (uiBut *)block->buttons.last; + while (but) { + if (ui_but_is_editable(but)) { + return but; + } + but = but->prev; + } + return NULL; +} + +bool ui_but_is_cursor_warp(const uiBut *but) +{ + if (U.uiflag & USER_CONTINUOUS_MOUSE) { + if (ELEM(but->type, + UI_BTYPE_NUM, + UI_BTYPE_NUM_SLIDER, + UI_BTYPE_TRACK_PREVIEW, + UI_BTYPE_HSVCUBE, + UI_BTYPE_HSVCIRCLE, + UI_BTYPE_CURVE, + UI_BTYPE_CURVEPROFILE)) { + return true; + } + } + + return false; +} + +bool ui_but_contains_password(const uiBut *but) +{ + return but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Button (#uiBut) Text + * \{ */ + +size_t ui_but_drawstr_len_without_sep_char(const uiBut *but) +{ + if (but->flag & UI_BUT_HAS_SEP_CHAR) { + const char *str_sep = strrchr(but->drawstr, UI_SEP_CHAR); + if (str_sep != NULL) { + return (str_sep - but->drawstr); + } + } + return strlen(but->drawstr); +} + +size_t ui_but_tip_len_only_first_line(const uiBut *but) +{ + if (but->tip == NULL) { + return 0; + } + + const char *str_sep = strchr(but->tip, '\n'); + if (str_sep != NULL) { + return (str_sep - but->tip); + } + return strlen(but->tip); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Block (#uiBlock) State + * \{ */ + +bool ui_block_is_menu(const uiBlock *block) +{ + return (((block->flag & UI_BLOCK_LOOP) != 0) && + /* non-menu popups use keep-open, so check this is off */ + ((block->flag & UI_BLOCK_KEEP_OPEN) == 0)); +} + +bool ui_block_is_popover(const uiBlock *block) +{ + return (block->flag & UI_BLOCK_POPOVER) != 0; +} + +bool ui_block_is_pie_menu(const uiBlock *block) +{ + return ((block->flag & UI_BLOCK_RADIAL) != 0); +} + +bool ui_block_is_popup_any(const uiBlock *block) +{ + return (ui_block_is_menu(block) || ui_block_is_popover(block) || ui_block_is_pie_menu(block)); +} + +static const uiBut *ui_but_next_non_separator(const uiBut *but) +{ + for (; but; but = but->next) { + if (!ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) { + return but; + } + } + return NULL; +} + +bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title) +{ + const uiBut *but = (const uiBut *)block->buttons.first; + if (skip_title) { + /* Skip the first label, since popups often have a title, + * we may want to consider the block empty in this case. */ + but = ui_but_next_non_separator(but); + if (but && but->type == UI_BTYPE_LABEL) { + but = but->next; + } + } + return (ui_but_next_non_separator(but) == NULL); +} + +bool UI_block_is_empty(const uiBlock *block) +{ + return UI_block_is_empty_ex(block, false); +} + +bool UI_block_can_add_separator(const uiBlock *block) +{ + if (ui_block_is_menu(block) && !ui_block_is_pie_menu(block)) { + const uiBut *but = (const uiBut *)block->buttons.last; + return (but && !ELEM(but->type, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR)); + } + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Block (#uiBlock) Spatial + * \{ */ + +uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, + const int x, + const int y, + bool only_clip) +{ + if (!ui_region_contains_point_px(region, x, y)) { + return NULL; + } + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + if (only_clip) { + if ((block->flag & UI_BLOCK_CLIP_EVENTS) == 0) { + continue; + } + } + float mx = x, my = y; + ui_window_to_block_fl(region, block, &mx, &my); + if (BLI_rctf_isect_pt(&block->rect, mx, my)) { + return block; + } + } + return NULL; +} + +uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, bool only_clip) +{ + return ui_block_find_mouse_over_ex(region, event->x, event->y, only_clip); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Region (#ARegion) State + * \{ */ + +uiBut *ui_region_find_active_but(ARegion *region) +{ + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active) { + return but; + } + } + } + + return NULL; +} + +uiBut *ui_region_find_first_but_test_flag(ARegion *region, int flag_include, int flag_exclude) +{ + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (((but->flag & flag_include) == flag_include) && ((but->flag & flag_exclude) == 0)) { + return but; + } + } + } + + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Region (#ARegion) Spatial + * \{ */ + +bool ui_region_contains_point_px(const ARegion *region, int x, int y) +{ + rcti winrct; + ui_region_winrct_get_no_margin(region, &winrct); + if (!BLI_rcti_isect_pt(&winrct, x, y)) { + return false; + } + + /* also, check that with view2d, that the mouse is not over the scroll-bars + * NOTE: care is needed here, since the mask rect may include the scroll-bars + * even when they are not visible, so we need to make a copy of the mask to + * use to check + */ + if (region->v2d.mask.xmin != region->v2d.mask.xmax) { + const View2D *v2d = ®ion->v2d; + int mx = x, my = y; + + ui_window_to_region(region, &mx, &my); + if (!BLI_rcti_isect_pt(&v2d->mask, mx, my) || + UI_view2d_mouse_in_scrollers(region, ®ion->v2d, x, y)) { + return false; + } + } + + return true; +} + +bool ui_region_contains_rect_px(const ARegion *region, const rcti *rect_px) +{ + rcti winrct; + ui_region_winrct_get_no_margin(region, &winrct); + if (!BLI_rcti_isect(&winrct, rect_px, NULL)) { + return false; + } + + /* See comment in 'ui_region_contains_point_px' */ + if (region->v2d.mask.xmin != region->v2d.mask.xmax) { + const View2D *v2d = ®ion->v2d; + rcti rect_region; + ui_window_to_region_rcti(region, &rect_region, rect_px); + if (!BLI_rcti_isect(&v2d->mask, &rect_region, NULL) || + UI_view2d_rect_in_scrollers(region, ®ion->v2d, rect_px)) { + return false; + } + } + + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Screen (#bScreen) Spatial + * \{ */ + +/** Check if the cursor is over any popups. */ +ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y) +{ + LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { + rcti winrct; + + ui_region_winrct_get_no_margin(region, &winrct); + + if (BLI_rcti_isect_pt(&winrct, x, y)) { + return region; + } + } + return NULL; +} + +ARegion *ui_screen_region_find_mouse_over(bScreen *screen, const wmEvent *event) +{ + return ui_screen_region_find_mouse_over_ex(screen, event->x, event->y); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Manage Internal State + * \{ */ + +void ui_interface_tag_script_reload_queries(void) +{ + g_ot_tool_set_by_id = NULL; +} + +/** \} */ |