Make the GtkComboBox in list mode scroll if the list is too large to fit

Mon Jul 26 00:38:27 2004  Matthias Clasen  <maclas@gmx.de>

	Make the GtkComboBox in list mode scroll if the list is too
	large to fit in the popup.  (#135543)

	* gtk/gtkcombobox.c (gtk_combo_box_set_popup_widget): Add a
	scrolled window to the popup in list mode.
	(gtk_combo_box_list_position): Calculate the height of the
	popup so that it fits on the screen, set the scrollbar policy
	of the scrolled window appropriately.
	(gtk_combo_box_popup): Use the height calculated by
	gtk_combo_box_list_position().
	(gtk_combo_box_remeasure): Don't add unnecessary padding.
	(gtk_combo_box_size_request): But add the focus with here.
	(gtk_combo_box_list_setup): Connect to enter notify on the
	popup window to activate auto scrolling.
	(gtk_combo_box_list_enter_notify): Activate auto scrolling.
	(gtk_combo_box_list_button_pressed): Setup a timeout for
	auto scrolling.
	(gtk_combo_box_list_scroll_timeout): Timeout function for
	auto scrolling.
	(gtk_combo_box_list_auto_scroll): Scroll the list when the
	pointer leaves the window.
This commit is contained in:
Matthias Clasen 2004-07-26 04:50:07 +00:00 committed by Matthias Clasen
parent 7ecccfdcb4
commit 9ccad0c92a
5 changed files with 295 additions and 47 deletions

View File

@ -1,3 +1,27 @@
Mon Jul 26 00:38:27 2004 Matthias Clasen <maclas@gmx.de>
Make the GtkComboBox in list mode scroll if the list is too
large to fit in the popup. (#135543)
* gtk/gtkcombobox.c (gtk_combo_box_set_popup_widget): Add a
scrolled window to the popup in list mode.
(gtk_combo_box_list_position): Calculate the height of the
popup so that it fits on the screen, set the scrollbar policy
of the scrolled window appropriately.
(gtk_combo_box_popup): Use the height calculated by
gtk_combo_box_list_position().
(gtk_combo_box_remeasure): Don't add unnecessary padding.
(gtk_combo_box_size_request): But add the focus with here.
(gtk_combo_box_list_setup): Connect to enter notify on the
popup window to activate auto scrolling.
(gtk_combo_box_list_enter_notify): Activate auto scrolling.
(gtk_combo_box_list_button_pressed): Setup a timeout for
auto scrolling.
(gtk_combo_box_list_scroll_timeout): Timeout function for
auto scrolling.
(gtk_combo_box_list_auto_scroll): Scroll the list when the
pointer leaves the window.
Sun Jul 25 19:51:17 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtknotebook.c (gtk_notebook_scroll): Ignore scroll

View File

@ -1,3 +1,27 @@
Mon Jul 26 00:38:27 2004 Matthias Clasen <maclas@gmx.de>
Make the GtkComboBox in list mode scroll if the list is too
large to fit in the popup. (#135543)
* gtk/gtkcombobox.c (gtk_combo_box_set_popup_widget): Add a
scrolled window to the popup in list mode.
(gtk_combo_box_list_position): Calculate the height of the
popup so that it fits on the screen, set the scrollbar policy
of the scrolled window appropriately.
(gtk_combo_box_popup): Use the height calculated by
gtk_combo_box_list_position().
(gtk_combo_box_remeasure): Don't add unnecessary padding.
(gtk_combo_box_size_request): But add the focus with here.
(gtk_combo_box_list_setup): Connect to enter notify on the
popup window to activate auto scrolling.
(gtk_combo_box_list_enter_notify): Activate auto scrolling.
(gtk_combo_box_list_button_pressed): Setup a timeout for
auto scrolling.
(gtk_combo_box_list_scroll_timeout): Timeout function for
auto scrolling.
(gtk_combo_box_list_auto_scroll): Scroll the list when the
pointer leaves the window.
Sun Jul 25 19:51:17 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtknotebook.c (gtk_notebook_scroll): Ignore scroll

View File

@ -1,3 +1,27 @@
Mon Jul 26 00:38:27 2004 Matthias Clasen <maclas@gmx.de>
Make the GtkComboBox in list mode scroll if the list is too
large to fit in the popup. (#135543)
* gtk/gtkcombobox.c (gtk_combo_box_set_popup_widget): Add a
scrolled window to the popup in list mode.
(gtk_combo_box_list_position): Calculate the height of the
popup so that it fits on the screen, set the scrollbar policy
of the scrolled window appropriately.
(gtk_combo_box_popup): Use the height calculated by
gtk_combo_box_list_position().
(gtk_combo_box_remeasure): Don't add unnecessary padding.
(gtk_combo_box_size_request): But add the focus with here.
(gtk_combo_box_list_setup): Connect to enter notify on the
popup window to activate auto scrolling.
(gtk_combo_box_list_enter_notify): Activate auto scrolling.
(gtk_combo_box_list_button_pressed): Setup a timeout for
auto scrolling.
(gtk_combo_box_list_scroll_timeout): Timeout function for
auto scrolling.
(gtk_combo_box_list_auto_scroll): Scroll the list when the
pointer leaves the window.
Sun Jul 25 19:51:17 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtknotebook.c (gtk_notebook_scroll): Ignore scroll

View File

@ -1,3 +1,27 @@
Mon Jul 26 00:38:27 2004 Matthias Clasen <maclas@gmx.de>
Make the GtkComboBox in list mode scroll if the list is too
large to fit in the popup. (#135543)
* gtk/gtkcombobox.c (gtk_combo_box_set_popup_widget): Add a
scrolled window to the popup in list mode.
(gtk_combo_box_list_position): Calculate the height of the
popup so that it fits on the screen, set the scrollbar policy
of the scrolled window appropriately.
(gtk_combo_box_popup): Use the height calculated by
gtk_combo_box_list_position().
(gtk_combo_box_remeasure): Don't add unnecessary padding.
(gtk_combo_box_size_request): But add the focus with here.
(gtk_combo_box_list_setup): Connect to enter notify on the
popup window to activate auto scrolling.
(gtk_combo_box_list_enter_notify): Activate auto scrolling.
(gtk_combo_box_list_button_pressed): Setup a timeout for
auto scrolling.
(gtk_combo_box_list_scroll_timeout): Timeout function for
auto scrolling.
(gtk_combo_box_list_auto_scroll): Scroll the list when the
pointer leaves the window.
Sun Jul 25 19:51:17 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtknotebook.c (gtk_notebook_scroll): Ignore scroll

View File

@ -31,6 +31,7 @@
#include "gtkliststore.h"
#include "gtkmain.h"
#include "gtkmenu.h"
#include "gtkscrolledwindow.h"
#include "gtkseparatormenuitem.h"
#include "gtktearoffmenuitem.h"
#include "gtktogglebutton.h"
@ -93,12 +94,14 @@ struct _GtkComboBoxPrivate
GtkWidget *popup_widget;
GtkWidget *popup_window;
GtkWidget *popup_frame;
GtkWidget *scrolled_window;
guint inserted_id;
guint deleted_id;
guint reordered_id;
guint changed_id;
guint popup_idle_id;
guint scroll_timer;
gint width;
GSList *cells;
@ -109,6 +112,7 @@ struct _GtkComboBoxPrivate
guint has_frame : 1;
guint is_cell_renderer : 1;
guint editing_canceled : 1;
guint auto_scroll : 1;
};
/* While debugging this evil code, I have learned that
@ -126,6 +130,7 @@ struct _GtkComboBoxPrivate
* popup_widget -> GtkMenu
* popup_window -> NULL
* popup_frame -> NULL
* scrolled_window -> NULL
*
* 2) menu mode, child added
*
@ -138,6 +143,7 @@ struct _GtkComboBoxPrivate
* popup_widget -> GtkMenu
* popup_window -> NULL
* popup_frame -> NULL
* scrolled_window -> NULL
*
* 3) list mode, no child added
*
@ -150,6 +156,7 @@ struct _GtkComboBoxPrivate
* popup_widget -> tree_view
* popup_window -> GtkWindow
* popup_frame -> GtkFrame, child of popup_window
* scrolled_window -> GtkScrolledWindow, child of popup_frame
*
* 4) list mode, child added
*
@ -162,6 +169,7 @@ struct _GtkComboBoxPrivate
* popup_widget -> tree_view
* popup_window -> GtkWindow
* popup_frame -> GtkFrame, child of popup_window
* scrolled_window -> GtkScrolledWindow, child of popup_frame
*
*/
@ -186,7 +194,7 @@ static GtkBinClass *parent_class = NULL;
static guint combo_box_signals[LAST_SIGNAL] = {0,};
#define BONUS_PADDING 4
#define SCROLL_TIME 100
/* common */
static void gtk_combo_box_class_init (GtkComboBoxClass *klass);
@ -303,6 +311,13 @@ static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
GdkEventKey *event,
gpointer data);
static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
GdkEventCrossing *event,
gpointer data);
static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
gint x,
gint y);
static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
GdkEventButton *event,
gpointer data);
@ -1015,10 +1030,24 @@ gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
combo_box->priv->popup_frame);
gtk_widget_show (combo_box->priv->popup_frame);
combo_box->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
GTK_POLICY_NEVER,
GTK_POLICY_NEVER);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
GTK_SHADOW_NONE);
gtk_widget_show (combo_box->priv->scrolled_window);
gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
combo_box->priv->scrolled_window);
}
gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
gtk_container_add (GTK_CONTAINER (combo_box->priv->scrolled_window),
popup);
gtk_widget_show (popup);
g_object_ref (G_OBJECT (popup));
combo_box->priv->popup_widget = popup;
@ -1191,15 +1220,20 @@ gtk_combo_box_list_position (GtkComboBox *combo_box,
gint monitor_num;
GdkRectangle monitor;
GtkRequisition popup_req;
GtkPolicyType hpolicy, vpolicy;
sample = GTK_BIN (combo_box)->child;
*width = sample->allocation.width;
gtk_widget_size_request (combo_box->priv->popup_window, &popup_req);
*height = popup_req.height;
gdk_window_get_origin (sample->window, x, y);
if (GTK_WIDGET_NO_WINDOW (sample))
{
*x += sample->allocation.x;
*y += sample->allocation.y;
}
*width = sample->allocation.width;
if (combo_box->priv->cell_view_frame && combo_box->priv->has_frame)
{
*x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
@ -1208,17 +1242,26 @@ gtk_combo_box_list_position (GtkComboBox *combo_box,
GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
}
if (GTK_WIDGET_NO_WINDOW (sample))
hpolicy = vpolicy = GTK_POLICY_NEVER;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
hpolicy, vpolicy);
gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
if (popup_req.width > *width)
{
*x += sample->allocation.x;
*y += sample->allocation.y;
hpolicy = GTK_POLICY_ALWAYS;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
hpolicy, vpolicy);
gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
}
*height = popup_req.height;
screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
monitor_num = gdk_screen_get_monitor_at_window (screen,
GTK_WIDGET (combo_box)->window);
gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
if (*x < monitor.x)
*x = monitor.x;
else if (*x + *width > monitor.x + monitor.width)
@ -1226,8 +1269,26 @@ gtk_combo_box_list_position (GtkComboBox *combo_box,
if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
*y += sample->allocation.height;
else
else if (*y - *height >= monitor.y)
*y -= *height;
else if (monitor.y + monitor.height - (*y + sample->allocation.height) > *y - monitor.y)
{
*y += sample->allocation.height;
*height = monitor.y + monitor.height - *y;
}
else
{
*height = *y - monitor.y;
*y = monitor.y;
}
if (popup_req.height > *height)
{
vpolicy = GTK_POLICY_ALWAYS;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
hpolicy, vpolicy);
}
}
static gboolean
@ -1428,7 +1489,7 @@ gtk_combo_box_popup (GtkComboBox *combo_box)
gtk_widget_show_all (combo_box->priv->popup_frame);
gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);
gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);
gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
/* popup */
@ -1451,8 +1512,6 @@ gtk_combo_box_popup (GtkComboBox *combo_box)
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK,
NULL, NULL, GDK_CURRENT_TIME);
gtk_grab_add (combo_box->priv->tree_view);
}
/**
@ -1518,7 +1577,6 @@ gtk_combo_box_remeasure (GtkComboBox *combo_box)
{
GtkTreeIter iter;
GtkTreePath *path;
gint padding = 0;
if (!combo_box->priv->model ||
!gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
@ -1528,16 +1586,6 @@ gtk_combo_box_remeasure (GtkComboBox *combo_box)
path = gtk_tree_path_new_from_indices (0, -1);
if (combo_box->priv->cell_view)
gtk_widget_style_get (combo_box->priv->cell_view,
"focus-line-width", &padding,
NULL);
else
padding = 0;
/* add some pixels for good measure */
padding += BONUS_PADDING;
do
{
GtkRequisition req;
@ -1548,8 +1596,7 @@ gtk_combo_box_remeasure (GtkComboBox *combo_box)
else
req.width = 0;
combo_box->priv->width = MAX (combo_box->priv->width,
req.width + padding);
combo_box->priv->width = MAX (combo_box->priv->width, req.width);
gtk_tree_path_next (path);
}
@ -1626,6 +1673,8 @@ gtk_combo_box_size_request (GtkWidget *widget,
/* sample + frame */
*requisition = bin_req;
requisition->width += 2 * focus_width;
if (combo_box->priv->cell_view_frame)
{
gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
@ -2066,7 +2115,7 @@ gtk_combo_box_menu_fill (GtkComboBox *combo_box)
gtk_widget_show (GTK_WIDGET (cell_view));
tmp = gtk_menu_item_new ();
gtk_container_add (GTK_CONTAINER (tmp), cell_view);
gtk_container_add (GTK_CONTAINER (tmp), GTK_WIDGET (cell_view));
g_signal_connect (tmp, "activate",
G_CALLBACK (gtk_combo_box_menu_item_activate),
@ -2597,16 +2646,6 @@ gtk_combo_box_list_setup (GtkComboBox *combo_box)
gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
combo_box->priv->model);
g_signal_connect (combo_box->priv->tree_view, "button_press_event",
G_CALLBACK (gtk_combo_box_list_button_pressed),
combo_box);
g_signal_connect (combo_box->priv->tree_view, "button_release_event",
G_CALLBACK (gtk_combo_box_list_button_released),
combo_box);
g_signal_connect (combo_box->priv->tree_view, "key_press_event",
G_CALLBACK (gtk_combo_box_list_key_press),
combo_box);
combo_box->priv->column = gtk_tree_view_column_new ();
gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
combo_box->priv->column);
@ -2653,6 +2692,19 @@ gtk_combo_box_list_setup (GtkComboBox *combo_box)
/* set sample/popup widgets */
gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
g_signal_connect (combo_box->priv->tree_view, "key_press_event",
G_CALLBACK (gtk_combo_box_list_key_press),
combo_box);
g_signal_connect (combo_box->priv->tree_view, "enter_notify_event",
G_CALLBACK (gtk_combo_box_list_enter_notify),
combo_box);
g_signal_connect (combo_box->priv->popup_window, "button_press_event",
G_CALLBACK (gtk_combo_box_list_button_pressed),
combo_box);
g_signal_connect (combo_box->priv->popup_window, "button_release_event",
G_CALLBACK (gtk_combo_box_list_button_released),
combo_box);
gtk_widget_show (combo_box->priv->tree_view);
}
@ -2668,6 +2720,16 @@ gtk_combo_box_list_destroy (GtkComboBox *combo_box)
0, 0, NULL,
gtk_combo_box_list_button_pressed,
NULL);
g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
G_SIGNAL_MATCH_DATA,
0, 0, NULL,
gtk_combo_box_list_button_pressed,
NULL);
g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
G_SIGNAL_MATCH_DATA,
0, 0, NULL,
gtk_combo_box_list_button_released,
NULL);
if (combo_box->priv->box)
g_signal_handlers_disconnect_matched (combo_box->priv->box,
G_SIGNAL_MATCH_DATA,
@ -2696,6 +2758,12 @@ gtk_combo_box_list_destroy (GtkComboBox *combo_box)
combo_box->priv->box = NULL;
}
if (combo_box->priv->scroll_timer)
{
g_source_remove (combo_box->priv->scroll_timer);
combo_box->priv->scroll_timer = 0;
}
gtk_widget_destroy (combo_box->priv->tree_view);
combo_box->priv->tree_view = NULL;
@ -2706,12 +2774,6 @@ gtk_combo_box_list_destroy (GtkComboBox *combo_box)
static void
gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
{
if (combo_box->priv->tree_view &&
GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
{
gtk_grab_remove (combo_box->priv->tree_view);
}
if (combo_box->priv->popup_window &&
GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
{
@ -2730,7 +2792,7 @@ gtk_combo_box_list_button_pressed (GtkWidget *widget,
GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
if (ewidget == combo_box->priv->tree_view)
if (ewidget == combo_box->priv->popup_window)
return TRUE;
if ((ewidget != combo_box->priv->button && ewidget != combo_box->priv->box) ||
@ -2742,6 +2804,12 @@ gtk_combo_box_list_button_pressed (GtkWidget *widget,
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
TRUE);
combo_box->priv->auto_scroll = FALSE;
if (combo_box->priv->scroll_timer == 0)
combo_box->priv->scroll_timer = g_timeout_add (SCROLL_TIME,
(GSourceFunc) gtk_combo_box_list_scroll_timeout,
combo_box);
combo_box->priv->popup_in_progress = TRUE;
return TRUE;
@ -2767,6 +2835,12 @@ gtk_combo_box_list_button_released (GtkWidget *widget,
combo_box->priv->popup_in_progress = FALSE;
}
if (combo_box->priv->scroll_timer)
{
g_source_remove (combo_box->priv->scroll_timer);
combo_box->priv->scroll_timer = 0;
}
if (ewidget != combo_box->priv->tree_view)
{
if (ewidget == combo_box->priv->button &&
@ -2789,7 +2863,7 @@ gtk_combo_box_list_button_released (GtkWidget *widget,
}
/* select something cool */
ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (combo_box->priv->tree_view),
event->x, event->y,
&path,
NULL, NULL, NULL);
@ -2944,6 +3018,84 @@ gtk_combo_box_list_key_press (GtkWidget *widget,
return FALSE;
}
static void
gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
gint x,
gint y)
{
GtkWidget *tree_view = combo_box->priv->tree_view;
GtkAdjustment *adj;
gdouble value;
adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
if (adj && adj->upper - adj->lower > adj->page_size)
{
if (x <= tree_view->allocation.x &&
adj->lower < adj->value)
{
value = adj->value - (tree_view->allocation.x - x + 1);
gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
}
else if (x >= tree_view->allocation.x + tree_view->allocation.width &&
adj->upper - adj->page_size > adj->value)
{
value = adj->value + (x - tree_view->allocation.x - tree_view->allocation.width + 1);
gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
}
}
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
if (adj && adj->upper - adj->lower > adj->page_size)
{
if (y <= tree_view->allocation.y &&
adj->lower < adj->value)
{
value = adj->value - (tree_view->allocation.y - y + 1);
gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
}
else if (y >= tree_view->allocation.y + tree_view->allocation.height &&
adj->upper - adj->page_size > adj->value)
{
value = adj->value + (y - tree_view->allocation.y - tree_view->allocation.height + 1);
gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
}
}
}
static gboolean
gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
{
gboolean result;
gint x, y;
GtkWidget *tv;
GDK_THREADS_ENTER ();
if (combo_box->priv->auto_scroll)
{
gdk_window_get_pointer (combo_box->priv->tree_view->window,
&x, &y, NULL);
gtk_combo_box_list_auto_scroll (combo_box, x, y);
}
GDK_THREADS_LEAVE ();
return TRUE;
}
static gboolean
gtk_combo_box_list_enter_notify (GtkWidget *widget,
GdkEventCrossing *event,
gpointer data)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (data);
combo_box->priv->auto_scroll = TRUE;
return TRUE;
}
static void
gtk_combo_box_list_row_changed (GtkTreeModel *model,
GtkTreePath *path,