diff --git a/share/icons/Dash/symbolic/actions/object-recolor-art-symbolic.svg b/share/icons/Dash/symbolic/actions/object-recolor-art-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..b078cf421c5c1beec4852445a720247bf9c2827c --- /dev/null +++ b/share/icons/Dash/symbolic/actions/object-recolor-art-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/share/icons/Dash/symbolic/actions/reset-colors-symbolic.svg b/share/icons/Dash/symbolic/actions/reset-colors-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..7d47572893ed89f875798b4b0e353615b756fc70 --- /dev/null +++ b/share/icons/Dash/symbolic/actions/reset-colors-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/share/icons/Dash/symbolic/actions/reset-symbolic.svg b/share/icons/Dash/symbolic/actions/reset-symbolic.svg deleted file mode 100644 index 09c40a1e3021e4ad13a7d5a0086ef0a972d948e6..0000000000000000000000000000000000000000 --- a/share/icons/Dash/symbolic/actions/reset-symbolic.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/share/icons/hicolor/symbolic/actions/reset-colors-symbolic.svg b/share/icons/hicolor/symbolic/actions/reset-colors-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..7d47572893ed89f875798b4b0e353615b756fc70 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/reset-colors-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/share/icons/hicolor/symbolic/actions/reset-symbolic.svg b/share/icons/hicolor/symbolic/actions/reset-symbolic.svg deleted file mode 100644 index 7dcef2b3f70fffc0d4e14bd1fdd4381979bf9177..0000000000000000000000000000000000000000 --- a/share/icons/hicolor/symbolic/actions/reset-symbolic.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - diff --git a/share/icons/multicolor/symbolic/actions/object-recolor-art-symbolic.svg b/share/icons/multicolor/symbolic/actions/object-recolor-art-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..b078cf421c5c1beec4852445a720247bf9c2827c --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/object-recolor-art-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/share/icons/multicolor/symbolic/actions/reset-colors-symbolic.svg b/share/icons/multicolor/symbolic/actions/reset-colors-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..7d47572893ed89f875798b4b0e353615b756fc70 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/reset-colors-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/share/icons/multicolor/symbolic/actions/reset-symbolic.svg b/share/icons/multicolor/symbolic/actions/reset-symbolic.svg deleted file mode 100644 index 7dcef2b3f70fffc0d4e14bd1fdd4381979bf9177..0000000000000000000000000000000000000000 --- a/share/icons/multicolor/symbolic/actions/reset-symbolic.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - diff --git a/share/ui/recolor-art.glade b/share/ui/recolor-art.glade new file mode 100644 index 0000000000000000000000000000000000000000..540e02da6573116884d1869630e83b2764fc7074 --- /dev/null +++ b/share/ui/recolor-art.glade @@ -0,0 +1,182 @@ + + + + + recolor-art + vertical + true + true + + + list-wheel-box + top + false + 200 + 400 + + + original-recolor-colors-list-box + vertical + true + true + + + original-reset-recolor + horizontal + center + center + 85 + + + Original + fill + + + + + reset + true + false + false + center + 18 + + + reset-settings + normal + + + + + + + New + center + + + + + + + true + true + true + centre + never + false + + + colors-list + vertical + true + fill + fill + + + false + fill + true + fill + + + + + + + + + + + horizontal + 6 + center + true + + + hamburger-menu + normal + + + + + Color List + center + center + + + + + + + + color-wheel-page + vertical + true + true + + + + Color Wheel Content Goes Here + + + + + + + + horizontal + 6 + center + true + + + color-wheel-symbolic + normal + + + + + Color Wheel + center + center + + + + + + + + + + + vertical + true + + + + + + liveP-apply + horizontal + 300 + + + liveP + Live Preview + end + start + + + + + + + + \ No newline at end of file diff --git a/share/ui/style.css b/share/ui/style.css index 5a9f9d2bf1d470bd221d55214edf8cb1777ab482..e76316ded3b0fcccb257362182368eb9a2954c6d 100644 --- a/share/ui/style.css +++ b/share/ui/style.css @@ -1572,3 +1572,28 @@ listview row.top-separator { #DialogMultipaned:drop(active), #DialogMultipaned:drop(active):focus { box-shadow: none; } + +/* +********************** +* Recolor Art class * +********************** +*/ + + +#recolor-art #original, +#recolored { + min-height: 6px; + border-radius: 4px; + border: 2px solid transparent; +} + +#original-recolor-box button#original:hover, +button#recolored:hover { + border-color: @theme_fq_color; +} + +#recolor-art listview row:selected { + background-color: transparent; + border: 2px solid @theme_selected_bg_color; + border-radius: 4px; +} diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index df176e03431ccbf30a5b47adb1941b7a8799563c..5d4f2108ceb642401946898a7384cb437703d60c 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -285,10 +285,10 @@ set(ui_SRC widget/unit-tracker.cpp widget/widget-vfuncs-class-init.cpp widget/xml-treeview.cpp + widget/recolor-art.cpp view/svg-view-widget.cpp - # ------- # Headers builder-utils.h @@ -597,7 +597,8 @@ set(ui_SRC widget/unit-tracker.h widget/widget-vfuncs-class-init.h widget/xml-treeview.h - + widget/recolor-art.h + view/svg-view-widget.h ) diff --git a/src/ui/dialog/fill-and-stroke.cpp b/src/ui/dialog/fill-and-stroke.cpp index a3736bae318004ddef8d59b2d620843806c00a30..55d9317b5fb844bfe7a0601cfc46bacd76270301 100644 --- a/src/ui/dialog/fill-and-stroke.cpp +++ b/src/ui/dialog/fill-and-stroke.cpp @@ -23,15 +23,17 @@ #include #include -#include "desktop.h" #include "desktop-style.h" +#include "desktop.h" #include "preferences.h" +#include "selection.h" #include "ui/icon-loader.h" #include "ui/icon-names.h" #include "ui/pack.h" #include "ui/widget/fill-style.h" -#include "ui/widget/stroke-style.h" #include "ui/widget/notebook-page.h" +#include "ui/widget/recolor-art.h" +#include "ui/widget/stroke-style.h" namespace Inkscape::UI::Dialog { @@ -40,12 +42,9 @@ FillAndStroke::FillAndStroke() , _page_fill(Gtk::make_managed(1, 1)) , _page_stroke_paint(Gtk::make_managed(1, 1)) , _page_stroke_style(Gtk::make_managed(1, 1)) - , _composite_settings(INKSCAPE_ICON("dialog-fill-and-stroke"), - "fillstroke", - UI::Widget::SimpleFilterModifier::ISOLATION | - UI::Widget::SimpleFilterModifier::BLEND | - UI::Widget::SimpleFilterModifier::BLUR | - UI::Widget::SimpleFilterModifier::OPACITY) + , _composite_settings(INKSCAPE_ICON("dialog-fill-and-stroke"), "fillstroke", + UI::Widget::SimpleFilterModifier::ISOLATION | UI::Widget::SimpleFilterModifier::BLEND | + UI::Widget::SimpleFilterModifier::BLUR | UI::Widget::SimpleFilterModifier::OPACITY) , fillWdgt(nullptr) , strokeWdgt(nullptr) { @@ -54,7 +53,8 @@ FillAndStroke::FillAndStroke() _notebook.append_page(*_page_fill, _createPageTabLabel(_("_Fill"), INKSCAPE_ICON("object-fill"))); _notebook.append_page(*_page_stroke_paint, _createPageTabLabel(_("Stroke _paint"), INKSCAPE_ICON("object-stroke"))); - _notebook.append_page(*_page_stroke_style, _createPageTabLabel(_("Stroke st_yle"), INKSCAPE_ICON("object-stroke-style"))); + _notebook.append_page(*_page_stroke_style, + _createPageTabLabel(_("Stroke st_yle"), INKSCAPE_ICON("object-stroke-style"))); _notebook.set_vexpand(true); _switch_page_conn = _notebook.signal_switch_page().connect(sigc::mem_fun(*this, &FillAndStroke::_onSwitchPage)); @@ -80,6 +80,7 @@ FillAndStroke::~FillAndStroke() void FillAndStroke::selectionChanged(Selection *selection) { + if (!page_changed) { changed_fill = true; changed_stroke = true; @@ -94,6 +95,7 @@ void FillAndStroke::selectionChanged(Selection *selection) if (strokeStyleWdgt && npage == 2) { strokeStyleWdgt->selectionChangedCB(); } + } void FillAndStroke::selectionModified(Selection *selection, guint flags) @@ -110,6 +112,7 @@ void FillAndStroke::selectionModified(Selection *selection, guint flags) if (strokeStyleWdgt && npage == 2) { strokeStyleWdgt->selectionModifiedCB(flags); } + } void FillAndStroke::desktopReplaced() @@ -129,7 +132,7 @@ void FillAndStroke::desktopReplaced() _subject.setDesktop(getDesktop()); } -void FillAndStroke::_onSwitchPage(Gtk::Widget * page, guint pagenum) +void FillAndStroke::_onSwitchPage(Gtk::Widget *page, guint pagenum) { npage = pagenum; if (page->is_visible()) { @@ -153,30 +156,26 @@ void FillAndStroke::_onSwitchPage(Gtk::Widget * page, guint pagenum) _savePagePref(pagenum); } -void -FillAndStroke::_savePagePref(guint page_num) +void FillAndStroke::_savePagePref(guint page_num) { // remember the current page Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt("/dialogs/fillstroke/page", page_num); } -void -FillAndStroke::_layoutPageFill() +void FillAndStroke::_layoutPageFill() { fillWdgt = Gtk::make_managed(FILL); _page_fill->table().attach(*fillWdgt, 0, 0, 1, 1); } -void -FillAndStroke::_layoutPageStrokePaint() +void FillAndStroke::_layoutPageStrokePaint() { strokeWdgt = Gtk::make_managed(STROKE); _page_stroke_paint->table().attach(*strokeWdgt, 0, 0, 1, 1); } -void -FillAndStroke::_layoutPageStrokeStyle() +void FillAndStroke::_layoutPageStrokeStyle() { strokeStyleWdgt = Gtk::make_managed(); strokeStyleWdgt->set_hexpand(); @@ -184,34 +183,30 @@ FillAndStroke::_layoutPageStrokeStyle() _page_stroke_style->table().attach(*strokeStyleWdgt, 0, 0, 1, 1); } -void -FillAndStroke::showPageFill() +void FillAndStroke::showPageFill() { blink(); _notebook.set_current_page(0); _savePagePref(0); - } -void -FillAndStroke::showPageStrokePaint() +void FillAndStroke::showPageStrokePaint() { blink(); _notebook.set_current_page(1); _savePagePref(1); } -void -FillAndStroke::showPageStrokeStyle() +void FillAndStroke::showPageStrokeStyle() { blink(); _notebook.set_current_page(2); _savePagePref(2); - } -Gtk::Box& -FillAndStroke::_createPageTabLabel(const Glib::ustring& label, const char *label_image) + + +Gtk::Box &FillAndStroke::_createPageTabLabel(Glib::ustring const &label, char const *label_image) { auto const _tab_label_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 4); diff --git a/src/ui/dialog/fill-and-stroke.h b/src/ui/dialog/fill-and-stroke.h index 0a918df2f0af4d1b9ec2a65a397ca69653e66a77..d8ac27dfe5b13627e880dfc1820a06745c67d7dc 100644 --- a/src/ui/dialog/fill-and-stroke.h +++ b/src/ui/dialog/fill-and-stroke.h @@ -24,6 +24,7 @@ namespace Gtk { class Box; +class Popover; } // namespace Gtk namespace Inkscape::UI { @@ -32,6 +33,7 @@ namespace Widget { class FillNStroke; class NotebookPage; class StrokeStyle; +class RecolorArt; } // namespace Widget namespace Dialog { @@ -47,6 +49,7 @@ public: void showPageFill(); void showPageStrokePaint(); void showPageStrokeStyle(); + void showPageRecolorArt(); protected: Gtk::Notebook _notebook; @@ -78,7 +81,6 @@ private: UI::Widget::FillNStroke *fillWdgt = nullptr; UI::Widget::FillNStroke *strokeWdgt = nullptr; UI::Widget::StrokeStyle *strokeStyleWdgt = nullptr; - sigc::scoped_connection _switch_page_conn; }; diff --git a/src/ui/widget/color-notebook.cpp b/src/ui/widget/color-notebook.cpp index 88f8d099c77e32568b4c6ef5f8fce566fc6462f5..fef1120bd32095a32e388c8ce285bd0595480f54 100644 --- a/src/ui/widget/color-notebook.cpp +++ b/src/ui/widget/color-notebook.cpp @@ -128,7 +128,7 @@ void ColorNotebook::_initUI() _book->set_margin_top(YPAD); _book->set_margin_bottom(YPAD); _book->set_hexpand(); - _book->set_vexpand(); + _book->set_vexpand(false); attach(*_book, 0, row, 2, 1); // restore the last active page @@ -195,7 +195,7 @@ void ColorNotebook::_initUI() gtk_widget_set_margin_start(rgbabox, XPAD); gtk_widget_set_margin_end(rgbabox, XPAD); - gtk_widget_set_margin_top(rgbabox, YPAD); + gtk_widget_set_margin_top(rgbabox, 8); gtk_widget_set_margin_bottom(rgbabox, YPAD); attach(*Glib::wrap(rgbabox), 0, row, 2, 1); diff --git a/src/ui/widget/paint-selector.cpp b/src/ui/widget/paint-selector.cpp index 5ad356bd70b23474f5b7816466c7b594282bde39..99f6a4239a11c1333ce860c31bee7e63a3bd956b 100644 --- a/src/ui/widget/paint-selector.cpp +++ b/src/ui/widget/paint-selector.cpp @@ -34,6 +34,7 @@ #include #include "desktop-style.h" +#include "desktop.h" #include "document.h" #include "style.h" #include "inkscape.h" @@ -49,6 +50,8 @@ #include "object/sp-stop.h" #include "path-prefix.h" #include "pattern-manipulation.h" +#include "selection.h" +#include "style.h" #include "svg/css-ostringstream.h" #include "ui/icon-loader.h" #include "ui/icon-names.h" @@ -58,6 +61,7 @@ #include "ui/widget/gradient-selector.h" #include "ui/widget/pattern-editor.h" #include "ui/widget/swatch-selector.h" +#include "ui/widget/recolor-art.h" #include "widgets/widget-sizes.h" #include "xml/repr.h" @@ -135,7 +139,14 @@ GradientSelectorInterface *PaintSelector::getGradientFromData() const PaintSelector::PaintSelector(FillOrStroke kind, std::shared_ptr colors) : _selected_colors(std::move(colors)) + , _recolorArtWdgt_1(Gtk::make_managed()) + , _recolorArtWdgt_2(Gtk::make_managed()) + , _recolorButtonTriger_1(Gtk::make_managed()) + , _recolorButtonTriger_2(Gtk::make_managed()) + , _recolorPopOver_1(Gtk::make_managed()) + , _recolorPopOver_2(Gtk::make_managed()) { + set_orientation(Gtk::Orientation::VERTICAL); _mode = static_cast(-1); // huh? do you mean 0xff? -- I think this means "not in the enum" @@ -209,6 +220,29 @@ PaintSelector::PaintSelector(FillOrStroke kind, std::shared_ptrset_visible(kind == FILL); + + _desktop = SP_ACTIVE_DESKTOP; + if (_desktop) { + auto selection = _desktop->getSelection(); + if (selection) { + selection->connectChanged(sigc::mem_fun(*this, &PaintSelector::onSelectionChanged)); + } + } + + setupRecolorButton(_recolorButtonTriger_1,_recolorPopOver_1 , _recolorArtWdgt_1); + setupRecolorButton(_recolorButtonTriger_2,_recolorPopOver_2,_recolorArtWdgt_2); + + _recolorButtonTriger_1->signal_clicked().connect([this]() { + _recolorPopOver_1->popup(); + _recolorArtWdgt_1->performUpdate(); + }); + + _recolorButtonTriger_2->signal_clicked().connect([this]() { + _recolorPopOver_2->popup(); + _recolorArtWdgt_2->performUpdate(); + }); + _frame->append(*_recolorButtonTriger_1); + _recolorButtonTriger_1->hide(); } StyleToggleButton *PaintSelector::style_button_add(gchar const *pixmap, PaintSelector::Mode mode, gchar const *tip) @@ -294,6 +328,11 @@ void PaintSelector::set_mode_ex(Mode mode, bool switch_style) { } _mode = mode; _signal_mode_changed.emit(_mode, switch_style); + if (_desktop = SP_ACTIVE_DESKTOP) { + if (auto sel = _desktop->getSelection()) { + onSelectionChanged(sel); + } +} _update = false; } } @@ -515,12 +554,16 @@ void PaintSelector::set_mode_color() auto const color_selector = Gtk::make_managed(_selected_colors); color_selector->set_visible(true); UI::pack_start(*_selector_solid_color, *color_selector, true, true); + UI::pack_start(*_selector_solid_color, *_recolorButtonTriger_2, false, false); + _recolorButtonTriger_2->hide(); + /* Pack everything to frame */ _frame->append(*_selector_solid_color); color_selector->set_label(_("Flat color")); } _selector_solid_color->set_visible(true); + _selector_solid_color->set_vexpand(false); } _label->set_markup(""); //_("Flat color")); @@ -921,8 +964,10 @@ void PaintSelector::pattern_destroy(GtkWidget *widget, PaintSelector * /*psel*/) g_object_unref(G_OBJECT(widget)); } -void PaintSelector::pattern_change(GtkWidget * /*widget*/, PaintSelector *psel) { psel->_signal_changed.emit(); } - +void PaintSelector::pattern_change(GtkWidget * /*widget*/, PaintSelector *psel) +{ + psel->_signal_changed.emit(); +} /*update pattern list*/ void PaintSelector::updatePatternList(SPPattern *pattern) @@ -1141,6 +1186,51 @@ PaintSelector::Mode PaintSelector::getModeForStyle(SPStyle const &style, FillOrS return mode; } +void PaintSelector::onSelectionChanged(Inkscape::Selection *selection) +{ + auto group = dynamic_cast(selection->single()); + if(selection->size()>1 || group) + { + if (_mode == MODE_MULTIPLE || _mode == MODE_UNSET) + { + _recolorButtonTriger_1->show(); + _recolorButtonTriger_2->hide(); + // std::cout<<"shoed 1 hide 2\n"; + } + else if (_mode == MODE_SOLID_COLOR) + { + _recolorButtonTriger_2->show(); + _recolorButtonTriger_1->hide(); + // std::cout<<"shoed 2 hide 1\n"; + } + else + { + _recolorButtonTriger_1->hide(); + _recolorButtonTriger_2->hide(); + // std::cout<<"hide 1,2 selection size : "<size()<<" mode : "<<_mode<hide(); + _recolorButtonTriger_2->hide(); + // std::cout<<"hide 1,2\n"; + } +} + +void PaintSelector::setupRecolorButton(Gtk::Button *recolorButtonTriger, Gtk::Popover *recolorPopOver , UI::Widget::RecolorArt *recolorArtWdgt) +{ + recolorButtonTriger->set_label("Recolor Selection"); + recolorButtonTriger->set_hexpand(false); + recolorButtonTriger->set_vexpand(false); + recolorButtonTriger->set_size_request(180); + recolorButtonTriger->set_halign(Gtk::Align::CENTER); + recolorButtonTriger->set_valign(Gtk::Align::START); + recolorPopOver->set_parent(*recolorButtonTriger); + recolorPopOver->set_child(*recolorArtWdgt); + recolorPopOver->set_position(Gtk::PositionType::LEFT); + recolorButtonTriger->set_margin_top(8); +} } // namespace Widget } // namespace UI diff --git a/src/ui/widget/paint-selector.h b/src/ui/widget/paint-selector.h index f76eda68f6cfa78a5620674edaf59896fcf29e63..3b2978c6e77737b013791d574d38dcf6da84b04c 100644 --- a/src/ui/widget/paint-selector.h +++ b/src/ui/widget/paint-selector.h @@ -28,6 +28,7 @@ #include "object/sp-gradient-units.h" #include "ui/widget/gradient-selector.h" #include "ui/widget/swatch-selector.h" +#include "selection.h" class SPGradient; class SPLinearGradient; @@ -36,12 +37,15 @@ class SPRadialGradient; class SPMeshGradient; #endif class SPDesktop; +class Selection; class SPPattern; class SPStyle; namespace Gtk { class Label; class ToggleButton; +class Button; +class Popover; } // namespace Gtk namespace Inkscape::UI::Widget { @@ -50,6 +54,7 @@ class FillRuleRadioButton; class GradientEditor; class PatternEditor; class StyleToggleButton; +class RecolorArt; /** * Generic paint selector widget. @@ -102,6 +107,16 @@ class PaintSelector : public Gtk::Box { Gtk::Box *_selector_mesh = nullptr; SwatchSelector *_selector_swatch = nullptr; PatternEditor* _selector_pattern = nullptr; + + UI::Widget::RecolorArt *_recolorArtWdgt_1 = nullptr; + UI::Widget::RecolorArt *_recolorArtWdgt_2 = nullptr; + Gtk::Button *_recolorButtonTriger_1 = nullptr; + Gtk::Button *_recolorButtonTriger_2 = nullptr; + Gtk::Popover* _recolorPopOver_1=nullptr; + Gtk::Popover* _recolorPopOver_2=nullptr; + SPDesktop*_desktop=nullptr; + void onSelectionChanged(Inkscape::Selection *selection); + void setupRecolorButton(Gtk::Button *recolorButtonTriger , Gtk::Popover*recolorPopOver , UI::Widget::RecolorArt *recolorArtWdgt); Gtk::Label *_label; GtkWidget *_patternmenu = nullptr; diff --git a/src/ui/widget/recolor-art.cpp b/src/ui/widget/recolor-art.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08ba482c9dd4d6a89c7738bfa440c55c7adbfbb5 --- /dev/null +++ b/src/ui/widget/recolor-art.cpp @@ -0,0 +1,566 @@ +#include "recolor-art.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #include +#include +#include + +#include "actions/actions-tools.h" +#include "canvas.h" +#include "color-notebook.h" +#include "desktop-style.h" +#include "desktop.h" +#include "document-undo.h" +#include "document.h" +#include "gradient-chemistry.h" +#include "object/sp-defs.h" +#include "object/sp-gradient.h" +#include "object/sp-linear-gradient.h" +#include "object/sp-marker.h" +#include "object/sp-mesh-gradient.h" +#include "object/sp-object.h" +#include "object/sp-pattern.h" +#include "object/sp-radial-gradient.h" +#include "object/sp-stop.h" +#include "object/sp-text.h" +#include "object/sp-use.h" +#include "pattern-manipulation.h" +#include "selection.h" +#include "style-internal.h" +#include "style.h" +#include "inkscape.h" +#include "ui/builder-utils.h" +#include "ui/dialog/dialog-base.h" +#include "ui/icon-names.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +RecolorArt::RecolorArt() + : Gtk::Box() + , _builder(create_builder("recolor-art.glade")) + , _solid_colors(std::make_shared()) +{ + set_name("RecolorArt"); + append(get_widget(_builder, "recolor-art")); + _solid_colors->set(Color(0x000000ff)); + + layoutColorPicker(); + _color_list = _builder->get_widget("colors-list"); + _live_preview = _builder->get_widget("liveP"); + _live_preview->signal_toggled().connect(sigc::mem_fun(*this, &RecolorArt::onLivePreviewToggled)); + _live_preview->set_active(true); + _reset = _builder->get_widget("reset"); + _reset->signal_clicked().connect(sigc::mem_fun(*this, &RecolorArt::onResetClicked)); + + _notebook = _builder->get_widget("list-wheel-box"); + _color_wheel_page = _builder->get_widget("color-wheel-page"); + _notebook->signal_switch_page().connect([this](Gtk::Widget *page, guint page_num) { + int wheel_index = _notebook->page_num(*_color_wheel_page); + + if (static_cast(page_num) == wheel_index) { + _color_picker_wdgt->set_visible(false); + // std::cout << "Color Wheel tab is active.\n"; + } else { + _color_picker_wdgt->set_visible(true); + // std::cout << "Color Wheel tab is NOT active.\n"; + } + }); + + _list_view = _builder->get_widget("recolor-art-list"); + _color_model = Gio::ListStore::create(); + _selection_model = Gtk::SingleSelection::create(_color_model); + _color_factory = Gtk::SignalListItemFactory::create(); + + _color_factory->signal_setup().connect([](Glib::RefPtr const &list_item) { + auto box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); + auto original = Gtk::make_managed(); + auto arrow = Gtk::make_managed(); + auto recolored = Gtk::make_managed(); + + original->set_hexpand(true); + + recolored->set_hexpand(true); + + arrow->set_use_markup(true); + arrow->set_width_chars(6); + arrow->set_markup(""); + arrow->set_halign(Gtk::Align::CENTER); + arrow->set_valign(Gtk::Align::CENTER); + arrow->set_margin_top(3); + + box->set_name("original-recolor-box"); + box->append(*original); + box->append(*arrow); + box->append(*recolored); + list_item->set_child(*box); + }); + + _color_factory->signal_bind().connect([this](Glib::RefPtr const &list_item) { + auto item = std::dynamic_pointer_cast(list_item->get_item()); + if (!item) + return; + + auto box = dynamic_cast(list_item->get_child()); + if (!box || box->get_first_child() == nullptr || box->get_last_child() == nullptr) + return; + + auto original = dynamic_cast(box->get_first_child()); + auto recolored = dynamic_cast(box->get_last_child()); + + if (original && recolored) { + colorButtons(original, item->old_color); + colorButtons(recolored, item->new_color); + + original->set_name("original"); + recolored->set_name("recolored"); + original->signal_clicked().connect([this, item, index = list_item->get_position()] { + _selection_model->set_selected(index); + onOriginalColorClicked(item->key); + }); + + recolored->signal_clicked().connect([this, item, index = list_item->get_position()] { + _selection_model->set_selected(index); + onOriginalColorClicked(item->key); + }); + buttons[item->key] = {original, recolored}; + } + }); + + _list_view->set_model(_selection_model); + _list_view->set_factory(_color_factory); + auto lm = _list_view->get_layout_manager(); + if (auto grid_layout = std::dynamic_pointer_cast(lm)) { + grid_layout->set_row_spacing(0); + } + _list_view->set_hexpand(false); + _list_view->set_vexpand(false); + + _selection_model->signal_selection_changed().connect([this](guint pos, guint n_items) { + int index = _selection_model->get_selected(); + if (index < 0) + return; + + auto item = _color_model->get_item(index); + auto color_item = std::dynamic_pointer_cast(item); + if (!color_item) + return; + + onOriginalColorClicked(color_item->key); + }); + + // _apply = _builder->get_widget("apply"); + // _apply->signal_clicked().connect(sigc::mem_fun(*this, &RecolorArt::_onApplyButtonClicked)); +} +RecolorArt::~RecolorArt() {} + +void RecolorArt::setDesktop(SPDesktop *desktop) +{ + if (_desktop != desktop) { + _desktop = desktop; + } + g_message("setDesktop\n"); +} +void RecolorArt::collectColors(std::vector items) +{ + _selected_colors.clear(); + for (auto item : items) { + extractItemColors(item); + } +} +void RecolorArt::extractItemColors(SPObject *item) +{ + if (auto group = dynamic_cast(item)) { + for (SPObject &child : group->children) { + extractItemColors(&child); + } + } else if (item) { + extractItemStyle(item); + } +} +void RecolorArt::extractGradientStops(SPObject *item, std::string kind, bool isFill) +{ + SPPaintServer *paint_server = isFill ? item->style->getFillPaintServer() : item->style->getStrokePaintServer(); + if (paint_server && dynamic_cast(paint_server)) { + SPGradient *gradient = dynamic_cast(paint_server); + SPGradient *vectorGradient = gradient->getVector(); + if (vectorGradient) { + if (vectorGradient->hasPatches()) { + vectorGradient->ensureArray(); + std::unique_ptr nodeArray; + if (auto mesh = dynamic_cast(gradient)) { + nodeArray = std::make_unique(mesh); + extractMeshStops(nodeArray->nodes, item, kind); + } + + } else { + vectorGradient->ensureVector(); + // int stopCount = vectorGradient->getStopCount(); + // std::cout << "Stops count : " << stopCount << std::endl; + // std::cout << "has patches : " << vectorGradient->hasPatches() << std::endl; + populateStopsMap(vectorGradient->getFirstStop()); + } + } + for (auto stop : vectorGradient->getGradientVector().stops) { + if (stop.color.has_value()) { + populateMap(stop.color.value(), item, kind); + } + } + } +} +void RecolorArt::extractMeshStops(std::vector> mesh_nodes, SPObject *item, std::string kind) +{ + for (auto nodes : mesh_nodes) { + for (auto node : nodes) { + populateStopsMap(node->stop); + if (node->color.has_value()) { + populateMap(node->color.value(), item, kind); + } + } + } +} +void RecolorArt::populateStopsMap(SPStop *stop) +{ + g_message("populateStopsMap in"); + while (stop) { + std::string color = stop->getColor().toString(); + _gradient_stops[color].push_back(stop); + stop = stop->getNextStop(); + } +} +void RecolorArt::populateMap(Color color, SPObject *item, std::string kind) +{ + color.addOpacity(); + ColorRef ref = ColorRef(item, kind); + ColorPair pair = ColorPair(color, color); + if (_selected_colors.count(color.toString())) { + _selected_colors[color.toString(true)].first.push_back(ref); + } else { + _selected_colors.emplace(color.toString(), std::make_pair(std::vector{ref}, pair)); + } +} +void RecolorArt::extractItemStyle(SPObject *item) +{ + // check item style + if (!item || !item->style) + return; + SPStyle *style = item->style; + extractMarkerColors(style->marker_start.get_value(), item); + extractMarkerColors(style->marker_mid.get_value(), item); + extractMarkerColors(style->marker_end.get_value(), item); + + // get flat fills + if (style->fill.isColor()) { + auto color = style->fill.getColor(); + populateMap(color, item, "fill"); + } + // get gradient stops strokes + else if (style->fill.isPaintserver()) { + auto ps = style->getFillPaintServer(); + if (auto pattern = dynamic_cast(ps)) { + extractPatternColors(pattern); + std::cout << "pattern detected\n"; + } + extractGradientStops(item, "stop", true); + } + + if (style->stroke.isColor()) { + auto color = style->stroke.getColor(); + populateMap(color, item, "stroke"); + } + // get gradient stops strokes + else if (style->stroke.isPaintserver()) { + extractGradientStops(item, "stop", false); + } +} +void RecolorArt::extractPatternColors(SPPattern *pattern) +{ + SPPattern *root = pattern->rootPattern(); + for (auto &child : root->children) { + extractItemStyle(&child); + } +} +void RecolorArt::extractMarkerColors(Glib::ustring marker, SPObject *item) +{ + // std::cout << "markerout : " << marker << std::endl; + if (marker.size() > 0 && item->document) { + // std::cout << "marker in : " << marker << std::endl; + std::string marker_id = std::string(marker.c_str() + 4, std::strlen(marker.c_str()) - 5); + // std::cout<<"marker id : "<document->getObjectByHref(marker_id); + if(!m) std::cout<<"m is null\n"; + if (auto marker_obj = dynamic_cast(m)) { + for (auto &child : marker_obj->item_list()) { + extractItemColors(&*child); + } + } + } else + return; +} +void RecolorArt::generateVisualList() +{ + _color_model->remove_all(); + buttons.clear(); + for (auto &[key, value] : _selected_colors) { + auto old_color = value.second.value().old_color.toString(); + auto new_color = value.second.value().new_color.toString(); + _color_model->append(ColorItem::create(key, old_color, new_color)); + } + _color_list->append(*_list_view); +} +void RecolorArt::layoutColorPicker(std::shared_ptr updated_color) +{ + if (updated_color) + _solid_colors = updated_color; + _color_picker_wdgt = Gtk::make_managed(_solid_colors); + _color_picker_wdgt->set_visible(true); + _color_picker_wdgt->set_label(_("Selected Color")); + _solid_colors->signal_changed.connect(sigc::mem_fun(*this, &RecolorArt::onColorPickerChanged)); + + auto container = _builder->get_widget("color-picker"); + if (container) { + for (auto child : container->get_children()) + container->remove(*child); + container->append(*_color_picker_wdgt); + } else { + g_warning("color picker not found"); + } +} +void RecolorArt::colorButtons(Gtk::Button *button, std::string color) +{ + if (color.empty() || color.size() < 7 || color[0] != '#') { + g_warning("Invalid color string: %s", color.c_str()); + return; + } + + if (button) { + auto css_provider = Gtk::CssProvider::create(); + Glib::ustring css = "button { background-color: " + Glib::ustring(color) + "; background-image: none;}"; + css_provider->load_from_data(css); + auto style_context = button->get_style_context(); + style_context->add_provider(css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER); + } else { + g_message("button not found"); + } +} +// void RecolorArt::colorBorderOnClick(Gtk::Button *button, bool kind) +// { +// auto &_last_button = kind == 1 ? _last_original_button : _last_recolored_button; +// if (_last_button && _last_button != button) { +// auto style_context = _last_button->get_style_context(); +// style_context->remove_class("selected-button"); +// } +// if (button) { +// auto style_context = button->get_style_context(); +// style_context->add_class("selected-button"); +// _last_button = button; +// } else { +// g_warning("button not found for border coloring"); +// } +// } +void RecolorArt::onOriginalColorClicked(std::string color_id) +{ + _current_color_id = color_id; + auto it = _selected_colors.find(color_id); + auto btns = buttons.find(color_id); + if (it != _selected_colors.end() && it->second.second.has_value() && btns != buttons.end()) { + Color color = it->second.second.value().new_color; + std::shared_ptr updated_color = std::make_shared(); + updated_color->set(color); + layoutColorPicker(updated_color); + // colorBorderOnClick(btns->second.first, 1); + // colorBorderOnClick(btns->second.second, 0); + // g_message("original color selected at color %s", color_id.c_str()); + } +} +void RecolorArt::onColorPickerChanged() +{ + auto it = buttons.find(_current_color_id); + if (it == buttons.end()) { + g_message("couldn't find the color id \" %s \" ", _current_color_id.c_str()); + return; + } + Gtk::Button *_current_recolor_button = it->second.second; + std::optional new_color = _solid_colors->get(); + if (!new_color.has_value()) { + g_message("there is no color"); + return; + } + std::string _color_string = new_color.value().toString(); + colorButtons(_current_recolor_button, _color_string); + auto _selected = _selected_colors.find(_current_color_id); + if (_selected != _selected_colors.end()) + _selected->second.second.value().new_color = new_color.value(); + if (_live_preview && _live_preview->property_active()) { + lpChecked(); + } + guint index = _selection_model->get_selected(); + auto item = _color_model->get_item(index); + auto color_item = std::dynamic_pointer_cast(item); + color_item->new_color = _color_string; + g_message("color picker changed"); +} +void RecolorArt::lpChecked() +{ + auto _selected = _selected_colors.find(_current_color_id); + std::optional new_color = _solid_colors->get(); + if (!new_color.has_value()) { + g_message("there is no color"); + return; + } + std::string _color_string = new_color.value().toString(); + if (_selected == _selected_colors.end()) { + g_message("no items found"); + return; + } + _selected->second.second.value().new_color = new_color.value(); + for (auto &item : _selected->second.first) { + if (item.kind == "stop") + continue; + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property_string(css, item.kind.c_str(), _color_string); + sp_desktop_apply_css_recursive(item.item, css, true); + sp_repr_css_attr_unref(css); + } + recolorStops(_selected->first, new_color.value()); + DocumentUndo::done(_desktop->getDocument(), _("Recolored items"), INKSCAPE_ICON("object-recolor-art")); +} +void RecolorArt::onResetClicked() +{ + for (auto [key, btns] : buttons) { + colorButtons(btns.second, key); + } + + for (auto i = 0; i < _color_model->get_n_items(); i++) { + auto item = _color_model->get_item(i); + auto color_item = std::dynamic_pointer_cast(item); + color_item->new_color = color_item->key; + } + revertToOriginalColors(); + _selection_model->set_selected(0); + onOriginalColorClicked(buttons.begin()->first); +} +void RecolorArt::revertToOriginalColors() +{ + for (auto [key, items] : _selected_colors) { + for (auto &item : items.first) { + if (item.kind == "stop") + continue; + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property_string(css, item.kind.c_str(), key); + sp_desktop_apply_css_recursive(item.item, css, true); + sp_repr_css_attr_unref(css); + } + recolorStops(key, items.second.value().old_color); + } +} +void RecolorArt::convertToRecoloredColors() +{ + for (auto [key, items] : _selected_colors) { + if (items.second.has_value()) { + std::string new_color = items.second.value().new_color.toString(); + for (auto &item : items.first) { + if (item.kind == "stop") + continue; + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property_string(css, item.kind.c_str(), new_color); + sp_desktop_apply_css_recursive(item.item, css, true); + sp_repr_css_attr_unref(css); + } + recolorStops(key, items.second.value().new_color); + } + } +} +void RecolorArt::recolorStops(std::string old_color, Color new_color) +{ + auto stops_vector = _gradient_stops.find(old_color); + if (stops_vector != _gradient_stops.end()) { + for (auto stop : stops_vector->second) { + stop->setColor(new_color); + } + } +} +void RecolorArt::onLivePreviewToggled() +{ + _is_preview = _live_preview->property_active(); + if (_is_preview) { + convertToRecoloredColors(); + } else { + revertToOriginalColors(); + } + g_message(_is_preview ? "is true" : "is false"); + g_message("LP toggled"); +} +bool RecolorArt::selectionSize() +{ + if (_desktop && _desktop->getSelection()->size() > 1) { + return true; + } + return false; +} +void RecolorArt::performUpdate() +{ + for (auto child : _color_list->get_children()) { + _color_list->remove(*child); + } + buttons.clear(); + _gradient_stops.clear(); + _last_original_button = nullptr; + _last_recolored_button = nullptr; + _desktop = SP_ACTIVE_DESKTOP; + // _current_color_id = ""; + if (auto selection = _desktop->getSelection()) { + std::vector vec(selection->items().begin(), selection->items().end()); + collectColors(vec); + if (!_selected_colors.empty()) { + generateVisualList(); + auto first_button_id = buttons.begin()->first; + onOriginalColorClicked(first_button_id); + } + g_message("Performing Update\n"); + } else + g_message("Desktop is NULL in Performupdate in recolor widegt\n"); +} +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +// void RecolorArt::_onApplyButtonClicked() +// { +// for (auto [key, items] : _selected_colors) { +// std::string _color_string = items.second.value().new_color.toString(); +// for (auto &item : items.first) { +// SPCSSAttr *css = sp_repr_css_attr_new(); +// sp_repr_css_set_property_string(css, item.kind.c_str(), _color_string); +// sp_desktop_apply_css_recursive(item.item, css, true); +// sp_repr_css_attr_unref(css); +// } +// } +// g_message("apply clicked"); +// } +// if (!_gradient_stops.empty()) { +// for (auto [key, value] : _gradient_stops) { +// std::cout << key << " : "; +// for (auto stop : value) +// std::cout << stop << " "; +// std::cout << std::endl; +// } +// g_message("filled stops map"); + +// } else { +// g_message("empty stops map"); +// } \ No newline at end of file diff --git a/src/ui/widget/recolor-art.h b/src/ui/widget/recolor-art.h new file mode 100644 index 0000000000000000000000000000000000000000..c04a3cb5b2f94c3a3bbd0859d813c95d1d5a6619 --- /dev/null +++ b/src/ui/widget/recolor-art.h @@ -0,0 +1,147 @@ + + +#ifndef SEEN_DIALOGS_SP_RECOLOR_ART_H +#define SEEN_DIALOGS_SP_RECOLOR_ART_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "canvas.h" +#include "style-internal.h" +#include "style.h" +#include "ui/widget/paint-selector.h" + +using namespace Inkscape::Colors; + +namespace Inkscape::Colors { +class Color; +class ColorSet; +namespace Space { +class AnySpace; +} +} // namespace Inkscape::Colors + +namespace Gtk { +class Widget; +class Builder; +class ListStore; +class Notebook; +} // namespace Gtk + +class SPDesktop; + +namespace Inkscape { +namespace UI { +namespace Tools { +class ToolBase; +} + +namespace Widget { +class ColorNotebook; + +struct ColorRef +{ + SPObject *item; + std::string kind; +}; + +struct ColorPair +{ + Color old_color; + Color new_color; +}; + +struct ColorItem : public Glib::Object +{ + Glib::ustring key; + Glib::ustring old_color; + Glib::ustring new_color; + + static Glib::RefPtr create(Glib::ustring const &k, Glib::ustring const &old_c, + Glib::ustring const &new_c) + { + auto item = Glib::make_refptr_for_instance(new ColorItem()); + item->key = k; + item->old_color = old_c; + item->new_color = new_c; + return item; + } +}; +class RecolorArt : public Gtk::Box +{ +private: + SPDesktop *_desktop = nullptr; + std::shared_ptr _solid_colors; + std::map, std::optional>> _selected_colors; + Inkscape::UI::Widget::ColorNotebook *_color_picker_wdgt = nullptr; + Gtk::Box *_color_list = nullptr; + Gtk::ListView *_list_view = nullptr; + Gtk::Button *_reset = nullptr; + Gtk::CheckButton *_live_preview = nullptr; + std::string _current_color_id; + std::map> buttons; // color_id : {original , recolored} + bool _is_preview = false; + + Glib::RefPtr> _color_model; + Glib::RefPtr _color_factory; + Glib::RefPtr _selection_model; + + Gtk::Button *_last_original_button = nullptr; + Gtk::Button *_last_recolored_button = nullptr; + + std::map> _gradient_stops; + + Gtk::Notebook *_notebook = nullptr; + Gtk::Box *_color_wheel_page = nullptr; + + // Gtk::Button *_apply = nullptr; + + Glib::RefPtr _builder; + void populateMap(Color color, SPObject *style, std::string kind); + void collectColors(std::vector items); + void extractGradientStops(SPObject *item, std::string kind, bool isFill); + void extractMeshStops(std::vector> mesh_nodes, SPObject *item, std::string kind); + void extractItemColors(SPObject *item); + void extractItemStyle(SPObject *item); + void extractPatternColors(SPPattern*pattern); + void extractMarkerColors(Glib::ustring marker ,SPObject *item); + void generateVisualList(); + void layoutColorPicker(std::shared_ptr updated_color = nullptr); + void colorButtons(Gtk::Button *button, std::string color); + // void colorBorderOnClick(Gtk::Button *button, bool kind); + + // signals handelrs + void onOriginalColorClicked(std::string color_id); + void onResetClicked(); + void onColorPickerChanged(); + void onLivePreviewToggled(); + void lpChecked(); + void revertToOriginalColors(); + void convertToRecoloredColors(); + // void _onApplyButtonClicked(); + + void populateStopsMap(SPStop *stop); + void recolorStops(std::string old_color, Color new_color); + +public: + RecolorArt(); + ~RecolorArt(); + void performUpdate(); + bool isInPreviewMode() { return _is_preview; } + void setDesktop(SPDesktop *desktop); + bool selectionSize(); +}; + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +#endif // SEEN_DIALOGS_SP_RECOLOR_ART_H