Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
canvas-item.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/*
7 * Author:
8 * Tavmjong Bah
9 *
10 * Copyright (C) 2020 Tavmjong Bah
11 *
12 * Rewrite of SPCanvasItem
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include "canvas-item.h"
18#include "canvas-item-group.h"
19#include "canvas-item-ctrl.h"
20
21#include "ui/widget/canvas.h"
22
23constexpr bool DEBUG_LOGGING = false;
24constexpr bool DEBUG_BOUNDS = false;
25
26namespace Inkscape {
27
29 : _context(context)
30 , _parent(nullptr)
31{
32 if constexpr (DEBUG_LOGGING) std::cout << "CanvasItem: create root " << get_name() << std::endl;
34}
35
37 : _context(parent->_context)
38 , _parent(parent)
39{
40 if constexpr (DEBUG_LOGGING) std::cout << "CanvasItem: add " << get_name() << " to " << parent->get_name() << " " << parent->items.size() << std::endl;
41 defer([=, this] {
42 parent->items.push_back(*this);
44 });
45}
46
48{
49 defer([=, this] {
50 // Clear canvas of item.
52
53 // Remove from parent.
54 if (_parent) {
55 if constexpr (DEBUG_LOGGING) std::cout << "CanvasItem: remove " << get_name() << " from " << _parent->get_name() << " " << _parent->items.size() << std::endl;
56 auto it = _parent->items.iterator_to(*this);
57 assert(it != _parent->items.end());
58 _parent->items.erase(it);
60 } else {
61 if constexpr (DEBUG_LOGGING) std::cout << "CanvasItem: destroy root " << get_name() << std::endl;
62 }
63
64 delete this;
65 });
66}
67
69{
70 // Clear any pointers to this object in canvas.
72}
73
74bool CanvasItem::is_descendant_of(CanvasItem const *ancestor) const
75{
76 auto item = this;
77 while (item) {
78 if (item == ancestor) {
79 return true;
80 }
81 item = item->_parent;
82 }
83 return false;
84}
85
87{
88 if (!_parent) {
89 std::cerr << "CanvasItem::set_z_position: No parent!" << std::endl;
90 return;
91 }
92
93 defer([=, this] {
94 _parent->items.erase(_parent->items.iterator_to(*this));
95
96 if (zpos <= 0) {
97 _parent->items.push_front(*this);
98 } else if (zpos >= _parent->items.size() - 1) {
99 _parent->items.push_back(*this);
100 } else {
101 auto it = _parent->items.begin();
102 std::advance(it, zpos);
103 _parent->items.insert(it, *this);
104 }
105 });
106}
107
109{
110 if (!_parent) {
111 std::cerr << "CanvasItem::raise_to_top: No parent!" << std::endl;
112 return;
113 }
114
115 defer([=, this] {
116 _parent->items.erase(_parent->items.iterator_to(*this));
117 _parent->items.push_back(*this);
118 });
119}
120
122{
123 if (!_parent) {
124 std::cerr << "CanvasItem::lower_to_bottom: No parent!" << std::endl;
125 return;
126 }
127
128 defer([=, this] {
129 _parent->items.erase(_parent->items.iterator_to(*this));
130 _parent->items.push_front(*this);
131 });
132}
133
134// Indicate geometry changed and bounds needs recalculating.
136{
137 if (_need_update || !_visible) {
138 return;
139 }
140
141 _need_update = true;
142
143 if (_parent) {
145 } else {
147 }
148}
149
150void CanvasItem::update(bool propagate)
151{
152 if (!_visible) {
154 return;
155 }
156
157 bool reappearing = !_net_visible;
158 _net_visible = true;
159
160 if (!_need_update && !reappearing && !propagate) {
161 return;
162 }
163
164 _need_update = false;
165
166 // Get new bounds
167 _update(propagate);
168
169 if (reappearing) {
171 }
172}
173
175{
176 if (!_net_visible) {
177 return;
178 }
179 _net_visible = false;
180 _need_update = false;
182 _bounds = {};
183}
184
185// Grab all events!
186void CanvasItem::grab(EventMask event_mask, Glib::RefPtr<Gdk::Cursor> const &cursor)
187{
188 if constexpr (DEBUG_LOGGING) std::cout << "CanvasItem::grab: " << _name << std::endl;
189
190 auto canvas = get_canvas();
191
192 // Don't grab if we already have a grabbed item!
193 if (canvas->get_grabbed_canvas_item()) {
194 return;
195 }
196
197 canvas->set_grabbed_canvas_item(this, event_mask);
198 canvas->set_current_canvas_item(this); // So that all events go to grabbed item.
199}
200
202{
203 if constexpr (DEBUG_LOGGING) std::cout << "CanvasItem::ungrab: " << _name << std::endl;
204
205 auto canvas = get_canvas();
206
207 if (canvas->get_grabbed_canvas_item() != this) {
208 return; // Sanity check
209 }
210
211 canvas->set_grabbed_canvas_item(nullptr, {}); // Zero mask
212}
213
215{
216 if (_visible && _bounds && _bounds->interiorIntersects(buf.rect)) {
217 _render(buf);
218 if constexpr (DEBUG_BOUNDS) {
219 auto bounds = *_bounds;
220 bounds.expandBy(-1);
221 bounds -= buf.rect.min();
222 buf.cr->set_source_rgba(1.0, 0.0, 0.0, 1.0);
223 buf.cr->rectangle(bounds.min().x(), bounds.min().y(), bounds.width(), bounds.height());
224 buf.cr->stroke();
225 }
226 }
227}
228
229/*
230 * The main invariant of the invisibility system is
231 *
232 * x needs update and is visible ==> parent(x) needs update or is invisible
233 *
234 * When x belongs to the visible subtree, meaning it and all its parents are visible,
235 * this condition reduces to
236 *
237 * x needs update ==> parent(x) needs update
238 *
239 * Thus within the visible subtree, the subset of nodes that need updating forms a subtree.
240 *
241 * In the update() function, we only walk this latter subtree.
242 */
243
245{
246 defer([=, this] {
247 if (_visible == visible) return;
248 if (_visible) {
250 _visible = false;
251 } else {
252 _visible = true;
253 _need_update = false;
255 }
256 });
257}
258
260{
261 // Queue redraw request
262 if (_bounds) {
264 }
265}
266
267void CanvasItem::set_fill(uint32_t fill)
268{
269 defer([=, this] {
270 if (_fill == fill) return;
271 _fill = fill;
273 });
274}
275
276void CanvasItem::set_fill_pattern(Cairo::RefPtr<Cairo::Pattern> fill_pattern)
277{
278 defer([fill_pattern = std::move(fill_pattern), this] () mutable {
279 if (_fill_pattern == fill_pattern) return;
280 _fill_pattern = std::move(fill_pattern);
282 });
283}
284
285void CanvasItem::set_stroke(uint32_t stroke)
286{
287 defer([=, this] {
288 if (_stroke == stroke) return;
289 _stroke = stroke;
291 });
292}
293
298{
299 defer([=, this] {
300 if (_stroke_width == width) return;
303 });
304}
305
309void CanvasItem::set_outline(uint32_t color)
310{
311 defer([=, this] {
312 if (_outline == color) return;
313 _outline = color;
315 });
316}
317
322{
323 defer([=, this] {
324 if (_outline_width == width) return;
327 });
328}
329
334{
335 // Outline extends in two directions, so we scale by 2
336 return _stroke_width + 2 * _outline_width;
337}
338
340{
341 if (auto ctrl = dynamic_cast<CanvasItemCtrl*>(this)) {
342 // We can't use set_size_default as the preference file is updated ->after<- the signal is emitted!
343 ctrl->set_size_via_index(size_index);
344 } else if (auto group = dynamic_cast<CanvasItemGroup*>(this)) {
345 for (auto &item : group->items) {
346 item.update_canvas_item_ctrl_sizes(size_index);
347 }
348 }
349}
350
351void CanvasItem::canvas_item_print_tree(int level, int zorder) const
352{
353 if (level == 0) {
354 std::cout << "Canvas Item Tree" << std::endl;
355 }
356
357 std::cout << "CC: ";
358 for (int i = 0; i < level; ++i) {
359 std::cout << " ";
360 }
361
362 std::cout << zorder << ": " << _name << std::endl;
363
364 if (auto group = dynamic_cast<Inkscape::CanvasItemGroup const*>(this)) {
365 int i = 0;
366 for (auto &item : group->items) {
367 item.canvas_item_print_tree(level + 1, i);
368 i++;
369 }
370 }
371}
372
373} // namespace Inkscape
374
375/*
376 Local Variables:
377 mode:c++
378 c-file-style:"stroustrup"
379 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
380 indent-tabs-mode:nil
381 fill-column:99
382 End:
383*/
384// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
constexpr bool DEBUG_LOGGING
A CanvasItem that contains other CanvasItem's.
constexpr bool DEBUG_LOGGING
Abstract base class for on-canvas control items.
constexpr bool DEBUG_BOUNDS
Geom::IntRect visible
Definition canvas.cpp:154
Geom::IntRect bounds
Definition canvas.cpp:182
Inkscape canvas widget.
void expandBy(C amount)
Expand the rectangle in both directions by the specified amount.
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
virtual void set_visible(bool visible)
void render(Inkscape::CanvasItemBuffer &buf) const
double get_effective_outline() const
Get the effective outline.
Cairo::RefPtr< Cairo::Pattern > _fill_pattern
CanvasItem(CanvasItemContext *context)
std::string const & get_name() const
Definition canvas-item.h:98
void set_outline(uint32_t color)
Set the outline color.
virtual void _mark_net_invisible()
void set_outline_width(double width)
Set the outline width.
void set_z_position(int zpos)
Geom::OptRect _bounds
void grab(EventMask event_mask, Glib::RefPtr< Gdk::Cursor > const &={})
CanvasItemGroup * _parent
void set_fill_pattern(Cairo::RefPtr< Cairo::Pattern > pattern)
virtual void set_fill(uint32_t rgba)
virtual void set_stroke(uint32_t rgba)
void canvas_item_print_tree(int level=0, int zorder=0) const
bool is_descendant_of(CanvasItem const *ancestor) const
virtual void _render(Inkscape::CanvasItemBuffer &buf) const =0
void set_stroke_width(double width)
Set the stroke width.
virtual void _update(bool propagate)=0
void update(bool propagate)
void update_canvas_item_ctrl_sizes(int size_index)
UI::Widget::Canvas * get_canvas() const
Definition canvas-item.h:61
A mask representing a subset of EventTypes.
Definition enums.h:38
void request_update()
Redraw after changing canvas item geometry.
Definition canvas.cpp:1681
void canvas_item_destructed(Inkscape::CanvasItem *item)
Clear current and grabbed items.
Definition canvas.cpp:1801
void redraw_area(Geom::Rect const &area)
Definition canvas.cpp:1673
static char const *const parent
Definition dir-util.cpp:70
SPItem * item
Helper class to stream background task notifications as a series of messages.
int buf
Class used when rendering canvas items.
double width