No user visible changes expected, these are just the internal API preparations. Modifies the Drop API for views so that tree-views can choose to insert items before, after and into other items. Note: While there is support for drag-tooltips that can explain how an item will be inserted, there is no drawing yet like in the Outliner, that indicates if an item is inserted before, after or into. There is some work on that but that can be done separately. Changes: - Removes `AbstractViewDropTarget` that was shared between tree- and grid-views, and adds `AbstractTreeViewDropTarget` and `AbstractGridViewDropTarget`. The tree-view needs specialized handling now, and although they could share some code still, it's not worth having another level of inheritance. - Modifies the drop-target API to use `DragInfo` which contains more info about the dragging operation than just the `wmDrag`. - Adds `determine_drop_location()` to the `DropTargetInterface` which drop targets can use to determine when dropping means inserting before, after or into. - Store the block and region in the view. This is needed unfortunately but shouldn't be an issue since the tree view is recreated on redraws, together with the block. - Various smaller tweaks and additions to views as needed. TODO (outside scope of this change): Increase row height so there is no gap between tree view items, but keep things visually the same otherwise. This reduces flickering while dragging. Pull Request: https://projects.blender.org/blender/blender/pulls/109825
279 lines
7.8 KiB
C++
279 lines
7.8 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup edinterface
|
|
*/
|
|
|
|
#include "UI_interface.h"
|
|
|
|
#include <cstdio>
|
|
#include <memory>
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "DNA_collection_types.h"
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_light_linking.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_prototypes.h"
|
|
|
|
#include "UI_interface.h"
|
|
#include "UI_interface.hh"
|
|
#include "UI_resources.h"
|
|
#include "UI_tree_view.hh"
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "ED_undo.h"
|
|
|
|
namespace blender::ui::light_linking {
|
|
|
|
namespace {
|
|
|
|
class CollectionDropTarget : public DropTargetInterface {
|
|
public:
|
|
CollectionDropTarget(Collection &collection) : collection_(collection) {}
|
|
|
|
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override
|
|
{
|
|
if (drag.type != WM_DRAG_ID) {
|
|
return false;
|
|
}
|
|
|
|
const wmDragID *drag_id = static_cast<wmDragID *>(drag.ids.first);
|
|
if (!drag_id) {
|
|
return false;
|
|
}
|
|
|
|
/* The dragged IDs are guaranteed to be the same type, so only check the type of the first one.
|
|
*/
|
|
const ID_Type id_type = GS(drag_id->id->name);
|
|
if (!ELEM(id_type, ID_OB, ID_GR)) {
|
|
*r_disabled_hint = "Can only add objects and collections to the light linking collection";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string drop_tooltip(const DragInfo & /*drag*/) const override
|
|
{
|
|
return TIP_("Add to light linking collection");
|
|
}
|
|
|
|
bool on_drop(struct bContext *C, const DragInfo &drag) const override
|
|
{
|
|
Main *bmain = CTX_data_main(C);
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
LISTBASE_FOREACH (wmDragID *, drag_id, &drag.drag_data.ids) {
|
|
BKE_light_linking_add_receiver_to_collection(
|
|
bmain, &collection_, drag_id->id, COLLECTION_LIGHT_LINKING_STATE_INCLUDE);
|
|
}
|
|
|
|
/* It is possible that the light linking collection is also used by the view layer.
|
|
* For this case send a notifier so that the UI is updated for the changes in the collection
|
|
* content. */
|
|
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
|
|
|
|
ED_undo_push(C, "Add to light linking collection");
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
Collection &collection_;
|
|
};
|
|
|
|
class CollectionViewItem : public BasicTreeViewItem {
|
|
public:
|
|
CollectionViewItem(Collection &collection,
|
|
ID &id,
|
|
CollectionLightLinking &collection_light_linking,
|
|
const BIFIconID icon)
|
|
: BasicTreeViewItem(id.name + 2, icon),
|
|
collection_(collection),
|
|
id_(&id),
|
|
collection_light_linking_(collection_light_linking)
|
|
{
|
|
}
|
|
|
|
void build_row(uiLayout &row) override
|
|
{
|
|
add_label(row);
|
|
|
|
uiLayout *sub = uiLayoutRow(&row, true);
|
|
uiLayoutSetPropDecorate(sub, false);
|
|
|
|
build_state_button(*sub);
|
|
build_remove_button(*sub);
|
|
}
|
|
|
|
private:
|
|
int get_state_icon() const
|
|
{
|
|
/* TODO(sergey): Use proper icons. */
|
|
switch (collection_light_linking_.link_state) {
|
|
case COLLECTION_LIGHT_LINKING_STATE_INCLUDE:
|
|
return ICON_OUTLINER_OB_LIGHT;
|
|
case COLLECTION_LIGHT_LINKING_STATE_EXCLUDE:
|
|
return ICON_LIGHT;
|
|
}
|
|
BLI_assert_unreachable();
|
|
return ICON_NONE;
|
|
}
|
|
|
|
static void link_state_toggle(CollectionLightLinking &collection_light_linking)
|
|
{
|
|
switch (collection_light_linking.link_state) {
|
|
case COLLECTION_LIGHT_LINKING_STATE_INCLUDE:
|
|
collection_light_linking.link_state = COLLECTION_LIGHT_LINKING_STATE_EXCLUDE;
|
|
return;
|
|
case COLLECTION_LIGHT_LINKING_STATE_EXCLUDE:
|
|
collection_light_linking.link_state = COLLECTION_LIGHT_LINKING_STATE_INCLUDE;
|
|
return;
|
|
}
|
|
|
|
BLI_assert_unreachable();
|
|
}
|
|
|
|
void build_state_button(uiLayout &row)
|
|
{
|
|
uiBlock *block = uiLayoutGetBlock(&row);
|
|
const int icon = get_state_icon();
|
|
|
|
PointerRNA collection_light_linking_ptr;
|
|
RNA_pointer_create(&collection_.id,
|
|
&RNA_CollectionLightLinking,
|
|
&collection_light_linking_,
|
|
&collection_light_linking_ptr);
|
|
|
|
uiBut *button = uiDefIconButR(block,
|
|
UI_BTYPE_BUT,
|
|
0,
|
|
icon,
|
|
0,
|
|
0,
|
|
UI_UNIT_X,
|
|
UI_UNIT_Y,
|
|
&collection_light_linking_ptr,
|
|
"link_state",
|
|
0,
|
|
0.0f,
|
|
0.0f,
|
|
0.0f,
|
|
0.0f,
|
|
nullptr);
|
|
|
|
UI_but_func_set(button, [this](bContext &) { link_state_toggle(collection_light_linking_); });
|
|
}
|
|
|
|
void build_remove_button(uiLayout &row)
|
|
{
|
|
PointerRNA id_ptr;
|
|
RNA_id_pointer_create(id_, &id_ptr);
|
|
|
|
PointerRNA collection_ptr;
|
|
RNA_id_pointer_create(&collection_.id, &collection_ptr);
|
|
|
|
uiLayoutSetContextPointer(&row, "id", &id_ptr);
|
|
uiLayoutSetContextPointer(&row, "collection", &collection_ptr);
|
|
|
|
uiItemO(&row, "", ICON_X, "OBJECT_OT_light_linking_unlink_from_collection");
|
|
}
|
|
|
|
Collection &collection_;
|
|
|
|
ID *id_{nullptr};
|
|
CollectionLightLinking &collection_light_linking_;
|
|
};
|
|
|
|
class CollectionView : public AbstractTreeView {
|
|
public:
|
|
explicit CollectionView(Collection &collection) : collection_(collection) {}
|
|
|
|
void build_tree() override
|
|
{
|
|
LISTBASE_FOREACH (CollectionChild *, collection_child, &collection_.children) {
|
|
Collection *child_collection = collection_child->collection;
|
|
add_tree_item<CollectionViewItem>(collection_,
|
|
child_collection->id,
|
|
collection_child->light_linking,
|
|
ICON_OUTLINER_COLLECTION);
|
|
}
|
|
|
|
LISTBASE_FOREACH (CollectionObject *, collection_object, &collection_.gobject) {
|
|
Object *child_object = collection_object->ob;
|
|
add_tree_item<CollectionViewItem>(
|
|
collection_, child_object->id, collection_object->light_linking, ICON_OBJECT_DATA);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<DropTargetInterface> create_drop_target() override
|
|
{
|
|
return std::make_unique<CollectionDropTarget>(collection_);
|
|
}
|
|
|
|
private:
|
|
Collection &collection_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
} // namespace blender::ui::light_linking
|
|
|
|
namespace ui = blender::ui;
|
|
|
|
void uiTemplateLightLinkingCollection(struct uiLayout *layout,
|
|
struct PointerRNA *ptr,
|
|
const char *propname)
|
|
{
|
|
if (!ptr->data) {
|
|
return;
|
|
}
|
|
|
|
PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
|
|
if (!prop) {
|
|
printf(
|
|
"%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname);
|
|
return;
|
|
}
|
|
|
|
if (RNA_property_type(prop) != PROP_POINTER) {
|
|
printf("%s: expected pointer property for %s.%s\n",
|
|
__func__,
|
|
RNA_struct_identifier(ptr->type),
|
|
propname);
|
|
return;
|
|
}
|
|
|
|
const PointerRNA collection_ptr = RNA_property_pointer_get(ptr, prop);
|
|
if (!collection_ptr.data) {
|
|
return;
|
|
}
|
|
if (collection_ptr.type != &RNA_Collection) {
|
|
printf("%s: expected collection pointer property for %s.%s\n",
|
|
__func__,
|
|
RNA_struct_identifier(ptr->type),
|
|
propname);
|
|
return;
|
|
}
|
|
|
|
Collection *collection = static_cast<Collection *>(collection_ptr.data);
|
|
|
|
uiBlock *block = uiLayoutGetBlock(layout);
|
|
|
|
ui::AbstractTreeView *tree_view = UI_block_add_view(
|
|
*block,
|
|
"Light Linking Collection Tree View",
|
|
std::make_unique<blender::ui::light_linking::CollectionView>(*collection));
|
|
tree_view->set_min_rows(3);
|
|
|
|
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
|
|
}
|