Blender V5.0
interface_template_shape_key_tree.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Foundation
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <fmt/format.h>
10
11#include "BKE_context.hh"
12#include "BKE_key.hh"
13#include "BKE_object.hh"
14
15#include "BLI_listbase.h"
16#include "BLT_translation.hh"
17
19#include "UI_tree_view.hh"
20
21#include "RNA_access.hh"
22#include "RNA_prototypes.hh"
23
24#include "DEG_depsgraph.hh"
25
26#include "DNA_key_types.h"
27
28#include "WM_api.hh"
29#include "WM_types.hh"
30
31#include "ED_undo.hh"
32
34
36 protected:
38
39 public:
41 {
42 is_flat_ = true;
43 };
44
45 void build_tree() override;
46};
47
54
56 private:
57 ShapeKey drag_key_;
58
59 public:
64
65 std::optional<eWM_DragDataType> get_drag_type() const override
66 {
67 return WM_DRAG_SHAPE_KEY;
68 }
69
70 void *create_drag_data() const override
71 {
72 int selected_count = [&]() -> int {
73 int count = 0;
74 LISTBASE_FOREACH (KeyBlock *, kb, &drag_key_.key->block) {
75 count += (kb->flag & KEYBLOCK_SEL) != 0;
76 }
77 return count;
78 }();
79
80 KeyBlock **selected_keys_ = MEM_calloc_arrayN<KeyBlock *>(selected_count,
81 "Selected Key Blocks");
82
83 selected_count = 0;
84 int index = 0;
85 LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &drag_key_.key->block, index) {
86 if (index == 0) {
87 /* Prevent basis shape key from dragging. */
88 continue;
89 }
90
91 if (kb->flag & KEYBLOCK_SEL) {
92 selected_keys_[selected_count] = kb;
93 selected_count++;
94 }
95 }
96 return selected_keys_;
97 }
98};
99
101 private:
102 KeyBlock &drop_kb_;
103 int drop_index_;
104
105 public:
107 ui::DropBehavior behavior,
108 KeyBlock &drop_kb,
109 int index)
110 : TreeViewItemDropTarget(item, behavior), drop_kb_(drop_kb), drop_index_(index)
111 {
112 }
113
114 bool can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const override
115 {
116 if (drag.type != WM_DRAG_SHAPE_KEY) {
117 return false;
118 }
119
120 const KeyBlock **drag_shapekey = static_cast<const KeyBlock **>(drag.poin);
121 if (!drag_shapekey || !drag_shapekey[0]) {
122 return false;
123 }
124
125 return true;
126 }
127
128 std::string drop_tooltip(const ui::DragInfo &drag_info) const override
129 {
130 const StringRef drag_name = TIP_("Selected Keys");
131 const StringRef drop_name = drop_kb_.name;
132
133 switch (drag_info.drop_location) {
136 break;
138 if (drop_index_ == 0) {
139 return TIP_("Cannot move above basis shape key");
140 }
141 return fmt::format(fmt::runtime(TIP_("Move {} above {}")), drag_name, drop_name);
143 return fmt::format(fmt::runtime(TIP_("Move {} below {}")), drag_name, drop_name);
144 default:
146 break;
147 }
148
149 return "";
150 }
151
152 bool on_drop(bContext *C, const ui::DragInfo &drag_info) const override
153 {
155 Key *key = BKE_key_from_object(ob);
156 const KeyBlock **drag_shapekey = static_cast<const KeyBlock **>(drag_info.drag_data.poin);
157
158 for (int8_t i = 0; drag_shapekey[i] != nullptr; i++) {
159 const int drag_index = BLI_findindex(&key->block, drag_shapekey[i]);
160 int drop_index = BLI_findindex(&key->block, &drop_kb_);
161
162 if (drag_index == -1) {
163 continue;
164 }
165
166 switch (drag_info.drop_location) {
169 break;
171 if (drop_index == 0) {
172 return false;
173 }
174 drop_index -= int(drag_index < drop_index);
175 break;
177 drop_index += int(drag_index > drop_index) + i;
178 break;
179 }
180
181 BKE_keyblock_move(ob, drag_index, drop_index);
182 }
183
184 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
186 ED_undo_push(C, "Drop Active Shape Key");
187
188 return true;
189 }
190};
191
193 private:
194 ShapeKey shape_key_;
195
196 public:
197 ShapeKeyItem(Object *object, Key *key, KeyBlock *kb, int index)
198 {
199 label_ = kb->name;
200 shape_key_.object = object;
201 shape_key_.key = key;
202 shape_key_.kb = kb;
203 shape_key_.index = index;
204 };
205
206 void build_row(uiLayout &row) override
207 {
208 uiItemL_ex(&row, this->label_, ICON_SHAPEKEY_DATA, false, false);
209 uiLayout *sub = &row.row(true);
210 sub->use_property_decorate_set(false);
212 &shape_key_.key->id, &RNA_ShapeKey, shape_key_.kb);
213
214 if (shape_key_.index > 0) {
215 sub->prop(&shapekey_ptr, "value", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
216 }
217
218 sub->prop(&shapekey_ptr, "mute", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
219 sub->prop(&shapekey_ptr, "lock_shape", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
220 if (shape_key_.kb->flag & KEYBLOCK_MUTE) {
221 row.active_set(false);
222 }
223 }
224
225 std::optional<bool> should_be_active() const override
226 {
227 return shape_key_.object->shapenr == shape_key_.index + 1;
228 }
229
230 void on_activate(bContext &C) override
231 {
233 &shape_key_.object->id, &RNA_Object, shape_key_.object);
234 PropertyRNA *prop = RNA_struct_find_property(&object_ptr, "active_shape_key_index");
235 RNA_property_int_set(&object_ptr, prop, shape_key_.index);
236 RNA_property_update(&C, &object_ptr, prop);
237
238 ED_undo_push(&C, "Set Active Shape Key");
239 }
240
241 std::optional<bool> should_be_selected() const override
242 {
243 return shape_key_.kb->flag & KEYBLOCK_SEL;
244 }
245
246 void set_selected(const bool select) override
247 {
248 AbstractViewItem::set_selected(select);
249 SET_FLAG_FROM_TEST(shape_key_.kb->flag, select, KEYBLOCK_SEL);
250 }
251
252 bool supports_renaming() const override
253 {
254 return true;
255 }
256
257 bool rename(const bContext &C, StringRefNull new_name) override
258 {
260 &shape_key_.key->id, &RNA_ShapeKey, shape_key_.kb);
261 RNA_string_set(&shapekey_ptr, "name", new_name.c_str());
262 ED_undo_push(const_cast<bContext *>(&C), "Rename shape key");
263 return true;
264 }
265
267 {
268 return label_;
269 }
270
271 void delete_item(bContext *C) override
272 {
273 Main *bmain = CTX_data_main(C);
274 BKE_object_shapekey_remove(bmain, shape_key_.object, shape_key_.kb);
275 DEG_id_tag_update(&shape_key_.object->id, ID_RECALC_GEOMETRY);
277 ED_undo_grouped_push(C, "Delete Shape Key");
278 }
279
280 void build_context_menu(bContext &C, uiLayout &layout) const override
281 {
282 MenuType *mt = WM_menutype_find("MESH_MT_shape_key_tree_context_menu", true);
283 if (!mt) {
284 return;
285 }
286 UI_menutype_draw(&C, mt, &layout);
287 }
288
289 std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override
290 {
291 return std::make_unique<ShapeKeyDragController>(
292 static_cast<ShapeKeyTreeView &>(get_tree_view()), shape_key_);
293 }
294
295 std::unique_ptr<ui::TreeViewItemDropTarget> create_drop_target() override
296 {
297 return std::make_unique<ShapeKeyDropTarget>(
298 *this, ui::DropBehavior::Reorder, *shape_key_.kb, shape_key_.index);
299 }
300};
301
303{
305 if (key == nullptr) {
306 return;
307 }
308 int index = 1;
309 LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &key->block, index) {
310 this->add_tree_item<ShapeKeyItem>(&object_, key, kb, index);
311 }
312}
313
315{
317 if (ob == nullptr) {
318 return;
319 }
320
321 uiBlock *block = layout->block();
322
324 *block,
325 "Shape Key Tree View",
326 std::make_unique<ed::object::shapekey::ShapeKeyTreeView>(*ob));
327 tree_view->set_context_menu_title("Shape Key");
328 tree_view->set_default_rows(4);
329 tree_view->allow_multiselect_items();
330
331 ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
332}
333} // namespace blender::ed::object::shapekey
Object * CTX_data_active_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
bool BKE_keyblock_move(Object *ob, int org_index, int new_index)
Definition key.cc:2274
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1791
General operations, lookup, etc. for blender objects.
bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define TIP_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ KEYBLOCK_SEL
@ KEYBLOCK_MUTE
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
void ED_undo_grouped_push(bContext *C, const char *str)
Definition ed_undo.cc:337
static AppView * view
#define C
Definition RandGen.cpp:29
blender::ui::AbstractGridView * UI_block_add_view(uiBlock &block, blender::StringRef idname, std::unique_ptr< blender::ui::AbstractGridView > grid_view)
@ UI_ITEM_R_ICON_ONLY
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
uiBut * uiItemL_ex(uiLayout *layout, blender::StringRef name, int icon, bool highlight, bool redalert)
#define ND_DRAW
Definition WM_types.hh:461
@ WM_DRAG_SHAPE_KEY
Definition WM_types.hh:1225
#define NC_OBJECT
Definition WM_types.hh:379
constexpr const char * c_str() const
std::optional< eWM_DragDataType > get_drag_type() const override
std::string drop_tooltip(const ui::DragInfo &drag_info) const override
bool on_drop(bContext *C, const ui::DragInfo &drag_info) const override
bool can_drop(const wmDrag &drag, const char **) const override
ShapeKeyDropTarget(ui::AbstractTreeViewItem &item, ui::DropBehavior behavior, KeyBlock &drop_kb, int index)
std::unique_ptr< ui::AbstractViewItemDragController > create_drag_controller() const override
bool rename(const bContext &C, StringRefNull new_name) override
void build_context_menu(bContext &C, uiLayout &layout) const override
std::unique_ptr< ui::TreeViewItemDropTarget > create_drop_target() override
ShapeKeyItem(Object *object, Key *key, KeyBlock *kb, int index)
Abstract base class for defining a customizable tree-view item.
AbstractTreeView & get_tree_view() const
Definition tree_view.cc:644
void set_default_rows(int default_rows)
Definition tree_view.cc:141
void set_context_menu_title(const std::string &title)
static void build_tree_view(const bContext &C, AbstractTreeView &tree_view, uiLayout &layout, bool add_box=true)
ItemT & add_tree_item(Args &&...args)
TreeViewItemDropTarget(AbstractTreeViewItem &view_item, DropBehavior behavior=DropBehavior::Insert)
Definition tree_view.cc:437
#define select(A, B, C)
int count
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void template_tree(uiLayout *layout, bContext *C)
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:414
char name[64]
ListBase block
const wmDrag & drag_data
const DropLocation drop_location
void use_property_decorate_set(bool is_sep)
uiBlock * block() const
void active_set(bool active)
uiLayout & row(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
eWM_DragDataType type
Definition WM_types.hh:1331
void * poin
Definition WM_types.hh:1332
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
MenuType * WM_menutype_find(const StringRef idname, bool quiet)