diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index c232b2d5825..030ca30f4c7 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -4642,6 +4642,9 @@ def km_grease_pencil_edit_mode(params): # Active layer op_menu("GREASE_PENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Move to layer + op_menu("GREASE_PENCIL_MT_move_to_layer", {"type": 'M', "value": 'PRESS'}), + # Context menu *_template_items_context_menu("VIEW3D_MT_greasepencil_edit_context_menu", params.context_menu_event), diff --git a/scripts/startup/bl_ui/properties_data_grease_pencil.py b/scripts/startup/bl_ui/properties_data_grease_pencil.py index b683847299f..0d2499e911c 100644 --- a/scripts/startup/bl_ui/properties_data_grease_pencil.py +++ b/scripts/startup/bl_ui/properties_data_grease_pencil.py @@ -55,6 +55,7 @@ class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel): col = row.column() sub = col.column(align=True) + sub.operator_context = 'EXEC_DEFAULT' sub.operator("grease_pencil.layer_add", icon='ADD', text="") sub.menu("GREASE_PENCIL_MT_grease_pencil_add_layer_extra", icon='DOWNARROW_HLT', text="") diff --git a/scripts/startup/bl_ui/properties_grease_pencil_common.py b/scripts/startup/bl_ui/properties_grease_pencil_common.py index 07119e03da9..593e3a840dd 100644 --- a/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -274,6 +274,30 @@ class GPENCIL_MT_layer_active(Menu): i -= 1 +class GREASE_PENCIL_MT_move_to_layer(Menu): + bl_label = "Move to Layer" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + grease_pencil = context.active_object.data + + layout.operator("grease_pencil.move_to_layer", text="New Layer", icon='ADD').add_new_layer = True + + if not grease_pencil.layers: + return + + layout.separator() + + for i in range(len(grease_pencil.layers) - 1, -1, -1): + layer = grease_pencil.layers[i] + if layer == grease_pencil.layers.active: + icon = 'GREASEPENCIL' + else: + icon = 'NONE' + layout.operator("grease_pencil.move_to_layer", text=layer.name, icon=icon).target_layer_name = layer.name + + class GREASE_PENCIL_MT_layer_active(Menu): bl_label = "Change Active Layer" @@ -942,6 +966,7 @@ classes = ( GPENCIL_UL_layer, GPENCIL_UL_masks, + GREASE_PENCIL_MT_move_to_layer, GREASE_PENCIL_MT_layer_active, GreasePencilFlipTintColors, diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 69fc5159ed7..474ce48bebd 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -5855,6 +5855,7 @@ class VIEW3D_MT_edit_greasepencil_stroke(Menu): layout.separator() + layout.menu("GREASE_PENCIL_MT_move_to_layer") layout.menu("VIEW3D_MT_grease_pencil_assign_material") layout.operator("grease_pencil.set_active_material") diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index eea98187958..ce81159532c 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -13,6 +13,7 @@ #include "BLI_math_vector_types.hh" #include "BLI_span.hh" #include "BLI_stack.hh" +#include "BLI_string.h" #include "BLT_translation.h" #include "DNA_material_types.h" @@ -34,13 +35,13 @@ #include "ED_curves.hh" #include "ED_grease_pencil.hh" +#include "ED_screen.hh" +#include "GEO_join_geometries.hh" #include "GEO_reorder.hh" #include "GEO_smooth_curves.hh" #include "GEO_subdivide_curves.hh" -#include "WM_api.hh" - #include "UI_resources.hh" namespace blender::ed::greasepencil { @@ -1693,6 +1694,122 @@ static void GREASE_PENCIL_OT_stroke_reorder(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Move To Layer Operator + * \{ */ + +static int grease_pencil_move_to_layer_exec(bContext *C, wmOperator *op) +{ + using namespace blender::bke; + using namespace bke::greasepencil; + const Scene *scene = CTX_data_scene(C); + bool changed = false; + + Object *object = CTX_data_active_object(C); + GreasePencil &grease_pencil = *static_cast(object->data); + + int target_layer_name_length; + char *target_layer_name = nullptr; + BLI_SCOPED_DEFER([&] { MEM_SAFE_FREE(target_layer_name); }); + const bool add_new_layer = RNA_boolean_get(op->ptr, "add_new_layer"); + if (add_new_layer) { + Layer &new_layer = grease_pencil.add_layer("Layer"); + target_layer_name = BLI_strdup_null(new_layer.name().c_str()); + } + else { + target_layer_name = RNA_string_get_alloc( + op->ptr, "target_layer_name", nullptr, 0, &target_layer_name_length); + } + + TreeNode *target_node = grease_pencil.find_node_by_name(target_layer_name); + if (target_node == nullptr) { + BKE_reportf(op->reports, RPT_ERROR, "There is no layer '%s'", target_layer_name); + return OPERATOR_CANCELLED; + } + + Layer *layer_dst = &target_node->as_layer(); + if (layer_dst->is_locked()) { + BKE_reportf(op->reports, RPT_ERROR, "'%s' Layer is locked", target_layer_name); + return OPERATOR_CANCELLED; + } + + /* Iterate through all the drawings at current scene frame. */ + const Array drawings_src = retrieve_editable_drawings(*scene, grease_pencil); + for (const MutableDrawingInfo &info : drawings_src) { + bke::CurvesGeometry &curves_src = info.drawing.strokes_for_write(); + IndexMaskMemory memory; + const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves_src, memory); + if (selected_strokes.is_empty()) { + continue; + } + + if (!layer_dst->has_drawing_at(info.frame_number)) { + /* Move geometry to a new drawing in target layer. */ + grease_pencil.insert_blank_frame(*layer_dst, info.frame_number, 0, BEZT_KEYTYPE_KEYFRAME); + Drawing &drawing_dst = *grease_pencil.get_editable_drawing_at(*layer_dst, info.frame_number); + drawing_dst.strokes_for_write() = bke::curves_copy_curve_selection( + curves_src, selected_strokes, {}); + + curves_src.remove_curves(selected_strokes, {}); + + drawing_dst.tag_topology_changed(); + } + else { + /* Append geometry to drawing in target layer. */ + Drawing &drawing_dst = *grease_pencil.get_editable_drawing_at(*layer_dst, info.frame_number); + bke::CurvesGeometry selected_elems = curves_copy_curve_selection( + curves_src, selected_strokes, {}); + Curves *selected_curves = bke::curves_new_nomain(std::move(selected_elems)); + Curves *layer_curves = bke::curves_new_nomain(std::move(drawing_dst.strokes_for_write())); + std::array geometry_sets{GeometrySet::from_curves(selected_curves), + GeometrySet::from_curves(layer_curves)}; + GeometrySet joined = geometry::join_geometries(geometry_sets, {}); + drawing_dst.strokes_for_write() = std::move(joined.get_curves_for_write()->geometry.wrap()); + + curves_src.remove_curves(selected_strokes, {}); + + drawing_dst.tag_topology_changed(); + } + + info.drawing.tag_topology_changed(); + changed = true; + } + + if (changed) { + /* updates */ + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, nullptr); + } + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_move_to_layer(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers. */ + ot->name = "Move to Layer"; + ot->idname = "GREASE_PENCIL_OT_move_to_layer"; + ot->description = "Move selected strokes to another layer"; + + /* callbacks. */ + ot->exec = grease_pencil_move_to_layer_exec; + ot->poll = editable_grease_pencil_poll; + + /* flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_string( + ot->srna, "target_layer_name", "Layer", INT16_MAX, "Name", "Target Grease Pencil Layer"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean( + ot->srna, "add_new_layer", false, "New Layer", "Move selection to a new layer"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/** \} */ + } // namespace blender::ed::greasepencil void ED_operatortypes_grease_pencil_edit() @@ -1715,4 +1832,5 @@ void ED_operatortypes_grease_pencil_edit() WM_operatortype_append(GREASE_PENCIL_OT_clean_loose); WM_operatortype_append(GREASE_PENCIL_OT_stroke_subdivide); WM_operatortype_append(GREASE_PENCIL_OT_stroke_reorder); + WM_operatortype_append(GREASE_PENCIL_OT_move_to_layer); } 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 3fbd8684a7a..fe168226817 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc @@ -76,13 +76,14 @@ static void GREASE_PENCIL_OT_layer_add(wmOperatorType *ot) ot->description = "Add a new Grease Pencil layer in the active object"; /* callbacks */ + ot->invoke = WM_operator_props_popup_confirm; ot->exec = grease_pencil_layer_add_exec; ot->poll = active_grease_pencil_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop = RNA_def_string( - ot->srna, "new_layer_name", nullptr, INT16_MAX, "Name", "Name of the new layer"); + ot->srna, "new_layer_name", "Layer", INT16_MAX, "Name", "Name of the new layer"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); ot->prop = prop; } diff --git a/source/blender/editors/include/ED_grease_pencil.hh b/source/blender/editors/include/ED_grease_pencil.hh index 50382821fba..ee2439191ab 100644 --- a/source/blender/editors/include/ED_grease_pencil.hh +++ b/source/blender/editors/include/ED_grease_pencil.hh @@ -16,6 +16,8 @@ #include "ED_keyframes_edit.hh" +#include "WM_api.hh" + struct bContext; struct Main; struct Object;