From 721dbfccfdb7e04114688a44cebcb2421ab61273 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sun, 7 Jul 2024 20:00:35 +0200 Subject: [PATCH] UI: Turn asset shelf popup into a popover, add operator to call from shortcut Turns the asset shelf into a popover which reduces some of the special handling. An operator `WM_OT_call_asset_shelf_popover()` (similar to `WM_OT_call_panel()`) is added to be able to call the popover from shortcuts. Exactly this was an important aspect for the brush assets project, to allow quick searching for brushes from the popup. A custom shortcut can be added to asset shelf popovers using "Assign Shortcut" in the context menu of buttons invoking it. The popover is spawned with the mouse hovering the first asset and the search button active using "semi modal" handling. That means while the popover is open, any text input is captured by the search button, while the rest of the popover stays interactive. So for example navigating through asset catalogs is possible, a single click activates an asset and closes the popover. Reviewed as part of the asset shelf project, see: - https://projects.blender.org/blender/blender/issues/116337 - https://projects.blender.org/blender/blender/pulls/106303 --- source/blender/blenkernel/BKE_screen.hh | 4 + source/blender/editors/asset/CMakeLists.txt | 2 +- .../blender/editors/asset/ED_asset_shelf.hh | 3 +- .../editors/asset/intern/asset_shelf.cc | 8 +- .../editors/asset/intern/asset_shelf.hh | 1 + .../asset/intern/asset_shelf_asset_view.cc | 13 +- ..._shelf_popup.cc => asset_shelf_popover.cc} | 114 +++++++++++++----- .../blender/editors/include/UI_interface.hh | 3 + .../blender/editors/include/UI_interface_c.hh | 11 +- .../interface/interface_context_menu.cc | 7 ++ .../editors/interface/interface_intern.hh | 7 +- .../editors/interface/interface_layout.cc | 12 ++ .../regions/interface_region_popover.cc | 34 ++++-- .../interface_template_asset_shelf_popover.cc | 78 ++++++++---- .../editors/space_image/space_image.cc | 2 +- .../editors/space_view3d/space_view3d.cc | 2 +- source/blender/makesrna/intern/rna_ui_api.cc | 2 +- .../windowmanager/intern/wm_operators.cc | 34 ++++++ 18 files changed, 258 insertions(+), 79 deletions(-) rename source/blender/editors/asset/intern/{asset_shelf_popup.cc => asset_shelf_popover.cc} (63%) diff --git a/source/blender/blenkernel/BKE_screen.hh b/source/blender/blenkernel/BKE_screen.hh index 2dc4226e18c..b05a4479872 100644 --- a/source/blender/blenkernel/BKE_screen.hh +++ b/source/blender/blenkernel/BKE_screen.hh @@ -11,6 +11,7 @@ #include #include "BLI_compiler_attrs.h" +#include "BLI_math_vector_types.hh" #include "BLI_vector.hh" #include "RNA_types.hh" @@ -295,6 +296,9 @@ struct PanelType { short region_type; /* For popovers, 0 for default. */ int ui_units_x; + /** For popovers, position the popover at the given offset (multiplied by #UI_UNIT_X/#UI_UNIT_Y) + * relative to the top left corner, if it's not attached to a button. */ + blender::float2 offset_units_xy; int order; int flag; diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 3e387636b46..ecc1a6fd4c2 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -34,7 +34,7 @@ set(SRC intern/asset_shelf.cc intern/asset_shelf_asset_view.cc intern/asset_shelf_catalog_selector.cc - intern/asset_shelf_popup.cc + intern/asset_shelf_popover.cc intern/asset_shelf_regiondata.cc intern/asset_shelf_settings.cc intern/asset_temp_id_consumer.cc diff --git a/source/blender/editors/asset/ED_asset_shelf.hh b/source/blender/editors/asset/ED_asset_shelf.hh index f3ae9c8203b..5c99f6f979a 100644 --- a/source/blender/editors/asset/ED_asset_shelf.hh +++ b/source/blender/editors/asset/ED_asset_shelf.hh @@ -65,7 +65,7 @@ void header_region_init(wmWindowManager *wm, ARegion *region); void header_region(const bContext *C, ARegion *region); void header_region_listen(const wmRegionListenerParams *params); int header_region_size(); -void header_regiontype_register(ARegionType *region_type, const int space_type); +void types_register(ARegionType *region_type, const int space_type); /** \} */ @@ -90,7 +90,6 @@ AssetShelfType *type_find_from_idname(const StringRef idname); /** \name Asset Shelf Popup * \{ */ -uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type); void type_popup_unlink(const AssetShelfType &shelf_type); /** \} */ diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 788b2e41c7e..60b0ba01c06 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -13,6 +13,7 @@ #include "AS_asset_catalog_path.hh" #include "AS_asset_library.hh" +#include "BLI_function_ref.hh" #include "BLI_string.h" #include "BKE_context.hh" @@ -831,7 +832,7 @@ static void asset_shelf_header_draw(const bContext *C, Header *header) uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_NONE, "", ICON_VIEWZOOM); } -void header_regiontype_register(ARegionType *region_type, const int space_type) +static void header_regiontype_register(ARegionType *region_type, const int space_type) { HeaderType *ht = MEM_cnew(__func__); STRNCPY(ht->idname, "ASSETSHELF_HT_settings"); @@ -843,8 +844,13 @@ void header_regiontype_register(ARegionType *region_type, const int space_type) }; BLI_addtail(®ion_type->headertypes, ht); +} +void types_register(ARegionType *region_type, const int space_type) +{ + header_regiontype_register(region_type, space_type); catalog_selector_panel_register(region_type); + popover_panel_register(region_type); } /** \} */ diff --git a/source/blender/editors/asset/intern/asset_shelf.hh b/source/blender/editors/asset/intern/asset_shelf.hh index 9270d631861..261a5716c8f 100644 --- a/source/blender/editors/asset/intern/asset_shelf.hh +++ b/source/blender/editors/asset/intern/asset_shelf.hh @@ -38,6 +38,7 @@ void build_asset_view(uiLayout &layout, const ARegion ®ion); void catalog_selector_panel_register(ARegionType *region_type); +void popover_panel_register(ARegionType *region_type); AssetShelf *active_shelf_from_context(const bContext *C); diff --git a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc index b92ee51aac8..2bc40461c3e 100644 --- a/source/blender/editors/asset/intern/asset_shelf_asset_view.cc +++ b/source/blender/editors/asset/intern/asset_shelf_asset_view.cc @@ -11,6 +11,7 @@ #include "AS_asset_library.hh" #include "AS_asset_representation.hh" +#include "BKE_asset.hh" #include "BKE_screen.hh" #include "BLI_fnmatch.h" @@ -41,13 +42,12 @@ class AssetView : public ui::AbstractGridView { const AssetShelf &shelf_; std::optional active_asset_; std::optional catalog_filter_ = std::nullopt; - bool is_popup_ = false; friend class AssetViewItem; friend class AssetDragController; public: - AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf, bool is_popup); + AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf); void build_items() override; bool begin_filtering(const bContext &C) const override; @@ -84,10 +84,8 @@ class AssetDragController : public ui::AbstractViewItemDragController { void *create_drag_data() const override; }; -AssetView::AssetView(const AssetLibraryReference &library_ref, - const AssetShelf &shelf, - const bool is_popup) - : library_ref_(library_ref), shelf_(shelf), is_popup_(is_popup) +AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf) + : library_ref_(library_ref), shelf_(shelf) { if (shelf.type->get_active_asset) { if (const AssetWeakReference *weak_ref = shelf.type->get_active_asset(shelf.type)) { @@ -294,8 +292,7 @@ void build_asset_view(uiLayout &layout, BLI_assert(tile_width != 0); BLI_assert(tile_height != 0); - const bool is_popup = region.regiontype == RGN_TYPE_TEMPORARY; - std::unique_ptr asset_view = std::make_unique(library_ref, shelf, is_popup); + std::unique_ptr asset_view = std::make_unique(library_ref, shelf); asset_view->set_catalog_filter(catalog_filter_from_shelf_settings(shelf.settings, *library)); asset_view->set_tile_size(tile_width, tile_height); diff --git a/source/blender/editors/asset/intern/asset_shelf_popup.cc b/source/blender/editors/asset/intern/asset_shelf_popover.cc similarity index 63% rename from source/blender/editors/asset/intern/asset_shelf_popup.cc rename to source/blender/editors/asset/intern/asset_shelf_popover.cc index 99e729b3367..6aa08f29eab 100644 --- a/source/blender/editors/asset/intern/asset_shelf_popup.cc +++ b/source/blender/editors/asset/intern/asset_shelf_popover.cc @@ -10,6 +10,8 @@ #include "BKE_screen.hh" +#include "BLI_string.h" + #include "BLT_translation.hh" #include "UI_interface_c.hh" @@ -22,6 +24,8 @@ #include "RNA_access.hh" #include "RNA_prototypes.h" +#include "WM_api.hh" + namespace blender::ed::asset::shelf { class StaticPopupShelves { @@ -51,20 +55,20 @@ void type_popup_unlink(const AssetShelfType &shelf_type) } } -static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type) +static AssetShelf *get_shelf_for_popup(const bContext &C, AssetShelfType &shelf_type) { Vector &popup_shelves = StaticPopupShelves::shelves(); for (AssetShelf *shelf : popup_shelves) { if (STREQ(shelf->idname, shelf_type.idname)) { - if (type_poll_for_popup(*C, ensure_shelf_has_type(*shelf))) { + if (type_poll_for_popup(C, ensure_shelf_has_type(*shelf))) { return shelf; } break; } } - if (type_poll_for_popup(*C, &shelf_type)) { + if (type_poll_for_popup(C, &shelf_type)) { AssetShelf *new_shelf = create_shelf_from_type(shelf_type); new_shelf->settings.display_flag |= ASSETSHELF_SHOW_NAMES; popup_shelves.append(new_shelf); @@ -134,10 +138,16 @@ class AssetCatalogTreeView : public ui::AbstractTreeView { return settings_is_active_catalog(shelf_.settings, catalog_path); }); - catalog_item.foreach_child( - [&view_item, this](const asset_system::AssetCatalogTreeItem &child) { - build_catalog_items_recursive(view_item, child); - }); + const int parent_count = view_item.count_parents() + 1; + + catalog_item.foreach_child([&, this](const asset_system::AssetCatalogTreeItem &child) { + ui::BasicTreeViewItem &child_item = build_catalog_items_recursive(view_item, child); + + /* Uncollapse to some level (gives quick acces, but don't let the tree get too big). */ + if (parent_count < 3) { + child_item.uncollapse_by_default(); + } + }); return view_item; } @@ -160,36 +170,43 @@ static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf) ui::TreeViewBuilder::build_tree_view(*tree_view, layout); } -uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type) +static AssetShelfType *lookup_type_from_idname_in_context(const bContext *C) { - bScreen *screen = CTX_wm_screen(C); - uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS); - UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER); - UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); - UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); - UI_block_direction_set(block, UI_DIR_DOWN); + const std::optional idname = CTX_data_string_get(C, "asset_shelf_idname"); + if (!idname) { + return nullptr; + } + return type_find_from_idname(*idname); +} - AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type); +constexpr int LEFT_COL_WIDTH_UNITS = 10; +constexpr int RIGHT_COL_WIDTH_UNITS = 30; +constexpr int LAYOUT_WIDTH_UNITS = LEFT_COL_WIDTH_UNITS + RIGHT_COL_WIDTH_UNITS; + +static void popover_panel_draw(const bContext *C, Panel *panel) +{ + AssetShelfType *shelf_type = lookup_type_from_idname_in_context(C); + BLI_assert_msg(shelf_type != nullptr, "couldn't find asset shelf type from context"); + + const ARegion *region = CTX_wm_region_popup(C) ? CTX_wm_region_popup(C) : CTX_wm_region(C); + + uiLayout *layout = panel->layout; + uiLayoutSetUnitsX(layout, LAYOUT_WIDTH_UNITS); + + AssetShelf *shelf = get_shelf_for_popup(*C, *shelf_type); if (!shelf) { BLI_assert_unreachable(); - return block; + return; } - const uiStyle *style = UI_style_get_dpi(); - - const int layout_width = UI_UNIT_X * 40; - const int left_col_width = 10 * UI_UNIT_X; - const int right_col_width = layout_width - left_col_width; - uiLayout *layout = UI_block_layout( - block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, layout_width, 0, 0, style); - + bScreen *screen = CTX_wm_screen(C); PointerRNA library_ref_ptr = RNA_pointer_create( &screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference); uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr); uiLayout *row = uiLayoutRow(layout, false); uiLayout *catalogs_col = uiLayoutColumn(row, false); - uiLayoutSetUnitsX(catalogs_col, left_col_width / UI_UNIT_X); + uiLayoutSetUnitsX(catalogs_col, LEFT_COL_WIDTH_UNITS); uiLayoutSetFixedSize(catalogs_col, true); library_selector_draw(C, catalogs_col, *shelf); catalog_tree_draw(*catalogs_col, *shelf); @@ -198,14 +215,53 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType * uiLayout *sub = uiLayoutRow(right_col, false); /* Same as file/asset browser header. */ PointerRNA shelf_ptr = RNA_pointer_create(&screen->id, &RNA_AssetShelf, shelf); - uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM); + uiItemR(sub, + &shelf_ptr, + "search_filter", + /* Force the button to be active in a semi-modal state. */ + UI_ITEM_R_TEXT_BUT_FORCE_SEMI_MODAL_ACTIVE, + "", + ICON_VIEWZOOM); uiLayout *asset_view_col = uiLayoutColumn(right_col, false); - uiLayoutSetUnitsX(asset_view_col, right_col_width / UI_UNIT_X); + uiLayoutSetUnitsX(asset_view_col, RIGHT_COL_WIDTH_UNITS); uiLayoutSetFixedSize(asset_view_col, true); - build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region); - return block; + build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region); +} + +static bool popover_panel_poll(const bContext *C, PanelType * /*panel_type*/) +{ + const AssetShelfType *shelf_type = lookup_type_from_idname_in_context(C); + if (!shelf_type) { + return false; + } + + return type_poll_for_popup(*C, shelf_type); +} + +void popover_panel_register(ARegionType *region_type) +{ + /* Uses global paneltype registry to allow usage as popover. So only register this once (may be + * called from multiple spaces). */ + if (WM_paneltype_find("ASSETSHELF_PT_popover_panel", true)) { + return; + } + + PanelType *pt = MEM_cnew(__func__); + STRNCPY(pt->idname, "ASSETSHELF_PT_popover_panel"); + STRNCPY(pt->label, N_("Asset Shelf Panel")); + STRNCPY(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->description = N_("Display an asset shelf in a popover panel"); + pt->draw = popover_panel_draw; + pt->poll = popover_panel_poll; + pt->listener = asset::list::asset_reading_region_listen_fn; + /* Move to have first asset item under cursor. */ + pt->offset_units_xy.x = -(LEFT_COL_WIDTH_UNITS + 1.5f); + /* Offset so mouse is below search button, over the first row of assets. */ + pt->offset_units_xy.y = 2.5f; + BLI_addtail(®ion_type->paneltypes, pt); + WM_paneltype_add(pt); } } // namespace blender::ed::asset::shelf diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 4288fe0be90..9815f383c3b 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -91,6 +91,9 @@ void attribute_search_add_items(StringRefNull str, uiSearchItems *items, bool is_first); +bool asset_shelf_popover_invoke(bContext &C, + blender::StringRef asset_shelf_idname, + ReportList &reports); /** * Some drop targets simply allow dropping onto/into them, others support dragging in-between them. * Classes implementing the drop-target interface can use this type to control the behavior by diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 7130c73c9aa..c32feaeba6d 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2269,6 +2269,10 @@ MenuType *UI_but_menutype_get(const uiBut *but); * This is a bit of a hack but best keep it in one place at least. */ PanelType *UI_but_paneltype_get(const uiBut *but); +/** + * This is a bit of a hack but best keep it in one place at least. + */ +std::optional UI_but_asset_shelf_type_idname_get(const uiBut *but); void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout); /** * Used for popup panels only. @@ -2789,8 +2793,11 @@ void uiTemplateAssetView(uiLayout *layout, namespace blender::ui { -void template_asset_shelf_popover( - uiLayout &layout, const bContext &C, StringRefNull asset_shelf_id, StringRef name, int icon); +void template_asset_shelf_popover(uiLayout &layout, + const bContext &C, + StringRefNull asset_shelf_id, + StringRefNull name, + int icon); } diff --git a/source/blender/editors/interface/interface_context_menu.cc b/source/blender/editors/interface/interface_context_menu.cc index 07a10906f8c..f688565e758 100644 --- a/source/blender/editors/interface/interface_context_menu.cc +++ b/source/blender/editors/interface/interface_context_menu.cc @@ -114,6 +114,13 @@ static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDPro return "WM_OT_call_menu"; } + if (std::optional asset_shelf_idname = UI_but_asset_shelf_type_idname_get(but)) { + IDProperty *prop = blender::bke::idprop::create_group(__func__).release(); + IDP_AddToGroup(prop, bke::idprop::create("name", *asset_shelf_idname).release()); + *r_prop = prop; + return "WM_OT_call_asset_shelf_popover"; + } + if (PanelType *pt = UI_but_paneltype_get(but)) { IDProperty *prop = blender::bke::idprop::create_group(__func__).release(); IDP_AddToGroup(prop, bke::idprop::create("name", pt->idname).release()); diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 6f6927e2fcf..68d5a806f70 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -1014,10 +1014,12 @@ uiPopupBlockHandle *ui_popup_menu_create( /* `interface_region_popover.cc` */ +using uiPopoverCreateFunc = std::function; + uiPopupBlockHandle *ui_popover_panel_create(bContext *C, ARegion *butregion, uiBut *but, - uiMenuCreateFunc menu_func, + uiPopoverCreateFunc popover_func, const PanelType *panel_type); /* `interface_region_menu_pie.cc` */ @@ -1552,6 +1554,9 @@ void UI_OT_eyedropper_driver(wmOperatorType *ot); void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot); +/* interface_template_asset_shelf_popover.cc */ +std::optional UI_asset_shelf_idname_from_button_context(const uiBut *but); + /* interface_template_asset_view.cc */ uiListType *UI_UL_asset_view(); diff --git a/source/blender/editors/interface/interface_layout.cc b/source/blender/editors/interface/interface_layout.cc index 528157a5db0..da96fa3b402 100644 --- a/source/blender/editors/interface/interface_layout.cc +++ b/source/blender/editors/interface/interface_layout.cc @@ -3195,6 +3195,10 @@ void uiItemPopoverPanel_ptr( icon = ICON_BLANK1; } + const bContextStore *previous_ctx = CTX_store_get(C); + /* Set context for polling (and panel header drawing). */ + CTX_store_set(const_cast(C), layout->context); + const bool ok = (pt->poll == nullptr) || pt->poll(C, pt); if (ok && (pt->draw_header != nullptr)) { layout = uiLayoutRow(layout, true); @@ -3206,6 +3210,9 @@ void uiItemPopoverPanel_ptr( panel.flag = PNL_POPOVER; pt->draw_header(C, &panel); } + + CTX_store_set(const_cast(C), previous_ctx); + uiBut *but = ui_item_menu( layout, name, icon, ui_item_paneltype_func, pt, nullptr, TIP_(pt->description), true); but->type = UI_BTYPE_POPOVER; @@ -6109,6 +6116,11 @@ PanelType *UI_but_paneltype_get(const uiBut *but) return nullptr; } +std::optional UI_but_asset_shelf_type_idname_get(const uiBut *but) +{ + return UI_asset_shelf_idname_from_button_context(but); +} + void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout) { Menu menu{}; diff --git a/source/blender/editors/interface/regions/interface_region_popover.cc b/source/blender/editors/interface/regions/interface_region_popover.cc index b9483ed6402..2bd6fd463da 100644 --- a/source/blender/editors/interface/regions/interface_region_popover.cc +++ b/source/blender/editors/interface/regions/interface_region_popover.cc @@ -61,8 +61,8 @@ struct uiPopover { wmKeyMap *keymap; wmEventHandler_Keymap *keymap_handler; - uiMenuCreateFunc menu_func; - const PanelType *menu_arg; + uiPopoverCreateFunc popover_func; + const PanelType *panel_type; /* Size in pixels (ui scale applied). */ int ui_size_x; @@ -114,9 +114,9 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v if (!pup->layout) { ui_popover_create_block(C, handle->region, pup, WM_OP_INVOKE_REGION_WIN); - if (pup->menu_func) { + if (pup->popover_func) { pup->block->handle = handle; - pup->menu_func(C, pup->layout, const_cast(pup->menu_arg)); + pup->popover_func(C, pup->layout, const_cast(pup->panel_type)); pup->block->handle = nullptr; } @@ -185,6 +185,20 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v * areas near the bottom of the window on refreshes. */ handle->max_size_y = UI_UNIT_Y * 16.0f; } + else if (pup->panel_type && + (pup->panel_type->offset_units_xy.x || pup->panel_type->offset_units_xy.y)) + { + UI_block_flag_enable(block, UI_BLOCK_LOOP); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + UI_block_direction_set(block, block->direction); + block->minbounds = UI_MENU_WIDTH_MIN; + + const int bounds_offset[2] = { + int(pup->panel_type->offset_units_xy.x * UI_UNIT_X), + int(pup->panel_type->offset_units_xy.y * UI_UNIT_Y), + }; + UI_block_bounds_set_popup(block, block_margin, bounds_offset); + } else { /* Not attached to a button. */ int bounds_offset[2] = {0, 0}; @@ -233,20 +247,20 @@ static void ui_block_free_func_POPOVER(void *arg_pup) wmWindow *window = pup->window; WM_event_remove_keymap_handler(&window->modalhandlers, pup->keymap); } - MEM_freeN(pup); + MEM_delete(pup); } uiPopupBlockHandle *ui_popover_panel_create(bContext *C, ARegion *butregion, uiBut *but, - uiMenuCreateFunc menu_func, + uiPopoverCreateFunc popover_func, const PanelType *panel_type) { wmWindow *window = CTX_wm_window(C); const uiStyle *style = UI_style_get_dpi(); /* Create popover, buttons are created from callback. */ - uiPopover *pup = MEM_cnew(__func__); + uiPopover *pup = MEM_new(__func__); pup->but = but; /* FIXME: maybe one day we want non panel popovers? */ @@ -259,8 +273,8 @@ uiPopupBlockHandle *ui_popover_panel_create(bContext *C, (text_points_max / float(UI_DEFAULT_TEXT_POINTS)); } - pup->menu_func = menu_func; - pup->menu_arg = panel_type; + pup->popover_func = popover_func; + pup->panel_type = panel_type; #ifdef USE_UI_POPOVER_ONCE { @@ -335,7 +349,7 @@ int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, Rep uiPopover *UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_button) { - uiPopover *pup = MEM_cnew(__func__); + uiPopover *pup = MEM_new(__func__); if (ui_menu_width == 0) { ui_menu_width = U.widget_unit * UI_POPOVER_WIDTH_UNITS; } diff --git a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc index 421a5d00547..4ae4dfa369a 100644 --- a/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc +++ b/source/blender/editors/interface/templates/interface_template_asset_shelf_popover.cc @@ -7,28 +7,28 @@ */ #include "BKE_context.hh" +#include "BKE_report.hh" #include "BKE_screen.hh" #include "RNA_access.hh" +#include "RNA_define.hh" +#include "RNA_prototypes.h" +#include "UI_interface.hh" #include "UI_interface_c.hh" #include "UI_resources.hh" #include "interface_intern.hh" #include "ED_asset_shelf.hh" -namespace blender::ui { +#include "WM_api.hh" -static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type) -{ - AssetShelfType *shelf_type = reinterpret_cast(arg_shelf_type); - return ed::asset::shelf::popup_block_create(C, region, shelf_type); -} +namespace blender::ui { void template_asset_shelf_popover(uiLayout &layout, const bContext &C, const StringRefNull asset_shelf_id, - const StringRef name, + const StringRefNull name, const BIFIconID icon) { AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_id); @@ -38,30 +38,64 @@ void template_asset_shelf_popover(uiLayout &layout, } const ARegion *region = CTX_wm_region(&C); + uiBlock *block = uiLayoutGetBlock(&layout); + + uiLayout *row = uiLayoutRow(&layout, true); const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype); const bool use_preview_icon = use_big_size; - const short width = [&]() -> short { - if (use_big_size) { - return UI_UNIT_X * 6; - } - return UI_UNIT_X * (name.is_empty() ? 1.6f : 7); - }(); - const short height = UI_UNIT_Y * (use_big_size ? 6 : 1); - uiBlock *block = uiLayoutGetBlock(&layout); - uiBut *but = uiDefBlockBut( - block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset"); + uiLayoutSetContextString(row, "asset_shelf_idname", asset_shelf_id); + if (use_big_size) { + uiLayoutSetScaleX(row, 6); + uiLayoutSetScaleY(row, 6); + } + else { + uiLayoutSetUnitsX(row, name.is_empty() ? 1.6f : 7); + } + + uiItemPopoverPanel(row, &C, "ASSETSHELF_PT_popover_panel", name.c_str(), icon); + uiBut *but = static_cast(block->buttons.last); if (use_preview_icon) { ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); } - else { - ui_def_but_icon(but, icon, UI_HAS_ICON); - UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); +} + +bool asset_shelf_popover_invoke(bContext &C, StringRef asset_shelf_idname, ReportList &reports) +{ + AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_idname); + if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) { + return false; } - if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) { - UI_but_flag_enable(but, UI_BUT_DISABLED); + PanelType *pt = WM_paneltype_find("ASSETSHELF_PT_popover_panel", true); + if (pt == nullptr) { + BKE_reportf(&reports, RPT_ERROR, "Asset shelf popover panel type not found"); + return false; } + + /* Skip panel poll check here. Should usually be done, but requires passing the asset shelf type + * name via some context-store, but there's nothing to provide that here. Asset shelf type is + * polled above, so it's okay. */ + + std::string asset_shelf_id_str = asset_shelf_idname; + ui_popover_panel_create( + &C, + nullptr, + nullptr, + [asset_shelf_id_str](bContext *C, uiLayout *layout, void *arg_pt) { + uiLayoutSetContextString(layout, "asset_shelf_idname", asset_shelf_id_str); + ui_item_paneltype_func(C, layout, arg_pt); + }, + pt); + + return true; } } // namespace blender::ui + +using namespace blender; + +std::optional UI_asset_shelf_idname_from_button_context(const uiBut *but) +{ + return UI_but_context_string_get(but, "asset_shelf_idname"); +} diff --git a/source/blender/editors/space_image/space_image.cc b/source/blender/editors/space_image/space_image.cc index e8234cb8a53..513790e5bd0 100644 --- a/source/blender/editors/space_image/space_image.cc +++ b/source/blender/editors/space_image/space_image.cc @@ -1224,7 +1224,7 @@ void ED_spacetype_image() art->listener = asset::shelf::header_region_listen; art->context = asset::shelf::context; BLI_addhead(&st->regiontypes, art); - asset::shelf::header_regiontype_register(art, SPACE_IMAGE); + asset::shelf::types_register(art, SPACE_IMAGE); /* regions: hud */ art = ED_area_type_hud(st->spaceid); diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 9dad3485753..386e6a98a99 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -2199,7 +2199,7 @@ void ED_spacetype_view3d() art->listener = asset::shelf::header_region_listen; art->context = asset::shelf::context; BLI_addhead(&st->regiontypes, art); - asset::shelf::header_regiontype_register(art, SPACE_VIEW3D); + asset::shelf::types_register(art, SPACE_VIEW3D); /* regions: hud */ art = ED_area_type_hud(st->spaceid); diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index bea5d5ee2c5..0ca2e26b127 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -973,7 +973,7 @@ void rna_uiTemplateAssetShelfPopover(uiLayout *layout, icon = icon_value; } - blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name, icon); + blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name ? name : "", icon); } PointerRNA rna_uiTemplatePopupConfirm(uiLayout *layout, diff --git a/source/blender/windowmanager/intern/wm_operators.cc b/source/blender/windowmanager/intern/wm_operators.cc index 728cba0030f..fefe740ecf8 100644 --- a/source/blender/windowmanager/intern/wm_operators.cc +++ b/source/blender/windowmanager/intern/wm_operators.cc @@ -2254,6 +2254,39 @@ static void WM_OT_call_panel(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +static int asset_shelf_popover_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + char *asset_shelf_id = RNA_string_get_alloc(op->ptr, "name", nullptr, 0, nullptr); + BLI_SCOPED_DEFER([&]() { MEM_freeN(asset_shelf_id); }); + + if (!blender::ui::asset_shelf_popover_invoke(*C, asset_shelf_id, *op->reports)) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return OPERATOR_INTERFACE; +} + +/* Needs to be defined at WM level to be globally accessible. */ +static void WM_OT_call_asset_shelf_popover(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Call Asset Shelf Popover"; + ot->idname = "WM_OT_call_asset_shelf_popover"; + ot->description = "Open a predefined asset shelf in a popup"; + + /* api callbacks */ + ot->invoke = asset_shelf_popover_invoke; + + ot->flag = OPTYPE_INTERNAL; + + RNA_def_string(ot->srna, + "name", + nullptr, + 0, + "Asset Shelf Name", + "Identifier of the asset shelf to display"); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -4140,6 +4173,7 @@ void wm_operatortypes_register() WM_operatortype_append(WM_OT_call_menu); WM_operatortype_append(WM_OT_call_menu_pie); WM_operatortype_append(WM_OT_call_panel); + WM_operatortype_append(WM_OT_call_asset_shelf_popover); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); #if defined(WIN32)