Blender V4.5
interface_template_grease_pencil_layer_tree.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10
11#include "BKE_context.hh"
12#include "BKE_grease_pencil.hh"
13
14#include "BLT_translation.hh"
15
16#include "DEG_depsgraph.hh"
17
18#include "UI_interface.hh"
19#include "UI_tree_view.hh"
20
21#include "RNA_access.hh"
22#include "RNA_prototypes.hh"
23
24#include "ED_grease_pencil.hh"
25#include "ED_undo.hh"
26
27#include "WM_api.hh"
28#include "WM_message.hh"
29
30#include <fmt/format.h>
31
33
34using namespace blender::bke::greasepencil;
35
37 public:
38 explicit LayerTreeView(GreasePencil &grease_pencil) : grease_pencil_(grease_pencil) {}
39
40 void build_tree() override;
41
42 private:
43 void build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node);
44 GreasePencil &grease_pencil_;
45};
46
48 TreeNode &drop_tree_node_;
49
50 public:
52 : TreeViewItemDropTarget(item, behavior), drop_tree_node_(drop_tree_node)
53 {
54 }
55
56 bool can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const override
57 {
59 return false;
60 }
61
62 wmDragGreasePencilLayer *active_drag_node = static_cast<wmDragGreasePencilLayer *>(drag.poin);
63 if (active_drag_node->node->wrap().is_layer()) {
64 return true;
65 }
66
67 LayerGroup &group = active_drag_node->node->wrap().as_group();
68 if (drop_tree_node_.is_child_of(group)) {
69 /* Don't drop group node into its child node. */
70 return false;
71 }
72 return true;
73 }
74
75 std::string drop_tooltip(const DragInfo &drag_info) const override
76 {
77 const wmDragGreasePencilLayer *drag_grease_pencil =
78 static_cast<const wmDragGreasePencilLayer *>(drag_info.drag_data.poin);
79 TreeNode &drag_node = drag_grease_pencil->node->wrap();
80
81 const StringRef drag_name = drag_node.name();
82 const StringRef drop_name = drop_tree_node_.name();
83 const StringRef node_type = drag_node.is_layer() ? "layer" : "group";
84
85 switch (drag_info.drop_location) {
87 return fmt::format(
88 fmt::runtime(TIP_("Move {} {} into {}")), node_type, drag_name, drop_name);
90 return fmt::format(
91 fmt::runtime(TIP_("Move {} {} above {}")), node_type, drag_name, drop_name);
93 return fmt::format(
94 fmt::runtime(TIP_("Move {} {} below {}")), node_type, drag_name, drop_name);
95 default:
97 break;
98 }
99
100 return "";
101 }
102
103 bool on_drop(bContext *C, const DragInfo &drag_info) const override
104 {
105 const wmDragGreasePencilLayer *drag_grease_pencil =
106 static_cast<const wmDragGreasePencilLayer *>(drag_info.drag_data.poin);
107 GreasePencil &grease_pencil = *drag_grease_pencil->grease_pencil;
108 TreeNode &drag_node = drag_grease_pencil->node->wrap();
109
110 if (!drop_tree_node_.parent_group()) {
111 /* Root node is not added to the tree view, so there should never be a drop target for this.
112 */
114 return false;
115 }
116
117 if (&drop_tree_node_ == &drag_node) {
118 return false;
119 }
120
121 switch (drag_info.drop_location) {
122 case DropLocation::Into: {
123 BLI_assert_msg(drop_tree_node_.is_group(),
124 "Inserting should not be possible for layers, only for groups, because "
125 "only groups use DropBehavior::Reorder_and_Insert");
126 LayerGroup &drop_group = drop_tree_node_.as_group();
127 grease_pencil.move_node_into(drag_node, drop_group);
128 break;
129 }
131 /* Draw order is inverted, so inserting before (above) means inserting the node after. */
132 grease_pencil.move_node_after(drag_node, drop_tree_node_);
133 break;
134 }
135 case DropLocation::After: {
136 /* Draw order is inverted, so inserting after (below) means inserting the node before. */
137 grease_pencil.move_node_before(drag_node, drop_tree_node_);
138 break;
139 }
140 default: {
142 return false;
143 }
144 }
145
146 if (drag_node.is_layer()) {
148 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
150 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3, layers);
151 }
152 else if (drag_node.is_group()) {
154 &grease_pencil.id,
155 &grease_pencil,
156 GreasePencilv3LayerGroup,
157 active);
159 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3, layer_groups);
160 }
161
162 ED_undo_push(C, "Reorder Layers");
163
164 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
166 return true;
167 }
168};
169
171 GreasePencil &grease_pencil_;
172 TreeNode &dragged_node_;
173
174 public:
176 GreasePencil &grease_pencil,
179 grease_pencil_(grease_pencil),
180 dragged_node_(node.wrap())
181 {
182 }
183
185 {
186 if (dragged_node_.wrap().is_layer()) {
188 }
190 }
191
192 void *create_drag_data() const override
193 {
195 drag_data->node = &dragged_node_;
196 drag_data->grease_pencil = &grease_pencil_;
197 return drag_data;
198 }
199
200 void on_drag_start() override
201 {
202 grease_pencil_.set_active_node(&dragged_node_);
203 }
204};
205
207 public:
208 LayerViewItem(GreasePencil &grease_pencil, Layer &layer)
209 : grease_pencil_(grease_pencil), layer_(layer)
210 {
211 this->label_ = layer.name();
212 }
213
214 void build_row(uiLayout &row) override
215 {
216 build_layer_name(row);
217
218 uiLayout *sub = &row.row(true);
219 uiLayoutSetPropDecorate(sub, false);
220
221 build_layer_buttons(*sub);
222 }
223
224 bool supports_collapsing() const override
225 {
226 /* This is a bit redundant since `LayerViewItem` can't have children.
227 * But being explicit might catch errors. */
228 return false;
229 }
230
231 std::optional<bool> should_be_active() const override
232 {
233 if (this->grease_pencil_.has_active_layer()) {
234 return reinterpret_cast<GreasePencilLayer *>(&layer_) ==
235 reinterpret_cast<GreasePencilLayer *>(this->grease_pencil_.get_active_layer());
236 }
237 return {};
238 }
239
240 void on_activate(bContext &C) override
241 {
243 &grease_pencil_.id, &RNA_GreasePencilv3Layers, nullptr);
245 &grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
246
247 PropertyRNA *prop = RNA_struct_find_property(&layers_ptr, "active");
248
249 if (grease_pencil_.has_active_group()) {
251 &grease_pencil_.id,
252 &grease_pencil_,
253 GreasePencilv3LayerGroup,
254 active);
255 }
256
257 RNA_property_pointer_set(&layers_ptr, prop, value_ptr, nullptr);
258 RNA_property_update(&C, &layers_ptr, prop);
259
260 ED_undo_push(&C, "Active Grease Pencil Layer");
261 }
262
263 bool supports_renaming() const override
264 {
265 return true;
266 }
267
268 bool rename(const bContext &C, StringRefNull new_name) override
269 {
271 &grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
272 PropertyRNA *prop = RNA_struct_find_property(&layer_ptr, "name");
273
274 RNA_property_string_set(&layer_ptr, prop, new_name.c_str());
275 RNA_property_update(&const_cast<bContext &>(C), &layer_ptr, prop);
276
277 ED_undo_push(&const_cast<bContext &>(C), "Rename Grease Pencil Layer");
278 return true;
279 }
280
282 {
283 return layer_.name();
284 }
285
286 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
287 {
288 return std::make_unique<LayerViewItemDragController>(
289 static_cast<LayerTreeView &>(get_tree_view()), grease_pencil_, layer_.base);
290 }
291
292 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
293 {
294 return std::make_unique<LayerNodeDropTarget>(*this, layer_.as_node(), DropBehavior::Reorder);
295 }
296
297 private:
298 GreasePencil &grease_pencil_;
299 Layer &layer_;
300
301 void build_layer_name(uiLayout &row)
302 {
303 uiBut *but = uiItemL_ex(
304 &row, layer_.name().c_str(), ICON_OUTLINER_DATA_GP_LAYER, false, false);
305
306 if (ID_IS_LINKED(&grease_pencil_)) {
308 }
309 else if (!layer_.is_editable()) {
310 UI_but_disable(but, "Layer is locked or not visible");
311 }
312 }
313
314 void build_layer_buttons(uiLayout &row)
315 {
316 uiLayout *sub;
318 &grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
319
320 sub = &row.row(true);
321 uiLayoutSetActive(sub, layer_.parent_group().use_masks());
322 sub->prop(&layer_ptr, "use_masks", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
323
324 sub = &row.row(true);
325 uiLayoutSetActive(sub, layer_.parent_group().use_onion_skinning());
326 sub->prop(&layer_ptr, "use_onion_skinning", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
327
328 sub = &row.row(true);
329 uiLayoutSetActive(sub, layer_.parent_group().is_visible());
330 sub->prop(&layer_ptr, "hide", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
331
332 sub = &row.row(true);
333 uiLayoutSetActive(sub, !layer_.parent_group().is_locked());
334 sub->prop(&layer_ptr, "lock", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
335 }
336};
337
339 public:
341 : grease_pencil_(grease_pencil), group_(group)
342 {
343 this->label_ = group_.name();
344 }
345
346 std::optional<bool> should_be_collapsed() const override
347 {
348 const bool is_collapsed = !group_.is_expanded();
349 return is_collapsed;
350 }
351
352 bool set_collapsed(const bool collapsed) override
353 {
354 if (!AbstractTreeViewItem::set_collapsed(collapsed)) {
355 return false;
356 }
357 group_.set_expanded(!collapsed);
358 return true;
359 }
360
361 void on_collapse_change(bContext &C, const bool is_collapsed) override
362 {
363 const bool is_expanded = !is_collapsed;
364
365 /* Let RNA handle the property change. This makes sure all the notifiers and DEG
366 * update calls are properly called. */
368 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
369 PropertyRNA *prop = RNA_struct_find_property(&group_ptr, "is_expanded");
370
371 RNA_property_boolean_set(&group_ptr, prop, is_expanded);
372 RNA_property_update(&C, &group_ptr, prop);
373 }
374
375 void build_row(uiLayout &row) override
376 {
377 build_layer_group_name(row);
378
379 uiLayout *sub = &row.row(true);
380 uiLayoutSetPropDecorate(sub, false);
381
382 build_layer_group_buttons(*sub);
383 }
384
385 std::optional<bool> should_be_active() const override
386 {
387 if (this->grease_pencil_.has_active_group()) {
388 return &group_ == this->grease_pencil_.get_active_group();
389 }
390 return {};
391 }
392
393 void build_context_menu(bContext &C, uiLayout &layout) const override
394 {
395 MenuType *mt = WM_menutype_find("GREASE_PENCIL_MT_group_context_menu", true);
396 if (!mt) {
397 return;
398 }
399 UI_menutype_draw(&C, mt, &layout);
400 }
401
402 void on_activate(bContext &C) override
403 {
404 PointerRNA grease_pencil_ptr = RNA_pointer_create_discrete(
405 &grease_pencil_.id, &RNA_GreasePencilv3LayerGroup, nullptr);
407 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
408
409 PropertyRNA *prop = RNA_struct_find_property(&grease_pencil_ptr, "active");
410
411 if (grease_pencil_.has_active_layer()) {
413 &grease_pencil_.id,
414 &grease_pencil_,
415 GreasePencilv3Layers,
416 active);
417 }
418
419 RNA_property_pointer_set(&grease_pencil_ptr, prop, value_ptr, nullptr);
420 RNA_property_update(&C, &grease_pencil_ptr, prop);
421
422 ED_undo_push(&C, "Active Grease Pencil Group");
423 }
424
425 bool supports_renaming() const override
426 {
427 return true;
428 }
429
430 bool rename(const bContext &C, StringRefNull new_name) override
431 {
433 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
434 PropertyRNA *prop = RNA_struct_find_property(&group_ptr, "name");
435
436 RNA_property_string_set(&group_ptr, prop, new_name.c_str());
437 RNA_property_update(&const_cast<bContext &>(C), &group_ptr, prop);
438
439 ED_undo_push(&const_cast<bContext &>(C), "Rename Grease Pencil Layer Group");
440 return true;
441 }
442
444 {
445 return group_.name();
446 }
447
448 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
449 {
450 return std::make_unique<LayerViewItemDragController>(
451 static_cast<LayerTreeView &>(get_tree_view()), grease_pencil_, group_.base);
452 }
453
454 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
455 {
456 return std::make_unique<LayerNodeDropTarget>(
457 *this, group_.as_node(), DropBehavior::ReorderAndInsert);
458 }
459
460 private:
461 GreasePencil &grease_pencil_;
462 LayerGroup &group_;
463
464 void build_layer_group_name(uiLayout &row)
465 {
466 short icon = ICON_GREASEPENCIL_LAYER_GROUP;
467 if (group_.color_tag != LAYERGROUP_COLOR_NONE) {
468 icon = ICON_LAYERGROUP_COLOR_01 + group_.color_tag;
469 }
470
471 uiBut *but = uiItemL_ex(&row, group_.name(), icon, false, false);
472 if (ID_IS_LINKED(&grease_pencil_)) {
474 }
475 else if (!group_.is_editable()) {
476 UI_but_disable(but, "Layer Group is locked or not visible");
477 }
478 }
479
480 void build_layer_group_buttons(uiLayout &row)
481 {
482 uiLayout *sub;
483 PointerRNA group_ptr = RNA_pointer_create_discrete(
484 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
485
486 sub = &row.row(true);
487 if (group_.as_node().parent_group()) {
488 uiLayoutSetActive(sub, group_.as_node().parent_group()->use_masks());
489 }
490 sub->prop(&group_ptr, "use_masks", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
491
492 sub = &row.row(true);
493 if (group_.as_node().parent_group()) {
494 uiLayoutSetActive(sub, group_.as_node().parent_group()->use_onion_skinning());
495 }
496 sub->prop(&group_ptr, "use_onion_skinning", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
497
498 sub = &row.row(true);
499 if (group_.as_node().parent_group()) {
500 uiLayoutSetActive(sub, group_.as_node().parent_group()->is_visible());
501 }
502 sub->prop(&group_ptr, "hide", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
503
504 sub = &row.row(true);
505 if (group_.as_node().parent_group()) {
506 uiLayoutSetActive(sub, !group_.as_node().parent_group()->is_locked());
507 }
508 sub->prop(&group_ptr, "lock", UI_ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
509 }
510};
511
512void LayerTreeView::build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node)
513{
514 using namespace blender::bke::greasepencil;
515 if (node.is_layer()) {
516 parent.add_tree_item<LayerViewItem>(this->grease_pencil_, node.as_layer());
517 }
518 else if (node.is_group()) {
519 LayerGroupViewItem &group_item = parent.add_tree_item<LayerGroupViewItem>(this->grease_pencil_,
520 node.as_group());
522 build_tree_node_recursive(group_item, node_->wrap());
523 }
524 }
525}
526
528{
529 using namespace blender::bke::greasepencil;
531 GreasePencilLayerTreeNode *, node, &this->grease_pencil_.root_group_ptr->children)
532 {
533 this->build_tree_node_recursive(*this, node->wrap());
534 }
535}
536
537} // namespace blender::ui::greasepencil
538
540{
541 using namespace blender;
542
544
545 if (grease_pencil == nullptr) {
546 return;
547 }
548
549 uiBlock *block = uiLayoutGetBlock(layout);
550
552 *block,
553 "Grease Pencil Layer Tree View",
554 std::make_unique<blender::ui::greasepencil::LayerTreeView>(*grease_pencil));
555 tree_view->set_context_menu_title("Grease Pencil Layer");
556 tree_view->set_default_rows(6);
557
558 ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
559}
wmMsgBus * CTX_wm_message_bus(const bContext *C)
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
#define ELEM(...)
#define TIP_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ LAYERGROUP_COLOR_NONE
struct GreasePencilLayerTreeNode GreasePencilLayerTreeNode
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
#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)
void UI_but_disable(uiBut *but, const char *disabled_hint)
void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C)
@ UI_BUT_DISABLED
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_ITEM_R_ICON_ONLY
void uiLayoutSetActive(uiLayout *layout, bool active)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiBut * uiItemL_ex(uiLayout *layout, blender::StringRef name, int icon, bool highlight, bool redalert)
#define NA_EDITED
Definition WM_types.hh:581
#define NC_GPENCIL
Definition WM_types.hh:396
eWM_DragDataType
Definition WM_types.hh:1197
@ WM_DRAG_GREASE_PENCIL_LAYER
Definition WM_types.hh:1218
@ WM_DRAG_GREASE_PENCIL_GROUP
Definition WM_types.hh:1219
bool is_group() const
bool is_layer() const
const LayerGroup & as_group() const
const Layer & as_layer() const
constexpr const char * c_str() const
const LayerGroup & parent_group() const
Abstract base class for defining a customizable tree-view item.
AbstractTreeView & get_tree_view() const
Definition tree_view.cc:620
virtual bool set_collapsed(bool collapsed)
Definition tree_view.cc:691
void set_default_rows(int default_rows)
Definition tree_view.cc:139
void set_context_menu_title(const std::string &title)
static void build_tree_view(const bContext &C, AbstractTreeView &tree_view, uiLayout &layout, std::optional< StringRef > search_string={}, bool add_box=true)
Definition tree_view.cc:980
TreeViewItemDropTarget(AbstractTreeViewItem &view_item, DropBehavior behavior=DropBehavior::Insert)
Definition tree_view.cc:394
std::unique_ptr< AbstractViewItemDragController > create_drag_controller() const override
void on_collapse_change(bContext &C, const bool is_collapsed) override
void build_context_menu(bContext &C, uiLayout &layout) const override
bool rename(const bContext &C, StringRefNull new_name) override
LayerGroupViewItem(GreasePencil &grease_pencil, LayerGroup &group)
std::unique_ptr< TreeViewItemDropTarget > create_drop_target() override
std::string drop_tooltip(const DragInfo &drag_info) const override
bool can_drop(const wmDrag &drag, const char **) const override
LayerNodeDropTarget(AbstractTreeViewItem &item, TreeNode &drop_tree_node, DropBehavior behavior)
bool on_drop(bContext *C, const DragInfo &drag_info) const override
LayerViewItemDragController(LayerTreeView &tree_view, GreasePencil &grease_pencil, GreasePencilLayerTreeNode &node)
bool rename(const bContext &C, StringRefNull new_name) override
std::unique_ptr< AbstractViewItemDragController > create_drag_controller() const override
std::unique_ptr< TreeViewItemDropTarget > create_drop_target() override
#define active
#define ID_IS_LINKED(_id)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
GreasePencil * from_context(bContext &C)
TreeViewItemContainer TreeViewOrItem
float wrap(float value, float max, float min)
Definition node_math.h:71
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
GreasePencilLayerTreeGroup * root_group_ptr
const wmDrag & drag_data
const DropLocation drop_location
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)
GreasePencil * grease_pencil
Definition WM_types.hh:1276
GreasePencilLayerTreeNode * node
Definition WM_types.hh:1277
eWM_DragDataType type
Definition WM_types.hh:1327
void * poin
Definition WM_types.hh:1328
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
MenuType * WM_menutype_find(const StringRef idname, bool quiet)
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)