From 0c07fb50c8b54e6db980b672bd64fbbc781bfdda Mon Sep 17 00:00:00 2001 From: Amelie Fondevilla Date: Wed, 26 Jul 2023 17:59:09 +0200 Subject: [PATCH] GPv3: Keyframe on-click selection Implementation of the click-selection operator of keyframes in the dopesheet, along with its alternatives : deselect all when no frames is hit, extend selection with shift, column selection with alt, select all keyframes on current channel with ctrl+alt. Includes the new following API functions : * `select_frame_at` : selects a frame in a layer at a specific time (if such frame exists), * `select_all_frames` : selects all frames of a layer, * `select_layer_channel` : selects a layer, and sets it as active (if layer is not null, otherwise the active layer is set null), * `layer_has_frame_selected` : checks if any of the frames in the layer is selected. Pull Request: https://projects.blender.org/blender/blender/pulls/110492 --- .../blender/blenkernel/BKE_grease_pencil.hh | 5 ++ .../editors/animation/anim_channels_edit.cc | 7 +++ .../blender/editors/animation/anim_filter.cc | 10 ++-- .../intern/grease_pencil_frames.cc | 49 +++++++++++++++++++ .../intern/grease_pencil_layers.cc | 14 ++++++ .../editors/include/ED_grease_pencil.h | 19 +++++++ .../editors/space_action/action_select.cc | 46 ++++++++++++++++- .../transform/transform_convert_action.cc | 3 ++ .../makesdna/DNA_grease_pencil_types.h | 1 + 9 files changed, 147 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index 9d37b333fb1..8f365becf17 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -499,6 +499,11 @@ inline bool GreasePencilFrame::is_implicit_hold() const return (this->flag & GP_FRAME_IMPLICIT_HOLD) != 0; } +inline bool GreasePencilFrame::is_selected() const +{ + return (this->flag & GP_FRAME_SELECTED) != 0; +} + inline blender::bke::greasepencil::TreeNode &GreasePencilLayerTreeNode::wrap() { return *reinterpret_cast(this); diff --git a/source/blender/editors/animation/anim_channels_edit.cc b/source/blender/editors/animation/anim_channels_edit.cc index 7aeb259bcda..3feab5b9dc8 100644 --- a/source/blender/editors/animation/anim_channels_edit.cc +++ b/source/blender/editors/animation/anim_channels_edit.cc @@ -32,6 +32,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_gpencil_legacy.h" +#include "BKE_grease_pencil.hh" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_mask.h" @@ -593,6 +594,12 @@ static void anim_channels_select_set(bAnimContext *ac, } break; } + case ANIMTYPE_GREASE_PENCIL_LAYER: { + using namespace blender::bke::greasepencil; + Layer *layer = static_cast(ale->data); + ACHANNEL_SET_FLAG(&(layer->base), sel, GP_LAYER_TREE_NODE_SELECT); + break; + } case ANIMTYPE_GPLAYER: { bGPDlayer *gpl = (bGPDlayer *)ale->data; diff --git a/source/blender/editors/animation/anim_filter.cc b/source/blender/editors/animation/anim_filter.cc index 140a587bc2f..d0d6ac6ad48 100644 --- a/source/blender/editors/animation/anim_filter.cc +++ b/source/blender/editors/animation/anim_filter.cc @@ -1872,12 +1872,12 @@ static size_t animdata_filter_grease_pencil_data(ListBase *anim_data, /* Add data block container (if for drawing, and it contains sub-channels). */ ANIMCHANNEL_NEW_CHANNEL( grease_pencil, ANIMTYPE_GREASE_PENCIL_DATABLOCK, grease_pencil, nullptr); - - /* Add the list of collected channels. */ - BLI_movelisttolist(anim_data, &tmp_data); - BLI_assert(BLI_listbase_is_empty(&tmp_data)); - items += tmp_items; } + + /* Add the list of collected channels. */ + BLI_movelisttolist(anim_data, &tmp_data); + BLI_assert(BLI_listbase_is_empty(&tmp_data)); + items += tmp_items; } return items; 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 45f13fc512b..317c69e7606 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_frames.cc @@ -6,6 +6,8 @@ * \ingroup edgreasepencil */ +#include "BLI_map.hh" + #include "BKE_context.h" #include "BKE_grease_pencil.hh" @@ -14,6 +16,7 @@ #include "DNA_scene_types.h" #include "ED_grease_pencil.h" +#include "ED_keyframes_edit.h" #include "RNA_access.h" #include "RNA_define.h" @@ -22,6 +25,52 @@ namespace blender::ed::greasepencil { +static void select_frame(GreasePencilFrame &frame, const short select_mode) +{ + switch (select_mode) { + case SELECT_ADD: + frame.flag |= GP_FRAME_SELECTED; + break; + case SELECT_SUBTRACT: + frame.flag &= ~GP_FRAME_SELECTED; + break; + case SELECT_INVERT: + frame.flag ^= GP_FRAME_SELECTED; + break; + } +} + +bool select_frame_at(bke::greasepencil::Layer *layer, + const int frame_number, + const short select_mode) +{ + + GreasePencilFrame *frame = layer->frames_for_write().lookup_ptr(frame_number); + if (frame == nullptr) { + return false; + } + select_frame(*frame, select_mode); + return true; +} + +void select_all_frames(bke::greasepencil::Layer *layer, const short select_mode) +{ + for (auto item : layer->frames_for_write().items()) { + select_frame(item.value, select_mode); + } +} + +bool layer_has_any_frame_selected(const bke::greasepencil::Layer *layer) +{ + for (auto item : layer->frames().items()) { + const GreasePencilFrame &frame = item.value; + if (frame.is_selected()) { + return true; + } + } + return false; +} + static int insert_blank_frame_exec(bContext *C, wmOperator *op) { using namespace blender::bke::greasepencil; 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 25da7edde40..6b0a56ba246 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_layers.cc @@ -22,6 +22,20 @@ namespace blender::ed::greasepencil { +void select_layer_channel(GreasePencil *grease_pencil, bke::greasepencil::Layer *layer) +{ + using namespace blender::bke::greasepencil; + + if (layer != nullptr) { + layer->base.flag |= GP_LAYER_TREE_NODE_SELECT; + } + + if (grease_pencil->active_layer != layer) { + grease_pencil->set_active_layer(layer); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, &grease_pencil); + } +} + static int grease_pencil_layer_add_exec(bContext *C, wmOperator *op) { using namespace blender::bke::greasepencil; diff --git a/source/blender/editors/include/ED_grease_pencil.h b/source/blender/editors/include/ED_grease_pencil.h index 670c20a4224..f0d11877ca1 100644 --- a/source/blender/editors/include/ED_grease_pencil.h +++ b/source/blender/editors/include/ED_grease_pencil.h @@ -50,11 +50,30 @@ eAttrDomain ED_grease_pencil_selection_domain_get(struct bContext *C); #ifdef __cplusplus +# include "BKE_grease_pencil.hh" + # include "BLI_generic_span.hh" # include "BLI_math_matrix_types.hh" namespace blender::ed::greasepencil { +void select_layer_channel(GreasePencil *grease_pencil, bke::greasepencil::Layer *layer); + +/** + * Sets the selection flag, according to \a selection_mode to the frame at \a frame_number in the + * \a layer if such frame exists. Returns false if no such frame exists. + */ +bool select_frame_at(bke::greasepencil::Layer *layer, + const int frame_number, + const short select_mode); + +void select_all_frames(bke::greasepencil::Layer *layer, const short select_mode); + +/** + * Returns true if any frame of the \a layer is selected. + */ +bool layer_has_any_frame_selected(const bke::greasepencil::Layer *layer); + bool active_grease_pencil_poll(bContext *C); bool editable_grease_pencil_poll(bContext *C); bool editable_grease_pencil_point_selection_poll(bContext *C); diff --git a/source/blender/editors/space_action/action_select.cc b/source/blender/editors/space_action/action_select.cc index 83189e50033..728be773779 100644 --- a/source/blender/editors/space_action/action_select.cc +++ b/source/blender/editors/space_action/action_select.cc @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_gpencil_legacy.h" +#include "BKE_grease_pencil.hh" #include "BKE_nla.h" #include "UI_interface.h" @@ -37,6 +38,7 @@ #include "ED_anim_api.h" #include "ED_gpencil_legacy.h" +#include "ED_grease_pencil.h" #include "ED_keyframes_edit.h" #include "ED_keyframes_keylist.h" #include "ED_markers.h" @@ -129,6 +131,11 @@ static void actkeys_list_element_to_keylist(bAnimContext *ac, bActionGroup *agrp = (bActionGroup *)ale->data; agroup_to_keylist(adt, agrp, keylist, 0); } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + /* TODO: why don't we just give grease pencil layers key_data too? */ + grease_pencil_cels_to_keylist( + adt, static_cast(ale->data), keylist, 0); + } else if (ale->type == ANIMTYPE_GPLAYER) { /* TODO: why don't we just give gplayers key_data too? */ bGPDlayer *gpl = (bGPDlayer *)ale->data; @@ -273,6 +280,14 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) break; } } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + if (blender::ed::greasepencil::layer_has_any_frame_selected( + static_cast(ale->data))) + { + sel = SELECT_SUBTRACT; + } + break; + } else { if (ANIM_fcurve_keyframes_loop( &ked, static_cast(ale->key_data), nullptr, test_cb, nullptr)) @@ -296,6 +311,11 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frame_select_set(static_cast(ale->data), sel); } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + blender::ed::greasepencil::select_all_frames( + static_cast(ale->data), sel); + ale->update |= ANIM_UPDATE_DEPS; + } else { ANIM_fcurve_keyframes_loop( &ked, static_cast(ale->key_data), nullptr, sel_cb, nullptr); @@ -1627,6 +1647,11 @@ static void actkeys_mselect_single(bAnimContext *ac, ED_gpencil_select_frame(static_cast(ale->data), selx, select_mode); ale->update |= ANIM_UPDATE_DEPS; } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + blender::ed::greasepencil::select_frame_at( + static_cast(ale->data), selx, select_mode); + ale->update |= ANIM_UPDATE_DEPS; + } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(static_cast(ale->data), selx, select_mode); } @@ -1692,6 +1717,11 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(static_cast(ale->data), selx, select_mode); } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + blender::ed::greasepencil::select_frame_at( + static_cast(ale->data), selx, select_mode); + ale->update |= ANIM_UPDATE_DEPS; + } else { AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -1731,6 +1761,11 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frames(static_cast(ale->data), select_mode); } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + blender::ed::greasepencil::select_all_frames( + static_cast(ale->data), select_mode); + ale->update |= ANIM_UPDATE_DEPS; + } else { if (ale->type == ANIMTYPE_SUMMARY && ale->datatype == ALE_ALL) { ListBase anim_data = {nullptr, nullptr}; @@ -1836,10 +1871,17 @@ static int mouse_action_keys(bAnimContext *ac, } } else if (ac->datatype == ANIMCONT_GPENCIL) { - /* deselect all other channels first */ + /* Deselect all other channels first. */ ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); - /* Highlight GPencil Layer */ + /* Highlight the grease pencil channel, and set the corresponding layer as active. */ + if (ale != nullptr && ale->data != nullptr && ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + using namespace blender::bke::greasepencil; + blender::ed::greasepencil::select_layer_channel( + reinterpret_cast(ale->id), static_cast(ale->data)); + } + + /* Highlight GPencil Layer (Legacy). */ if (ale != nullptr && ale->data != nullptr && ale->type == ANIMTYPE_GPLAYER) { bGPdata *gpd = (bGPdata *)ale->id; bGPDlayer *gpl = static_cast(ale->data); diff --git a/source/blender/editors/transform/transform_convert_action.cc b/source/blender/editors/transform/transform_convert_action.cc index ebe3d7e6af4..84bd2b79b83 100644 --- a/source/blender/editors/transform/transform_convert_action.cc +++ b/source/blender/editors/transform/transform_convert_action.cc @@ -367,6 +367,9 @@ static void createTransActionData(bContext *C, TransInfo *t) adt_count = count_gplayer_frames( static_cast(ale->data), t->frame_side, cfra, is_prop_edit); } + else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) { + /* GPv3: To be implemented. */ + } else if (ale->type == ANIMTYPE_MASKLAYER) { adt_count = count_masklayer_frames( static_cast(ale->data), t->frame_side, cfra, is_prop_edit); diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index 68b05a56b1c..5018e74ef83 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -156,6 +156,7 @@ typedef struct GreasePencilFrame { static GreasePencilFrame null(); bool is_null() const; bool is_implicit_hold() const; + bool is_selected() const; #endif } GreasePencilFrame;