Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
themes.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Jabiertxof
8 * Martin Owens
9 *
10 * Copyright (C) 2017-2021 Authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include "themes.h"
16
17#include <cstddef>
18#include <cstring>
19#include <regex>
20#include <string>
21#include <utility>
22#include <gio/gio.h>
23#include <glibmm/regex.h>
24#include <glibmm/ustring.h>
25#include <gdkmm/display.h>
26#include <gtk/gtk.h>
27#include <gtkmm/cssprovider.h>
28#include <gtkmm/csssection.h>
29#include <gtkmm/settings.h>
30#include <gtkmm/styleprovider.h>
31#include <gtkmm/window.h>
32#include <pangomm/font.h>
33#include <pangomm/fontdescription.h>
34
35#include "config.h"
36#include "desktop.h"
37#include "inkscape.h"
38#include "inkscape-window.h"
39#include "preferences.h"
40#include "colors/utils.h"
41#include "io/resource.h"
42#include "object/sp-item-group.h" // set_default_highlight_colors
46#include "ui/util.h"
48
49#if WITH_GSOURCEVIEW
50# include <gtksourceview/gtksource.h>
51#endif
52
53namespace Inkscape::UI {
54
56 : _fontsizeprovider{Gtk::CssProvider::create()}
57{
58}
59
61
65void ThemeContext::inkscape_fill_gtk(const gchar *path, gtkThemeList &themes)
66{
67 const gchar *dir_entry;
68 GDir *dir = g_dir_open(path, 0, nullptr);
69 if (!dir)
70 return;
71 while ((dir_entry = g_dir_read_name(dir))) {
72 gchar *filename = g_build_filename(path, dir_entry, "gtk-4.0", "gtk.css", nullptr);
73 bool has_prefer_dark = false;
74
75 Glib::ustring theme = dir_entry;
76 gchar *filenamedark = g_build_filename(path, dir_entry, "gtk-4.0", "gtk-dark.css", nullptr);
77 if (g_file_test(filenamedark, G_FILE_TEST_IS_REGULAR))
78 has_prefer_dark = true;
79 if (themes.find(theme) != themes.end() && !has_prefer_dark) {
80 continue;
81 }
82 if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
83 themes[theme] = has_prefer_dark;
84 }
85 g_free(filename);
86 g_free(filenamedark);
87 }
88
89 g_dir_close(dir);
90}
91
95std::map<Glib::ustring, bool>
97{
98 // NOTE: This function tries to mimic what _gtk_css_find_theme in gtk4 does to locate a theme;
99 // that is we traverse resources and then certain directories looking for themes.
100 // We only gather theme names as this is what's saved in gtk settings to select a UI theme.
101 // gtk4 will load a theme based solely on its name searching for it in a list of folders (that we cannot change).
102
103 gtkThemeList themes;
104 Glib::ustring theme = "";
105 gchar *path;
106 gchar **builtin_themes;
107 guint i, j;
108 const gchar *const *dirs;
109
110 /* Builtin themes */
111 builtin_themes = g_resources_enumerate_children("/org/gtk/libgtk/theme", G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
112 for (i = 0; builtin_themes[i] != NULL; i++) {
113 if (g_str_has_suffix(builtin_themes[i], "/")) {
114 theme = builtin_themes[i];
115 theme.resize(theme.size() - 1);
116 Glib::ustring theme_path = "/org/gtk/libgtk/theme";
117 theme_path += "/" + theme;
118 gchar **builtin_themes_files =
119 g_resources_enumerate_children(theme_path.c_str(), G_RESOURCE_LOOKUP_FLAGS_NONE, nullptr);
120 bool has_prefer_dark = false;
121 if (builtin_themes_files != NULL) {
122 for (j = 0; builtin_themes_files[j] != NULL; j++) {
123 Glib::ustring file = builtin_themes_files[j];
124 if (file == "gtk-dark.css") {
125 has_prefer_dark = true;
126 }
127 }
128 }
129 g_strfreev(builtin_themes_files);
130 themes[theme] = has_prefer_dark;
131 }
132 }
133
134 g_strfreev(builtin_themes);
135
136 path = g_build_filename(g_get_user_data_dir(), "themes", nullptr);
137 inkscape_fill_gtk(path, themes);
138 g_free(path);
139
140 path = g_build_filename(g_get_home_dir(), ".themes", nullptr);
141 inkscape_fill_gtk(path, themes);
142 g_free(path);
143
144 dirs = g_get_system_data_dirs();
145 for (i = 0; dirs[i]; i++) {
146 path = g_build_filename(dirs[i], "themes", nullptr);
147 inkscape_fill_gtk(path, themes);
148 g_free(path);
149 }
150 return themes;
151}
152
153Glib::ustring
155{
156 Glib::ustring css_str;
158 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
159 guint32 colorsetbase = 0x2E3436ff;
160 guint32 colorsetbase_inverse;
161 guint32 colorsetsuccess = 0x4AD589ff;
162 guint32 colorsetwarning = 0xF57900ff;
163 guint32 colorseterror = 0xCC0000ff;
164 colorsetbase = prefs->getUInt("/theme/" + themeiconname + "/symbolicBaseColor", colorsetbase);
165 colorsetsuccess = prefs->getUInt("/theme/" + themeiconname + "/symbolicSuccessColor", colorsetsuccess);
166 colorsetwarning = prefs->getUInt("/theme/" + themeiconname + "/symbolicWarningColor", colorsetwarning);
167 colorseterror = prefs->getUInt("/theme/" + themeiconname + "/symbolicErrorColor", colorseterror);
168 colorsetbase_inverse = colorsetbase ^ 0xffffff00;
169 css_str += "@define-color warning_color " + Inkscape::Colors::rgba_to_hex(colorsetwarning) + ";\n";
170 css_str += "@define-color error_color " + Inkscape::Colors::rgba_to_hex(colorseterror) + ";\n";
171 css_str += "@define-color success_color " + Inkscape::Colors::rgba_to_hex(colorsetsuccess) + ";\n";
172 /* ":not(.rawstyle) > image" works only on images in first level of widget container
173 if in the future we use a complex widget with more levels and we dont want to tweak the color
174 here, retaining default we can add more lines like ":not(.rawstyle) > > image"
175 if we not override the color we use defautt theme colors*/
176 bool overridebasecolor = !prefs->getBool("/theme/symbolicDefaultBaseColors", true);
177 if (overridebasecolor) {
178 css_str += "#InkRuler:not(.shadow):not(.page):not(.selection),";
179 css_str += ":not(.rawstyle) > image:not(.arrow),";
180 css_str += ":not(.rawstyle) treeview.image";
181 css_str += "{color:";
182 css_str += Inkscape::Colors::rgba_to_hex(colorsetbase);
183 css_str += ";}";
184 }
185 css_str += ".dark .forcebright :not(.rawstyle) > image,";
186 css_str += ".dark .forcebright image:not(.rawstyle),";
187 css_str += ".bright .forcedark :not(.rawstyle) > image,";
188 css_str += ".bright .forcedark image:not(.rawstyle),";
189 css_str += ".dark :not(.rawstyle) > image.forcebright,";
190 css_str += ".dark image.forcebright:not(.rawstyle),";
191 css_str += ".bright :not(.rawstyle) > image.forcedark,";
192 css_str += ".bright image.forcedark:not(.rawstyle),";
193 css_str += ".inverse :not(.rawstyle) > image,";
194 css_str += ".inverse image:not(.rawstyle)";
195 css_str += "{color:";
196 if (overridebasecolor) {
197 css_str += Inkscape::Colors::rgba_to_hex(colorsetbase_inverse);
198 } else {
199 // we override base color in this special cases using inverse color
200 css_str += "@theme_bg_color";
201 }
202 css_str += ";}";
203 return css_str;
204}
205
206std::string sp_tweak_background_colors(std::string cssstring, double crossfade, double contrast, bool dark)
207{
208 static std::regex re_no_affect("(inherit|unset|initial|none|url)");
209 static std::regex re_color("background-color( ){0,3}:(.*?);");
210 static std::regex re_image("background-image( ){0,3}:(.*?\\)) *?;");
211 std::string sub = "";
212 std::smatch m;
213 std::regex_search(cssstring, m, re_no_affect);
214 if (m.size() == 0) {
215 if (cssstring.find("background-color") != std::string::npos) {
216 sub = "background-color:shade($2," + Inkscape::ustring::format_classic(crossfade) + ");";
217 cssstring = std::regex_replace(cssstring, re_color, sub);
218 } else if (cssstring.find("background-image") != std::string::npos) {
219 if (dark) {
220 contrast = std::clamp((int)((contrast) * 27), 0, 100);
221 sub = "background-image:cross-fade(" + Inkscape::ustring::format_classic(contrast) + "% image(rgb(255,255,255)), image($2));";
222 } else {
223 contrast = std::clamp((int)((contrast) * 90), 0 , 100);
224 sub = "background-image:cross-fade(" + Inkscape::ustring::format_classic(contrast) + "% image(rgb(0,0,0)), image($2));";
225 }
226 cssstring = std::regex_replace(cssstring, re_image, sub);
227 }
228 } else {
229 cssstring = "";
230 }
231 return cssstring;
232}
233
234static void
235show_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section, const Glib::Error& error)
236{
237#ifndef NDEBUG
238 g_warning("There is a warning parsing theme CSS:: %s", error.what());
239#endif
240}
241
242// callback for a "narrow spinbutton" preference change
243struct NarrowSpinbuttonObserver : Preferences::Observer {
244 NarrowSpinbuttonObserver(const char* path, Glib::RefPtr<Gtk::CssProvider> provider):
245 Preferences::Observer(path), _provider(std::move(provider)) {}
246
247 void notify(Preferences::Entry const& new_val) override {
248 auto const display = Gdk::Display::get_default();
249 if (new_val.getBool()) {
250 Gtk::StyleProvider::add_provider_for_display(display, _provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
251 }
252 else {
253 Gtk::StyleProvider::remove_provider_for_display(display, _provider);
254 }
255 }
256
257 Glib::RefPtr<Gtk::CssProvider> _provider;
258};
259
264void ThemeContext::add_gtk_css(bool only_providers, bool cached)
265{
266 using namespace Inkscape::IO::Resource;
267 // Add style sheet (GTK3)
268 auto const display = Gdk::Display::get_default();
270 gchar *gtkThemeName = nullptr;
271 gchar *gtkIconThemeName = nullptr;
272 Glib::ustring themeiconname;
273 gboolean gtkApplicationPreferDarkTheme;
274 GtkSettings *settings = gtk_settings_get_default();
275 if (settings && !only_providers) {
276 g_object_get(settings, "gtk-icon-theme-name", &gtkIconThemeName, nullptr);
277 g_object_get(settings, "gtk-theme-name", &gtkThemeName, nullptr);
278 g_object_get(settings, "gtk-application-prefer-dark-theme", &gtkApplicationPreferDarkTheme, nullptr);
279 prefs->setBool("/theme/defaultPreferDarkTheme", gtkApplicationPreferDarkTheme);
280 prefs->setString("/theme/defaultGtkTheme", Glib::ustring(gtkThemeName));
281 prefs->setString("/theme/defaultIconTheme", Glib::ustring(gtkIconThemeName));
282 Glib::ustring gtkthemename = prefs->getString("/theme/gtkTheme");
283 if (gtkthemename != "") {
284 g_object_set(settings, "gtk-theme-name", gtkthemename.c_str(), nullptr);
285 }
286 bool preferdarktheme = prefs->getBool("/theme/preferDarkTheme", false);
287 g_object_set(settings, "gtk-application-prefer-dark-theme", preferdarktheme, nullptr);
288 themeiconname = prefs->getString("/theme/iconTheme");
289 if (themeiconname != "") {
290 g_object_set(settings, "gtk-icon-theme-name", themeiconname.c_str(), nullptr);
291 }
292 }
293
294 g_free(gtkThemeName);
295 g_free(gtkIconThemeName);
296
297 int themecontrast = prefs->getInt("/theme/contrast", 10);
299 _contrastthemeprovider = Gtk::CssProvider::create();
300 // We can uncomment this line to remove warnings and errors on the theme
301 _contrastthemeprovider->signal_parsing_error().connect(sigc::ptr_fun(show_parsing_error));
302 }
303 static std::string cssstringcached = "";
304 // we use contrast only if is setup (!= 10)
305 if (themecontrast < 10) {
306 Glib::ustring css_contrast = "";
307 double contrast = (10 - themecontrast) / 30.0;
308 double shade = 1 - contrast;
309 const gchar *variant = nullptr;
310 if (prefs->getBool("/theme/preferDarkTheme", false)) {
311 variant = "dark";
312 }
313 bool dark = prefs->getBool("/theme/darkTheme", false);
314 if (dark) {
315 contrast *= 2.5;
316 shade = 1 + contrast;
317 }
318 Glib::ustring current_theme = prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", ""));
319
320 std::string cssstring = "";
321 if (cached && !cssstringcached.empty()) {
322 cssstring = cssstringcached;
323 } else {
324 auto current_themeprovider = Gtk::CssProvider::create();
325 current_themeprovider->load_named(current_theme, variant);
326 cssstring = current_themeprovider->to_string();
327 }
328 if (contrast) {
329 std::string cssdefined = "";
330 // we do this way to fix issue Inkscape#2345
331 // windows seem crash if text length > 2000;
332
333 std::istringstream f(cssstring);
334 std::string line;
335 while (std::getline(f, line)) {
336 // here we ignore most of class to parse because is in additive mode
337 // so stiles not applied are set on previous context style
338 if (line.find(";") != std::string::npos &&
339 line.find("background-image") == std::string::npos &&
340 line.find("background-color") == std::string::npos)
341 {
342 continue;
343 }
344 cssdefined += sp_tweak_background_colors(line, shade, contrast, dark);
345 cssdefined += "\n";
346 if (!cached) {
347 cssstringcached += line;
348 cssstringcached += "\n";
349 }
350 }
351 if (!cached) {
352 // Split on curly brackets. Even tokens are selectors, odd are values.
353 std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[}{]", cssstringcached.c_str()); // Must use c_str() as a std::string
354 // cannot be converted converted directly
355 // to a Glib::UStringView in Gtk4.
356
357 cssstringcached = "";
358 for (unsigned i = 0; i < tokens.size() - 1; i += 2) {
359 Glib::ustring selector = tokens[i];
360 Glib::ustring properties = "";
361 if ((i + 1) < tokens.size()) {
362 properties = tokens[i + 1];
363 }
364 if (properties.find(";") != Glib::ustring::npos) {
365 cssstringcached += selector;
366 cssstringcached += "{\n";
367 cssstringcached += properties;
368 cssstringcached += "}\n";
369 }
370 }
371 }
372 cssstring = cssdefined;
373 }
374 if (!cssstring.empty()) {
375 _contrastthemeprovider->load_from_data(cssstring);
376 Gtk::StyleProvider::add_provider_for_display(display, _contrastthemeprovider, GTK_STYLE_PROVIDER_PRIORITY_SETTINGS);
377 }
378 } else {
379 cssstringcached = "";
381 Gtk::StyleProvider::remove_provider_for_display(display, _contrastthemeprovider);
382 }
383 }
384 Glib::ustring style = get_filename(UIS, "style.css");
385 if (!style.empty()) {
386 if (_styleprovider) {
387 Gtk::StyleProvider::remove_provider_for_display(display, _styleprovider);
388 }
389 if (!_styleprovider) {
390 _styleprovider = Gtk::CssProvider::create();
391 }
392 try {
393 _styleprovider->load_from_path(style);
394 } catch (const Gtk::CssParserError &ex) {
395 g_critical("CSSProviderError::load_from_path(): failed to load '%s'\n(%s)", style.c_str(),
396 ex.what());
397 }
398 // note: priority higher than that of the theme, so we can override styles that not even higher specificity can patch
399 Gtk::StyleProvider::add_provider_for_display(display, _styleprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
400 }
401 // load small CSS snippet to style spinbuttons by removing excessive padding
402 if (!_spinbuttonprovider) {
403 _spinbuttonprovider = Gtk::CssProvider::create();
404 Glib::ustring style = get_filename(UIS, "spinbutton.css");
405 if (!style.empty()) {
406 try {
407 _spinbuttonprovider->load_from_path(style);
408 } catch (const Gtk::CssParserError &ex) {
409 g_critical("CSSProviderError::load_from_path(): failed to load '%s'\n(%s)", style.c_str(), ex.what());
410 }
411 }
412 }
413 _spinbutton_observer = std::make_unique<NarrowSpinbuttonObserver>("/theme/narrowSpinButton", _spinbuttonprovider);
414 // note: ideally we should remove the callback during destruction, but ThemeContext is never deleted
416 // establish default value, so both this setting here and checkbox in preferences are in sync
417 if (!prefs->getEntry(_spinbutton_observer->observed_path).isValidBool()) {
418 prefs->setBool(_spinbutton_observer->observed_path, true);
419 }
420 _spinbutton_observer->notify(prefs->getEntry(_spinbutton_observer->observed_path));
421
422 Glib::ustring gtkthemename = prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", ""));
423 gtkthemename += ".css";
424 style = get_filename(UIS, gtkthemename.c_str(), false, true);
425 if (!style.empty()) {
426 if (_themeprovider) {
427 Gtk::StyleProvider::remove_provider_for_display(display, _themeprovider);
428 }
429 if (!_themeprovider) {
430 _themeprovider = Gtk::CssProvider::create();
431 }
432 try {
433 _themeprovider->load_from_path(style);
434 } catch (const Gtk::CssParserError &ex) {
435 g_critical("CSSProviderError::load_from_path(): failed to load '%s'\n(%s)", style.c_str(),
436 ex.what());
437 }
438 Gtk::StyleProvider::add_provider_for_display(display, _themeprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
439 }
440
441 if (!_colorizeprovider) {
442 _colorizeprovider = Gtk::CssProvider::create();
443 }
444 Glib::ustring css_str = "";
445 if (prefs->getBool("/theme/symbolicIcons", false)) {
446 css_str = get_symbolic_colors();
447 }
448 try {
449 _colorizeprovider->load_from_data(css_str);
450 } catch (const Gtk::CssParserError &ex) {
451 g_critical("CSSProviderError::load_from_data(): failed to load '%s'\n(%s)", css_str.c_str(), ex.what());
452 }
453 Gtk::StyleProvider::add_provider_for_display(display, _colorizeprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
454
455#if __APPLE__
456 Glib::ustring macstyle = get_filename(UIS, "mac.css");
457 if (!macstyle.empty()) {
458 if (_macstyleprovider) {
459 Gtk::StyleProvider::remove_provider_for_display(display, _macstyleprovider);
460 }
461 if (!_macstyleprovider) {
462 _macstyleprovider = Gtk::CssProvider::create();
463 }
464 try {
465 _macstyleprovider->load_from_path(macstyle);
466 } catch (const Gtk::CssParserError &ex) {
467 g_critical("CSSProviderError::load_from_path(): failed to load '%s'\n(%s)", macstyle.c_str(), ex.what());
468 }
469 Gtk::StyleProvider::add_provider_for_display(display, _macstyleprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
470 }
471#endif
472
473 style = get_filename(UIS, "user.css");
474 if (!style.empty()) {
475 if (_userprovider) {
476 Gtk::StyleProvider::remove_provider_for_display(display, _userprovider);
477 }
478 if (!_userprovider) {
479 _userprovider = Gtk::CssProvider::create();
480 }
481 try {
482 _userprovider->load_from_path(style);
483 } catch (const Gtk::CssParserError &ex) {
484 g_critical("CSSProviderError::load_from_path(): failed to load '%s'\n(%s)", style.c_str(),
485 ex.what());
486 }
487 Gtk::StyleProvider::add_provider_for_display(display, _userprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
488 }
489}
490
498bool ThemeContext::isCurrentThemeDark(Gtk::Window * const window)
499{
500 if (!window) return false;
501
503 Glib::ustring current_theme =
504 prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", ""));
505
506 if (auto const settings = Gtk::Settings::get_default()) {
507 settings->property_gtk_application_prefer_dark_theme() = prefs->getBool("/theme/preferDarkTheme", false);
508 }
509
510 auto dark = current_theme.find(":dark") != std::string::npos;
511
512 // if theme is dark or we use contrast slider feature and have set preferDarkTheme we force the theme dark
513 // and avoid color check, this fix a issue with low contrast themes bad switch of dark theme toggle
514 dark = dark || (prefs->getInt("/theme/contrast", 10) != 10 && prefs->getBool("/theme/preferDarkTheme", false));
515 if (dark) return true;
516
517 // Otherwise, check the foreground color, and if that has luminance >= 50%, we conclude the theme is dark.
518 // Note: Use @theme_fg_color, since currentColor might not be set or correct
519 auto const rgba = get_color_with_class(*window, "theme_fg_color");
520 dark = get_luminance(rgba) >= 0.5;
521 return dark;
522}
523
524void
527 // sync "dark" class between app window and floating dialog windows to ensure that
528 // CSS providers relying on it apply in dialog windows too
529 auto dark = prefs->getBool("/theme/darkTheme", false);
530 std::vector<Gtk::Window *> winds;
532 winds.push_back(dynamic_cast<Gtk::Window *>(wnd));
533 }
534 if (auto desktops = INKSCAPE.get_desktops()) {
535 for (auto & desktop : *desktops) {
536 if (desktop == SP_ACTIVE_DESKTOP) {
537 winds.emplace_back(desktop->getInkscapeWindow());
538 } else {
539 winds.insert(winds.begin(), desktop->getInkscapeWindow());
540 }
541 }
542 }
543 for (auto wnd : winds) {
544 if (auto w = wnd->get_surface()) {
545 set_dark_titlebar(w, dark);
546 }
547 if (dark) {
548 wnd->add_css_class("dark");
549 wnd->remove_css_class("bright");
550 } else {
551 wnd->add_css_class("bright");
552 wnd->remove_css_class("dark");
553 }
554 if (prefs->getBool("/theme/symbolicIcons", false)) {
555 wnd->add_css_class("symbolic");
556 wnd->remove_css_class("regular");
557 } else {
558 wnd->add_css_class("regular");
559 wnd->remove_css_class("symbolic");
560 }
561#if (defined (_WIN32) || defined (_WIN64))
562 wnd->present();
563#endif
564 }
565
566 // set default highlight colors (dark/light theme-specific)
567 if (!winds.empty()) {
569 }
570
571 // select default syntax coloring theme, if needed
572 if (auto desktop = INKSCAPE.active_desktop()) {
574 }
575}
576
581std::vector<guint32> ThemeContext::getHighlightColors(Gtk::Window *window)
582{
583 std::vector<guint32> colors;
584 if (!window) return colors;
585
586 auto const child = window->get_child();
587 if (!child) return colors;
588
589 Glib::ustring name = "highlight-color-";
590
591 for (int i = 1; i <= 8; ++i) {
592 // The highlight colors will be attached to a GtkWidget
593 // but it isn't neccessary to use this in the .css file.
594 // N.B. We must use Window:child; Window itself gives a constant color.
595
596 auto const css_class = name + std::to_string(i);
597 child->add_css_class(css_class);
598
599 auto const rgba = child->get_color();
600 colors.push_back( to_guint32(rgba) );
601
602 child->remove_css_class(css_class);
603 }
604
605 return colors;
606}
607
609 if (factor < 0.1 || factor > 10) {
610 g_warning("Invalid font scaling factor %f in ThemeContext::adjust_global_font_scale", factor);
611 return;
612 }
613
614 auto display = Gdk::Display::get_default();
615 Gtk::StyleProvider::remove_provider_for_display(display, _fontsizeprovider);
616
618 os.precision(3);
619 os << "widget, menuitem, popover, box { font-size: " << factor << "rem; }\n";
620
621 os << ".mono-font {";
622 auto desc = getMonospacedFont();
623 os << "font-family: " << desc.get_family() << ";";
624 switch (desc.get_style()) {
625 case Pango::Style::ITALIC:
626 os << "font-style: italic;";
627 break;
628 case Pango::Style::OBLIQUE:
629 os << "font-style: oblique;";
630 break;
631 }
632 os << "font-weight: " << static_cast<int>(desc.get_weight()) << ";";
633 double size = desc.get_size();
634 os << "font-size: " << factor * (desc.get_size_is_absolute() ? size : size / Pango::SCALE) << "px;";
635 os << "}";
636
637 _fontsizeprovider->load_from_data(os.str());
638
639 // note: priority set to APP - 1 to make sure styles.css take precedence over generic font-size
640 Gtk::StyleProvider::add_provider_for_display(display, _fontsizeprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
641}
642
644#if WITH_GSOURCEVIEW
645 auto manager = gtk_source_style_scheme_manager_get_default();
646 // to reset path: gtk_source_style_scheme_manager_set_search_path(manager, nullptr);
648 gtk_source_style_scheme_manager_prepend_search_path(manager, themes.c_str());
649#endif
650}
651
653{
654#if WITH_GSOURCEVIEW
655 auto prefs = Inkscape::Preferences::get();
656 auto default_theme = prefs->getString("/theme/syntax-color-theme");
657 auto light = "inkscape-light";
658 auto dark = "inkscape-dark";
659 if (default_theme.empty() || default_theme == light || default_theme == dark) {
660 prefs->setString("/theme/syntax-color-theme", dark_theme ? dark : light);
661 }
662#endif
663}
664
665void ThemeContext::saveMonospacedFont(Pango::FontDescription desc)
666{
668}
669
670Pango::FontDescription ThemeContext::getMonospacedFont() const
671{
672 auto font = Preferences::get()->getString(get_monospaced_font_pref_path(), "Monospace 13");
673 return Pango::FontDescription(font);
674}
675
677{
678 return Preferences::get()->getDoubleLimited(get_font_scale_pref_path(), 100.0, 10.0, 500.0);
679}
680
685
686} // namespace Inkscape::UI
687
688/*
689 Local Variables:
690 mode:c++
691 c-file-style:"stroustrup"
692 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
693 indent-tabs-mode:nil
694 fill-column:99
695 End:
696*/
697// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
static constexpr uint16_t get_luminance(uint32_t r, uint32_t g, uint32_t b)
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
std::streamsize precision() const
bool isValidBool() const
Check if the preference value can be interpreted as a Boolean.
Base class for preference observers.
Definition preferences.h:82
Observer(Glib::ustring path)
Constructor.
Preference storage class.
Definition preferences.h:61
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
unsigned int getUInt(Glib::ustring const &pref_path, unsigned int def=0)
Retrieve an unsigned integer.
static Preferences * get()
Access the singleton Preferences object.
Entry const getEntry(Glib::ustring const &pref_path)
Retrieve a preference entry without specifying its type.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void addObserver(Observer &)
Register a preference observer.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
double getDoubleLimited(Glib::ustring const &pref_path, double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="")
Retrieve a limited floating point value.
static DialogManager & singleton()
std::vector< DialogWindow * > get_all_floating_dialog_windows()
static std::vector< guint32 > getHighlightColors(Gtk::Window *window)
Load the highlight colours from the current theme.
Definition themes.cpp:581
Glib::RefPtr< Gtk::CssProvider > _styleprovider
Definition themes.h:87
Glib::RefPtr< Gtk::CssProvider > _userprovider
Definition themes.h:92
Pango::FontDescription getMonospacedFont() const
User-selected monospaced font used by XML dialog and attribute editor.
Definition themes.cpp:670
Glib::RefPtr< Gtk::CssProvider > _themeprovider
Definition themes.h:88
std::unique_ptr< Preferences::Observer > _spinbutton_observer
Definition themes.h:96
std::map< Glib::ustring, bool > gtkThemeList
Definition themes.h:42
Glib::RefPtr< Gtk::CssProvider > _colorizeprovider
Definition themes.h:90
Glib::ustring get_symbolic_colors()
Definition themes.cpp:154
bool isCurrentThemeDark(Gtk::Window *window)
Check if current applied theme is dark or not by looking at style context.
Definition themes.cpp:498
static void select_default_syntax_style(bool dark_theme)
Definition themes.cpp:652
void inkscape_fill_gtk(const gchar *path, gtkThemeList &themes)
Inkscape fill gtk, taken from glib/gtk code with our own checks.
Definition themes.cpp:65
std::map< Glib::ustring, bool > get_available_themes()
Get available themes based on locations of gtk directories.
Definition themes.cpp:96
void add_gtk_css(bool only_providers, bool cached=false)
Add our CSS style sheets.
Definition themes.cpp:264
Glib::RefPtr< Gtk::CssProvider > _macstyleprovider
Definition themes.h:94
void adjustGlobalFontScale(double factor)
Set application-wide font size adjustment by a factor, where 1 is 100% (no change)
Definition themes.cpp:608
Glib::RefPtr< Gtk::CssProvider > _spinbuttonprovider
Definition themes.h:91
Glib::RefPtr< Gtk::CssProvider > _fontsizeprovider
Definition themes.h:97
double getFontScale() const
Get current font scaling factor (50 - 150, percent of "normal" size)
Definition themes.cpp:676
static void initialize_source_syntax_styles()
Definition themes.cpp:643
void saveMonospacedFont(Pango::FontDescription desc)
Definition themes.cpp:665
static Glib::ustring get_monospaced_font_pref_path()
Definition themes.h:73
void saveFontScale(double scale)
Save font scaling factor in preferences.
Definition themes.cpp:681
static Glib::ustring get_font_scale_pref_path()
Definition themes.h:68
Glib::RefPtr< Gtk::CssProvider > _contrastthemeprovider
Definition themes.h:89
InkscapeWindow const * getInkscapeWindow() const
Definition desktop.cpp:975
const double w
Definition conic-4.cpp:19
TODO: insert short description here.
Editable view implementation.
A window for floating docks.
unsigned int guint32
Inkscape - An SVG editor.
Definition desktop.h:50
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...
Definition utils.cpp:70
std::string get_path_string(Domain domain, Type type, char const *filename, char const *extra)
Definition resource.cpp:148
User interface code.
Definition desktop.h:113
static void show_parsing_error(const Glib::RefPtr< const Gtk::CssSection > &section, const Glib::Error &error)
Definition themes.cpp:235
std::string sp_tweak_background_colors(std::string cssstring, double crossfade, double contrast, bool dark)
Definition themes.cpp:206
Glib::ustring format_classic(T const &... args)
STL namespace.
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
Inkscape::IO::Resource - simple resource API.
void set_default_highlight_colors(std::vector< guint32 > colors)
SPDesktop * desktop
Gtk <themes> helper code.
std::unique_ptr< Toolbar >(* create)()
Definition toolbars.cpp:56
Glib::ustring name
Definition toolbars.cpp:55
guint32 to_guint32(Gdk::RGBA const &rgba)
Definition util.cpp:297
Gdk::RGBA get_color_with_class(Gtk::Widget &widget, Glib::ustring const &css_class)
Definition util.cpp:288
void set_dark_titlebar(Glib::RefPtr< Gdk::Surface > const &surface, bool is_dark)
Definition util.cpp:418