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>
45 separator->set_margin_top (5);
46 separator->set_margin_bottom(5);
48 auto const config = Gtk::make_managed<PopoverMenuItem>(_(
"Configure..."),
true);
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);
55 return std::make_pair(std::move(menu),
std::ref(*config));
62 _scroll_btn(
get_widget<
Gtk::FlowBox>(_builder,
"scroll-buttons")),
67 _scroll(
get_widget<
Gtk::ScrolledWindow>(_builder,
"scroll-wnd"))
69 get_widget<Gtk::CheckButton>(
_builder,
"show-labels").set_visible(
false);
70 _normal_box.set_filter_func([](Gtk::FlowBoxChild*){
return true; });
72 auto& box = get_widget<Gtk::Box>(
_builder,
"palette-box");
76 _menu = std::move(menu);
77 auto& btn_menu = get_widget<Gtk::MenuButton>(
_builder,
"btn-menu");
78 btn_menu.set_popover(*
_menu);
80 config.signal_activate().connect([&,
this] {
81 dlg.set_parent(btn_menu);
85 auto&
size = get_widget<Gtk::Scale>(
_builder,
"size-slider");
86 size.signal_change_value().connect([=,&
size](Gtk::ScrollType,
double val) {
92 auto& aspect = get_widget<Gtk::Scale>(
_builder,
"aspect-slider");
93 aspect.signal_change_value().connect([=,&aspect](Gtk::ScrollType,
double val) {
100 border.signal_change_value().connect([=,&
border](Gtk::ScrollType,
double val) {
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()));
113 auto& sb = get_widget<Gtk::CheckButton>(
_builder,
"use-sb");
115 sb.signal_toggled().connect([=,&sb](){
120 auto& stretch = get_widget<Gtk::CheckButton>(
_builder,
"stretch");
122 stretch.signal_toggled().connect([=,&stretch](){
128 auto& large = get_widget<Gtk::CheckButton>(
_builder,
"enlarge");
130 large.signal_toggled().connect([=,&large](){
136 auto& sl = get_widget<Gtk::CheckButton>(
_builder,
"show-labels");
137 sl.set_visible(
false);
139 sl.signal_toggled().connect([=,&sl](){
145 _scroll.set_min_content_height(1);
152 set_vexpand_set(
true);
170 return get_widget<Gtk::Popover>(
_builder,
"config-popup");
174 auto& btn_menu = get_widget<Gtk::MenuButton>(
_builder,
"btn-menu");
175 btn_menu.set_visible(show);
179 if (
auto vert =
_scroll.get_vscrollbar()) {
180 vert->get_adjustment()->set_value(vert->get_adjustment()->get_value() + dy);
182 if (
auto horz =
_scroll.get_hscrollbar()) {
183 horz->get_adjustment()->set_value(horz->get_adjustment()->get_value() +
dx);
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());
194 bool fire_again =
false;
196 if (
auto vert = ptr->_scroll.get_vscrollbar()) {
197 auto value = vert->get_adjustment()->get_value();
199 if (fabs(ptr->_scroll_final - value) < fabs(ptr->_scroll_step)) {
200 vert->get_adjustment()->set_value(ptr->_scroll_final);
205 vert->get_adjustment()->set_value(pos);
207 if (pos >
range.first && pos <
range.second) {
215 ptr->_active_timeout = 0;
222 if (
auto vert =
_scroll.get_vscrollbar()) {
223 if (
smooth && dy != 0.0) {
243 vert->get_adjustment()->set_value(vert->get_adjustment()->get_value() + dy);
246 if (
auto horz =
_scroll.get_hscrollbar()) {
247 horz->get_adjustment()->set_value(horz->get_adjustment()->get_value() +
dx);
269 auto& slider = get_widget<Gtk::Scale>(
_builder,
"border-slider");
276 if (border < 0 || border > 100) {
277 g_warning(
"Unexpected tile border size of color palette: %d",
border);
287 auto& slider = get_widget<Gtk::Scale>(
_builder,
"size-slider");
288 slider.set_value(
size);
294 if (size < 1 || size > 1000) {
295 g_warning(
"Unexpected tile size for color palette: %d",
size);
305 auto& slider = get_widget<Gtk::Scale>(
_builder,
"aspect-slider");
306 slider.set_value(aspect);
312 if (aspect < -2.0 || aspect > 2.0) {
313 g_warning(
"Unexpected aspect ratio for color palette: %f", aspect);
328 auto& slider = get_widget<Gtk::Scale>(
_builder,
"row-slider");
329 slider.set_value(rows);
333 if (rows ==
_rows)
return;
335 if (rows < 1 || rows > 1000) {
336 g_warning(
"Unexpected number of rows for color palette: %d", rows);
345 auto& sb = get_widget<Gtk::CheckButton>(
_builder,
"use-sb");
347 bool sens =
_rows == 1;
348 if (sb.get_sensitive() != sens) sb.set_sensitive(sens);
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);
372 auto& stretch = get_widget<Gtk::CheckButton>(
_builder,
"stretch");
373 stretch.set_active(enable);
387 auto& sl = get_widget<Gtk::CheckButton>(
_builder,
"show-labels");
388 sl.set_active(labels);
397 auto& aspect = get_widget<Gtk::Scale>(
_builder,
"aspect-slider");
399 auto& label = get_widget<Gtk::Label>(
_builder,
"aspect-label");
404 auto& sb = get_widget<Gtk::CheckButton>(
_builder,
"use-sb");
417 auto &box = get_widget<Gtk::Box>(
_builder,
"palette-box");
418 auto &btn_menu = get_widget<Gtk::MenuButton>(
_builder,
"btn-menu");
420 auto normal_count =
std::max(1,
static_cast<int>(colors.size()));
423 _normal_box.set_max_children_per_line(normal_count);
425 _pinned_box.set_max_children_per_line(pinned_count);
428 auto alloc_width =
_normal_box.get_parent()->get_allocated_width();
435 if (
_normal_box.get_max_children_per_line() != cols) {
443 btn_menu.set_margin_bottom(0);
444 btn_menu.set_margin_end(0);
446 set_valign(Gtk::Align::START);
449 _scroll.set_valign(Gtk::Align::END);
454 _normal_box.set_min_children_per_line(normal_count);
486 btn_menu.set_margin_bottom(2);
487 btn_menu.set_margin_end(2);
513 size =
static_cast<int>(round((1.0 + aspect) *
_size));
515 else if (aspect < 0) {
516 size =
static_cast<int>(round((1.0 / (1.0 - aspect)) *
_size));
537 auto& checkbox = get_widget<Gtk::CheckButton>(
_builder,
"enlarge");
538 checkbox.set_active(large);
560 _scroll.set_size_request(-1, -1);
579 int pinned_width =
width;
580 int pinned_height =
height;
586 item->set_size_request(pinned_width, pinned_height);
595 for (
auto item : swatches) {
596 if (
item->is_pinned()) {
601 item->signal_modified().connect([=] {
603 if (auto label = dynamic_cast<Gtk::Label *>(&w)) {
604 label->set_text(item->get_description());
616 auto &flowbox =
dynamic_cast<Gtk::FlowBox &
>(*parent);
617 flowbox.remove(*
item);
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());
627 return Gtk::manage(
item);
630void ColorPalette::rebuild_widgets()
632 _normal_box.freeze_notify();
633 _pinned_box.freeze_notify();
638 for (
auto item : _normal_items) {
640 if (!_show_labels &&
item->is_group())
continue;
643 if (_show_labels &&
item->is_filler())
continue;
645 _normal_box.append(*_get_widget(
item));
647 for (
auto item : _pinned_items) {
648 _pinned_box.append(*_get_widget(
item));
653 _normal_box.thaw_notify();
654 _pinned_box.thaw_notify();
659 ColorPaletteMenuItem(Gtk::CheckButton *&group,
660 Glib::ustring
const &label,
662 std::vector<rgb_t> colors)
663 :
Glib::ObjectBase{
"ColorPaletteMenuItem"}
665 , _radio_button{
Gtk::make_managed<
Gtk::CheckButton>(label)}
670 _radio_button->set_group(*group);
672 group = _radio_button;
675 box->append(*_radio_button);
676 box->append(*_preview);
680 void set_active(
bool const active) { _radio_button->set_active(active); }
682 Glib::ustring
const id;
685 Gtk::CheckButton *_radio_button =
nullptr;
686 ColorPalettePreview *_preview =
nullptr;
689void ColorPalette::set_palettes(std::vector<palette_t>
const &palettes)
691 for (
auto const &
item: _palette_menu_items) {
692 _menu->remove(*
item);
695 _palette_menu_items.clear();
696 _palette_menu_items.reserve(palettes.size());
698 Gtk::CheckButton *group =
nullptr;
700 for (
auto it = palettes.crbegin(); it != palettes.crend(); ++it) {
701 auto& name = it->name;
703 auto item = std::make_unique<ColorPaletteMenuItem>(group, name,
id, it->colors);
704 item->signal_activate().connect([=](){
707 _signal_palette_selected.emit(
id);
711 item->set_visible(
true);
712 _menu->prepend(*
item);
713 _palette_menu_items.push_back(std::move(
item));
717sigc::signal<void (Glib::ustring)>& ColorPalette::get_palette_selected_signal() {
718 return _signal_palette_selected;
721sigc::signal<void ()>& ColorPalette::get_settings_changed_signal() {
722 return _signal_settings_changed;
725void ColorPalette::set_selected(
const Glib::ustring&
id) {
728 for (
auto const &
item : _palette_menu_items) {
735void ColorPalette::set_page_size(
int page_size) {
736 _page_size = page_size;
740 _normal_box.set_filter_func([=](Gtk::FlowBoxChild*
c){
741 auto child =
c->get_child();
742 if (
auto box =
dynamic_cast<Gtk::Box*
>(
child)) {
746 return filter(*color);
752void ColorPalette::apply_filter() {
753 _normal_box.invalidate_filter();
Two-dimensional point with integer coordinates.
The color item you see on-screen as a clickable box.
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.
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.
Button
helper to stop accidents on int vs gtkmm3's weak=typed enums, & looks nicer!
void remove_all_children(Widget &widget)
For each child in get_children(widget), call widget.remove(*child). May not cause delete child!
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().
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
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.
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.
@ HORIZONTAL
The x-dimension (0).
@ VERTICAL
The y-dimension (1).
Glib::RefPtr< Gtk::Adjustment > smooth