diff --git a/scripts/startup/bl_ui/space_dopesheet.py b/scripts/startup/bl_ui/space_dopesheet.py index db06b44c138..96eb6ffd7b3 100644 --- a/scripts/startup/bl_ui/space_dopesheet.py +++ b/scripts/startup/bl_ui/space_dopesheet.py @@ -582,6 +582,7 @@ class DOPESHEET_MT_key(Menu): def draw(self, _context): layout = self.layout + ob = _context.active_object layout.menu("DOPESHEET_MT_key_transform", text="Transform") @@ -600,6 +601,8 @@ class DOPESHEET_MT_key(Menu): layout.operator("action.paste", text="Paste Flipped").flipped = True layout.operator("action.duplicate_move") layout.operator("action.delete") + if ob and ob.type == 'GREASEPENCIL': + layout.operator("grease_pencil.delete_breakdown") layout.separator() layout.operator_menu_enum("action.keyframe_type", "type", text="Keyframe Type") @@ -785,6 +788,7 @@ class DOPESHEET_MT_context_menu(Menu): if st.mode == 'GPENCIL': layout.separator() + layout.operator("grease_pencil.delete_breakdown") layout.operator_context = 'EXEC_REGION_WIN' layout.operator("action.delete") diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc index f350c403dc3..87cf1e0d053 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc @@ -906,6 +906,84 @@ static void GREASE_PENCIL_OT_active_frame_delete(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all", false, "Delete all", "Delete active keyframes of all layers"); } +bool grease_pencil_active_breakdown_frame_poll(bContext *C) +{ + if (!active_grease_pencil_poll(C)) { + return false; + } + const Object &ob = *CTX_data_active_object(C); + const Scene &scene = *CTX_data_scene(C); + + /* Ensure that there is a breakdown keyframe visible at the current frame. */ + const GreasePencil &grease_pencil = *static_cast(ob.data); + if (const bke::greasepencil::Layer *active_layer = grease_pencil.get_active_layer()) { + const GreasePencilFrame *frame = active_layer->frame_at(scene.r.cfra); + if (frame && frame->type == BEZT_KEYTYPE_BREAKDOWN) { + return true; + } + } + return false; +} + +static int grease_pencil_delete_breakdown_frames_exec(bContext *C, wmOperator * /*op*/) +{ + const Object &ob = *CTX_data_active_object(C); + const Scene &scene = *CTX_data_scene(C); + GreasePencil &grease_pencil = *static_cast(ob.data); + bke::greasepencil::Layer *active_layer = grease_pencil.get_active_layer(); + const int current_frame = active_layer->start_frame_at(scene.r.cfra).value(); + + const Span sorted_keys = active_layer->sorted_keys(); + const int curr_frame_index = sorted_keys.first_index(current_frame); + Vector frame_numbers_to_remove; + + for (int i = curr_frame_index; i <= sorted_keys.size(); i++) { + int frame_number = sorted_keys[i]; + GreasePencilFrame *frame = active_layer->frame_at(frame_number); + if (frame && frame->type == BEZT_KEYTYPE_BREAKDOWN) { + frame_numbers_to_remove.append(frame_number); + continue; + } + break; + } + for (int i = curr_frame_index - 1; i >= 0; i--) { + int frame_number = sorted_keys[i]; + GreasePencilFrame *frame = active_layer->frame_at(frame_number); + if (frame && frame->type == BEZT_KEYTYPE_BREAKDOWN) { + frame_numbers_to_remove.append(frame_number); + continue; + } + break; + } + + if (frame_numbers_to_remove.is_empty()) { + return OPERATOR_CANCELLED; + } + + grease_pencil.remove_frames(*active_layer, frame_numbers_to_remove); + + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr); + + return OPERATOR_FINISHED; +} + +static void GREASE_PENCIL_OT_delete_breakdown(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Breakdown Frames"; + ot->idname = "GREASE_PENCIL_OT_delete_breakdown"; + ot->description = + "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; + + /* callback */ + ot->exec = grease_pencil_delete_breakdown_frames_exec; + ot->poll = grease_pencil_active_breakdown_frame_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + } // namespace blender::ed::greasepencil void ED_operatortypes_grease_pencil_frames() @@ -915,4 +993,5 @@ void ED_operatortypes_grease_pencil_frames() WM_operatortype_append(GREASE_PENCIL_OT_frame_clean_duplicate); WM_operatortype_append(GREASE_PENCIL_OT_frame_duplicate); WM_operatortype_append(GREASE_PENCIL_OT_active_frame_delete); + WM_operatortype_append(GREASE_PENCIL_OT_delete_breakdown); }