Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
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#include "colors/manager.h"
27#include "colors/xml-color.h"
28#include "desktop-style.h"
29#include "document-undo.h"
30#include "gradient-chemistry.h"
31#include "message-context.h"
32#include "selection.h"
33#include "style.h"
34
35#include "object/sp-hatch.h"
38#include "object/sp-namedview.h"
39#include "object/sp-pattern.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"
50#include "ui/widget/canvas.h"
55#include "util/units.h"
56#include "util/value-utils.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
69static const Glib::ustring (*get_type_strings())[2][2] {
70 // In order of PaintType enum: fill, stroke; label, tooltip.
71 static const Glib::ustring type_strings[][2][2] = {
72 // clang-format off
73 {{ _("N/A"), _("Nothing selected")},
74 { _("N/A"), _("Nothing selected")}},
75 {{C_("Fill", "<i>None</i>"), _("No fill, middle-click for black fill")},
76 {C_("Stroke", "<i>None</i>"), _("No stroke, middle-click for black stroke")}},
77 {{ _("<b>Unset</b>"), _("Unset fill")},
78 { _("<b>Unset</b>"), _("Unset stroke")}},
79 {{ _("≠"), _("Different fills")},
80 { _("≠"), _("Different strokes")}},
81 {{ _("Pattern"), _("Pattern (fill)")},
82 { _("Pattern"), _("Pattern (stroke)")}},
83 {{ _("Hatch"), _("Pattern (fill)")},
84 { _("Hatch"), _("Pattern (stroke)")}},
85 {{ _("<b>L</b>"), _("Linear gradient (fill)")},
86 { _("<b>L</b>"), _("Linear gradient (stroke)")}},
87 {{ _("<b>R</b>"), _("Radial gradient (fill)")},
88 { _("<b>R</b>"), _("Radial gradient (stroke)")}},
89 {{ _("<b>M</b>"), _("Mesh gradient (fill)")},
90 { _("<b>M</b>"), _("Mesh gradient (stroke)")}},
91 {{ _("<b>C</b>"), _("Flat color (fill)")},
92 { _("<b>C</b>"), _("Flat color (stroke)")}}
93 // clang-format on
94 };
95 return type_strings;
96}
97
98static void
104
105static void
106ss_selection_modified( Inkscape::Selection *selection, guint flags, gpointer data )
107{
108 // Don't update the style when dragging or doing non-style related changes
109 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG)) {
110 ss_selection_changed (selection, data);
111 }
112}
113
114namespace Inkscape::UI::Widget {
115
116struct SelectedStyleDropTracker final {
117 SelectedStyle* parent;
118 int item;
119};
120
121/* Drag and Drop */
125
126/* convenience function */
128
130{
131 set_name("SelectedStyle");
132 set_size_request (SELECTED_STYLE_WIDTH, -1);
133
134 grid = Gtk::make_managed<Gtk::Grid>();
135 grid->set_size_request(SELECTED_STYLE_WIDTH, -1);
136
137 // Fill and stroke
138 for (int i = 0; i <2; i++) {
139 label[i] = Gtk::make_managed<Gtk::Label>(i == 0 ? _("Fill:") : _("Stroke:"));
140 label[i]->set_halign(Gtk::Align::END);
141
142 // Multiple, Average, or Single
143 tag[i] = Gtk::make_managed<Gtk::Label>(); // "m", "a", or empty
144 tag[i]->set_size_request(SELECTED_STYLE_FLAG_WIDTH, -1);
145 tag[i]->set_name("Tag");
146
147 // Type of fill
148 type_label[i] = std::make_unique<Gtk::Label>(get_type_strings()[0][i][0]);
149 type_label[i]->set_hexpand(true);
150
151 // CSS sets width to 54.
152 gradient_preview[i] = std::make_unique<GradientImage>(nullptr);
153 gradient_preview[i]->set_visible(false);
154
155 color_preview[i] = std::make_unique<Inkscape::UI::Widget::ColorPreview>(0);
156 color_preview[i]->set_size_request(SELECTED_STYLE_PLACE_WIDTH, -1);
157 color_preview[i]->set_hexpand(true);
158 color_preview[i]->set_visible(false);
159
160 // Shows one or two children at a time.
161 swatch[i] = Gtk::make_managed<RotateableSwatch>(this, i);
162 swatch[i]->set_orientation(Gtk::Orientation::HORIZONTAL);
163 swatch[i]->set_hexpand(false);
164 swatch[i]->append(*type_label[i]);
165 swatch[i]->append(*gradient_preview[i]);
166 swatch[i]->append(*color_preview[i]);
167 swatch[i]->set_tooltip_text(get_type_strings()[0][i][1]);
168 swatch[i]->set_size_request(SELECTED_STYLE_PLACE_WIDTH, -1);
169
170 // Drag color from color palette, for example.
171 drop[i] = std::make_unique<SelectedStyleDropTracker>();
172 drop[i]->parent = this;
173 drop[i]->item = i;
174 auto target = Gtk::DropTarget::create(Util::GlibValue::type<Colors::Paint>(), Gdk::DragAction::COPY | Gdk::DragAction::MOVE);
175 target->signal_drop().connect([this, i] (Glib::ValueBase const &value, double, double) {
176 if (!dropEnabled[i]) {
177 return false;
178 }
179
180 auto const &tracker = *drop[i];
181 auto const paint = Util::GlibValue::get<Colors::Paint>(value);
182 auto const colorspec = std::visit(VariantVisitor{
183 [] (Colors::Color const &color) { return color.toString(false); },
184 [] (Colors::NoColor) -> std::string { return "none"; }
185 }, *paint);
186
187 auto const css = sp_repr_css_attr_new();
188 sp_repr_css_set_property_string(css, tracker.item == SS_FILL ? "fill" : "stroke", colorspec);
189 sp_desktop_set_style(tracker.parent->_desktop, css);
191
192 DocumentUndo::done(tracker.parent->_desktop->getDocument(), _("Drop color"), "");
193 return true;
194 }, true);
195 swatch[i]->add_controller(target);
196
197 auto const click = Gtk::GestureClick::create();
198 auto const callback = i == 0 ? sigc::mem_fun(*this, &SelectedStyle::on_fill_click)
199 : sigc::mem_fun(*this, &SelectedStyle::on_stroke_click);
200 click->set_button(0); // any
201 click->signal_released().connect(Controller::use_state(std::move(callback), *click));
202 swatch[i]->add_controller(click);
203
204 grid->attach(*label[i], 0, i, 1, 1);
205 grid->attach(*tag[i], 1, i, 1, 1);
206 grid->attach(*swatch[i], 2, i, 1, 1);
207
208 make_popup(static_cast<FillOrStroke>(i));
209 _mode[i] = SS_NA;
210 }
211
212 // Stroke width
213 stroke_width = Gtk::make_managed<Gtk::Label>("1");
214 stroke_width_rotateable = Gtk::make_managed<RotateableStrokeWidth>(this);
217 {
218 auto const click = Gtk::GestureClick::create();
219 click->set_button(0); // any
220 click->signal_released().connect(Controller::use_state(sigc::mem_fun(*this, &SelectedStyle::on_sw_click), *click));
221 stroke_width_rotateable->add_controller(click);
222 }
223 grid->attach(*stroke_width_rotateable, 3, 1, 1, 1);
224
225 // Opacity
227 opacity_label = Gtk::make_managed<Gtk::Label>(_("O:"));
228 opacity_adjustment = Gtk::Adjustment::create(100, 0.0, 100, 1.0, 10.0);
229 opacity_sb = Gtk::make_managed<Inkscape::UI::Widget::SpinButton>(0.02, 0);
230 opacity_sb->set_adjustment(opacity_adjustment);
231 opacity_sb->set_size_request(SELECTED_STYLE_SB_WIDTH);
232 opacity_sb->set_sensitive(false);
234
235 auto opacity_box = Gtk::make_managed<Gtk::Box>();
236 opacity_box->append(*opacity_label);
237 opacity_box->append(*opacity_sb);
238
239 auto const click = Gtk::GestureClick::create();
240 click->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
241 click->set_button(2); // middle
242 click->signal_pressed().connect([&click = *click](auto &&...) { click.set_state(Gtk::EventSequenceState::CLAIMED); });
243 click->signal_released().connect(Controller::use_state(sigc::mem_fun(*this, &SelectedStyle::on_opacity_click), *click));
244 opacity_box->add_controller(click);
245
246 on_popup_menu(*opacity_box, sigc::mem_fun(*this, &SelectedStyle::on_opacity_popup));
247 opacity_sb->signal_value_changed().connect(sigc::mem_fun(*this, &SelectedStyle::on_opacity_changed));
248
249 grid->attach(*opacity_box, 4, 0, 1, 2);
250
251 grid->set_column_spacing(4);
252 setChild(grid);
253
255}
256
258{
259 if (_desktop) {
260 selection_changed_connection.disconnect();
262 }
263
265
266 if (_desktop) {
267 auto selection = desktop->getSelection();
268
270 sigc::bind(&ss_selection_changed, this)
271 );
272 selection_modified_connection = selection->connectModified(
273 sigc::bind(&ss_selection_modified, this)
274 );
275 update();
276
278 }
279}
280
283 sp_repr_css_set_property (css, "fill", "none");
284 sp_desktop_set_style (_desktop, css, true, true);
286 DocumentUndo::done(_desktop->getDocument(), _("Remove fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
287}
288
291 sp_repr_css_set_property (css, "stroke", "none");
292 sp_desktop_set_style (_desktop, css, true, true);
294 DocumentUndo::done(_desktop->getDocument(), _("Remove stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
295}
296
300 sp_desktop_set_style (_desktop, css, true, true);
302 DocumentUndo::done(_desktop->getDocument(), _("Unset fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
303
304}
305
309 sp_repr_css_unset_property (css, "stroke-opacity");
310 sp_repr_css_unset_property (css, "stroke-width");
311 sp_repr_css_unset_property (css, "stroke-miterlimit");
312 sp_repr_css_unset_property (css, "stroke-linejoin");
313 sp_repr_css_unset_property (css, "stroke-linecap");
314 sp_repr_css_unset_property (css, "stroke-dashoffset");
315 sp_repr_css_unset_property (css, "stroke-dasharray");
316 sp_desktop_set_style (_desktop, css, true, true);
318 DocumentUndo::done(_desktop->getDocument(), _("Unset stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
319}
320
323 sp_repr_css_set_property (css, "fill-opacity", "1");
326 DocumentUndo::done(_desktop->getDocument(), _("Make fill opaque"), INKSCAPE_ICON("dialog-fill-and-stroke"));
327}
328
331 sp_repr_css_set_property (css, "stroke-opacity", "1");
334 DocumentUndo::done(_desktop->getDocument(), _("Make fill opaque"), INKSCAPE_ICON("dialog-fill-and-stroke"));
335}
336
339 auto color = sp_desktop_get_color(_desktop, true);
340 sp_repr_css_set_property_string(css, "fill", color ? color->toString() : "none");
343 DocumentUndo::done(_desktop->getDocument(), _("Apply last set color to fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
344}
345
348 auto color = sp_desktop_get_color(_desktop, false);
349 sp_repr_css_set_property_string(css, "fill", color ? color->toString() : "none");
352 DocumentUndo::done(_desktop->getDocument(), _("Apply last set color to stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
353}
354
360 DocumentUndo::done(_desktop->getDocument(), _("Apply last selected color to fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
361}
362
368 DocumentUndo::done(_desktop->getDocument(), _("Apply last selected color to stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
369}
370
373 auto color = _thisselected[SS_FILL];
376 return;
377
378 }
379
380 if (_mode[SS_FILL] != SS_COLOR) return;
381 color->invert();
382 sp_repr_css_set_property_string(css, "fill", color->toString());
385 DocumentUndo::done(_desktop->getDocument(), _("Invert fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
386}
387
390 auto color = _thisselected[SS_STROKE];
393 return;
394 }
395 if (_mode[SS_STROKE] != SS_COLOR) return;
396 color->invert();
397 sp_repr_css_set_property_string(css, "stroke", color->toString());
400 DocumentUndo::done(_desktop->getDocument(), _("Invert stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
401}
402
405 sp_repr_css_set_property (css, "fill", "#ffffff");
406 sp_repr_css_set_property (css, "fill-opacity", "1");
409 DocumentUndo::done(_desktop->getDocument(), _("White fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
410}
411
414 sp_repr_css_set_property (css, "stroke", "#ffffff");
415 sp_repr_css_set_property (css, "stroke-opacity", "1");
418 DocumentUndo::done(_desktop->getDocument(), _("White stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
419}
420
423 sp_repr_css_set_property (css, "fill", "#000000");
424 sp_repr_css_set_property (css, "fill-opacity", "1.0");
427 DocumentUndo::done(_desktop->getDocument(), _("Black fill"), INKSCAPE_ICON("dialog-fill-and-stroke"));
428}
429
432 sp_repr_css_set_property (css, "stroke", "#000000");
433 sp_repr_css_set_property (css, "stroke-opacity", "1.0");
436 DocumentUndo::done(_desktop->getDocument(), _("Black stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
437}
438
440 if (_mode[SS_FILL] == SS_COLOR) {
441 auto text = _thisselected[SS_FILL]->toString();
442 if (!text.empty()) {
443 auto const display = Gdk::Display::get_default();
444 display->get_primary_clipboard()->set_text(text);
445 }
446 }
447}
448
450 if (_mode[SS_STROKE] == SS_COLOR) {
451 auto text = _thisselected[SS_STROKE]->toString();
452 if (!text.empty()) {
453 auto const display = Gdk::Display::get_default();
454 display->get_primary_clipboard()->set_text(text);
455 }
456 }
457}
458
459void SelectedStyle::_on_paste_callback(Glib::RefPtr<Gio::AsyncResult>& result, Glib::ustring typepaste)
460{
461 auto const display = Gdk::Display::get_default();
462 Glib::RefPtr<Gdk::Clipboard> refClipboard = display->get_primary_clipboard();
463 // Parse the clipboard text as if it was a color string.
464 Glib::ustring text;
465 try {
466 text = refClipboard->read_text_finish(result);
467 } catch (Glib::Error const &err) {
468 std::cout << "Pasting text failed: " << err.what() << std::endl;
469 return;
470 }
471 if (auto color = Inkscape::Colors::Color::parse(text)) {
473 sp_repr_css_set_property_string(css, "fill", color->toString());
476 DocumentUndo::done(_desktop->getDocument(), typepaste == "fill" ? _("Paste fill") : _("Paste stroke"), INKSCAPE_ICON("dialog-fill-and-stroke"));
477 }
478}
479
481 auto const display = Gdk::Display::get_default();
482 Glib::RefPtr<Gdk::Clipboard> refClipboard = display->get_primary_clipboard();
483 refClipboard->read_text_async(sigc::bind(sigc::mem_fun(*this, &SelectedStyle::_on_paste_callback), "fill"));
484}
485
487 auto const display = Gdk::Display::get_default();
488 Glib::RefPtr<Gdk::Clipboard> refClipboard = display->get_primary_clipboard();
489 refClipboard->read_text_async(sigc::bind(sigc::mem_fun(*this, &SelectedStyle::_on_paste_callback), "stroke"));
490}
491
495
498 fs->showPageFill();
499}
500
503 fs->showPageStrokePaint();
504}
505
506Gtk::EventSequenceState SelectedStyle::on_fill_click(Gtk::GestureClick const &click, int n_press, double /*x*/,
507 double /*y*/)
508{
509 auto const button = click.get_current_button();
510 if (button == 1 && !dragging) { // click, open fill&stroke
512 fs->showPageFill();
513 } else if (button == 3) { // right-click, popup menu
514 setPopover(_popup[SS_FILL].get());
515 _popup[SS_FILL]->popup_at_center(*swatch[SS_FILL]);
516 } else if (button == 2) { // middle click, toggle none/lastcolor
517 if (_mode[SS_FILL] == SS_NONE) {
519 } else {
521 }
522 }
523 return Gtk::EventSequenceState::CLAIMED;
524}
525
526Gtk::EventSequenceState SelectedStyle::on_stroke_click(Gtk::GestureClick const &click, int n_press, double /*x*/,
527 double /*y*/)
528{
529 auto const button = click.get_current_button();
530 if (button == 1 && !dragging) { // click, open fill&stroke
532 fs->showPageStrokePaint();
533 } else if (button == 3) { // right-click, popup menu
535 _popup[SS_STROKE]->popup_at_center(*swatch[SS_STROKE]);
536 } else if (button == 2) { // middle click, toggle none/lastcolor
537 if (_mode[SS_STROKE] == SS_NONE) {
539 } else {
541 }
542 }
543 return Gtk::EventSequenceState::CLAIMED;
544}
545
546Gtk::EventSequenceState SelectedStyle::on_sw_click(Gtk::GestureClick const &click, int n_press, double, double)
547{
548 auto const button = click.get_current_button();
549 if (button == 1 && !dragging) { // click, open fill&stroke
551 fs->showPageStrokeStyle();
552 } else if (button == 3) { // right-click, popup menu
553 auto const it = std::find_if(_unit_mis.cbegin(), _unit_mis.cend(), [this] (auto mi) {
554 return mi->get_label() == _sw_unit->abbr;
555 });
556 if (it != _unit_mis.cend()) (*it)->set_active(true);
557
558 setPopover(_popup_sw.get());
559 _popup_sw->popup_at_center(*stroke_width);
560 } else if (button == 2) { // middle click, toggle none/lastwidth?
561 //
562 }
563 return Gtk::EventSequenceState::CLAIMED;
564}
565
566Gtk::EventSequenceState
567SelectedStyle::on_opacity_click(Gtk::GestureClick const & /*click*/,
568 int /*n_press*/, double /*x*/, double /*y*/)
569{
570 const char* opacity = opacity_sb->get_value() < 50? "0.5" : (opacity_sb->get_value() == 100? "0" : "1");
572 sp_repr_css_set_property (css, "opacity", opacity);
575 DocumentUndo::done(_desktop->getDocument(), _("Change opacity"), INKSCAPE_ICON("dialog-fill-and-stroke"));
576 return Gtk::EventSequenceState::CLAIMED;
577}
578
579template <typename Slot, typename ...Args>
580static UI::Widget::PopoverMenuItem *make_menu_item(Glib::ustring const &label, Slot slot,
581 Args &&...args)
582{
583 auto const item = Gtk::make_managed<UI::Widget::PopoverMenuItem>(std::forward<Args>(args)...);
584 item->set_child(*Gtk::make_managed<Gtk::Label>(label, Gtk::Align::START, Gtk::Align::START));
585 item->signal_activate().connect(std::move(slot));
586 return item;
587};
588
590{
591 _popup[i] = std::make_unique<UI::Widget::PopoverMenu>(Gtk::PositionType::TOP);
592
593 auto const add_item = [&](Glib::ustring const & fill_label, auto const fill_method,
594 Glib::ustring const &stroke_label, auto const stroke_method)
595 {
596 auto const &label = i == SS_FILL || stroke_label.empty() ? fill_label : stroke_label ;
597 auto const method = i == SS_FILL || stroke_method == nullptr ? fill_method : stroke_method;
598 auto const item = make_menu_item(label, sigc::mem_fun(*this, method));
599 _popup[i]->append(*item);
600 return item;
601 };
602
603 add_item(_("Edit Fill..." ) , &SelectedStyle:: on_fill_edit ,
604 _("Edit Stroke...") , &SelectedStyle::on_stroke_edit );
605
606 _popup[i]->append_separator();
607
608
609 add_item(_("Last Set Color") , &SelectedStyle:: on_fill_lastused ,
611 add_item(_("Last Selected Color") , &SelectedStyle:: on_fill_lastselected,
613
614 _popup[i]->append_separator();
615
616 add_item(_("Invert") , &SelectedStyle:: on_fill_invert ,
618
619 _popup[i]->append_separator();
620
621 add_item(_("White") , &SelectedStyle:: on_fill_white ,
623 add_item(_("Black") , &SelectedStyle:: on_fill_black ,
625
626 _popup[i]->append_separator();
627
628 _popup_copy[i] = add_item(
629 _("Copy Color") , &SelectedStyle:: on_fill_copy ,
631 _popup_copy[i]->set_sensitive(false);
632
633 add_item(_("Paste Color") , &SelectedStyle:: on_fill_paste ,
635 add_item(_("Swap Fill and Stroke"), &SelectedStyle::on_fillstroke_swap ,
636 {} , nullptr );
637
638 _popup[i]->append_separator();
639
640 add_item(_("Make Fill Opaque" ) , &SelectedStyle:: on_fill_opaque ,
641 _("Make Stroke Opaque") , &SelectedStyle::on_stroke_opaque );
642 //TRANSLATORS COMMENT: unset is a verb here
643 add_item(_("Unset Fill" ) , &SelectedStyle:: on_fill_unset ,
644 _("Unset Stroke") , &SelectedStyle::on_stroke_unset );
645 add_item(_("Remove Fill" ) , &SelectedStyle:: on_fill_remove ,
646 _("Remove Stroke") , &SelectedStyle::on_stroke_remove );
647}
648
650{
651 _popup_sw = std::make_unique<UI::Widget::PopoverMenu>(Gtk::PositionType::TOP);
652
653 _popup_sw->append_section_label(_("<b>Stroke Width</b>"));
654
655 _popup_sw->append_separator();
656
657 _popup_sw->append_section_label(_("Unit"));
658 Gtk::CheckButton *group = nullptr;
659 auto const &unit_table = Util::UnitTable::get();
660 for (auto const &[key, value] : unit_table.units(Inkscape::Util::UNIT_TYPE_LINEAR)) {
661 auto const item = Gtk::make_managed<UI::Widget::PopoverMenuItem>();
662 auto const radio = Gtk::make_managed<Gtk::CheckButton>(key);
663 if (!group) {
664 group = radio;
665 } else {
666 radio->set_group(*group);
667 }
668 item->set_child(*radio);
669 _unit_mis.push_back(radio);
670 auto const u = unit_table.getUnit(key);
671 item->signal_activate().connect(
672 sigc::bind(sigc::mem_fun(*this, &SelectedStyle::on_popup_units), u));
673 _popup_sw->append(*item);
674 }
675
676 _popup_sw->append_separator();
677
678 _popup_sw->append_section_label(_("Width"));
679 for (std::size_t i = 0; i < _sw_presets.size(); ++i) {
681 sigc::bind(sigc::mem_fun(*this, &SelectedStyle::on_popup_preset), i)));
682 }
683
684 _popup_sw->append_separator();
685
686 _popup_sw->append(*make_menu_item(_("Remove Stroke"),
687 sigc::mem_fun(*this, &SelectedStyle::on_stroke_remove)));
688}
689
691 _sw_unit = unit;
692 update();
693}
694
697 gdouble w;
698 if (_sw_unit) {
700 } else {
701 w = _sw_presets[i];
702 }
704 os << w;
705 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
706 // FIXME: update dash patterns!
709 DocumentUndo::done(_desktop->getDocument(), _("Change stroke width"), INKSCAPE_ICON("swatches"));
710}
711
713{
714 if (_desktop) {
715 _desktop->getCanvas()->grab_focus();
716 }
717}
718
719void
721{
722 if (_desktop == nullptr)
723 return;
724
725 // Create temporary style
726 SPStyle query(_desktop->getDocument());
727
728 for (int i = SS_FILL; i <= SS_STROKE; i++) {
729
730 // New
731 type_label[i]->show(); // Used by all types except solid color.
732 gradient_preview[i]->set_visible(false);
733 color_preview[i]->set_visible(false);
734
735 _mode[i] = SS_NA;
736 _paintserver_id[i].clear();
737 _popup_copy[i]->set_sensitive(false);
738
739 // Query style from desktop. This returns a result flag and fills query with the style of
740 // subselection, if any, or selection.
742 (i == SS_FILL) ?
745
746 switch (result) {
748
749 tag[i]->set_markup("");
750
751 type_label[i]->set_markup(get_type_strings()[SS_NA][i][0]);
752 swatch[i]->set_tooltip_text(get_type_strings()[SS_NA][i][1]);
753
754 if (dropEnabled[i]) {
755 dropEnabled[i] = false;
756 }
757 break;
761 dropEnabled[i] = true;
762
763 auto paint = i == SS_FILL ? query.fill.upcast() : query.stroke.upcast();
764 double opacity = i == SS_FILL ? query.fill_opacity : query.stroke_opacity;
765 if (paint->set && paint->isPaintserver()) {
766 SPPaintServer *server = (i == SS_FILL)? SP_STYLE_FILL_SERVER (&query) : SP_STYLE_STROKE_SERVER (&query);
767 if ( server ) {
768 Inkscape::XML::Node *srepr = server->getRepr();
769 _paintserver_id[i] += "url(#";
770 _paintserver_id[i] += srepr->attribute("id");
771 _paintserver_id[i] += ")";
772
773 if (is<SPLinearGradient>(server)) {
774 auto vector = cast<SPGradient>(server)->getVector();
775
776 type_label[i]->set_markup( get_type_strings()[SS_LGRADIENT][i][0]);
777 swatch[i]->set_tooltip_text(get_type_strings()[SS_LGRADIENT][i][1]);
778 gradient_preview[i]->set_gradient(vector);
779 gradient_preview[i]->show();
780
781 _mode[i] = SS_LGRADIENT;
782 } else if (is<SPRadialGradient>(server)) {
783 auto vector = cast<SPGradient>(server)->getVector();
784
785 type_label[i]->set_markup( get_type_strings()[SS_RGRADIENT][i][0]);
786 swatch[i]->set_tooltip_text(get_type_strings()[SS_RGRADIENT][i][1]);
787 gradient_preview[i]->set_gradient(vector);
788 gradient_preview[i]->show();
789
790 _mode[i] = SS_RGRADIENT;
791 } else if (is<SPMeshGradient>(server)) {
792 auto array = cast<SPGradient>(server)->getArray();
793
794 type_label[i]->set_markup( get_type_strings()[SS_MGRADIENT][i][0]);
795 swatch[i]->set_tooltip_text(get_type_strings()[SS_MGRADIENT][i][1]);
796 gradient_preview[i]->set_gradient(array);
797 gradient_preview[i]->show();
798
799 _mode[i] = SS_MGRADIENT;
800 } else if (is<SPPattern>(server)) {
801 type_label[i]->set_markup( get_type_strings()[SS_PATTERN][i][0]);
802 swatch[i]->set_tooltip_text(get_type_strings()[SS_PATTERN][i][1]);
803
804 _mode[i] = SS_PATTERN;
805 } else if (is<SPHatch>(server)) {
806 type_label[i]->set_markup( get_type_strings()[SS_HATCH][i][0]);
807 swatch[i]->set_tooltip_text(get_type_strings()[SS_HATCH][i][1]);
808
809 _mode[i] = SS_HATCH;
810 }
811 } else {
812 g_warning ("file %s: line %d: Unknown paint server", __FILE__, __LINE__);
813 }
814 } else if (paint->set && paint->isColor()) {
815 auto color = paint->getColor();
816 color.addOpacity(opacity);
817
819 _thisselected[i] = color; // include opacity
820
821 // No type_label.
822 swatch[i]->set_tooltip_text(get_type_strings()[SS_COLOR][i][1] + ": " + color.toString() +
823 _(", drag to adjust, middle-click to remove"));
824 type_label[i]->set_visible(false);
825 color_preview[i]->setRgba32(color.toRGBA());
826 color_preview[i]->show();
827
828 _mode[i] = SS_COLOR;
829 _popup_copy[i]->set_sensitive(true);
830 } else if (paint->set && paint->isNone()) {
831 type_label[i]->set_markup(get_type_strings()[ SS_NONE][i][0]);
832 swatch[i]->set_tooltip_text(get_type_strings()[SS_NONE][i][1]);
833 _mode[i] = SS_NONE;
834 } else if (!paint->set) {
835 type_label[i]->set_markup(get_type_strings()[ SS_UNSET][i][0]);
836 swatch[i]->set_tooltip_text(get_type_strings()[SS_UNSET][i][1]);
837
838 _mode[i] = SS_UNSET;
839 }
840
842 // TRANSLATORS: A means "Averaged"
843 tag[i]->set_markup("<b>a</b>");
844 tag[i]->set_tooltip_text(i == 0 ?
845 _("Fill is averaged over selected objects") :
846 _("Stroke is averaged over selected objects"));
847
848 } else if (result == QUERY_STYLE_MULTIPLE_SAME) {
849 // TRANSLATORS: M means "Multiple"
850 tag[i]->set_markup("<b>m</b>");
851 tag[i]->set_tooltip_text(i == 0 ?
852 _("Multiple selected objects have same fill") :
853 _("Multiple selected objects have same stroke"));
854 } else {
855 tag[i]->set_markup("");
856 tag[i]->set_tooltip_text("");
857 }
858 break;
859 }
860
862 type_label[i]->set_markup(get_type_strings()[ SS_MANY][i][0]);
863 swatch[i]->set_tooltip_text(get_type_strings()[SS_MANY][i][1]);
864
865 _mode[i] = SS_MANY;
866 break;
867 default:
868 break;
869 }
870 }
871
872// Now query opacity
874
875 switch (result) {
877 opacity_sb->set_tooltip_text(_("Nothing selected"));
878 opacity_sb->set_sensitive(false);
879 break;
883 opacity_sb->set_tooltip_markup(_("<b>Opacity (%)</b>\nMiddle-click cycles through 0%, 50%, 100%"));
884
885 if (_opacity_blocked) break;
886
887 _opacity_blocked = true;
888 opacity_sb->set_sensitive(true);
889 opacity_adjustment->set_value(SP_SCALE24_TO_FLOAT(query.opacity.value) * 100);
890 _opacity_blocked = false;
891 break;
892 }
893
894// Now query stroke_width
896 switch (result_sw) {
898 stroke_width->set_markup("");
900 break;
904 {
905 if (query.stroke_extensions.hairline) {
906 stroke_width->set_markup(_("Hairline"));
907 stroke_width->set_tooltip_text(_("Stroke width: Hairline"));
908 } else {
909 double w;
910 if (_sw_unit) {
912 } else {
913 w = query.stroke_width.computed;
914 }
916
917 {
918 gchar *str = g_strdup_printf(" %#.3g", w);
919 if (str[strlen(str) - 1] == ',' || str[strlen(str) - 1] == '.') {
920 str[strlen(str)-1] = '\0';
921 }
922 stroke_width->set_markup(str);
923 g_free (str);
924 }
925 {
926 gchar *str = g_strdup_printf(_("Stroke width: %.5g%s%s"),
927 w,
928 _sw_unit? _sw_unit->abbr.c_str() : "px",
929 (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED)?
930 _(" (averaged)") : "");
931 stroke_width->set_tooltip_text(str);
932 g_free (str);
933 }
934 }
935 break;
936 }
937 default:
938 break;
939 }
940}
941
944void SelectedStyle::opacity_05() {opacity_sb->set_value(50);}
946void SelectedStyle::opacity_1() {opacity_sb->set_value(100);}
947
949{
950 _popup_opacity = std::make_unique<UI::Widget::PopoverMenu>(Gtk::PositionType::TOP);
951 auto const add_item = [&] (Glib::ustring const &label, auto method) {
952 _popup_opacity->append(*make_menu_item(label, sigc::mem_fun(*this, method)));
953 };
954 add_item(_("0% (Transparent)"), &SelectedStyle::opacity_0 );
955 add_item("25%", &SelectedStyle::opacity_025);
956 add_item("50%", &SelectedStyle::opacity_05 );
957 add_item("75%", &SelectedStyle::opacity_075);
958 add_item(_("100% (Opaque)" ), &SelectedStyle::opacity_1 );
959}
960
962{
964 _popup_opacity->popup_at_center(*opacity_sb);
965 return true;
966}
967
969{
970 g_return_if_fail(_desktop); // TODO this shouldn't happen!
971 if (_opacity_blocked) {
972 return;
973 }
974 _opacity_blocked = true;
977 os << CLAMP ((opacity_adjustment->get_value() / 100), 0.0, 1.0);
978 sp_repr_css_set_property (css, "opacity", os.str().c_str());
981 DocumentUndo::maybeDone(_desktop->getDocument(), "fillstroke:opacity", _("Change opacity"), INKSCAPE_ICON("dialog-fill-and-stroke"));
982 _opacity_blocked = false;
983}
984
985/* ============================================= RotateableSwatch */
986
988 : fillstroke(mode)
989 , parent(parent)
990{
991 set_name("RotatableSwatch");
992}
993
995
996std::pair<double, double> RotateableSwatch::color_adjust(Colors::Color const &cc, double by, guint modifier)
997{
998 static int map[4] = {0,2,1,3};
999 auto hsl = *cc.converted(Colors::Space::Type::HSL);
1000 int ch = map[modifier];
1001 double old = hsl[ch];
1002
1003 hsl.set(ch, old + by * (by > 0 ? (1 - hsl[ch]) : hsl[ch]));
1004 hsl.normalize();
1005 double diff = hsl[ch] - old;
1006 hsl.convert(cc.getSpace());
1007
1009 if (modifier == 3) { // alpha
1010 sp_repr_css_set_property_double(css, (fillstroke == SS_FILL) ? "fill-opacity" : "stroke-opacity", hsl.getOpacity());
1011 } else {
1012 sp_repr_css_set_property_string(css, (fillstroke == SS_FILL) ? "fill" : "stroke", hsl.toString(false));
1013 }
1016 return {old, diff};
1017}
1018
1019void RotateableSwatch::do_motion(double by, guint modifier)
1020{
1021 if (parent->_mode[fillstroke] != SS_COLOR) {
1022 return;
1023 }
1024 parent->dragging = true;
1025
1026 if (!scrolling && modifier != cursor_state) {
1027 std::string cursor_filename = "adjust_hue.svg";
1028 if (modifier == 2) {
1029 cursor_filename = "adjust_saturation.svg";
1030 } else if (modifier == 1) {
1031 cursor_filename = "adjust_lightness.svg";
1032 } else if (modifier == 3) {
1033 cursor_filename = "adjust_alpha.svg";
1034 }
1035 set_svg_cursor(*this, cursor_filename);
1036
1038 }
1039
1040 if (!startcolor) {
1042 }
1043
1044 auto ret = color_adjust(*startcolor, by, modifier);
1045
1046 if (modifier == 3) { // alpha
1047 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust alpha")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1050 _("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"),
1051 ret.first - ret.second, ret.first, ret.second);
1052
1053 } else if (modifier == 2) { // saturation
1054 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust saturation")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1057 _("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"),
1058 ret.first - ret.second, ret.first, ret.second);
1059
1060 } else if (modifier == 1) { // lightness
1061 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust lightness")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1064 _("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"),
1065 ret.first - ret.second, ret.first, ret.second);
1066
1067 } else { // hue
1068 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust hue")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1071 _("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"),
1072 ret.first - ret.second, ret.first, ret.second);
1073 }
1074}
1075
1076void RotateableSwatch::do_scroll(double by, guint modifier)
1077{
1078 do_motion(by/30.0, modifier);
1079 do_release(by/30.0, modifier);
1080}
1081
1082void RotateableSwatch::do_release(double by, guint modifier)
1083{
1085 return;
1086
1087 parent->dragging = false;
1089
1090 if (cursor_state != -1) {
1091 if (auto window = dynamic_cast<Gtk::Window *>(get_root())) {
1092 window->set_cursor(); // Use parent window cursor.
1093 }
1094 cursor_state = -1;
1095 }
1096
1097 if (modifier == 3) { // alpha
1098 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust alpha"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1099
1100 } else if (modifier == 2) { // saturation
1101 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust saturation"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1102
1103 } else if (modifier == 1) { // lightness
1104 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust lightness"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1105
1106 } else { // hue
1107 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, ("Adjust hue"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1108 }
1109
1110 if (!strcmp(undokey, "ssrot1")) {
1111 undokey = "ssrot2";
1112 } else {
1113 undokey = "ssrot1";
1114 }
1115
1116 parent->getDesktop()->getTool()->message_context->clear();
1117 startcolor.reset();
1118}
1119
1120/* ============================================= RotateableStrokeWidth */
1121
1123 parent(parent),
1124 startvalue(0),
1125 startvalue_set(false),
1126 undokey("swrot1")
1127{
1128}
1129
1131
1132double
1133RotateableStrokeWidth::value_adjust(double current, double by, guint /*modifier*/, bool final)
1134{
1135 double newval;
1136 // by is -1..1
1137 double max_f = 50; // maximum width is (current * max_f), minimum - zero
1138 newval = current * (std::exp(std::log(max_f-1) * (by+1)) - 1) / (max_f-2);
1139
1141 if (final && newval < 1e-6) {
1142 // if dragged into zero and this is the final adjust on mouse release, delete stroke;
1143 // if it's not final, leave it a chance to increase again (which is not possible with "none")
1144 sp_repr_css_set_property (css, "stroke", "none");
1145 } else {
1146 newval = Inkscape::Util::Quantity::convert(newval, parent->_sw_unit, "px");
1148 os << newval;
1149 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
1150 }
1151
1154 return newval - current;
1155}
1156
1157void RotateableStrokeWidth::do_motion(double by, guint modifier)
1158{
1159 // if this is the first motion after a mouse grab, remember the current width
1160 if (!startvalue_set) {
1162 // if it's 0, adjusting (which uses multiplication) will not be able to change it, so we
1163 // cheat and provide a non-zero value
1164 if (startvalue == 0)
1165 startvalue = 1;
1166 startvalue_set = true;
1167 }
1168 parent->dragging = true;
1169
1170 if (modifier == 3) { // Alt, do nothing
1171 } else {
1172 double diff = value_adjust(startvalue, by, modifier, false);
1173 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust stroke width")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1174 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);
1175 }
1176}
1177
1178void RotateableStrokeWidth::do_release(double by, guint modifier)
1179{
1180 parent->dragging = false;
1181
1182 if (modifier == 3) { // do nothing
1183
1184 } else {
1185 value_adjust(startvalue, by, modifier, true);
1186 startvalue_set = false;
1187 DocumentUndo::maybeDone(parent->getDesktop()->getDocument(), undokey, (_("Adjust stroke width")), INKSCAPE_ICON("dialog-fill-and-stroke"));
1188 }
1189
1190 if (!strcmp(undokey, "swrot1")) {
1191 undokey = "swrot2";
1192 } else {
1193 undokey = "swrot1";
1194 }
1195 parent->getDesktop()->getTool()->message_context->clear();
1196}
1197
1198void RotateableStrokeWidth::do_scroll(double by, guint modifier)
1199{
1200 do_motion(by/10.0, modifier);
1201 do_release(by / 10.0, modifier);
1202 startvalue_set = false;
1203}
1204
1206{
1207 desktop->getContainer()->new_dialog("FillStroke");
1208 return dynamic_cast<Dialog::FillAndStroke *>(desktop->getContainer()->get_dialog("FillStroke"));
1209}
1210
1211} // namespace Inkscape::UI::Widget
1212
1213/*
1214 Local Variables:
1215 mode:c++
1216 c-file-style:"stroustrup"
1217 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1218 indent-tabs-mode:nil
1219 fill-column:99
1220 End:
1221*/
1222// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Inkscape canvas widget.
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
std::optional< Color > converted(Color const &other) const
Return a copy of this color converted to the same format as the other color.
Definition color.cpp:189
std::shared_ptr< Space::AnySpace > const & getSpace() const
Definition color.h:39
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void maybeDone(SPDocument *document, const gchar *keyconst, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
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:193
void setPopover(Gtk::Popover *popover)
Definition popover-bin.h:24
void setChild(Gtk::Widget *child)
Definition popover-bin.h:23
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)
std::optional< Colors::Color > startcolor
void do_motion(double by, guint state) override
void do_release(double by, guint state) override
std::pair< double, double > color_adjust(Colors::Color const &cc, double by, 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)
sigc::scoped_connection selection_changed_connection
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)
std::optional< Colors::Color > _thisselected[2]
RotateableStrokeWidth * stroke_width_rotateable
std::unique_ptr< GradientImage > gradient_preview[2]
bool on_opacity_popup(PopupMenuOptionalClick)
std::optional< Colors::Color > _lastselected[2]
Gtk::EventSequenceState on_stroke_click(Gtk::GestureClick const &click, int n_press, double x, double y)
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
sigc::scoped_connection selection_modified_connection
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)
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:588
static UnitTable & get()
Definition units.cpp:441
Glib::ustring abbr
Definition units.h:81
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.
To do: update description of desktop.
Definition desktop.h:149
Inkscape::UI::Widget::Canvas * getCanvas() const
Definition desktop.h:190
SPDocument * getDocument() const
Definition desktop.h:189
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::UI::Dialog::DialogContainer * getContainer()
Definition desktop.cpp:335
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
Inkscape::Util::Unit const * display_units
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
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.
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
std::optional< Color > 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
@ QUERY_STYLE_PROPERTY_STROKEWIDTH
@ QUERY_STYLE_PROPERTY_FILL
@ QUERY_STYLE_PROPERTY_MASTEROPACITY
@ QUERY_STYLE_MULTIPLE_DIFFERENT
@ QUERY_STYLE_SINGLE
@ QUERY_STYLE_NOTHING
@ QUERY_STYLE_MULTIPLE_AVERAGED
@ QUERY_STYLE_MULTIPLE_SAME
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
static char const *const parent
Definition dir-util.cpp:70
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
Glib::ustring label
Interface for locally managing a current status message.
auto use_state(Slot &&slot)
Definition controller.h:43
Custom widgets.
Definition desktop.h:126
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,...
std::optional< PopupMenuClick > PopupMenuOptionalClick
Optional: not present if popup wasnʼt triggered by click.
Definition popup-menu.h:41
@ UNIT_TYPE_LINEAR
Definition units.h:32
Glib::ustring format_classic(T const &... args)
void set_svg_cursor(Gtk::Widget &widget, std::string const &file_name, std::optional< Colors::Color > fill, std::optional< Colors::Color > stroke)
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
static cairo_user_data_key_t key
int mode
A replacement for GTK3ʼs Gtk::MenuItem, as removed in GTK4.
A replacement for GTK3ʼs Gtk::Menu, as removed in GTK4.
unsigned long mi
Definition quantize.cpp:40
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_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
Definition repr-css.cpp:224
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_set_property_string(SPCSSAttr *css, char const *name, std::string const &value)
Set a style property to a standard string.
Definition repr-css.cpp:235
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 const Glib::ustring(* get_type_strings())[2]
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 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.
static const Point data[]
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
Wrapper for the GLib value API.