GPv3: Initial Layer Tree UI #109197
|
@ -22,6 +22,7 @@ _modules = [
|
|||
"properties_data_curves",
|
||||
"properties_data_empty",
|
||||
"properties_data_gpencil",
|
||||
"properties_data_grease_pencil",
|
||||
"properties_data_light",
|
||||
"properties_data_lattice",
|
||||
"properties_data_mesh",
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
class DataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return hasattr(context, "grease_pencil") and context.grease_pencil
|
||||
|
||||
|
||||
class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
grease_pencil = context.grease_pencil
|
||||
space = context.space_data
|
||||
|
||||
if ob:
|
||||
layout.template_ID(ob, "data")
|
||||
elif grease_pencil:
|
||||
layout.template_ID(space, "pin_id")
|
||||
|
||||
class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel):
|
||||
bl_label = "Layers"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.template_grease_pencil_layer_tree()
|
||||
|
||||
col = row.column()
|
||||
col.operator("grease_pencil.layer_add", icon='ADD', text="")
|
||||
col.operator("grease_pencil.layer_remove", icon='REMOVE', text="")
|
||||
|
||||
|
||||
classes = (
|
||||
DATA_PT_context_grease_pencil,
|
||||
DATA_PT_grease_pencil_layers,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
|
@ -183,10 +183,16 @@ class Layer : public ::GreasePencilLayer {
|
|||
Layer(const Layer &other);
|
||||
~Layer();
|
||||
|
||||
StringRefNull name() const
|
||||
{
|
||||
return this->base.name;
|
||||
}
|
||||
/**
|
||||
* \returns the layer name.
|
||||
*/
|
||||
StringRefNull name() const;
|
||||
void set_name(StringRefNull new_name);
|
||||
|
||||
/**
|
||||
* \returns the parent layer group.
|
||||
*/
|
||||
LayerGroup &parent_group() const;
|
||||
|
||||
/**
|
||||
* \returns the frames mapping.
|
||||
|
@ -257,6 +263,11 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
|
|||
~LayerGroup();
|
||||
|
||||
public:
|
||||
StringRefNull name() const
|
||||
{
|
||||
return this->base.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a group at the end of this group.
|
||||
*/
|
||||
|
@ -269,6 +280,18 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
|
|||
Layer &add_layer(Layer *layer);
|
||||
Layer &add_layer(StringRefNull name);
|
||||
|
||||
/**
|
||||
* Adds a layer before \a link and returns it.
|
||||
*/
|
||||
Layer &add_layer_before(Layer *layer, Layer *link);
|
||||
Layer &add_layer_before(StringRefNull name, Layer *link);
|
||||
|
||||
/**
|
||||
* Adds a layer after \a link and returns it.
|
||||
*/
|
||||
Layer &add_layer_after(Layer *layer, Layer *link);
|
||||
Layer &add_layer_after(StringRefNull name, Layer *link);
|
||||
|
||||
/**
|
||||
* Returns the number of direct nodes in this group.
|
||||
*/
|
||||
|
@ -285,6 +308,12 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
|
|||
*/
|
||||
void remove_child(int64_t index);
|
||||
|
||||
/**
|
||||
* Tries to unlink the layer from the list of nodes in this group.
|
||||
* \returns true, if the layer was successfully unlinked.
|
||||
*/
|
||||
bool unlink_layer(Layer *link);
|
||||
|
||||
/**
|
||||
* Returns a `Span` of pointers to all the `TreeNode`s in this group.
|
||||
*/
|
||||
|
@ -313,6 +342,16 @@ class LayerGroup : public ::GreasePencilLayerTreeGroup {
|
|||
void tag_nodes_cache_dirty() const;
|
||||
};
|
||||
|
||||
inline StringRefNull Layer::name() const
|
||||
{
|
||||
return this->base.name;
|
||||
}
|
||||
|
||||
inline LayerGroup &Layer::parent_group() const
|
||||
{
|
||||
return this->base.parent->wrap();
|
||||
}
|
||||
|
||||
namespace convert {
|
||||
|
||||
void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include "BLI_span.hh"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
|
@ -41,7 +44,7 @@
|
|||
using blender::float3;
|
||||
using blender::Span;
|
||||
using blender::uint3;
|
||||
using blender::Vector;
|
||||
using blender::VectorSet;
|
||||
|
||||
static void grease_pencil_init_data(ID *id)
|
||||
{
|
||||
|
@ -108,8 +111,8 @@ static void grease_pencil_copy_data(Main * /*bmain*/,
|
|||
|
||||
/* Set active layer. */
|
||||
if (grease_pencil_src->has_active_layer()) {
|
||||
grease_pencil_dst->active_layer = grease_pencil_dst->find_layer_by_name(
|
||||
grease_pencil_src->active_layer->wrap().name());
|
||||
grease_pencil_dst->set_active_layer(
|
||||
grease_pencil_dst->find_layer_by_name(grease_pencil_src->active_layer->wrap().name()));
|
||||
}
|
||||
|
||||
/* Make sure the runtime pointer exists. */
|
||||
|
@ -384,6 +387,11 @@ Layer::~Layer()
|
|||
this->runtime = nullptr;
|
||||
}
|
||||
|
||||
void Layer::set_name(StringRefNull new_name)
|
||||
{
|
||||
this->base.name = BLI_strdup(new_name.c_str());
|
||||
}
|
||||
|
||||
const Map<int, GreasePencilFrame> &Layer::frames() const
|
||||
{
|
||||
return this->runtime->frames_;
|
||||
|
@ -546,12 +554,46 @@ Layer &LayerGroup::add_layer(Layer *layer)
|
|||
return *layer;
|
||||
}
|
||||
|
||||
Layer &LayerGroup::add_layer_before(Layer *layer, Layer *link)
|
||||
{
|
||||
BLI_assert(layer != nullptr && link != nullptr);
|
||||
BLI_insertlinkbefore(&this->children,
|
||||
reinterpret_cast<GreasePencilLayerTreeNode *>(link),
|
||||
reinterpret_cast<GreasePencilLayerTreeNode *>(layer));
|
||||
layer->base.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
|
||||
this->tag_nodes_cache_dirty();
|
||||
return *layer;
|
||||
}
|
||||
|
||||
Layer &LayerGroup::add_layer_after(Layer *layer, Layer *link)
|
||||
{
|
||||
BLI_assert(layer != nullptr && link != nullptr);
|
||||
BLI_insertlinkafter(&this->children,
|
||||
reinterpret_cast<GreasePencilLayerTreeNode *>(link),
|
||||
reinterpret_cast<GreasePencilLayerTreeNode *>(layer));
|
||||
layer->base.parent = reinterpret_cast<GreasePencilLayerTreeGroup *>(this);
|
||||
this->tag_nodes_cache_dirty();
|
||||
return *layer;
|
||||
}
|
||||
|
||||
Layer &LayerGroup::add_layer(StringRefNull name)
|
||||
{
|
||||
Layer *new_layer = MEM_new<Layer>(__func__, name);
|
||||
return this->add_layer(new_layer);
|
||||
}
|
||||
|
||||
Layer &LayerGroup::add_layer_before(StringRefNull name, Layer *link)
|
||||
{
|
||||
Layer *new_layer = MEM_new<Layer>(__func__, name);
|
||||
return this->add_layer_before(new_layer, link);
|
||||
}
|
||||
|
||||
Layer &LayerGroup::add_layer_after(StringRefNull name, Layer *link)
|
||||
{
|
||||
Layer *new_layer = MEM_new<Layer>(__func__, name);
|
||||
return this->add_layer_after(new_layer, link);
|
||||
}
|
||||
|
||||
int64_t LayerGroup::num_direct_nodes() const
|
||||
{
|
||||
return BLI_listbase_count(&this->children);
|
||||
|
@ -570,6 +612,15 @@ void LayerGroup::remove_child(int64_t index)
|
|||
this->tag_nodes_cache_dirty();
|
||||
}
|
||||
|
||||
bool LayerGroup::unlink_layer(Layer *link)
|
||||
{
|
||||
if (BLI_remlink_safe(&this->children, link)) {
|
||||
this->tag_nodes_cache_dirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<const TreeNode *> LayerGroup::nodes() const
|
||||
{
|
||||
this->ensure_nodes_cache();
|
||||
|
@ -1143,13 +1194,81 @@ blender::Span<blender::bke::greasepencil::Layer *> GreasePencil::layers_for_writ
|
|||
return this->root_group.wrap().layers_for_write();
|
||||
}
|
||||
|
||||
blender::Span<const blender::bke::greasepencil::TreeNode *> GreasePencil::nodes() const
|
||||
{
|
||||
BLI_assert(this->runtime != nullptr);
|
||||
return this->root_group.wrap().nodes();
|
||||
}
|
||||
|
||||
const blender::bke::greasepencil::Layer *GreasePencil::get_active_layer() const
|
||||
{
|
||||
if (this->active_layer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return &this->active_layer->wrap();
|
||||
}
|
||||
|
||||
blender::bke::greasepencil::Layer *GreasePencil::get_active_layer_for_write()
|
||||
{
|
||||
if (this->active_layer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return &this->active_layer->wrap();
|
||||
}
|
||||
|
||||
void GreasePencil::set_active_layer(const blender::bke::greasepencil::Layer *layer)
|
||||
{
|
||||
this->active_layer = const_cast<GreasePencilLayer *>(
|
||||
reinterpret_cast<const GreasePencilLayer *>(layer));
|
||||
}
|
||||
|
||||
static blender::VectorSet<blender::StringRefNull> get_node_names(GreasePencil &grease_pencil)
|
||||
{
|
||||
using namespace blender;
|
||||
VectorSet<StringRefNull> names;
|
||||
for (const blender::bke::greasepencil::TreeNode *node : grease_pencil.nodes()) {
|
||||
names.add(node->name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
static bool check_unique_layer_cb(void *arg, const char *name)
|
||||
{
|
||||
using namespace blender;
|
||||
VectorSet<StringRefNull> &names = *reinterpret_cast<VectorSet<StringRefNull> *>(arg);
|
||||
return names.contains(name);
|
||||
}
|
||||
|
||||
static bool unique_layer_name(VectorSet<blender::StringRefNull> &names, char *name)
|
||||
{
|
||||
return BLI_uniquename_cb(check_unique_layer_cb, &names, "GP_Layer", '.', name, MAX_NAME);
|
||||
}
|
||||
|
||||
blender::bke::greasepencil::Layer &GreasePencil::add_layer(
|
||||
blender::bke::greasepencil::LayerGroup &group, const blender::StringRefNull name)
|
||||
{
|
||||
using namespace blender;
|
||||
/* TODO: Check for name collisions and resolve them. */
|
||||
/* StringRefNull checked_name; */
|
||||
return group.add_layer(name);
|
||||
VectorSet<StringRefNull> names = get_node_names(*this);
|
||||
std::string unique_name(name.c_str());
|
||||
unique_layer_name(names, unique_name.data());
|
||||
return group.add_layer(unique_name);
|
||||
}
|
||||
|
||||
blender::bke::greasepencil::Layer &GreasePencil::add_layer_after(
|
||||
blender::bke::greasepencil::LayerGroup &group,
|
||||
blender::bke::greasepencil::Layer *layer,
|
||||
const blender::StringRefNull name)
|
||||
{
|
||||
using namespace blender;
|
||||
VectorSet<StringRefNull> names = get_node_names(*this);
|
||||
std::string unique_name(name.c_str());
|
||||
unique_layer_name(names, unique_name.data());
|
||||
return group.add_layer_after(unique_name, layer);
|
||||
}
|
||||
|
||||
blender::bke::greasepencil::Layer &GreasePencil::add_layer(const blender::StringRefNull name)
|
||||
{
|
||||
return this->add_layer(this->root_group.wrap(), name);
|
||||
}
|
||||
|
||||
const blender::bke::greasepencil::Layer *GreasePencil::find_layer_by_name(
|
||||
|
@ -1164,6 +1283,55 @@ blender::bke::greasepencil::Layer *GreasePencil::find_layer_by_name(
|
|||
return this->root_group.wrap().find_layer_by_name(name);
|
||||
}
|
||||
|
||||
void GreasePencil::rename_layer(blender::bke::greasepencil::Layer &layer,
|
||||
blender::StringRefNull new_name)
|
||||
{
|
||||
using namespace blender;
|
||||
if (layer.name() == new_name) {
|
||||
return;
|
||||
}
|
||||
VectorSet<StringRefNull> names = get_node_names(*this);
|
||||
std::string unique_name(new_name.c_str());
|
||||
unique_layer_name(names, unique_name.data());
|
||||
layer.set_name(unique_name);
|
||||
}
|
||||
|
||||
void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
/* If the layer is active, update the active layer. */
|
||||
const Layer *active_layer = this->get_active_layer();
|
||||
if (active_layer == &layer) {
|
||||
Span<const Layer *> layers = this->layers();
|
||||
/* If there is no other layer available , unset the active layer. */
|
||||
if (layers.size() == 1) {
|
||||
this->set_active_layer(nullptr);
|
||||
}
|
||||
else {
|
||||
/* Make the layer below active (if possible). */
|
||||
if (active_layer == layers.first()) {
|
||||
this->set_active_layer(layers[1]);
|
||||
}
|
||||
else {
|
||||
int64_t active_index = layers.first_index(active_layer);
|
||||
this->set_active_layer(layers[active_index - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlink the layer from the parent group. */
|
||||
layer.parent_group().unlink_layer(&layer);
|
||||
|
||||
/* Remove drawings. */
|
||||
/* TODO: In the future this should only remove drawings when the user count hits zero. */
|
||||
for (GreasePencilFrame frame : layer.frames_for_write().values()) {
|
||||
this->remove_drawing(frame.drawing_index);
|
||||
}
|
||||
|
||||
/* Delete the layer. */
|
||||
MEM_delete(&layer);
|
||||
}
|
||||
|
||||
void GreasePencil::print_layer_tree()
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
|
|
@ -235,7 +235,7 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
|
|||
}
|
||||
|
||||
if ((gpl->flag & GP_LAYER_ACTIVE) != 0) {
|
||||
grease_pencil.active_layer = static_cast<GreasePencilLayer *>(&new_layer);
|
||||
grease_pencil.set_active_layer(&new_layer);
|
||||
}
|
||||
|
||||
/* TODO: Update drawing user counts. */
|
||||
|
|
|
@ -24,6 +24,7 @@ set(INC_SYS
|
|||
set(SRC
|
||||
intern/grease_pencil_add.cc
|
||||
intern/grease_pencil_edit.cc
|
||||
intern/grease_pencil_layers.cc
|
||||
intern/grease_pencil_ops.cc
|
||||
intern/grease_pencil_select.cc
|
||||
)
|
||||
|
|
|
@ -1200,7 +1200,7 @@ void create_blank(Main &bmain, Object &object, const int frame_number)
|
|||
object.actcol = material_index + 1;
|
||||
|
||||
Layer &new_layer = grease_pencil.add_layer(grease_pencil.root_group.wrap(), "GP_Layer");
|
||||
grease_pencil.active_layer = &new_layer;
|
||||
grease_pencil.set_active_layer(&new_layer);
|
||||
|
||||
grease_pencil.add_empty_drawings(1);
|
||||
|
||||
|
@ -1224,7 +1224,7 @@ void create_stroke(Main &bmain, Object &object, float4x4 matrix, const int frame
|
|||
|
||||
Layer &layer_lines = grease_pencil.add_layer(grease_pencil.root_group.wrap(), "Lines");
|
||||
Layer &layer_color = grease_pencil.add_layer(grease_pencil.root_group.wrap(), "Color");
|
||||
grease_pencil.active_layer = &layer_lines;
|
||||
grease_pencil.set_active_layer(&layer_lines);
|
||||
|
||||
grease_pencil.add_empty_drawings(2);
|
||||
|
||||
|
@ -1289,7 +1289,7 @@ void create_suzanne(Main &bmain, Object &object, float4x4 matrix, const int fram
|
|||
|
||||
Layer &layer_fills = grease_pencil.add_layer(grease_pencil.root_group.wrap(), "Fills");
|
||||
Layer &layer_lines = grease_pencil.add_layer(grease_pencil.root_group.wrap(), "Lines");
|
||||
grease_pencil.active_layer = &layer_lines;
|
||||
grease_pencil.set_active_layer(&layer_lines);
|
||||
|
||||
grease_pencil.add_empty_drawings(2);
|
||||
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
bool active_grease_pencil_poll(bContext *C)
|
||||
{
|
||||
Object *object = CTX_data_active_object(C);
|
||||
if (object == nullptr || object->type != OB_GREASE_PENCIL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool editable_grease_pencil_poll(bContext *C)
|
||||
{
|
||||
Object *object = CTX_data_active_object(C);
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edgreasepencil
|
||||
*/
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_grease_pencil.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
static int grease_pencil_layer_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
Object *object = CTX_data_active_object(C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
||||
int new_layer_name_length;
|
||||
char *new_layer_name = RNA_string_get_alloc(
|
||||
op->ptr, "new_layer_name", nullptr, 0, &new_layer_name_length);
|
||||
|
||||
if (grease_pencil.has_active_layer()) {
|
||||
LayerGroup &active_group = grease_pencil.get_active_layer()->parent_group();
|
||||
Layer &new_layer = grease_pencil.add_layer_after(
|
||||
active_group, grease_pencil.get_active_layer_for_write(), new_layer_name);
|
||||
grease_pencil.set_active_layer(&new_layer);
|
||||
}
|
||||
else {
|
||||
Layer &new_layer = grease_pencil.add_layer(new_layer_name);
|
||||
grease_pencil.set_active_layer(&new_layer);
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(new_layer_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_add(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
/* identifiers */
|
||||
ot->name = "Add New Layer";
|
||||
ot->idname = "GREASE_PENCIL_OT_layer_add";
|
||||
ot->description = "Add new a new Grease Pencil layer in the active object";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = grease_pencil_layer_add_exec;
|
||||
ot->poll = editable_grease_pencil_poll;
|
||||
|
||||
prop = RNA_def_string(
|
||||
ot->srna, "new_layer_name", "GP_Layer", INT16_MAX, "Name", "Name of the new layer");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
ot->prop = prop;
|
||||
}
|
||||
|
||||
static int grease_pencil_layer_remove_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
Object *object = CTX_data_active_object(C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
||||
if (!grease_pencil.has_active_layer()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
grease_pencil.remove_layer(*grease_pencil.get_active_layer_for_write());
|
||||
|
||||
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_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Layer";
|
||||
ot->idname = "GREASE_PENCIL_OT_layer_remove";
|
||||
ot->description = "Remove the active Grease Pencil layer";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = grease_pencil_layer_remove_exec;
|
||||
ot->poll = editable_grease_pencil_poll;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
void ED_operatortypes_grease_pencil_layers(void)
|
||||
{
|
||||
using namespace blender::ed::greasepencil;
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_layer_add);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_layer_remove);
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
void ED_operatortypes_grease_pencil(void)
|
||||
{
|
||||
ED_operatortypes_grease_pencil_select();
|
||||
ED_operatortypes_grease_pencil_draw();
|
||||
ED_operatortypes_grease_pencil_layers();
|
||||
ED_operatortypes_grease_pencil_select();
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ extern "C" {
|
|||
|
||||
void ED_operatortypes_grease_pencil(void);
|
||||
void ED_operatortypes_grease_pencil_draw(void);
|
||||
void ED_operatortypes_grease_pencil_layers(void);
|
||||
void ED_operatortypes_grease_pencil_select(void);
|
||||
void ED_keymap_grease_pencil(struct wmKeyConfig *keyconf);
|
||||
/**
|
||||
|
@ -44,6 +45,7 @@ eAttrDomain ED_grease_pencil_selection_domain_get(struct bContext *C);
|
|||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
bool active_grease_pencil_poll(bContext *C);
|
||||
bool editable_grease_pencil_poll(bContext *C);
|
||||
bool editable_grease_pencil_point_selection_poll(bContext *C);
|
||||
|
||||
|
|
|
@ -2630,6 +2630,8 @@ void uiTemplateLightLinkingCollection(struct uiLayout *layout,
|
|||
struct PointerRNA *ptr,
|
||||
const char *propname);
|
||||
|
||||
void uiTemplateGreasePencilLayerTree(uiLayout *layout, struct bContext *C);
|
||||
|
||||
/**
|
||||
* \return: A RNA pointer for the operator properties.
|
||||
*/
|
||||
|
|
|
@ -70,6 +70,7 @@ set(SRC
|
|||
interface_template_asset_view.cc
|
||||
interface_template_attribute_search.cc
|
||||
interface_template_light_linking.cc
|
||||
interface_template_grease_pencil_layer_tree.cc
|
||||
interface_template_list.cc
|
||||
interface_template_search_menu.cc
|
||||
interface_template_search_operator.cc
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edinterface
|
||||
*/
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_tree_view.hh"
|
||||
|
||||
namespace blender::ui::greasepencil {
|
||||
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
class LayerViewItem : public AbstractTreeViewItem {
|
||||
public:
|
||||
LayerViewItem(GreasePencil &grease_pencil, Layer &layer)
|
||||
: grease_pencil_(grease_pencil), layer_(layer)
|
||||
{
|
||||
this->label_ = layer.name();
|
||||
}
|
||||
|
||||
void build_row(uiLayout &row) override
|
||||
{
|
||||
uiLayout *sub = uiLayoutRow(&row, true);
|
||||
uiItemL(sub, IFACE_(layer_.name().c_str()), ICON_GREASEPENCIL);
|
||||
}
|
||||
|
||||
bool supports_collapsing() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<bool> should_be_active() const override
|
||||
{
|
||||
if (this->grease_pencil_.has_active_layer()) {
|
||||
return reinterpret_cast<GreasePencilLayer *>(&layer_) == this->grease_pencil_.active_layer;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void on_activate()
|
||||
{
|
||||
this->grease_pencil_.set_active_layer(&layer_);
|
||||
}
|
||||
|
||||
bool supports_renaming() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rename(StringRefNull new_name) override
|
||||
{
|
||||
grease_pencil_.rename_layer(layer_, new_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
StringRef get_rename_string() const override
|
||||
{
|
||||
return layer_.name();
|
||||
}
|
||||
|
||||
private:
|
||||
GreasePencil &grease_pencil_;
|
||||
Layer &layer_;
|
||||
};
|
||||
|
||||
class LayerGroupViewItem : public AbstractTreeViewItem {
|
||||
public:
|
||||
LayerGroupViewItem(GreasePencil &grease_pencil, LayerGroup &group)
|
||||
: grease_pencil_(grease_pencil), group_(group)
|
||||
{
|
||||
this->label_ = group_.name();
|
||||
}
|
||||
|
||||
void build_row(uiLayout &row) override
|
||||
{
|
||||
uiLayout *sub = uiLayoutRow(&row, true);
|
||||
uiItemL(sub, IFACE_(group_.name().c_str()), ICON_FILE_FOLDER);
|
||||
}
|
||||
|
||||
private:
|
||||
GreasePencil &grease_pencil_;
|
||||
LayerGroup &group_;
|
||||
};
|
||||
|
||||
class LayerTreeView : public AbstractTreeView {
|
||||
public:
|
||||
explicit LayerTreeView(GreasePencil &grease_pencil) : grease_pencil_(grease_pencil) {}
|
||||
|
||||
void build_tree() override;
|
||||
|
||||
private:
|
||||
void build_tree_node_recursive(TreeNode &node);
|
||||
GreasePencil &grease_pencil_;
|
||||
};
|
||||
|
||||
void LayerTreeView::build_tree_node_recursive(TreeNode &node)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
if (node.is_layer()) {
|
||||
add_tree_item<LayerViewItem>(this->grease_pencil_, node.as_layer_for_write());
|
||||
}
|
||||
else if (node.is_group()) {
|
||||
add_tree_item<LayerGroupViewItem>(this->grease_pencil_, node.as_group_for_write());
|
||||
LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, node_, &node.as_group().children) {
|
||||
build_tree_node_recursive(node_->wrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LayerTreeView::build_tree()
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
LISTBASE_FOREACH_BACKWARD (
|
||||
GreasePencilLayerTreeNode *, node, &this->grease_pencil_.root_group.children)
|
||||
{
|
||||
this->build_tree_node_recursive(node->wrap());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ui::greasepencil
|
||||
|
||||
void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
Object *object = CTX_data_active_object(C);
|
||||
if (!object || object->type != OB_GREASE_PENCIL) {
|
||||
return;
|
||||
}
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
|
||||
ui::AbstractTreeView *tree_view = UI_block_add_view(
|
||||
*block,
|
||||
"Grease Pencil Layer Tree View",
|
||||
std::make_unique<blender::ui::greasepencil::LayerTreeView>(grease_pencil));
|
||||
tree_view->set_min_rows(3);
|
||||
|
||||
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
|
||||
}
|
|
@ -58,7 +58,7 @@ struct PaintOperationExecutor {
|
|||
BLI_assert_unreachable();
|
||||
// grease_pencil.runtime->set_active_layer_index(0);
|
||||
}
|
||||
const bke::greasepencil::Layer &active_layer = grease_pencil.active_layer->wrap();
|
||||
const bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
|
||||
int index = active_layer.drawing_index_at(scene->r.cfra);
|
||||
BLI_assert(index != -1);
|
||||
|
||||
|
@ -95,8 +95,8 @@ void PaintOperation::on_stroke_done(const bContext &C)
|
|||
GreasePencil &grease_pencil_orig = *static_cast<GreasePencil *>(obact->data);
|
||||
GreasePencil &grease_pencil_eval = *static_cast<GreasePencil *>(ob_eval->data);
|
||||
BLI_assert(grease_pencil_orig.has_active_layer() && grease_pencil_eval.has_active_layer());
|
||||
const bke::greasepencil::Layer &active_layer_orig = grease_pencil_orig.active_layer->wrap();
|
||||
const bke::greasepencil::Layer &active_layer_eval = grease_pencil_eval.active_layer->wrap();
|
||||
const bke::greasepencil::Layer &active_layer_orig = *grease_pencil_orig.get_active_layer();
|
||||
const bke::greasepencil::Layer &active_layer_eval = *grease_pencil_eval.get_active_layer();
|
||||
int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra);
|
||||
int index_eval = active_layer_eval.drawing_index_at(scene->r.cfra);
|
||||
BLI_assert(index_orig != -1 && index_eval != -1);
|
||||
|
|
|
@ -262,6 +262,9 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
|
|||
if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && ELEM(type, -1, OB_GPENCIL_LEGACY)) {
|
||||
return true;
|
||||
}
|
||||
if (RNA_struct_is_a(ptr->type, &RNA_GreasePencilv3) && ELEM(type, -1, OB_GREASE_PENCIL)) {
|
||||
return true;
|
||||
}
|
||||
if (RNA_struct_is_a(ptr->type, &RNA_Curves) && ELEM(type, -1, OB_CURVES)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -301,6 +304,7 @@ static bool buttons_context_path_modifier(ButsContextPath *path)
|
|||
OB_SURF,
|
||||
OB_LATTICE,
|
||||
OB_GPENCIL_LEGACY,
|
||||
OB_GREASE_PENCIL,
|
||||
OB_CURVES,
|
||||
OB_POINTCLOUD,
|
||||
OB_VOLUME))
|
||||
|
@ -838,6 +842,7 @@ const char *buttons_context_dir[] = {
|
|||
"line_style",
|
||||
"collection",
|
||||
"gpencil",
|
||||
"grease_pencil",
|
||||
"curves",
|
||||
#ifdef WITH_POINT_CLOUD
|
||||
"pointcloud",
|
||||
|
@ -1165,6 +1170,10 @@ int /*eContextResult*/ buttons_context(const bContext *C,
|
|||
set_pointer_type(path, result, &RNA_GreasePencil);
|
||||
return CTX_RESULT_OK;
|
||||
}
|
||||
if (CTX_data_equals(member, "grease_pencil")) {
|
||||
set_pointer_type(path, result, &RNA_GreasePencilv3);
|
||||
return CTX_RESULT_OK;
|
||||
}
|
||||
return CTX_RESULT_MEMBER_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
|
|
@ -443,13 +443,27 @@ typedef struct GreasePencil {
|
|||
blender::Span<const blender::bke::greasepencil::Layer *> layers() const;
|
||||
blender::Span<blender::bke::greasepencil::Layer *> layers_for_write();
|
||||
|
||||
blender::Span<const blender::bke::greasepencil::TreeNode *> nodes() const;
|
||||
|
||||
bool has_active_layer() const;
|
||||
const blender::bke::greasepencil::Layer *get_active_layer() const;
|
||||
blender::bke::greasepencil::Layer *get_active_layer_for_write();
|
||||
void set_active_layer(const blender::bke::greasepencil::Layer *layer);
|
||||
|
||||
blender::bke::greasepencil::Layer &add_layer(blender::bke::greasepencil::LayerGroup &group,
|
||||
blender::StringRefNull name);
|
||||
blender::bke::greasepencil::Layer &add_layer(blender::StringRefNull name);
|
||||
blender::bke::greasepencil::Layer &add_layer_after(blender::bke::greasepencil::LayerGroup &group,
|
||||
blender::bke::greasepencil::Layer *layer,
|
||||
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);
|
||||
|
||||
void rename_layer(blender::bke::greasepencil::Layer &layer, blender::StringRefNull new_name);
|
||||
|
||||
void remove_layer(blender::bke::greasepencil::Layer &layer);
|
||||
|
||||
void add_empty_drawings(int add_num);
|
||||
void remove_drawing(int index);
|
||||
|
||||
|
|
|
@ -1981,6 +1981,11 @@ void RNA_api_ui_layout(StructRNA *srna)
|
|||
RNA_def_function_ui_description(func,
|
||||
"Visualization of a content of a light linking collection");
|
||||
api_ui_item_rna_common(func);
|
||||
|
||||
func = RNA_def_function(
|
||||
srna, "template_grease_pencil_layer_tree", "uiTemplateGreasePencilLayerTree");
|
||||
RNA_def_function_ui_description(func, "View of the active grease pencil layer tree");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue