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>
43 boost::hash<int> hasher;
62 _selector_label(
get_widget<
Gtk::Label>(_builder,
"selector-label")),
63 _selector_menu{compact ? nullptr :
std::make_unique<UI::Widget::
PopoverMenu>(
Gtk::PositionType::BOTTOM)},
73 _palette = Gtk::make_managed<Inkscape::UI::Widget::ColorPalette>();
86 get_widget<Gtk::MenuButton>(
_builder,
"settings").set_popover(popover);
91 auto& search = get_widget<Gtk::SearchEntry2>(
_builder,
"search");
92 search.signal_search_changed().connect([
this, &search]{
93 if (search.get_text().length() == 0) {
96 filter_colors(search.get_text());
108 auto path = prefs->getString(
_prefs_path +
"/palette-path");
124 bool embedded = compact;
147 _list_btn.signal_toggled().connect([
this]{
150 _grid_btn.signal_toggled().connect([
this]{
170 get_widget<Gtk::Button>(
_builder,
"open").signal_clicked().connect([
this]{
258 conn_gradients = doc->connectResourcesChanged(
"gradient", [
this] {
267 if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
298 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
353 for (
int i = 0; i < grads.size(); i++) {
363 assert(
isswatch.size() == grads.size());
365 bool modified =
false;
367 for (
int i = 0; i < grads.size(); i++) {
383 auto current_color = [&,
this] (
bool fill) -> std::optional<ColorKey> {
394 auto attr = style.getFillOrStroke(
fill);
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()) {
407 }
else if (grad->ref) {
408 if (
auto ref = grad->ref->getObject();
ref &&
ref->isSwatch()) {
424 if (
auto fill = current_color(
true)) {
426 for (
auto it = range.first; it != range.second; ++it) {
431 if (
auto stroke = current_color(
false)) {
433 for (
auto it = range.first; it != range.second; ++it) {
447 for (
auto const &
c : p.
colors) {
464 std::vector<UI::Widget::palette_t> palettes;
470 palettes.push_back({_(
"Document swatches"),
auto_id, {}});
475 palettes.emplace_back(std::move(palette));
482 std::transform(palettes.begin(), palettes.end(), std::back_inserter(
_palettes),
483 [](
auto &&palette){ return PaletteLoaded{std::move(palette), false}; });
489void SwatchesPanel::rebuild()
491 std::vector<std::unique_ptr<ColorItem>> palette;
496 current_fill.clear();
497 current_stroke.clear();
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));
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());
510 for (
auto &
c : pal->colors) {
513 return std::make_unique<ColorItem>(
"");
516 return std::make_unique<ColorItem>(g.name);
519 auto w = std::make_unique<ColorItem>(
c, dialog);
520 w->set_pinned_pref(_prefs_path);
521 widgetmap.emplace(
c,
w.get());
525 palette.push_back(std::move(
w));
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());
535 w->signal_pinned().connect([
this]{
538 palette.push_back(std::move(
w));
544 update_fillstroke_indicators();
547 _palette->set_colors(std::move(palette));
548 _palette->set_selected(_current_palette_id);
551bool SwatchesPanel::load_swatches() {
552 auto window =
dynamic_cast<Gtk::Window *
>(get_root());
555 if (file && load_swatches(file->get_path())) {
557 prefs->setString(_prefs_path +
"/palette", _loaded_palette.id);
558 prefs->setString(_prefs_path +
"/palette-path", file->get_path());
564bool SwatchesPanel::load_swatches(std::string
const &path)
574 _loaded_palette = std::move(*res.palette);
576 }
else if (
auto desktop = getDesktop()) {
583void SwatchesPanel::update_loaded_palette_entry() {
585 if (_palettes.empty() || !_palettes.back().second) {
586 _palettes.emplace_back();
588 auto &[palette, loaded] = _palettes.back();
593void SwatchesPanel::setup_selector_menu()
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);
601bool SwatchesPanel::on_selector_key_pressed(
unsigned const keyval,
unsigned ,
602 Gdk::ModifierType
const state)
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;
611 int const old_index = std::distance(begin, it), back = _palettes.size() - 1;
612 int new_index = old_index;
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;
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);
632 static constexpr int max_chars = 35;
634 auto const label = Gtk::make_managed<Gtk::Label>(palette.
name,
true);
635 label->set_xalign(0.0);
638 auto const box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 1);
640 box->append(*Gtk::make_managed<UI::Widget::ColorPalettePreview>(palette.
colors));
642 auto const item = Gtk::make_managed<UI::Widget::PopoverMenuItem>();
643 item->set_child(*box);
648void SwatchesPanel::update_selector_menu()
650 g_assert(_selector_menu);
652 _selector.set_sensitive(
false);
653 _selector_label.set_label({});
654 _selector_menu->remove_all();
656 if (_palettes.empty())
return;
661 auto const size_group = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL);
664 item->signal_activate().connect([
id = palette.id,
this]{ set_palette(id); });
665 size_group->add_widget(*
label);
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);
677 _selector.set_sensitive(
true);
678 size_group->add_widget(_selector_label);
681void SwatchesPanel::update_selector_label(Glib::ustring
const &active_id)
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);
690void SwatchesPanel::clear_filter() {
691 if (_color_filter_text.empty())
return;
693 _color_filter_text.erase();
694 _palette->apply_filter();
697void SwatchesPanel::filter_colors(
const Glib::ustring& text) {
698 auto search = text.lowercase();
699 if (_color_filter_text == search)
return;
701 _color_filter_text = search;
702 _palette->apply_filter();
706 if (_color_filter_text.empty())
return true;
712 return text.find(_color_filter_text) != Glib::ustring::npos;
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
The color item you see on-screen as a clickable box.
const Glib::ustring & get_description() const
DialogBase is the base class for the dialog system.
SPDocument * getDocument() const
Glib::ustring const _prefs_path
SPDesktop * getDesktop() const
static GlobalPalettes const & get()
const std::vector< PaletteFileData > & palettes() const
void update_fillstroke_indicators()
void update_palettes(bool compact)
Process the list of available palettes and update the list in the _palette widget.
void select_palette(const Glib::ustring &id)
std::vector< ColorItem * > current_fill
PaletteFileData _loaded_palette
Glib::ustring _current_palette_id
bool filter_callback(const Dialog::ColorItem &color) const
std::vector< bool > isswatch
Gtk::Button & _delete_btn
std::vector< ColorItem * > current_stroke
void rebuild()
Rebuild the list of color items shown by the palette.
Glib::RefPtr< Gtk::Builder > _builder
void documentReplaced() final
Inkscape::PrefObserver _pinned_observer
void update_selector_menu()
std::unique_ptr< UI::Widget::PopoverMenu > _selector_menu
Inkscape::UI::Widget::ColorPalette * _palette
void update_loaded_palette_entry()
void set_palette(const Glib::ustring &id)
void selectionChanged(Selection *selection) final
const PaletteFileData * get_palette(const Glib::ustring &id)
Gtk::ToggleButton & _grid_btn
sigc::connection conn_gradients
SwatchesPanel(bool compact, char const *prefsPath="/dialogs/swatches")
void update_selector_label(Glib::ustring const &active_id)
sigc::connection conn_defs
void selectionModified(Selection *selection, guint flags) final
void desktopReplaced() final
Called when the desktop has certainly changed.
Gtk::ToggleButton & _list_btn
boost::unordered_multimap< ColorKey, ColorItem * > widgetmap
std::vector< PaletteLoaded > _palettes
void setup_selector_menu()
void showNotice(Glib::ustring const &msg, int timeout=0)
std::vector< SPObject * > const getResourceList(char const *key)
SPObject is an abstract base class of all of the document nodes at the SVG document level.
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.
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
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_MULTIPLE_AVERAGED
@ QUERY_STYLE_MULTIPLE_SAME
Editable view implementation.
A set of useful color modifying functions which do not fit as generic methods on the color class itse...
std::size_t hash_value(Color const &b)
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).
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)
static auto to_palette_t(PaletteFileData const &p)
static constexpr auto auto_id
void ellipsize(Gtk::Label &label, int const max_width_chars, Pango::EllipsizeMode const mode)
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)
static cairo_user_data_key_t key
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.
SPStyle - a style object for SPItem objects.
Glib::RefPtr< Gtk::Builder > builder