This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/interface/interface_region_popover.c

287 lines
7.5 KiB
C
Raw Normal View History

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* The Original Code is Copyright (C) 2008 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/interface/interface_region_popover.c
* \ingroup edinterface
*
* Pop-Over Region
*
* \note This is very close to 'interface_region_menu_popup.c'
*
* We could even merge them, however menu logic is already over-loaded.
* PopOver's have the following differences.
*
* - UI is not constrained to a list.
* - Pressing a button won't close the pop-over.
* - Different draw style (to show this is has different behavior from a menu).
* - #PanelType are used insetead of #MenuType.
* - No menu flipping support.
* - No moving the menu to fit the mouse cursor.
* - No key accelerators to access menu items
* (if we add support they would work differently).
* - No arrow key navigation.
* - No menu memory.
* - No title.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "MEM_guardedalloc.h"
#include "DNA_userdef_types.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BKE_report.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 Popup Menu with Callback or String
* \{ */
struct uiPopover {
uiBlock *block;
uiLayout *layout;
uiBut *but;
ARegion *butregion;
int mx, my;
bool popover, slideout;
uiMenuCreateFunc menu_func;
void *menu_arg;
};
static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
{
uiBlock *block;
uiPopover *pup = arg_pup;
int offset[2], minwidth, width, height;
if (pup->menu_func) {
pup->block->handle = handle;
pup->menu_func(C, pup->layout, pup->menu_arg);
pup->block->handle = NULL;
}
if (pup->but) {
/* minimum width to enforece */
minwidth = BLI_rctf_size_x(&pup->but->rect);
}
else {
minwidth = 50;
}
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 = UI_DIR_DOWN;
UI_block_layout_resolve(block, &width, &height);
UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER);
UI_block_direction_set(block, UI_DIR_DOWN | UI_DIR_CENTER_X);
if (pup->popover) {
UI_block_flag_enable(block, UI_BLOCK_LOOP);
UI_block_direction_set(block, block->direction);
block->minbounds = minwidth;
UI_block_bounds_set_popup(block, 1, offset[0], offset[1]);
}
else {
/* for a header menu we set the direction automatic */
block->minbounds = minwidth;
UI_block_bounds_set_normal(block, 1);
}
/* 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_popover_panel_create(
bContext *C, ARegion *butregion, uiBut *but,
uiMenuCreateFunc menu_func, void *arg)
{
wmWindow *window = CTX_wm_window(C);
uiStyle *style = UI_style_get_dpi();
uiPopupBlockHandle *handle;
uiPopover *pup;
pup = MEM_callocN(sizeof(uiPopover), __func__);
pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS);
UI_block_emboss_set(pup->block, UI_EMBOSS);
pup->layout = UI_block_layout(
pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, U.widget_unit * 10, 0, MENU_PADDING, style);
pup->slideout = false; // 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 popover */
pup->mx = window->eventstate->x;
pup->my = window->eventstate->y;
pup->popover = 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 (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_POPOVER, pup);
if (!but) {
handle->popup = true;
UI_popup_handlers_add(C, &window->modalhandlers, handle, 0);
WM_event_add_mousemove(C);
}
handle->can_refresh = false;
MEM_freeN(pup);
return handle;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Popup Menu API with begin & end
* \{ */
/**
* Only return handler, and set optional title.
* \param block_name: Assigned to uiBlock.name (useful info for debugging).
*/
uiPopover *UI_popover_begin_ex(bContext *C, const char *block_name)
{
uiStyle *style = UI_style_get_dpi();
uiPopover *pup = MEM_callocN(sizeof(uiPopover), "popover menu");
pup->block = UI_block_begin(C, NULL, block_name, UI_EMBOSS);
pup->layout = UI_block_layout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 0, MENU_PADDING, style);
/* Copied from menus, change if needed. */
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");
return pup;
}
uiPopover *UI_popover_begin(bContext *C)
{
return UI_popover_begin_ex(C, __func__);
}
/**
* Setting the button makes the popover open from the button instead of the cursor.
*/
#if 0
void UI_popover_panel_but_set(uiPopover *pup, struct ARegion *butregion, uiBut *but)
{
pup->but = but;
pup->butregion = butregion;
}
#endif
/* set the whole structure to work */
void UI_popover_end(bContext *C, uiPopover *pup)
{
wmWindow *window = CTX_wm_window(C);
uiPopupBlockHandle *menu;
uiBut *but = NULL;
ARegion *butregion = NULL;
pup->popover = true;
pup->mx = window->eventstate->x;
pup->my = window->eventstate->y;
if (pup->but) {
but = pup->but;
butregion = pup->butregion;
}
menu = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPOVER, pup);
menu->popup = true;
UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
WM_event_add_mousemove(C);
menu->can_refresh = false;
MEM_freeN(pup);
}
uiLayout *UI_popover_layout(uiPopover *pup)
{
return pup->layout;
}
/** \} */
/* We may want to support this in futurew */
/* Similar to UI_popup_menu_invoke */
// int UI_popover_panel_invoke(bContext *C, const char *idname, ReportList *reports);