Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
color-notebook.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Tomasz Boczkowski <penginsbacon@gmail.com> (c++-sification)
9 *
10 * Copyright (C) 2001-2014 Authors
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include "color-notebook.h"
15
16#undef SPCS_PREVIEW
17#define noDUMP_CHANGE_INFO
18
19#include <glibmm/i18n.h>
20#include <gtkmm/box.h>
21#include <gtkmm/button.h>
22#include <gtkmm/label.h>
23#include <gtkmm/stack.h>
24#include <gtkmm/stackswitcher.h>
25
26#include "desktop.h"
27#include "document.h"
28#include "inkscape.h"
29#include "preferences.h"
30#include "colors/manager.h"
31#include "colors/spaces/base.h"
33#include "ui/dialog-events.h"
34#include "ui/icon-loader.h"
35#include "ui/pack.h"
37#include "ui/util.h"
41
42static constexpr int XPAD = 2;
43static constexpr int YPAD = 1;
44
45namespace Inkscape::UI::Widget {
46
47ColorNotebook::ColorNotebook(std::shared_ptr<Colors::ColorSet> colors)
48 : _colors(std::move(colors))
49{
50 set_name("ColorNotebook");
51
52 _initUI();
53
54 //_colors->signal_changed.connect(sigc::mem_fun(*this, &ColorNotebook::_onSelectedColorChanged));
55
56 auto desktop = SP_ACTIVE_DESKTOP;
59}
60
62{
63 if (_onetimepick)
64 _onetimepick.disconnect();
65 _doc_replaced_connection.disconnect();
66 setDocument(nullptr);
67}
68
70{
71 _document = document;
72 // XXX Watch for new icc spaces here using the profile tracker
73}
74
75void ColorNotebook::set_label(const Glib::ustring& label) {
76 _label->set_markup(label);
77}
78
80{
81 guint row = 0;
82
83 _book = Gtk::make_managed<Gtk::Stack>();
84 _book->set_transition_type(Gtk::StackTransitionType::CROSSFADE);
85 _book->set_transition_duration(130);
86 _book->set_vhomogeneous(false);
87
88 // mode selection switcher widget shows all buttons for color mode selection, side by side
89 _switcher = Gtk::make_managed<Gtk::StackSwitcher>();
90 _switcher->set_stack(*_book);
91 // cannot leave it homogeneous - in some themes switcher gets very wide
92 // TODO: GTK4: Figure out whether this is still needed / possible to do
93 //_switcher->set_homogeneous(false);
94 _switcher->set_halign(Gtk::Align::CENTER);
95 attach(*_switcher, 0, row++, 2);
96
97 _buttonbox = Gtk::make_managed<Gtk::Box>();
98
99 // combo mode selection is compact and only shows one entry (active)
100 _combo = Gtk::make_managed<IconComboBox>();
101 _combo->set_focusable(false);
102 _combo->set_visible();
103 _combo->set_tooltip_text(_("Choose style of color selection"));
104
105 // Add all universal (non-document icc profile) color spaces
106 for (auto &space : Colors::Manager::get().spaces(Space::Traits::Picker)) {
107 _addPageForSpace(space);
108 }
109
110 _label = Gtk::make_managed<Gtk::Label>();
111 _label->set_visible();
112 _label->set_xalign(0);
113 _label->set_margin_end(XPAD);
114 UI::pack_start(*_buttonbox, *_label, true, true);
115 UI::pack_end(*_buttonbox, *_combo, false, false);
116 _combo->signal_changed().connect([this](int id){ _setCurrentPage(id, false); });
117
118 _buttonbox->set_margin_start(XPAD);
119 _buttonbox->set_margin_end(XPAD);
120 _buttonbox->set_margin_top(YPAD);
121 _buttonbox->set_margin_bottom(YPAD);
122 _buttonbox->set_hexpand();
123 _buttonbox->set_valign(Gtk::Align::START);
124 attach(*_buttonbox, 0, row, 2);
125
126 row++;
127
128 _book->set_margin_top(YPAD);
129 _book->set_margin_bottom(YPAD);
130 _book->set_hexpand();
131 _book->set_vexpand();
132 attach(*_book, 0, row, 2, 1);
133
134 // restore the last active page
136 Glib::ustring page_name = prefs->getString("/colorselector/page", "");
137 _setCurrentPage(getPageIndex(page_name), true);
138 row++;
139
140 _observer = prefs->createObserver("/colorselector/switcher", [this](const Preferences::Entry& new_value) {
141 _switcher->set_visible(!new_value.getBool());
142 _buttonbox->set_visible(new_value.getBool());
143 });
144 _observer->call();
145
146 GtkWidget *rgbabox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
147 auto const rgbabox_box = GTK_BOX(rgbabox);
148
149 /* Create color management icons */
150 _colormanaged = sp_get_icon_image("color-management", GTK_ICON_SIZE_NORMAL);
151 gtk_widget_set_tooltip_text(_colormanaged, _("Color Managed"));
152 gtk_widget_set_sensitive(_colormanaged, false);
153 gtk_box_append(rgbabox_box, _colormanaged);
154
155 _outofgamut = sp_get_icon_image("out-of-gamut-icon", GTK_ICON_SIZE_NORMAL);
156 gtk_widget_set_tooltip_text(_outofgamut, _("Out of gamut!"));
157 gtk_widget_set_sensitive(_outofgamut, false);
158 gtk_box_append(rgbabox_box, _outofgamut);
159
160 _toomuchink = sp_get_icon_image("too-much-ink-icon", GTK_ICON_SIZE_NORMAL);
161 gtk_widget_set_tooltip_text(_toomuchink, _("Too much ink!"));
162 gtk_widget_set_sensitive(_toomuchink, false);
163 gtk_box_append(rgbabox_box, _toomuchink);
164
165 /* Color picker */
166 _btn_picker = gtk_button_new();
167 gtk_button_set_has_frame(GTK_BUTTON(_btn_picker), false);
168 gtk_button_set_icon_name(GTK_BUTTON(_btn_picker), "color-picker");
169 gtk_widget_set_tooltip_text(_btn_picker, _("Pick colors from image"));
170 gtk_box_append(rgbabox_box, _btn_picker);
171 g_signal_connect(G_OBJECT(_btn_picker), "clicked", G_CALLBACK(ColorNotebook::_onPickerClicked), this);
172
173 auto const pick_under = Gtk::make_managed<Gtk::Button>();
174 pick_under->set_has_frame(false);
175 pick_under->set_action_name("app.chameleon-fill");
176 pick_under->set_icon_name("color-picker-chameleon");
177 pick_under->set_tooltip_text(_("Chameleon Fill"));
178 gtk_box_append(rgbabox_box, GTK_WIDGET(pick_under->gobj()));
179
180 /* Create RGBA entry and color preview */
181 _rgbal = gtk_label_new_with_mnemonic(_("RGBA"));
182 gtk_widget_set_halign(_rgbal, GTK_ALIGN_END);
183 gtk_widget_set_hexpand(_rgbal, TRUE);
184 gtk_box_append(rgbabox_box, _rgbal);
185
186 auto const rgba_entry = Gtk::make_managed<ColorEntry>(_colors);
187 rgba_entry->set_max_width_chars(8);
188 auto const rgba_entry_widget = rgba_entry->Gtk::Widget::gobj();
189 sp_dialog_defocus_on_enter(rgba_entry);
190 gtk_box_append(rgbabox_box, rgba_entry_widget);
191 gtk_label_set_mnemonic_widget(GTK_LABEL(_rgbal), rgba_entry_widget);
192
193 // the "too much ink" icon is initially hidden
194 gtk_widget_set_visible(_toomuchink, false);
195
196 gtk_widget_set_margin_start(rgbabox, XPAD);
197 gtk_widget_set_margin_end(rgbabox, XPAD);
198 gtk_widget_set_margin_top(rgbabox, YPAD);
199 gtk_widget_set_margin_bottom(rgbabox, YPAD);
200 attach(*Glib::wrap(rgbabox), 0, row, 2, 1);
201
202 // remember the page we switched to
203 _book->property_visible_child_name().signal_changed().connect([this]() {
204 // We don't want to remember auto cms selection
205 Glib::ustring name = _book->get_visible_child_name();
206 if (get_visible() && !name.empty() && name != "CMS") {
207 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
208 prefs->setString("/colorselector/page", name);
209 }
210 });
211
212#ifdef SPCS_PREVIEW
213 _p = sp_color_preview_new(0xffffffff);
214 gtk_widget_set_visible(_p, true);
215 attach(*Glib::wrap(_p), 2, 3, row, row + 1, Gtk::FILL, Gtk::FILL, XPAD, YPAD);
216#endif
217}
218
219void ColorNotebook::_onPickerClicked(GtkWidget * /*widget*/, ColorNotebook *colorbook)
220{
221 // Set the dropper into a "one click" mode, so it reverts to the previous tool after a click
222 if (colorbook->_onetimepick) {
223 colorbook->_onetimepick.disconnect();
224 }
225 else {
227 auto tool = dynamic_cast<Inkscape::UI::Tools::DropperTool *>(SP_ACTIVE_DESKTOP->getTool());
228 if (tool) {
229 colorbook->_onetimepick = tool->onetimepick_signal.connect([colorbook](Colors::Color const &color) {
230 // Set color to color notebook here.
231 colorbook->_colors->setAll(color);
232 });
233 }
234 }
235}
236
237/*void ColorNotebook::_onSelectedColorChanged() {
238 _updateICCButtons();
239}*/
240
241/*void ColorNotebook::_updateICCButtons()
242{
243 if (!_document)
244 return;
245
246 // update color management icon
247 // XXX auto space = _colors->getConstrainSpace();
248 //bool is_cms = space->getDocument();
249 //gtk_widget_set_sensitive(_colormanaged, is_cms);
250 //gtk_widget_set_sensitive(_toomuchink, _selected_color->isOutOfGamut());
251 //gtk_widget_set_sensitive(_outofgamut, _colors->average().isOverInked());
252
253
254 gtk_widget_set_sensitive(_toomuchink, false);
255 gtk_widget_set_sensitive(_outofgamut, false);
256
257 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
258 auto page = prefs->getString("/colorselector/page");
259 _setCurrentPage(getPageIndex(page), true);
260}*/
261
262int ColorNotebook::getPageIndex(const Glib::ustring &name)
263{
264 return getPageIndex(_book->get_child_by_name(name));
265}
266
267int ColorNotebook::getPageIndex(Gtk::Widget *widget)
268{
269 auto const pages = UI::get_children(*_book);
270 auto const n_pages = static_cast<int>(pages.size());
271 for (int i = 0; i < n_pages; ++i) {
272 if (pages[i] == widget) {
273 return i;
274 }
275 }
276 return 0;
277}
278
279void ColorNotebook::_setCurrentPage(int i, bool sync_combo)
280{
281 auto const pages = UI::get_children(*_book);
282 auto const n_pages = static_cast<int>(pages.size());
283 if (i >= n_pages) {
284 // page index could be outside the valid range if we manipulate visible color pickers;
285 // default to the first page, so we show something
286 i = 0;
287 }
288
289 if (i >= 0 && i < n_pages) {
290 _book->set_visible_child(*pages[i]);
291 if (sync_combo) {
293 }
294 }
295}
296
297void ColorNotebook::_addPageForSpace(std::shared_ptr<Colors::Space::AnySpace> space)
298{
299 auto selector_widget = Gtk::make_managed<ColorPage>(space, _colors);
300 auto mode_name = space->getName();
301 _book->add(*selector_widget, mode_name, mode_name);
302
303 auto const n_pages = static_cast<int>(UI::get_children(*_book).size());
304 auto const page_num = n_pages - 1;
305 _combo->add_row(space->getIcon(), mode_name, page_num);
306
307 auto prefs = Inkscape::Preferences::get();
308 auto obs = prefs->createObserver(space->getPrefsPath() + "visible", [this,selector_widget,page_num](const Preferences::Entry& value) {
309 _combo->set_row_visible(page_num, value.getBool());
310 selector_widget->set_visible(value.getBool());
311 });
312 obs->call();
313 _visibility_observers.emplace_back(std::move(obs));
314}
315
316} // namespace Inkscape::UI::Widget
317
318/*
319 Local Variables:
320 mode:c++
321 c-file-style:"stroustrup"
322 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
323 indent-tabs-mode:nil
324 fill-column:99
325 End:
326*/
327// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
static Manager & get()
Definition manager.h:36
std::vector< std::shared_ptr< Space::AnySpace > > spaces(Space::Traits traits)
Definition manager.cpp:84
Data type representing a typeless value of a preference.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
Preference storage class.
Definition preferences.h:61
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
std::unique_ptr< PreferencesObserver > createObserver(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
Create an observer watching preference 'path' and calling provided function when preference changes.
ColorNotebook(std::shared_ptr< Colors::ColorSet > color)
int getPageIndex(const Glib::ustring &name)
std::shared_ptr< Colors::ColorSet > _colors
void setDocument(SPDocument *document)
void _addPageForSpace(std::shared_ptr< Colors::Space::AnySpace > space)
std::vector< PrefObserver > _visibility_observers
void set_label(const Glib::ustring &label)
static void _onPickerClicked(GtkWidget *widget, ColorNotebook *colorbook)
void _setCurrentPage(int i, bool sync_combo)
void add_row(Glib::ustring const &icon_name, Glib::ustring const &label, int id)
sigc::signal< void(int)> & signal_changed()
sigc::connection connectDocumentReplaced(F &&slot)
Definition desktop.h:257
SPDocument * getDocument() const
Definition desktop.h:189
Typed SVG document implementation.
Definition document.h:101
Entry widget for typing color value in css form.
static constexpr int XPAD
static constexpr int YPAD
A notebook with RGB, CMYK, CMS, HSL, and Wheel pages.
Build a set of color sliders for a given color space.
SPObject of the color-profile object found a direct child of defs.
double c[8][4]
Editable view implementation.
void sp_dialog_defocus_on_enter(Gtk::Entry *e)
Event handler for dialog windows.
Gtk::Image * sp_get_icon_image(Glib::ustring const &icon_name, int size)
Icon Loader.
Glib::ustring label
void sp_toggle_dropper(SPDesktop *dt)
Toggles current tool between active tool and dropper tool.
Custom widgets.
Definition desktop.h:126
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
Definition pack.cpp:153
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
Definition util.cpp:141
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
STL namespace.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Singleton class to access the preferences file in a convenient way.
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55