Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
dropper-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Tool for picking colors from drawing
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 1999-2005 Authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include <glibmm/i18n.h>
16#include <gdk/gdk.h>
17#include <gdk/gdkkeysyms.h>
18
19#include <2geom/transforms.h>
20#include <2geom/circle.h>
21
22#include "desktop-style.h"
23#include "desktop.h"
24#include "document-undo.h"
25#include "message-context.h"
26#include "preferences.h"
27#include "selection.h"
28#include "style.h"
29#include "page-manager.h"
30
31#include "display/drawing.h"
34
35#include "object/sp-namedview.h"
36
37#include "ui/cursor-utils.h"
38#include "ui/icon-names.h"
40#include "ui/widget/canvas.h"
42
44
45namespace Inkscape::UI::Tools {
46
52
54 : ToolBase(desktop, "/tools/dropper", "dropper-pick-fill.svg")
55{
56 area = make_canvasitem<CanvasItemBpath>(desktop->getCanvasControls());
57 area->set_stroke(0x0000007f);
58 area->set_fill(0x0, SP_WIND_RULE_EVENODD);
59 area->set_visible(false);
60
61 auto prefs = Preferences::get();
62
63 if (prefs->getBool("/tools/dropper/selcue")) {
65 }
66
67 if (prefs->getBool("/tools/dropper/gradientdrag")) {
69 }
70}
71
77
90std::optional<Colors::Color> DropperTool::get_color(bool invert, bool non_dropping) const
91{
92 auto prefs = Preferences::get();
93
94 int pick = prefs->getInt("/tools/dropper/pick", PICK_VISIBLE);
95 bool setalpha = prefs->getBool("/tools/dropper/setalpha", true);
96
97 // non_dropping ignores dropping mode and always uses color from canvas.
98 // Used by the clipboard
99 auto color = non_dropping ? non_dropping_color : stored_color;
100
101 if (color && invert)
102 color->invert();
103
104 if (color && (pick != PICK_ACTUAL || !setalpha))
105 color->enableOpacity(false);
106
107 return color;
108}
109
111{
112 auto prefs = Preferences::get();
113 int pick = prefs->getInt("/tools/dropper/pick", PICK_VISIBLE);
114
115 // Decide first what kind of 'mode' we're in.
116 auto modifiers = event.modifiersAfter();
117 stroke = modifiers & GDK_SHIFT_MASK;
118 dropping = modifiers & GDK_CONTROL_MASK; // Even on macOS.
119 invert = modifiers & GDK_ALT_MASK;
120
121 // Get color from selected object
122 // Only if dropping mode enabled and object's color is set.
123 // Otherwise dropping mode disabled.
124 if (dropping) {
125 auto selection = _desktop->getSelection();
126 g_assert(selection);
127
128 std::optional<Inkscape::Colors::Color> apply_color;
129 for (auto const &obj: selection->objects()) {
130 if (obj->style) {
131 if (!stroke && obj->style->fill.set) {
132 apply_color = obj->style->fill.getColor();
133 apply_color->addOpacity(obj->style->fill_opacity);
134 } else if (stroke && obj->style->stroke.set) {
135 apply_color = obj->style->stroke.getColor();
136 apply_color->addOpacity(obj->style->stroke_opacity);
137 }
138 }
139 }
140
141 if (apply_color) {
142 stored_color = apply_color;
143 } else {
144 // This means that having no selection or some other error
145 // we will default back to normal dropper mode.
146 dropping = false;
147 }
148 }
149
150 bool ret = false;
151 bool self_destroyed = false;
152
153 inspect_event(event,
154 [&] (ButtonPressEvent const &event) {
155 if (event.num_press != 1) {
156 return;
157 }
158
159 if (event.button == 1) {
160 centre = event.pos;
161 dragging = true;
162 ret = true;
163 }
164
170 },
171
172 [&] (MotionEvent const &event) {
173 if (event.modifiers & (GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) {
174 // pass on middle and right drag
175 return;
176 }
177
178 // otherwise, constantly calculate color no matter if any button pressed or not
179 Geom::IntRect pick_area;
180 if (dragging) {
181 // Calculate average
182
183 // Radius
184 double rw = std::min((event.pos - centre).length(), 400.0);
185 if (rw == 0) { // happens sometimes, little idea why...
186 return;
187 }
188 radius = rw;
189
190 auto const cd = _desktop->w2d(centre);
191 auto const w2dt = _desktop->w2d();
192 auto const scale = rw * w2dt.descrim();
193 auto const sm = Geom::Scale(scale) * Geom::Translate(cd);
194
195 // Show circle on canvas.
196 auto path = Geom::Path(Geom::Circle(0, 0, 1)); // Unit circle centered at origin.
197 path *= sm;
198 area->set_bpath(std::move(path));
199 area->set_visible(true);
200
201 // Get buffer
202 auto r = Geom::Rect(centre, centre);
203 r.expandBy(rw);
204 if (!r.hasZeroArea()) {
205 pick_area = r.roundOutwards();
206 }
207 } else {
208 // Pick single pixel
209 pick_area = Geom::IntRect::from_xywh(0, 0, 1, 1) + event.pos.floor();
210 }
211
212 auto canvas_item_drawing = _desktop->getCanvasDrawing();
213 auto drawing = canvas_item_drawing->get_drawing();
214
215 // Get average color of on screen pixels (sRGB)
216 auto avg = drawing->averageColor(pick_area);
217
218 if (pick == PICK_VISIBLE || avg.getOpacity() == 0.0) {
219 // Compose with page color
221 avg = bg.composed(avg);
222 }
223
224 // Remember color
225 if (!dropping) {
226 stored_color = avg;
227 }
228
229 // Remember color from canvas, even in dropping mode.
230 // These values are used by the clipboard.
231 non_dropping_color = avg;
232 ret = true;
233 },
234
235 [&] (ButtonReleaseEvent const &event) {
236 if (event.button != 1) {
237 return;
238 }
239
240 area->set_visible(false);
241 dragging = false;
242
244
245 auto selection = _desktop->getSelection();
246 g_assert(selection);
247
248 auto old_selection = std::vector<SPItem*>(selection->items().begin(), selection->items().end());
249
250 if (dropping) {
251 auto const button_w = event.pos;
252 // Remember clicked item, disregarding groups, honoring Alt.
253 item_to_select = sp_event_context_find_item(_desktop, button_w, event.modifiers & GDK_ALT_MASK, true);
254
255 // Change selected object to object under cursor.
256 if (item_to_select) {
257 selection->set(item_to_select);
258 }
259 }
260
261 auto picked_color = get_color(invert);
262
263 // One time pick has active signal, call them all and clear.
264 if (!onetimepick_signal.empty()) {
265 onetimepick_signal.emit(*picked_color);
266 onetimepick_signal.clear();
267 // Do this last as it destroys the picker tool.
269 self_destroyed = true;
270 return;
271 }
272
273 // do the actual color setting
274 sp_desktop_set_color(_desktop, *picked_color, false, !stroke);
275
276 // REJON: set aux. toolbar input to hex color!
277 if (!_desktop->getSelection()->isEmpty()) {
278 DocumentUndo::done(_desktop->getDocument(), _("Set picked color"), INKSCAPE_ICON("color-picker"));
279 }
280
281 if (dropping) {
282 selection->setList(old_selection);
283 }
284
285 ret = true;
286 },
287
288 [&] (KeyPressEvent const &event) {
289 switch (get_latin_keyval(event)) {
290 case GDK_KEY_Up:
291 case GDK_KEY_Down:
292 case GDK_KEY_KP_Up:
293 case GDK_KEY_KP_Down:
294 // Prevent the zoom field from activating.
295 if (!mod_ctrl_only(event)) {
296 ret = true;
297 }
298 break;
299 case GDK_KEY_Escape:
301 break;
302 default:
303 break;
304 }
305 },
306
307 [&] (CanvasEvent const &event) {}
308 );
309
310 if (self_destroyed) {
311 return true;
312 }
313
314 // set the status message to the right text.
315 auto color = get_color(invert);
316
317 if (color) {
318 // alpha of color under cursor, to show in the statusbar
319 // locale-sensitive printf is OK, since this goes to the UI, not into SVG
320 auto alphastr = g_strdup_printf(_(" alpha %.3g"), color->getOpacity());
321 // where the color is picked, to show in the statusbar
322 auto where = dragging ? g_strdup_printf(_(", averaged with radius %d"), (int)radius) : g_strdup_printf("%s", _(" under cursor"));
323 // message, to show in the statusbar
324 auto message = dragging ? _("<b>Release mouse</b> to set color.") : _("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard");
325
328 "<b>%s%s</b>%s. %s", color->toString(false).c_str(),
329 pick == PICK_VISIBLE ? "" : alphastr, where, message);
330
331 g_free(where);
332 g_free(alphastr);
333 }
334
335 // Set the right cursor for the mode and apply the special Fill color
336 _cursor_filename = dropping ? (stroke ? "dropper-drop-stroke.svg" : "dropper-drop-fill.svg")
337 : (stroke ? "dropper-pick-stroke.svg" : "dropper-pick-fill.svg");
338
339 // We do this ourselves to get color correct.
341
342 if (!ret) {
343 ret = ToolBase::root_handler(event);
344 }
345
346 return ret;
347}
348
349} // namespace Inkscape::UI::Tools
350
351/*
352 Local Variables:
353 mode:c++
354 c-file-style:"stroustrup"
355 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
356 indent-tabs-mode:nil
357 fill-column:99
358 End:
359*/
360// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition aa.cpp:228
Inkscape canvas widget.
Circle shape.
Set of all points at a fixed distance from the center.
Definition circle.h:55
Axis aligned, non-empty, generic rectangle.
static CRect from_xywh(C x, C y, C w, C h)
Create rectangle from origin and dimensions.
Sequence of contiguous curves, aka spline.
Definition path.h:353
Axis aligned, non-empty rectangle.
Definition rect.h:92
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
Inkscape::Drawing * get_drawing()
Color composed(Color const &other) const
Return the composition of this color, plus the other color on top.
Definition color.cpp:530
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Colors::Color averageColor(Geom::IntRect const &area) const
Definition drawing.cpp:374
void setF(MessageType type, char const *format,...) G_GNUC_PRINTF(3
pushes a message on the stack using prinf-style formatting, and replacing our old message
void clear()
Unselects all selected objects.
bool isEmpty()
Returns true if no items are selected.
Colors::Color const & getDefaultBackgroundColor() const
static Preferences * get()
Access the singleton Preferences object.
sigc::signal< void(Colors::Color const &)> onetimepick_signal
bool root_handler(CanvasEvent const &event) override
CanvasItemPtr< CanvasItemBpath > area
Circle depicting region's borders in dragging mode.
double radius
Size of region under dragging mode.
std::optional< Colors::Color > get_color(bool invert=false, bool non_dropping=false) const
Returns the current dropper context color.
std::optional< Colors::Color > stored_color
bool stroke
Set to stroke color. In dropping mode, set from stroke color.
bool invert
Set color to inverse rgb value.
bool dropping
When true, get color from selected objects instead of canvas.
std::optional< Colors::Color > non_dropping_color
Geom::Point centre
Center of region in dragging mode.
Base class for Event processors.
Definition tool-base.h:107
void ungrabCanvasEvents()
Ungrab events from the Canvas Catchall.
void grabCanvasEvents(EventMask mask=EventType::KEY_PRESS|EventType::BUTTON_RELEASE|EventType::MOTION|EventType::BUTTON_PRESS)
Grab events from the Canvas Catchall.
bool dragging
are we dragging?
Definition tool-base.h:146
SPItem * item_to_select
the item where mouse_press occurred, to be selected if this is a click not drag
Definition tool-base.h:152
virtual bool root_handler(CanvasEvent const &event)
void enableGrDrag(bool enable=true)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
MessageContext * defaultMessageContext() const
Definition tool-base.h:123
To do: update description of desktop.
Definition desktop.h:149
Inkscape::UI::Widget::Canvas * getCanvas() const
Definition desktop.h:190
Inkscape::CanvasItemGroup * getCanvasControls() const
Definition desktop.h:196
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::CanvasItemDrawing * getCanvasDrawing() const
Definition desktop.h:204
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Inkscape::PageManager & getPageManager()
Definition document.h:162
void sp_desktop_set_color(SPDesktop *desktop, Color const &color, bool is_relative, bool fill)
Set color on selection on desktop.
Editable view implementation.
TODO: insert short description here.
SVG drawing for display.
Macro for icon names used in Inkscape.
Interface for locally managing a current status message.
SPItem * sp_event_context_find_item(SPDesktop *desktop, Geom::Point const &p, bool select_under, bool into_groups)
Returns item at point p in desktop.
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
void sp_toggle_dropper(SPDesktop *dt)
Toggles current tool between active tool and dropper tool.
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
bool mod_ctrl_only(unsigned modifiers)
void set_svg_cursor(Gtk::Widget &widget, std::string const &file_name, std::optional< Colors::Color > fill, std::optional< Colors::Color > stroke)
Loads an SVG cursor from the specified file name, and sets it as the cursor of the given widget.
@ NORMAL_MESSAGE
Definition message.h:26
Singleton class to access the preferences file in a convenient way.
void invert(const double v[16], double alpha[16])
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
A key has been pressed.
Movement of the mouse pointer.
@ SP_WIND_RULE_EVENODD
Definition style-enums.h:26
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
Affine transformation classes.