Inkscape
Vector Graphics Editor
color-palette.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/* Authors:
6 * Michael Kowalski
7 *
8 * Copyright (C) 2021 Michael Kowalski
9 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
10 */
11
12#include <utility>
13#include <glibmm/i18n.h>
14#include <glibmm/main.h>
15#include <glibmm/ustring.h>
16#include <gtkmm/adjustment.h>
17#include <gtkmm/builder.h>
18#include <gtkmm/button.h>
19#include <gtkmm/flowbox.h>
20#include <gtkmm/flowboxchild.h>
21#include <gtkmm/label.h>
22#include <gtkmm/menubutton.h>
23#include <gtkmm/popover.h>
24#include <gtkmm/checkbutton.h>
25#include <gtkmm/scale.h>
26#include <gtkmm/scrolledwindow.h>
27#include <gtkmm/scrollbar.h>
28#include <gtkmm/scrolledwindow.h>
29#include <gtkmm/separator.h>
30#include <sigc++/functors/mem_fun.h>
31
32#include "color-palette.h"
33#include "ui/builder-utils.h"
35#include "ui/util.h"
39
40namespace Inkscape::UI::Widget {
41
42[[nodiscard]] static auto make_menu()
43{
44 auto const separator = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
45 separator->set_margin_top (5);
46 separator->set_margin_bottom(5);
47
48 auto const config = Gtk::make_managed<PopoverMenuItem>(_("Configure..."), true);
49
50 auto menu = std::make_unique<PopoverMenu>(nullptr, Gtk::PositionType::TOP);
51 menu->add_css_class("ColorPalette");
52 menu->append(*separator);
53 menu->append(*config);
54
55 return std::make_pair(std::move(menu), std::ref(*config));
56}
57
59 _builder(create_builder("color-palette.glade")),
60 _normal_box(get_widget<Gtk::FlowBox>(_builder, "flow-box")),
61 _pinned_box(get_widget<Gtk::FlowBox>(_builder, "pinned")),
62 _scroll_btn(get_widget<Gtk::FlowBox>(_builder, "scroll-buttons")),
63 _scroll_left(get_widget<Gtk::Button>(_builder, "btn-left")),
64 _scroll_right(get_widget<Gtk::Button>(_builder, "btn-right")),
65 _scroll_up(get_widget<Gtk::Button>(_builder, "btn-up")),
66 _scroll_down(get_widget<Gtk::Button>(_builder, "btn-down")),
67 _scroll(get_widget<Gtk::ScrolledWindow>(_builder, "scroll-wnd"))
68{
69 get_widget<Gtk::CheckButton>(_builder, "show-labels").set_visible(false);
70 _normal_box.set_filter_func([](Gtk::FlowBoxChild*){ return true; });
71
72 auto& box = get_widget<Gtk::Box>(_builder, "palette-box");
73 set_child(box);
74
75 auto [menu, config] = make_menu();
76 _menu = std::move(menu);
77 auto& btn_menu = get_widget<Gtk::MenuButton>(_builder, "btn-menu");
78 btn_menu.set_popover(*_menu);
79 auto& dlg = get_settings_popover();
80 config.signal_activate().connect([&, this] {
81 dlg.set_parent(btn_menu);
82 dlg.popup();
83 });
84
85 auto& size = get_widget<Gtk::Scale>(_builder, "size-slider");
86 size.signal_change_value().connect([=,&size](Gtk::ScrollType, double val) {
87 _set_tile_size(static_cast<int>(size.get_value()));
89 return true;
90 }, true);
91
92 auto& aspect = get_widget<Gtk::Scale>(_builder, "aspect-slider");
93 aspect.signal_change_value().connect([=,&aspect](Gtk::ScrollType, double val) {
94 _set_aspect(aspect.get_value());
96 return true;
97 }, true);
98
99 auto& border = get_widget<Gtk::Scale>(_builder, "border-slider");
100 border.signal_change_value().connect([=,&border](Gtk::ScrollType, double val) {
101 _set_tile_border(static_cast<int>(border.get_value()));
103 return true;
104 }, true);
105
106 auto& rows = get_widget<Gtk::Scale>(_builder, "row-slider");
107 rows.signal_change_value().connect([=,&rows](Gtk::ScrollType, double val) {
108 _set_rows(static_cast<int>(rows.get_value()));
110 return true;
111 }, true);
112
113 auto& sb = get_widget<Gtk::CheckButton>(_builder, "use-sb");
114 sb.set_active(_force_scrollbar);
115 sb.signal_toggled().connect([=,&sb](){
116 _enable_scrollbar(sb.get_active());
118 });
119
120 auto& stretch = get_widget<Gtk::CheckButton>(_builder, "stretch");
121 stretch.set_active(_force_scrollbar);
122 stretch.signal_toggled().connect([=,&stretch](){
123 _enable_stretch(stretch.get_active());
125 });
127
128 auto& large = get_widget<Gtk::CheckButton>(_builder, "enlarge");
129 large.set_active(_large_pinned_panel);
130 large.signal_toggled().connect([=,&large](){
131 _set_large_pinned_panel(large.get_active());
133 });
135
136 auto& sl = get_widget<Gtk::CheckButton>(_builder, "show-labels");
137 sl.set_visible(false);
138 sl.set_active(_show_labels);
139 sl.signal_toggled().connect([=,&sl](){
140 _show_labels = sl.get_active();
143 });
144
145 _scroll.set_min_content_height(1);
146
147 _scroll_down.signal_clicked().connect([=](){ scroll(0, get_palette_height(), get_tile_height() + _border, true); });
148 _scroll_up.signal_clicked().connect([=](){ scroll(0, -get_palette_height(), get_tile_height() + _border, true); });
149 _scroll_left.signal_clicked().connect([=](){ scroll(-10 * (get_tile_width() + _border), 0, 0.0, false); });
150 _scroll_right.signal_clicked().connect([=](){ scroll(10 * (get_tile_width() + _border), 0, 0.0, false); });
151
152 set_vexpand_set(true);
154
155 connectBeforeResize([this] (int w, int h, int) {
156 auto const a = Geom::IntPoint{w, h};
157 if (_allocation == a) return;
158 _allocation = a;
160 });
161}
162
164 if (_active_timeout) {
165 g_source_remove(_active_timeout);
166 }
167}
168
170 return get_widget<Gtk::Popover>(_builder, "config-popup");
171}
172
174 auto& btn_menu = get_widget<Gtk::MenuButton>(_builder, "btn-menu");
175 btn_menu.set_visible(show);
176}
177
178void ColorPalette::do_scroll(int dx, int dy) {
179 if (auto vert = _scroll.get_vscrollbar()) {
180 vert->get_adjustment()->set_value(vert->get_adjustment()->get_value() + dy);
181 }
182 if (auto horz = _scroll.get_hscrollbar()) {
183 horz->get_adjustment()->set_value(horz->get_adjustment()->get_value() + dx);
184 }
185}
186
187std::pair<double, double> get_range(Gtk::Scrollbar& sb) {
188 auto adj = sb.get_adjustment();
189 return std::make_pair(adj->get_lower(), adj->get_upper() - adj->get_page_size());
190}
191
192gboolean ColorPalette::scroll_cb(gpointer self) {
193 auto ptr = static_cast<ColorPalette*>(self);
194 bool fire_again = false;
195
196 if (auto vert = ptr->_scroll.get_vscrollbar()) {
197 auto value = vert->get_adjustment()->get_value();
198 // is this the final adjustment step?
199 if (fabs(ptr->_scroll_final - value) < fabs(ptr->_scroll_step)) {
200 vert->get_adjustment()->set_value(ptr->_scroll_final);
201 fire_again = false; // cancel timer
202 }
203 else {
204 auto pos = value + ptr->_scroll_step;
205 vert->get_adjustment()->set_value(pos);
206 auto range = get_range(*vert);
207 if (pos > range.first && pos < range.second) {
208 // not yet done
209 fire_again = true; // fire this callback again
210 }
211 }
212 }
213
214 if (!fire_again) {
215 ptr->_active_timeout = 0;
216 }
217
218 return fire_again;
219}
220
221void ColorPalette::scroll(int dx, int dy, double snap, bool smooth) {
222 if (auto vert = _scroll.get_vscrollbar()) {
223 if (smooth && dy != 0.0) {
224 _scroll_final = vert->get_adjustment()->get_value() + dy;
225 if (snap > 0) {
226 // round it to whole 'dy' increments
227 _scroll_final -= fmod(_scroll_final, snap);
228 }
229 auto range = get_range(*vert);
230 if (_scroll_final < range.first) {
231 _scroll_final = range.first;
232 }
233 else if (_scroll_final > range.second) {
234 _scroll_final = range.second;
235 }
236 _scroll_step = dy / 4.0;
237 if (!_active_timeout && vert->get_adjustment()->get_value() != _scroll_final) {
238 // limit refresh to 60 fps, in practice it will be slower
239 _active_timeout = g_timeout_add(1000 / 60, &ColorPalette::scroll_cb, this);
240 }
241 }
242 else {
243 vert->get_adjustment()->set_value(vert->get_adjustment()->get_value() + dy);
244 }
245 }
246 if (auto horz = _scroll.get_hscrollbar()) {
247 horz->get_adjustment()->set_value(horz->get_adjustment()->get_value() + dx);
248 }
249}
250
252 return _size;
253}
254
256 return _border;
257}
258
260 return _rows;
261}
262
264 return _aspect;
265}
266
269 auto& slider = get_widget<Gtk::Scale>(_builder, "border-slider");
270 slider.set_value(border);
271}
272
274 if (border == _border) return;
275
276 if (border < 0 || border > 100) {
277 g_warning("Unexpected tile border size of color palette: %d", border);
278 return;
279 }
280
281 _border = border;
282 refresh();
283}
284
287 auto& slider = get_widget<Gtk::Scale>(_builder, "size-slider");
288 slider.set_value(size);
289}
290
292 if (size == _size) return;
293
294 if (size < 1 || size > 1000) {
295 g_warning("Unexpected tile size for color palette: %d", size);
296 return;
297 }
298
299 _size = size;
300 refresh();
301}
302
303void ColorPalette::set_aspect(double aspect) {
304 _set_aspect(aspect);
305 auto& slider = get_widget<Gtk::Scale>(_builder, "aspect-slider");
306 slider.set_value(aspect);
307}
308
309void ColorPalette::_set_aspect(double aspect) {
310 if (aspect == _aspect) return;
311
312 if (aspect < -2.0 || aspect > 2.0) {
313 g_warning("Unexpected aspect ratio for color palette: %f", aspect);
314 return;
315 }
316
317 _aspect = aspect;
318 refresh();
319}
320
323 queue_resize();
324}
325
327 _set_rows(rows);
328 auto& slider = get_widget<Gtk::Scale>(_builder, "row-slider");
329 slider.set_value(rows);
330}
331
333 if (rows == _rows) return;
334
335 if (rows < 1 || rows > 1000) {
336 g_warning("Unexpected number of rows for color palette: %d", rows);
337 return;
338 }
339 _rows = rows;
341 refresh();
342}
343
345 auto& sb = get_widget<Gtk::CheckButton>(_builder, "use-sb");
346 // scrollbar can only be applied to single-row layouts
347 bool sens = _rows == 1;
348 if (sb.get_sensitive() != sens) sb.set_sensitive(sens);
349}
350
351void ColorPalette::set_compact(bool compact) {
352 if (_compact != compact) {
353 _compact = compact;
355
356 get_widget<Gtk::Scale>(_builder, "row-slider").set_visible(compact);
357 get_widget<Gtk::Label>(_builder, "row-label").set_visible(compact);
358 get_widget<Gtk::CheckButton>(_builder, "enlarge").set_visible(compact);
359 // get_widget<Gtk::CheckButton>(_builder, "show-labels").set_visible(false);
360 }
361}
362
364 return _force_scrollbar;
365}
366
368 return _stretch_tiles;
369}
370
372 auto& stretch = get_widget<Gtk::CheckButton>(_builder, "stretch");
373 stretch.set_active(enable);
374 _enable_stretch(enable);
375}
376
378 if (_stretch_tiles == enable) return;
379
380 _stretch_tiles = enable;
381 _normal_box.set_halign(enable ? Gtk::Align::FILL : Gtk::Align::START);
383 refresh();
384}
385
387 auto& sl = get_widget<Gtk::CheckButton>(_builder, "show-labels");
388 sl.set_active(labels);
389 if (_show_labels != labels) {
390 _show_labels = labels;
392 refresh();
393 }
394}
395
397 auto& aspect = get_widget<Gtk::Scale>(_builder, "aspect-slider");
398 aspect.set_sensitive(!_stretch_tiles);
399 auto& label = get_widget<Gtk::Label>(_builder, "aspect-label");
400 label.set_sensitive(!_stretch_tiles);
401}
402
404 auto& sb = get_widget<Gtk::CheckButton>(_builder, "use-sb");
405 sb.set_active(show);
406 _enable_scrollbar(show);
407}
408
410 if (_force_scrollbar == show) return;
411
412 _force_scrollbar = show;
414}
415
417 auto &box = get_widget<Gtk::Box>(_builder, "palette-box");
418 auto &btn_menu = get_widget<Gtk::MenuButton>(_builder, "btn-menu");
419 auto const colors = UI::get_children(_normal_box);
420 auto normal_count = std::max(1, static_cast<int>(colors.size()));
421 auto pinned_count = std::max(1, static_cast<int>(UI::get_children(_pinned_box).size()));
422
423 _normal_box.set_max_children_per_line(normal_count);
424 _normal_box.set_min_children_per_line(1);
425 _pinned_box.set_max_children_per_line(pinned_count);
426 _pinned_box.set_min_children_per_line(1);
427
428 auto alloc_width = _normal_box.get_parent()->get_allocated_width();
429 // if page-size is defined, align color tiles in columns
430 if (_page_size > 1 && alloc_width > 1 && !_show_labels && !colors.empty()) {
431 int width = get_tile_width();
432 if (width > 1) {
433 int cols = alloc_width / (width + _border);
434 cols = std::max(cols - cols % _page_size, _page_size);
435 if (_normal_box.get_max_children_per_line() != cols) {
436 _normal_box.set_max_children_per_line(cols);
437 }
438 }
439 }
440
441 if (_compact) {
442 box.set_orientation(Gtk::Orientation::HORIZONTAL);
443 btn_menu.set_margin_bottom(0);
444 btn_menu.set_margin_end(0);
445 // in compact mode scrollbars are hidden; they take up too much space
446 set_valign(Gtk::Align::START);
447 set_vexpand(false);
448
449 _scroll.set_valign(Gtk::Align::END);
450 _normal_box.set_valign(Gtk::Align::END);
451
452 if (_rows == 1 && _force_scrollbar) {
453 // horizontal scrolling with single row
454 _normal_box.set_min_children_per_line(normal_count);
455
456 _scroll_btn.set_visible(false);
457
458 if (_force_scrollbar) {
459 _scroll_left.set_visible(false);
460 _scroll_right.set_visible(false);
461 }
462 else {
463 _scroll_left.set_visible(true);
464 _scroll_right.set_visible(true);
465 }
466
467 // ideally we should be able to use POLICY_AUTOMATIC, but on some themes this leads to a scrollbar
468 // that obscures color tiles (it overlaps them); thus resorting to manual scrollbar selection
469 _scroll.set_policy(_force_scrollbar ? Gtk::PolicyType::ALWAYS : Gtk::PolicyType::EXTERNAL, Gtk::PolicyType::NEVER);
470 }
471 else {
472 // vertical scrolling with multiple rows
473 // 'external' allows scrollbar to shrink vertically
474 _scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::EXTERNAL);
475 _scroll_left.set_visible(false);
476 _scroll_right.set_visible(false);
477 _scroll_btn.set_visible(true);
478 }
479
480 int div = _large_pinned_panel ? (_rows > 2 ? 2 : 1) : _rows;
481 _pinned_box.set_max_children_per_line(std::max((pinned_count + div - 1) / div, 1));
482 _pinned_box.set_margin_end(_border);
483 }
484 else {
485 box.set_orientation(Gtk::Orientation::VERTICAL);
486 btn_menu.set_margin_bottom(2);
487 btn_menu.set_margin_end(2);
488 // in normal mode use regular full-size scrollbars
489 set_valign(Gtk::Align::FILL);
490 set_vexpand(true);
491
492 _scroll_left.set_visible(false);
493 _scroll_right.set_visible(false);
494 _scroll_btn.set_visible(false);
495
496 _normal_box.set_valign(Gtk::Align::START);
497 _scroll.set_valign(Gtk::Align::FILL);
498 // 'always' allocates space for scrollbar
500 }
501
502 resize();
503}
504
505int ColorPalette::get_tile_size(bool horz) const {
506 if (_stretch_tiles) return _size;
507
508 double aspect = horz ? _aspect : -_aspect;
509 int scale = _show_labels ? 2.0 : 1.0;
510 int size = 0;
511
512 if (aspect > 0) {
513 size = static_cast<int>(round((1.0 + aspect) * _size));
514 }
515 else if (aspect < 0) {
516 size = static_cast<int>(round((1.0 / (1.0 - aspect)) * _size));
517 }
518 else {
519 size = _size;
520 }
521 return size * scale;
522}
523
525 return get_tile_size(true);
526}
527
529 return get_tile_size(false);
530}
531
533 return (get_tile_height() + _border) * _rows;
534}
535
537 auto& checkbox = get_widget<Gtk::CheckButton>(_builder, "enlarge");
538 checkbox.set_active(large);
540}
541
543 if (_large_pinned_panel == large) return;
544
545 _large_pinned_panel = large;
546 refresh();
547}
548
550 return _large_pinned_panel;
551}
552
554 return _show_labels;
555}
556
558 if ((_rows == 1 && _force_scrollbar) || !_compact) {
559 // auto size for single row to allocate space for scrollbar
560 _scroll.set_size_request(-1, -1);
561 }
562 else {
563 // exact size for multiple rows
565 _scroll.set_size_request(1, height);
566 }
567
568 _normal_box.set_column_spacing(_border);
569 _normal_box.set_row_spacing(_border);
570 _pinned_box.set_column_spacing(_border);
571 _pinned_box.set_row_spacing(_border);
572
573 int width = get_tile_width();
574 int height = get_tile_height();
575 for (auto item : _normal_items) {
576 item->set_size_request(width, height);
577 }
578
579 int pinned_width = width;
580 int pinned_height = height;
582 double mult = _rows > 2 ? _rows / 2.0 : 2.0;
583 pinned_width = pinned_height = static_cast<int>((height + _border) * mult - _border);
584 }
585 for (auto item : _pinned_items) {
586 item->set_size_request(pinned_width, pinned_height);
587 }
588}
589
590void ColorPalette::set_colors(std::vector<Dialog::ColorItem*> const &swatches)
591{
592 _normal_items.clear();
593 _pinned_items.clear();
594
595 for (auto item : swatches) {
596 if (item->is_pinned()) {
597 _pinned_items.emplace_back(item);
598 } else {
599 _normal_items.emplace_back(item);
600 }
601 item->signal_modified().connect([=] {
602 UI::for_each_child(*item->get_parent(), [=](Gtk::Widget& w) {
603 if (auto label = dynamic_cast<Gtk::Label *>(&w)) {
604 label->set_text(item->get_description());
605 }
607 });
608 });
609 }
610 rebuild_widgets();
611 refresh();
612}
613
614Gtk::Widget *ColorPalette::_get_widget(Dialog::ColorItem *item) {
615 if (auto parent = item->get_parent()) {
616 auto &flowbox = dynamic_cast<Gtk::FlowBox &>(*parent);
617 flowbox.remove(*item);
618 }
619 if (_show_labels) {
620 item->set_valign(Gtk::Align::CENTER);
621 auto const box = Gtk::make_managed<Gtk::Box>();
622 auto const label = Gtk::make_managed<Gtk::Label>(item->get_description());
623 box->append(*item);
624 box->append(*label);
625 return box;
626 }
627 return Gtk::manage(item);
628}
629
630void ColorPalette::rebuild_widgets()
631{
632 _normal_box.freeze_notify();
633 _pinned_box.freeze_notify();
634
635 UI::remove_all_children(_normal_box);
636 UI::remove_all_children(_pinned_box);
637
638 for (auto item : _normal_items) {
639 // in a tile mode (no labels) group headers are hidden:
640 if (!_show_labels && item->is_group()) continue;
641
642 // in a list mode with labels, do not show fillers:
643 if (_show_labels && item->is_filler()) continue;
644
645 _normal_box.append(*_get_widget(item));
646 }
647 for (auto item : _pinned_items) {
648 _pinned_box.append(*_get_widget(item));
649 }
650
651 set_up_scrolling();
652
653 _normal_box.thaw_notify();
654 _pinned_box.thaw_notify();
655}
656
657class ColorPaletteMenuItem : public PopoverMenuItem {
658public:
659 ColorPaletteMenuItem(Gtk::CheckButton *&group,
660 Glib::ustring const &label,
661 Glib::ustring id,
662 std::vector<rgb_t> colors)
663 : Glib::ObjectBase{"ColorPaletteMenuItem"}
665 , _radio_button{Gtk::make_managed<Gtk::CheckButton>(label)}
666 , _preview{Gtk::make_managed<ColorPalettePreview>(std::move(colors))}
667 , id{std::move(id)}
668 {
669 if (group) {
670 _radio_button->set_group(*group);
671 } else {
672 group = _radio_button;
673 }
674 auto const box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 1);
675 box->append(*_radio_button);
676 box->append(*_preview);
677 set_child(*box);
678 }
679
680 void set_active(bool const active) { _radio_button->set_active(active); }
681
682 Glib::ustring const id;
683
684private:
685 Gtk::CheckButton *_radio_button = nullptr;
686 ColorPalettePreview *_preview = nullptr;
687};
688
689void ColorPalette::set_palettes(std::vector<palette_t> const &palettes)
690{
691 for (auto const &item: _palette_menu_items) {
692 _menu->remove(*item);
693 }
694
695 _palette_menu_items.clear();
696 _palette_menu_items.reserve(palettes.size());
697
698 Gtk::CheckButton *group = nullptr;
699 // We prepend in reverse so we add the palettes above the constant separator & Configure items.
700 for (auto it = palettes.crbegin(); it != palettes.crend(); ++it) {
701 auto& name = it->name;
702 auto& id = it->id;
703 auto item = std::make_unique<ColorPaletteMenuItem>(group, name, id, it->colors);
704 item->signal_activate().connect([=](){
705 if (!_in_update) {
706 _in_update = true;
707 _signal_palette_selected.emit(id);
708 _in_update = false;
709 }
710 });
711 item->set_visible(true);
712 _menu->prepend(*item);
713 _palette_menu_items.push_back(std::move(item));
714 }
715}
716
717sigc::signal<void (Glib::ustring)>& ColorPalette::get_palette_selected_signal() {
718 return _signal_palette_selected;
719}
720
721sigc::signal<void ()>& ColorPalette::get_settings_changed_signal() {
722 return _signal_settings_changed;
723}
724
725void ColorPalette::set_selected(const Glib::ustring& id) {
726 _in_update = true;
727
728 for (auto const &item : _palette_menu_items) {
729 item->set_active(item->id == id);
730 }
731
732 _in_update = false;
733}
734
735void ColorPalette::set_page_size(int page_size) {
736 _page_size = page_size;
737}
738
739void ColorPalette::set_filter(std::function<bool (const Dialog::ColorItem&)> filter) {
740 _normal_box.set_filter_func([=](Gtk::FlowBoxChild* c){
741 auto child = c->get_child();
742 if (auto box = dynamic_cast<Gtk::Box*>(child)) {
743 child = UI::get_children(*box).at(0);
744 }
745 if (auto color = dynamic_cast<Dialog::ColorItem*>(child)) {
746 return filter(*color);
747 }
748 return true;
749 });
750}
751
752void ColorPalette::apply_filter() {
753 _normal_box.invalidate_filter();
754}
755
756} // namespace Inkscape::UI::Widget
757
758/*
759 Local Variables:
760 mode:c++
761 c-file-style:"stroustrup"
762 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
763 indent-tabs-mode:nil
764 fill-column:99
765 End:
766*/
767// vim:filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99:
double scale
Definition: aa.cpp:228
Gtk builder utilities.
Two-dimensional point with integer coordinates.
Definition: int-point.h:57
The color item you see on-screen as a clickable box.
Definition: color-item.h:51
sigc::connection connectBeforeResize(F &&slot)
Register a handler to run immediately before a resize operation.
Definition: bin.h:53
void set_child(Gtk::Widget *child)
Sets (parents) the child widget, or unsets (unparents) it if child is null.
Definition: bin.cpp:45
A Gtk::DrawingArea to preview color palette menu items by showing a small example of the colors.
std::vector< Dialog::ColorItem * > _normal_items
sigc::signal< void()> _signal_settings_changed
Glib::RefPtr< Gtk::Builder > _builder
static gboolean scroll_cb(gpointer self)
std::unique_ptr< PopoverMenu > _menu
void set_colors(std::vector< Dialog::ColorItem * > const &swatches)
std::vector< Dialog::ColorItem * > _pinned_items
void scroll(int dx, int dy, double snap, bool smooth)
A replacement for GTK3ʼs Gtk::MenuItem, as removed in GTK4.
char * id
Definition: sp-object.h:178
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
double c[8][4]
Definition: cylinder3d.cpp:40
@ FILL
SPItem * item
Definition: imagemagick.cpp:43
SymmetricMatrix< N > adj(const ConstBaseSymmetricMatrix< N > &S)
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Definition: sbasisN.h:158
Definition: desktop.h:51
Button
helper to stop accidents on int vs gtkmm3's weak=typed enums, & looks nicer!
Definition: controller.h:70
Custom widgets.
Definition: desktop.h:127
static std::vector< T > range(int const steps, T start, T end)
static constexpr int height
std::pair< double, double > get_range(Gtk::Scrollbar &sb)
static constexpr int dx
static auto make_menu()
void remove_all_children(Widget &widget)
For each child in get_children(widget), call widget.remove(*child). May not cause delete child!
Definition: util.h:79
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
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Definition: builder-utils.h:33
Gtk::Widget * for_each_child(Gtk::Widget &widget, Func &&func, bool const plus_self=false, bool const recurse=false, int const level=0)
Call Func with a reference to each child of parent, until it returns _break.
Definition: util.h:94
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
void set_filter(Gtk::FileDialog &file_dialog, Glib::RefPtr< Gtk::FileFilter > const &filter)
Set the available filters & the default filter, to the single filter passed.
Definition: choose-file.cpp:36
STL namespace.
@ HORIZONTAL
The x-dimension (0).
Definition: rectangle.h:43
@ VERTICAL
The y-dimension (1).
Definition: rectangle.h:47
int size
A replacement for GTK3ʼs Gtk::MenuItem, as removed in GTK4.
A replacement for GTK3ʼs Gtk::Menu, as removed in GTK4.
Ocnode * child[8]
Definition: quantize.cpp:33
Ocnode * parent
Definition: quantize.cpp:31
Ocnode ** ref
Definition: quantize.cpp:32
double border
double width
Glib::RefPtr< Gtk::Adjustment > smooth