diff --git a/scripts/startup/bl_ui/properties_data_grease_pencil.py b/scripts/startup/bl_ui/properties_data_grease_pencil.py index ae803bfe7c6..fe05f33d9f5 100644 --- a/scripts/startup/bl_ui/properties_data_grease_pencil.py +++ b/scripts/startup/bl_ui/properties_data_grease_pencil.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later import bpy -from bpy.types import Panel +from bpy.types import Panel, Menu class DataButtonsPanel: @@ -32,6 +32,15 @@ class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel): layout.template_ID(space, "pin_id") +class GREASE_PENCIL_MT_grease_pencil_add_layer_extra(Menu): + bl_label = "Add Extra" + + def draw(self, context): + layout = self.layout + + layout.operator("grease_pencil.layer_group_add", text="Add Group") + + class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel): bl_label = "Layers" @@ -42,13 +51,17 @@ class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel): row.template_grease_pencil_layer_tree() col = row.column() - col.operator("grease_pencil.layer_add", icon='ADD', text="") + sub = col.column(align=True) + sub.operator("grease_pencil.layer_add", icon='ADD', text="") + sub.menu("GREASE_PENCIL_MT_grease_pencil_add_layer_extra", icon='DOWNARROW_HLT', text="") + col.operator("grease_pencil.layer_remove", icon='REMOVE', text="") classes = ( DATA_PT_context_grease_pencil, DATA_PT_grease_pencil_layers, + GREASE_PENCIL_MT_grease_pencil_add_layer_extra, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 44ddc9db263..c0a2305ccda 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -207,6 +207,12 @@ class Layer : public ::GreasePencilLayer { */ LayerGroup &parent_group() const; + /** + * \returns the layer as a `TreeNode`. + */ + const TreeNode &as_node() const; + TreeNode &as_node(); + /** * \returns the frames mapping. */ @@ -287,6 +293,12 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup { LayerGroup &add_group(LayerGroup *group); LayerGroup &add_group(StringRefNull name); + /** + * Adds a layer group after \a link and returns it. + */ + LayerGroup &add_group_after(LayerGroup *group, TreeNode *link); + LayerGroup &add_group_after(StringRefNull name, TreeNode *link); + /** * Adds a layer at the end of this group and returns it. */ @@ -359,6 +371,15 @@ inline LayerGroup &Layer::parent_group() const return this->base.parent->wrap(); } +inline const TreeNode &Layer::as_node() const +{ + return *reinterpret_cast(this); +} +inline TreeNode &Layer::as_node() +{ + return *reinterpret_cast(this); +} + namespace convert { void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf, diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 72c7aec88d6..2216cf576a7 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -631,6 +631,23 @@ LayerGroup &LayerGroup::add_group(StringRefNull name) return this->add_group(new_group); } +LayerGroup &LayerGroup::add_group_after(LayerGroup *group, TreeNode *link) +{ + BLI_assert(group != nullptr && link != nullptr); + BLI_insertlinkafter(&this->children, + reinterpret_cast(link), + reinterpret_cast(group)); + group->base.parent = reinterpret_cast(this); + this->tag_nodes_cache_dirty(); + return *group; +} + +LayerGroup &LayerGroup::add_group_after(StringRefNull name, TreeNode *link) +{ + LayerGroup *new_group = MEM_new(__func__, name); + return this->add_group_after(new_group, link); +} + Layer &LayerGroup::add_layer(Layer *layer) { BLI_assert(layer != nullptr); @@ -1245,7 +1262,7 @@ static blender::VectorSet get_node_names(GreasePencil &g return names; } -static bool check_unique_layer_cb(void *arg, const char *name) +static bool check_unique_node_cb(void *arg, const char *name) { using namespace blender; VectorSet &names = *reinterpret_cast *>(arg); @@ -1254,7 +1271,12 @@ static bool check_unique_layer_cb(void *arg, const char *name) static bool unique_layer_name(VectorSet &names, char *name) { - return BLI_uniquename_cb(check_unique_layer_cb, &names, "GP_Layer", '.', name, MAX_NAME); + return BLI_uniquename_cb(check_unique_node_cb, &names, "GP_Layer", '.', name, MAX_NAME); +} + +static bool unique_layer_group_name(VectorSet &names, char *name) +{ + return BLI_uniquename_cb(check_unique_node_cb, &names, "GP_Group", '.', name, MAX_NAME); } blender::bke::greasepencil::Layer &GreasePencil::add_layer( @@ -1284,6 +1306,34 @@ blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::String return this->add_layer(this->root_group.wrap(), name); } +blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group( + blender::bke::greasepencil::LayerGroup &group, const blender::StringRefNull name) +{ + using namespace blender; + VectorSet names = get_node_names(*this); + std::string unique_name(name.c_str()); + unique_layer_group_name(names, unique_name.data()); + return group.add_group(unique_name); +} + +blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group_after( + blender::bke::greasepencil::LayerGroup &group, + blender::bke::greasepencil::TreeNode *node, + const blender::StringRefNull name) +{ + using namespace blender; + VectorSet names = get_node_names(*this); + std::string unique_name(name.c_str()); + unique_layer_group_name(names, unique_name.data()); + return group.add_group_after(unique_name, node); +} + +blender::bke::greasepencil::LayerGroup &GreasePencil::add_layer_group( + const blender::StringRefNull name) +{ + return this->add_layer_group(this->root_group.wrap(), name); +} + const blender::bke::greasepencil::Layer *GreasePencil::find_layer_by_name( const blender::StringRefNull name) const { diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc index 3877b4066ae..ba0b8ccf62d 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc @@ -179,6 +179,57 @@ static void GREASE_PENCIL_OT_layer_reorder(wmOperatorType *ot) ot->srna, "location", prop_layer_reorder_location, LAYER_REORDER_ABOVE, "Location", ""); } +static int grease_pencil_layer_group_add_exec(bContext *C, wmOperator *op) +{ + using namespace blender::bke::greasepencil; + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + int new_layer_group_name_length; + char *new_layer_group_name = RNA_string_get_alloc( + op->ptr, "new_layer_group_name", nullptr, 0, &new_layer_group_name_length); + + if (grease_pencil.has_active_layer()) { + LayerGroup &active_group = grease_pencil.get_active_layer()->parent_group(); + grease_pencil.add_layer_group_after(active_group, + &grease_pencil.get_active_layer_for_write()->as_node(), + new_layer_group_name); + } + else { + grease_pencil.add_layer_group(new_layer_group_name); + } + + MEM_SAFE_FREE(new_layer_group_name); + + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil); + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_layer_group_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Layer Group"; + ot->idname = "GREASE_PENCIL_OT_layer_group_add"; + ot->description = "Add a new Grease Pencil layer group in the active object"; + + /* callbacks */ + ot->exec = grease_pencil_layer_group_add_exec; + ot->poll = active_grease_pencil_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop = RNA_def_string(ot->srna, + "new_layer_group_name", + "GP_Group", + INT16_MAX, + "Name", + "Name of the new layer group"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + ot->prop = prop; +} + } // namespace blender::ed::greasepencil void ED_operatortypes_grease_pencil_layers(void) @@ -187,4 +238,6 @@ void ED_operatortypes_grease_pencil_layers(void) WM_operatortype_append(GREASE_PENCIL_OT_layer_add); WM_operatortype_append(GREASE_PENCIL_OT_layer_remove); WM_operatortype_append(GREASE_PENCIL_OT_layer_reorder); + + WM_operatortype_append(GREASE_PENCIL_OT_layer_group_add); } diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index a687eb5a79b..74f1b2984b5 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -450,6 +450,14 @@ typedef struct GreasePencil { blender::bke::greasepencil::Layer *layer, blender::StringRefNull name); + blender::bke::greasepencil::LayerGroup &add_layer_group( + blender::bke::greasepencil::LayerGroup &group, blender::StringRefNull name); + blender::bke::greasepencil::LayerGroup &add_layer_group(blender::StringRefNull name); + blender::bke::greasepencil::LayerGroup &add_layer_group_after( + blender::bke::greasepencil::LayerGroup &group, + blender::bke::greasepencil::TreeNode *node, + blender::StringRefNull name); + const blender::bke::greasepencil::Layer *find_layer_by_name(blender::StringRefNull name) const; blender::bke::greasepencil::Layer *find_layer_by_name(blender::StringRefNull name);