From 27c4c19333af35e15f834ecc1cde3ad2cb94b6e3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 10 Nov 2020 15:07:07 -0500 Subject: [PATCH 1/5] Don't hide widgets in dispose() This is leftover code from when widgets were hidden by default, and was setting them back to their initial state. This is getting in the way now, as hiding the widget updates the HIDDEN accessible state, which ends up re-creating the at context that we've already disposed of, leading to memory leaks. --- gtk/gtkwidget.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index c29cc88ec0..8d6e4067d5 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -7040,8 +7040,6 @@ gtk_widget_dispose (GObject *object) if (priv->parent) gtk_widget_unparent (widget); - else if (_gtk_widget_get_visible (widget)) - gtk_widget_hide (widget); while (priv->paintables) gtk_widget_paintable_set_widget (priv->paintables->data, NULL); From 5ed3ad6cc802b39b02262cdcf5c2be032ff2e5fe Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 10 Nov 2020 16:12:16 -0500 Subject: [PATCH 2/5] gtk-demo: Split off SvgPaintable Put the SvgPaintable implementation into its own source files, for ease of copying. --- demos/gtk-demo/demo.gresource.xml | 2 + demos/gtk-demo/meson.build | 1 + demos/gtk-demo/paintable_svg.c | 173 +----------------------------- demos/gtk-demo/svgpaintable.c | 166 ++++++++++++++++++++++++++++ demos/gtk-demo/svgpaintable.h | 13 +++ 5 files changed, 187 insertions(+), 168 deletions(-) create mode 100644 demos/gtk-demo/svgpaintable.c create mode 100644 demos/gtk-demo/svgpaintable.h diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index 32b830a67b..a6e6e48a8f 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -208,6 +208,8 @@ demo3widget.ui + svgpaintable.h + svgpaintable.c org.gtk.gtk4.NodeEditor.Devel.svg diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index b14f1e04f2..fcddbde09e 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -139,6 +139,7 @@ librsvg_dep = dependency('librsvg-2.0', version: '>= 2.46.0', required: false) if librsvg_dep.found() demos += files('paintable_svg.c') + extra_demo_sources += files(['svgpaintable.c']) gtkdemo_deps += [ librsvg_dep ] endif diff --git a/demos/gtk-demo/paintable_svg.c b/demos/gtk-demo/paintable_svg.c index 975c650f93..eab506f4ed 100644 --- a/demos/gtk-demo/paintable_svg.c +++ b/demos/gtk-demo/paintable_svg.c @@ -9,183 +9,20 @@ #include #include -#define SVG_TYPE_PAINTABLE (svg_paintable_get_type ()) +#include "svgpaintable.h" -G_DECLARE_FINAL_TYPE (SvgPaintable, svg_paintable, SVG, PAINTABLE, GObject) - -struct _SvgPaintable -{ - GObject parent_instance; - GFile *file; - RsvgHandle *handle; -}; - -struct _SvgPaintableClass -{ - GObjectClass parent_class; -}; - -enum { - PROP_FILE = 1, - NUM_PROPERTIES -}; - -static void -svg_paintable_snapshot (GdkPaintable *paintable, - GdkSnapshot *snapshot, - double width, - double height) -{ - SvgPaintable *self = SVG_PAINTABLE (paintable); - cairo_t *cr; - GError *error = NULL; - - cr = gtk_snapshot_append_cairo (GTK_SNAPSHOT (snapshot), - &GRAPHENE_RECT_INIT (0, 0, width, height)); - - if (!rsvg_handle_render_document (self->handle, cr, - &(RsvgRectangle) {0, 0, width, height}, - &error)) - { - g_error ("%s", error->message); - } - - cairo_destroy (cr); -} - -static int -svg_paintable_get_intrinsic_width (GdkPaintable *paintable) -{ - SvgPaintable *self = SVG_PAINTABLE (paintable); - RsvgDimensionData data; - - rsvg_handle_get_dimensions (self->handle, &data); - - return data.width; -} - -static int -svg_paintable_get_intrinsic_height (GdkPaintable *paintable) -{ - SvgPaintable *self = SVG_PAINTABLE (paintable); - RsvgDimensionData data; - - rsvg_handle_get_dimensions (self->handle, &data); - - return data.height; -} - -static void -svg_paintable_init_interface (GdkPaintableInterface *iface) -{ - iface->snapshot = svg_paintable_snapshot; - iface->get_intrinsic_width = svg_paintable_get_intrinsic_width; - iface->get_intrinsic_height = svg_paintable_get_intrinsic_height; -} - -G_DEFINE_TYPE_WITH_CODE (SvgPaintable, svg_paintable, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, - svg_paintable_init_interface)) - -static void -svg_paintable_init (SvgPaintable *self) -{ -} - -static void -svg_paintable_dispose (GObject *object) -{ - SvgPaintable *self = SVG_PAINTABLE (object); - - g_clear_object (&self->file); - g_clear_object (&self->handle); - - G_OBJECT_CLASS (svg_paintable_parent_class)->dispose (object); -} - -static void -svg_paintable_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SvgPaintable *self = SVG_PAINTABLE (object); - - switch (prop_id) - { - case PROP_FILE: - { - GFile *file = g_value_get_object (value); - RsvgHandle *handle = rsvg_handle_new_from_gfile_sync (file, - RSVG_HANDLE_FLAGS_NONE, - NULL, - NULL); - rsvg_handle_set_dpi (handle, 90); - - g_set_object (&self->file, file); - g_set_object (&self->handle, handle); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -svg_paintable_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SvgPaintable *self = SVG_PAINTABLE (object); - - switch (prop_id) - { - case PROP_FILE: - g_value_set_object (value, self->file); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - - -static void -svg_paintable_class_init (SvgPaintableClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->dispose = svg_paintable_dispose; - object_class->set_property = svg_paintable_set_property; - object_class->get_property = svg_paintable_get_property; - - g_object_class_install_property (object_class, PROP_FILE, - g_param_spec_object ("file", "File", "File", - G_TYPE_FILE, - G_PARAM_READWRITE)); -} - -static SvgPaintable * -svg_paintable_new (GFile *file) -{ - return g_object_new (SVG_TYPE_PAINTABLE, - "file", file, - NULL); -} static void file_set (GtkFileChooserButton *button, GtkWidget *picture) { GFile *file; - SvgPaintable *paintable; + GdkPaintable *paintable; file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (button)); paintable = svg_paintable_new (file); - gtk_picture_set_paintable (GTK_PICTURE (picture), GDK_PAINTABLE (paintable)); + gtk_picture_set_paintable (GTK_PICTURE (picture), paintable); g_object_unref (paintable); g_object_unref (file); @@ -201,7 +38,7 @@ do_paintable_svg (GtkWidget *do_widget) GtkFileFilter *filter; GtkWidget *button; GFile *file; - SvgPaintable *paintable; + GdkPaintable *paintable; if (!window) { @@ -228,7 +65,7 @@ do_paintable_svg (GtkWidget *do_widget) file = g_file_new_for_uri ("resource:///paintable_svg/org.gtk.gtk4.NodeEditor.Devel.svg"); paintable = svg_paintable_new (file); - gtk_picture_set_paintable (GTK_PICTURE (picture), GDK_PAINTABLE (paintable)); + gtk_picture_set_paintable (GTK_PICTURE (picture), paintable); g_object_unref (paintable); g_object_unref (file); } diff --git a/demos/gtk-demo/svgpaintable.c b/demos/gtk-demo/svgpaintable.c new file mode 100644 index 0000000000..21879ed0d9 --- /dev/null +++ b/demos/gtk-demo/svgpaintable.c @@ -0,0 +1,166 @@ +#include "svgpaintable.h" + +#include +#include + +struct _SvgPaintable +{ + GObject parent_instance; + GFile *file; + RsvgHandle *handle; +}; + +struct _SvgPaintableClass +{ + GObjectClass parent_class; +}; + +enum { + PROP_FILE = 1, + NUM_PROPERTIES +}; + +static void +svg_paintable_snapshot (GdkPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height) +{ + SvgPaintable *self = SVG_PAINTABLE (paintable); + cairo_t *cr; + GError *error = NULL; + + cr = gtk_snapshot_append_cairo (GTK_SNAPSHOT (snapshot), + &GRAPHENE_RECT_INIT (0, 0, width, height)); + + if (!rsvg_handle_render_document (self->handle, cr, + &(RsvgRectangle) {0, 0, width, height}, + &error)) + { + g_error ("%s", error->message); + } + + cairo_destroy (cr); +} + +static int +svg_paintable_get_intrinsic_width (GdkPaintable *paintable) +{ + SvgPaintable *self = SVG_PAINTABLE (paintable); + RsvgDimensionData data; + + rsvg_handle_get_dimensions (self->handle, &data); + + return data.width; +} + +static int +svg_paintable_get_intrinsic_height (GdkPaintable *paintable) +{ + SvgPaintable *self = SVG_PAINTABLE (paintable); + RsvgDimensionData data; + + rsvg_handle_get_dimensions (self->handle, &data); + + return data.height; +} + +static void +svg_paintable_init_interface (GdkPaintableInterface *iface) +{ + iface->snapshot = svg_paintable_snapshot; + iface->get_intrinsic_width = svg_paintable_get_intrinsic_width; + iface->get_intrinsic_height = svg_paintable_get_intrinsic_height; +} + +G_DEFINE_TYPE_WITH_CODE (SvgPaintable, svg_paintable, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, + svg_paintable_init_interface)) + +static void +svg_paintable_init (SvgPaintable *self) +{ +} + +static void +svg_paintable_dispose (GObject *object) +{ + SvgPaintable *self = SVG_PAINTABLE (object); + + g_clear_object (&self->file); + g_clear_object (&self->handle); + + G_OBJECT_CLASS (svg_paintable_parent_class)->dispose (object); +} + +static void +svg_paintable_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SvgPaintable *self = SVG_PAINTABLE (object); + + switch (prop_id) + { + case PROP_FILE: + { + GFile *file = g_value_get_object (value); + RsvgHandle *handle = rsvg_handle_new_from_gfile_sync (file, + RSVG_HANDLE_FLAGS_NONE, + NULL, + NULL); + rsvg_handle_set_dpi (handle, 90); + + g_set_object (&self->file, file); + g_set_object (&self->handle, handle); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +svg_paintable_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SvgPaintable *self = SVG_PAINTABLE (object); + + switch (prop_id) + { + case PROP_FILE: + g_value_set_object (value, self->file); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + + +static void +svg_paintable_class_init (SvgPaintableClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = svg_paintable_dispose; + object_class->set_property = svg_paintable_set_property; + object_class->get_property = svg_paintable_get_property; + + g_object_class_install_property (object_class, PROP_FILE, + g_param_spec_object ("file", "File", "File", + G_TYPE_FILE, + G_PARAM_READWRITE)); +} + +GdkPaintable * +svg_paintable_new (GFile *file) +{ + return g_object_new (SVG_TYPE_PAINTABLE, + "file", file, + NULL); +} diff --git a/demos/gtk-demo/svgpaintable.h b/demos/gtk-demo/svgpaintable.h new file mode 100644 index 0000000000..f6015e517e --- /dev/null +++ b/demos/gtk-demo/svgpaintable.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +#define SVG_TYPE_PAINTABLE (svg_paintable_get_type ()) + +G_DECLARE_FINAL_TYPE (SvgPaintable, svg_paintable, SVG, PAINTABLE, GObject) + +GdkPaintable * svg_paintable_new (GFile *file); + +G_END_DECLS From 0db504edde63eb77e801a5b8f4e70008e77d64dd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 11 Nov 2020 10:35:09 -0500 Subject: [PATCH 3/5] label: Optimize GtkLabel setters The overarching goal here is to not queue a resize unless something has actually changed. In columnview scenarios, we often deal with hundreds of labels. Labels are cattle, not pets. --- gtk/gtklabel.c | 67 ++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 6aedef6dd0..26c6419460 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -449,7 +449,7 @@ static gboolean gtk_label_query_tooltip (GtkWidget *widget, static void gtk_label_set_text_internal (GtkLabel *self, char *str); -static void gtk_label_set_label_internal (GtkLabel *self, +static gboolean gtk_label_set_label_internal (GtkLabel *self, char *str); static gboolean gtk_label_set_use_markup_internal (GtkLabel *self, gboolean val); @@ -1702,15 +1702,22 @@ gtk_label_set_text_internal (GtkLabel *self, gtk_label_select_region_index (self, 0, 0); } -static void +static gboolean gtk_label_set_label_internal (GtkLabel *self, - char *str) + char *str) { - g_free (self->label); + if (g_strcmp0 (str, self->label) == 0) + { + g_free (str); + return FALSE; + } + g_free (self->label); self->label = str; g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_LABEL]); + + return TRUE; } static gboolean @@ -1784,7 +1791,7 @@ gtk_label_recalculate (GtkLabel *self) * @str: The text you want to set * * Sets the text within the #GtkLabel widget. It overwrites any text that - * was there before. + * was there before. * * This function will clear any previously set mnemonic accelerators, and * set the #GtkLabel:use-underline property to %FALSE as a side effect. @@ -1796,17 +1803,16 @@ gtk_label_recalculate (GtkLabel *self) **/ void gtk_label_set_text (GtkLabel *self, - const char *str) + const char *str) { g_return_if_fail (GTK_IS_LABEL (self)); - + g_object_freeze_notify (G_OBJECT (self)); - gtk_label_set_label_internal (self, g_strdup (str ? str : "")); - gtk_label_set_use_markup_internal (self, FALSE); - gtk_label_set_use_underline_internal (self, FALSE); - - gtk_label_recalculate (self); + if (gtk_label_set_label_internal (self, g_strdup (str ? str : "")) || + gtk_label_set_use_markup_internal (self, FALSE) || + gtk_label_set_use_underline_internal (self, FALSE)) + gtk_label_recalculate (self); g_object_thaw_notify (G_OBJECT (self)); } @@ -1882,14 +1888,14 @@ gtk_label_get_attributes (GtkLabel *self) **/ void gtk_label_set_label (GtkLabel *self, - const char *str) + const char *str) { g_return_if_fail (GTK_IS_LABEL (self)); g_object_freeze_notify (G_OBJECT (self)); - gtk_label_set_label_internal (self, g_strdup (str ? str : "")); - gtk_label_recalculate (self); + if (gtk_label_set_label_internal (self, g_strdup (str ? str : ""))) + gtk_label_recalculate (self); g_object_thaw_notify (G_OBJECT (self)); } @@ -2413,11 +2419,10 @@ gtk_label_set_markup (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - gtk_label_set_label_internal (self, g_strdup (str ? str : "")); - gtk_label_set_use_markup_internal (self, TRUE); - gtk_label_set_use_underline_internal (self, FALSE); - - gtk_label_recalculate (self); + if (gtk_label_set_label_internal (self, g_strdup (str ? str : "")) || + gtk_label_set_use_markup_internal (self, TRUE) || + gtk_label_set_use_underline_internal (self, FALSE)) + gtk_label_recalculate (self); g_object_thaw_notify (G_OBJECT (self)); } @@ -2445,11 +2450,10 @@ gtk_label_set_markup_with_mnemonic (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - gtk_label_set_label_internal (self, g_strdup (str ? str : "")); - gtk_label_set_use_markup_internal (self, TRUE); - gtk_label_set_use_underline_internal (self, TRUE); - - gtk_label_recalculate (self); + if (gtk_label_set_label_internal (self, g_strdup (str ? str : "")) || + gtk_label_set_use_markup_internal (self, TRUE) || + gtk_label_set_use_underline_internal (self, TRUE)) + gtk_label_recalculate (self); g_object_thaw_notify (G_OBJECT (self)); } @@ -3568,11 +3572,10 @@ gtk_label_set_text_with_mnemonic (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - gtk_label_set_label_internal (self, g_strdup (str)); - gtk_label_set_use_markup_internal (self, FALSE); - gtk_label_set_use_underline_internal (self, TRUE); - - gtk_label_recalculate (self); + if (gtk_label_set_label_internal (self, g_strdup (str)) || + gtk_label_set_use_markup_internal (self, FALSE) || + gtk_label_set_use_underline_internal (self, TRUE)) + gtk_label_recalculate (self); g_object_thaw_notify (G_OBJECT (self)); } @@ -4825,7 +4828,7 @@ gtk_label_get_layout_offsets (GtkLabel *self, **/ void gtk_label_set_use_markup (GtkLabel *self, - gboolean setting) + gboolean setting) { g_return_if_fail (GTK_IS_LABEL (self)); @@ -4865,7 +4868,7 @@ gtk_label_get_use_markup (GtkLabel *self) */ void gtk_label_set_use_underline (GtkLabel *self, - gboolean setting) + gboolean setting) { g_return_if_fail (GTK_IS_LABEL (self)); From 82b855cc4b4127bd5f1f8b41a8808433375c0bd7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 11 Nov 2020 12:24:35 -0500 Subject: [PATCH 4/5] label: Avoid a needless strdup Don't duplicate the string before we know if it has actually changed. --- gtk/gtklabel.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 26c6419460..3d82096bfb 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -450,7 +450,7 @@ static gboolean gtk_label_query_tooltip (GtkWidget *widget, static void gtk_label_set_text_internal (GtkLabel *self, char *str); static gboolean gtk_label_set_label_internal (GtkLabel *self, - char *str); + const char *str); static gboolean gtk_label_set_use_markup_internal (GtkLabel *self, gboolean val); static gboolean gtk_label_set_use_underline_internal (GtkLabel *self, @@ -1703,17 +1703,14 @@ gtk_label_set_text_internal (GtkLabel *self, } static gboolean -gtk_label_set_label_internal (GtkLabel *self, - char *str) +gtk_label_set_label_internal (GtkLabel *self, + const char *str) { if (g_strcmp0 (str, self->label) == 0) - { - g_free (str); - return FALSE; - } + return FALSE; g_free (self->label); - self->label = str; + self->label = g_strdup (str ? str : ""); g_object_notify_by_pspec (G_OBJECT (self), label_props[PROP_LABEL]); @@ -1809,7 +1806,7 @@ gtk_label_set_text (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - if (gtk_label_set_label_internal (self, g_strdup (str ? str : "")) || + if (gtk_label_set_label_internal (self, str) || gtk_label_set_use_markup_internal (self, FALSE) || gtk_label_set_use_underline_internal (self, FALSE)) gtk_label_recalculate (self); @@ -1894,7 +1891,7 @@ gtk_label_set_label (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - if (gtk_label_set_label_internal (self, g_strdup (str ? str : ""))) + if (gtk_label_set_label_internal (self, str)) gtk_label_recalculate (self); g_object_thaw_notify (G_OBJECT (self)); @@ -2419,7 +2416,7 @@ gtk_label_set_markup (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - if (gtk_label_set_label_internal (self, g_strdup (str ? str : "")) || + if (gtk_label_set_label_internal (self, str) || gtk_label_set_use_markup_internal (self, TRUE) || gtk_label_set_use_underline_internal (self, FALSE)) gtk_label_recalculate (self); @@ -2450,7 +2447,7 @@ gtk_label_set_markup_with_mnemonic (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - if (gtk_label_set_label_internal (self, g_strdup (str ? str : "")) || + if (gtk_label_set_label_internal (self, str) || gtk_label_set_use_markup_internal (self, TRUE) || gtk_label_set_use_underline_internal (self, TRUE)) gtk_label_recalculate (self); @@ -3572,7 +3569,7 @@ gtk_label_set_text_with_mnemonic (GtkLabel *self, g_object_freeze_notify (G_OBJECT (self)); - if (gtk_label_set_label_internal (self, g_strdup (str)) || + if (gtk_label_set_label_internal (self, str) || gtk_label_set_use_markup_internal (self, FALSE) || gtk_label_set_use_underline_internal (self, TRUE)) gtk_label_recalculate (self); From bfe5b0d1b7a13749642a94a8a75fd82b6401a9ae Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 11 Nov 2020 13:07:31 -0500 Subject: [PATCH 5/5] Adwaita: Don't do n-th child things for list rows Making the list row child css depend on the position is very expensive, and does not acutally work correctly (since we don't have widgets for all children, so the position of the child widget does not reflect the actual model item position). To make this more palatable, use the bottom border instead of the top border, since most lists have a natural border at the top (with headers), and may end up with empty space at the bottom. --- gtk/theme/Adwaita/_common.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gtk/theme/Adwaita/_common.scss b/gtk/theme/Adwaita/_common.scss index a552965cdd..8b5c53bdec 100644 --- a/gtk/theme/Adwaita/_common.scss +++ b/gtk/theme/Adwaita/_common.scss @@ -3098,13 +3098,13 @@ list { > row.expander { padding: 0px; } > row.expander .row-header { padding: 2px; } - &.horizontal row.separator:not(:first-child), - &.separators.horizontal > row:not(:first-child) { + &.horizontal row.separator, + &.separators.horizontal > row { border-left: 1px solid $_treeview_borders_color; } - &:not(.horizontal) row.separator:not(:first-child), - &.separators:not(.horizontal) > row:not(:first-child) { - border-top: 1px solid $_treeview_borders_color; + &:not(.horizontal) row.separator, + &.separators:not(.horizontal) > row { + border-bottom: 1px solid $_treeview_borders_color; } }