1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Utility functions for UI
9 * Copyright (C) 2013, 2018 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 #include <cstddef> // size_t
18 #include <type_traits>
21 #include <pangomm/layout.h> // Pango::EllipsizeMode
22 #include <gdkmm/rgba.h>
23 #include <gtkmm/cellrenderer.h>
24 #include <gtkmm/enums.h>
25 #include <gtkmm/notebook.h>
26 #include <gtkmm/widget.h>
28 #include <2geom/affine.h>
29 #include <2geom/point.h>
30 #include <2geom/rect.h>
33 * Use these errors when building from glade files for graceful
34 * fallbacks and prevent crashes from corrupt ui files.
36 class UIBuilderError
: public std::exception
{};
37 class UIFileUnavailable
: public UIBuilderError
{};
38 class WidgetUnavailable
: public UIBuilderError
{};
56 namespace Inkscape::Colors
{
58 } // namespace Inkscape::Colors
60 Glib::ustring
ink_ellipsize_text(Glib::ustring
const &src
, std::size_t maxlen
);
62 void reveal_widget(Gtk::Widget
*widget
, bool show
);
64 // check if widget in a container is actually visible
65 bool is_widget_effectively_visible(Gtk::Widget
const *widget
);
67 namespace Inkscape::UI
{
69 void set_icon_sizes(Gtk::Widget
*parent
, int pixel_size
);
70 void set_icon_sizes(GtkWidget
*parent
, int pixel_size
);
72 void gui_warning(const std::string
&msg
, Gtk::Window
* parent_window
= nullptr);
74 /// Whether for_each_*() will continue or stop after calling Func per child.
75 enum class ForEachResult
{
76 _continue
, // go on to the next widget
77 _break
, // stop here, return current widget
78 _skip
// do not recurse into current widget, go to the next one
81 /// Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
82 std::vector
<Gtk::Widget
*> get_children(Gtk::Widget
&widget
);
83 /// Get the widgetʼs child at the given position. Throws std::out_of_range if the index is invalid.
84 Gtk::Widget
&get_nth_child(Gtk::Widget
&widget
, std::size_t index
);
85 /// For each child in get_children(widget), call widget.remove(*child). May not cause delete child!
86 template <typename Widget
> void remove_all_children(Widget
&widget
)
88 for (auto const child
: get_children(widget
)) {
89 widget
.remove(*child
);
93 /// Call Func with a reference to each child of parent, until it returns _break.
94 /// Accessing children changes between GTK3 & GTK4, so best consolidate it here.
95 /// @param widget The initial widget at the top of the hierarchy, to start at
96 /// @param func The widget-testing predicate, returning whether to continue
97 /// @param plus_self Whether to call the predicate @a func on the initial widget
98 /// @param recurse Whether to recurse also calling @a func for nested children
99 /// @return The first widget for which @a func returns _break or nullptr if none
100 template <typename Func
>
101 Gtk::Widget
*for_each_child(Gtk::Widget
&widget
, Func
&&func
,
102 bool const plus_self
= false, bool const recurse
= false,
105 static_assert(std::is_invocable_r_v
<ForEachResult
, Func
, Gtk::Widget
&>);
108 auto ret
= func(widget
);
109 if (ret
== ForEachResult::_break
) return &widget
;
112 if (ret
== ForEachResult::_skip
) return nullptr;
115 if (!recurse
&& level
> 0) return nullptr;
117 for (auto const child
: get_children(widget
)) {
118 auto const descendant
= for_each_child(*child
, func
, true, recurse
, level
+ 1);
119 if (descendant
) return descendant
;
125 /// Like for_each_child() but also tests the initial widget & recurses through childrenʼs children.
126 /// @param widget The initial widget at the top of the hierarchy, to start at
127 /// @param func The widget-testing predicate, returning whether to continue
128 /// @return The first widget for which @a func returns _break or nullptr if none
129 template <typename Func
>
130 Gtk::Widget
*for_each_descendant(Gtk::Widget
&widget
, Func
&&func
)
132 return for_each_child(widget
, std::forward
<Func
>(func
), true, true);
135 /// Call Func with a reference to successive parents, until Func returns _break.
136 template <typename Func
>
137 Gtk::Widget
*for_each_parent(Gtk::Widget
&widget
, Func
&&func
)
139 static_assert(std::is_invocable_r_v
<ForEachResult
, Func
, Gtk::Widget
&>);
140 for (auto parent
= widget
.get_parent(); parent
; parent
= parent
->get_parent()) {
141 if (func(*parent
) == ForEachResult::_break
) {
148 /// Similar to for_each_child, but only iterates over pages in a notebook
149 template <typename Func
>
150 Gtk::Widget
* for_each_page(Gtk::Notebook
& notebook
, Func
&& func
) {
151 static_assert(std::is_invocable_r_v
<ForEachResult
, Func
, Gtk::Widget
&>);
153 const int page_number
= notebook
.get_n_pages();
154 for (int page_index
= 0; page_index
< page_number
; ++page_index
) {
155 auto page
= notebook
.get_nth_page(page_index
);
158 if (func(*page
) == ForEachResult::_break
) return page
;
164 [[nodiscard
]] Gtk::Widget
*find_widget_by_name(Gtk::Widget
&parent
, Glib::ustring
const &name
, bool visible_only
);
165 [[nodiscard
]] Gtk::Widget
*find_focusable_widget(Gtk::Widget
&parent
);
166 [[nodiscard
]] bool is_descendant_of(Gtk::Widget
const &descendant
, Gtk::Widget
const &ancestor
);
167 [[nodiscard
]] bool contains_focus(Gtk::Widget
&widget
);
169 [[nodiscard
]] int get_font_size(Gtk::Widget
&widget
);
171 // If max_width_chars is > 0, then the created Label has :max-width-chars set to
172 // that limit, the :ellipsize mode is set to the passed-in @a mode, & a ::query-
173 // tooltip handler is connected to show the label as the tooltip when ellipsized
174 void ellipsize(Gtk::Label
&label
, int max_width_chars
, Pango::EllipsizeMode mode
);
176 } // namespace Inkscape::UI
178 // Mix two RGBA colors using simple linear interpolation:
179 // 0 -> only a, 1 -> only b, x in 0..1 -> (1 - x)*a + x*b
180 Gdk::RGBA
mix_colors(const Gdk::RGBA
& a
, const Gdk::RGBA
& b
, float ratio
);
182 // Create the same color, but with a different opacity (alpha)
183 Gdk::RGBA
change_alpha(const Gdk::RGBA
& color
, double new_alpha
);
185 /// Calculate luminance of an RGBA color from its RGB in range 0 to 1 inclusive.
186 /// This uses the perceived brightness formula given at: https://www.w3.org/TR/AERT/#color-contrast
187 double get_luminance(const Gdk::RGBA
&color
);
189 // Get CSS color for a Widget, based on its current state & a given CSS class.
190 // N.B.!! Big GTK devs donʼt think changing classes should work ‘within a frame’
191 // …but it does… & GTK3 GtkCalendar does that – so keep doing it, till we canʼt!
192 Gdk::RGBA
get_color_with_class(Gtk::Widget
&widget
,
193 Glib::ustring
const &css_class
);
195 // Convert a Gdk color to a hex code for css injection.
196 Glib::ustring
gdk_to_css_color(const Gdk::RGBA
& color
);
197 Gdk::RGBA
css_color_to_gdk(const char *value
);
199 guint32
to_guint32(Gdk::RGBA
const &rgba
);
200 Gdk::RGBA
color_to_rgba(Inkscape::Colors::Color
const &color
);
201 Gdk::RGBA
to_rgba(guint32
const u32
);
203 // convert Gdk::RGBA into 32-bit rrggbbaa color, optionally replacing alpha, if specified
204 uint32_t conv_gdk_color_to_rgba(const Gdk::RGBA
& color
, double replace_alpha
= -1);
206 Geom::IntRect
cairo_to_geom(const Cairo::RectangleInt
&rect
);
207 Cairo::RectangleInt
geom_to_cairo(const Geom::IntRect
&rect
);
208 Cairo::Matrix
geom_to_cairo(const Geom::Affine
&affine
);
209 Geom::IntPoint
dimensions(const Cairo::RefPtr
<Cairo::ImageSurface
> &surface
);
210 Geom::IntPoint
dimensions(const Gdk::Rectangle
&allocation
);
212 // create a gradient with multiple steps to approximate profile described by given cubic spline
213 Cairo::RefPtr
<Cairo::LinearGradient
> create_cubic_gradient(
215 const Gdk::RGBA
& from
,
219 Geom::Point p0
= Geom::Point(0, 0),
220 Geom::Point p1
= Geom::Point(1, 1),
224 // If on Windows, get the native window & set it to DWMA_USE_IMMERSIVE_DARK_MODE
225 void set_dark_titlebar(Glib::RefPtr
<Gdk::Surface
> const &surface
, bool is_dark
);
226 unsigned int get_color_value(const Glib::ustring color
);
228 // Parse string that can contain floating point numbers and round them to given precision;
229 // Used on path data ("d" attribute).
230 Glib::ustring
round_numbers(const Glib::ustring
& text
, int precision
);
232 // As above, but operating in-place on a TextBuffer
233 void truncate_digits(const Glib::RefPtr
<Gtk::TextBuffer
>& buffer
, int precision
);
236 * Convert an image surface in ARGB32 format to a texture.
237 * The texture shares data with the surface, so the surface shouldn't modified afterwards.
239 Glib::RefPtr
<Gdk::Texture
> to_texture(Cairo::RefPtr
<Cairo::Surface
> const &surface
);
241 // Restrict widget's min size (min-width & min-height) to specified minimum to keep it square (when it's centered).
242 // Widget has to have a name given with set_name.
243 void restrict_minsize_to_square(Gtk::Widget
& widget
, int min_size_px
);
245 /// Get the text from a GtkEditable without the temporary copy imposed by gtkmm.
246 char const *get_text(Gtk::Editable
const &editable
);
248 #endif // UI_UTIL_SEEN
253 c-file-style:"stroustrup"
254 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
259 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :