WIP: Rewrite asset browser as separate editor #107576
|
@ -874,7 +874,7 @@ def km_view2d_buttons_list(_params):
|
|||
return keymap
|
||||
|
||||
|
||||
def km_user_interface(_params):
|
||||
def km_user_interface(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"User Interface",
|
||||
|
@ -908,6 +908,8 @@ def km_user_interface(_params):
|
|||
("ui.reset_default_button", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("all", True)]}),
|
||||
# UI lists (polls check if there's a UI list under the cursor).
|
||||
("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
|
||||
# UI views (polls check if there's a UI view under the cursor).
|
||||
*_template_items_select_actions(params, "ui.view_select_all"),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
|
|
@ -15,12 +15,12 @@ extern "C" {
|
|||
struct KDTree_1d;
|
||||
struct wmOperatorType;
|
||||
|
||||
enum {
|
||||
typedef enum SelectAction {
|
||||
SEL_TOGGLE = 0,
|
||||
SEL_SELECT = 1,
|
||||
SEL_DESELECT = 2,
|
||||
SEL_INVERT = 3,
|
||||
};
|
||||
} SelectAction;
|
||||
|
||||
typedef enum WalkSelectDirection {
|
||||
UI_SELECT_WALK_UP,
|
||||
|
|
|
@ -76,6 +76,12 @@ class AbstractView {
|
|||
*/
|
||||
virtual std::unique_ptr<AbstractViewDropTarget> create_drop_target();
|
||||
|
||||
/**
|
||||
* Iterate over all elements in the view executing \a iter_fn for each. Typically views would
|
||||
* want to implement their own `foreach_item()` function with a more specific type.
|
||||
*/
|
||||
virtual void foreach_abstract_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const = 0;
|
||||
|
||||
/** Listen to a notifier, returning true if a redraw is needed. */
|
||||
virtual bool listen(const wmNotifier &) const;
|
||||
|
||||
|
@ -129,6 +135,7 @@ class AbstractViewItem {
|
|||
AbstractView *view_ = nullptr;
|
||||
bool is_interactive_ = true;
|
||||
bool is_active_ = false;
|
||||
bool is_selected_ = false;
|
||||
bool is_renaming_ = false;
|
||||
|
||||
public:
|
||||
|
@ -183,6 +190,22 @@ class AbstractViewItem {
|
|||
*/
|
||||
bool is_active() const;
|
||||
|
||||
/**
|
||||
* Mark this item as selected.
|
||||
* \return True if the selection state changed (redraw needed).
|
||||
*/
|
||||
bool select();
|
||||
/**
|
||||
* Mark this item as not selected.
|
||||
* \return True if the selection state changed (redraw needed).
|
||||
*/
|
||||
bool deselect();
|
||||
/**
|
||||
* Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we
|
||||
* can't be sure about the item state.
|
||||
*/
|
||||
bool is_selected() const;
|
||||
|
||||
bool is_renaming() const;
|
||||
void begin_renaming();
|
||||
void end_renaming();
|
||||
|
|
|
@ -105,6 +105,7 @@ class AbstractGridView : public AbstractView {
|
|||
AbstractGridView();
|
||||
virtual ~AbstractGridView() = default;
|
||||
|
||||
void foreach_abstract_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const override;
|
||||
using ItemIterFn = FunctionRef<void(AbstractGridViewItem &)>;
|
||||
void foreach_item(ItemIterFn iter_fn) const;
|
||||
|
||||
|
|
|
@ -3260,6 +3260,7 @@ void UI_interface_tag_script_reload(void);
|
|||
|
||||
bool UI_view_item_is_interactive(const uiViewItemHandle *item_handle);
|
||||
bool UI_view_item_is_active(const uiViewItemHandle *item_handle);
|
||||
bool UI_view_item_is_selected(const uiViewItemHandle *item_handle);
|
||||
bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle);
|
||||
/**
|
||||
* Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around
|
||||
|
@ -3284,7 +3285,9 @@ bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_);
|
|||
* \param xy: Coordinate to find a view item at, in window space.
|
||||
* \param pad: Extra padding added to the bounding box of the view.
|
||||
*/
|
||||
uiViewHandle *UI_region_view_find_at(const struct ARegion *region, const int xy[2], int pad);
|
||||
uiViewHandle *UI_region_view_find_at(const struct ARegion *region,
|
||||
const int xy[2],
|
||||
int pad CPP_ARG_DEFAULT(0));
|
||||
/**
|
||||
* \param xy: Coordinate to find a view item at, in window space.
|
||||
*/
|
||||
|
|
|
@ -121,6 +121,11 @@ std::unique_ptr<DropTargetInterface> view_item_drop_target(uiViewItemHandle *ite
|
|||
std::unique_ptr<DropTargetInterface> region_views_find_drop_target_at(const ARegion *region,
|
||||
const int xy[2]);
|
||||
|
||||
/**
|
||||
* \return True if any selection state changed (redraw necessary).
|
||||
*/
|
||||
bool view_select_all_items(uiViewHandle *view_handle, int /*SelectAction*/ action);
|
||||
|
||||
} // namespace blender::ui
|
||||
|
||||
enum eUIListFilterResult {
|
||||
|
|
|
@ -67,7 +67,6 @@ class TreeViewItemContainer {
|
|||
|
||||
/* Keep ENUM_OPERATORS() below updated! */
|
||||
};
|
||||
using ItemIterFn = FunctionRef<void(AbstractTreeViewItem &)>;
|
||||
|
||||
/**
|
||||
* Convenience wrapper constructing the item by forwarding given arguments to the constructor of
|
||||
|
@ -90,7 +89,8 @@ class TreeViewItemContainer {
|
|||
AbstractTreeViewItem &add_tree_item(std::unique_ptr<AbstractTreeViewItem> item);
|
||||
|
||||
protected:
|
||||
void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
|
||||
void foreach_item_recursive(FunctionRef<void(AbstractTreeViewItem &)> iter_fn,
|
||||
IterOptions options = IterOptions::None) const;
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(TreeViewItemContainer::IterOptions,
|
||||
|
@ -116,7 +116,9 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer {
|
|||
public:
|
||||
virtual ~AbstractTreeView() = default;
|
||||
|
||||
void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
|
||||
void foreach_abstract_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const override;
|
||||
void foreach_item(FunctionRef<void(AbstractTreeViewItem &)> iter_fn,
|
||||
IterOptions options = IterOptions::None) const;
|
||||
|
||||
/** Visual feature: Define a number of item rows the view will always show at minimum. If there
|
||||
* are fewer items, empty dummy items will be added. These contribute to the view bounds, so the
|
||||
|
|
|
@ -2276,7 +2276,10 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
|
|||
|
||||
is_push = -1;
|
||||
if (view_item_but->view_item) {
|
||||
is_push = UI_view_item_is_active(view_item_but->view_item);
|
||||
/* Consider both active and selected as pushed state. Drawing can differentiate the state
|
||||
* further for visual feedback. */
|
||||
is_push = UI_view_item_is_active(view_item_but->view_item) ||
|
||||
UI_view_item_is_selected(view_item_but->view_item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2368,6 +2368,48 @@ static void UI_OT_list_start_filter(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UI View Select All Operator
|
||||
* \{ */
|
||||
|
||||
static bool ui_view_under_cursor_poll(bContext *C)
|
||||
{
|
||||
const wmWindow *win = CTX_wm_window(C);
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
if (region == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return UI_region_view_find_at(region, win->eventstate->xy) != nullptr;
|
||||
}
|
||||
|
||||
static int ui_view_select_all_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
uiViewHandle *view = UI_region_view_find_at(region, event->xy);
|
||||
|
||||
const int action = RNA_enum_get(op->ptr, "action");
|
||||
|
||||
if (view_select_all_items(view, action)) {
|
||||
ED_region_tag_redraw(region);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void UI_OT_view_select_all(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "View Select All";
|
||||
ot->idname = "UI_OT_view_select_all";
|
||||
ot->description = "Select or deselect all items in the view";
|
||||
|
||||
ot->invoke = ui_view_select_all_invoke;
|
||||
ot->poll = ui_view_under_cursor_poll;
|
||||
|
||||
WM_operator_properties_select_all(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UI View Drop Operator
|
||||
* \{ */
|
||||
|
@ -2555,6 +2597,7 @@ void ED_operatortypes_ui(void)
|
|||
|
||||
WM_operatortype_append(UI_OT_list_start_filter);
|
||||
|
||||
WM_operatortype_append(UI_OT_view_select_all);
|
||||
WM_operatortype_append(UI_OT_view_drop);
|
||||
WM_operatortype_append(UI_OT_view_item_rename);
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* \ingroup edinterface
|
||||
*/
|
||||
|
||||
#include "ED_select_utils.h"
|
||||
|
||||
#include "interface_intern.hh"
|
||||
|
||||
#include "UI_abstract_view.hh"
|
||||
|
@ -117,6 +119,70 @@ std::optional<rcti> AbstractView::get_bounds() const
|
|||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Selection API
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* If \a action is #SEL_TOGGLE, check the selection if the actual action to apply should be
|
||||
* #SEL_DESELECT or #SEL_SELECT and return that.
|
||||
*/
|
||||
static SelectAction select_all_refine_action_type(const AbstractView &view, SelectAction action)
|
||||
{
|
||||
if (action != SEL_TOGGLE) {
|
||||
return action;
|
||||
}
|
||||
|
||||
bool any_selected = false;
|
||||
view.foreach_abstract_item([&any_selected](AbstractViewItem &item) {
|
||||
if (item.is_selected()) {
|
||||
any_selected = true;
|
||||
}
|
||||
});
|
||||
|
||||
return any_selected ? SEL_DESELECT : SEL_SELECT;
|
||||
}
|
||||
|
||||
bool view_select_all_items(uiViewHandle *view_handle, const int /*SelectAction*/ action)
|
||||
{
|
||||
AbstractView &view = reinterpret_cast<AbstractView &>(*view_handle);
|
||||
const SelectAction refined_action = select_all_refine_action_type(view, SelectAction(action));
|
||||
|
||||
bool changed = false;
|
||||
view.foreach_abstract_item([&changed, refined_action](AbstractViewItem &item) {
|
||||
switch (refined_action) {
|
||||
case SEL_SELECT:
|
||||
if (item.select()) {
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case SEL_DESELECT:
|
||||
if (item.deselect()) {
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case SEL_INVERT:
|
||||
if (item.is_selected()) {
|
||||
item.deselect();
|
||||
}
|
||||
else {
|
||||
item.select();
|
||||
}
|
||||
changed = true;
|
||||
break;
|
||||
case SEL_TOGGLE:
|
||||
BLI_assert_msg(false,
|
||||
"TOGGLE action should have been refined to be either SELECT or DESELECT at "
|
||||
"this point");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name General API functions
|
||||
* \{ */
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace blender::ui {
|
|||
void AbstractViewItem::update_from_old(const AbstractViewItem &old)
|
||||
{
|
||||
is_active_ = old.is_active_;
|
||||
is_selected_ = old.is_selected_;
|
||||
is_renaming_ = old.is_renaming_;
|
||||
}
|
||||
|
||||
|
@ -221,6 +222,31 @@ bool AbstractViewItem::is_active() const
|
|||
return is_active_;
|
||||
}
|
||||
|
||||
bool AbstractViewItem::select()
|
||||
{
|
||||
if (is_selected_) {
|
||||
return false;
|
||||
}
|
||||
is_selected_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractViewItem::deselect()
|
||||
{
|
||||
if (!is_selected_) {
|
||||
return false;
|
||||
}
|
||||
is_selected_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractViewItem::is_selected() const
|
||||
{
|
||||
BLI_assert_msg(get_view().is_reconstructed(),
|
||||
"State can't be queried until reconstruction is completed");
|
||||
return is_selected_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@ -300,6 +326,12 @@ bool UI_view_item_is_active(const uiViewItemHandle *item_handle)
|
|||
return item.is_active();
|
||||
}
|
||||
|
||||
bool UI_view_item_is_selected(const uiViewItemHandle *item_handle)
|
||||
{
|
||||
const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
|
||||
return item.is_selected();
|
||||
}
|
||||
|
||||
bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle)
|
||||
{
|
||||
const AbstractViewItem &a = reinterpret_cast<const AbstractViewItem &>(*a_handle);
|
||||
|
|
|
@ -35,6 +35,11 @@ AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridVie
|
|||
return added_item;
|
||||
}
|
||||
|
||||
void AbstractGridView::foreach_abstract_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const
|
||||
{
|
||||
foreach_item([&iter_fn](AbstractGridViewItem &item) { iter_fn(item); });
|
||||
}
|
||||
|
||||
void AbstractGridView::foreach_item(ItemIterFn iter_fn) const
|
||||
{
|
||||
for (const auto &item_ptr : items_) {
|
||||
|
|
|
@ -47,7 +47,8 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
|
|||
return added_item;
|
||||
}
|
||||
|
||||
void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const
|
||||
void TreeViewItemContainer::foreach_item_recursive(
|
||||
FunctionRef<void(AbstractTreeViewItem &)> iter_fn, IterOptions options) const
|
||||
{
|
||||
for (const auto &child : children_) {
|
||||
iter_fn(*child);
|
||||
|
@ -61,7 +62,13 @@ void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptio
|
|||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const
|
||||
void AbstractTreeView::foreach_abstract_item(FunctionRef<void(AbstractViewItem &)> iter_fn) const
|
||||
{
|
||||
foreach_item_recursive([&iter_fn](AbstractTreeViewItem &item) { iter_fn(item); });
|
||||
}
|
||||
|
||||
void AbstractTreeView::foreach_item(FunctionRef<void(AbstractTreeViewItem &)> iter_fn,
|
||||
IterOptions options) const
|
||||
{
|
||||
foreach_item_recursive(iter_fn, options);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue