Blender V4.3
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
9#include "BKE_context.hh"
10#include "BKE_grease_pencil.hh"
11
12#include "BLT_translation.hh"
13
14#include "DEG_depsgraph.hh"
15
16#include "UI_interface.hh"
17#include "UI_tree_view.hh"
18
19#include "RNA_access.hh"
20#include "RNA_prototypes.hh"
21
22#include "ED_grease_pencil.hh"
23#include "ED_undo.hh"
24
25#include "WM_api.hh"
26#include "WM_message.hh"
27
28#include <fmt/format.h>
29
31
32using namespace blender::bke::greasepencil;
33
35 public:
36 explicit LayerTreeView(GreasePencil &grease_pencil) : grease_pencil_(grease_pencil) {}
37
38 void build_tree() override;
39
40 private:
41 void build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node);
42 GreasePencil &grease_pencil_;
43};
44
46 TreeNode &drop_tree_node_;
47
48 public:
50 : TreeViewItemDropTarget(item, behavior), drop_tree_node_(drop_tree_node)
51 {
52 }
53
54 bool can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const override
55 {
57 return false;
58 }
59
60 wmDragGreasePencilLayer *active_drag_node = static_cast<wmDragGreasePencilLayer *>(drag.poin);
61 if (active_drag_node->node->wrap().is_layer()) {
62 return true;
63 }
64
65 LayerGroup &group = active_drag_node->node->wrap().as_group();
66 if (drop_tree_node_.is_child_of(group)) {
67 /* Don't drop group node into its child node. */
68 return false;
69 }
70 return true;
71 }
72
73 std::string drop_tooltip(const DragInfo &drag_info) const override
74 {
75 const wmDragGreasePencilLayer *drag_grease_pencil =
76 static_cast<const wmDragGreasePencilLayer *>(drag_info.drag_data.poin);
77 TreeNode &drag_node = drag_grease_pencil->node->wrap();
78
79 const StringRef drag_name = drag_node.name();
80 const StringRef drop_name = drop_tree_node_.name();
81 const StringRef node_type = drag_node.is_layer() ? "layer" : "group";
82
83 switch (drag_info.drop_location) {
85 return fmt::format(TIP_("Move {} {} into {}"), node_type, drag_name, drop_name);
87 return fmt::format(TIP_("Move {} {} above {}"), node_type, drag_name, drop_name);
89 return fmt::format(TIP_("Move {} {} below {}"), node_type, drag_name, drop_name);
90 default:
92 break;
93 }
94
95 return "";
96 }
97
98 bool on_drop(bContext *C, const DragInfo &drag_info) const override
99 {
100 const wmDragGreasePencilLayer *drag_grease_pencil =
101 static_cast<const wmDragGreasePencilLayer *>(drag_info.drag_data.poin);
102 GreasePencil &grease_pencil = *drag_grease_pencil->grease_pencil;
103 TreeNode &drag_node = drag_grease_pencil->node->wrap();
104
105 if (!drop_tree_node_.parent_group()) {
106 /* Root node is not added to the tree view, so there should never be a drop target for this.
107 */
109 return false;
110 }
111
112 if (&drop_tree_node_ == &drag_node) {
113 return false;
114 }
115
116 switch (drag_info.drop_location) {
117 case DropLocation::Into: {
118 BLI_assert_msg(drop_tree_node_.is_group(),
119 "Inserting should not be possible for layers, only for groups, because "
120 "only groups use DropBehavior::Reorder_and_Insert");
121 LayerGroup &drop_group = drop_tree_node_.as_group();
122 grease_pencil.move_node_into(drag_node, drop_group);
123 break;
124 }
126 /* Draw order is inverted, so inserting before (above) means inserting the node after. */
127 grease_pencil.move_node_after(drag_node, drop_tree_node_);
128 break;
129 }
130 case DropLocation::After: {
131 /* Draw order is inverted, so inserting after (below) means inserting the node before. */
132 grease_pencil.move_node_before(drag_node, drop_tree_node_);
133 break;
134 }
135 default: {
137 return false;
138 }
139 }
140
141 if (drag_node.is_layer()) {
143 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3Layers, active);
145 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3, layers);
146 }
147 else if (drag_node.is_group()) {
149 &grease_pencil.id,
150 &grease_pencil,
151 GreasePencilv3LayerGroup,
152 active);
154 CTX_wm_message_bus(C), &grease_pencil.id, &grease_pencil, GreasePencilv3, layer_groups);
155 }
156
157 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
159 return true;
160 }
161};
162
164 GreasePencil &grease_pencil_;
165 TreeNode &dragged_node_;
166
167 public:
169 GreasePencil &grease_pencil,
172 grease_pencil_(grease_pencil),
173 dragged_node_(node.wrap())
174 {
175 }
176
178 {
179 if (dragged_node_.wrap().is_layer()) {
181 }
183 }
184
185 void *create_drag_data() const override
186 {
187 wmDragGreasePencilLayer *drag_data = MEM_cnew<wmDragGreasePencilLayer>(__func__);
188 drag_data->node = &dragged_node_;
189 drag_data->grease_pencil = &grease_pencil_;
190 return drag_data;
191 }
192
193 void on_drag_start() override
194 {
195 grease_pencil_.set_active_node(&dragged_node_);
196 }
197};
198
200 public:
201 LayerViewItem(GreasePencil &grease_pencil, Layer &layer)
202 : grease_pencil_(grease_pencil), layer_(layer)
203 {
204 this->label_ = layer.name();
205 }
206
207 void build_row(uiLayout &row) override
208 {
209 build_layer_name(row);
210
211 uiLayout *sub = uiLayoutRow(&row, true);
212 uiLayoutSetPropDecorate(sub, false);
213
214 build_layer_buttons(*sub);
215 }
216
217 bool supports_collapsing() const override
218 {
219 /* This is a bit redundant since `LayerViewItem` can't have children.
220 * But being explicit might catch errors. */
221 return false;
222 }
223
224 std::optional<bool> should_be_active() const override
225 {
226 if (this->grease_pencil_.has_active_layer()) {
227 return reinterpret_cast<GreasePencilLayer *>(&layer_) ==
228 reinterpret_cast<GreasePencilLayer *>(this->grease_pencil_.get_active_layer());
229 }
230 return {};
231 }
232
233 void on_activate(bContext &C) override
234 {
235 PointerRNA layers_ptr = RNA_pointer_create(
236 &grease_pencil_.id, &RNA_GreasePencilv3Layers, nullptr);
237 PointerRNA value_ptr = RNA_pointer_create(&grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
238
239 PropertyRNA *prop = RNA_struct_find_property(&layers_ptr, "active");
240
241 if (grease_pencil_.has_active_group()) {
243 &grease_pencil_.id,
244 &grease_pencil_,
245 GreasePencilv3LayerGroup,
246 active);
247 }
248
249 RNA_property_pointer_set(&layers_ptr, prop, value_ptr, nullptr);
250 RNA_property_update(&C, &layers_ptr, prop);
251
252 ED_undo_push(&C, "Active Grease Pencil Layer");
253 }
254
255 bool supports_renaming() const override
256 {
257 return true;
258 }
259
260 bool rename(const bContext &C, StringRefNull new_name) override
261 {
262 PointerRNA layer_ptr = RNA_pointer_create(&grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
263 PropertyRNA *prop = RNA_struct_find_property(&layer_ptr, "name");
264
265 RNA_property_string_set(&layer_ptr, prop, new_name.c_str());
266 RNA_property_update(&const_cast<bContext &>(C), &layer_ptr, prop);
267
268 ED_undo_push(&const_cast<bContext &>(C), "Rename Grease Pencil Layer");
269 return true;
270 }
271
273 {
274 return layer_.name();
275 }
276
277 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
278 {
279 return std::make_unique<LayerViewItemDragController>(
280 static_cast<LayerTreeView &>(get_tree_view()), grease_pencil_, layer_.base);
281 }
282
283 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
284 {
285 return std::make_unique<LayerNodeDropTarget>(*this, layer_.as_node(), DropBehavior::Reorder);
286 }
287
288 private:
289 GreasePencil &grease_pencil_;
290 Layer &layer_;
291
292 void build_layer_name(uiLayout &row)
293 {
294 uiBut *but = uiItemL_ex(
295 &row, layer_.name().c_str(), ICON_OUTLINER_DATA_GP_LAYER, false, false);
296 if (!layer_.is_editable()) {
297 UI_but_disable(but, "Layer is locked or not visible");
298 }
299 }
300
301 void build_layer_buttons(uiLayout &row)
302 {
303 uiLayout *sub;
304 PointerRNA layer_ptr = RNA_pointer_create(&grease_pencil_.id, &RNA_GreasePencilLayer, &layer_);
305
306 sub = uiLayoutRow(&row, true);
307 uiLayoutSetActive(sub, layer_.parent_group().use_masks());
308 uiItemR(sub, &layer_ptr, "use_masks", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
309
310 sub = uiLayoutRow(&row, true);
311 uiLayoutSetActive(sub, layer_.parent_group().use_onion_skinning());
312 uiItemR(sub, &layer_ptr, "use_onion_skinning", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
313
314 sub = uiLayoutRow(&row, true);
315 uiLayoutSetActive(sub, layer_.parent_group().is_visible());
316 uiItemR(sub, &layer_ptr, "hide", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
317
318 sub = uiLayoutRow(&row, true);
319 uiLayoutSetActive(sub, !layer_.parent_group().is_locked());
320 uiItemR(sub, &layer_ptr, "lock", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
321 }
322};
323
325 public:
327 : grease_pencil_(grease_pencil), group_(group)
328 {
329 this->label_ = group_.name();
330 }
331
332 void build_row(uiLayout &row) override
333 {
334 build_layer_group_name(row);
335
336 uiLayout *sub = uiLayoutRow(&row, true);
337 uiLayoutSetPropDecorate(sub, false);
338
339 build_layer_group_buttons(*sub);
340 }
341
342 std::optional<bool> should_be_active() const override
343 {
344 if (this->grease_pencil_.has_active_group()) {
345 return &group_ == this->grease_pencil_.get_active_group();
346 }
347 return {};
348 }
349
350 void build_context_menu(bContext &C, uiLayout &layout) const override
351 {
352 MenuType *mt = WM_menutype_find("GREASE_PENCIL_MT_group_context_menu", true);
353 if (!mt) {
354 return;
355 }
356 UI_menutype_draw(&C, mt, &layout);
357 }
358
359 void on_activate(bContext &C) override
360 {
361 PointerRNA grease_pencil_ptr = RNA_pointer_create(
362 &grease_pencil_.id, &RNA_GreasePencilv3LayerGroup, nullptr);
363 PointerRNA value_ptr = RNA_pointer_create(
364 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
365
366 PropertyRNA *prop = RNA_struct_find_property(&grease_pencil_ptr, "active");
367
368 if (grease_pencil_.has_active_layer()) {
370 &grease_pencil_.id,
371 &grease_pencil_,
372 GreasePencilv3Layers,
373 active);
374 }
375
376 RNA_property_pointer_set(&grease_pencil_ptr, prop, value_ptr, nullptr);
377 RNA_property_update(&C, &grease_pencil_ptr, prop);
378
379 ED_undo_push(&C, "Active Grease Pencil Group");
380 }
381
382 bool supports_renaming() const override
383 {
384 return true;
385 }
386
387 bool rename(const bContext &C, StringRefNull new_name) override
388 {
389 PointerRNA group_ptr = RNA_pointer_create(
390 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
391 PropertyRNA *prop = RNA_struct_find_property(&group_ptr, "name");
392
393 RNA_property_string_set(&group_ptr, prop, new_name.c_str());
394 RNA_property_update(&const_cast<bContext &>(C), &group_ptr, prop);
395
396 ED_undo_push(&const_cast<bContext &>(C), "Rename Grease Pencil Layer Group");
397 return true;
398 }
399
401 {
402 return group_.name();
403 }
404
405 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override
406 {
407 return std::make_unique<LayerViewItemDragController>(
408 static_cast<LayerTreeView &>(get_tree_view()), grease_pencil_, group_.base);
409 }
410
411 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override
412 {
413 return std::make_unique<LayerNodeDropTarget>(
414 *this, group_.as_node(), DropBehavior::ReorderAndInsert);
415 }
416
417 private:
418 GreasePencil &grease_pencil_;
419 LayerGroup &group_;
420
421 void build_layer_group_name(uiLayout &row)
422 {
423 short icon = ICON_GREASEPENCIL_LAYER_GROUP;
424 if (group_.color_tag != LAYERGROUP_COLOR_NONE) {
425 icon = ICON_LAYERGROUP_COLOR_01 + group_.color_tag;
426 }
427
428 uiBut *but = uiItemL_ex(&row, group_.name().c_str(), icon, false, false);
429 if (!group_.is_editable()) {
430 UI_but_disable(but, "Layer Group is locked or not visible");
431 }
432 }
433
434 void build_layer_group_buttons(uiLayout &row)
435 {
436 uiLayout *sub;
437 PointerRNA group_ptr = RNA_pointer_create(
438 &grease_pencil_.id, &RNA_GreasePencilLayerGroup, &group_);
439
440 sub = uiLayoutRow(&row, true);
441 if (group_.as_node().parent_group()) {
442 uiLayoutSetActive(sub, group_.as_node().parent_group()->use_masks());
443 }
444 const int icon_mask = (group_.base.flag & GP_LAYER_TREE_NODE_HIDE_MASKS) == 0 ?
445 ICON_CLIPUV_DEHLT :
446 ICON_CLIPUV_HLT;
447 uiItemR(sub, &group_ptr, "use_masks", UI_ITEM_R_ICON_ONLY, nullptr, icon_mask);
448
449 sub = uiLayoutRow(&row, true);
450 if (group_.as_node().parent_group()) {
451 uiLayoutSetActive(sub, group_.as_node().parent_group()->use_onion_skinning());
452 }
453 uiItemR(sub, &group_ptr, "use_onion_skinning", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
454
455 sub = uiLayoutRow(&row, true);
456 if (group_.as_node().parent_group()) {
457 uiLayoutSetActive(sub, group_.as_node().parent_group()->is_visible());
458 }
459 uiItemR(sub, &group_ptr, "hide", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
460
461 sub = uiLayoutRow(&row, true);
462 if (group_.as_node().parent_group()) {
463 uiLayoutSetActive(sub, !group_.as_node().parent_group()->is_locked());
464 }
465 uiItemR(sub, &group_ptr, "lock", UI_ITEM_R_ICON_ONLY, nullptr, ICON_NONE);
466 }
467};
468
469void LayerTreeView::build_tree_node_recursive(TreeViewOrItem &parent, TreeNode &node)
470{
471 using namespace blender::bke::greasepencil;
472 if (node.is_layer()) {
473 parent.add_tree_item<LayerViewItem>(this->grease_pencil_, node.as_layer());
474 }
475 else if (node.is_group()) {
476 LayerGroupViewItem &group_item = parent.add_tree_item<LayerGroupViewItem>(this->grease_pencil_,
477 node.as_group());
478 group_item.uncollapse_by_default();
479 LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, node_, &node.as_group().children) {
480 build_tree_node_recursive(group_item, node_->wrap());
481 }
482 }
483}
484
486{
487 using namespace blender::bke::greasepencil;
489 GreasePencilLayerTreeNode *, node, &this->grease_pencil_.root_group_ptr->children)
490 {
491 this->build_tree_node_recursive(*this, node->wrap());
492 }
493}
494
495} // namespace blender::ui::greasepencil
496
498{
499 using namespace blender;
500
502
503 if (grease_pencil == nullptr) {
504 return;
505 }
506
507 uiBlock *block = uiLayoutGetBlock(layout);
508
510 *block,
511 "Grease Pencil Layer Tree View",
512 std::make_unique<blender::ui::greasepencil::LayerTreeView>(*grease_pencil));
513 tree_view->set_context_menu_title("Grease Pencil Layer");
514 tree_view->set_default_rows(6);
515
516 ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
517}
wmMsgBus * CTX_wm_message_bus(const bContext *C)
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#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:1041
@ LAYERGROUP_COLOR_NONE
@ GP_LAYER_TREE_NODE_HIDE_MASKS
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
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 uiLayoutSetActive(uiLayout *layout, bool active)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C)
uiBut * uiItemL_ex(uiLayout *layout, const char *name, int icon, bool highlight, bool redalert)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_ICON_ONLY
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
eWM_DragDataType
Definition WM_types.hh:1152
@ WM_DRAG_GREASE_PENCIL_LAYER
Definition WM_types.hh:1173
@ WM_DRAG_GREASE_PENCIL_GROUP
Definition WM_types.hh:1174
constexpr const char * c_str() const
const LayerGroup & parent_group() const
const LayerGroup & as_group() 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:565
void set_default_rows(int default_rows)
Definition tree_view.cc:129
void set_context_menu_title(const std::string &title)
static void build_tree_view(AbstractTreeView &tree_view, uiLayout &layout, std::optional< StringRef > search_string={}, bool add_box=true)
Definition tree_view.cc:918
std::unique_ptr< AbstractViewItemDragController > create_drag_controller() const 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
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)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
GreasePencilLayerTreeNode base
GreasePencilLayerTreeNode base
const wmDrag & drag_data
const DropLocation drop_location
GreasePencil * grease_pencil
Definition WM_types.hh:1231
GreasePencilLayerTreeNode * node
Definition WM_types.hh:1232
eWM_DragDataType type
Definition WM_types.hh:1282
void * poin
Definition WM_types.hh:1283
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
MenuType * WM_menutype_find(const char *idname, bool quiet)
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)