18#include <cairomm/pattern.h>
19#include <glibmm/i18n.h>
20#include <glibmm/regex.h>
21#include <glibmm/spawn.h>
22#include <gtkmm/adjustment.h>
23#include <gtkmm/cssprovider.h>
24#include <gtkmm/image.h>
25#include <gtkmm/label.h>
26#include <gtkmm/messagedialog.h>
27#include <gtkmm/revealer.h>
28#include <gtkmm/spinbutton.h>
29#include <gtkmm/textview.h>
30#include <gtkmm/tooltip.h>
31#include <gtkmm/widget.h>
32#include <pangomm/context.h>
33#include <pangomm/fontdescription.h>
34#include <pangomm/layout.h>
42#if (defined (_WIN32) || defined (_WIN64))
44#include <gdk/win32/gdkwin32.h>
47#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_OLD
48#define DWMWA_USE_IMMERSIVE_DARK_MODE_OLD 19
51#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
52#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
69 if (src.length() > maxlen && maxlen > 8) {
71 size_t p1 = (size_t) maxlen / 2;
72 size_t p2 = (size_t) src.length() - (maxlen - p1 - 1);
73 return src.substr(0, p1) +
"…" + src.substr(p2);
85 auto revealer =
dynamic_cast<Gtk::Revealer *
>(widget->get_parent());
87 revealer->set_reveal_child(show);
90 widget->set_visible(
true);
91 }
else if (!revealer) {
92 widget->set_visible(
false);
98 if (!widget)
return false;
101 return widget->get_child_visible();
117 if (
dynamic_cast<Gtk::SpinButton*
>(&widget)) {
121 if (
auto const ico =
dynamic_cast<Gtk::Image *
>(&widget)) {
122 ico->set_from_icon_name(ico->get_icon_name());
123 ico->set_pixel_size(pixel_size);
135 g_warning(
"%s",
msg.c_str());
136 if (INKSCAPE.active_desktop()) {
137 Gtk::MessageDialog warning(_(
msg.c_str()),
false, Gtk::MessageType::WARNING, Gtk::ButtonsType::OK,
true);
138 warning.set_transient_for(parent_window ? *parent_window : *INKSCAPE.active_desktop()->getInkscapeWindow());
146 ShellExecute(
nullptr,
"open", path.c_str(),
nullptr,
nullptr, SW_SHOWDEFAULT);
147#elif defined(__APPLE__)
148 Glib::spawn_async(
"", {
"open", path.raw() }, Glib::SpawnFlags::SEARCH_PATH);
150 char *
const uripath = g_filename_to_uri(path.c_str(),
nullptr,
nullptr);
151 Glib::spawn_async(
"", {
"xdg-open", uripath }, Glib::SpawnFlags::SEARCH_PATH);
158 auto children = std::vector<Gtk::Widget *>{};
160 children.push_back(
child);
167 auto child = widget.get_first_child();
168 for (std::size_t i = 0;
true; ++i) {
169 if (!
child)
throw std::out_of_range{
"get_nth_child()"};
170 if (i ==
index)
break;
221 if (widget.has_focus()) {
225 Gtk::Root
const *
root = widget.get_root();
230 Gtk::Widget
const *focused =
root->get_focus();
235 return focused->is_ancestor(widget);
241 auto pango_context = widget.get_pango_context();
242 auto font_description = pango_context->get_font_description();
243 double font_size = font_description.get_size();
244 font_size /= Pango::SCALE;
245 if (font_description.get_size_is_absolute()) {
253 if (max_width_chars <= 0)
return;
255 label.set_max_width_chars(max_width_chars);
257 label.set_has_tooltip(
true);
258 label.signal_query_tooltip().connect([&](
int,
int,
bool,
259 Glib::RefPtr<Gtk::Tooltip>
const &tooltip)
261 if (!
label.get_layout()->is_ellipsized())
return false;
263 tooltip->set_text(
label.get_text());
277 Gdk::RGBA gdk_color = Gdk::RGBA(color);
279 gdk_color.get_blue(), gdk_color.get_alpha());
283Gdk::RGBA
mix_colors(
const Gdk::RGBA& a,
const Gdk::RGBA& b,
float ratio) {
284 auto lerp = [](
double v0,
double v1,
double t){
return (1.0 - t) * v0 + t * v1; };
287 lerp(a.get_red(), b.get_red(), ratio),
288 lerp(a.get_green(), b.get_green(), ratio),
289 lerp(a.get_blue(), b.get_blue(), ratio),
290 lerp(a.get_alpha(), b.get_alpha(), ratio)
298 return 0.299 * rgba.get_red ()
299 + 0.587 * rgba.get_green()
300 + 0.114 * rgba.get_blue ();
304 Glib::ustring
const &css_class)
306 if (!css_class.empty()) widget.add_css_class(css_class);
307 auto result = widget.get_color();
308 if (!css_class.empty()) widget.remove_css_class(css_class);
314 return static_cast<guint32>(0xFF * rgba.get_red () + 0.5) << 24 |
315 static_cast<guint32>(0xFF * rgba.get_green() + 0.5) << 16 |
316 static_cast<guint32>(0xFF * rgba.get_blue () + 0.5) << 8 |
317 static_cast<guint32>(0xFF * rgba.get_alpha() + 0.5);
327 auto rgba = Gdk::RGBA{};
328 rgba.set_red (((u32 & 0xFF000000) >> 24) / 255.0);
329 rgba.set_green(((u32 & 0x00FF0000) >> 16) / 255.0);
330 rgba.set_blue (((u32 & 0x0000FF00) >> 8) / 255.0);
331 rgba.set_alpha(((u32 & 0x000000FF) ) / 255.0);
365 return Cairo::Matrix(affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
375 return Geom::IntPoint(allocation.get_width(), allocation.get_height());
379 const Gdk::RGBA& from,
388 for (
auto&& pt : {p0, ctrl1, ctrl2, p1}) {
389 if (pt.x() < 0 || pt.x() > 1 ||
390 pt.y() < 0 || pt.y() > 1) {
391 throw std::invalid_argument(
"Invalid points for cubic gradient; 0..1 coordinates expected.");
394 if (steps < 2 || steps > 999) {
395 throw std::invalid_argument(
"Invalid number of steps for cubic gradient; 2 to 999 steps expected.");
398 std::vector<GskColorStop>
result;
401 for (
int step = 0; step <= steps; ++step) {
402 auto t =
static_cast<double>(step) / steps;
408 result.push_back(GskColorStop{
419 copy.set_alpha(new_alpha);
424 auto alpha = replace_alpha >= 0 ? replace_alpha : color.get_alpha();
426 uint32_t(0xff * color.get_red()) << 24 |
427 uint32_t(0xff * color.get_green()) << 16 |
428 uint32_t(0xff * color.get_blue()) << 8 |
429 uint32_t(0xff * alpha);
435#if (defined (_WIN32) || defined (_WIN64))
437 BOOL w32_darkmode = is_dark;
438 HWND hwnd = (HWND)gdk_win32_surface_get_handle((GdkSurface*)
surface->gobj());
439 if (DwmSetWindowAttribute) {
440 DWORD attr = DWMWA_USE_IMMERSIVE_DARK_MODE;
441 if (FAILED(DwmSetWindowAttribute(hwnd, attr, &w32_darkmode,
sizeof(w32_darkmode)))) {
442 attr = DWMWA_USE_IMMERSIVE_DARK_MODE_OLD;
443 DwmSetWindowAttribute(hwnd, attr, &w32_darkmode,
sizeof(w32_darkmode));
451static int fmt_number(
const _GMatchInfo* match, _GString* ret,
void* prec) {
452 auto number = g_match_info_fetch(match, 1);
455 double val = g_ascii_strtod(number, &
end);
456 if (*number && (
end ==
nullptr ||
end > number)) {
457 auto precision = *
static_cast<int*
>(prec);
459 g_string_append(ret,
fmt.c_str());
461 g_string_append(ret, number);
464 auto text = g_match_info_fetch(match, 2);
465 g_string_append(ret, text);
475 static const auto numbers = Glib::Regex::create(
"([-+]?(?:(?:\\d+\\.?\\d*)|(?:\\.\\d+))(?:[eE][-+]?\\d*)?)([^+\\-0-9]*)", Glib::Regex::CompileFlags::MULTILINE);
477 return numbers->replace_eval(text, text.size(), 0, Glib::Regex::MatchFlags::NOTEMPTY, &
fmt_number, &precision);
484 auto start = buffer->begin();
485 auto end = buffer->end();
487 bool had_selection = buffer->get_has_selection();
488 int start_idx = 0, end_idx = 0;
490 buffer->get_selection_bounds(
start,
end);
491 start_idx =
start.get_offset();
492 end_idx =
end.get_offset();
495 auto text = buffer->get_text(
start,
end);
498 buffer->insert_at_cursor(ret);
502 end_idx -= text.size() - ret.size();
503 if (end_idx < start_idx) {
506 buffer->select_range(buffer->get_iter_at_offset(start_idx), buffer->get_iter_at_offset(end_idx));
516 assert(
surface->get_type() == Cairo::Surface::Type::IMAGE);
518 auto img = Cairo::ImageSurface(
surface->cobj());
519 assert(img.get_format() == Cairo::ImageSurface::Format::ARGB32);
521 auto bytes = g_bytes_new_with_free_func(img.get_data(),
522 img.get_stride() * img.get_height(),
523 (GDestroyNotify)cairo_surface_destroy,
524 cairo_surface_reference(
surface->cobj()));
526 auto texture = gdk_memory_texture_new(img.get_width(),
532 g_bytes_unref(bytes);
534 return Glib::wrap(texture);
538 auto name = widget.get_name();
539 assert(!
name.empty());
540 auto css = Gtk::CssProvider::create();
541 std::ostringstream ost;
542 ost <<
"#" <<
name <<
" {min-width:" << min_size_px <<
"px; min-height:" << min_size_px <<
"px;}";
543 css->load_from_string(ost.str());
544 auto style_context = widget.get_style_context();
546 style_context->add_provider(
css, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2);
551 return gtk_editable_get_text(
const_cast<GtkEditable *
>(editable.gobj()));
Bernstein-Bezier polynomial.
Cairo::RefPtr< Cairo::ImageSurface > surface
3x3 matrix representing an affine transformation.
Axis aligned, non-empty, generic rectangle.
static CRect from_xywh(C x, C y, C w, C h)
Create rectangle from origin and dimensions.
C top() const
Return top coordinate of the rectangle (+Y is downwards).
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
Two-dimensional point with integer coordinates.
Two-dimensional point that doubles as a vector.
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
constexpr uint32_t SP_RGBA32_F_COMPOSE(double r, double g, double b, double a)
Utility functions to convert ascii representations to numbers.
std::shared_ptr< Css const > css
Editable view implementation.
static char const *const parent
Inkscape - An SVG editor.
T bernstein_value_at(double t, T const *c_, unsigned n)
Compute the value of a Bernstein-Bezier polynomial.
uint32_t hex_to_rgba(std::string const &value)
Parse a color directly without any CSS or CMS support.
std::string rgba_to_hex(uint32_t value, bool alpha)
Output the RGBA value as a #RRGGBB hex color, if alpha is true then the output will be #RRGGBBAA inst...
Gtk::Widget * for_each_parent(Gtk::Widget &widget, Func &&func)
Call Func with a reference to successive parents, until Func returns _break.
void set_icon_sizes(Gtk::Widget *parent, int pixel_size)
Recursively set all the icon sizes inside this parent widget.
void gui_warning(const std::string &msg, Gtk::Window *parent_window)
Gtk::Widget & get_nth_child(Gtk::Widget &widget, std::size_t const index)
Get the widgetʼs child at the given position. Throws std::out_of_range if the index is invalid.
Gtk::Widget * for_each_descendant(Gtk::Widget &widget, Func &&func)
Like for_each_child() but also tests the initial widget & recurses through childrenʼs children.
Gtk::Widget * find_widget_by_name(Gtk::Widget &parent, Glib::ustring const &name, bool visible_only)
Returns a named descendent of parent, which has the given name, or nullptr if there's none.
bool is_descendant_of(Gtk::Widget const &descendant, Gtk::Widget const &ancestor)
Returns if widget is a descendant of given ancestor, i.e.: itself, a child, or a childʼs child.
int get_font_size(Gtk::Widget &widget)
Get the relative font size as determined by a widgetʼs style/Pango contexts.
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
void ellipsize(Gtk::Label &label, int const max_width_chars, Pango::EllipsizeMode const mode)
int dialog_run(Gtk::Dialog &dialog)
This is a GTK4 porting aid meant to replace the removal of the Gtk::Dialog synchronous API.
void system_open(const Glib::ustring &path)
Opens the given path with platform-specific tools.
bool contains_focus(Gtk::Widget &widget)
Returns if widget or one of its descendants has focus.
Gtk::Widget * find_focusable_widget(Gtk::Widget &parent)
This function traverses a tree of widgets searching for first focusable widget.
std::string format_number(double val, unsigned int precision=3)
Glib::ustring ink_ellipsize_text(Glib::ustring const &src, std::size_t maxlen)
Glib::ustring round_numbers(const Glib::ustring &text, int precision)
unsigned int get_color_value(const Glib::ustring color)
Color is store as a string in the form #RRGGBBAA, '0' means "unset".
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.
Gdk::RGBA to_rgba(guint32 const u32)
Cairo::RectangleInt geom_to_cairo(const Geom::IntRect &rect)
guint32 to_guint32(Gdk::RGBA const &rgba)
std::vector< GskColorStop > create_cubic_gradient(const Gdk::RGBA &from, const Gdk::RGBA &to, Geom::Point ctrl1, Geom::Point ctrl2, Geom::Point p0, Geom::Point p1, int steps)
Gdk::RGBA css_color_to_gdk(const char *value)
uint32_t conv_gdk_color_to_rgba(const Gdk::RGBA &color, double replace_alpha)
void restrict_minsize_to_square(Gtk::Widget &widget, int min_size_px)
bool is_widget_effectively_visible(Gtk::Widget const *widget)
static int fmt_number(const _GMatchInfo *match, _GString *ret, void *prec)
Gdk::RGBA get_color_with_class(Gtk::Widget &widget, Glib::ustring const &css_class)
double get_luminance(Gdk::RGBA const &rgba)
Calculate luminance of an RGBA color from its RGB in range 0 to 1 inclusive.
char const * get_text(Gtk::Editable const &editable)
Get the text from a GtkEditable without the temporary copy imposed by gtkmm.
Gdk::RGBA change_alpha(const Gdk::RGBA &color, double new_alpha)
Gdk::RGBA mix_colors(const Gdk::RGBA &a, const Gdk::RGBA &b, float ratio)
Geom::IntRect cairo_to_geom(const Cairo::RectangleInt &rect)
void reveal_widget(Gtk::Widget *widget, bool show)
Show widget, if the widget has a Gtk::Reveal parent, reveal instead.
void truncate_digits(const Glib::RefPtr< Gtk::TextBuffer > &buffer, int precision)
void set_dark_titlebar(Glib::RefPtr< Gdk::Surface > const &surface, bool is_dark)
Gdk::RGBA color_to_rgba(Inkscape::Colors::Color const &color)
Glib::ustring gdk_to_css_color(const Gdk::RGBA &color)
These GUI related color conversions allow us to convert from SVG xml attributes to Gdk colors,...
Geom::IntPoint dimensions(const Cairo::RefPtr< Cairo::ImageSurface > &surface)