This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/interface/interface_view.cc
Julian Eisel 5bee991132 UI: Port view item features to base class, merge view item button types
No user visible changes expected.

Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).

This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
  implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
  selection).
- Simplifies APIs, there don't have to be functions for individual view
  item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
  two into one.

I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:31:23 +02:00

191 lines
5.3 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*
* This part of the UI-View API is mostly needed to support persistent state of items within the
* view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we
* can compare the old view items with the new view items and keep state persistent for matching
* ones.
*/
#include <memory>
#include <type_traits>
#include <variant>
#include "DNA_screen_types.h"
#include "BKE_screen.h"
#include "BLI_listbase.h"
#include "ED_screen.h"
#include "interface_intern.h"
#include "UI_interface.hh"
#include "UI_abstract_view.hh"
#include "UI_grid_view.hh"
#include "UI_tree_view.hh"
using namespace blender;
using namespace blender::ui;
/**
* Wrapper to store views in a #ListBase, addressable via an identifier.
*/
struct ViewLink : public Link {
std::string idname;
std::unique_ptr<AbstractView> view;
};
template<class T>
static T *ui_block_add_view_impl(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractView> view)
{
ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link);
view_link->view = std::move(view);
view_link->idname = idname;
return dynamic_cast<T *>(view_link->view.get());
}
AbstractGridView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractGridView> grid_view)
{
return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_view));
}
AbstractTreeView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractTreeView> tree_view)
{
return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view));
}
void ui_block_free_views(uiBlock *block)
{
LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
MEM_delete(link);
}
}
void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
{
ARegion *region = listener_params->region;
LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
if (view_link->view->listen(*listener_params->notifier)) {
ED_region_tag_redraw(region);
}
}
}
uiViewItemHandle *UI_block_view_find_item_at(const ARegion *region, const int xy[2])
{
uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy);
if (!item_but) {
return nullptr;
}
return item_but->view_item;
}
uiViewItemHandle *UI_block_view_find_active_item(const ARegion *region)
{
uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region);
if (!item_but) {
return nullptr;
}
return item_but->view_item;
}
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view)
{
/* First get the idname the of the view we're looking for. */
LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
if (view_link->view.get() == &view) {
return view_link->idname;
}
}
return {};
}
template<class T>
static T *ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block,
const T &new_view)
{
uiBlock *old_block = new_block.oldblock;
if (!old_block) {
return nullptr;
}
StringRef idname = ui_block_view_find_idname(new_block, new_view);
if (idname.is_empty()) {
return nullptr;
}
LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
if (old_view_link->idname == idname) {
return dynamic_cast<T *>(old_view_link->view.get());
}
}
return nullptr;
}
uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
const uiViewHandle *new_view_handle)
{
BLI_assert(new_block && new_view_handle);
const AbstractView &new_view = reinterpret_cast<const AbstractView &>(*new_view_handle);
AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(*new_block, new_view);
return reinterpret_cast<uiViewHandle *>(old_view);
}
uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block(
const uiBlock *new_block, const uiViewItemHandle *new_item_handle)
{
uiBlock *old_block = new_block->oldblock;
if (!old_block) {
return nullptr;
}
const AbstractViewItem &new_item = *reinterpret_cast<const AbstractViewItem *>(new_item_handle);
const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(
*new_block, new_item.get_view());
if (!old_view) {
return nullptr;
}
LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) {
if (old_but->type != UI_BTYPE_VIEW_ITEM) {
continue;
}
uiButViewItem *old_item_but = (uiButViewItem *)old_but;
if (!old_item_but->view_item) {
continue;
}
AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item);
/* Check if the item is from the expected view. */
if (&old_item.get_view() != old_view) {
continue;
}
if (UI_view_item_matches(reinterpret_cast<const uiViewItemHandle *>(&new_item),
reinterpret_cast<const uiViewItemHandle *>(&old_item))) {
return old_item_but;
}
}
return nullptr;
}