summaryrefslogtreecommitdiffstats
path: root/debian/patches/keyboard-Correctly-update-labels-for-IBus-engines.patch
blob: 3019576da65d601c9ef39f6abeaed9d0b7bdaa94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
From: Simon McVittie <smcv@debian.org>
Date: Mon, 6 Mar 2023 22:23:28 +0000
Subject: keyboard: Correctly update labels for IBus engines

After the port from GTK 3 to 4, in general there's an additional level
of indirection: the children of priv->input_list are GtkListBoxRow
objects containing the widget whose user-data is the InputWidget. This
means we didn't find the InputWidget and therefore couldn't update its
display name.

Unfortunately, there is one exception to the rule that every child
of a GtkListBox is a GtkListBoxRow: the placeholder object, in our
case priv->no_results, is not wrapped in a GtkListBoxRow (see also
GNOME/gtk#4523). This means that walking the GtkWidget tree seems rather
fragile: it's difficult to tell whether each child of the GtkListBox
is a GtkListBoxRow, or the placeholder object priv->no_results, or
some third type of object that could be added by a future GTK version
(particularly since there is no particular type-safety here).

Instead of walking the widget tree, maintain our own parallel list of
known keyboard layouts and other input methods. For this list, we can
safely assert that every item is something that we put there, with the
invariant that it's a GtkBox with an InputWidget attached.

This means that we can reliably find the InputWidget, and update its
associated display name.

Fixes: ad500afc "keyboard: Port to GTK4"
Bug: https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/180
Signed-off-by: Simon McVittie <smcv@debian.org>
Bug-Debian: https://bugs.debian.org/1032382
Forwarded: https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/180
---
 .../pages/keyboard/cc-input-chooser.c              | 56 ++++++++++++----------
 1 file changed, 31 insertions(+), 25 deletions(-)

diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.c b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
index 2dd58e9..ae8f2d7 100644
--- a/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
+++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c
@@ -50,6 +50,7 @@ struct _CcInputChooserPrivate
 {
         GtkWidget *filter_entry;
         GtkWidget *input_list;
+        GPtrArray *input_widget_boxes;
 	GHashTable *inputs;
 
         GtkWidget *no_results;
@@ -96,6 +97,11 @@ typedef struct {
         gboolean is_extra;
 } InputWidget;
 
+/*
+ * Invariant: for each box in priv->input_widget_boxes,
+ * get_input_widget (row) is non-null and
+ * get_input_widget (row)->box == box
+ */
 static InputWidget *
 get_input_widget (GtkWidget *widget)
 {
@@ -258,21 +264,20 @@ static void
 sync_all_checkmarks (CcInputChooser *chooser)
 {
         CcInputChooserPrivate *priv;
-        GtkWidget *row;
         gboolean invalidate = FALSE;
+        gsize i;
 
         priv = cc_input_chooser_get_instance_private (chooser);
-        row = gtk_widget_get_first_child (priv->input_list);
-        while (row) {
+
+        for (i = 0; i < priv->input_widget_boxes->len; i++) {
                 InputWidget *widget;
                 GtkWidget *child;
                 gboolean should_be_visible;
 
-                child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row));
+                child = g_ptr_array_index (priv->input_widget_boxes, i);
                 widget = get_input_widget (child);
-
-                if (widget == NULL)
-                        break;
+                g_assert (widget != NULL);
+                g_assert (widget->box == child);
 
 	        if (priv->id == NULL || priv->type == NULL)
 		        should_be_visible = FALSE;
@@ -287,8 +292,6 @@ sync_all_checkmarks (CcInputChooser *chooser)
                         widget->is_extra = FALSE;
                         invalidate = TRUE;
                 }
-
-                row = gtk_widget_get_next_sibling (row);
         }
 
         if (invalidate) {
@@ -335,28 +338,26 @@ static void
 choose_non_extras (CcInputChooser *chooser)
 {
         CcInputChooserPrivate *priv;
-        GtkWidget *row;
         guint count = 0;
+        gsize i;
 
         priv = cc_input_chooser_get_instance_private (chooser);
-        row = gtk_widget_get_first_child (priv->input_list);
-        while (row) {
+
+        for (i = 0; i < priv->input_widget_boxes->len; i++) {
                 InputWidget *widget;
                 GtkWidget *child;
 
                 if (++count > MIN_ROWS)
                         break;
 
-                child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row));
+                child = g_ptr_array_index (priv->input_widget_boxes, i);
                 widget = get_input_widget (child);
-                if (widget == NULL)
-                        break;
+                g_assert (widget != NULL);
+                g_assert (widget->box == child);
 
                 g_debug ("Picking %s (%s:%s) as non-extra",
                          widget->name, widget->type, widget->id);
                 widget->is_extra = FALSE;
-
-                row = gtk_widget_get_next_sibling (row);
         }
 
         /* Changing is_extra above affects the ordering and the visibility
@@ -391,6 +392,7 @@ add_rows_to_list (CcInputChooser  *chooser,
 		g_hash_table_add (priv->inputs, key);
 
 		widget = input_widget_new (chooser, type, id, TRUE);
+		g_ptr_array_add (priv->input_widget_boxes, g_object_ref_sink (widget));
 		gtk_list_box_append (GTK_LIST_BOX (priv->input_list), widget);
 	}
 }
@@ -588,21 +590,21 @@ update_ibus_active_sources (CcInputChooser *chooser)
 {
         CcInputChooserPrivate *priv;
         IBusEngineDesc *engine_desc;
-        GtkWidget *child;
         const gchar *type;
         const gchar *id;
         gchar *name;
+        gsize i;
 
         priv = cc_input_chooser_get_instance_private (chooser);
-        child = gtk_widget_get_first_child (priv->input_list);
-        while (child) {
-                InputWidget *row;
 
-		row = get_input_widget (child);
-                child = gtk_widget_get_next_sibling (child);
+        for (i = 0; i < priv->input_widget_boxes->len; i++) {
+                GtkWidget *child;
+                InputWidget *row;
 
-		if (row == NULL)
-			continue;
+                child = g_ptr_array_index (priv->input_widget_boxes, i);
+                row = get_input_widget (child);
+                g_assert (row != NULL);
+                g_assert (row->box == child);
 
                 type = row->type;
                 id = row->id;
@@ -774,6 +776,7 @@ cc_input_chooser_finalize (GObject *object)
 
 	g_clear_object (&priv->xkb_info);
 	g_hash_table_unref (priv->inputs);
+        g_clear_pointer (&priv->input_widget_boxes, g_ptr_array_unref);
 #ifdef HAVE_IBUS
         g_clear_object (&priv->ibus);
         if (priv->ibus_cancellable)
@@ -844,7 +847,10 @@ cc_input_chooser_class_init (CcInputChooserClass *klass)
 static void
 cc_input_chooser_init (CcInputChooser *chooser)
 {
+        CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser);
+
         gtk_widget_init_template (GTK_WIDGET (chooser));
+        priv->input_widget_boxes = g_ptr_array_new_with_free_func (g_object_unref);
 }
 
 void