Work around an annoying GTK behaviour I noticed the other day on my

Ubuntu 12.04 machine. What seems to happen is that we set up a window
containing a menu bar, a drawing area and a status bar, and set the
size of the drawing area; then the window is displayed _without_ the
menu bar; then we reduce the drawing area's size request to (1,1) to
let the user resize the window smaller; and now GTK gets round to
constructing the menu bar, and the drawing area helpfully shrinks a
bit to make room for it.

My fix is to set a 'shrink pending' flag instead of shrinking the
drawing area's size request, and defer the actual shrink operation
until the menu bar and status bar are both present.

[originally from svn r9711]
This commit is contained in:
Simon Tatham
2012-11-20 20:05:27 +00:00
parent 083aeafc5c
commit 2a2520b8e7

93
gtk.c
View File

@ -117,6 +117,7 @@ struct frontend {
GtkAccelGroup *accelgroup; GtkAccelGroup *accelgroup;
GtkWidget *area; GtkWidget *area;
GtkWidget *statusbar; GtkWidget *statusbar;
GtkWidget *menubar;
guint statusctx; guint statusctx;
int w, h; int w, h;
midend *me; midend *me;
@ -149,6 +150,7 @@ struct frontend {
#ifdef OLD_FILESEL #ifdef OLD_FILESEL
char *filesel_name; char *filesel_name;
#endif #endif
int drawing_area_shrink_pending;
GSList *preset_radio; GSList *preset_radio;
int n_preset_menu_items; int n_preset_menu_items;
int preset_threaded; int preset_threaded;
@ -1661,6 +1663,63 @@ static void changed_preset(frontend *fe)
} }
} }
static gboolean not_size_allocated_yet(GtkWidget *w)
{
/*
* This function tests whether a widget has not yet taken up space
* on the screen which it will occupy in future. (Therefore, it
* returns true only if the widget does exist but does not have a
* size allocation. A null widget is already taking up all the
* space it ever will.)
*/
GtkAllocation a;
if (!w)
return FALSE; /* nonexistent widgets aren't a problem */
gtk_widget_get_allocation(w, &a);
return a.height == 0 || a.width == 0;
}
static void try_shrink_drawing_area(frontend *fe)
{
if (fe->drawing_area_shrink_pending &&
!not_size_allocated_yet(fe->menubar) &&
!not_size_allocated_yet(fe->statusbar)) {
/*
* In order to permit the user to resize the window smaller as
* well as bigger, we call this function after the window size
* has ended up where we want it. This shouldn't shrink the
* window immediately; it just arranges that the next time the
* user tries to shrink it, they can.
*
* However, at puzzle creation time, we defer the first of
* these operations until after the menu bar and status bar
* are actually visible. On Ubuntu 12.04 I've found that these
* can take a while to be displayed, and that it's a mistake
* to reduce the drawing area's size allocation before they've
* turned up or else the drawing area makes room for them by
* shrinking to less than the size we intended.
*/
gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
fe->drawing_area_shrink_pending = FALSE;
}
}
static gint configure_window(GtkWidget *widget,
GdkEventConfigure *event, gpointer data)
{
frontend *fe = (frontend *)data;
/*
* When the main puzzle window changes size, it might be because
* the menu bar or status bar has turned up after starting off
* absent, in which case we should have another go at enacting a
* pending shrink of the drawing area.
*/
try_shrink_drawing_area(fe);
return FALSE;
}
static void resize_fe(frontend *fe) static void resize_fe(frontend *fe)
{ {
int x, y; int x, y;
@ -1668,18 +1727,15 @@ static void resize_fe(frontend *fe)
get_size(fe, &x, &y); get_size(fe, &x, &y);
fe->w = x; fe->w = x;
fe->h = y; fe->h = y;
fe->drawing_area_shrink_pending = FALSE;
gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
{ {
GtkRequisition req; GtkRequisition req;
gtk_widget_size_request(GTK_WIDGET(fe->window), &req); gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height); gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
} }
/* fe->drawing_area_shrink_pending = TRUE;
* Now that we've established the preferred size of the window, try_shrink_drawing_area(fe);
* reduce the drawing area's size request so the user can shrink
* the window.
*/
gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
} }
static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
@ -2036,7 +2092,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
{ {
frontend *fe; frontend *fe;
GtkBox *vbox, *hbox; GtkBox *vbox, *hbox;
GtkWidget *menubar, *menu, *menuitem; GtkWidget *menu, *menuitem;
GdkPixmap *iconpm; GdkPixmap *iconpm;
GList *iconlist; GList *iconlist;
int x, y, n; int x, y, n;
@ -2125,12 +2181,12 @@ static frontend *new_window(char *arg, int argtype, char **error)
gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0); gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0);
gtk_widget_show(GTK_WIDGET(hbox)); gtk_widget_show(GTK_WIDGET(hbox));
menubar = gtk_menu_bar_new(); fe->menubar = gtk_menu_bar_new();
gtk_box_pack_start(hbox, menubar, TRUE, TRUE, 0); gtk_box_pack_start(hbox, fe->menubar, TRUE, TRUE, 0);
gtk_widget_show(menubar); gtk_widget_show(fe->menubar);
menuitem = gtk_menu_item_new_with_mnemonic("_Game"); menuitem = gtk_menu_item_new_with_mnemonic("_Game");
gtk_container_add(GTK_CONTAINER(menubar), menuitem); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
gtk_widget_show(menuitem); gtk_widget_show(menuitem);
menu = gtk_menu_new(); menu = gtk_menu_new();
@ -2169,7 +2225,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
int i; int i;
menuitem = gtk_menu_item_new_with_mnemonic("_Type"); menuitem = gtk_menu_item_new_with_mnemonic("_Type");
gtk_container_add(GTK_CONTAINER(menubar), menuitem); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
gtk_widget_show(menuitem); gtk_widget_show(menuitem);
submenu = gtk_menu_new(); submenu = gtk_menu_new();
@ -2248,7 +2304,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q'); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
menuitem = gtk_menu_item_new_with_mnemonic("_Help"); menuitem = gtk_menu_item_new_with_mnemonic("_Help");
gtk_container_add(GTK_CONTAINER(menubar), menuitem); gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
gtk_widget_show(menuitem); gtk_widget_show(menuitem);
menu = gtk_menu_new(); menu = gtk_menu_new();
@ -2328,6 +2384,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
GTK_WIDGET_UNSET_FLAGS(fe->area, GTK_DOUBLE_BUFFERED); GTK_WIDGET_UNSET_FLAGS(fe->area, GTK_DOUBLE_BUFFERED);
#endif #endif
get_size(fe, &x, &y); get_size(fe, &x, &y);
fe->drawing_area_shrink_pending = FALSE;
gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
fe->w = x; fe->w = x;
fe->h = y; fe->h = y;
@ -2361,6 +2418,8 @@ static frontend *new_window(char *arg, int argtype, char **error)
GTK_SIGNAL_FUNC(map_window), fe); GTK_SIGNAL_FUNC(map_window), fe);
gtk_signal_connect(GTK_OBJECT(fe->area), "configure_event", gtk_signal_connect(GTK_OBJECT(fe->area), "configure_event",
GTK_SIGNAL_FUNC(configure_area), fe); GTK_SIGNAL_FUNC(configure_area), fe);
gtk_signal_connect(GTK_OBJECT(fe->window), "configure_event",
GTK_SIGNAL_FUNC(configure_window), fe);
gtk_widget_add_events(GTK_WIDGET(fe->area), gtk_widget_add_events(GTK_WIDGET(fe->area),
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_PRESS_MASK |
@ -2386,12 +2445,8 @@ static frontend *new_window(char *arg, int argtype, char **error)
gtk_widget_show(fe->area); gtk_widget_show(fe->area);
gtk_widget_show(fe->window); gtk_widget_show(fe->window);
/* fe->drawing_area_shrink_pending = TRUE;
* Now that we've established the preferred size of the window, try_shrink_drawing_area(fe);
* reduce the drawing area's size request so the user can shrink
* the window.
*/
gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
set_window_background(fe, 0); set_window_background(fe, 0);
return fe; return fe;