497e66ce6a
Will be used by the next commit to avoid going through the GObject machinery when retrieving the scale factor. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1176
488 lines
14 KiB
C
488 lines
14 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
/*
|
|
* st-theme-context.c: holds global information about a tree of styled objects
|
|
*
|
|
* Copyright 2009, 2010 Red Hat, Inc.
|
|
* Copyright 2009 Florian Müllner
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation, either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "st-private.h"
|
|
#include "st-settings.h"
|
|
#include "st-texture-cache.h"
|
|
#include "st-theme.h"
|
|
#include "st-theme-context.h"
|
|
#include "st-theme-node-private.h"
|
|
|
|
struct _StThemeContext {
|
|
GObject parent;
|
|
|
|
PangoFontDescription *font;
|
|
StThemeNode *root_node;
|
|
StTheme *theme;
|
|
|
|
/* set of StThemeNode */
|
|
GHashTable *nodes;
|
|
|
|
gulong stylesheets_changed_id;
|
|
|
|
int scale_factor;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_SCALE_FACTOR
|
|
};
|
|
|
|
enum
|
|
{
|
|
CHANGED,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0, };
|
|
|
|
G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
|
|
|
|
static PangoFontDescription *get_interface_font_description (void);
|
|
static void on_font_name_changed (StSettings *settings,
|
|
GParamSpec *pspec,
|
|
StThemeContext *context);
|
|
static void on_icon_theme_changed (StTextureCache *cache,
|
|
StThemeContext *context);
|
|
static void st_theme_context_changed (StThemeContext *context);
|
|
|
|
static void st_theme_context_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void st_theme_context_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void
|
|
st_theme_context_finalize (GObject *object)
|
|
{
|
|
StThemeContext *context = ST_THEME_CONTEXT (object);
|
|
|
|
g_signal_handlers_disconnect_by_func (st_settings_get (),
|
|
(gpointer) on_font_name_changed,
|
|
context);
|
|
g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
|
|
(gpointer) on_icon_theme_changed,
|
|
context);
|
|
g_signal_handlers_disconnect_by_func (clutter_get_default_backend (),
|
|
(gpointer) st_theme_context_changed,
|
|
context);
|
|
|
|
g_clear_signal_handler (&context->stylesheets_changed_id, context->theme);
|
|
|
|
if (context->nodes)
|
|
g_hash_table_unref (context->nodes);
|
|
if (context->root_node)
|
|
g_object_unref (context->root_node);
|
|
if (context->theme)
|
|
g_object_unref (context->theme);
|
|
|
|
pango_font_description_free (context->font);
|
|
|
|
G_OBJECT_CLASS (st_theme_context_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
st_theme_context_class_init (StThemeContextClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->set_property = st_theme_context_set_property;
|
|
object_class->get_property = st_theme_context_get_property;
|
|
object_class->finalize = st_theme_context_finalize;
|
|
|
|
/**
|
|
* StThemeContext:scale-factor:
|
|
*
|
|
* The scaling factor used or high dpi scaling.
|
|
*/
|
|
g_object_class_install_property (object_class,
|
|
PROP_SCALE_FACTOR,
|
|
g_param_spec_int ("scale-factor",
|
|
"Scale factor",
|
|
"Integer scale factor used for high dpi scaling",
|
|
0, G_MAXINT, 1,
|
|
ST_PARAM_READWRITE));
|
|
|
|
signals[CHANGED] =
|
|
g_signal_new ("changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, /* no default handler slot */
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
st_theme_context_init (StThemeContext *context)
|
|
{
|
|
context->font = get_interface_font_description ();
|
|
|
|
g_signal_connect (st_settings_get (),
|
|
"notify::font-name",
|
|
G_CALLBACK (on_font_name_changed),
|
|
context);
|
|
g_signal_connect (st_texture_cache_get_default (),
|
|
"icon-theme-changed",
|
|
G_CALLBACK (on_icon_theme_changed),
|
|
context);
|
|
g_signal_connect_swapped (clutter_get_default_backend (),
|
|
"resolution-changed",
|
|
G_CALLBACK (st_theme_context_changed),
|
|
context);
|
|
|
|
context->nodes = g_hash_table_new_full ((GHashFunc) st_theme_node_hash,
|
|
(GEqualFunc) st_theme_node_equal,
|
|
g_object_unref, NULL);
|
|
context->scale_factor = 1;
|
|
}
|
|
|
|
static void
|
|
st_theme_context_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
StThemeContext *context = ST_THEME_CONTEXT (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SCALE_FACTOR:
|
|
{
|
|
int scale_factor = g_value_get_int (value);
|
|
if (scale_factor != context->scale_factor)
|
|
{
|
|
context->scale_factor = scale_factor;
|
|
st_theme_context_changed (context);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
st_theme_context_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
StThemeContext *context = ST_THEME_CONTEXT (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SCALE_FACTOR:
|
|
g_value_set_int (value, context->scale_factor);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_new:
|
|
*
|
|
* Create a new theme context not associated with any #ClutterStage.
|
|
* This can be useful in testing scenarios, or if using StThemeContext
|
|
* with something other than #ClutterActor objects, but you generally
|
|
* should use st_theme_context_get_for_stage() instead.
|
|
*/
|
|
StThemeContext *
|
|
st_theme_context_new (void)
|
|
{
|
|
StThemeContext *context;
|
|
|
|
context = g_object_new (ST_TYPE_THEME_CONTEXT, NULL);
|
|
|
|
return context;
|
|
}
|
|
|
|
static PangoFontDescription *
|
|
get_interface_font_description (void)
|
|
{
|
|
StSettings *settings = st_settings_get ();
|
|
g_autofree char *font_name = NULL;
|
|
|
|
g_object_get (settings, "font-name", &font_name, NULL);
|
|
return pango_font_description_from_string (font_name);
|
|
}
|
|
|
|
static void
|
|
on_stage_destroy (ClutterStage *stage)
|
|
{
|
|
StThemeContext *context = st_theme_context_get_for_stage (stage);
|
|
|
|
g_object_set_data (G_OBJECT (stage), "st-theme-context", NULL);
|
|
g_object_unref (context);
|
|
}
|
|
|
|
static void
|
|
st_theme_context_changed (StThemeContext *context)
|
|
{
|
|
StThemeNode *old_root = context->root_node;
|
|
context->root_node = NULL;
|
|
g_hash_table_remove_all (context->nodes);
|
|
|
|
g_signal_emit (context, signals[CHANGED], 0);
|
|
|
|
if (old_root)
|
|
g_object_unref (old_root);
|
|
}
|
|
|
|
static void
|
|
on_font_name_changed (StSettings *settings,
|
|
GParamSpec *pspect,
|
|
StThemeContext *context)
|
|
{
|
|
PangoFontDescription *font_desc = get_interface_font_description ();
|
|
st_theme_context_set_font (context, font_desc);
|
|
|
|
pango_font_description_free (font_desc);
|
|
}
|
|
|
|
static gboolean
|
|
changed_idle (gpointer userdata)
|
|
{
|
|
st_theme_context_changed (userdata);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
on_icon_theme_changed (StTextureCache *cache,
|
|
StThemeContext *context)
|
|
{
|
|
guint id;
|
|
|
|
/* Note that an icon theme change isn't really a change of the StThemeContext;
|
|
* the style information has changed. But since the style factors into the
|
|
* icon_name => icon lookup, faking a theme context change is a good way
|
|
* to force users such as StIcon to look up icons again.
|
|
*/
|
|
id = g_idle_add ((GSourceFunc) changed_idle, context);
|
|
g_source_set_name_by_id (id, "[gnome-shell] changed_idle");
|
|
}
|
|
|
|
static void
|
|
on_custom_stylesheets_changed (StTheme *theme,
|
|
StThemeContext *context)
|
|
{
|
|
GHashTableIter iter;
|
|
StThemeNode *node;
|
|
|
|
if (context->root_node)
|
|
_st_theme_node_reset_for_stylesheet_change (context->root_node);
|
|
|
|
g_hash_table_iter_init (&iter, context->nodes);
|
|
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &node, NULL))
|
|
_st_theme_node_reset_for_stylesheet_change (node);
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_get_for_stage:
|
|
* @stage: a #ClutterStage
|
|
*
|
|
* Gets a singleton theme context associated with the stage.
|
|
*
|
|
* Return value: (transfer none): the singleton theme context for the stage
|
|
*/
|
|
StThemeContext *
|
|
st_theme_context_get_for_stage (ClutterStage *stage)
|
|
{
|
|
StThemeContext *context;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
|
|
|
|
context = g_object_get_data (G_OBJECT (stage), "st-theme-context");
|
|
if (context)
|
|
return context;
|
|
|
|
context = st_theme_context_new ();
|
|
g_object_set_data (G_OBJECT (stage), "st-theme-context", context);
|
|
g_signal_connect (stage, "destroy",
|
|
G_CALLBACK (on_stage_destroy), NULL);
|
|
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_set_theme:
|
|
* @context: a #StThemeContext
|
|
*
|
|
* Sets the default set of theme stylesheets for the context. This theme will
|
|
* be used for the root node and for nodes descending from it, unless some other
|
|
* style is explicitely specified.
|
|
*/
|
|
void
|
|
st_theme_context_set_theme (StThemeContext *context,
|
|
StTheme *theme)
|
|
{
|
|
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
|
g_return_if_fail (theme == NULL || ST_IS_THEME (theme));
|
|
|
|
if (context->theme != theme)
|
|
{
|
|
if (context->theme)
|
|
g_clear_signal_handler (&context->stylesheets_changed_id, context->theme);
|
|
|
|
g_set_object (&context->theme, theme);
|
|
|
|
if (context->theme)
|
|
{
|
|
context->stylesheets_changed_id =
|
|
g_signal_connect (context->theme, "custom-stylesheets-changed",
|
|
G_CALLBACK (on_custom_stylesheets_changed),
|
|
context);
|
|
}
|
|
|
|
st_theme_context_changed (context);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_get_theme:
|
|
* @context: a #StThemeContext
|
|
*
|
|
* Gets the default theme for the context. See st_theme_context_set_theme()
|
|
*
|
|
* Return value: (transfer none): the default theme for the context
|
|
*/
|
|
StTheme *
|
|
st_theme_context_get_theme (StThemeContext *context)
|
|
{
|
|
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
|
|
|
|
return context->theme;
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_set_font:
|
|
* @context: a #StThemeContext
|
|
* @font: the default font for theme context
|
|
*
|
|
* Sets the default font for the theme context. This is the font that
|
|
* is inherited by the root node of the tree of theme nodes. If the
|
|
* font is not overriden, then this font will be used. If the font is
|
|
* partially modified (for example, with 'font-size: 110%', then that
|
|
* modification is based on this font.
|
|
*/
|
|
void
|
|
st_theme_context_set_font (StThemeContext *context,
|
|
const PangoFontDescription *font)
|
|
{
|
|
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
|
|
g_return_if_fail (font != NULL);
|
|
|
|
if (context->font == font ||
|
|
pango_font_description_equal (context->font, font))
|
|
return;
|
|
|
|
pango_font_description_free (context->font);
|
|
context->font = pango_font_description_copy (font);
|
|
st_theme_context_changed (context);
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_get_font:
|
|
* @context: a #StThemeContext
|
|
*
|
|
* Gets the default font for the theme context. See st_theme_context_set_font().
|
|
*
|
|
* Return value: the default font for the theme context.
|
|
*/
|
|
const PangoFontDescription *
|
|
st_theme_context_get_font (StThemeContext *context)
|
|
{
|
|
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
|
|
|
|
return context->font;
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_get_root_node:
|
|
* @context: a #StThemeContext
|
|
*
|
|
* Gets the root node of the tree of theme style nodes that associated with this
|
|
* context. For the node tree associated with a stage, this node represents
|
|
* styles applied to the stage itself.
|
|
*
|
|
* Return value: (transfer none): the root node of the context's style tree
|
|
*/
|
|
StThemeNode *
|
|
st_theme_context_get_root_node (StThemeContext *context)
|
|
{
|
|
if (context->root_node == NULL)
|
|
context->root_node = st_theme_node_new (context, NULL, context->theme,
|
|
G_TYPE_NONE, NULL, NULL, NULL, NULL);
|
|
|
|
return context->root_node;
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_intern_node:
|
|
* @context: a #StThemeContext
|
|
* @node: a #StThemeNode
|
|
*
|
|
* Return an existing node matching @node, or if that isn't possible,
|
|
* @node itself.
|
|
*
|
|
* Return value: (transfer none): a node with the same properties as @node
|
|
*/
|
|
StThemeNode *
|
|
st_theme_context_intern_node (StThemeContext *context,
|
|
StThemeNode *node)
|
|
{
|
|
StThemeNode *mine = g_hash_table_lookup (context->nodes, node);
|
|
|
|
/* this might be node or not - it doesn't actually matter */
|
|
if (mine != NULL)
|
|
return mine;
|
|
|
|
g_hash_table_add (context->nodes, g_object_ref (node));
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* st_theme_context_get_scale_factor:
|
|
* @context: a #StThemeContext
|
|
*
|
|
* Return the current scale factor of @context.
|
|
*
|
|
* Return value: a scale factor
|
|
*/
|
|
int
|
|
st_theme_context_get_scale_factor (StThemeContext *context)
|
|
{
|
|
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), -1);
|
|
|
|
return context->scale_factor;
|
|
}
|