Blender V5.0
abstract_view_item.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_context.hh"
10
11#include "BLI_fnmatch.h"
12#include "BLI_listbase.h"
13
14#include "WM_api.hh"
15
17#include "interface_intern.hh"
18
19#include "UI_abstract_view.hh"
20
21#include <stdexcept>
22
23namespace blender::ui {
24
25/* ---------------------------------------------------------------------- */
28
36
38
39/* ---------------------------------------------------------------------- */
42
44{
45 /* Do nothing by default. */
46}
47
48std::optional<bool> AbstractViewItem::should_be_active() const
49{
50 return std::nullopt;
51}
52
54{
55 BLI_assert_msg(get_view().is_reconstructed(),
56 "Item activation cannot be done until reconstruction is completed");
57
58 if (!is_activatable_) {
59 return false;
60 }
61 if (is_active()) {
62 return false;
63 }
64
65 /* Deactivate other items in the view. */
66 this->get_view().foreach_view_item([](auto &item) { item.deactivate(); });
67
68 is_active_ = true;
69 return true;
70}
71
73{
75 on_activate(C);
76 }
77
78 /* Make sure active item is selected. */
79 if (is_active()) {
80 set_selected(true);
81 }
82}
83
85{
87 this->activate(C);
88 }
89 else {
90 this->set_state_active();
91 }
92}
93
95{
96 is_active_ = false;
97 is_selected_ = false;
98}
99
100std::optional<bool> AbstractViewItem::should_be_selected() const
101{
102 return std::nullopt;
103}
104
109
111
112/* ---------------------------------------------------------------------- */
115
117{
118 if (const std::optional<bool> should_be_active = this->should_be_active()) {
119 if (*should_be_active) {
120 /* Don't call #activate() here, since this reflects an external state change and therefore
121 * shouldn't call #on_activate(). */
123 }
124 else if (is_active_) {
125 is_active_ = false;
126 this->set_selected(false);
127 }
128 }
129 if (std::optional<bool> is_selected = should_be_selected()) {
130 set_selected(is_selected.value_or(false));
131 }
132}
133
135
136/* ---------------------------------------------------------------------- */
139
141{
142 /* No renaming by default. */
143 return false;
144}
145bool AbstractViewItem::rename(const bContext & /*C*/, StringRefNull /*new_name*/)
146{
147 /* No renaming by default. */
148 return false;
149}
150
152{
153 /* No rename string by default. */
154 return {};
155}
156
158{
159 return is_renaming_;
160}
161
163{
164 AbstractView &view = this->get_view();
165 if (view.is_renaming() || !supports_renaming()) {
166 return;
167 }
168
169 if (view.begin_renaming()) {
170 is_renaming_ = true;
171 }
172
173 StringRef initial_str = this->get_rename_string();
174 std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer()));
175}
176
178{
179 const AbstractView &view = this->get_view();
180 rename(C, view.get_rename_buffer().data());
181 end_renaming();
182}
183
185{
186 if (!is_renaming()) {
187 return;
188 }
189
190 is_renaming_ = false;
191
192 AbstractView &view = this->get_view();
193 view.end_renaming();
194}
195
197{
198 /* A minimal sanity check, can't do much more here. */
199 BLI_assert(rename_but.type == ButType::Text && rename_but.poin);
200
201 for (const std::unique_ptr<uiBut> &but : rename_but.block->buttons) {
202 if (but->type != ButType::ViewItem) {
203 continue;
204 }
205
206 uiButViewItem *view_item_but = (uiButViewItem *)but.get();
207 AbstractViewItem *item = reinterpret_cast<AbstractViewItem *>(view_item_but->view_item);
208 const AbstractView &view = item->get_view();
209
210 if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) {
211 return item;
212 }
213 }
214
215 return nullptr;
216}
217
218static void rename_button_fn(bContext *C, void *arg, char * /*origstr*/)
219{
220 const uiBut *rename_but = static_cast<uiBut *>(arg);
222 BLI_assert(item);
223 item->rename_apply(*C);
224}
225
227{
228 AbstractView &view = this->get_view();
229 uiBut *rename_but = uiDefBut(&block,
231 1,
232 "",
233 0,
234 0,
235 UI_UNIT_X * 10,
236 UI_UNIT_Y,
237 view.get_rename_buffer().data(),
238 1.0f,
239 view.get_rename_buffer().size(),
240 "");
241
242 /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the
243 * callback is executed. */
244 UI_but_func_rename_set(rename_but, rename_button_fn, rename_but);
245 UI_but_flag_disable(rename_but, UI_BUT_UNDO);
246
247 const bContext *evil_C = reinterpret_cast<bContext *>(block.evil_C);
248 ARegion *region = CTX_wm_region_popup(evil_C) ? CTX_wm_region_popup(evil_C) :
249 CTX_wm_region(evil_C);
250 /* Returns false if the button was removed. */
251 if (UI_but_active_only(evil_C, region, &block, rename_but) == false) {
252 end_renaming();
253 }
254}
255
257{
258 /* No deletion by default. Needs type specific implementation. */
259}
260
262{
263 /* No action by default. Needs type specific implementation. */
264}
265
267
268/* ---------------------------------------------------------------------- */
271
273{
274 /* No context menu by default. */
275}
276
278
279/* ---------------------------------------------------------------------- */
282
284{
286 return fnmatch(filter_string.c_str(), name.data(), FNM_CASEFOLD) == 0;
287}
288
290{
291 BLI_assert(get_view().needs_filtering_ == false);
293}
294
296
297/* ---------------------------------------------------------------------- */
300
301std::unique_ptr<AbstractViewItemDragController> AbstractViewItem::create_drag_controller() const
302{
303 /* There's no drag controller (and hence no drag support) by default. */
304 return nullptr;
305}
306
307std::unique_ptr<DropTargetInterface> AbstractViewItem::create_item_drop_target()
308{
309 /* There's no drop target (and hence no drop support) by default. */
310 return nullptr;
311}
312
313std::optional<std::string> AbstractViewItem::debug_name() const
314{
315 return {};
316}
317
319
321{
322 /* Do nothing by default. */
323}
324
326
327/* ---------------------------------------------------------------------- */
330
332{
333 if (UNLIKELY(!view_)) {
334 throw std::runtime_error(
335 "Invalid state, item must be registered through AbstractView::register_item()");
336 }
337 return *view_;
338}
339
344
349
354
359
364
369
374
376{
377 return is_interactive_;
378}
379
381{
382 BLI_assert_msg(this->get_view().is_reconstructed(),
383 "State cannot be queried until reconstruction is completed");
384 return is_active_;
385}
386
388{
389 BLI_assert_msg(this->get_view().is_reconstructed(),
390 "State can't be queried until reconstruction is completed");
391 return is_selected_;
392}
393
398
400
401} // namespace blender::ui
402
403/* ---------------------------------------------------------------------- */
406
407namespace blender::ui {
408
414 public:
415 static bool matches(const AbstractViewItem &a, const AbstractViewItem &b)
416 {
417 if (typeid(a) != typeid(b)) {
418 return false;
419 }
420 /* TODO should match the view as well. */
421 return a.matches(b);
422 }
423
425 {
426 std::swap(a.view_item_but_, b.view_item_but_);
427 }
428};
429
430} // namespace blender::ui
431
432using namespace blender::ui;
433
438
443
445{
446 const AbstractView &view = item.get_view();
447 return !view.is_renaming() && item.supports_renaming();
448}
449
454
456{
457 return item.create_drag_controller() != nullptr;
458}
459
461{
462 return item.get_view().get_popup_keep_open();
463}
464
466{
467 const std::unique_ptr<AbstractViewItemDragController> drag_controller =
469 if (!drag_controller) {
470 return false;
471 }
472
473 if (const std::optional<eWM_DragDataType> drag_type = drag_controller->get_drag_type()) {
475 &C, ICON_NONE, *drag_type, drag_controller->create_drag_data(), WM_DRAG_FREE_DATA);
476 }
477 drag_controller->on_drag_start(C);
478
479 /* Make sure the view item is highlighted as active when dragging from it. This is useful user
480 * feedback. */
481 item.set_state_active();
482
483 return true;
484}
485
ARegion * CTX_wm_region_popup(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define UNLIKELY(x)
static AppView * view
#define C
Definition RandGen.cpp:29
void UI_but_flag_disable(uiBut *but, int flag)
bool UI_view_item_can_rename(const blender::ui::AbstractViewItem &item)
#define UI_UNIT_Y
bool UI_view_item_drag_start(bContext &C, blender::ui::AbstractViewItem &item)
@ UI_BUT_UNDO
bool UI_view_item_popup_keep_open(const blender::ui::AbstractViewItem &item)
void UI_view_item_begin_rename(blender::ui::AbstractViewItem &item)
void UI_but_func_rename_set(uiBut *but, uiButHandleRenameFunc func, void *arg1)
bool UI_view_item_matches(const blender::ui::AbstractViewItem &a, const blender::ui::AbstractViewItem &b)
bool UI_but_active_only(const bContext *C, ARegion *region, uiBlock *block, uiBut *but)
bool UI_view_item_supports_drag(const blender::ui::AbstractViewItem &item)
#define UI_UNIT_X
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
@ WM_DRAG_FREE_DATA
Definition WM_types.hh:1230
constexpr const char * c_str() const
virtual void delete_item(bContext *C)
virtual void set_selected(const bool select)
virtual void update_from_old(const AbstractViewItem &old)
void add_rename_button(uiBlock &block)
virtual bool should_be_filtered_visible(StringRefNull filter_string) const
virtual void on_activate(bContext &C)
virtual std::unique_ptr< DropTargetInterface > create_item_drop_target()
virtual bool supports_renaming() const
uiButViewItem * view_item_button() const
virtual std::optional< bool > should_be_active() const
virtual bool rename(const bContext &C, StringRefNull new_name)
virtual bool matches(const AbstractViewItem &other) const =0
virtual void build_context_menu(bContext &C, uiLayout &column) const
virtual std::optional< bool > should_be_selected() const
virtual StringRef get_rename_string() const
void rename_apply(const bContext &C)
void activate_for_context_menu(bContext &C)
virtual std::unique_ptr< AbstractViewItemDragController > create_drag_controller() const
virtual std::optional< std::string > debug_name() const
virtual void foreach_view_item(FunctionRef< void(AbstractViewItem &)> iter_fn) const =0
static bool matches(const AbstractViewItem &a, const AbstractViewItem &b)
static void swap_button_pointers(AbstractViewItem &a, AbstractViewItem &b)
#define select(A, B, C)
void ui_view_item_swap_button_pointers(blender::ui::AbstractViewItem &a, blender::ui::AbstractViewItem &b)
static void rename_button_fn(bContext *C, void *arg, char *)
static AbstractViewItem * find_item_from_rename_button(const uiBut &rename_but)
const char * name
blender::Vector< std::unique_ptr< uiBut > > buttons
blender::ui::AbstractViewItem * view_item
ButType type
uiBlock * block
void WM_event_start_drag(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)