Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-04-29 07:32:55 +0300
committerHans Goudey <h.goudey@me.com>2021-04-29 07:32:55 +0300
commit2038250c62fa3753fcc31129f8dd5f4fdf08e2e7 (patch)
tree013d5ca0f535ebae01f3b03731cc507520a414c7 /source/blender/editors/interface/interface_query.cc
parent38ebac86cc28e53d7640dcc8ce3bc4edbb8f2a1b (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.cc702
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, &region->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, &region->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, &region->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, &region->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, &region->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, &region->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 = &region->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, &region->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 = &region->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, &region->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;
+}
+
+/** \} */