Fix #143699: Pose library asset shelf does not apply poses

This was quite involved to get to work. Basic idea is to make
`bl_activate_operator` work for the pose library asset shelf, and
introducing a `bl_drag_operator` for blending poses.

- Make pose asset operators take an asset reference, which is how
  `bl_activate_operator` usually gets the asset to operate on. This way
  poses references can be assigned to a shortcut, identified by asset
  library and relative asset path within the library. Falls back to
  getting the asset from context.
- Trigger `bl_activate_operator` on every click, instead of only when
  an un-active item becomes active. Needed so poses can be re-applied
  as before.
- Fix button context not passed to the `bl_activate_operator` when
  force-activating, e.g. on right-click events.
- Allow registering a `bl_drag_operator` in the asset shelf definition.
  Executed when dragging an asset in the shelf.
- When dragging an asset, highlight it as active, without calling the
  `bl_activate_operator`. This is important feedback to the user.
- Activate/select view items on click instead of drag, so dragging is
  possible.
- Let pose applying operators handle the Ctrl key to apply poses
  flipped. There's no simple way to attach such alternative behaviors
  to `bl_activate_operator`/`bl_drag_operator`
- Remove keymap items that were there for the previous "hacky" solution
  to apply & blend poses.

Pull Request: https://projects.blender.org/blender/blender/pulls/144023
This commit is contained in:
Julian Eisel
2025-08-28 12:04:31 +02:00
committed by Julian Eisel
parent 5d72498154
commit 450f428434
21 changed files with 265 additions and 86 deletions

View File

@@ -40,9 +40,8 @@ class PoseLibraryPanel:
class VIEW3D_AST_pose_library(bpy.types.AssetShelf):
bl_space_type = "VIEW_3D"
# We have own keymap items to add custom drag behavior (pose blending), disable the default
# asset dragging.
bl_options = {'NO_ASSET_DRAG'}
bl_activate_operator = "POSELIB_OT_apply_pose_asset"
bl_drag_operator = "POSELIB_OT_blend_pose_asset"
@classmethod
def poll(cls, context: Context) -> bool:

View File

@@ -21,21 +21,6 @@ def register() -> None:
kmi = km.keymap_items.new("poselib.apply_pose_asset", "LEFTMOUSE", "DOUBLE_CLICK")
addon_keymaps.append((km, kmi))
# Asset Shelf
km = wm.keyconfigs.addon.keymaps.new(name="Asset Shelf")
# Click to apply pose.
kmi = km.keymap_items.new("poselib.apply_pose_asset", "LEFTMOUSE", "CLICK")
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.apply_pose_asset", "LEFTMOUSE", "CLICK", ctrl=True)
kmi.properties.flipped = True
addon_keymaps.append((km, kmi))
# Drag to blend pose.
kmi = km.keymap_items.new("poselib.blend_pose_asset", "LEFTMOUSE", "CLICK_DRAG")
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.blend_pose_asset", "LEFTMOUSE", "CLICK_DRAG", ctrl=True)
kmi.properties.flipped = True
addon_keymaps.append((km, kmi))
def unregister() -> None:
# Clear shortcuts from the keymap.

View File

@@ -1042,7 +1042,7 @@ def km_user_interface(_params):
("ui.view_scroll", {"type": 'WHEELUPMOUSE', "value": 'ANY'}, None),
("ui.view_scroll", {"type": 'WHEELDOWNMOUSE', "value": 'ANY'}, None),
("ui.view_scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("extend", True)]}),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},

View File

@@ -420,7 +420,7 @@ def km_user_interface(params):
("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "alt": True}, None),
("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None),
("anim.keyingset_button_remove", {"type": 'K', "value": 'PRESS', "alt": True}, None),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None),
])
return keymap

View File

@@ -12,7 +12,14 @@ from bpy.app.translations import (
class BrushAssetShelf:
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
bl_options = {
'DEFAULT_VISIBLE',
'NO_ASSET_DRAG',
'STORE_ENABLED_CATALOGS_IN_PREFERENCES',
# Ensure `bl_activate_operator` is called when spawning the context menu. Operators there
# rely on the imported, active brush, not just the active asset representation.
'ACTIVATE_FOR_CONTEXT_MENU',
}
bl_activate_operator = "BRUSH_OT_asset_activate"
bl_default_preview_size = 48
brush_type_prop = None

View File

@@ -641,6 +641,12 @@ enum AssetShelfTypeFlag {
ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG = (1 << 0),
ASSET_SHELF_TYPE_FLAG_DEFAULT_VISIBLE = (1 << 1),
ASSET_SHELF_TYPE_FLAG_STORE_CATALOGS_IN_PREFS = (1 << 2),
/**
* When spawning a context menu for an asset, activate the asset and call the activate operator
* (`bl_activate_operator`/#AssetShelfType.activate_operator) if present, rather than just
* highlighting the asset as active.
*/
ASSET_SHELF_TYPE_FLAG_ACTIVATE_FOR_CONTEXT_MENU = (1 << 3),
ASSET_SHELF_TYPE_FLAG_MAX
};
@@ -656,6 +662,8 @@ struct AssetShelfType {
/** Operator to call when activating a grid view item. */
std::string activate_operator;
/** Operator to call when dragging a grid view item. */
std::string drag_operator;
AssetShelfTypeFlag flag;

View File

@@ -38,6 +38,7 @@
#include "UI_interface.hh"
#include "ED_asset.hh"
#include "ED_asset_menu_utils.hh"
#include "ED_keyframing.hh"
#include "ED_screen.hh"
#include "ED_util.hh"
@@ -334,7 +335,37 @@ static void poselib_tempload_exit(PoseBlendData *pbd)
static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op)
{
using namespace blender::ed;
const AssetRepresentationHandle *asset = CTX_wm_asset(C);
const blender::asset_system::AssetRepresentation *asset = nullptr;
if (asset::operator_asset_reference_props_is_set(*op->ptr)) {
asset = asset::operator_asset_reference_props_get_asset_from_all_library(
*C, *op->ptr, op->reports);
if (!asset) {
/* Explicit asset reference passed, but cannot be found. Error out. */
BKE_reportf(op->reports,
RPT_ERROR,
"Asset not found: '%s'",
RNA_string_get(op->ptr, "relative_asset_identifier").c_str());
return nullptr;
}
}
else {
/* If no explicit asset reference was passed, get asset from context. */
asset = CTX_wm_asset(C);
if (!asset) {
BKE_report(op->reports, RPT_ERROR, "No asset in context");
return nullptr;
}
}
if (asset->get_id_type() != ID_AC) {
BKE_reportf(op->reports,
RPT_ERROR,
"Asset ('%s') is not an action data-block",
asset->get_name().c_str());
return nullptr;
}
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
@@ -378,6 +409,9 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
pbd->act = poselib_blend_init_get_action(C, op);
if (pbd->act == nullptr) {
/* No report here. The poll function cannot check if the operator properties have an asset
* reference to determine the asset to operate on, in which case we fallback to getting the
* asset from context. */
return false;
}
if (pbd->act->wrap().slots().size() == 0) {
@@ -385,7 +419,9 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *
return false;
}
pbd->is_flipped = RNA_boolean_get(op->ptr, "flipped");
pbd->is_flipped = RNA_struct_property_is_set(op->ptr, "flipped") ?
RNA_boolean_get(op->ptr, "flipped") :
(event && (event->modifier & KM_CTRL));
pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor");
/* Only construct the flipped pose if there is a chance it's actually needed. */
@@ -568,6 +604,20 @@ static wmOperatorStatus poselib_blend_modal(bContext *C, wmOperator *op, const w
return operator_result;
}
static wmOperatorStatus poselib_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (!poselib_blend_init_data(C, op, event)) {
poselib_blend_free(op);
return OPERATOR_CANCELLED;
}
poselib_blend_apply(C, op);
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
pbd->state = POSE_BLEND_CONFIRM;
return poselib_blend_exit(C, op);
}
/* Modal Operator init. */
static wmOperatorStatus poselib_blend_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
@@ -589,23 +639,7 @@ static wmOperatorStatus poselib_blend_invoke(bContext *C, wmOperator *op, const
/* Single-shot apply. */
static wmOperatorStatus poselib_blend_exec(bContext *C, wmOperator *op)
{
if (!poselib_blend_init_data(C, op, nullptr)) {
poselib_blend_free(op);
return OPERATOR_CANCELLED;
}
poselib_blend_apply(C, op);
PoseBlendData *pbd = static_cast<PoseBlendData *>(op->customdata);
pbd->state = POSE_BLEND_CONFIRM;
return poselib_blend_exit(C, op);
}
static bool poselib_asset_in_context(bContext *C)
{
/* Check whether the context provides the asset data needed to add a pose. */
const AssetRepresentationHandle *asset = CTX_wm_asset(C);
return asset && (asset->get_id_type() == ID_AC);
return poselib_apply_invoke(C, op, nullptr);
}
/* Poll callback for operators that require existing PoseLib data (with poses) to work. */
@@ -617,9 +651,12 @@ static bool poselib_blend_poll(bContext *C)
return false;
}
return poselib_asset_in_context(C);
return true;
}
/* Operator propreties can set an asset reference to determine the asset to operate on (the pose
* can then be applied via shortcut too, for example). If this isn't set, an active asset from
* context is queried. */
void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -630,13 +667,14 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
ot->description = "Apply the given Pose Action to the rig";
/* Callbacks: */
ot->exec = poselib_blend_exec;
ot->invoke = poselib_apply_invoke;
ot->poll = poselib_blend_poll;
/* Flags: */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties: */
blender::ed::asset::operator_asset_reference_props_register(*ot->srna);
RNA_def_float_factor(ot->srna,
"blend_factor",
1.0f,
@@ -655,6 +693,7 @@ void POSELIB_OT_apply_pose_asset(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* See comment on #POSELIB_OT_apply_pose_asset. */
void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -675,6 +714,7 @@ void POSELIB_OT_blend_pose_asset(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties: */
blender::ed::asset::operator_asset_reference_props_register(*ot->srna);
prop = RNA_def_float_factor(ot->srna,
"blend_factor",
0.0f,

View File

@@ -56,6 +56,13 @@ void operator_asset_reference_props_set(const asset_system::AssetRepresentation
RNA_string_set(&ptr, "relative_asset_identifier", weak_ref.relative_asset_identifier);
}
bool operator_asset_reference_props_is_set(PointerRNA &ptr)
{
return RNA_struct_property_is_set(&ptr, "asset_library_type") &&
RNA_struct_property_is_set(&ptr, "asset_library_identifier") &&
RNA_struct_property_is_set(&ptr, "relative_asset_identifier");
}
/**
* #AssetLibrary::resolve_asset_weak_reference_to_full_path() currently does not support local
* assets.

View File

@@ -78,8 +78,9 @@ class AssetDragController : public ui::AbstractViewItemDragController {
public:
AssetDragController(ui::AbstractGridView &view, asset_system::AssetRepresentation &asset);
eWM_DragDataType get_drag_type() const override;
std::optional<eWM_DragDataType> get_drag_type() const override;
void *create_drag_data() const override;
void on_drag_start(bContext &C) override;
};
AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf)
@@ -123,6 +124,14 @@ void AssetView::build_items()
if (shelf_.type->flag & ASSET_SHELF_TYPE_FLAG_NO_ASSET_DRAG) {
item.disable_asset_drag();
}
/* Make sure every click calls the #bl_activate_operator. We might want to add a flag to
* enable/disable this. Or we only call #bl_activate_operator when an item becomes active, and
* add a #bl_click_operator for repeated execution on every click. So far it seems like every
* asset shelf use case works with activating on every click though. */
item.always_reactivate_on_click();
if (shelf_.type->flag & ASSET_SHELF_TYPE_FLAG_ACTIVATE_FOR_CONTEXT_MENU) {
item.activate_for_context_menu_set();
}
return true;
});
@@ -185,7 +194,7 @@ void AssetViewItem::disable_asset_drag()
* Needs freeing with #WM_operator_properties_free() (will be done by button if passed to that) and
* #MEM_freeN().
*/
static std::optional<wmOperatorCallParams> create_activate_operator_params(
static std::optional<wmOperatorCallParams> create_asset_operator_params(
const StringRefNull op_name, const asset_system::AssetRepresentation &asset)
{
if (op_name.is_empty()) {
@@ -212,7 +221,7 @@ void AssetViewItem::build_grid_tile(const bContext & /*C*/, uiLayout &layout) co
layout.block(), reinterpret_cast<uiBut *>(view_item_but_), "asset", &asset_ptr);
uiBut *item_but = reinterpret_cast<uiBut *>(this->view_item_button());
if (std::optional<wmOperatorCallParams> activate_op = create_activate_operator_params(
if (std::optional<wmOperatorCallParams> activate_op = create_asset_operator_params(
shelf_type.activate_operator, asset_))
{
/* Attach the operator, but don't call it through the button. We call it using
@@ -287,7 +296,7 @@ void AssetViewItem::on_activate(bContext &C)
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
if (std::optional<wmOperatorCallParams> activate_op = create_activate_operator_params(
if (std::optional<wmOperatorCallParams> activate_op = create_asset_operator_params(
shelf_type.activate_operator, asset_))
{
WM_operator_name_call_ptr(
@@ -305,7 +314,10 @@ bool AssetViewItem::should_be_filtered_visible(const StringRefNull filter_string
std::unique_ptr<ui::AbstractViewItemDragController> AssetViewItem::create_drag_controller() const
{
if (!allow_asset_drag_) {
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
if (!allow_asset_drag_ && shelf_type.drag_operator.empty()) {
return nullptr;
}
return std::make_unique<AssetDragController>(this->get_view(), asset_);
@@ -361,11 +373,32 @@ AssetDragController::AssetDragController(ui::AbstractGridView &view,
{
}
eWM_DragDataType AssetDragController::get_drag_type() const
std::optional<eWM_DragDataType> AssetDragController::get_drag_type() const
{
const AssetView &asset_view = this->get_view<AssetView>();
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
/* Disable asset dragging, only call #AssetShelfType::drag_operator in #on_drag_start(). */
if (!shelf_type.drag_operator.empty()) {
return std::nullopt;
}
return asset_.is_local_id() ? WM_DRAG_ID : WM_DRAG_ASSET;
}
void AssetDragController::on_drag_start(bContext &C)
{
const AssetView &asset_view = this->get_view<AssetView>();
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
if (std::optional<wmOperatorCallParams> drag_op = create_asset_operator_params(
shelf_type.drag_operator, asset_))
{
WM_operator_name_call_ptr(&C, drag_op->optype, drag_op->opcontext, drag_op->opptr, nullptr);
WM_operator_properties_free(drag_op->opptr);
MEM_delete(drag_op->opptr);
}
}
void *AssetDragController::create_drag_data() const
{
ID *local_id = asset_.local_id();

View File

@@ -31,6 +31,7 @@ void draw_menu_for_catalog(const asset_system::AssetCatalogTreeItem &item,
void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset,
PointerRNA &ptr);
bool operator_asset_reference_props_is_set(PointerRNA &ptr);
void operator_asset_reference_props_register(StructRNA &srna);
const asset_system::AssetRepresentation *find_asset_from_weak_ref(

View File

@@ -216,12 +216,28 @@ class AbstractViewItem {
* children currently.
*/
bool is_always_collapsible_ = false;
/** See #always_reactivate_on_click(). */
bool reactivate_on_click_ = false;
/** See #activate_for_context_menu_set(). */
bool activate_for_context_menu_ = false;
public:
virtual ~AbstractViewItem() = default;
virtual void build_context_menu(bContext &C, uiLayout &column) const;
/**
* Like #activate() but does not call #on_activate(). Use it to reflect changes in the active
* state that happened externally. Or to simply highlight the item as active without triggering
* activation with an `on_activate()` call. E.g. this is done when spawning a context menu if
* #activate_for_context_menu_set() wasn't called, to indicate which item the context menu
* belongs to.
*
* Can be overridden to customize behavior but should always call the base class implementation.
*
* \return true of the item was activated.
*/
virtual bool set_state_active();
/**
* Called when the view changes an item's state from inactive to active. Will only be called if
* the state change is triggered through the view, not through external changes. E.g. a click on
@@ -296,6 +312,11 @@ class AbstractViewItem {
bool is_interactive() const;
void disable_activatable();
/** Call #on_activate() on every click on the item, even when the item was active before. */
void always_reactivate_on_click();
/** Call #on_activate() when spawning a context menu. Otherwise the item will only be highlighted
* as active to indicate where the context menu was spawned from. */
void activate_for_context_menu_set();
/**
* Activates this item, deactivates other items, and calls the #AbstractViewItem::on_activate()
* function. Should only be called when the item was activated through the view (e.g. through a
@@ -308,6 +329,13 @@ class AbstractViewItem {
* actual item state is unknown, possibly calling state-change update functions incorrectly.
*/
void activate(bContext &C);
/**
* If #activate_for_context_menu_set() was called, properly (re)activates the item including a
* #AbstractViewItem::on_activate() call. Otherwise, the item will only be highlighted as active,
* to indicate which item the context menu belongs to.
* Should be used when spawning a context menu for this item.
*/
void activate_for_context_menu(bContext &C);
void deactivate();
/**
* Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we
@@ -349,14 +377,6 @@ class AbstractViewItem {
*/
virtual void update_from_old(const AbstractViewItem &old);
/**
* Like #activate() but does not call #on_activate(). Use it to reflect changes in the active
* state that happened externally.
* Can be overridden to customize behavior but should always call the base class implementation.
* \return true of the item was activated.
*/
virtual bool set_state_active();
/**
* See #AbstractView::change_state_delayed(). Overrides should call the base class
* implementation.
@@ -393,9 +413,13 @@ class AbstractViewItemDragController {
AbstractViewItemDragController(AbstractView &view);
virtual ~AbstractViewItemDragController() = default;
virtual eWM_DragDataType get_drag_type() const = 0;
virtual std::optional<eWM_DragDataType> get_drag_type() const = 0;
virtual void *create_drag_data() const = 0;
virtual void on_drag_start();
/**
* Called when beginning to drag. Also called when #get_drag_type() doesn't return a value, so an
* arbitrary action can be executed.
*/
virtual void on_drag_start(bContext &C);
/** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
* exception if the view is not of the requested type. */

View File

@@ -3023,7 +3023,7 @@ bool UI_view_item_popup_keep_open(const blender::ui::AbstractViewItem &item);
* support dragging, i.e. if it won't create a drag-controller upon request.
* \return True if dragging started successfully, otherwise false.
*/
bool UI_view_item_drag_start(bContext &C, const blender::ui::AbstractViewItem &item);
bool UI_view_item_drag_start(bContext &C, blender::ui::AbstractViewItem &item);
/**
* \param xy: Coordinate to find a view item at, in window space.

View File

@@ -5088,7 +5088,12 @@ static void force_activate_view_item_but(bContext *C,
/* For popups. Other abstract view instances correctly calls the select operator, see:
* #141235. */
if (but->context) {
CTX_store_set(C, but->context);
}
but->view_item->activate(*C);
CTX_store_set(C, nullptr);
ED_region_tag_redraw_no_rebuild(region);
ED_region_tag_refresh_ui(region);
@@ -8316,7 +8321,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
but->type == ButType::ViewItem ? but :
ui_view_item_find_mouse_over(data->region, event->xy));
if (clicked_view_item_but) {
clicked_view_item_but->view_item->activate(*C);
clicked_view_item_but->view_item->activate_for_context_menu(*C);
ED_region_tag_redraw_no_rebuild(data->region);
}
/* RMB has two options now */
@@ -10142,15 +10148,25 @@ static int ui_handle_view_item_event(bContext *C,
}
break;
case LEFTMOUSE:
if ((event->val == KM_PRESS) && (event->modifier == 0)) {
if (event->modifier == 0) {
/* Only bother finding the active view item button if the active button isn't already a
* view item. */
uiButViewItem *view_but = static_cast<uiButViewItem *>(
(active_but && active_but->type == ButType::ViewItem) ?
active_but :
ui_view_item_find_mouse_over(region, event->xy));
/* Will free active button if there already is one. */
if (view_but) {
if (UI_view_item_supports_drag(*view_but->view_item)) {
if (event->val != KM_CLICK) {
break;
}
}
else if (event->val != KM_PRESS) {
break;
}
/* Will free active button if there already is one. */
/* Close the popup when clicking on the view item directly, not any overlapped button. */
const bool close_popup = view_but == active_but;
force_activate_view_item_but(C, region, view_but, close_popup);

View File

@@ -81,9 +81,9 @@ class BoneCollectionDragController : public AbstractViewItemDragController {
bArmature &armature,
const int bcoll_index);
eWM_DragDataType get_drag_type() const override;
std::optional<eWM_DragDataType> get_drag_type() const override;
void *create_drag_data() const override;
void on_drag_start() override;
void on_drag_start(bContext &C) override;
};
class BoneCollectionDropTarget : public TreeViewItemDropTarget {
@@ -443,7 +443,7 @@ BoneCollectionDragController::BoneCollectionDragController(BoneCollectionTreeVie
{
}
eWM_DragDataType BoneCollectionDragController::get_drag_type() const
std::optional<eWM_DragDataType> BoneCollectionDragController::get_drag_type() const
{
return WM_DRAG_BONE_COLLECTION;
}
@@ -455,7 +455,7 @@ void *BoneCollectionDragController::create_drag_data() const
return drag_data;
}
void BoneCollectionDragController::on_drag_start()
void BoneCollectionDragController::on_drag_start(bContext & /*C*/)
{
ANIM_armature_bonecoll_active_index_set(drag_arm_bcoll_.armature, drag_arm_bcoll_.bcoll_index);
}

View File

@@ -182,7 +182,7 @@ class LayerViewItemDragController : public AbstractViewItemDragController {
{
}
eWM_DragDataType get_drag_type() const override
std::optional<eWM_DragDataType> get_drag_type() const override
{
if (dragged_node_.wrap().is_layer()) {
return WM_DRAG_GREASE_PENCIL_LAYER;
@@ -198,7 +198,7 @@ class LayerViewItemDragController : public AbstractViewItemDragController {
return drag_data;
}
void on_drag_start() override
void on_drag_start(bContext & /*C*/) override
{
grease_pencil_.set_active_node(&dragged_node_);
}

View File

@@ -201,7 +201,7 @@ class ItemDragController : public AbstractViewItemDragController {
{
}
eWM_DragDataType get_drag_type() const override
std::optional<eWM_DragDataType> get_drag_type() const override
{
return WM_DRAG_ID;
}

View File

@@ -52,7 +52,7 @@ class NodeTreeInterfaceDragController : public AbstractViewItemDragController {
bNodeTree &tree);
~NodeTreeInterfaceDragController() override = default;
eWM_DragDataType get_drag_type() const override;
std::optional<eWM_DragDataType> get_drag_type() const override;
void *create_drag_data() const override;
};
@@ -345,7 +345,7 @@ NodeTreeInterfaceDragController::NodeTreeInterfaceDragController(NodeTreeInterfa
{
}
eWM_DragDataType NodeTreeInterfaceDragController::get_drag_type() const
std::optional<eWM_DragDataType> NodeTreeInterfaceDragController::get_drag_type() const
{
return WM_DRAG_NODE_TREE_INTERFACE;
}

View File

@@ -70,7 +70,7 @@ bool AbstractViewItem::set_state_active()
void AbstractViewItem::activate(bContext &C)
{
if (set_state_active()) {
if (set_state_active() || reactivate_on_click_) {
on_activate(C);
}
@@ -80,9 +80,20 @@ void AbstractViewItem::activate(bContext &C)
}
}
void AbstractViewItem::activate_for_context_menu(bContext &C)
{
if (activate_for_context_menu_) {
this->activate(C);
}
else {
this->set_state_active();
}
}
void AbstractViewItem::deactivate()
{
is_active_ = false;
is_selected_ = false;
}
std::optional<bool> AbstractViewItem::should_be_selected() const
@@ -298,7 +309,7 @@ std::optional<std::string> AbstractViewItem::debug_name() const
AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view) {}
void AbstractViewItemDragController::on_drag_start()
void AbstractViewItemDragController::on_drag_start(bContext & /*C*/)
{
/* Do nothing by default. */
}
@@ -328,6 +339,16 @@ void AbstractViewItem::disable_activatable()
is_activatable_ = false;
}
void AbstractViewItem::always_reactivate_on_click()
{
reactivate_on_click_ = true;
}
void AbstractViewItem::activate_for_context_menu_set()
{
activate_for_context_menu_ = true;
}
void AbstractViewItem::disable_interaction()
{
is_interactive_ = false;
@@ -423,7 +444,7 @@ bool UI_view_item_popup_keep_open(const AbstractViewItem &item)
return item.get_view().get_popup_keep_open();
}
bool UI_view_item_drag_start(bContext &C, const AbstractViewItem &item)
bool UI_view_item_drag_start(bContext &C, AbstractViewItem &item)
{
const std::unique_ptr<AbstractViewItemDragController> drag_controller =
item.create_drag_controller();
@@ -431,12 +452,15 @@ bool UI_view_item_drag_start(bContext &C, const AbstractViewItem &item)
return false;
}
WM_event_start_drag(&C,
ICON_NONE,
drag_controller->get_drag_type(),
drag_controller->create_drag_data(),
WM_DRAG_FREE_DATA);
drag_controller->on_drag_start();
if (const std::optional<eWM_DragDataType> drag_type = drag_controller->get_drag_type()) {
WM_event_start_drag(
&C, ICON_NONE, *drag_type, drag_controller->create_drag_data(), WM_DRAG_FREE_DATA);
}
drag_controller->on_drag_start(C);
/* Make sure the view item is highlighted as active when dragging from it. This is useful user
* feedback. */
item.set_state_active();
return true;
}

View File

@@ -62,7 +62,7 @@ class ShapeKeyDragController : public ui::AbstractViewItemDragController {
{
}
eWM_DragDataType get_drag_type() const override
std::optional<eWM_DragDataType> get_drag_type() const override
{
return WM_DRAG_SHAPE_KEY;
}
@@ -73,7 +73,7 @@ class ShapeKeyDragController : public ui::AbstractViewItemDragController {
*drag_data = drag_key_;
return drag_data;
}
void on_drag_start() override
void on_drag_start(bContext & /*C*/) override
{
drag_key_.object->shapenr = drag_key_.index + 1;
}

View File

@@ -104,9 +104,9 @@ class AssetCatalogDragController : public ui::AbstractViewItemDragController {
explicit AssetCatalogDragController(AssetCatalogTreeView &tree_view,
const AssetCatalogTreeItem &catalog_item);
eWM_DragDataType get_drag_type() const override;
std::optional<eWM_DragDataType> get_drag_type() const override;
void *create_drag_data() const override;
void on_drag_start() override;
void on_drag_start(bContext &C) override;
};
class AssetCatalogDropTarget : public ui::TreeViewItemDropTarget {
@@ -553,7 +553,7 @@ AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tre
{
}
eWM_DragDataType AssetCatalogDragController::get_drag_type() const
std::optional<eWM_DragDataType> AssetCatalogDragController::get_drag_type() const
{
return WM_DRAG_ASSET_CATALOG;
}
@@ -566,7 +566,7 @@ void *AssetCatalogDragController::create_drag_data() const
return drag_catalog;
}
void AssetCatalogDragController::on_drag_start()
void AssetCatalogDragController::on_drag_start(bContext & /*C*/)
{
AssetCatalogTreeView &tree_view_ = this->get_view<AssetCatalogTreeView>();
tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id());

View File

@@ -1311,6 +1311,24 @@ static void rna_AssetShelf_activate_operator_set(PointerRNA *ptr, const char *va
shelf->type->activate_operator = value;
}
static void rna_AssetShelf_drag_operator_get(PointerRNA *ptr, char *value)
{
AssetShelf *shelf = static_cast<AssetShelf *>(ptr->data);
strcpy(value, shelf->type->drag_operator.c_str());
}
static int rna_AssetShelf_drag_operator_length(PointerRNA *ptr)
{
AssetShelf *shelf = static_cast<AssetShelf *>(ptr->data);
return shelf->type->drag_operator.size();
}
static void rna_AssetShelf_drag_operator_set(PointerRNA *ptr, const char *value)
{
AssetShelf *shelf = static_cast<AssetShelf *>(ptr->data);
shelf->type->drag_operator = value;
}
static StructRNA *rna_AssetShelf_refine(PointerRNA *shelf_ptr)
{
AssetShelf *shelf = (AssetShelf *)shelf_ptr->data;
@@ -2320,6 +2338,12 @@ static void rna_def_asset_shelf(BlenderRNA *brna)
"Store Enabled Catalogs in Preferences",
"Store the shelf's enabled catalogs in the preferences rather than the local asset shelf "
"settings"},
{ASSET_SHELF_TYPE_FLAG_ACTIVATE_FOR_CONTEXT_MENU,
"ACTIVATE_FOR_CONTEXT_MENU",
0,
"",
"When spawning a context menu for an asset, activate the asset and call "
"`bl_activate_operator` if present, rather than just highlighting the asset"},
{0, nullptr, 0, nullptr, nullptr},
};
@@ -2367,6 +2391,17 @@ static void rna_def_asset_shelf(BlenderRNA *brna)
"Activate Operator",
"Operator to call when activating an item with asset reference properties");
prop = RNA_def_property(srna, "bl_drag_operator", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop,
"rna_AssetShelf_drag_operator_get",
"rna_AssetShelf_drag_operator_length",
"rna_AssetShelf_drag_operator_set");
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(
prop,
"Drag Operator",
"Operator to call when dragging an item with asset reference properties");
prop = RNA_def_property(srna, "bl_default_preview_size", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "type->default_preview_size");
RNA_def_property_range(prop, 32, 256);