Support active item

Makes activating assets in the Asset Browser work. The active item is only
stored at the UI level now, so it's not stored in files, not accessible via
context and is lost when changing editors. The Asset Browser itself will have
to get a way to store that.
This commit is contained in:
Julian Eisel 2022-02-14 17:48:51 +01:00
parent c9c332f422
commit 696295f849
8 changed files with 208 additions and 19 deletions

View File

@ -49,6 +49,8 @@ class AbstractGridViewItem {
const AbstractGridView *view_;
bool is_active_ = false;
protected:
/** This label is used as the default way to identifying an item in the view. */
std::string label_{};
@ -60,8 +62,6 @@ class AbstractGridViewItem {
virtual void build_grid_tile(uiLayout &layout) const = 0;
const AbstractGridView &get_view() const;
/**
* Compare this item to \a other to check if they represent the same data.
* Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active,
@ -70,9 +70,36 @@ class AbstractGridViewItem {
*/
virtual bool matches(const AbstractGridViewItem &other) const;
const AbstractGridView &get_view() const;
/**
* Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we
* can't be sure about the item state.
*/
bool is_active() const;
protected:
AbstractGridViewItem() = default;
/** Called when the item's state changes from inactive to active. */
virtual void on_activate();
/**
* Copy persistent state (e.g. active, selection, etc.) from a matching item of
* the last redraw to this item. If sub-classes introduce more advanced state they should
* override this and make it update their state accordingly.
*/
virtual void update_from_old(const AbstractGridViewItem &old);
/**
* Activates this item, deactivates other items, and calls the
* #AbstractGridViewItem::on_activate() function.
* Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the
* actual item state is unknown, possibly calling state-change update functions incorrectly.
*/
void activate();
void deactivate();
private:
static void grid_tile_click_fn(bContext *, void *but_arg1, void *);
void add_grid_tile_button(uiBlock &block);
@ -91,12 +118,14 @@ struct GridViewStyle {
};
class AbstractGridView {
friend class AbstractGridViewItem;
friend class GridViewBuilder;
friend class GridViewLayoutBuilder;
protected:
Vector<std::unique_ptr<AbstractGridViewItem>> items_;
GridViewStyle style_;
bool is_reconstructed_ = false;
public:
AbstractGridView();
@ -128,7 +157,22 @@ class AbstractGridView {
protected:
virtual void build_items() = 0;
/**
* Check if the view is fully (re-)constructed. That means, both #build_items() and
* #update_from_old() have finished.
*/
bool is_reconstructed() const;
private:
/**
* Match the grid-view against an earlier version of itself (if any) and copy the old UI state
* (e.g. active, selected, renaming, etc.) to the new one. See
* #AbstractGridViewItem.update_from_old().
*/
void update_from_old(uiBlock &new_block);
AbstractGridViewItem *find_matching_item(const AbstractGridViewItem &lookup_item,
const AbstractGridView &view) const;
/**
* Add an already constructed item, moving ownership to the grid-view.
* All items must be added through this, it handles important invariants!
@ -169,7 +213,6 @@ class GridViewBuilder {
*/
class PreviewGridItem : public AbstractGridViewItem {
public:
std::string label{};
int preview_icon_id = ICON_NONE;
PreviewGridItem(StringRef label, int preview_icon_id);

View File

@ -3165,6 +3165,7 @@ void UI_interface_tag_script_reload(void);
void UI_block_views_listen(const uiBlock *block,
const struct wmRegionListenerParams *listener_params);
bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle);
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b);
bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);

View File

@ -60,6 +60,49 @@ bool AbstractGridView::listen(const wmNotifier &) const
return false;
}
AbstractGridViewItem *AbstractGridView::find_matching_item(const AbstractGridViewItem &lookup_item,
const AbstractGridView &view) const
{
for (const auto &iter_item_ptr : view.items_) {
if (lookup_item.matches(*iter_item_ptr)) {
/* We have a matching item! */
return iter_item_ptr.get();
}
}
return nullptr;
}
void AbstractGridView::update_from_old(uiBlock &new_block)
{
uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block(
&new_block, reinterpret_cast<uiGridViewHandle *>(this));
if (!old_view_handle) {
/* Initial construction, nothing to update. */
is_reconstructed_ = true;
return;
}
AbstractGridView &old_view = reinterpret_cast<AbstractGridView &>(*old_view_handle);
foreach_item([this, &old_view](AbstractGridViewItem &new_item) {
const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_view);
if (!matching_old_item) {
return;
}
new_item.update_from_old(*matching_old_item);
});
/* Finished (re-)constructing the tree. */
is_reconstructed_ = true;
}
bool AbstractGridView::is_reconstructed() const
{
return is_reconstructed_;
}
const GridViewStyle &AbstractGridView::get_style() const
{
return style_;
@ -89,7 +132,7 @@ void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/,
AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>(
*grid_tile_but->view_item);
// tree_item.activate();
grid_item.activate();
}
void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
@ -114,6 +157,45 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr);
}
bool AbstractGridViewItem::is_active() const
{
BLI_assert_msg(get_view().is_reconstructed(),
"State can't be queried until reconstruction is completed");
return is_active_;
}
void AbstractGridViewItem::on_activate()
{
/* Do nothing by default. */
}
void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old)
{
is_active_ = old.is_active_;
}
void AbstractGridViewItem::activate()
{
BLI_assert_msg(get_view().is_reconstructed(),
"Item activation can't be done until reconstruction is completed");
if (is_active()) {
return;
}
/* Deactivate other items in the tree. */
get_view().foreach_item([](auto &item) { item.deactivate(); });
on_activate();
is_active_ = true;
}
void AbstractGridViewItem::deactivate()
{
is_active_ = false;
}
const AbstractGridView &AbstractGridViewItem::get_view() const
{
if (UNLIKELY(!view_)) {
@ -337,6 +419,7 @@ GridViewBuilder::GridViewBuilder(uiBlock &block) : block_(block)
void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D &v2d)
{
grid_view.build_items();
grid_view.update_from_old(block_);
GridViewLayoutBuilder builder(block_);
builder.build_from_view(grid_view, v2d);
@ -350,8 +433,9 @@ void GridViewBuilder::build_grid_view(AbstractGridView &grid_view, const View2D
/* ---------------------------------------------------------------------- */
PreviewGridItem::PreviewGridItem(StringRef label, int preview_icon_id)
: label(label), preview_icon_id(preview_icon_id)
: preview_icon_id(preview_icon_id)
{
label_ = label;
}
void PreviewGridItem::build_grid_tile(uiLayout &layout) const
@ -362,7 +446,7 @@ void PreviewGridItem::build_grid_tile(uiLayout &layout) const
UI_BTYPE_PREVIEW_TILE,
0,
preview_icon_id,
label.c_str(),
label_.c_str(),
0,
0,
style.tile_width,
@ -388,6 +472,12 @@ using namespace blender::ui;
using namespace blender::ui;
bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle)
{
const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle);
return item.is_active();
}
bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view_handle,
const wmNotifier *notifier)
{

View File

@ -2229,6 +2229,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
}
break;
}
case UI_BTYPE_GRID_TILE: {
uiButGridTile *grid_tile_but = (uiButGridTile *)but;
is_push = -1;
if (grid_tile_but->view_item) {
is_push = UI_grid_view_item_is_active(grid_tile_but->view_item);
}
break;
}
default:
is_push = -1;
break;

View File

@ -4855,7 +4855,6 @@ static int ui_do_but_GRIDTILE(bContext *C,
case KM_DBL_CLICK:
data->cancel = true;
// UI_tree_view_item_begin_rename(grid_tile_but->tree_item);
printf("rename\n");
ED_region_tag_redraw(CTX_wm_region(C));
return WM_UI_HANDLER_BREAK;
}
@ -4909,6 +4908,10 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
ret = WM_UI_HANDLER_CONTINUE;
}
}
const uiBut *view_but = ui_view_item_find_mouse_over(data->region, event->xy);
if (view_but) {
ret = WM_UI_HANDLER_CONTINUE;
}
button_activate_state(C, but, BUTTON_STATE_EXIT);
return ret;
}
@ -9720,6 +9723,21 @@ static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *regio
return WM_UI_HANDLER_CONTINUE;
}
static int ui_handle_view_item_event(bContext *C,
const wmEvent *event,
ARegion *region,
uiBut *view_but)
{
BLI_assert(ui_but_is_view_item(view_but));
if (event->type == LEFTMOUSE) {
/* Will free active button if there already is one. */
ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER);
return ui_do_button(C, view_but->block, view_but, event);
}
return WM_UI_HANDLER_CONTINUE;
}
static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but)
{
uiHandleButtonData *data = but->active;
@ -11323,6 +11341,12 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use
/* Always do this, to reliably update view item highlighting, even if the mouse hovers a button
* nested in the item (it's an overlapping layout). */
ui_handle_view_items_hover(event, region);
if (retval == WM_UI_HANDLER_CONTINUE) {
uiBut *view_item = ui_view_item_find_mouse_over(region, event->xy);
if (view_item) {
retval = ui_handle_view_item_event(C, event, region, view_item);
}
}
/* delayed apply callbacks */
ui_apply_but_funcs_after(C);

View File

@ -1517,8 +1517,10 @@ void ui_interface_tag_script_reload_queries(void);
/* interface_view.cc */
void ui_block_free_views(struct uiBlock *block);
uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
const uiTreeViewHandle *new_view);
uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(const uiBlock *new_block,
const uiTreeViewHandle *new_view);
uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block(
const uiBlock *new_block, const uiGridViewHandle *new_view_handle);
uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
const uiTreeViewItemHandle *new_item_handle);

View File

@ -44,6 +44,12 @@ struct ViewLink : public Link {
std::variant<TreeViewPtr, GridViewPtr> view;
};
template<class T> constexpr void check_if_valid_view_type()
{
static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>,
"Unsupported view type");
}
template<class T> T *get_view_from_link(ViewLink &link)
{
auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view);
@ -53,8 +59,8 @@ template<class T> T *get_view_from_link(ViewLink &link)
template<class T>
static T *ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr<T> view)
{
static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>,
"Unsupported view type");
check_if_valid_view_type<T>();
ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link);
@ -125,11 +131,13 @@ uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region)
return tree_row_but->tree_item;
}
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view)
template<class T> static StringRef ui_block_view_find_idname(const uiBlock &block, const T &view)
{
check_if_valid_view_type<T>();
/* First get the idname the of the view we're looking for. */
LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
if (get_view_from_link<AbstractTreeView>(*view_link) == &view) {
if (get_view_from_link<T>(*view_link) == &view) {
return view_link->idname;
}
}
@ -137,9 +145,11 @@ static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractT
return {};
}
static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock &new_block,
const AbstractTreeView &new_view)
template<class T>
static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const T &new_view)
{
check_if_valid_view_type<T>();
uiBlock *old_block = new_block.oldblock;
if (!old_block) {
return nullptr;
@ -152,15 +162,15 @@ static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock
LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
if (old_view_link->idname == idname) {
return get_view_from_link<AbstractTreeView>(*old_view_link);
return get_view_from_link<T>(*old_view_link);
}
}
return nullptr;
}
uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
const uiTreeViewHandle *new_view_handle)
uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(
const uiBlock *new_block, const uiTreeViewHandle *new_view_handle)
{
BLI_assert(new_block && new_view_handle);
const AbstractTreeView &new_view = reinterpret_cast<const AbstractTreeView &>(*new_view_handle);
@ -169,6 +179,16 @@ uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_bl
return reinterpret_cast<uiTreeViewHandle *>(old_view);
}
uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block(
const uiBlock *new_block, const uiGridViewHandle *new_view_handle)
{
BLI_assert(new_block && new_view_handle);
const AbstractGridView &new_view = reinterpret_cast<const AbstractGridView &>(*new_view_handle);
AbstractGridView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view);
return reinterpret_cast<uiGridViewHandle *>(old_view);
}
uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
const uiTreeViewItemHandle *new_item_handle)
{

View File

@ -88,7 +88,7 @@ void AbstractTreeView::update_from_old(uiBlock &new_block)
return;
}
uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block(
uiTreeViewHandle *old_view_handle = ui_block_tree_view_find_matching_in_old_block(
&new_block, reinterpret_cast<uiTreeViewHandle *>(this));
if (old_view_handle == nullptr) {
is_reconstructed_ = true;