2018-04-22 17:16:39 +02:00
|
|
|
/*
|
|
|
|
* ***** 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);
|
|
|
|
|
2018-04-22 22:08:48 +02:00
|
|
|
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);
|
2018-04-22 17:16:39 +02:00
|
|
|
|
|
|
|
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);
|
2018-04-22 18:39:29 +02:00
|
|
|
pup->layout = UI_block_layout(
|
|
|
|
pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, U.widget_unit * 10, 0, MENU_PADDING, style);
|
2018-04-22 17:16:39 +02:00
|
|
|
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);
|