mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-04 09:40:19 +00:00
eebe370e0a
and friends. This used to work OK via Container.add() but stopped working in GTK4. While we have some ways left to TRY to add children (via GtkWindow and Box), those don't work and result in broken layout and assertion failures. Add basic API that can allow this to work again. Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/6001
398 lines
12 KiB
C
398 lines
12 KiB
C
/* gtkshortcutsgroup.c
|
|
*
|
|
* Copyright (C) 2015 Christian Hergert <christian@hergert.me>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkshortcutsgroup.h"
|
|
|
|
#include "gtkbox.h"
|
|
#include "gtkbuildable.h"
|
|
#include "gtklabel.h"
|
|
#include "gtkorientable.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtkshortcutsshortcut.h"
|
|
#include "gtksizegroup.h"
|
|
#include "gtkaccessible.h"
|
|
|
|
/**
|
|
* GtkShortcutsGroup:
|
|
*
|
|
* A `GtkShortcutsGroup` represents a group of related keyboard shortcuts
|
|
* or gestures.
|
|
*
|
|
* The group has a title. It may optionally be associated with a view
|
|
* of the application, which can be used to show only relevant shortcuts
|
|
* depending on the application context.
|
|
*
|
|
* This widget is only meant to be used with [class@Gtk.ShortcutsWindow].
|
|
*
|
|
* The recommended way to construct a `GtkShortcutsGroup` is with
|
|
* [class@Gtk.Builder], by using the `<child>` tag to populate a
|
|
* `GtkShortcutsGroup` with one or more [class@Gtk.ShortcutsShortcut]
|
|
* instances.
|
|
*
|
|
* If you need to add a shortcut programmatically, use
|
|
* [method@Gtk.ShortcutsGroup.add_shortcut].
|
|
*/
|
|
|
|
struct _GtkShortcutsGroup
|
|
{
|
|
GtkBox parent_instance;
|
|
|
|
GtkLabel *title;
|
|
char *view;
|
|
guint height;
|
|
|
|
GtkSizeGroup *accel_size_group;
|
|
GtkSizeGroup *title_size_group;
|
|
};
|
|
|
|
struct _GtkShortcutsGroupClass
|
|
{
|
|
GtkBoxClass parent_class;
|
|
};
|
|
|
|
static void gtk_shortcuts_group_buildable_iface_init (GtkBuildableIface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkShortcutsGroup, gtk_shortcuts_group, GTK_TYPE_BOX,
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
|
|
gtk_shortcuts_group_buildable_iface_init))
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_TITLE,
|
|
PROP_VIEW,
|
|
PROP_ACCEL_SIZE_GROUP,
|
|
PROP_TITLE_SIZE_GROUP,
|
|
PROP_HEIGHT,
|
|
LAST_PROP
|
|
};
|
|
|
|
static GParamSpec *properties[LAST_PROP];
|
|
|
|
static void
|
|
gtk_shortcuts_group_apply_accel_size_group (GtkShortcutsGroup *group,
|
|
GtkWidget *child)
|
|
{
|
|
if (GTK_IS_SHORTCUTS_SHORTCUT (child))
|
|
g_object_set (child, "accel-size-group", group->accel_size_group, NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_apply_title_size_group (GtkShortcutsGroup *group,
|
|
GtkWidget *child)
|
|
{
|
|
if (GTK_IS_SHORTCUTS_SHORTCUT (child))
|
|
g_object_set (child, "title-size-group", group->title_size_group, NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_set_accel_size_group (GtkShortcutsGroup *group,
|
|
GtkSizeGroup *size_group)
|
|
{
|
|
GtkWidget *child;
|
|
|
|
g_set_object (&group->accel_size_group, size_group);
|
|
|
|
for (child = gtk_widget_get_first_child (GTK_WIDGET (group));
|
|
child != NULL;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
gtk_shortcuts_group_apply_accel_size_group (group, child);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_set_title_size_group (GtkShortcutsGroup *group,
|
|
GtkSizeGroup *size_group)
|
|
{
|
|
GtkWidget *child;
|
|
|
|
g_set_object (&group->title_size_group, size_group);
|
|
|
|
for (child = gtk_widget_get_first_child (GTK_WIDGET (group));
|
|
child != NULL;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
gtk_shortcuts_group_apply_title_size_group (group, child);
|
|
}
|
|
|
|
static guint
|
|
gtk_shortcuts_group_get_height (GtkShortcutsGroup *group)
|
|
{
|
|
GtkWidget *child;
|
|
guint height;
|
|
|
|
height = 1;
|
|
|
|
for (child = gtk_widget_get_first_child (GTK_WIDGET (group));
|
|
child != NULL;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
if (!gtk_widget_get_visible (child))
|
|
continue;
|
|
else if (GTK_IS_SHORTCUTS_SHORTCUT (child))
|
|
height += 1;
|
|
}
|
|
|
|
return height;
|
|
}
|
|
|
|
static GtkBuildableIface *parent_buildable_iface;
|
|
|
|
static void
|
|
gtk_shortcuts_group_buildable_add_child (GtkBuildable *buildable,
|
|
GtkBuilder *builder,
|
|
GObject *child,
|
|
const char *type)
|
|
{
|
|
if (GTK_IS_SHORTCUTS_SHORTCUT (child))
|
|
{
|
|
gtk_shortcuts_group_add_shortcut (GTK_SHORTCUTS_GROUP (buildable),
|
|
GTK_SHORTCUTS_SHORTCUT (child));
|
|
}
|
|
else
|
|
parent_buildable_iface->add_child (buildable, builder, child, type);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_buildable_iface_init (GtkBuildableIface *iface)
|
|
{
|
|
parent_buildable_iface = g_type_interface_peek_parent (iface);
|
|
|
|
iface->add_child = gtk_shortcuts_group_buildable_add_child;
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_TITLE:
|
|
g_value_set_string (value, gtk_label_get_label (self->title));
|
|
break;
|
|
|
|
case PROP_VIEW:
|
|
g_value_set_string (value, self->view);
|
|
break;
|
|
|
|
case PROP_HEIGHT:
|
|
g_value_set_uint (value, gtk_shortcuts_group_get_height (self));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_direction_changed (GtkWidget *widget,
|
|
GtkTextDirection previous_dir)
|
|
{
|
|
GTK_WIDGET_CLASS (gtk_shortcuts_group_parent_class)->direction_changed (widget, previous_dir);
|
|
g_object_notify (G_OBJECT (widget), "height");
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_TITLE:
|
|
gtk_label_set_label (self->title, g_value_get_string (value));
|
|
break;
|
|
|
|
case PROP_VIEW:
|
|
g_free (self->view);
|
|
self->view = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_ACCEL_SIZE_GROUP:
|
|
gtk_shortcuts_group_set_accel_size_group (self, GTK_SIZE_GROUP (g_value_get_object (value)));
|
|
break;
|
|
|
|
case PROP_TITLE_SIZE_GROUP:
|
|
gtk_shortcuts_group_set_title_size_group (self, GTK_SIZE_GROUP (g_value_get_object (value)));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_finalize (GObject *object)
|
|
{
|
|
GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object);
|
|
|
|
g_free (self->view);
|
|
g_set_object (&self->accel_size_group, NULL);
|
|
g_set_object (&self->title_size_group, NULL);
|
|
|
|
G_OBJECT_CLASS (gtk_shortcuts_group_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_dispose (GObject *object)
|
|
{
|
|
GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object);
|
|
|
|
g_clear_pointer ((GtkWidget **)&self->title, gtk_widget_unparent);
|
|
|
|
G_OBJECT_CLASS (gtk_shortcuts_group_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_class_init (GtkShortcutsGroupClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
object_class->finalize = gtk_shortcuts_group_finalize;
|
|
object_class->get_property = gtk_shortcuts_group_get_property;
|
|
object_class->set_property = gtk_shortcuts_group_set_property;
|
|
object_class->dispose = gtk_shortcuts_group_dispose;
|
|
|
|
widget_class->direction_changed = gtk_shortcuts_group_direction_changed;
|
|
|
|
/**
|
|
* GtkShortcutsGroup:title:
|
|
*
|
|
* The title for this group of shortcuts.
|
|
*/
|
|
properties[PROP_TITLE] =
|
|
g_param_spec_string ("title", NULL, NULL,
|
|
"",
|
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GtkShortcutsGroup:view:
|
|
*
|
|
* An optional view that the shortcuts in this group are relevant for.
|
|
*
|
|
* The group will be hidden if the [property@Gtk.ShortcutsWindow:view-name]
|
|
* property does not match the view of this group.
|
|
*
|
|
* Set this to %NULL to make the group always visible.
|
|
*/
|
|
properties[PROP_VIEW] =
|
|
g_param_spec_string ("view", NULL, NULL,
|
|
NULL,
|
|
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GtkShortcutsGroup:accel-size-group:
|
|
*
|
|
* The size group for the accelerator portion of shortcuts in this group.
|
|
*
|
|
* This is used internally by GTK, and must not be modified by applications.
|
|
*/
|
|
properties[PROP_ACCEL_SIZE_GROUP] =
|
|
g_param_spec_object ("accel-size-group", NULL, NULL,
|
|
GTK_TYPE_SIZE_GROUP,
|
|
(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GtkShortcutsGroup:title-size-group:
|
|
*
|
|
* The size group for the textual portion of shortcuts in this group.
|
|
*
|
|
* This is used internally by GTK, and must not be modified by applications.
|
|
*/
|
|
properties[PROP_TITLE_SIZE_GROUP] =
|
|
g_param_spec_object ("title-size-group", NULL, NULL,
|
|
GTK_TYPE_SIZE_GROUP,
|
|
(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GtkShortcutsGroup:height:
|
|
*
|
|
* A rough measure for the number of lines in this group.
|
|
*
|
|
* This is used internally by GTK, and is not useful for applications.
|
|
*/
|
|
properties[PROP_HEIGHT] =
|
|
g_param_spec_uint ("height", NULL, NULL,
|
|
0, G_MAXUINT, 1,
|
|
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROP, properties);
|
|
|
|
gtk_widget_class_set_css_name (widget_class, I_("shortcuts-group"));
|
|
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP);
|
|
}
|
|
|
|
static void
|
|
gtk_shortcuts_group_init (GtkShortcutsGroup *self)
|
|
{
|
|
PangoAttrList *attrs;
|
|
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
|
|
gtk_box_set_spacing (GTK_BOX (self), 10);
|
|
|
|
attrs = pango_attr_list_new ();
|
|
pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
|
|
self->title = g_object_new (GTK_TYPE_LABEL,
|
|
"accessible-role", GTK_ACCESSIBLE_ROLE_CAPTION,
|
|
"attributes", attrs,
|
|
"visible", TRUE,
|
|
"xalign", 0.0f,
|
|
NULL);
|
|
pango_attr_list_unref (attrs);
|
|
|
|
gtk_box_append (GTK_BOX (self), GTK_WIDGET (self->title));
|
|
|
|
gtk_accessible_update_relation (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->title, NULL,
|
|
-1);
|
|
}
|
|
|
|
/**
|
|
* gtk_shortcuts_group_add_shortcut:
|
|
* @self: a `GtkShortcutsGroup`
|
|
* @shortcut: the `GtkShortcutsShortcut` to add
|
|
*
|
|
* Adds a shortcut to the shortcuts group.
|
|
*
|
|
* This is the programmatic equivalent to using [class@Gtk.Builder] and a
|
|
* `<child>` tag to add the child. Adding children with other API is not
|
|
* appropriate as `GtkShortcutsGroup` manages its children internally.
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
void
|
|
gtk_shortcuts_group_add_shortcut (GtkShortcutsGroup *self,
|
|
GtkShortcutsShortcut *shortcut)
|
|
{
|
|
g_return_if_fail (GTK_IS_SHORTCUTS_GROUP (self));
|
|
g_return_if_fail (GTK_IS_SHORTCUTS_SHORTCUT (shortcut));
|
|
g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (shortcut)) == NULL);
|
|
|
|
GtkWidget *widget = GTK_WIDGET (shortcut);
|
|
gtk_box_append (GTK_BOX (self), widget);
|
|
gtk_shortcuts_group_apply_accel_size_group (self, widget);
|
|
gtk_shortcuts_group_apply_title_size_group (self, widget);
|
|
}
|