diff --git a/share/ui/toolbar-star.ui b/share/ui/toolbar-star.ui index ddaf932d719c46f648a0bbb32018558c23b6e8b0..d1704c343efb895475b39f83d53887f1171e00bc 100644 --- a/share/ui/toolbar-star.ui +++ b/share/ui/toolbar-star.ui @@ -29,6 +29,11 @@ True True + + 1000000 + 0.10 + 5 + start center @@ -195,6 +200,34 @@ + + + Length of side + + + 5 + L: + + + + + True + 7 + 0.000 + _length_adj + 0.10 + 3 + + + + + + + + + diff --git a/src/object/sp-star.cpp b/src/object/sp-star.cpp index de4e195d97824ff50916b8f20405569757841577..59ddcbbd1a6192df1e8fd3fe69718bcdbb437bc4 100644 --- a/src/object/sp-star.cpp +++ b/src/object/sp-star.cpp @@ -522,6 +522,31 @@ void SPStar::update_patheffect(bool write) { SPShape::update_patheffect(write); } +/** + * Calculate the average side length of the polygon. + * + * For spoked polygons (stars) this is the radius delta; for non-spokjed + * polygons this is the regular side length directly. + * + * @returns the average length of the polygon sides. + */ +double SPStar::getSideLength() const +{ + if (!flatsided) { + // Pointy star + std::cout << "getSideLength(star: " << std::abs(r[0] - r[1]) << ")\n"; + return std::abs(r[0] - r[1]); + } + double diameter = 0.0; + auto tr = i2doc_affine(); + for (gint i = 1; i < sides; i++) { + diameter += Geom::distance(sp_star_get_xy(this, SP_STAR_POINT_KNOT1, i - 1, false) * tr, + sp_star_get_xy(this, SP_STAR_POINT_KNOT1, i, false) * tr); + } + std::cout << "getSideLength(polygon: " << (diameter / sides) << ")\n"; + return diameter / sides; +} + /** * sp_star_get_xy: Get X-Y value as item coordinate system * @star: star item diff --git a/src/object/sp-star.h b/src/object/sp-star.h index 47abebf70b9487ee47650ccc77cda3b2a39b036b..2c8fb53200b2bcd0e3b26afd15db27a1ab9ccc70 100644 --- a/src/object/sp-star.h +++ b/src/object/sp-star.h @@ -38,11 +38,6 @@ public: double rounded; double randomized; -// CPPIFY: This derivation is a bit weird. -// parent_class = reinterpret_cast(g_type_class_ref(SP_TYPE_SHAPE)); -// So shouldn't star be derived from shape instead of polygon? -// What does polygon have that shape doesn't? - void build(SPDocument *document, Inkscape::XML::Node *repr) override; Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override; void set(SPAttr key, char const* value) override; @@ -55,6 +50,8 @@ public: void update_patheffect(bool write) override; void set_shape() override; Geom::Affine set_transform(Geom::Affine const& xform) override; + + double getSideLength() const; }; void sp_star_position_set (SPStar *star, int sides, Geom::Point center, double r1, double r2, double arg1, double arg2, bool isflat, double rounded, double randomized); diff --git a/src/ui/toolbar/star-toolbar.cpp b/src/ui/toolbar/star-toolbar.cpp index 27ef1109eacc02f4332b7520899b4382a5b0731c..cc348593efc466b38ae5b8443f30b301605763f3 100644 --- a/src/ui/toolbar/star-toolbar.cpp +++ b/src/ui/toolbar/star-toolbar.cpp @@ -41,7 +41,9 @@ #include "ui/icon-names.h" #include "ui/tools/star-tool.h" #include "ui/util.h" +#include "ui/widget/combo-tool-item.h" #include "ui/widget/spinbutton.h" +#include "ui/widget/unit-tracker.h" using Inkscape::DocumentUndo; @@ -59,6 +61,8 @@ StarToolbar::StarToolbar(Glib::RefPtr const &builder) , _spoke_item{get_derived_widget(builder, "_spoke_item")} , _roundedness_item{get_derived_widget(builder, "_roundedness_item")} , _randomization_item{get_derived_widget(builder, "_randomization_item")} + , _tracker{std::make_unique(Util::UNIT_TYPE_LINEAR)} + , _length_item{get_derived_widget(builder, "_length_item")} { bool is_flat_sided = Preferences::get()->getBool("/tools/shapes/star/isflatsided", false); @@ -111,6 +115,7 @@ StarToolbar::StarToolbar(Glib::RefPtr const &builder) setup_derived_spin_button(_spoke_item, "proportion", 0.5, &StarToolbar::proportion_value_changed); setup_derived_spin_button(_roundedness_item, "rounded", 0.0, &StarToolbar::rounded_value_changed); setup_derived_spin_button(_randomization_item, "randomized", 0.0, &StarToolbar::randomized_value_changed); + setup_derived_spin_button(_length_item, "length", 0.0, &StarToolbar::length_value_changed); // Flatsided checkbox _flat_item_buttons.push_back(&get_widget(builder, "flat_polygon_button")); @@ -128,6 +133,10 @@ StarToolbar::StarToolbar(Glib::RefPtr const &builder) _spoke_box.set_visible(!is_flat_sided); + auto unit_menu = _tracker->create_tool_item(_("Units"), ("")); + get_widget(builder, "unit_menu_box").append(*unit_menu); + _tracker->addAdjustment(_length_item.get_adjustment()->gobj()); + _initMenuBtns(); } @@ -166,6 +175,7 @@ void StarToolbar::setDesktop(SPDesktop *desktop) { if (_desktop) { _selection_changed_conn.disconnect(); + _selection_modified_conn.disconnect(); if (_repr) { _detachRepr(); @@ -177,10 +187,16 @@ void StarToolbar::setDesktop(SPDesktop *desktop) if (_desktop) { auto sel = _desktop->getSelection(); _selection_changed_conn = sel->connectChanged(sigc::mem_fun(*this, &StarToolbar::_selectionChanged)); + _selection_modified_conn = sel->connectChanged(sigc::mem_fun(*this, &StarToolbar::_selectionModified)); _selectionChanged(sel); // Synthesize an emission to trigger the update } } +void StarToolbar::setActiveUnit(Util::Unit const *unit) +{ + _tracker->setActiveUnit(unit); +} + void StarToolbar::side_mode_changed(int mode) { bool const flat = mode == 0; @@ -356,6 +372,25 @@ void StarToolbar::randomized_value_changed() } } +void StarToolbar::length_value_changed() +{ + if (!_blocker.pending() || _tracker->isUpdating()) { + // in turn, prevent listener from responding + auto guard = _blocker.block(); + + auto adj = _length_item.get_adjustment(); + auto value = Util::Quantity::convert(adj->get_value(), _tracker->getActiveUnit(), "px"); + for (auto item : _desktop->getSelection()->items()) { + if (auto star = cast(item)) { + auto scale = Geom::Scale(value / star->getSideLength()); + auto set_tmp = ObjectSet{_desktop}; + set_tmp.add(item); + set_tmp.scaleRelative(star->center, scale); + } + } + } +} + void StarToolbar::_setDefaults() { _batchundo = true; @@ -393,20 +428,46 @@ void StarToolbar::_selectionChanged(Selection *selection) int n_selected = 0; XML::Node *repr = nullptr; + double lengths = 0; for (auto item : selection->items()) { - if (is(item)) { + if (auto star = cast(item)) { n_selected++; repr = item->getRepr(); + lengths += star->getSideLength(); } } _mode_item.set_markup(n_selected == 0 ? _("New:") : _("Change:")); + _length_item.set_sensitive(n_selected > 0); + if (n_selected == 1) { _attachRepr(repr); _repr->synthesizeEvents(*this); // Fixme: BAD } + _selectionModified(selection); +} + +void StarToolbar::_selectionModified(Selection *selection) +{ + if (!_blocker.pending()|| _tracker->isUpdating()) { + auto guard = _blocker.block(); + auto length_adj = _length_item.get_adjustment(); + + int n_selected = 0; + double lengths = 0; + for (auto item : selection->items()) { + if (auto star = cast(item)) { + n_selected++; + lengths += star->getSideLength(); + } + } + if (n_selected > 0) { + auto value = Util::Quantity::convert(lengths / n_selected, "px", _tracker->getActiveUnit()); + length_adj->set_value(value); + } + } } void StarToolbar::notifyAttributeChanged(XML::Node &, GQuark name_, Util::ptr_shared, Util::ptr_shared) diff --git a/src/ui/toolbar/star-toolbar.h b/src/ui/toolbar/star-toolbar.h index d3dbd2726d67d95aefd84e321f73cbc97d7b99e5..9bad4f4659ab8c9fef3f82375c14b69f920ee0cd 100644 --- a/src/ui/toolbar/star-toolbar.h +++ b/src/ui/toolbar/star-toolbar.h @@ -45,6 +45,7 @@ namespace Tools { class ToolBase; } namespace Widget { class Label; class SpinButton; +class UnitTracker; } // namespace Widget } // namespace UI namespace XML { class Node; } @@ -61,6 +62,7 @@ public: ~StarToolbar() override; void setDesktop(SPDesktop *desktop) override; + void setActiveUnit(Util::Unit const *unit) override; private: StarToolbar(Glib::RefPtr const &builder); @@ -74,6 +76,9 @@ private: UI::Widget::SpinButton &_spoke_item; UI::Widget::SpinButton &_roundedness_item; UI::Widget::SpinButton &_randomization_item; + UI::Widget::SpinButton &_length_item; + + std::unique_ptr _tracker; XML::Node *_repr = nullptr; void _attachRepr(XML::Node *repr); @@ -82,6 +87,7 @@ private: bool _batchundo = false; OperationBlocker _blocker; sigc::connection _selection_changed_conn; + sigc::connection _selection_modified_conn; void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value, ValueChangedMemFun value_changed_mem_fun); @@ -90,8 +96,10 @@ private: void proportion_value_changed(); void rounded_value_changed(); void randomized_value_changed(); + void length_value_changed(); void _setDefaults(); void _selectionChanged(Selection *selection); + void _selectionModified(Selection *selection); void notifyAttributeChanged(XML::Node &node, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value) override; };