Inkscape
Vector Graphics Editor
selected-style.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Author:
4 * buliabyak@gmail.com
5 * Abhishek Sharma
6 * Jon A. Cruz <jon@joncruz.org>
7 *
8 * Copyright (C) 2005 author
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <algorithm>
14#include <cmath>
15#include <vector>
16#include <sigc++/connection.h>
17#include <sigc++/adaptors/bind.h>
18#include <sigc++/functors/mem_fun.h>
19#include <glibmm/ustring.h>
20#include <gtkmm/adjustment.h>
21#include <gtkmm/droptarget.h>
22#include <gtkmm/gestureclick.h>
23#include <gtkmm/checkbutton.h>
24
25#include "selected-style.h"
26
27#include "desktop-style.h"
28#include "document-undo.h"
29#include "gradient-chemistry.h"
30#include "message-context.h"
31#include "selection.h"
32#include "style.h"
33
34#include "object/sp-hatch.h"
37#include "object/sp-namedview.h"
38#include "object/sp-pattern.h"
41#include "svg/svg-color.h"
42#include "ui/controller.h"
43#include "ui/clipboard.h"
44#include "ui/cursor-utils.h"
48#include "ui/icon-names.h"
49#include "ui/tools/tool-base.h"
54#include "util/safe-printf.h"
55#include "util/units.cpp"
57#include "widgets/paintdef.h"
59
60static constexpr int SELECTED_STYLE_SB_WIDTH = 48;
61static constexpr int SELECTED_STYLE_PLACE_WIDTH = 50;
62static constexpr int SELECTED_STYLE_STROKE_WIDTH = 40;
63static constexpr int SELECTED_STYLE_FLAG_WIDTH = 12;
64static constexpr int SELECTED_STYLE_WIDTH = 250;
65
66static constexpr std::array<double, 15> _sw_presets{
67 32, 16, 10, 8, 6, 4, 3, 2, 1.5, 1, 0.75, 0.5, 0.25, 0.1};
68
69// In order of PaintType enum: fill, stroke; label, tooltip.
70static const Glib::ustring type_strings[][2][2] = {
71 {{ _("N/A"), _("Nothing selected")},
72 { _("N/A"), _("Nothing selected")}},
73 {{C_("Fill", "<i>None</i>"), _("No fill, middle-click for black fill")},
74 {C_("Stroke", "<i>None</i>"), _("No stroke, middle-click for black stroke")}},
75 {{ _("<b>Unset</b>"), _("Unset fill")},
76 { _("<b>Unset</b>"), _("Unset stroke")}},
77 {{ _("≠"), _("Different fills")},
78 { _("≠"), _("Different strokes")}},
79 {{ _("Pattern"), _("Pattern (fill)")},
80 { _("Pattern"), _("Pattern (stroke)")}},
81 {{ _("Hatch"), _("Pattern (fill)")},
82 { _("Hatch"), _("Pattern (stroke)")}},
83 {{ _("<b>L</b>"), _("Linear gradient (fill)")},
84 { _("<b>L</b>"), _("Linear gradient (stroke)")}},
85 {{ _("<b>R</b>"), _("Radial gradient (fill)")},
86 { _("<b>R</b>"), _("Radial gradient (stroke)")}},
87 {{ _("<b>M</b>"), _("Mesh gradient (fill)")},
88 { _("<b>M</b>"), _("Mesh gradient (stroke)")}},
89 {{ _("<b>C</b>"), _("Flat color (fill)")},
90 { _("<b>C</b>"), _("Flat color (stroke)")}}
91};
92
93static void
95{
97 ss->update();
98}
99
100static void
101ss_selection_modified( Inkscape::Selection *selection, guint flags, gpointer data )
102{
103 // Don't update the style when dragging or doing non-style related changes
104 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG)) {
105 ss_selection_changed (selection, data);
106 }
107}
108
109static void
110ss_subselection_changed( gpointer /*dragger*/, gpointer data )
111{
112 ss_selection_changed (nullptr, data);
113}
114
115namespace Inkscape::UI::Widget {
116
117struct SelectedStyleDropTracker final {
118 SelectedStyle* parent;
119 int item;
120};
121
122/* Drag and Drop */
126
127/* convenience function */
129
131 : Gtk::Box(Gtk::Orientation::HORIZONTAL)
132 , dragging(false)
133{
134 set_name("SelectedStyle");
135 set_size_request (SELECTED_STYLE_WIDTH, -1);
136
137 grid = Gtk::make_managed<Gtk::Grid>();
138 grid->set_size_request(SELECTED_STYLE_WIDTH, -1);
139
140 // Fill and stroke
141 for (int i = 0; i <2; i++) {
142 label[i] = Gtk::make_managed<Gtk::Label>(i == 0 ? _("Fill:") : _("Stroke:"));
143 label[i]->set_halign(Gtk::Align::END);
144
145 // Multiple, Average, or Single
146 tag[i] = Gtk::make_managed<Gtk::Label>(); // "m", "a", or empty
147 tag[i]->set_size_request(SELECTED_STYLE_FLAG_WIDTH, -1);
148 tag[i]->set_name("Tag");
149
150 // Type of fill
151 type_label[i] = std::make_unique<Gtk::Label>(type_strings[0][i][0]);
152 type_label[i]->set_hexpand(true);
153
154 // CSS sets width to 54.
155 gradient_preview[i] = std::make_unique<GradientImage>(nullptr);
156 gradient_preview[i]->set_visible(false);
157
158 color_preview[i] = std::make_unique<Inkscape::UI::Widget::ColorPreview>(0);
159 color_preview[i]->set_size_request(SELECTED_STYLE_PLACE_WIDTH, -1);
160 color_preview[i]->set_hexpand(true);
161 color_preview[i]->set_visible(false);
162
163 // Shows one or two children at a time.
164 swatch[i] = Gtk::make_managed<RotateableSwatch>(this, i);
165 swatch[i]->set_orientation(Gtk::Orientation::HORIZONTAL);
166 swatch[i]->set_hexpand(false);
167 swatch[i]->append(*type_label[i]);
168 swatch[i]->append(*gradient_preview[i]);
169 swatch[i]->append(*color_preview[i]);
170 swatch[i]->set_tooltip_text(type_strings[0][i][1]);
171 swatch[i]->set_size_request(SELECTED_STYLE_PLACE_WIDTH, -1);
172
173 // Drag color from color palette, for example.
174 drop[i] = std::make_unique<SelectedStyleDropTracker>();
175 drop[i]->parent = this;
176 drop[i]->item = i;
177 auto target = Gtk::DropTarget::create(Glib::Value<PaintDef>::value_type(), Gdk::DragAction::COPY | Gdk::DragAction::MOVE);
178 target->signal_drop().connect([this, i] (Glib::ValueBase const &value, double, double) {
179 if (!dropEnabled[i]) {
180 return false;
181 }
182
183 auto const &tracker = *drop[i];
184 auto const &paintdef = *reinterpret_cast<PaintDef *>(g_value_get_boxed(value.gobj()));
185
186 // copied from drag-and-drop.cpp, case PaintDef
187 std::string colorspec;
188 if (paintdef.get_type() == PaintDef::NONE) {
189 colorspec = "none";
190 } else {
191 auto const [r, g, b] = paintdef.get_rgb();
192 colorspec.resize(63);
193 sp_svg_write_color(colorspec.data(), colorspec.size() + 1, SP_RGBA32_U_COMPOSE(r, g, b, 0xff));
194 colorspec.resize(std::strlen(colorspec.c_str()));
195 }
196
197 auto const css = sp_repr_css_attr_new();
198 sp_repr_css_set_property(css, tracker.item == SS_FILL ? "fill" : "stroke", colorspec.c_str());
199 sp_desktop_set_style(tracker.parent->_desktop, css);
201
202 DocumentUndo::done(tracker.parent->_desktop->getDocument(), _("Drop color"), "");
203 return true;
204 }, true);
205 swatch[i]->add_controller(target);
206 Controller::add_click(*swatch[i], {}, sigc::mem_fun(*this,
207 i == 0 ?
210
211 grid->attach(*label[i], 0, i, 1, 1);
212 grid->attach(*tag[i], 1, i, 1, 1);
213 grid->attach(*swatch[i], 2, i, 1, 1);
214
215 make_popup(static_cast<FillOrStroke>(i));
216 _mode[i] = SS_NA;
217 }
218
219 // Stroke width
220 stroke_width = Gtk::make_managed<Gtk::Label>("1");
221 stroke_width_rotateable = Gtk::make_managed<RotateableStrokeWidth>(this);
225 sigc::mem_fun(*this, &SelectedStyle::on_sw_click));
226 grid->attach(*stroke_width_rotateable, 3, 1, 1, 1);
227
228 // Opacity
230 opacity_label = Gtk::make_managed<Gtk::Label>(_("O:"));
231 opacity_adjustment = Gtk::Adjustment::create(100, 0.0, 100, 1.0, 10.0);
232 opacity_sb = Gtk::make_managed<Inkscape::UI::Widget::SpinButton>(0.02, 0);
233 opacity_sb->set_adjustment(opacity_adjustment);
234 opacity_sb->set_size_request(SELECTED_STYLE_SB_WIDTH);
235 opacity_sb->set_sensitive(false);
237 sigc::mem_fun(*this, &SelectedStyle::on_opacity_click),
240 opacity_sb->signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
241
242 grid->attach(*opacity_label, 4, 0, 1, 2);
243 grid->attach(*opacity_sb, 5, 0, 1, 2);
244
245 grid->set_column_spacing(4);
246 append(*grid);
247
249}
250
251void
253{
254 _desktop = desktop;
255
256 Inkscape::Selection *selection = desktop->getSelection();
257
259 sigc::bind(&ss_selection_changed, this)
260 );
262 sigc::bind(&ss_selection_modified, this)
263 );
265 sigc::bind(&ss_subselection_changed, this)
266 );
267
268 _sw_unit = desktop->getNamedView()->display_units;
269}
270
271
272
275 sp_repr_css_set_property (css, "fill", "none");
276 sp_desktop_set_style (_desktop, css, true, true);
278 DocumentUndo::done(_desktop->getDocument(), _("Remove fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
279}
280
283 sp_repr_css_set_property (css, "stroke", "none");
284 sp_desktop_set_style (_desktop, css, true, true);
286 DocumentUndo::done(_desktop->getDocument(), _("Remove stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
287}
288
292 sp_desktop_set_style (_desktop, css, true, true);
294 DocumentUndo::done(_desktop->getDocument(), _("Unset fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
295
296}
297
301 sp_repr_css_unset_property (css, "stroke-opacity");
302 sp_repr_css_unset_property (css, "stroke-width");
303 sp_repr_css_unset_property (css, "stroke-miterlimit");
304 sp_repr_css_unset_property (css, "stroke-linejoin");
305 sp_repr_css_unset_property (css, "stroke-linecap");
306 sp_repr_css_unset_property (css, "stroke-dashoffset");
307 sp_repr_css_unset_property (css, "stroke-dasharray");
308 sp_desktop_set_style (_desktop, css, true, true);
310 DocumentUndo::done(_desktop->getDocument(), _("Unset stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
311}
312
315 sp_repr_css_set_property (css, "fill-opacity", "1");
318 DocumentUndo::done(_desktop->getDocument(), _("Make fill opaque"), INKSCAPE_ICON("dialog-fill-and-stroke"));
319}
320
323 sp_repr_css_set_property (css, "stroke-opacity", "1");
326 DocumentUndo::done(_desktop->getDocument(), _("Make fill opaque"), INKSCAPE_ICON("dialog-fill-and-stroke"));
327}
328
332 gchar c[64];
333 sp_svg_write_color (c, sizeof(c), color);
334 sp_repr_css_set_property (css, "fill", c);
337 DocumentUndo::done(_desktop->getDocument(), _("Apply last set color to fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
338}
339
342 guint32 color = sp_desktop_get_color(_desktop, false);
343 gchar c[64];
344 sp_svg_write_color (c, sizeof(c), color);
345 sp_repr_css_set_property (css, "stroke", c);
348 DocumentUndo::done(_desktop->getDocument(), _("Apply last set color to stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
349}
350
353 gchar c[64];
355 sp_repr_css_set_property (css, "fill", c);
358 DocumentUndo::done(_desktop->getDocument(), _("Apply last selected color to fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
359}
360
363 gchar c[64];
365 sp_repr_css_set_property (css, "stroke", c);
368 DocumentUndo::done(_desktop->getDocument(), _("Apply last selected color to stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
369}
370
374 gchar c[64];
377 return;
378
379 }
380
381 if (_mode[SS_FILL] != SS_COLOR) return;
382 sp_svg_write_color (c, sizeof(c),
383 SP_RGBA32_U_COMPOSE(
384 (255 - SP_RGBA32_R_U(color)),
385 (255 - SP_RGBA32_G_U(color)),
386 (255 - SP_RGBA32_B_U(color)),
387 SP_RGBA32_A_U(color)
388 )
389 );
390 sp_repr_css_set_property (css, "fill", c);
393 DocumentUndo::done(_desktop->getDocument(), _("Invert fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
394}
395
399 gchar c[64];
402 return;
403 }
404 if (_mode[SS_STROKE] != SS_COLOR) return;
405 sp_svg_write_color (c, sizeof(c),
406 SP_RGBA32_U_COMPOSE(
407 (255 - SP_RGBA32_R_U(color)),
408 (255 - SP_RGBA32_G_U(color)),
409 (255 - SP_RGBA32_B_U(color)),
410 SP_RGBA32_A_U(color)
411 )
412 );
413 sp_repr_css_set_property (css, "stroke", c);
416 DocumentUndo::done(_desktop->getDocument(), _("Invert stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
417}
418
421 gchar c[64];
422 sp_svg_write_color (c, sizeof(c), 0xffffffff);
423 sp_repr_css_set_property (css, "fill", c);
424 sp_repr_css_set_property (css, "fill-opacity", "1");
427 DocumentUndo::done(_desktop->getDocument(), _("White fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
428}
429
432 gchar c[64];
433 sp_svg_write_color (c, sizeof(c), 0xffffffff);
434 sp_repr_css_set_property (css, "stroke", c);
435 sp_repr_css_set_property (css, "stroke-opacity", "1");
438 DocumentUndo::done(_desktop->getDocument(), _("White stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
439}
440
443 gchar c[64];
444 sp_svg_write_color (c, sizeof(c), 0x000000ff);
445 sp_repr_css_set_property (css, "fill", c);
446 sp_repr_css_set_property (css, "fill-opacity", "1.0");
449 DocumentUndo::done(_desktop->getDocument(), _("Black fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
450}
451
454 gchar c[64];
455 sp_svg_write_color (c, sizeof(c), 0x000000ff);
456 sp_repr_css_set_property (css, "stroke", c);
457 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
460 DocumentUndo::done(_desktop->getDocument(), _("Black stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
461}
462
464 if (_mode[SS_FILL] == SS_COLOR) {
465 gchar c[64];
467 Glib::ustring text;
468 text += c;
469 if (!text.empty()) {
470 auto const display = Gdk::Display::get_default();
471 display->get_primary_clipboard()->set_text(text);
472 }
473 }
474}
475
477 if (_mode[SS_STROKE] == SS_COLOR) {
478 gchar c[64];
480 Glib::ustring text;
481 text += c;
482 if (!text.empty()) {
483 auto const display = Gdk::Display::get_default();
484 display->get_primary_clipboard()->set_text(text);
485 }
486 }
487}
488
489void SelectedStyle::_on_paste_callback(Glib::RefPtr<Gio::AsyncResult>& result, Glib::ustring typepaste)
490{
491 auto const display = Gdk::Display::get_default();
492 Glib::RefPtr<Gdk::Clipboard> refClipboard = display->get_primary_clipboard();
493 // Parse the clipboard text as if it was a color string.
494 Glib::ustring text;
495 try {
496 text = refClipboard->read_text_finish(result);
497 } catch (Glib::Error const &err) {
498 std::cout << "Pasting text failed: " << err.what() << std::endl;
499 return;
500 }
501 if (!text.empty()) {
502 guint32 color = sp_svg_read_color(text.c_str(), 0x000000ff); // impossible value, as SVG color cannot have opacity
503 if (color == 0x000000ff) // failed to parse color string
504 return;
505
507 sp_repr_css_set_property (css, typepaste.c_str(), text.c_str());
510 DocumentUndo::done(_desktop->getDocument(), typepaste.c_str() == "fill" ? _("Paste fill") : _("Paste stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
511 }
512}
513
515 auto const display = Gdk::Display::get_default();
516 Glib::RefPtr<Gdk::Clipboard> refClipboard = display->get_primary_clipboard();
517 refClipboard->read_text_async(sigc::bind(sigc::mem_fun(*this, &SelectedStyle::_on_paste_callback), "fill"));
518}
519
521 auto const display = Gdk::Display::get_default();
522 Glib::RefPtr<Gdk::Clipboard> refClipboard = display->get_primary_clipboard();
523 refClipboard->read_text_async(sigc::bind(sigc::mem_fun(*this, &SelectedStyle::_on_paste_callback), "stroke"));
524}
525
528}
529
532 fs->showPageFill();
533}
534
537 fs->showPageStrokePaint();
538}
539
540Gtk::EventSequenceState SelectedStyle::on_fill_click(Gtk::GestureClick const &click, int n_press, double /*x*/,
541 double /*y*/)
542{
543 auto const button = click.get_current_button();
544 if (button == 1 && !dragging) { // click, open fill&stroke
546 fs->showPageFill();
547 } else if (button == 3) { // right-click, popup menu
548 _popup[SS_FILL]->popup_at_center(*swatch[SS_FILL]);
549 } else if (button == 2) { // middle click, toggle none/lastcolor
550 if (_mode[SS_FILL] == SS_NONE) {
552 } else {
554 }
555 }
556 return Gtk::EventSequenceState::CLAIMED;
557}
558
559Gtk::EventSequenceState SelectedStyle::on_stroke_click(Gtk::GestureClick const &click, int n_press, double /*x*/,
560 double /*y*/)
561{
562 auto const button = click.get_current_button();
563 if (button == 1 && !dragging) { // click, open fill&stroke
565 fs->showPageStrokePaint();
566 } else if (button == 3) { // right-click, popup menu
567 _popup[SS_STROKE]->popup_at_center(*swatch[SS_STROKE]);
568 } else if (button == 2) { // middle click, toggle none/lastcolor
569 if (_mode[SS_STROKE] == SS_NONE) {
571 } else {
573 }
574 }
575 return Gtk::EventSequenceState::CLAIMED;
576}
577
578Gtk::EventSequenceState SelectedStyle::on_sw_click(Gtk::GestureClick const &click, int n_press, double /*x*/,
579 double /*y*/)
580{
581 auto const button = click.get_current_button();
582 if (button == 1 && !dragging) { // click, open fill&stroke
584 fs->showPageStrokeStyle();
585 } else if (button == 3) { // right-click, popup menu
586 auto const it = std::find_if(_unit_mis.cbegin(), _unit_mis.cend(),
587 [=](auto const mi){ return mi->get_label() == _sw_unit->abbr; });
588 if (it != _unit_mis.cend()) (*it)->set_active(true);
589
590 _popup_sw->popup_at_center(*stroke_width);
591 } else if (button == 2) { // middle click, toggle none/lastwidth?
592 //
593 }
594 return Gtk::EventSequenceState::CLAIMED;
595}
596
597Gtk::EventSequenceState
598SelectedStyle::on_opacity_click(Gtk::GestureClick const & /*click*/,
599 int /*n_press*/, double /*x*/, double /*y*/)
600{
601 const char* opacity = opacity_sb->get_value() < 50? "0.5" : (opacity_sb->get_value() == 100? "0" : "1");
603 sp_repr_css_set_property (css, "opacity", opacity);
606 DocumentUndo::done(_desktop->getDocument(), _("Change opacity"), INKSCAPE_ICON("dialog-fill-and-stroke"));
607 return Gtk::EventSequenceState::CLAIMED;
608}
609
610template <typename Slot, typename ...Args>
611static UI::Widget::PopoverMenuItem *make_menu_item(Glib::ustring const &label, Slot slot,
612 Args &&...args)
613{
614 auto const item = Gtk::make_managed<UI::Widget::PopoverMenuItem>(std::forward<Args>(args)...);
615 item->set_child(*Gtk::make_managed<Gtk::Label>(label, Gtk::Align::START, Gtk::Align::START));
616 item->signal_activate().connect(std::move(slot));
617 return item;
618};
619
621{
622 _popup[i] = std::make_unique<UI::Widget::PopoverMenu>(*this, Gtk::PositionType::TOP);
623
624
625 auto const add_item = [&](Glib::ustring const & fill_label, auto const fill_method,
626 Glib::ustring const &stroke_label, auto const stroke_method)
627 {
628 auto const &label = i == SS_FILL || stroke_label.empty() ? fill_label : stroke_label ;
629 auto const method = i == SS_FILL || stroke_method == nullptr ? fill_method : stroke_method;
630 auto const item = make_menu_item(label, sigc::mem_fun(*this, method));
631 _popup[i]->append(*item);
632 return item;
633 };
634
635 add_item(_("Edit Fill..." ) , &SelectedStyle:: on_fill_edit ,
636 _("Edit Stroke...") , &SelectedStyle::on_stroke_edit );
637
638 _popup[i]->append_separator();
639
640
641 add_item(_("Last Set Color") , &SelectedStyle:: on_fill_lastused ,
643 add_item(_("Last Selected Color") , &SelectedStyle:: on_fill_lastselected,
645
646 _popup[i]->append_separator();
647
648 add_item(_("Invert") , &SelectedStyle:: on_fill_invert ,
650
651 _popup[i]->append_separator();
652
653 add_item(_("White") , &SelectedStyle:: on_fill_white ,
655 add_item(_("Black") , &SelectedStyle:: on_fill_black ,
657
658 _popup[i]->append_separator();
659
660 _popup_copy[i] = add_item(
661 _("Copy Color") , &SelectedStyle:: on_fill_copy ,
663 _popup_copy[i]->set_sensitive(false);
664
665 add_item(_("Paste Color") , &SelectedStyle:: on_fill_paste ,
667 add_item(_("Swap Fill and Stroke"), &SelectedStyle::on_fillstroke_swap ,
668 {} , nullptr );
669
670 _popup[i]->append_separator();
671
672 add_item(_("Make Fill Opaque" ) , &SelectedStyle:: on_fill_opaque ,
673 _("Make Stroke Opaque") , &SelectedStyle::on_stroke_opaque );
674 //TRANSLATORS COMMENT: unset is a verb here
675 add_item(_("Unset Fill" ) , &SelectedStyle:: on_fill_unset ,
676 _("Unset Stroke") , &SelectedStyle::on_stroke_unset );
677 add_item(_("Remove Fill" ) , &SelectedStyle:: on_fill_remove ,
678 _("Remove Stroke") , &SelectedStyle::on_stroke_remove );
679}
680
682{
683 _popup_sw = std::make_unique<UI::Widget::PopoverMenu>(*this, Gtk::PositionType::TOP);
684
685 _popup_sw->append_section_label(_("<b>Stroke Width</b>"));
686
687 _popup_sw->append_separator();
688
689 _popup_sw->append_section_label(_("Unit"));
690 Gtk::CheckButton *group = nullptr;
691 auto const &unit_table = Util::UnitTable::get();
692 for (auto const &[key, value] : unit_table.units(Inkscape::Util::UNIT_TYPE_LINEAR)) {
693 auto const item = Gtk::make_managed<UI::Widget::PopoverMenuItem>();
694 auto const radio = Gtk::make_managed<Gtk::CheckButton>(key);
695 if (!group) {
696 group = radio;
697 } else {
698 radio->set_group(*group);
699 }
700 item->set_child(*radio);
701 _unit_mis.push_back(radio);
702 auto const u = unit_table.getUnit(key);
703 item->signal_activate().connect(
704 sigc::bind(sigc::mem_fun(*this, &SelectedStyle::on_popup_units), u));
705 _popup_sw->append(*item);
706 }
707
708 _popup_sw->append_separator();
709
710 _popup_sw->append_section_label(_("Width"));
711 for (std::size_t i = 0; i < _sw_presets.size(); ++i) {
713 sigc::bind(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i)));
714 }
715
716 _popup_sw->append_separator();
717
718 _popup_sw->append(*make_menu_item(_("Remove Stroke"),
719 sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove)));
720}
721
723 _sw_unit = unit;
724 update();
725}
726
729 gdouble w;
730 if (_sw_unit) {
732 } else {
733 w = _sw_presets[i];
734 }
736 os << w;
737 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
738 // FIXME: update dash patterns!
741 DocumentUndo::done(_desktop->getDocument(), _("Change stroke width"), INKSCAPE_ICON("swatches"));
742}
743
744void
746{
747 if (_desktop == nullptr)
748 return;
749
750 // Create temporary style
751 SPStyle query(_desktop->getDocument());
752
753 for (int i = SS_FILL; i <= SS_STROKE; i++) {
754
755 // New
756 type_label[i]->show(); // Used by all types except solid color.
757 gradient_preview[i]->set_visible(false);
758 color_preview[i]->set_visible(false);
759
760 _mode[i] = SS_NA;
761 _paintserver_id[i].clear();
762 _popup_copy[i]->set_sensitive(false);
763
764 // Query style from desktop. This returns a result flag and fills query with the style of
765 // subselection, if any, or selection.
767 (i == SS_FILL) ?
770
771 switch (result) {
773
774 tag[i]->set_markup("");
775
776 type_label[i]->set_markup(type_strings[SS_NA][i][0]);
777 swatch[i]->set_tooltip_text(type_strings[SS_NA][i][1]);
778
779 if (dropEnabled[i]) {
780 dropEnabled[i] = false;
781 }
782 break;
786 dropEnabled[i] = true;
787
788 auto paint = i == SS_FILL ? query.fill.upcast() : query.stroke.upcast();
789 if (paint->set && paint->isPaintserver()) {
790 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (&query) : SP_STYLE_STROKE_SERVER (&query);
791 if ( server ) {
792 Inkscape::XML::Node *srepr = server->getRepr();
793 _paintserver_id[i] += "url(#";
794 _paintserver_id[i] += srepr->attribute("id");
795 _paintserver_id[i] += ")";
796
797 if (is<SPLinearGradient>(server)) {
798 auto vector = cast<SPGradient>(server)->getVector();
799
800 type_label[i]->set_markup( type_strings[SS_LGRADIENT][i][0]);
801 swatch[i]->set_tooltip_text(type_strings[SS_LGRADIENT][i][1]);
802 gradient_preview[i]->set_gradient(vector);
803 gradient_preview[i]->show();
804
805 _mode[i] = SS_LGRADIENT;
806 } else if (is<SPRadialGradient>(server)) {
807 auto vector = cast<SPGradient>(server)->getVector();
808
809 type_label[i]->set_markup( type_strings[SS_RGRADIENT][i][0]);
810 swatch[i]->set_tooltip_text(type_strings[SS_RGRADIENT][i][1]);
811 gradient_preview[i]->set_gradient(vector);
812 gradient_preview[i]->show();
813
814 _mode[i] = SS_RGRADIENT;
815 } else if (is<SPMeshGradient>(server)) {
816 auto array = cast<SPGradient>(server)->getArray();
817
818 type_label[i]->set_markup( type_strings[SS_MGRADIENT][i][0]);
819 swatch[i]->set_tooltip_text(type_strings[SS_MGRADIENT][i][1]);
820 gradient_preview[i]->set_gradient(array);
821 gradient_preview[i]->show();
822
823 _mode[i] = SS_MGRADIENT;
824 } else if (is<SPPattern>(server)) {
825 type_label[i]->set_markup( type_strings[SS_PATTERN][i][0]);
826 swatch[i]->set_tooltip_text(type_strings[SS_PATTERN][i][1]);
827
828 _mode[i] = SS_PATTERN;
829 } else if (is<SPHatch>(server)) {
830 type_label[i]->set_markup( type_strings[SS_HATCH][i][0]);
831 swatch[i]->set_tooltip_text(type_strings[SS_HATCH][i][1]);
832
833 _mode[i] = SS_HATCH;
834 }
835 } else {
836 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
837 }
838 } else if (paint->set && paint->isColor()) {
839 guint32 color = paint->value.color.toRGBA32(
840 SP_SCALE24_TO_FLOAT ((i == SS_FILL) ?
841 query.fill_opacity.value :
842 query.stroke_opacity.value));
844 _thisselected[i] = color; // include opacity
845
846 gchar c_string[64];
847 safeprintf (c_string, "%06x/%.3g", color >> 8, SP_RGBA32_A_F(color));
848
849 // No type_label.
850 swatch[i]->set_tooltip_text(type_strings[SS_COLOR][i][1] + ": " + c_string +
851 _(", drag to adjust, middle-click to remove"));
852 type_label[i]->set_visible(false);
853 color_preview[i]->setRgba32(color);
854 color_preview[i]->show();
855
856 _mode[i] = SS_COLOR;
857 _popup_copy[i]->set_sensitive(true);
858 } else if (paint->set && paint->isNone()) {
859 type_label[i]->set_markup(type_strings[ SS_NONE][i][0]);
860 swatch[i]->set_tooltip_text(type_strings[SS_NONE][i][1]);
861
862 _mode[i] = SS_NONE;
863 } else if (!paint->set) {
864 type_label[i]->set_markup(type_strings[ SS_UNSET][i][0]);
865 swatch[i]->set_tooltip_text(type_strings[SS_UNSET][i][1]);
866
867 _mode[i] = SS_UNSET;
868 }
869
871 // TRANSLATORS: A means "Averaged"
872 tag[i]->set_markup("<b>a</b>");
873 tag[i]->set_tooltip_text(i == 0 ?
874 _("Fill is averaged over selected objects") :
875 _("Stroke is averaged over selected objects"));
876
877 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
878 // TRANSLATORS: M means "Multiple"
879 tag[i]->set_markup("<b>m</b>");
880 tag[i]->set_tooltip_text(i == 0 ?
881 _("Multiple selected objects have same fill") :
882 _("Multiple selected objects have same stroke"));
883 } else {
884 tag[i]->set_markup("");
885 tag[i]->set_tooltip_text("");
886 }
887 break;
888 }
889
891 type_label[i]->set_markup(type_strings[ SS_MANY][i][0]);
892 swatch[i]->set_tooltip_text(type_strings[SS_MANY][i][1]);
893
894 _mode[i] = SS_MANY;
895 break;
896 default:
897 break;
898 }
899 }
900
901// Now query opacity
903
904 switch (result) {
906 opacity_sb->set_tooltip_text(_("Nothing selected"));
907 opacity_sb->set_sensitive(false);
908 break;
912 opacity_sb->set_tooltip_text(_("Opacity (%)"));
913
914 if (_opacity_blocked) break;
915
916 _opacity_blocked = true;
917 opacity_sb->set_sensitive(true);
918 opacity_adjustment->set_value(SP_SCALE24_TO_FLOAT(query.opacity.value) * 100);
919 _opacity_blocked = false;
920 break;
921 }
922
923// Now query stroke_width
925 switch (result_sw) {
927 stroke_width->set_markup("");
929 break;
933 {
934 if (query.stroke_extensions.hairline) {
935 stroke_width->set_markup(_("Hairline"));
936 stroke_width->set_tooltip_text(_("Stroke width: Hairline"));
937 } else {
938 double w;
939 if (_sw_unit) {
941 } else {
942 w = query.stroke_width.computed;
943 }
945
946 {
947 gchar *str = g_strdup_printf(" %#.3g", w);
948 if (str[strlen(str) - 1] == ',' || str[strlen(str) - 1] == '.') {
949 str[strlen(str)-1] = '\0';
950 }
951 stroke_width->set_markup(str);
952 g_free (str);
953 }
954 {
955 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
956 w,
957 _sw_unit? _sw_unit->abbr.c_str() : "px",
958 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
959 _(" (averaged)") : "");
960 stroke_width->set_tooltip_text(str);
961 g_free (str);
962 }
963 }
964 break;
965 }
966 default:
967 break;
968 }
969}
970
973void SelectedStyle::opacity_05() {opacity_sb->set_value(50);}
975void SelectedStyle::opacity_1() {opacity_sb->set_value(100);}
976
978{
979 _popup_opacity = std::make_unique<UI::Widget::PopoverMenu>(*this, Gtk::PositionType::TOP);
980 auto const add_item = [&](Glib::ustring const &label, auto const method)
981 { _popup_opacity->append(*make_menu_item(label, sigc::mem_fun(*this, method))); };
982 add_item(_("0 (Transparent)"), &SelectedStyle::opacity_0 );
983 add_item(_("25%" ), &SelectedStyle::opacity_025);
984 add_item(_("50%" ), &SelectedStyle::opacity_05 );
985 add_item(_("75%" ), &SelectedStyle::opacity_075);
986 add_item(_("100% (Opaque)" ), &SelectedStyle::opacity_1 );
987}
988
990{
991 _popup_opacity->popup_at_center(*opacity_sb);
992 return true;
993}
994
996{
997 g_return_if_fail(_desktop); // TODO this shouldn't happen!
998 if (_opacity_blocked) {
999 return;
1000 }
1001 _opacity_blocked = true;
1004 os << CLAMP ((opacity_adjustment->get_value() / 100), 0.0, 1.0);
1005 sp_repr_css_set_property (css, "opacity", os.str().c_str());
1008 DocumentUndo::maybeDone(_desktop->getDocument(), "fillstroke:opacity", _("Change opacity"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1009 _opacity_blocked = false;
1010}
1011
1012/* ============================================= RotateableSwatch */
1013
1015 : fillstroke(mode)
1016 , parent(parent)
1017{
1018 set_name("RotatableSwatch");
1019}
1020
1022
1023double
1024RotateableSwatch::color_adjust(float *hsla, double by, guint32 cc, guint modifier)
1025{
1026 SPColor::rgb_to_hsl_floatv (hsla, SP_RGBA32_R_F(cc), SP_RGBA32_G_F(cc), SP_RGBA32_B_F(cc));
1027 hsla[3] = SP_RGBA32_A_F(cc);
1028 double diff = 0;
1029 if (modifier == 2) { // saturation
1030 double old = hsla[1];
1031 if (by > 0) {
1032 hsla[1] += by * (1 - hsla[1]);
1033 } else {
1034 hsla[1] += by * (hsla[1]);
1035 }
1036 diff = hsla[1] - old;
1037 } else if (modifier == 1) { // lightness
1038 double old = hsla[2];
1039 if (by > 0) {
1040 hsla[2] += by * (1 - hsla[2]);
1041 } else {
1042 hsla[2] += by * (hsla[2]);
1043 }
1044 diff = hsla[2] - old;
1045 } else if (modifier == 3) { // alpha
1046 double old = hsla[3];
1047 hsla[3] += by/2;
1048 if (hsla[3] < 0) {
1049 hsla[3] = 0;
1050 } else if (hsla[3] > 1) {
1051 hsla[3] = 1;
1052 }
1053 diff = hsla[3] - old;
1054 } else { // hue
1055 double old = hsla[0];
1056 hsla[0] += by/2;
1057 while (hsla[0] < 0)
1058 hsla[0] += 1;
1059 while (hsla[0] > 1)
1060 hsla[0] -= 1;
1061 diff = hsla[0] - old;
1062 }
1063
1064 float rgb[3];
1065 SPColor::hsl_to_rgb_floatv (rgb, hsla[0], hsla[1], hsla[2]);
1066
1067 gchar c[64];
1068 sp_svg_write_color (c, sizeof(c),
1069 SP_RGBA32_U_COMPOSE(
1070 (SP_COLOR_F_TO_U(rgb[0])),
1071 (SP_COLOR_F_TO_U(rgb[1])),
1072 (SP_COLOR_F_TO_U(rgb[2])),
1073 0xff
1074 )
1075 );
1076
1078
1079 if (modifier == 3) { // alpha
1081 osalpha << hsla[3];
1082 sp_repr_css_set_property(css, (fillstroke == SS_FILL) ? "fill-opacity" : "stroke-opacity", osalpha.str().c_str());
1083 } else {
1084 sp_repr_css_set_property (css, (fillstroke == SS_FILL) ? "fill" : "stroke", c);
1085 }
1088 return diff;
1089}
1090
1091void RotateableSwatch::do_motion(double by, guint modifier)
1092{
1093 if (parent->_mode[fillstroke] != SS_COLOR) {
1094 return;
1095 }
1096 parent->dragging = true;
1097
1098 if (!scrolling && modifier != cursor_state) {
1099 std::string cursor_filename = "adjust_hue.svg";
1100 if (modifier == 2) {
1101 cursor_filename = "adjust_saturation.svg";
1102 } else if (modifier == 1) {
1103 cursor_filename = "adjust_lightness.svg";
1104 } else if (modifier == 3) {
1105 cursor_filename = "adjust_alpha.svg";
1106 }
1107 set_svg_cursor(*this, cursor_filename);
1108
1110 }
1111
1112 guint32 cc;
1113 if (!startcolor_set) {
1115 startcolor_set = true;
1116 } else {
1117 cc = startcolor;
1118 }
1119
1120 float hsla[4];
1121 double diff = 0;
1122
1123 diff = color_adjust(hsla, by, cc, modifier);
1124
1125 if (modifier == 3) { // alpha
1126 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust alpha")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1127 double ch = hsla[3];
1130 _("Adjusting <b>alpha</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Ctrl</b> to adjust lightness, with <b>Shift</b> to adjust saturation, without modifiers to adjust hue"),
1131 ch - diff, ch, diff);
1132
1133 } else if (modifier == 2) { // saturation
1134 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust saturation")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1135 double ch = hsla[1];
1138 _("Adjusting <b>saturation</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Ctrl</b> to adjust lightness, with <b>Alt</b> to adjust alpha, without modifiers to adjust hue"),
1139 ch - diff, ch, diff);
1140
1141 } else if (modifier == 1) { // lightness
1142 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust lightness")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1143 double ch = hsla[2];
1146 _("Adjusting <b>lightness</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, with <b>Alt</b> to adjust alpha, without modifiers to adjust hue"),
1147 ch - diff, ch, diff);
1148
1149 } else { // hue
1150 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust hue")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1151 double ch = hsla[0];
1154 _("Adjusting <b>hue</b>: was %.3g, now <b>%.3g</b> (diff %.3g); with <b>Shift</b> to adjust saturation, with <b>Alt</b> to adjust alpha, with <b>Ctrl</b> to adjust lightness"),
1155 ch - diff, ch, diff);
1156 }
1157}
1158
1159void RotateableSwatch::do_scroll(double by, guint modifier)
1160{
1161 do_motion(by/30.0, modifier);
1162 do_release(by/30.0, modifier);
1163}
1164
1165void RotateableSwatch::do_release(double by, guint modifier)
1166{
1168 return;
1169
1170 parent->dragging = false;
1171 float hsla[4];
1172 color_adjust(hsla, by, startcolor, modifier);
1173
1174 if (cursor_state != -1) {
1175 if (auto window = dynamic_cast<Gtk::Window *>(get_root())) {
1176 window->set_cursor(); // Use parent window cursor.
1177 }
1178 cursor_state = -1;
1179 }
1180
1181 if (modifier == 3) { // alpha
1182 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust alpha"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1183
1184 } else if (modifier == 2) { // saturation
1185 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust saturation"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1186
1187 } else if (modifier == 1) { // lightness
1188 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust lightness"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1189
1190 } else { // hue
1191 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust hue"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1192 }
1193
1194 if (!strcmp(undokey, "ssrot1")) {
1195 undokey = "ssrot2";
1196 } else {
1197 undokey = "ssrot1";
1198 }
1199
1200 parent->getDesktop()->getTool()->message_context->clear();
1201 startcolor_set = false;
1202}
1203
1204/* ============================================= RotateableStrokeWidth */
1205
1207 parent(parent),
1208 startvalue(0),
1209 startvalue_set(false),
1210 undokey("swrot1")
1211{
1212}
1213
1215
1216double
1217RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final)
1218{
1219 double newval;
1220 // by is -1..1
1221 double max_f = 50; // maximum width is (current * max_f), minimum - zero
1222 newval = current * (std::exp(std::log(max_f-1) * (by+1)) - 1) / (max_f-2);
1223
1225 if (final && newval < 1e-6) {
1226 // if dragged into zero and this is the final adjust on mouse release, delete stroke;
1227 // if it's not final, leave it a chance to increase again (which is not possible with "none")
1228 sp_repr_css_set_property (css, "stroke", "none");
1229 } else {
1230 newval = Inkscape::Util::Quantity::convert(newval, parent->_sw_unit, "px");
1232 os << newval;
1233 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
1234 }
1235
1238 return newval - current;
1239}
1240
1241void RotateableStrokeWidth::do_motion(double by, guint modifier)
1242{
1243 // if this is the first motion after a mouse grab, remember the current width
1244 if (!startvalue_set) {
1246 // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we
1247 // cheat and provide a non-zero value
1248 if (startvalue == 0)
1249 startvalue = 1;
1250 startvalue_set = true;
1251 }
1252 parent->dragging = true;
1253
1254 if (modifier == 3) { // Alt, do nothing
1255 } else {
1256 double diff = value_adjust(startvalue, by, modifier, false);
1257 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust stroke width")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1258 parent->getDesktop()->getTool()->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Adjusting <b>stroke width</b>: was %.3g, now <b>%.3g</b> (diff %.3g)"), startvalue, startvalue + diff, diff);
1259 }
1260}
1261
1262void RotateableStrokeWidth::do_release(double by, guint modifier)
1263{
1264 parent->dragging = false;
1265
1266 if (modifier == 3) { // do nothing
1267
1268 } else {
1269 value_adjust(startvalue, by, modifier, true);
1270 startvalue_set = false;
1271 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust stroke width")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1272 }
1273
1274 if (!strcmp(undokey, "swrot1")) {
1275 undokey = "swrot2";
1276 } else {
1277 undokey = "swrot1";
1278 }
1279 parent->getDesktop()->getTool()->message_context->clear();
1280}
1281
1282void RotateableStrokeWidth::do_scroll(double by, guint modifier)
1283{
1284 do_motion(by/10.0, modifier);
1285 do_release(by / 10.0, modifier);
1286 startvalue_set = false;
1287}
1288
1290{
1291 desktop->getContainer()->new_dialog("FillStroke");
1292 return dynamic_cast<Dialog::FillAndStroke *>(desktop->getContainer()->get_dialog("FillStroke"));
1293}
1294
1295} // namespace Inkscape::UI::Widget
1296
1297/*
1298 Local Variables:
1299 mode:c++
1300 c-file-style:"stroustrup"
1301 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1302 indent-tabs-mode:nil
1303 fill-column:99
1304 End:
1305*/
1306// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
static void maybeDone(SPDocument *document, const gchar *keyconst, Glib::ustring const &event_description, Glib::ustring const &undo_icon)
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon)
The set of selected SPObjects for a given document and layer model.
Definition: selection.h:55
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition: selection.cpp:208
sigc::connection connectModified(sigc::slot< void(Selection *, unsigned)> slot)
Connects a slot to be notified of selected object modifications.
Definition: selection.cpp:230
DialogBase * get_dialog(const Glib::ustring &dialog_type)
void new_dialog(const Glib::ustring &dialog_type)
Add new dialog to the current container or in a floating window, based on preferences.
std::unique_ptr< MessageContext > message_context
Definition: tool-base.h:192
A replacement for GTK3ʼs Gtk::MenuItem, as removed in GTK4.
void do_motion(double by, guint state) override
double value_adjust(double current, double by, guint modifier, bool final)
void do_scroll(double by, guint state) override
void do_release(double by, guint state) override
RotateableSwatch(SelectedStyle *parent, guint mode)
void do_motion(double by, guint state) override
void do_release(double by, guint state) override
double color_adjust(float *hsl, double by, guint32 cc, guint state)
void do_scroll(double by, guint state) override
Selected style indicator (fill, stroke, opacity).
UI::Widget::PopoverMenuItem * _popup_copy[2]
void on_popup_units(Inkscape::Util::Unit const *u)
std::unique_ptr< Gtk::Label > type_label[2]
void setDesktop(SPDesktop *desktop)
Glib::RefPtr< Gtk::Adjustment > opacity_adjustment
std::unique_ptr< ColorPreview > color_preview[2]
Gtk::EventSequenceState on_fill_click(Gtk::GestureClick const &click, int n_press, double x, double y)
RotateableStrokeWidth * stroke_width_rotateable
std::unique_ptr< GradientImage > gradient_preview[2]
bool on_opacity_popup(PopupMenuOptionalClick)
Gtk::EventSequenceState on_stroke_click(Gtk::GestureClick const &click, int n_press, double x, double y)
auto_connection subselection_changed_connection
Gtk::EventSequenceState on_sw_click(Gtk::GestureClick const &click, int n_press, double x, double y)
Inkscape::Util::Unit const * _sw_unit
std::unique_ptr< UI::Widget::PopoverMenu > _popup_sw
std::unique_ptr< UI::Widget::PopoverMenu > _popup[2]
std::unique_ptr< SelectedStyleDropTracker > drop[2]
std::vector< Gtk::CheckButton * > _unit_mis
Inkscape::UI::Widget::SpinButton * opacity_sb
Gtk::EventSequenceState on_opacity_click(Gtk::GestureClick const &click, int n_press, double x, double y)
std::unique_ptr< UI::Widget::PopoverMenu > _popup_opacity
void _on_paste_callback(Glib::RefPtr< Gio::AsyncResult > &result, Glib::ustring typepaste)
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition: units.cpp:525
static UnitTable & get()
Definition: units.cpp:410
Glib::ustring abbr
Definition: units.h:76
Interface for refcounted XML nodes.
Definition: node.h:80
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Pure data representation of a color definition.
Definition: paintdef.h:56
std::array< unsigned, 3 > const & get_rgb() const
Definition: paintdef.h:75
@ NONE
Definition: paintdef.h:60
static void hsl_to_rgb_floatv(float *rgb, float h, float s, float l)
Fill rgb float array from h,s,l float values.
Definition: color.cpp:457
static void rgb_to_hsl_floatv(float *hsl, float r, float g, float b)
Fill hsl float array from r,g,b float values.
Definition: color.cpp:413
To do: update description of desktop.
Definition: desktop.h:150
SPDocument * getDocument() const
Definition: desktop.h:187
SPNamedView * getNamedView() const
Definition: desktop.h:190
Inkscape::UI::Dialog::DialogContainer * getContainer()
Definition: desktop.cpp:413
Inkscape::Selection * getSelection() const
Definition: desktop.h:186
Inkscape::UI::Tools::ToolBase * getTool() const
Definition: desktop.h:185
sigc::connection connectToolSubselectionChanged(const sigc::slot< void(gpointer)> &slot)
Definition: desktop.cpp:1275
Inkscape::Util::Unit const * display_units
Definition: sp-namedview.h:71
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
Definition: sp-object.cpp:232
An SVG style object.
Definition: style.h:45
T< SPAttr::FILL, SPIPaint > fill
fill
Definition: style.h:240
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition: style.h:247
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition: style.h:249
T< SPAttr::FILL_OPACITY, SPIScale24 > fill_opacity
fill-opacity
Definition: style.h:242
T< SPAttr::STROKE_OPACITY, SPIScale24 > stroke_opacity
stroke-opacity
Definition: style.h:261
T< SPAttr::OPACITY, SPIScale24 > opacity
opacity
Definition: style.h:216
T< SPAttr::STROKE_EXTENSIONS, SPIStrokeExtensions > stroke_extensions
-inkscape-stroke
Definition: style.h:263
System-wide clipboard management - class declaration.
unsigned int guint32
Definition: color.h:22
const double w
Definition: conic-4.cpp:19
Utilities to more easily use Gtk::EventController & subclasses like Gesture.
TODO: insert short description here.
std::shared_ptr< Css const > css
Css & result
double c[8][4]
Definition: cylinder3d.cpp:40
guint32 sp_desktop_get_color(SPDesktop *desktop, bool is_fill)
Return the desktop's current color.
void sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write_current, bool switch_style)
Apply style on selection on desktop.
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
Definition: desktop-style.h:46
@ QUERY_STYLE_PROPERTY_STROKEWIDTH
Definition: desktop-style.h:47
@ QUERY_STYLE_PROPERTY_FILL
Definition: desktop-style.h:45
@ QUERY_STYLE_PROPERTY_MASTEROPACITY
Definition: desktop-style.h:61
@ QUERY_STYLE_MULTIPLE_DIFFERENT
Definition: desktop-style.h:39
@ QUERY_STYLE_SINGLE
Definition: desktop-style.h:37
@ QUERY_STYLE_NOTHING
Definition: desktop-style.h:36
@ QUERY_STYLE_MULTIPLE_AVERAGED
Definition: desktop-style.h:40
@ QUERY_STYLE_MULTIPLE_SAME
Definition: desktop-style.h:38
A base class for all dialogs.
A widget that manages DialogNotebook's and other widgets inside a horizontal DialogMultipaned.
static char const *const current
Definition: dir-util.cpp:71
TODO: insert short description here.
Fill and Stroke dialog.
void sp_gradient_invert_selected_gradients(SPDesktop *desktop, Inkscape::PaintTarget fill_or_stroke)
Macro for icon names used in Inkscape.
SPItem * item
Definition: imagemagick.cpp:43
Interface for locally managing a current status message.
Definition: desktop.h:51
static void err(const char *fmt,...)
Definition: pov-out.cpp:57
Gtk::GestureClick & add_click(Gtk::Widget &widget, ClickSlot on_pressed, ClickSlot on_released, Button const button, Gtk::PropagationPhase const phase, When const when)
Create a click gesture for the given widget; by default claim sequence.
Definition: controller.cpp:105
Custom widgets.
Definition: desktop.h:127
static Dialog::FillAndStroke * get_fill_and_stroke_panel(SPDesktop *desktop)
static UI::Widget::PopoverMenuItem * make_menu_item(Glib::ustring const &label, Slot slot, Args &&...args)
void on_popup_menu(Gtk::Widget &widget, PopupMenuSlot slot)
Connect slot to a widgetʼs key and button events that traditionally trigger a popup menu,...
Definition: popup-menu.cpp:61
std::optional< PopupMenuClick > PopupMenuOptionalClick
Optional: not present if popup wasnʼt triggered by click.
Definition: popup-menu.h:43
@ UNIT_TYPE_LINEAR
Definition: units.h:34
Glib::ustring format_classic(T const &... args)
@ FOR_STROKE
Definition: sp-gradient.h:68
static void append(std::vector< T > &target, std::vector< T > &&source)
Definition: shortcuts.cpp:515
void set_svg_cursor(Gtk::Widget &widget, std::string const &file_name, std::uint32_t const fill, std::uint32_t const stroke, double fill_opacity, double stroke_opacity)
Loads an SVG cursor from the specified file name, and sets it as the cursor of the given widget.
@ IMMEDIATE_MESSAGE
Definition: message.h:27
@ HORIZONTAL
The x-dimension (0).
Definition: rectangle.h:43
static cairo_user_data_key_t key
Definition: nr-svgfonts.cpp:46
int mode
Definition: parametrics.cpp:20
unsigned char * data
A replacement for GTK3ʼs Gtk::MenuItem, as removed in GTK4.
A replacement for GTK3ʼs Gtk::Menu, as removed in GTK4.
Piecewise< SBasis > log(Interval in)
Definition: pw-funcs.cpp:37
unsigned long mi
Definition: quantize.cpp:40
RGB rgb
Definition: quantize.cpp:36
Ocnode * parent
Definition: quantize.cpp:31
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition: repr-css.cpp:67
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition: repr-css.cpp:76
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition: repr-css.cpp:202
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition: repr-css.cpp:191
static void ss_selection_modified(Inkscape::Selection *selection, guint flags, gpointer data)
static void ss_subselection_changed(gpointer, gpointer data)
static constexpr int SELECTED_STYLE_SB_WIDTH
static constexpr int SELECTED_STYLE_FLAG_WIDTH
static constexpr std::array< double, 15 > _sw_presets
static void ss_selection_changed(Inkscape::Selection *, gpointer data)
static const Glib::ustring type_strings[][2][2]
static constexpr int SELECTED_STYLE_PLACE_WIDTH
static constexpr int SELECTED_STYLE_STROKE_WIDTH
static constexpr int SELECTED_STYLE_WIDTH
SVG <hatch> implementation.
TODO: insert short description here.
TODO: insert short description here.
SVG <pattern> implementation.
TODO: insert short description here.
SPStyle - a style object for SPItem objects.
void sp_svg_write_color(gchar *buf, unsigned const buflen, guint32 const rgba32)
Converts an RGBA32 colour to a CSS/SVG representation of the RGB portion of that colour.
Definition: svg-color.cpp:482
guint32 sp_svg_read_color(gchar const *str, guint32 const dfl)
Definition: svg-color.cpp:205
TODO: insert short description here.
std::unique_ptr< Toolbar >(* create)(SPDesktop *desktop)
Definition: toolbars.cpp:63