Blender V5.0
interface_view.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
19
20#include <memory>
21
22#include "DNA_screen_types.h"
23
24#include "BKE_screen.hh"
25
26#include "BLI_listbase.h"
27#include "BLI_map.hh"
28#include "BLI_rect.h"
29
30#include "ED_screen.hh"
31
32#include "interface_intern.hh"
33
34#include "UI_abstract_view.hh"
35#include "UI_grid_view.hh"
36#include "UI_tree_view.hh"
37
38using namespace blender;
39using namespace blender::ui;
40
44struct ViewLink : public Link {
45 std::string idname;
46 std::unique_ptr<AbstractView> view;
47
48 static void views_bounds_calc(const uiBlock &block);
49};
50
51template<class T>
53 StringRef idname,
54 std::unique_ptr<AbstractView> view)
55{
57
58 ViewLink *view_link = MEM_new<ViewLink>(__func__);
59 BLI_addtail(&block.views, view_link);
60
61 view_link->view = std::move(view);
62 view_link->idname = idname;
63
64 return dynamic_cast<T *>(view_link->view.get());
65}
66
68 StringRef idname,
69 std::unique_ptr<AbstractGridView> grid_view)
70{
71 return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_view));
72}
73
75 StringRef idname,
76 std::unique_ptr<AbstractTreeView> tree_view)
77{
78 return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view));
79}
80
82{
83 LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
84 MEM_delete(link);
85 }
86}
87
89{
90 Map<AbstractView *, rcti> views_bounds;
91
92 rcti minmax;
93 BLI_rcti_init_minmax(&minmax);
94 LISTBASE_FOREACH (ViewLink *, link, &block.views) {
95 views_bounds.add(link->view.get(), minmax);
96 }
97
98 for (const std::unique_ptr<uiBut> &but : block.buttons) {
99 if (but->type != ButType::ViewItem) {
100 continue;
101 }
102 uiButViewItem *view_item_but = static_cast<uiButViewItem *>(but.get());
103 if (!view_item_but->view_item) {
104 continue;
105 }
106
107 /* Get the view from the button. */
108 AbstractViewItem &view_item = reinterpret_cast<AbstractViewItem &>(*view_item_but->view_item);
109 AbstractView &view = view_item.get_view();
110
111 rcti &bounds = views_bounds.lookup(&view);
112 rcti but_rcti{};
113 BLI_rcti_rctf_copy_round(&but_rcti, &view_item_but->rect);
114 BLI_rcti_do_minmax_rcti(&bounds, &but_rcti);
115 }
116
117 for (const auto item : views_bounds.items()) {
118 const rcti &bounds = item.value;
120 continue;
121 }
122
123 AbstractView &view = *item.key;
124 view.bounds_ = bounds;
125 }
126}
127
129 const uiBlock &block,
131{
132 StringRef idname = [&]() -> StringRef {
133 LISTBASE_FOREACH (ViewLink *, link, &block.views) {
134 if (link->view.get() == &view) {
135 return link->idname;
136 }
137 }
138 return "";
139 }();
140
141 if (idname.is_empty()) {
143 return;
144 }
145
146 LISTBASE_FOREACH (uiViewStateLink *, stored_state, &region.view_states) {
147 if (stored_state->idname == idname) {
148 view.persistent_state_apply(stored_state->state);
149 }
150 }
151}
152
154{
155 LISTBASE_FOREACH (uiViewStateLink *, stored_state, &region.view_states) {
156 if (link.idname == stored_state->idname) {
157 return stored_state;
158 }
159 }
160
161 uiViewStateLink *new_state = MEM_callocN<uiViewStateLink>(__func__);
162 link.idname.copy(new_state->idname, sizeof(new_state->idname));
163 BLI_addhead(&region.view_states, new_state);
164 return new_state;
165}
166
167void ui_block_views_end(ARegion *region, const uiBlock *block)
168{
170
171 if (region && region->regiontype != RGN_TYPE_TEMPORARY) {
172 LISTBASE_FOREACH (const ViewLink *, link, &block->views) {
173 /* Ensure persistent view state storage for writing to files if needed. */
174 if (std::optional<uiViewState> temp_state = link->view->persistent_state()) {
175 uiViewStateLink *state_link = ensure_view_state(*region, *link);
176 state_link->state = *temp_state;
177 }
178 }
179 }
180}
181
182void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
183{
184 ARegion *region = listener_params->region;
185
186 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
187 if (view_link->view->listen(*listener_params->notifier)) {
188 ED_region_tag_redraw(region);
189 }
190 }
191}
192
193void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block)
194{
195 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
196 view_link->view->draw_overlays(*region, *block);
197 }
198}
199
201 const int xy[2],
202 const int pad)
203{
204 /* NOTE: Similar to #ui_but_find_mouse_over_ex(). */
205
206 if (!ui_region_contains_point_px(region, xy)) {
207 return nullptr;
208 }
209 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
210 float mx = xy[0], my = xy[1];
211 ui_window_to_block_fl(region, block, &mx, &my);
212
213 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
214 std::optional<rcti> bounds = view_link->view->get_bounds();
215 if (!bounds) {
216 continue;
217 }
218
219 rcti padded_bounds = *bounds;
220 if (pad) {
221 BLI_rcti_pad(&padded_bounds, pad, pad);
222 }
223 if (BLI_rcti_isect_pt(&padded_bounds, mx, my)) {
224 return view_link->view.get();
225 }
226 }
227 }
228
229 return nullptr;
230}
231
233{
235 if (!item_but) {
236 return nullptr;
237 }
238
239 return item_but->view_item;
240}
241
243{
245 if (!item_but) {
246 return nullptr;
247 }
248
249 return item_but->view_item;
250}
251
256
258{
259 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
260 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
261 view_link->view->clear_search_highlight();
262 }
263 }
264}
265
266namespace blender::ui {
267
268std::unique_ptr<DropTargetInterface> region_views_find_drop_target_at(const ARegion *region,
269 const int xy[2])
270{
272 if (std::unique_ptr<DropTargetInterface> target = item->create_item_drop_target()) {
273 return target;
274 }
275 }
276
277 /* Get style for some sensible padding around the view items. */
278 const uiStyle *style = UI_style_get_dpi();
279 if (AbstractView *view = UI_region_view_find_at(region, xy, style->buttonspacex)) {
280 if (std::unique_ptr<DropTargetInterface> target = view->create_drop_target()) {
281 return target;
282 }
283 }
284
285 if (AbstractView *view = UI_region_view_find_at(region, xy, 0)) {
286 /* If we are above a tree, but not hovering any specific element, dropping something should
287 * insert it after the last item. */
288 if (AbstractTreeView *tree_view = dynamic_cast<AbstractTreeView *>(view)) {
289 /* Find the last item which we want to drop below. */
290 AbstractTreeViewItem *last_item = nullptr;
291 tree_view->foreach_root_item([&](AbstractTreeViewItem &item) {
292 if (!item.is_interactive()) {
293 return;
294 }
295 last_item = &item;
296 });
297 if (last_item) {
298 return last_item->create_item_drop_target();
299 }
300 }
301 }
302
303 return nullptr;
304}
305
306} // namespace blender::ui
307
309{
310 /* First get the `idname` of the view we're looking for. */
311 LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
312 if (view_link->view.get() == &view) {
313 return view_link->idname;
314 }
315 }
316
317 return {};
318}
319
320template<class T>
322 const T &new_view)
323{
324 uiBlock *old_block = new_block.oldblock;
325 if (!old_block) {
326 return nullptr;
327 }
328
329 StringRef idname = ui_block_view_find_idname(new_block, new_view);
330 if (idname.is_empty()) {
331 return nullptr;
332 }
333
334 LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
335 if (old_view_link->idname == idname) {
336 return dynamic_cast<T *>(old_view_link->view.get());
337 }
338 }
339
340 return nullptr;
341}
342
348
350 const uiBlock &new_block, const ui::AbstractViewItem &new_item)
351{
352 uiBlock *old_block = new_block.oldblock;
353 if (!old_block) {
354 return nullptr;
355 }
356
358 new_block, new_item.get_view());
359 if (!old_view) {
360 return nullptr;
361 }
362
363 for (const std::unique_ptr<uiBut> &old_but : old_block->buttons) {
364 if (old_but->type != ButType::ViewItem) {
365 continue;
366 }
367 uiButViewItem *old_item_but = (uiButViewItem *)old_but.get();
368 if (!old_item_but->view_item) {
369 continue;
370 }
371 AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item);
372 /* Check if the item is from the expected view. */
373 if (&old_item.get_view() != old_view) {
374 continue;
375 }
376
377 if (UI_view_item_matches(new_item, old_item)) {
378 return old_item_but;
379 }
380 }
381
382 return nullptr;
383}
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
void BLI_rcti_init_minmax(struct rcti *rect)
Definition rct.cc:474
void BLI_rcti_rctf_copy_round(struct rcti *dst, const struct rctf *src)
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
Definition rct.cc:629
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
bool BLI_rcti_is_empty(const struct rcti *rect)
void BLI_rcti_do_minmax_rcti(struct rcti *rect, const struct rcti *other)
@ RGN_TYPE_TEMPORARY
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
static AppView * view
blender::ui::AbstractView * UI_region_view_find_at(const ARegion *region, const int xy[2], int pad)
const uiStyle * UI_style_get_dpi()
bool UI_view_item_matches(const blender::ui::AbstractViewItem &a, const blender::ui::AbstractViewItem &b)
blender::ui::AbstractViewItem * UI_region_views_find_item_at(const ARegion &region, const int xy[2])
int pad[32 - sizeof(int)]
long long int int64_t
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
ItemIterator items() const &
Definition BLI_map.hh:902
constexpr bool is_empty() const
constexpr int64_t size() const
Abstract base class for defining a customizable tree-view item.
std::unique_ptr< DropTargetInterface > create_item_drop_target() final
Definition tree_view.cc:629
void ui_window_to_block_fl(const ARegion *region, const uiBlock *block, float *x, float *y)
Definition interface.cc:191
uiBut uiBut * ui_view_item_find_active(const ARegion *region)
uiBut * ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) ATTR_NONNULL(1
bool ui_region_contains_point_px(const ARegion *region, const int xy[2]) ATTR_NONNULL(1
void UI_region_views_clear_search_highlight(const ARegion *region)
blender::ui::AbstractView * UI_region_view_find_at(const ARegion *region, const int xy[2], const int pad)
void ui_block_views_end(ARegion *region, const uiBlock *block)
void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block)
void ui_block_view_persistent_state_restore(const ARegion &region, const uiBlock &block, ui::AbstractView &view)
uiButViewItem * ui_block_view_find_matching_view_item_but_in_old_block(const uiBlock &new_block, const ui::AbstractViewItem &new_item)
static T * ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block, const T &new_view)
AbstractGridView * UI_block_add_view(uiBlock &block, StringRef idname, std::unique_ptr< AbstractGridView > grid_view)
static uiViewStateLink * ensure_view_state(ARegion &region, const ViewLink &link)
void ui_block_free_views(uiBlock *block)
blender::ui::AbstractView * ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const blender::ui::AbstractView &new_view)
ui::AbstractViewItem * UI_region_views_find_item_at(const ARegion &region, const int xy[2])
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view)
ui::AbstractViewItem * UI_region_views_find_active_item(const ARegion *region)
void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
static T * ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr< AbstractView > view)
uiBut * UI_region_views_find_active_item_but(const ARegion *region)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
#define T
std::unique_ptr< DropTargetInterface > region_views_find_drop_target_at(const ARegion *region, const int xy[2])
ARegionRuntimeHandle * runtime
ListBase view_states
blender::Vector< std::unique_ptr< uiBut > > buttons
uiBlock * oldblock
ListBase views
blender::ui::AbstractViewItem * view_item
short buttonspacex
const wmNotifier * notifier
int xy[2]
Definition wm_draw.cc:178