Blender V4.5
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_interface.hh"
35
36#include "UI_abstract_view.hh"
37#include "UI_grid_view.hh"
38#include "UI_tree_view.hh"
39
40using namespace blender;
41using namespace blender::ui;
42
46struct ViewLink : public Link {
47 std::string idname;
48 std::unique_ptr<AbstractView> view;
49
50 static void views_bounds_calc(const uiBlock &block);
51};
52
53template<class T>
55 StringRef idname,
56 std::unique_ptr<AbstractView> view)
57{
59
60 ViewLink *view_link = MEM_new<ViewLink>(__func__);
61 BLI_addtail(&block.views, view_link);
62
63 view_link->view = std::move(view);
64 view_link->idname = idname;
65
66 return dynamic_cast<T *>(view_link->view.get());
67}
68
70 StringRef idname,
71 std::unique_ptr<AbstractGridView> grid_view)
72{
73 return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(grid_view));
74}
75
77 StringRef idname,
78 std::unique_ptr<AbstractTreeView> tree_view)
79{
80 return ui_block_add_view_impl<AbstractTreeView>(block, idname, std::move(tree_view));
81}
82
84{
85 LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
86 MEM_delete(link);
87 }
88}
89
91{
92 Map<AbstractView *, rcti> views_bounds;
93
94 rcti minmax;
95 BLI_rcti_init_minmax(&minmax);
96 LISTBASE_FOREACH (ViewLink *, link, &block.views) {
97 views_bounds.add(link->view.get(), minmax);
98 }
99
100 for (const std::unique_ptr<uiBut> &but : block.buttons) {
101 if (but->type != UI_BTYPE_VIEW_ITEM) {
102 continue;
103 }
104 uiButViewItem *view_item_but = static_cast<uiButViewItem *>(but.get());
105 if (!view_item_but->view_item) {
106 continue;
107 }
108
109 /* Get the view from the button. */
110 AbstractViewItem &view_item = reinterpret_cast<AbstractViewItem &>(*view_item_but->view_item);
111 AbstractView &view = view_item.get_view();
112
113 rcti &bounds = views_bounds.lookup(&view);
114 rcti but_rcti{};
115 BLI_rcti_rctf_copy_round(&but_rcti, &view_item_but->rect);
116 BLI_rcti_do_minmax_rcti(&bounds, &but_rcti);
117 }
118
119 for (const auto item : views_bounds.items()) {
120 const rcti &bounds = item.value;
122 continue;
123 }
124
125 AbstractView &view = *item.key;
126 view.bounds_ = bounds;
127 }
128}
129
131 const uiBlock &block,
133{
134 StringRef idname = [&]() -> StringRef {
135 LISTBASE_FOREACH (ViewLink *, link, &block.views) {
136 if (link->view.get() == &view) {
137 return link->idname;
138 }
139 }
140 return "";
141 }();
142
143 if (idname.is_empty()) {
145 return;
146 }
147
148 LISTBASE_FOREACH (uiViewStateLink *, stored_state, &region.view_states) {
149 if (stored_state->idname == idname) {
150 view.persistent_state_apply(stored_state->state);
151 }
152 }
153}
154
156{
157 LISTBASE_FOREACH (uiViewStateLink *, stored_state, &region.view_states) {
158 if (link.idname == stored_state->idname) {
159 return stored_state;
160 }
161 }
162
163 uiViewStateLink *new_state = MEM_callocN<uiViewStateLink>(__func__);
164 link.idname.copy(new_state->idname, sizeof(new_state->idname));
165 BLI_addhead(&region.view_states, new_state);
166 return new_state;
167}
168
169void ui_block_views_end(ARegion *region, const uiBlock *block)
170{
172
173 if (region && region->regiontype != RGN_TYPE_TEMPORARY) {
174 LISTBASE_FOREACH (const ViewLink *, link, &block->views) {
175 /* Ensure persistent view state storage for writing to files if needed. */
176 if (std::optional<uiViewState> temp_state = link->view->persistent_state()) {
177 uiViewStateLink *state_link = ensure_view_state(*region, *link);
178 state_link->state = *temp_state;
179 }
180 }
181 }
182}
183
184void ui_block_views_listen(const uiBlock *block, const wmRegionListenerParams *listener_params)
185{
186 ARegion *region = listener_params->region;
187
188 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
189 if (view_link->view->listen(*listener_params->notifier)) {
190 ED_region_tag_redraw(region);
191 }
192 }
193}
194
195void ui_block_views_draw_overlays(const ARegion *region, const uiBlock *block)
196{
197 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
198 view_link->view->draw_overlays(*region, *block);
199 }
200}
201
203 const int xy[2],
204 const int pad)
205{
206 /* NOTE: Similar to #ui_but_find_mouse_over_ex(). */
207
208 if (!ui_region_contains_point_px(region, xy)) {
209 return nullptr;
210 }
211 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
212 float mx = xy[0], my = xy[1];
213 ui_window_to_block_fl(region, block, &mx, &my);
214
215 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
216 std::optional<rcti> bounds = view_link->view->get_bounds();
217 if (!bounds) {
218 continue;
219 }
220
221 rcti padded_bounds = *bounds;
222 if (pad) {
223 BLI_rcti_pad(&padded_bounds, pad, pad);
224 }
225 if (BLI_rcti_isect_pt(&padded_bounds, mx, my)) {
226 return view_link->view.get();
227 }
228 }
229 }
230
231 return nullptr;
232}
233
235{
237 if (!item_but) {
238 return nullptr;
239 }
240
241 return item_but->view_item;
242}
243
245{
247 if (!item_but) {
248 return nullptr;
249 }
250
251 return item_but->view_item;
252}
253
258
260{
261 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
262 LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
263 view_link->view->clear_search_highlight();
264 }
265 }
266}
267
268namespace blender::ui {
269
270std::unique_ptr<DropTargetInterface> region_views_find_drop_target_at(const ARegion *region,
271 const int xy[2])
272{
274 if (std::unique_ptr<DropTargetInterface> target = item->create_item_drop_target()) {
275 return target;
276 }
277 }
278
279 /* Get style for some sensible padding around the view items. */
280 const uiStyle *style = UI_style_get_dpi();
281 if (AbstractView *view = UI_region_view_find_at(region, xy, style->buttonspacex)) {
282 if (std::unique_ptr<DropTargetInterface> target = view->create_drop_target()) {
283 return target;
284 }
285 }
286
287 if (AbstractView *view = UI_region_view_find_at(region, xy, 0)) {
288 /* If we are above a tree, but not hovering any specific element, dropping something should
289 * insert it after the last item. */
290 if (AbstractTreeView *tree_view = dynamic_cast<AbstractTreeView *>(view)) {
291 /* Find the last item which we want to drop below. */
292 AbstractTreeViewItem *last_item = nullptr;
293 tree_view->foreach_root_item([&](AbstractTreeViewItem &item) {
294 if (!item.is_interactive()) {
295 return;
296 }
297 last_item = &item;
298 });
299 if (last_item) {
300 return last_item->create_item_drop_target();
301 }
302 }
303 }
304
305 return nullptr;
306}
307
308} // namespace blender::ui
309
311{
312 /* First get the idname the of the view we're looking for. */
313 LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
314 if (view_link->view.get() == &view) {
315 return view_link->idname;
316 }
317 }
318
319 return {};
320}
321
322template<class T>
324 const T &new_view)
325{
326 uiBlock *old_block = new_block.oldblock;
327 if (!old_block) {
328 return nullptr;
329 }
330
331 StringRef idname = ui_block_view_find_idname(new_block, new_view);
332 if (idname.is_empty()) {
333 return nullptr;
334 }
335
336 LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
337 if (old_view_link->idname == idname) {
338 return dynamic_cast<T *>(old_view_link->view.get());
339 }
340 }
341
342 return nullptr;
343}
344
350
352 const uiBlock &new_block, const ui::AbstractViewItem &new_item)
353{
354 uiBlock *old_block = new_block.oldblock;
355 if (!old_block) {
356 return nullptr;
357 }
358
360 new_block, new_item.get_view());
361 if (!old_view) {
362 return nullptr;
363 }
364
365 for (const std::unique_ptr<uiBut> &old_but : old_block->buttons) {
366 if (old_but->type != UI_BTYPE_VIEW_ITEM) {
367 continue;
368 }
369 uiButViewItem *old_item_but = (uiButViewItem *)old_but.get();
370 if (!old_item_but->view_item) {
371 continue;
372 }
373 AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item);
374 /* Check if the item is from the expected view. */
375 if (&old_item.get_view() != old_view) {
376 continue;
377 }
378
379 if (UI_view_item_matches(new_item, old_item)) {
380 return old_item_but;
381 }
382 }
383
384 return nullptr;
385}
#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:639
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)
@ UI_BTYPE_VIEW_ITEM
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:605
void ui_window_to_block_fl(const ARegion *region, const uiBlock *block, float *x, float *y)
Definition interface.cc:186
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:174