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
This commit is contained in:
Amelie Fondevilla
2023-07-26 17:59:09 +02:00
committed by Falk David
parent 0fab1f4f68
commit 0c07fb50c8
9 changed files with 147 additions and 7 deletions

View File

@@ -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<blender::bke::greasepencil::TreeNode *>(this);

View File

@@ -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<Layer *>(ale->data);
ACHANNEL_SET_FLAG(&(layer->base), sel, GP_LAYER_TREE_NODE_SELECT);
break;
}
case ANIMTYPE_GPLAYER: {
bGPDlayer *gpl = (bGPDlayer *)ale->data;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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<blender::bke::greasepencil::Layer *>(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<blender::bke::greasepencil::Layer *>(ale->data)))
{
sel = SELECT_SUBTRACT;
}
break;
}
else {
if (ANIM_fcurve_keyframes_loop(
&ked, static_cast<FCurve *>(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<MaskLayer *>(ale->data), sel);
}
else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) {
blender::ed::greasepencil::select_all_frames(
static_cast<blender::bke::greasepencil::Layer *>(ale->data), sel);
ale->update |= ANIM_UPDATE_DEPS;
}
else {
ANIM_fcurve_keyframes_loop(
&ked, static_cast<FCurve *>(ale->key_data), nullptr, sel_cb, nullptr);
@@ -1627,6 +1647,11 @@ static void actkeys_mselect_single(bAnimContext *ac,
ED_gpencil_select_frame(static_cast<bGPDlayer *>(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<blender::bke::greasepencil::Layer *>(ale->data), selx, select_mode);
ale->update |= ANIM_UPDATE_DEPS;
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
ED_mask_select_frame(static_cast<MaskLayer *>(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<MaskLayer *>(ale->data), selx, select_mode);
}
else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) {
blender::ed::greasepencil::select_frame_at(
static_cast<blender::bke::greasepencil::Layer *>(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<MaskLayer *>(ale->data), select_mode);
}
else if (ale->type == ANIMTYPE_GREASE_PENCIL_LAYER) {
blender::ed::greasepencil::select_all_frames(
static_cast<blender::bke::greasepencil::Layer *>(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<GreasePencil *>(ale->id), static_cast<Layer *>(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<bGPDlayer *>(ale->data);

View File

@@ -367,6 +367,9 @@ static void createTransActionData(bContext *C, TransInfo *t)
adt_count = count_gplayer_frames(
static_cast<bGPDlayer *>(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<MaskLayer *>(ale->data), t->frame_side, cfra, is_prop_edit);

View File

@@ -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;