Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
swatches.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/* Authors:
3 * Jon A. Cruz
4 * John Bintz
5 * Abhishek Sharma
6 * PBS <pbs3141@gmail.com>
7 *
8 * Copyright (C) 2005 Jon A. Cruz
9 * Copyright (C) 2008 John Bintz
10 * Copyright (C) 2022 PBS
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include "swatches.h"
16
17#include <glibmm/i18n.h>
18#include <gtkmm/accelerator.h>
19#include <gtkmm/eventcontrollerkey.h>
20#include <gtkmm/menubutton.h>
21#include <gtkmm/searchentry2.h>
22#include <gtkmm/sizegroup.h>
23#include <gtkmm/togglebutton.h>
24
25#include "desktop.h"
26#include "desktop-style.h"
27#include "document.h"
28#include "style.h"
29#include "object/sp-defs.h"
31#include "ui/builder-utils.h"
33#include "ui/controller.h"
35#include "ui/util.h" // ellipsize()
39
40namespace Inkscape::Colors {
41 std::size_t hash_value(Color const& b)
42 {
43 boost::hash<int> hasher;
44 return hasher(b.toRGBA());
45 }
46}
47
48namespace Inkscape::UI::Dialog {
49
50static constexpr auto auto_id = "Auto";
51
52/*
53 * Lifecycle
54 */
55
56SwatchesPanel::SwatchesPanel(bool compact, char const *prefsPath)
57 : DialogBase(prefsPath, "Swatches"),
58 _builder(create_builder("dialog-swatches.glade")),
59 _list_btn(get_widget<Gtk::ToggleButton>(_builder, "list")),
60 _grid_btn(get_widget<Gtk::ToggleButton>(_builder, "grid")),
61 _selector(get_widget<Gtk::MenuButton>(_builder, "selector")),
62 _selector_label(get_widget<Gtk::Label>(_builder, "selector-label")),
63 _selector_menu{compact ? nullptr : std::make_unique<UI::Widget::PopoverMenu>(Gtk::PositionType::BOTTOM)},
64 _new_btn(get_widget<Gtk::Button>(_builder, "new")),
65 _edit_btn(get_widget<Gtk::Button>(_builder, "edit")),
66 _delete_btn(get_widget<Gtk::Button>(_builder, "delete"))
67{
68 // hide edit buttons - this functionality is not implemented
69 _new_btn.set_visible(false);
70 _edit_btn.set_visible(false);
71 _delete_btn.set_visible(false);
72
73 _palette = Gtk::make_managed<Inkscape::UI::Widget::ColorPalette>();
74 _palette->set_visible();
75 if (compact) {
77 } else {
78 get_widget<Gtk::Box>(_builder, "content").append(*_palette);
79 _palette->set_expand();
80
82
83 // Steal popover from colour palette and attach it to our button instead. Fixme: Bad, fragile.
84 auto &popover = _palette->get_settings_popover();
85 popover.unparent();
86 get_widget<Gtk::MenuButton>(_builder, "settings").set_popover(popover);
87
88 _palette->set_filter([this](Dialog::ColorItem const &color){
89 return filter_callback(color);
90 });
91 auto& search = get_widget<Gtk::SearchEntry2>(_builder, "search");
92 search.signal_search_changed().connect([this, &search]{
93 if (search.get_text().length() == 0) {
94 clear_filter();
95 } else {
96 filter_colors(search.get_text());
97 }
98 });
99 }
100
101 auto prefs = Inkscape::Preferences::get();
102 _current_palette_id = prefs->getString(_prefs_path + "/palette");
103 if (auto p = get_palette(_current_palette_id)) {
104 _current_palette_id = p->id; // Canonicalise to id, in case name lookup was used.
105 } else {
106 _current_palette_id = auto_id; // Fall back to auto palette.
107 }
108 auto path = prefs->getString(_prefs_path + "/palette-path");
109 auto loaded = load_swatches(Glib::filename_from_utf8(path));
110
111 update_palettes(compact);
112
113 if (!compact) {
114 if (loaded) {
116 }
117
118 g_assert(_selector_menu);
122 }
123
124 bool embedded = compact;
125 _palette->set_compact(embedded);
126
127 // restore palette settings
128 _palette->set_tile_size(prefs->getInt(_prefs_path + "/tile_size", 16));
129 _palette->set_aspect(prefs->getDoubleLimited(_prefs_path + "/tile_aspect", 0.0, -2, 2));
130 _palette->set_tile_border(prefs->getInt(_prefs_path + "/tile_border", 1));
131 _palette->set_rows(prefs->getInt(_prefs_path + "/rows", 2));
132 _palette->enable_stretch(prefs->getBool(_prefs_path + "/tile_stretch", true));
133 _palette->set_large_pinned_panel(embedded && prefs->getBool(_prefs_path + "/enlarge_pinned", true));
134 _palette->enable_labels(!embedded && prefs->getBool(_prefs_path + "/show_labels", true));
135
136 // save settings when they change
137 _palette->get_settings_changed_signal().connect([=, this]{
138 prefs->setInt(_prefs_path + "/tile_size", _palette->get_tile_size());
139 prefs->setDouble(_prefs_path + "/tile_aspect", _palette->get_aspect());
140 prefs->setInt(_prefs_path + "/tile_border", _palette->get_tile_border());
141 prefs->setInt(_prefs_path + "/rows", _palette->get_rows());
142 prefs->setBool(_prefs_path + "/tile_stretch", _palette->is_stretch_enabled());
143 prefs->setBool(_prefs_path + "/enlarge_pinned", _palette->is_pinned_panel_large());
144 prefs->setBool(_prefs_path + "/show_labels", !embedded && _palette->are_labels_enabled());
145 });
146
147 _list_btn.signal_toggled().connect([this]{
148 _palette->enable_labels(true);
149 });
150 _grid_btn.signal_toggled().connect([this]{
151 _palette->enable_labels(false);
152 });
153 (_palette->are_labels_enabled() ? _list_btn : _grid_btn).set_active();
154
155 // Watch for pinned palette options.
156 _pinned_observer = prefs->createObserver(_prefs_path + "/pinned/", [this]() {
157 rebuild();
158 });
159
160 rebuild();
161
162 if (compact) {
163 // Respond to requests from the palette widget to change palettes.
164 _palette->get_palette_selected_signal().connect([this] (Glib::ustring name) {
166 });
167 } else {
168 append(get_widget<Gtk::Box>(_builder, "main"));
169
170 get_widget<Gtk::Button>(_builder, "open").signal_clicked().connect([this]{
171 // load a color palette file selected by the user
172 if (load_swatches()) {
176 }
177 });;
178 }
179}
180
185
186/*
187 * Activation
188 */
189
190// Note: The "Auto" palette shows the list of gradients that are swatches. When this palette is
191// shown (and we have a document), we therefore need to track both addition/removal of gradients
192// and changes to the isSwatch() status to keep the palette up-to-date.
193
195{
196 if (getDocument()) {
199 }
200 } else {
202 }
203
205 rebuild();
206 }
207}
208
213
214void SwatchesPanel::set_palette(const Glib::ustring& id) {
215 auto prefs = Preferences::get();
216 prefs->setString(_prefs_path + "/palette", id);
217 select_palette(id);
218}
219
220const PaletteFileData *SwatchesPanel::get_palette(const Glib::ustring& id) {
221 if (auto p = GlobalPalettes::get().find_palette(id)) return p;
222
223 if (_loaded_palette.id == id) return &_loaded_palette;
224
225 return nullptr;
226}
227
228void SwatchesPanel::select_palette(const Glib::ustring& id) {
229 if (_current_palette_id == id) return;
230
232
233 bool edit = false;
234 if (id == auto_id) {
235 if (getDocument()) {
237 edit = false; /*TODO: true; when swatch editing is ready */
238 }
239 } else {
241 }
242
244
245 _new_btn.set_visible(edit);
246 _edit_btn.set_visible(edit);
247 _delete_btn.set_visible(edit);
248
249 rebuild();
250}
251
253{
254 auto doc = getDocument();
255
256 // Subscribe to the addition and removal of gradients.
257 conn_gradients.disconnect();
258 conn_gradients = doc->connectResourcesChanged("gradient", [this] {
259 gradients_changed = true;
261 });
262
263 // Subscribe to child modifications of the defs section. We will use this to monitor
264 // each gradient for whether its isSwatch() status changes.
265 conn_defs.disconnect();
266 conn_defs = doc->getDefs()->connectModified([this] (SPObject*, unsigned flags) {
267 if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
268 defs_changed = true;
270 }
271 });
272
273 gradients_changed = false;
274 defs_changed = false;
276}
277
279{
280 conn_gradients.disconnect();
281 conn_defs.disconnect();
282 gradients_changed = false;
283 defs_changed = false;
284}
285
286/*
287 * Updating
288 */
289
295
297{
298 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
299 selection_changed = true;
301 }
302}
303
305{
306 if (_tick_callback) {
307 return;
308 }
309
310 _tick_callback = add_tick_callback([this] (auto &&) {
311 _tick_callback = 0;
312 _update();
313 return false;
314 });
315}
316
317// Document updates are handled asynchronously by setting a flag and queuing a tick callback.
318// This results in the following function being run just before the widget is relayouted,
319// so that multiple document updates only result in a single UI update.
321{
322 if (gradients_changed) {
323 assert(_current_palette_id == auto_id);
324 // We are in the "Auto" palette, and a gradient was added or removed.
325 // The list of widgets has therefore changed, and must be completely rebuilt.
326 // We must also rebuild the tracking information for each gradient's isSwatch() status.
328 rebuild();
329 } else if (defs_changed) {
330 assert(_current_palette_id == auto_id);
331 // We are in the "Auto" palette, and a gradient's isSwatch() status was possibly modified.
332 // Check if it has; if so, then the list of widgets has changed, and must be rebuilt.
333 if (update_isswatch()) {
334 rebuild();
335 }
336 }
337
338 if (selection_changed) {
340 }
341
342 selection_changed = false;
343 gradients_changed = false;
344 defs_changed = false;
345}
346
348{
349 auto grads = getDocument()->getResourceList("gradient");
350
351 isswatch.resize(grads.size());
352
353 for (int i = 0; i < grads.size(); i++) {
354 isswatch[i] = static_cast<SPGradient*>(grads[i])->isSwatch();
355 }
356}
357
359{
360 auto grads = getDocument()->getResourceList("gradient");
361
362 // Should be guaranteed because we catch all size changes and call rebuild_isswatch() instead.
363 assert(isswatch.size() == grads.size());
364
365 bool modified = false;
366
367 for (int i = 0; i < grads.size(); i++) {
368 if (isswatch[i] != static_cast<SPGradient*>(grads[i])->isSwatch()) {
369 isswatch[i].flip();
370 modified = true;
371 }
372 }
373
374 return modified;
375}
376
378{
379 auto doc = getDocument();
380 auto style = SPStyle(doc);
381
382 // Get the current fill or stroke as a ColorKey.
383 auto current_color = [&, this] (bool fill) -> std::optional<ColorKey> {
385 {
389 break;
390 default:
391 return {};
392 }
393
394 auto attr = style.getFillOrStroke(fill);
395 if (!attr->set) {
396 return {};
397 }
398
399 if (attr->isNone()) {
400 return std::monostate{};
401 } else if (attr->isColor()) {
402 return attr->getColor();
403 } else if (attr->isPaintserver()) {
404 if (auto grad = cast<SPGradient>(fill ? style.getFillPaintServer() : style.getStrokePaintServer())) {
405 if (grad->isSwatch()) {
406 return grad;
407 } else if (grad->ref) {
408 if (auto ref = grad->ref->getObject(); ref && ref->isSwatch()) {
409 return ref;
410 }
411 }
412 }
413 }
414
415 return {};
416 };
417
418 for (auto w : current_fill) w->set_fill(false);
419 for (auto w : current_stroke) w->set_stroke(false);
420
421 current_fill.clear();
422 current_stroke.clear();
423
424 if (auto fill = current_color(true)) {
425 auto range = widgetmap.equal_range(*fill);
426 for (auto it = range.first; it != range.second; ++it) {
427 current_fill.emplace_back(it->second);
428 }
429 }
430
431 if (auto stroke = current_color(false)) {
432 auto range = widgetmap.equal_range(*stroke);
433 for (auto it = range.first; it != range.second; ++it) {
434 current_stroke.emplace_back(it->second);
435 }
436 }
437
438 for (auto w : current_fill) w->set_fill(true);
439 for (auto w : current_stroke) w->set_stroke(true);
440}
441
442[[nodiscard]] static auto to_palette_t(PaletteFileData const &p)
443{
444 UI::Widget::palette_t palette;
445 palette.name = p.name;
446 palette.id = p.id;
447 for (auto const &c : p.colors) {
448 std::visit(VariantVisitor {
449 [&](const Colors::Color& c) {
450 auto rgb = *c.converted(Colors::Space::Type::RGB);
451 palette.colors.push_back({rgb[0], rgb[1], rgb[2]});
452 },
453 [](const PaletteFileData::SpacerItem&) {},
454 [](const PaletteFileData::GroupStart&) {}
455 }, c);
456 }
457 return palette;
458}
459
464 std::vector<UI::Widget::palette_t> palettes;
465 palettes.reserve(1 + GlobalPalettes::get().palettes().size());
466
467 // The first palette in the list is always the "Auto" palette. Although this
468 // will contain colors when selected, the preview we show for it is empty.
469 // TRANSLATORS: A list of swatches in the document
470 palettes.push_back({_("Document swatches"), auto_id, {}});
471
472 // The remaining palettes in the list are the global palettes.
473 for (auto &p : GlobalPalettes::get().palettes()) {
474 auto palette = to_palette_t(p);
475 palettes.emplace_back(std::move(palette));
476 }
477
478 _palette->set_palettes(palettes);
479
480 _palettes.clear();
481 _palettes.reserve(palettes.size());
482 std::transform(palettes.begin(), palettes.end(), std::back_inserter(_palettes),
483 [](auto &&palette){ return PaletteLoaded{std::move(palette), false}; });
484}
485
489void SwatchesPanel::rebuild()
490{
491 std::vector<std::unique_ptr<ColorItem>> palette;
492
493 // The pointers in widgetmap are to widgets owned by the ColorPalette. It is assumed it will not
494 // delete them unless we ask, via the call to set_colors() later in this function.
495 widgetmap.clear();
496 current_fill.clear();
497 current_stroke.clear();
498
499 // Add the "remove-color" color.
500 auto w = std::make_unique<ColorItem>(this);
501 w->set_pinned_pref(_prefs_path);
502 widgetmap.emplace(std::monostate{}, w.get());
503 palette.push_back(std::move(w));
504
505 _palette->set_page_size(0);
506 if (auto pal = get_palette(_current_palette_id)) {
507 _palette->set_page_size(pal->columns);
508 palette.reserve(palette.size() + pal->colors.size());
509 auto dialog = this;
510 for (auto &c : pal->colors) {
511 auto w = std::visit(VariantVisitor {
512 [](const PaletteFileData::SpacerItem&) {
513 return std::make_unique<ColorItem>("");
514 },
515 [](const PaletteFileData::GroupStart& g) {
516 return std::make_unique<ColorItem>(g.name);
517 },
518 [=, this](const Colors::Color& c) {
519 auto w = std::make_unique<ColorItem>(c, dialog);
520 w->set_pinned_pref(_prefs_path);
521 widgetmap.emplace(c, w.get());
522 return w;
523 },
524 }, c);
525 palette.push_back(std::move(w));
526 }
527 } else if (_current_palette_id == auto_id && getDocument()) {
528 auto grads = getDocument()->getResourceList("gradient");
529 for (auto obj : grads) {
530 auto grad = cast_unsafe<SPGradient>(obj);
531 if (grad->isSwatch()) {
532 auto w = std::make_unique<ColorItem>(grad, this);
533 widgetmap.emplace(grad, w.get());
534 // Rebuild if the gradient gets pinned or unpinned
535 w->signal_pinned().connect([this]{
536 rebuild();
537 });
538 palette.push_back(std::move(w));
539 }
540 }
541 }
542
543 if (getDocument()) {
544 update_fillstroke_indicators();
545 }
546
547 _palette->set_colors(std::move(palette));
548 _palette->set_selected(_current_palette_id);
549}
550
551bool SwatchesPanel::load_swatches() {
552 auto window = dynamic_cast<Gtk::Window *>(get_root());
553 auto file = choose_palette_file(window);
554 auto loaded = false;
555 if (file && load_swatches(file->get_path())) {
556 auto prefs = Preferences::get();
557 prefs->setString(_prefs_path + "/palette", _loaded_palette.id);
558 prefs->setString(_prefs_path + "/palette-path", file->get_path());
559 loaded = true;
560 }
561 return loaded;
562}
563
564bool SwatchesPanel::load_swatches(std::string const &path)
565{
566 if (path.empty()) {
567 return false;
568 }
569
570 // load colors
571 auto res = load_palette(path);
572 if (res.palette) {
573 // use loaded palette
574 _loaded_palette = std::move(*res.palette);
575 return true;
576 } else if (auto desktop = getDesktop()) {
577 desktop->showNotice(res.error_message);
578 }
579
580 return false;
581}
582
583void SwatchesPanel::update_loaded_palette_entry() {
584 // add or update last entry in a store to match loaded palette
585 if (_palettes.empty() || !_palettes.back().second) { // last palette !loaded
586 _palettes.emplace_back();
587 }
588 auto &[palette, loaded] = _palettes.back();
589 palette = to_palette_t(_loaded_palette);
590 loaded = true;
591}
592
593void SwatchesPanel::setup_selector_menu()
594{
595 auto const key = Gtk::EventControllerKey::create();
596 key->signal_key_pressed().connect(sigc::mem_fun(*this, &SwatchesPanel::on_selector_key_pressed), true);
597 _selector.set_popover(*_selector_menu);
598 _selector.add_controller(key);
599}
600
601bool SwatchesPanel::on_selector_key_pressed(unsigned const keyval, unsigned /*keycode*/,
602 Gdk::ModifierType const state)
603{
604 // We act like GtkComboBox in that we only move the active item if no modifier key was pressed:
605 if (Controller::has_flag(state, Gtk::Accelerator::get_default_mod_mask())) return false;
606
607 auto const begin = _palettes.cbegin(), end = _palettes.cend();
608 auto it = std::find_if(begin, end, [&](auto &p){ return p.first.id == _current_palette_id; });
609 if (it == end) return false;
610
611 int const old_index = std::distance(begin, it), back = _palettes.size() - 1;
612 int new_index = old_index;
613
614 switch (keyval) {
615 case GDK_KEY_Up : --new_index ; break;
616 case GDK_KEY_Down: ++new_index ; break;
617 case GDK_KEY_Home: new_index = 0 ; break;
618 case GDK_KEY_End : new_index = back; break;
619 default: return false;
620 }
621
622 new_index = std::clamp(new_index, 0, back);
623 if (new_index != old_index) {
624 it = begin + new_index;
625 set_palette(it->first.id);
626 }
627 return true;
628}
629
630[[nodiscard]] static auto make_selector_item(UI::Widget::palette_t const &palette)
631{
632 static constexpr int max_chars = 35; // Make PopoverMenuItems ellipsize long labels, in middle.
633
634 auto const label = Gtk::make_managed<Gtk::Label>(palette.name, true);
635 label->set_xalign(0.0);
636 UI::ellipsize(*label, max_chars, Pango::EllipsizeMode::MIDDLE);
637
638 auto const box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 1);
639 box->append(*label);
640 box->append(*Gtk::make_managed<UI::Widget::ColorPalettePreview>(palette.colors));
641
642 auto const item = Gtk::make_managed<UI::Widget::PopoverMenuItem>();
643 item->set_child(*box);
644
645 return std::pair{item, label};
646}
647
648void SwatchesPanel::update_selector_menu()
649{
650 g_assert(_selector_menu);
651
652 _selector.set_sensitive(false);
653 _selector_label.set_label({});
654 _selector_menu->remove_all();
655
656 if (_palettes.empty()) return;
657
658 // TODO: GTK4: probably nicer to use GtkGridView.
659 Inkscape::UI::ColumnMenuBuilder builder{*_selector_menu, 2};
660 // Items are put in a SizeGroup to keep the two columnsʼ widths homogeneous
661 auto const size_group = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL);
662 auto const add_item = [&](UI::Widget::palette_t const &palette){
663 auto const [item, label] = make_selector_item(palette);
664 item->signal_activate().connect([id = palette.id, this]{ set_palette(id); });
665 size_group->add_widget(*label);
666 builder.add_item(*item);
667 };
668 // Acrobatics are done to sort down columns, not over rows
669 auto const size = _palettes.size(), half = (size + 1) / 2;
670 for (std::size_t left = 0; left < half; ++left) {
671 add_item(_palettes.at(left).first);
672 if (auto const right = left + half; right < size) {
673 add_item(_palettes.at(right).first);
674 }
675 }
676
677 _selector.set_sensitive(true);
678 size_group->add_widget(_selector_label);
679}
680
681void SwatchesPanel::update_selector_label(Glib::ustring const &active_id)
682{
683 // Set the new paletteʼs name as label of the selector menubutton.
684 auto const it = std::find_if(_palettes.cbegin(), _palettes.cend(),
685 [&](auto const &pair){ return pair.first.id == active_id; });
686 g_assert(it != _palettes.cend());
687 _selector_label.set_label(it->first.name);
688}
689
690void SwatchesPanel::clear_filter() {
691 if (_color_filter_text.empty()) return;
692
693 _color_filter_text.erase();
694 _palette->apply_filter();
695}
696
697void SwatchesPanel::filter_colors(const Glib::ustring& text) {
698 auto search = text.lowercase();
699 if (_color_filter_text == search) return;
700
701 _color_filter_text = search;
702 _palette->apply_filter();
703}
704
705bool SwatchesPanel::filter_callback(const Dialog::ColorItem& color) const {
706 if (_color_filter_text.empty()) return true;
707
708 // let's hide group headers and fillers when searching for a matching color
709 if (color.is_filler() || color.is_group()) return false;
710
711 auto text = color.get_description().lowercase();
712 return text.find(_color_filter_text) != Glib::ustring::npos;
713}
714
715} // namespace Inkscape::UI::Dialog
716
717/*
718 Local Variables:
719 mode:c++
720 c-file-style:"stroustrup"
721 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
722 indent-tabs-mode:nil
723 fill-column:99
724 End:
725*/
726// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Gtk builder utilities.
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
Definition color.cpp:117
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
The color item you see on-screen as a clickable box.
Definition color-item.h:46
const Glib::ustring & get_description() const
Definition color-item.h:79
DialogBase is the base class for the dialog system.
Definition dialog-base.h:40
SPDocument * getDocument() const
Definition dialog-base.h:81
Glib::ustring const _prefs_path
Definition dialog-base.h:86
SPDesktop * getDesktop() const
Definition dialog-base.h:77
static GlobalPalettes const & get()
const std::vector< PaletteFileData > & palettes() const
void update_palettes(bool compact)
Process the list of available palettes and update the list in the _palette widget.
Definition swatches.cpp:463
void select_palette(const Glib::ustring &id)
Definition swatches.cpp:228
std::vector< ColorItem * > current_fill
Definition swatches.h:111
bool filter_callback(const Dialog::ColorItem &color) const
Definition swatches.cpp:705
std::vector< ColorItem * > current_stroke
Definition swatches.h:112
void rebuild()
Rebuild the list of color items shown by the palette.
Definition swatches.cpp:489
Glib::RefPtr< Gtk::Builder > _builder
Definition swatches.h:116
Inkscape::PrefObserver _pinned_observer
Definition swatches.h:115
std::unique_ptr< UI::Widget::PopoverMenu > _selector_menu
Definition swatches.h:122
Inkscape::UI::Widget::ColorPalette * _palette
Definition swatches.h:86
void set_palette(const Glib::ustring &id)
Definition swatches.cpp:214
void selectionChanged(Selection *selection) final
Definition swatches.cpp:290
const PaletteFileData * get_palette(const Glib::ustring &id)
Definition swatches.cpp:220
Gtk::ToggleButton & _grid_btn
Definition swatches.h:118
SwatchesPanel(bool compact, char const *prefsPath="/dialogs/swatches")
Definition swatches.cpp:56
void update_selector_label(Glib::ustring const &active_id)
Definition swatches.cpp:681
void selectionModified(Selection *selection, guint flags) final
Definition swatches.cpp:296
void desktopReplaced() final
Called when the desktop has certainly changed.
Definition swatches.cpp:209
Gtk::ToggleButton & _list_btn
Definition swatches.h:117
boost::unordered_multimap< ColorKey, ColorItem * > widgetmap
Definition swatches.h:110
std::vector< PaletteLoaded > _palettes
Definition swatches.h:126
void set_filter(std::function< bool(const Dialog::ColorItem &)> filter)
sigc::signal< void()> & get_settings_changed_signal()
sigc::signal< void(Glib::ustring)> & get_palette_selected_signal()
void set_palettes(const std::vector< palette_t > &palettes)
A replacement for GTK3ʼs Gtk::Menu, as removed in GTK4.
void showNotice(Glib::ustring const &msg, int timeout=0)
Definition desktop.cpp:1268
std::vector< SPObject * > const getResourceList(char const *key)
Gradient.
Definition sp-gradient.h:86
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
An SVG style object.
Definition style.h:45
Color item used in palettes and swatches UI.
A Gtk::DrawingArea to preview color palette menu items by showing a small example of the colors.
Color palette widget.
const double w
Definition conic-4.cpp:19
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
Colors::Color fill
Colors::Color stroke
double c[8][4]
int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property)
Query the subselection (if any) or selection on the given desktop for the given property,...
@ QUERY_STYLE_PROPERTY_STROKE
@ QUERY_STYLE_PROPERTY_FILL
@ QUERY_STYLE_SINGLE
@ QUERY_STYLE_MULTIPLE_AVERAGED
@ QUERY_STYLE_MULTIPLE_SAME
Editable view implementation.
SPItem * item
Glib::ustring label
Definition desktop.h:50
A set of useful color modifying functions which do not fit as generic methods on the color class itse...
Definition profile.cpp:24
std::size_t hash_value(Color const &b)
Definition swatches.cpp:41
bool has_flag(Gdk::ModifierType const state, Gdk::ModifierType const flags)
Helper to query if ModifierType state contains one or more of given flag(s).
Definition controller.h:25
Dialog code.
Definition desktop.h:117
Glib::RefPtr< Gio::File > choose_palette_file(Gtk::Window *window)
PaletteResult load_palette(std::string const &path)
static auto make_selector_item(UI::Widget::palette_t const &palette)
Definition swatches.cpp:630
static auto to_palette_t(PaletteFileData const &p)
Definition swatches.cpp:442
static constexpr auto auto_id
Definition swatches.cpp:50
void ellipsize(Gtk::Label &label, int const max_width_chars, Pango::EllipsizeMode const mode)
Definition util.cpp:236
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
static void append(std::vector< T > &target, std::vector< T > &&source)
STL namespace.
static cairo_user_data_key_t key
RGB rgb
Definition quantize.cpp:36
Ocnode ** ref
Definition quantize.cpp:32
TODO: insert short description here.
The data loaded from a palette file.
Glib::ustring id
Unique ID of this palette.
std::vector< ColorItem > colors
The list of colors in the palette.
Glib::ustring name
Name of the palette, either specified in the file or taken from the filename.
std::vector< rgb_t > colors
Definition palette_t.h:21
SPStyle - a style object for SPItem objects.
Color swatches dialog.
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder