Codebase list cdebconf / ba983d6
gtk: Don't add text to GtkTextView until GTK has done initial layout Pango >= 1.44 sometimes behaves unexpectedly when asked to fit text into a space that is too small to be reasonable: when we give it the amount of space that it previously said was adequate, it will sometimes ask for more space. This leads to GTK 2 going into an infinite loop of redoing the layout (#987587) as the combination of GTK and Pango flaps between two size requests. It isn't clear whether this is a bug in GTK 2 or Pango or Debconf, but we can dodge it by making sure GTK gets a chance to decide on the layout before we add text, so that initial text wrapping is done at a more reasonable width. Signed-off-by: Simon McVittie <smcv@debian.org> Closes: #988787 Simon McVittie 2 years ago
1 changed file(s) with 80 addition(s) and 14 deletion(s). Raw diff Collapse all Expand all
122122 /** vertical padding around descriptions */
123123 #define DESCRIPTION_VPADDING 3
124124
125 typedef struct
126 {
127 GtkTextView *view;
128 char *text;
129 gboolean italic;
130 } SetTextLater;
131
132 static void
133 set_text_later_free(gpointer p)
134 {
135 SetTextLater *data = p;
136
137 g_return_if_fail (data != NULL);
138 g_clear_object(&data->view);
139 g_clear_pointer(&data->text, g_free);
140 g_free(data);
141 }
142
143 static gboolean
144 set_text_in_idle(gpointer user_data)
145 {
146 SetTextLater *data = user_data;
147 GtkTextBuffer *buffer;
148
149 g_return_val_if_fail(data != NULL, G_SOURCE_REMOVE);
150 g_return_val_if_fail(GTK_IS_TEXT_VIEW(data->view), G_SOURCE_REMOVE);
151
152 buffer = gtk_text_view_get_buffer(data->view);
153
154 gtk_text_buffer_set_text(buffer, data->text, -1 /* until '\0' */);
155
156 if (data->italic) {
157 GtkTextIter start;
158 GtkTextIter end;
159
160 gtk_text_buffer_create_tag(buffer, "italic", "style",
161 PANGO_STYLE_ITALIC, NULL);
162 gtk_text_buffer_get_start_iter(buffer, &start);
163 gtk_text_buffer_get_end_iter(buffer, &end);
164 gtk_text_buffer_apply_tag_by_name(buffer, "italic", &start, &end);
165 }
166
167 return G_SOURCE_REMOVE;
168 }
169
170 /* Takes ownership of text */
171 static void
172 set_text_later (GtkTextView *view,
173 char *text,
174 gboolean italic)
175 {
176 SetTextLater *data = g_new0 (SetTextLater, 1);
177
178 /* This is a workaround for a relayout loop in debian-installer,
179 * where nowhere near enough space is initially allocated for the text,
180 * leading to Pango giving strange answers when asked how much space it
181 * needs, and GTK 2 constantly redoing the layout when Pango flaps
182 * between two answers. If we do the layout first and add the text
183 * afterwards, then GTK already knows it has plenty of space for the
184 * text and doesn't need to ask Pango for a worst-case scenario.
185 *
186 * This workaround will only work if we let GTK do its layout first,
187 * so the priority used for g_main_context_invoke_full below must be
188 * something less urgent (numerically larger) than GTK_PRIORITY_RESIZE. */
189 G_STATIC_ASSERT (G_PRIORITY_DEFAULT_IDLE > GTK_PRIORITY_RESIZE);
190
191 g_return_if_fail(view != NULL);
192 g_return_if_fail(text != NULL);
193
194 data->view = g_object_ref (view);
195 data->text = text;
196 data->italic = italic;
197 g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT_IDLE,
198 set_text_in_idle, data, set_text_later_free);
199 }
200
125201 /** Add a description to a given container.
126202 *
127203 * @param fe cdebconf frontend
132208 GtkWidget * container)
133209 {
134210 GtkWidget * view;
135 GtkTextBuffer * buffer;
136 GtkTextIter start;
137 GtkTextIter end;
138211 char * description;
139212
140213 description = q_get_description(fe, question);
141214 view = gtk_text_view_new();
142 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
143 gtk_text_buffer_set_text(buffer, description, -1 /* until '\0' */);
144 g_free(description);
215 set_text_later(GTK_TEXT_VIEW(view), g_steal_pointer (&description),
216 TRUE /* italic */);
145217 gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
146218 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
147219 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
148220 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), DESCRIPTION_MARGIN);
149221 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), DESCRIPTION_MARGIN);
150 gtk_text_buffer_create_tag(buffer, "italic", "style",
151 PANGO_STYLE_ITALIC, NULL);
152 gtk_text_buffer_get_start_iter(buffer, &start);
153 gtk_text_buffer_get_end_iter(buffer, &end);
154 gtk_text_buffer_apply_tag_by_name(buffer, "italic", &start, &end);
155222 gtk_widget_modify_base(view, GTK_STATE_NORMAL, get_background_color(fe));
156223 gtk_box_pack_start(GTK_BOX(container), view,
157224 FALSE /* don't expand */, FALSE /* don't fill */,
173240 struct question * question,
174241 GtkWidget * container)
175242 {
176 GtkTextBuffer * buffer;
177243 GtkWidget * view;
178244 char * ext_description;
179245
183249 ext_description = q_get_extended_description(fe, question);
184250 if ('\0' != ext_description[0]) {
185251 view = gtk_text_view_new();
186 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
187 gtk_text_buffer_set_text(buffer, ext_description, -1);
252 set_text_later(GTK_TEXT_VIEW(view), g_steal_pointer (&ext_description),
253 FALSE /* italic */);
188254 gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
189255 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
190256 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);