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)