diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 1bb2d5a06d80c3e42eaa4f0ecf7a68f3ef30e4cf..b6af330cb8d1a85235bc0b2b838e3e1a9b817d91 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -27,6 +27,7 @@ #include "actions/actions-tools.h" // Tool switching. #include "io/resource.h" #include "ui/dialog/dialog-base.h" // Tool switching. +#include "ui/themes.h" // Icon scaling namespace Inkscape { namespace UI { @@ -171,6 +172,9 @@ AlignAndDistribute::AlignAndDistribute(Inkscape::UI::Dialog::DialogBase* dlg) } } + // ------------ Apply icon scaling ---------------- + ThemeContext::applyIconScaling(this); + // ------------ Set initial values ------------ diff --git a/src/ui/dialog/dialog-container.cpp b/src/ui/dialog/dialog-container.cpp index 6735ff1ac86c50097007066acfe34a7994bebc5d..cb011b2688411aa73d630509e853eb754bb3ae98 100644 --- a/src/ui/dialog/dialog-container.cpp +++ b/src/ui/dialog/dialog-container.cpp @@ -60,6 +60,7 @@ #include "ui/dialog/xml-tree.h" #include "ui/icon-names.h" #include "ui/widget/canvas-grid.h" +#include "ui/themes.h" namespace Inkscape { namespace UI { @@ -169,7 +170,7 @@ Gtk::Widget *DialogContainer::create_notebook_tab(Glib::ustring label_str, Glib: Gtk::Label *label = Gtk::manage(new Gtk::Label(label_str)); Gtk::Image *image = Gtk::manage(new Gtk::Image()); Gtk::Button *close = Gtk::manage(new Gtk::Button()); - image->set_from_icon_name(image_str, Gtk::ICON_SIZE_MENU); + image->set_from_icon_name(image_str, ThemeContext::getScaledIconSize(Gtk::ICON_SIZE_MENU)); Gtk::Box *tab = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 2)); close->set_image_from_icon_name("window-close"); close->set_halign(Gtk::ALIGN_END); diff --git a/src/ui/dialog/layer-properties.cpp b/src/ui/dialog/layer-properties.cpp index 9ea78e4cf6bfc7a16e8dd4b515a8c86a0a6c4878..5476168bcd4b4e8581cb75f37a147888fa3f7106 100644 --- a/src/ui/dialog/layer-properties.cpp +++ b/src/ui/dialog/layer-properties.cpp @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later /** * @file - * Dialog for renaming layers. + * Dialog for selecting a layer in certain actions. + * This dialog (LayerPropertiesDialog) is only used in few places, e.g. in the menu action "move to layer". + * This dialog is NOT used for "Objects and Layers" that you usually see in the GUI, see ObjectsPanel instead. */ /* Author: * Bryce W. Harrington @@ -299,6 +301,8 @@ void LayerPropertiesDialog::_setup_layers_controls() int nameColNum = _tree.append_column("Name", *_text_renderer) - 1; Gtk::TreeView::Column *_name_column = _tree.get_column(nameColNum); _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); + _name_column->set_expand(true); + _tree.set_expander_column(*_tree.get_column(nameColNum)); _tree.signal_key_press_event().connect([=](GdkEventKey *ev) {return _handleKeyEvent(ev);}, false); diff --git a/src/ui/dialog/objects.cpp b/src/ui/dialog/objects.cpp index dfd5751cca37f2cb84b204ef5bdaff27e539ad41..338690eed0a21b0b79eec3af251216e15ea3b4a9 100644 --- a/src/ui/dialog/objects.cpp +++ b/src/ui/dialog/objects.cpp @@ -707,10 +707,8 @@ ObjectsPanel::ObjectsPanel() : _is_editing = false; }); - const int icon_col_width = 24; auto icon_renderer = Gtk::manage(new Inkscape::UI::Widget::CellRendererItemIcon()); icon_renderer->property_xpad() = 2; - icon_renderer->property_width() = icon_col_width; _tree.append_column(*_name_column); _name_column->set_expand(true); _name_column->pack_start(*icon_renderer, false); @@ -731,7 +729,6 @@ ObjectsPanel::ObjectsPanel() : col->add_attribute(_item_state_toggler->property_active_icon(), _model->_colItemState); col->add_attribute(_item_state_toggler->property_cell_background_rgba(), _model->_colBgColor); col->add_attribute(_item_state_toggler->property_activatable(), _model->_colHover); - col->set_fixed_width(icon_col_width); _blend_mode_column = col; } @@ -830,7 +827,6 @@ ObjectsPanel::ObjectsPanel() : eye->add_attribute(eyeRenderer->property_cell_background_rgba(), _model->_colBgColor); eye->add_attribute(eyeRenderer->property_activatable(), _model->_colHover); eye->add_attribute(eyeRenderer->property_gossamer(), _model->_colAncestorInvisible); - eye->set_fixed_width(icon_col_width); _eye_column = eye; } @@ -843,7 +839,6 @@ ObjectsPanel::ObjectsPanel() : lock->add_attribute(lockRenderer->property_cell_background_rgba(), _model->_colBgColor); lock->add_attribute(lockRenderer->property_activatable(), _model->_colHover); lock->add_attribute(lockRenderer->property_gossamer(), _model->_colAncestorLocked); - lock->set_fixed_width(icon_col_width); _lock_column = lock; } diff --git a/src/ui/dialog/tile.cpp b/src/ui/dialog/tile.cpp index 661c4f663f84f49e9ebab6979c4959dcc88bead3..68aeaed65bd062ae9baeb1ce0ececc509c1a5f92 100644 --- a/src/ui/dialog/tile.cpp +++ b/src/ui/dialog/tile.cpp @@ -23,15 +23,16 @@ #include "ui/dialog/polar-arrange-tab.h" #include "ui/dialog/align-and-distribute.h" #include "ui/icon-names.h" +#include "ui/icon-loader.h" namespace Inkscape { namespace UI { namespace Dialog { Gtk::Box& create_tab_label(const char* label_text, const char* icon_name) { + // TODO deduplicate with FillAndStroke::_createPageTabLabel and posibly others auto box = Gtk::make_managed(Gtk::ORIENTATION_HORIZONTAL, 4); - auto image = Gtk::make_managed(); - image->set_from_icon_name(icon_name, Gtk::ICON_SIZE_MENU); + auto image = Gtk::manage(sp_get_icon_image(icon_name, Gtk::ICON_SIZE_MENU)); auto label = Gtk::make_managed(label_text, true); box->pack_start(*image, false, true); box->pack_start(*label, false, true); diff --git a/src/ui/dialog/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp index 2dfad025cbfb3a78681b9eca46e576df334afd1e..460288df07ecbb9faf9d824ceb7603d5648ec34a 100644 --- a/src/ui/dialog/xml-tree.cpp +++ b/src/ui/dialog/xml-tree.cpp @@ -46,6 +46,7 @@ #include "ui/icon-loader.h" #include "ui/icon-names.h" #include "ui/tools/tool-base.h" +#include "ui/themes.h" #include "widgets/sp-xmlview-tree.h" @@ -204,6 +205,9 @@ XmlTree::XmlTree() static_cast(item)->signal_activate().connect([=](){ set_layout(layout); }); } + // apply DPI-dependent icon scaling + ThemeContext::applyIconScaling(this); + _layout = static_cast(prefs->getIntLimited("/dialogs/xml/layout", Auto, Auto, Vertical)); static_cast(menu_items.at(_layout))->set_active(); set_layout(_layout); diff --git a/src/ui/icon-loader.cpp b/src/ui/icon-loader.cpp index f524028ce2335667a57406b746a19e38101ce4e6..16f6139377efe15b1a6005626371ddd47645c8ce 100644 --- a/src/ui/icon-loader.cpp +++ b/src/ui/icon-loader.cpp @@ -16,6 +16,7 @@ #include "io/resource.h" #include "svg/svg-color.h" #include "widgets/toolbox.h" +#include "ui/themes.h" #include #include @@ -26,31 +27,32 @@ Gtk::Image *sp_get_icon_image(Glib::ustring icon_name, gint size) { Gtk::Image *icon = new Gtk::Image(); icon->set_from_icon_name(icon_name, Gtk::IconSize(Gtk::ICON_SIZE_BUTTON)); - icon->set_pixel_size(size); + icon->set_pixel_size(Inkscape::UI::ThemeContext::getScaledIconSize(size)); return icon; } Gtk::Image *sp_get_icon_image(Glib::ustring icon_name, Gtk::IconSize icon_size) { - Gtk::Image *icon = new Gtk::Image(); - icon->set_from_icon_name(icon_name, icon_size); - return icon; + int width, height; + Gtk::IconSize::lookup(icon_size, width, height); + return sp_get_icon_image(icon_name, width); } Gtk::Image *sp_get_icon_image(Glib::ustring icon_name, Gtk::BuiltinIconSize icon_size) { Gtk::Image *icon = new Gtk::Image(); - icon->set_from_icon_name(icon_name, icon_size); + icon->set_from_icon_name(icon_name, Inkscape::UI::ThemeContext::getScaledIconSize(icon_size)); return icon; } GtkWidget *sp_get_icon_image(Glib::ustring icon_name, GtkIconSize icon_size) { - return gtk_image_new_from_icon_name(icon_name.c_str(), icon_size); + return gtk_image_new_from_icon_name(icon_name.c_str(), Inkscape::UI::ThemeContext::getScaledIconSize(icon_size)); } Glib::RefPtr sp_get_icon_pixbuf(Glib::ustring icon_name, gint size) { + size = Inkscape::UI::ThemeContext::getScaledIconSize(size); // SP_ACTIVE_DESKTOP is not always available when we want icons (see start screen) auto window = SP_ACTIVE_DESKTOP ? SP_ACTIVE_DESKTOP->getToplevel() : nullptr; diff --git a/src/ui/themes.cpp b/src/ui/themes.cpp index bb582457550d183bbafdd56b2fa2b04d5a64f29f..27e802a670589dffb3a9ddb158ac3de70a3ddda6 100644 --- a/src/ui/themes.cpp +++ b/src/ui/themes.cpp @@ -25,6 +25,7 @@ #include #include #include "svg/css-ostringstream.h" +#include "src/widgets/spw-utilities.h" // sp_traverse_widget namespace Inkscape { namespace UI { @@ -531,6 +532,116 @@ void ThemeContext::adjust_global_font_scale(double factor) { Gtk::StyleContext::add_provider_for_screen(screen, _fontsizeprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1); } + +/** + * Apply fractional scaling to icon size. + * + * Gtk natively only uses integer scaling (e.g. factor 2 for screens with >= 2 * 96 DPI). + * Therefore we need to do fractional scaling (e.g. factor 1.5 for 1.5 * 96 DPI) on our own. + * This function multiplies the raw icon size (e.g., 16) by a fractional factor. + * The integer part of the scaling factor is ignored because Gtk does this transparently. + * For example, for 2.5 * 96 DPI Gtk will scale all logical pixels by factor 2. + * so Inkscape itself needs to handle the remaining factor 1.25. + * + * @param window current window + * @param icon_size_px original icon size in logical Gtk pixels + * @return scaled icon size in logical Gtk pixels + */ +guint ThemeContext::getScaledIconSize(Glib::RefPtr window, guint icon_size_px) { + Glib::RefPtr screen; + if (window) { + screen = window->get_screen(); + } else { + g_debug("getScaledIconSize called with nullptr window. Falling back to default window."); + Glib::RefPtr display = Gdk::Display::get_default(); + screen = display->get_default_screen(); + } + if (!screen) { + g_warning("failed to get default screen"); + return icon_size_px; + } + double dpi = screen->get_resolution(); + if (dpi < 96) { + dpi = 96; + } + return ceil(icon_size_px * dpi / 96.); +} + +guint ThemeContext::getScaledIconSize(guint iconSizePx) { + // FIXME suppress warning + // TODO DOC + // Limitation: this doesn't get the actual screen of the window, just the default screen + return ThemeContext::getScaledIconSize(Glib::RefPtr(), iconSizePx); +} + + +/** + * Apply fractional scaling to icon size. + * + * This variant of the function operates on predefined icon sizes and gets the next-best predefined size. + * + * @see ThemeContext::getScaledIconSize(Gtk::Window *window, guint icon_size_px) +*/ +Gtk::BuiltinIconSize ThemeContext::getScaledIconSize(Glib::RefPtr window, Gtk::BuiltinIconSize icon_size) { + int width, height; + Gtk::IconSize::lookup(icon_size, width, height); + int scaled_width = ThemeContext::getScaledIconSize(window, width); + if (scaled_width == width) { + return icon_size; + } else if (scaled_width >= (48 * 0.9)) { + return Gtk::BuiltinIconSize::ICON_SIZE_DIALOG; // 48px + } else if (scaled_width >= (32 * 0.9)) { + return Gtk::BuiltinIconSize::ICON_SIZE_DND; // 32px + } else if (scaled_width >= (24 * 0.9)) { + return Gtk::BuiltinIconSize::ICON_SIZE_LARGE_TOOLBAR; // 24px + } else { + return icon_size; // 16px - return unchanged + } +} + +Gtk::BuiltinIconSize ThemeContext::getScaledIconSize(Gtk::BuiltinIconSize icon_size) { + // TODO DOC + // For limitations, see other getScaledIconSize implementations + return ThemeContext::getScaledIconSize(Glib::RefPtr(), icon_size); +} + + +GtkIconSize ThemeContext::getScaledIconSize(GtkIconSize icon_size) { + return static_cast(ThemeContext::getScaledIconSize(static_cast(icon_size))); +} + +/** + * For each button icon in all sub-widgets, update the icon size to apply fractional DPI scaling. + * TODO: also add a configurable user scale factor + * TODO: support further widget types +*/ +void ThemeContext::applyIconScaling(Gtk::Widget *widget) { + sp_traverse_widget_tree(widget, [=](Gtk::Widget* subwidget){ + // For each button: + auto button = dynamic_cast(subwidget); + if (!button) { + return false; + } + // For each image inside the button ... + auto image = dynamic_cast(button->get_image()); + if (!image) { + return false; + } + // ... that has the icon-size property set (e.g. GTK_ICON_SIZE_BUTTON): + int icon_size = image->property_icon_size(); + if (icon_size <= 0) { + return false; + } + // determine scaled icon size + int width, height; + Gtk::IconSize::lookup(Gtk::IconSize(icon_size), width, height); + int scaled_width = ThemeContext::getScaledIconSize(image->get_window(), width); + // and apply it + image->set_pixel_size(scaled_width); + return false; + }); +} + } // UI } // Inkscape diff --git a/src/ui/themes.h b/src/ui/themes.h index a99cd3ea13ba3ef7fc7a6247d4d63a86866d057b..5258eadf5e1b96e8f030785acc22a4ec680c9287 100644 --- a/src/ui/themes.h +++ b/src/ui/themes.h @@ -58,6 +58,18 @@ bool isCurrentThemeDark(Gtk::Container *window); static std::vector getHighlightColors(Gtk::Window *window); +static guint getScaledIconSize(guint iconSizePx); + +static guint getScaledIconSize(Glib::RefPtr window, guint iconSizePx); + +static Gtk::BuiltinIconSize getScaledIconSize(Gtk::BuiltinIconSize icon_size); + +static GtkIconSize getScaledIconSize(GtkIconSize icon_size); + +static void applyIconScaling(Gtk::Widget *widget); + +static Gtk::BuiltinIconSize getScaledIconSize(Glib::RefPtr window, Gtk::BuiltinIconSize icon_size); + private: // user change theme sigc::signal _signal_change_theme; diff --git a/src/ui/widget/color-palette.cpp b/src/ui/widget/color-palette.cpp index cb01c1536dec541646b68c7513b39c730d425c55..cfe86167f1c8a3e06896c414274935132d35e81d 100644 --- a/src/ui/widget/color-palette.cpp +++ b/src/ui/widget/color-palette.cpp @@ -13,6 +13,8 @@ #include "color-palette.h" #include "ui/builder-utils.h" +#include "ui/themes.h" + namespace Inkscape { namespace UI { @@ -455,18 +457,20 @@ void ColorPalette::set_up_scrolling() { } int ColorPalette::get_tile_size(bool horz) const { - if (_stretch_tiles) return _size; double aspect = horz ? _aspect : -_aspect; - if (aspect > 0) { - return static_cast(round((1.0 + aspect) * _size)); + if (_stretch_tiles) { + aspect = 0; } - else if (aspect < 0) { - return static_cast(round((1.0 / (1.0 - aspect)) * _size)); + + int scaled_size = ThemeContext::getScaledIconSize(this->get_window(), _size); + + if (aspect >= 0) { + return static_cast(round((1.0 + aspect) * scaled_size)); } else { - return _size; + return static_cast(round((1.0 / (1.0 - aspect)) * scaled_size)); } } diff --git a/src/ui/widget/imagetoggler.cpp b/src/ui/widget/imagetoggler.cpp index 829c470eaf30b6479f74da88295387b5522f6a7c..518a01843e1d1e7bd4144e8a5a1739161cad33b8 100644 --- a/src/ui/widget/imagetoggler.cpp +++ b/src/ui/widget/imagetoggler.cpp @@ -15,6 +15,7 @@ #include "ui/icon-loader.h" #include "ui/icon-names.h" +#include "ui/themes.h" namespace Inkscape { namespace UI { @@ -33,7 +34,8 @@ ImageToggler::ImageToggler( char const* on, char const* off) : _property_pixbuf_off(*this, "pixbuf_off", Glib::RefPtr(nullptr)) { property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; - Gtk::IconSize::lookup(Gtk::ICON_SIZE_MENU, _size, _size); + Gtk::IconSize::lookup(Gtk::ICON_SIZE_MENU, _icon_size_raw, _icon_size_raw); + _size = ThemeContext::getScaledIconSize(_icon_size_raw); } void ImageToggler::get_preferred_height_vfunc(Gtk::Widget& widget, int& min_h, int& nat_h) const @@ -61,8 +63,8 @@ void ImageToggler::render_vfunc( const Cairo::RefPtr& cr, // Lazy/late pixbuf rendering to get access to scale factor from widget. if(!_property_pixbuf_on.get_value()) { int scale = widget.get_scale_factor(); - _property_pixbuf_on = sp_get_icon_pixbuf(_pixOnName, _size * scale); - _property_pixbuf_off = sp_get_icon_pixbuf(_pixOffName, _size * scale); + _property_pixbuf_on = sp_get_icon_pixbuf(_pixOnName, _icon_size_raw * scale); + _property_pixbuf_off = sp_get_icon_pixbuf(_pixOffName, _icon_size_raw * scale); } std::string icon_name = _property_active_icon.get_value(); diff --git a/src/ui/widget/imagetoggler.h b/src/ui/widget/imagetoggler.h index 579f6b9ff9cf475b3c5a8074fe0972010319afe9..a6ed2d5138836806c3553ede547117cda1a42819 100644 --- a/src/ui/widget/imagetoggler.h +++ b/src/ui/widget/imagetoggler.h @@ -60,7 +60,8 @@ protected: private: - int _size; + int _size; /// size after applying fractional icon size scaling (ThemeContext::getScaledIconSize()) + int _icon_size_raw; /// size before applying fractional icon size scaling (e.g. 16 pixels) Glib::ustring _pixOnName; Glib::ustring _pixOffName; bool _active = false; diff --git a/src/ui/widget/paint-selector.cpp b/src/ui/widget/paint-selector.cpp index 450493bec6fae8ef5921d2263c46b0809f436ce9..acda306acb67e87a9c1c3fe0a45fc5344d573af9 100644 --- a/src/ui/widget/paint-selector.cpp +++ b/src/ui/widget/paint-selector.cpp @@ -186,7 +186,7 @@ PaintSelector::PaintSelector(FillOrStroke kind) _evenodd->set_tooltip_text( _("Any path self-intersections or subpaths create holes in the fill (fill-rule: evenodd)")); _evenodd->set_fillrule(PaintSelector::FILLRULE_EVENODD); - auto w = sp_get_icon_image("fill-rule-even-odd", GTK_ICON_SIZE_MENU); + auto w = sp_get_icon_image("fill-rule-even-odd", GTK_ICON_SIZE_MENU); // TODO make scalable gtk_container_add(GTK_CONTAINER(_evenodd->gobj()), w); _fillrulebox->pack_start(*_evenodd, false, false, 0); _evenodd->signal_toggled().connect( diff --git a/src/ui/widget/pattern-editor.cpp b/src/ui/widget/pattern-editor.cpp index d9a52aab9dc69ea683d6fbf542b40afa029ee159..ddba3b178740b173093008d453595525b766476d 100644 --- a/src/ui/widget/pattern-editor.cpp +++ b/src/ui/widget/pattern-editor.cpp @@ -34,6 +34,7 @@ #include "preferences.h" #include "util/units.h" #include "widgets/spw-utilities.h" +#include "ui/themes.h" namespace Inkscape { namespace UI { @@ -271,6 +272,9 @@ PatternEditor::PatternEditor(const char* prefs, Inkscape::PatternManager& manage update_scale_link(); pack_start(_main_grid); + + // scale icons (fractionally) according to DPI + ThemeContext::applyIconScaling(this); } PatternEditor::~PatternEditor() noexcept {} diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 907e9e178ebd33313c3482ae7ee1745e08db6333..b66b6493818d4465729ad40a2ab1e448adc46e15 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -40,6 +40,7 @@ #include "ui/widget/style-swatch.h" #include "widgets/spw-utilities.h" // sp_traverse_widget_tree() #include "widgets/widget-sizes.h" +#include "ui/themes.h" #include "ui/toolbar/arc-toolbar.h" #include "ui/toolbar/box3d-toolbar.h" @@ -89,6 +90,7 @@ enum BarId { #define BAR_ID_KEY "BarIdValue" #define HANDLE_POS_MARK "x-inkscape-pos" + int ToolboxFactory::prefToPixelSize(Glib::ustring const& path) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int size = prefs->getIntLimited(path, 16, 16, 48); @@ -99,7 +101,7 @@ void ToolboxFactory::set_icon_size(GtkWidget* toolbox, int pixel_size) { sp_traverse_widget_tree(Glib::wrap(toolbox), [=](Gtk::Widget* widget) { if (auto ico = dynamic_cast(widget)) { ico->set_from_icon_name(ico->get_icon_name(), static_cast(Gtk::ICON_SIZE_BUTTON)); - ico->set_pixel_size(pixel_size); + ico->set_pixel_size(ThemeContext::getScaledIconSize(widget->get_window(), pixel_size)); } return false; });