diff options
Diffstat (limited to 'source/blender/editors/interface/interface_region_menu_popup.c')
-rw-r--r-- | source/blender/editors/interface/interface_region_menu_popup.c | 670 |
1 files changed, 0 insertions, 670 deletions
diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c deleted file mode 100644 index 7d8f4315710..00000000000 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ /dev/null @@ -1,670 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2008 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edinterface - * - * PopUp Menu Region - */ - -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> - -#include "MEM_guardedalloc.h" - -#include "DNA_userdef_types.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - -#include "BLI_ghash.h" -#include "BLI_rect.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "BKE_context.h" -#include "BKE_report.h" -#include "BKE_screen.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_access.h" - -#include "UI_interface.h" - -#include "BLT_translation.h" - -#include "ED_screen.h" - -#include "interface_intern.h" -#include "interface_regions_intern.h" - -/* -------------------------------------------------------------------- */ -/** \name Utility Functions - * \{ */ - -bool ui_but_menu_step_poll(const uiBut *but) -{ - BLI_assert(but->type == UI_BTYPE_MENU); - - /* currently only RNA buttons */ - return ((but->menu_step_func != NULL) || - (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM)); -} - -int ui_but_menu_step(uiBut *but, int direction) -{ - if (ui_but_menu_step_poll(but)) { - if (but->menu_step_func) { - return but->menu_step_func(but->block->evil_C, direction, but->poin); - } - - const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop); - return RNA_property_enum_step( - but->block->evil_C, &but->rnapoin, but->rnaprop, curval, direction); - } - - printf("%s: cannot cycle button '%s'\n", __func__, but->str); - return 0; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Popup Menu Memory - * - * Support menu-memory, a feature that positions the cursor - * over the previously used menu item. - * - * \note This is stored for each unique menu title. - * \{ */ - -static uint ui_popup_string_hash(const char *str, const bool use_sep) -{ - /* sometimes button contains hotkey, sometimes not, strip for proper compare */ - int hash; - const char *delimit = use_sep ? strrchr(str, UI_SEP_CHAR) : NULL; - - if (delimit) { - hash = BLI_ghashutil_strhash_n(str, delimit - str); - } - else { - hash = BLI_ghashutil_strhash(str); - } - - return hash; -} - -uint ui_popup_menu_hash(const char *str) -{ - return BLI_ghashutil_strhash(str); -} - -/* but == NULL read, otherwise set */ -static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) -{ - static uint mem[256]; - static bool first = true; - - const uint hash = block->puphash; - const uint hash_mod = hash & 255; - - if (first) { - /* init */ - memset(mem, -1, sizeof(mem)); - first = 0; - } - - if (but) { - /* set */ - mem[hash_mod] = ui_popup_string_hash(but->str, but->flag & UI_BUT_HAS_SEP_CHAR); - return NULL; - } - - /* get */ - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - if (mem[hash_mod] == - ui_popup_string_hash(but_iter->str, but_iter->flag & UI_BUT_HAS_SEP_CHAR)) { - return but_iter; - } - } - - return NULL; -} - -uiBut *ui_popup_menu_memory_get(uiBlock *block) -{ - return ui_popup_menu_memory__internal(block, NULL); -} - -void ui_popup_menu_memory_set(uiBlock *block, uiBut *but) -{ - ui_popup_menu_memory__internal(block, but); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Popup Menu with Callback or String - * \{ */ - -struct uiPopupMenu { - uiBlock *block; - uiLayout *layout; - uiBut *but; - ARegion *butregion; - - int mx, my; - bool popup, slideout; - - uiMenuCreateFunc menu_func; - void *menu_arg; -}; - -static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup) -{ - uiBlock *block; - uiPopupMenu *pup = arg_pup; - int minwidth, width, height; - char direction; - bool flip; - - if (pup->menu_func) { - pup->block->handle = handle; - pup->menu_func(C, pup->layout, pup->menu_arg); - pup->block->handle = NULL; - } - - /* Find block minimum width. */ - if (uiLayoutGetUnitsX(pup->layout) != 0.0f) { - /* Use the minimum width from the layout if it's set. */ - minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X; - } - else if (pup->but) { - /* Minimum width to enforce. */ - if (pup->but->drawstr[0]) { - minwidth = BLI_rctf_size_x(&pup->but->rect); - } - else { - /* For buttons with no text, use the minimum (typically icon only). */ - minwidth = UI_MENU_WIDTH_MIN; - } - } - else { - minwidth = UI_MENU_WIDTH_MIN; - } - - /* Find block direction. */ - if (pup->but) { - if (pup->block->direction != 0) { - /* allow overriding the direction from menu_func */ - direction = pup->block->direction; - } - else { - direction = UI_DIR_DOWN; - } - } - else { - direction = UI_DIR_DOWN; - } - - flip = (direction == UI_DIR_DOWN); - - block = pup->block; - - /* in some cases we create the block before the region, - * so we set it delayed here if necessary */ - if (BLI_findindex(&handle->region->uiblocks, block) == -1) { - UI_block_region_set(block, handle->region); - } - - block->direction = direction; - - UI_block_layout_resolve(block, &width, &height); - - UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT); - - if (pup->popup) { - int offset[2]; - - uiBut *but_activate = NULL; - UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_NUMSELECT); - UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); - UI_block_direction_set(block, direction); - - /* offset the mouse position, possibly based on earlier selection */ - uiBut *bt; - if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) { - /* position mouse on last clicked item, at 0.8*width of the - * button, so it doesn't overlap the text too much, also note - * the offset is negative because we are inverse moving the - * block to be under the mouse */ - offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)); - offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y); - - if (ui_but_is_editable(bt)) { - but_activate = bt; - } - } - else { - /* position mouse at 0.8*width of the button and below the tile - * on the first item */ - offset[0] = 0; - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - offset[0] = min_ii(offset[0], - -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect))); - } - - offset[1] = 2.1 * UI_UNIT_Y; - - LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) { - if (ui_but_is_editable(but_iter)) { - but_activate = but_iter; - break; - } - } - } - - /* in rare cases this is needed since moving the popup - * to be within the window bounds may move it away from the mouse, - * This ensures we set an item to be active. */ - if (but_activate) { - ui_but_activate_over(C, handle->region, but_activate); - } - - block->minbounds = minwidth; - UI_block_bounds_set_menu(block, 1, offset); - } - else { - /* for a header menu we set the direction automatic */ - if (!pup->slideout && flip) { - ARegion *region = CTX_wm_region(C); - if (region) { - if (RGN_TYPE_IS_HEADER_ANY(region->regiontype)) { - if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) == RGN_ALIGN_BOTTOM) { - UI_block_direction_set(block, UI_DIR_UP); - UI_block_order_flip(block); - } - } - } - } - - block->minbounds = minwidth; - UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X); - } - - /* if menu slides out of other menu, override direction */ - if (pup->slideout) { - UI_block_direction_set(block, UI_DIR_RIGHT); - } - - return pup->block; -} - -uiPopupBlockHandle *ui_popup_menu_create( - bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg) -{ - wmWindow *window = CTX_wm_window(C); - const uiStyle *style = UI_style_get_dpi(); - uiPopupBlockHandle *handle; - uiPopupMenu *pup; - - pup = MEM_callocN(sizeof(uiPopupMenu), __func__); - pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS_PULLDOWN); - pup->block->flag |= UI_BLOCK_NUMSELECT; /* default menus to numselect */ - pup->layout = UI_block_layout( - pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); - pup->slideout = but ? ui_block_is_menu(but->block) : false; - pup->but = but; - uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN); - - if (!but) { - /* no button to start from, means we are a popup */ - pup->mx = window->eventstate->xy[0]; - pup->my = window->eventstate->xy[1]; - pup->popup = true; - pup->block->flag |= UI_BLOCK_NO_FLIP; - } - /* some enums reversing is strange, currently we have no good way to - * reverse some enum's but not others, so reverse all so the first menu - * items are always close to the mouse cursor */ - else { -#if 0 - /* if this is an rna button then we can assume its an enum - * flipping enums is generally not good since the order can be - * important T28786. */ - if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) { - pup->block->flag |= UI_BLOCK_NO_FLIP; - } -#endif - if (but->context) { - uiLayoutContextCopy(pup->layout, but->context); - } - } - - /* menu is created from a callback */ - pup->menu_func = menu_func; - pup->menu_arg = arg; - - handle = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup, NULL); - - if (!but) { - handle->popup = true; - - UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); - WM_event_add_mousemove(window); - } - - MEM_freeN(pup); - - return handle; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Popup Menu API with begin & end - * \{ */ - -uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, - const char *title, - const char *block_name, - int icon) -{ - const uiStyle *style = UI_style_get_dpi(); - uiPopupMenu *pup = MEM_callocN(sizeof(uiPopupMenu), "popup menu"); - uiBut *but; - - pup->block = UI_block_begin(C, NULL, block_name, UI_EMBOSS_PULLDOWN); - pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP; - pup->block->puphash = ui_popup_menu_hash(title); - pup->layout = UI_block_layout( - pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); - - /* NOTE: this intentionally differs from the menu & sub-menu default because many operators - * use popups like this to select one of their options - - * where having invoke doesn't make sense */ - uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN); - - /* create in advance so we can let buttons point to retval already */ - pup->block->handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); - - /* create title button */ - if (title[0]) { - char titlestr[256]; - - if (icon) { - BLI_snprintf(titlestr, sizeof(titlestr), " %s", title); - uiDefIconTextBut(pup->block, - UI_BTYPE_LABEL, - 0, - icon, - titlestr, - 0, - 0, - 200, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0, - 0, - ""); - } - else { - but = uiDefBut( - pup->block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); - but->drawflag = UI_BUT_TEXT_LEFT; - } - - uiItemS(pup->layout); - } - - return pup; -} - -uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) -{ - return UI_popup_menu_begin_ex(C, title, __func__, icon); -} - -void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but) -{ - pup->but = but; - pup->butregion = butregion; -} - -void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) -{ - wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *menu; - uiBut *but = NULL; - ARegion *butregion = NULL; - - pup->popup = true; - pup->mx = window->eventstate->xy[0]; - pup->my = window->eventstate->xy[1]; - - if (pup->but) { - but = pup->but; - butregion = pup->butregion; - } - - menu = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup, NULL); - menu->popup = true; - - UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); - WM_event_add_mousemove(window); - - MEM_freeN(pup); -} - -bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup) -{ - if (!UI_block_is_empty_ex(pup->block, true)) { - UI_popup_menu_end(C, pup); - return true; - } - UI_block_layout_resolve(pup->block, NULL, NULL); - MEM_freeN(pup->block->handle); - UI_block_free(C, pup->block); - MEM_freeN(pup); - return false; -} - -uiLayout *UI_popup_menu_layout(uiPopupMenu *pup) -{ - return pup->layout; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Standard Popup Menus - * \{ */ - -void UI_popup_menu_reports(bContext *C, ReportList *reports) -{ - uiPopupMenu *pup = NULL; - uiLayout *layout; - - if (!CTX_wm_window(C)) { - return; - } - - LISTBASE_FOREACH (Report *, report, &reports->list) { - int icon; - const char *msg, *msg_next; - - if (report->type < reports->printlevel) { - continue; - } - - if (pup == NULL) { - char title[UI_MAX_DRAW_STR]; - BLI_snprintf(title, sizeof(title), "%s: %s", IFACE_("Report"), report->typestr); - /* popup_menu stuff does just what we need (but pass meaningful block name) */ - pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE); - layout = UI_popup_menu_layout(pup); - } - else { - uiItemS(layout); - } - - /* split each newline into a label */ - msg = report->message; - icon = UI_icon_from_report_type(report->type); - do { - char buf[UI_MAX_DRAW_STR]; - msg_next = strchr(msg, '\n'); - if (msg_next) { - msg_next++; - BLI_strncpy(buf, msg, MIN2(sizeof(buf), msg_next - msg)); - msg = buf; - } - uiItemL(layout, msg, icon); - icon = ICON_NONE; - } while ((msg = msg_next) && *msg); - } - - if (pup) { - UI_popup_menu_end(C, pup); - } -} - -int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) -{ - uiPopupMenu *pup; - uiLayout *layout; - MenuType *mt = WM_menutype_find(idname, true); - - if (mt == NULL) { - BKE_reportf(reports, RPT_ERROR, "Menu \"%s\" not found", idname); - return OPERATOR_CANCELLED; - } - - if (WM_menutype_poll(C, mt) == false) { - /* cancel but allow event to pass through, just like operators do */ - return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); - } - - pup = UI_popup_menu_begin(C, IFACE_(mt->label), ICON_NONE); - layout = UI_popup_menu_layout(pup); - - UI_menutype_draw(C, mt, layout); - - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Popup Block API - * \{ */ - -void UI_popup_block_invoke_ex( - bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, bool can_refresh) -{ - wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *handle; - - handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg, arg_free); - handle->popup = true; - - /* It can be useful to disable refresh (even though it will work) - * as this exists text fields which can be disruptive if refresh isn't needed. */ - handle->can_refresh = can_refresh; - - UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); - UI_block_active_only_flagged_buttons(C, handle->region, handle->region->uiblocks.first); - WM_event_add_mousemove(window); -} - -void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free) -{ - UI_popup_block_invoke_ex(C, func, arg, arg_free, true); -} - -void UI_popup_block_ex(bContext *C, - uiBlockCreateFunc func, - uiBlockHandleFunc popup_func, - uiBlockCancelFunc cancel_func, - void *arg, - wmOperator *op) -{ - wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *handle; - - handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg, NULL); - handle->popup = true; - handle->retvalue = 1; - handle->can_refresh = true; - - handle->popup_op = op; - handle->popup_arg = arg; - handle->popup_func = popup_func; - handle->cancel_func = cancel_func; - // handle->opcontext = opcontext; - - UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); - UI_block_active_only_flagged_buttons(C, handle->region, handle->region->uiblocks.first); - WM_event_add_mousemove(window); -} - -#if 0 /* UNUSED */ -void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, wmOperatorCallContext opcontext) -{ - wmWindow *window = CTX_wm_window(C); - uiPopupBlockHandle *handle; - - handle = ui_popup_block_create(C, NULL, NULL, func, NULL, op, NULL); - handle->popup = 1; - handle->retvalue = 1; - handle->can_refresh = true; - - handle->popup_arg = op; - handle->popup_func = operator_cb; - handle->cancel_func = confirm_cancel_operator; - handle->opcontext = opcontext; - - UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); - WM_event_add_mousemove(C); -} -#endif - -void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block) -{ - /* if loading new .blend while popup is open, window will be NULL */ - if (block->handle) { - if (win) { - const bScreen *screen = WM_window_get_active_screen(win); - - UI_popup_handlers_remove(&win->modalhandlers, block->handle); - ui_popup_block_free(C, block->handle); - - /* In the case we have nested popups, - * closing one may need to redraw another, see: T48874 */ - LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { - ED_region_tag_refresh_ui(region); - } - } - } -} - -bool UI_popup_block_name_exists(const bScreen *screen, const char *name) -{ - LISTBASE_FOREACH (const ARegion *, region, &screen->regionbase) { - LISTBASE_FOREACH (const uiBlock *, block, ®ion->uiblocks) { - if (STREQ(block->name, name)) { - return true; - } - } - } - return false; -} - -/** \} */ |