2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
|
* \ingroup edinterface
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
2023-08-03 16:54:39 +02:00
|
|
|
#include "BKE_icons.h"
|
|
|
|
|
|
2022-06-16 11:29:20 +02:00
|
|
|
#include "BLI_index_range.hh"
|
|
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_types.hh"
|
2022-06-16 11:29:20 +02:00
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "UI_interface.hh"
|
2022-11-26 00:21:17 -06:00
|
|
|
#include "interface_intern.hh"
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
#include "UI_grid_view.hh"
|
|
|
|
|
|
|
|
|
|
namespace blender::ui {
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
AbstractGridView::AbstractGridView() : style_(UI_preview_tile_size_x(), UI_preview_tile_size_y())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridViewItem> item)
|
|
|
|
|
{
|
|
|
|
|
items_.append(std::move(item));
|
|
|
|
|
|
|
|
|
|
AbstractGridViewItem &added_item = *items_.last();
|
|
|
|
|
item_map_.add(added_item.identifier_, &added_item);
|
2022-07-20 16:44:15 +02:00
|
|
|
register_item(added_item);
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
return added_item;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-26 16:33:28 +02:00
|
|
|
/* Implementation for the base class virtual function. More specialized iterators below. */
|
|
|
|
|
void AbstractGridView::foreach_view_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const
|
|
|
|
|
{
|
|
|
|
|
for (const auto &item_ptr : items_) {
|
|
|
|
|
iter_fn(*item_ptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-16 11:29:20 +02:00
|
|
|
void AbstractGridView::foreach_item(ItemIterFn iter_fn) const
|
|
|
|
|
{
|
2022-06-25 19:05:31 -05:00
|
|
|
for (const auto &item_ptr : items_) {
|
2022-06-16 11:29:20 +02:00
|
|
|
iter_fn(*item_ptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-12 11:41:00 +02:00
|
|
|
void AbstractGridView::foreach_filtered_item(ItemIterFn iter_fn) const
|
|
|
|
|
{
|
|
|
|
|
for (const auto &item_ptr : items_) {
|
|
|
|
|
if (item_ptr->is_filtered_visible_cached()) {
|
|
|
|
|
iter_fn(*item_ptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-16 11:29:20 +02:00
|
|
|
AbstractGridViewItem *AbstractGridView::find_matching_item(
|
|
|
|
|
const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const
|
|
|
|
|
{
|
|
|
|
|
AbstractGridViewItem *const *match = view_to_search_in.item_map_.lookup_ptr(
|
|
|
|
|
item_to_match.identifier_);
|
|
|
|
|
BLI_assert(!match || item_to_match.matches(**match));
|
|
|
|
|
|
|
|
|
|
return match ? *match : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
UI: Add AbstractView base class for views, unify reconstruction in there
No user visible changes expected.
There's plenty of duplicated code in the grid and the tree view, and I expect
this to become more. This starts the process of unifying these parts, which
should also make it easier to add new views. Complexity in the view classes is
reduced, and some type shenanigans for C compatibility and general view
management can be removed, since there is now a common base type.
For the start this ports some of the view reconstruction, where the view and
its items are compared to the version of itself in the previous redraw, so that
state (highlighted, active, renaming, collapsed, ...) can be preserved.
Notifier listening is also ported.
2022-07-02 21:49:21 +02:00
|
|
|
void AbstractGridView::update_children_from_old(const AbstractView &old_view)
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
UI: Add AbstractView base class for views, unify reconstruction in there
No user visible changes expected.
There's plenty of duplicated code in the grid and the tree view, and I expect
this to become more. This starts the process of unifying these parts, which
should also make it easier to add new views. Complexity in the view classes is
reduced, and some type shenanigans for C compatibility and general view
management can be removed, since there is now a common base type.
For the start this ports some of the view reconstruction, where the view and
its items are compared to the version of itself in the previous redraw, so that
state (highlighted, active, renaming, collapsed, ...) can be preserved.
Notifier listening is also ported.
2022-07-02 21:49:21 +02:00
|
|
|
const AbstractGridView &old_grid_view = dynamic_cast<const AbstractGridView &>(old_view);
|
2022-06-16 11:29:20 +02:00
|
|
|
|
UI: Add AbstractView base class for views, unify reconstruction in there
No user visible changes expected.
There's plenty of duplicated code in the grid and the tree view, and I expect
this to become more. This starts the process of unifying these parts, which
should also make it easier to add new views. Complexity in the view classes is
reduced, and some type shenanigans for C compatibility and general view
management can be removed, since there is now a common base type.
For the start this ports some of the view reconstruction, where the view and
its items are compared to the version of itself in the previous redraw, so that
state (highlighted, active, renaming, collapsed, ...) can be preserved.
Notifier listening is also ported.
2022-07-02 21:49:21 +02:00
|
|
|
foreach_item([this, &old_grid_view](AbstractGridViewItem &new_item) {
|
|
|
|
|
const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_grid_view);
|
2022-06-16 11:29:20 +02:00
|
|
|
if (!matching_old_item) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_item.update_from_old(*matching_old_item);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GridViewStyle &AbstractGridView::get_style() const
|
|
|
|
|
{
|
|
|
|
|
return style_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int AbstractGridView::get_item_count() const
|
|
|
|
|
{
|
|
|
|
|
return items_.size();
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-12 11:41:00 +02:00
|
|
|
int AbstractGridView::get_item_count_filtered() const
|
|
|
|
|
{
|
|
|
|
|
if (item_count_filtered_) {
|
|
|
|
|
return *item_count_filtered_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
foreach_filtered_item([&i](const auto &) { i++; });
|
|
|
|
|
|
|
|
|
|
BLI_assert(i <= get_item_count());
|
|
|
|
|
item_count_filtered_ = i;
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 18:16:30 +02:00
|
|
|
void AbstractGridView::set_tile_size(int tile_width, int tile_height)
|
|
|
|
|
{
|
|
|
|
|
style_.tile_width = tile_width;
|
|
|
|
|
style_.tile_height = tile_height;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
GridViewStyle::GridViewStyle(int width, int height) : tile_width(width), tile_height(height) {}
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(identifier) {}
|
2022-06-16 11:29:20 +02:00
|
|
|
|
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:14:42 +02:00
|
|
|
bool AbstractGridViewItem::matches(const AbstractViewItem &other) const
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
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:14:42 +02:00
|
|
|
const AbstractGridViewItem &other_grid_item = dynamic_cast<const AbstractGridViewItem &>(other);
|
|
|
|
|
return identifier_ == other_grid_item.identifier_;
|
2022-06-16 11:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-26 16:47:17 +02:00
|
|
|
void AbstractGridViewItem::grid_tile_click_fn(bContext *C, void *but_arg1, void * /*arg2*/)
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
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:14:42 +02:00
|
|
|
uiButViewItem *view_item_but = (uiButViewItem *)but_arg1;
|
2022-06-16 11:29:20 +02:00
|
|
|
AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>(
|
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:14:42 +02:00
|
|
|
*view_item_but->view_item);
|
2022-06-16 11:29:20 +02:00
|
|
|
|
2023-07-26 16:47:17 +02:00
|
|
|
grid_item.activate(*C);
|
2022-06-16 11:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
|
|
|
|
|
{
|
|
|
|
|
const GridViewStyle &style = get_view().get_style();
|
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:14:42 +02:00
|
|
|
view_item_but_ = (uiButViewItem *)uiDefBut(&block,
|
|
|
|
|
UI_BTYPE_VIEW_ITEM,
|
2022-06-16 11:29:20 +02:00
|
|
|
0,
|
|
|
|
|
"",
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
style.tile_width,
|
|
|
|
|
style.tile_height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
"");
|
|
|
|
|
|
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:14:42 +02:00
|
|
|
view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this);
|
2023-02-03 16:12:14 +01:00
|
|
|
UI_but_func_set(view_item_but_, grid_tile_click_fn, view_item_but_, nullptr);
|
2022-06-16 11:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-13 18:11:14 +02:00
|
|
|
AbstractGridView &AbstractGridViewItem::get_view() const
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
|
|
|
|
if (UNLIKELY(!view_)) {
|
|
|
|
|
throw std::runtime_error(
|
|
|
|
|
"Invalid state, item must be added through AbstractGridView::add_item()");
|
|
|
|
|
}
|
2022-07-20 17:13:15 +02:00
|
|
|
return dynamic_cast<AbstractGridView &>(*view_);
|
2022-06-16 11:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
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
2023-07-11 14:30:26 +02:00
|
|
|
std::unique_ptr<DropTargetInterface> AbstractGridViewItem::create_item_drop_target()
|
|
|
|
|
{
|
|
|
|
|
return create_drop_target();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<GridViewItemDropTarget> AbstractGridViewItem::create_drop_target()
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GridViewItemDropTarget::GridViewItemDropTarget(AbstractGridView &view) : view_(view) {}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2022-06-16 11:29:20 +02:00
|
|
|
/**
|
|
|
|
|
* Helper for only adding layout items for grid items that are actually in view. 3 main functions:
|
|
|
|
|
* - #is_item_visible(): Query if an item of a given index is visible in the view (others should be
|
|
|
|
|
* skipped when building the layout).
|
|
|
|
|
* - #fill_layout_before_visible(): Add empty space to the layout before a visible row is drawn, so
|
|
|
|
|
* the layout height is the same as if all items were added (important to get the correct scroll
|
|
|
|
|
* height).
|
|
|
|
|
* - #fill_layout_after_visible(): Same thing, just adds empty space for after the last visible
|
|
|
|
|
* row.
|
|
|
|
|
*
|
|
|
|
|
* Does two assumptions:
|
|
|
|
|
* - Top-to-bottom flow (ymax = 0 and ymin < 0). If that's not good enough, View2D should
|
|
|
|
|
* probably provide queries for the scroll offset.
|
|
|
|
|
* - Only vertical scrolling. For horizontal scrolling, spacers would have to be added on the
|
|
|
|
|
* side(s) as well.
|
|
|
|
|
*/
|
|
|
|
|
class BuildOnlyVisibleButtonsHelper {
|
|
|
|
|
const View2D &v2d_;
|
|
|
|
|
const AbstractGridView &grid_view_;
|
|
|
|
|
const GridViewStyle &style_;
|
|
|
|
|
const int cols_per_row_ = 0;
|
|
|
|
|
/* Indices of items within the view. Calculated by constructor */
|
|
|
|
|
IndexRange visible_items_range_{};
|
|
|
|
|
|
|
|
|
|
public:
|
2022-06-25 19:05:31 -05:00
|
|
|
BuildOnlyVisibleButtonsHelper(const View2D &v2d,
|
2022-06-16 11:29:20 +02:00
|
|
|
const AbstractGridView &grid_view,
|
|
|
|
|
int cols_per_row);
|
|
|
|
|
|
|
|
|
|
bool is_item_visible(int item_idx) const;
|
2022-06-25 19:05:31 -05:00
|
|
|
void fill_layout_before_visible(uiBlock &block) const;
|
|
|
|
|
void fill_layout_after_visible(uiBlock &block) const;
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
IndexRange get_visible_range() const;
|
2022-06-25 19:05:31 -05:00
|
|
|
void add_spacer_button(uiBlock &block, int row_count) const;
|
2022-06-16 11:29:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d,
|
|
|
|
|
const AbstractGridView &grid_view,
|
|
|
|
|
const int cols_per_row)
|
|
|
|
|
: v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
|
|
|
|
|
{
|
|
|
|
|
visible_items_range_ = get_visible_range();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
|
|
|
|
|
{
|
|
|
|
|
int first_idx_in_view = 0;
|
|
|
|
|
|
|
|
|
|
const float scroll_ofs_y = abs(v2d_.cur.ymax - v2d_.tot.ymax);
|
|
|
|
|
if (!IS_EQF(scroll_ofs_y, 0)) {
|
2022-09-25 18:33:28 +10:00
|
|
|
const int scrolled_away_rows = int(scroll_ofs_y) / style_.tile_height;
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
first_idx_in_view = scrolled_away_rows * cols_per_row_;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-13 14:39:29 +02:00
|
|
|
const int view_height = BLI_rcti_size_y(&v2d_.mask);
|
|
|
|
|
const int count_rows_in_view = std::max(view_height / style_.tile_height, 1);
|
|
|
|
|
const int max_items_in_view = (count_rows_in_view + 1) * cols_per_row_;
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
BLI_assert(max_items_in_view > 0);
|
|
|
|
|
return IndexRange(first_idx_in_view, max_items_in_view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const
|
|
|
|
|
{
|
|
|
|
|
return visible_items_range_.contains(item_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const
|
|
|
|
|
{
|
2023-07-13 14:39:29 +02:00
|
|
|
const int first_idx_in_view = visible_items_range_.first();
|
|
|
|
|
if (first_idx_in_view < 1) {
|
2022-06-16 11:29:20 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2023-07-13 14:39:29 +02:00
|
|
|
const int tot_tiles_before_visible = first_idx_in_view;
|
|
|
|
|
const int scrolled_away_rows = tot_tiles_before_visible / cols_per_row_;
|
2022-06-16 11:29:20 +02:00
|
|
|
add_spacer_button(block, scrolled_away_rows);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const
|
|
|
|
|
{
|
2023-06-12 11:41:00 +02:00
|
|
|
const int last_item_idx = grid_view_.get_item_count_filtered() - 1;
|
2022-06-16 11:29:20 +02:00
|
|
|
const int last_visible_idx = visible_items_range_.last();
|
|
|
|
|
|
|
|
|
|
if (last_item_idx > last_visible_idx) {
|
2023-07-13 13:03:39 +02:00
|
|
|
const int remaining_rows = (cols_per_row_ > 0) ? ceilf((last_item_idx - last_visible_idx) /
|
|
|
|
|
float(cols_per_row_)) :
|
|
|
|
|
0;
|
2022-06-16 11:29:20 +02:00
|
|
|
BuildOnlyVisibleButtonsHelper::add_spacer_button(block, remaining_rows);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuildOnlyVisibleButtonsHelper::add_spacer_button(uiBlock &block, const int row_count) const
|
|
|
|
|
{
|
|
|
|
|
/* UI code only supports button dimensions of `signed short` size, the layout height we want to
|
|
|
|
|
* fill may be bigger than that. So add multiple labels of the maximum size if necessary. */
|
|
|
|
|
for (int remaining_rows = row_count; remaining_rows > 0;) {
|
|
|
|
|
const short row_count_this_iter = std::min(
|
|
|
|
|
std::numeric_limits<short>::max() / style_.tile_height, remaining_rows);
|
|
|
|
|
|
|
|
|
|
uiDefBut(&block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
"",
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
UI_UNIT_X,
|
|
|
|
|
row_count_this_iter * style_.tile_height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
"");
|
|
|
|
|
remaining_rows -= row_count_this_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
class GridViewLayoutBuilder {
|
|
|
|
|
uiBlock &block_;
|
|
|
|
|
|
|
|
|
|
friend class GridViewBuilder;
|
|
|
|
|
|
|
|
|
|
public:
|
2023-03-20 11:35:45 +01:00
|
|
|
GridViewLayoutBuilder(uiLayout &layout);
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
void build_from_view(const AbstractGridView &grid_view, const View2D &v2d) const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void build_grid_tile(uiLayout &grid_layout, AbstractGridViewItem &item) const;
|
|
|
|
|
|
|
|
|
|
uiLayout *current_layout() const;
|
|
|
|
|
};
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
GridViewLayoutBuilder::GridViewLayoutBuilder(uiLayout &layout) : block_(*uiLayoutGetBlock(&layout))
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GridViewLayoutBuilder::build_grid_tile(uiLayout &grid_layout,
|
|
|
|
|
AbstractGridViewItem &item) const
|
|
|
|
|
{
|
|
|
|
|
uiLayout *overlap = uiLayoutOverlap(&grid_layout);
|
2023-05-01 12:46:27 +02:00
|
|
|
uiLayoutSetFixedSize(overlap, true);
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
item.add_grid_tile_button(block_);
|
|
|
|
|
item.build_grid_tile(*uiLayoutRow(overlap, false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view,
|
|
|
|
|
const View2D &v2d) const
|
|
|
|
|
{
|
2023-03-20 11:35:45 +01:00
|
|
|
uiLayout *parent_layout = current_layout();
|
2022-06-16 11:29:20 +02:00
|
|
|
|
2023-05-01 12:46:27 +02:00
|
|
|
uiLayout &layout = *uiLayoutColumn(current_layout(), true);
|
2022-06-16 11:29:20 +02:00
|
|
|
const GridViewStyle &style = grid_view.get_style();
|
|
|
|
|
|
|
|
|
|
const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1);
|
|
|
|
|
|
|
|
|
|
BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row);
|
|
|
|
|
|
|
|
|
|
build_visible_helper.fill_layout_before_visible(block_);
|
|
|
|
|
|
|
|
|
|
int item_idx = 0;
|
2023-05-01 12:46:27 +02:00
|
|
|
uiLayout *row = nullptr;
|
2023-06-12 11:41:00 +02:00
|
|
|
grid_view.foreach_filtered_item([&](AbstractGridViewItem &item) {
|
2022-06-16 11:29:20 +02:00
|
|
|
/* Skip if item isn't visible. */
|
|
|
|
|
if (!build_visible_helper.is_item_visible(item_idx)) {
|
|
|
|
|
item_idx++;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-01 12:46:27 +02:00
|
|
|
/* Start a new row for every first item in the row. */
|
|
|
|
|
if ((item_idx % cols_per_row) == 0) {
|
|
|
|
|
row = uiLayoutRow(&layout, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
build_grid_tile(*row, item);
|
2022-06-16 11:29:20 +02:00
|
|
|
item_idx++;
|
|
|
|
|
});
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
UI_block_layout_set_current(&block_, parent_layout);
|
2022-06-16 11:29:20 +02:00
|
|
|
|
|
|
|
|
build_visible_helper.fill_layout_after_visible(block_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uiLayout *GridViewLayoutBuilder::current_layout() const
|
|
|
|
|
{
|
|
|
|
|
return block_.curlayout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
GridViewBuilder::GridViewBuilder(uiBlock & /*block*/) {}
|
2022-06-16 11:29:20 +02:00
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
void GridViewBuilder::build_grid_view(AbstractGridView &grid_view,
|
|
|
|
|
const View2D &v2d,
|
|
|
|
|
uiLayout &layout)
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
2023-03-20 11:35:45 +01:00
|
|
|
uiBlock &block = *uiLayoutGetBlock(&layout);
|
|
|
|
|
|
2022-06-16 11:29:20 +02:00
|
|
|
grid_view.build_items();
|
2023-03-20 11:35:45 +01:00
|
|
|
grid_view.update_from_old(block);
|
2022-06-16 11:29:20 +02:00
|
|
|
grid_view.change_state_delayed();
|
|
|
|
|
|
2023-03-20 11:35:45 +01:00
|
|
|
/* Ensure the given layout is actually active. */
|
|
|
|
|
UI_block_layout_set_current(&block, &layout);
|
|
|
|
|
|
|
|
|
|
GridViewLayoutBuilder builder(layout);
|
2022-06-16 11:29:20 +02:00
|
|
|
builder.build_from_view(grid_view, v2d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
PreviewGridItem::PreviewGridItem(StringRef identifier, StringRef label, int preview_icon_id)
|
|
|
|
|
: AbstractGridViewItem(identifier), label(label), preview_icon_id(preview_icon_id)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PreviewGridItem::build_grid_tile(uiLayout &layout) const
|
|
|
|
|
{
|
|
|
|
|
const GridViewStyle &style = get_view().get_style();
|
|
|
|
|
uiBlock *block = uiLayoutGetBlock(&layout);
|
|
|
|
|
|
|
|
|
|
uiBut *but = uiDefBut(block,
|
|
|
|
|
UI_BTYPE_PREVIEW_TILE,
|
|
|
|
|
0,
|
2023-08-03 16:54:39 +02:00
|
|
|
hide_label_ ? "" : label.c_str(),
|
2022-06-16 11:29:20 +02:00
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
style.tile_width,
|
|
|
|
|
style.tile_height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
"");
|
2023-08-03 16:54:39 +02:00
|
|
|
/* Draw icons that are not previews or images as normal icons with a fixed icon size. Otherwise
|
|
|
|
|
* they will be upscaled to the button size. Should probably be done by the widget code. */
|
|
|
|
|
const int is_preview_flag = (BKE_icon_is_preview(preview_icon_id) ||
|
|
|
|
|
BKE_icon_is_image(preview_icon_id)) ?
|
|
|
|
|
int(UI_BUT_ICON_PREVIEW) :
|
|
|
|
|
0;
|
2022-06-16 11:29:20 +02:00
|
|
|
ui_def_but_icon(but,
|
|
|
|
|
preview_icon_id,
|
|
|
|
|
/* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
|
2023-08-03 16:54:39 +02:00
|
|
|
UI_HAS_ICON | is_preview_flag);
|
|
|
|
|
UI_but_func_tooltip_label_set(but, [this](const uiBut * /*but*/) { return label; });
|
2023-05-04 13:08:46 +02:00
|
|
|
but->emboss = UI_EMBOSS_NONE;
|
2022-06-16 11:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PreviewGridItem::set_on_activate_fn(ActivateFn fn)
|
|
|
|
|
{
|
|
|
|
|
activate_fn_ = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PreviewGridItem::set_is_active_fn(IsActiveFn fn)
|
|
|
|
|
{
|
|
|
|
|
is_active_fn_ = fn;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-03 16:54:39 +02:00
|
|
|
void PreviewGridItem::hide_label()
|
|
|
|
|
{
|
|
|
|
|
hide_label_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-26 16:47:17 +02:00
|
|
|
void PreviewGridItem::on_activate(bContext &C)
|
2022-06-16 11:29:20 +02:00
|
|
|
{
|
|
|
|
|
if (activate_fn_) {
|
2023-07-26 16:47:17 +02:00
|
|
|
activate_fn_(C, *this);
|
2022-06-16 11:29:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<bool> PreviewGridItem::should_be_active() const
|
|
|
|
|
{
|
|
|
|
|
if (is_active_fn_) {
|
|
|
|
|
return is_active_fn_();
|
|
|
|
|
}
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::ui
|