Compare commits

...

6 Commits

Author SHA1 Message Date
Matthias Clasen
dfc4c4271f file chooser dialog: Prevent uneven button heights
The height of the text buttons depends on the font height,
whereas the search button has a fixed-size icon in it...
Prevent unevent heights by putting them all in a size group.

https://bugzilla.gnome.org/show_bug.cgi?id=745263
2015-02-26 21:15:19 -05:00
Matthias Clasen
61c77fcd80 app chooser dialog: Prevent uneven button heights
The height of the text buttons depends on the font height,
whereas the search button has a fixed-size icon in it...
Prevent unevent heights by putting them all in a size group.
2015-02-26 20:55:54 -05:00
Carlos Garnacho
5b51273cec gtkwindow: Move window dragging to a standalone drag gesture
The gesture is hooked to the capture phase, so it works for buttons in
header bars and whatnot. In order to be friendly to the widget it is
capturing events from, an ugly hack is in place to avoid capturing
events when the target widget has a gesture that would consume motion
events.
2015-02-26 15:45:22 +01:00
Carlos Garnacho
88d0526ac5 gtkmenubutton: Popup menu/popover on GtkButton:clicked
This happens on button release, which is more convenient if the gesture
can be consumed by something else (eg. window dragging), and already behaves
correctly wrt cancelled gestures, broken grabs, etc.

This also allows us to unify pointer and keyboard behavior, popping up the
menu widget in a single place.
2015-02-26 15:45:22 +01:00
Carlos Garnacho
4c1312028a gesture: On cancel, remove touchpoint before checking the recognized state
Otherwise the touch being removed doesn't account in recognition.
2015-02-26 14:31:57 +01:00
Carlos Garnacho
3593e161bf button: Ensure the button is "left" when a gesture is cancelled
This prevents from stale widget states if the gesture happens to be
cancelled mid-press.
2015-02-26 14:31:57 +01:00
11 changed files with 190 additions and 93 deletions

View File

@@ -57,6 +57,7 @@
#include "gtkheaderbar.h"
#include "gtkdialogprivate.h"
#include "gtksearchbar.h"
#include "gtksizegroup.h"
#include <string.h>
#include <glib/gi18n-lib.h>
@@ -80,6 +81,8 @@ struct _GtkAppChooserDialogPrivate {
GtkWidget *show_more_button;
GtkWidget *software_button;
GtkSizeGroup *buttons;
gboolean show_more_clicked;
gboolean dismissed;
};
@@ -494,6 +497,7 @@ setup_search (GtkAppChooserDialog *self)
header = gtk_dialog_get_header_bar (GTK_DIALOG (self));
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
gtk_size_group_add_widget (self->priv->buttons, button);
g_object_bind_property (button, "active",
self->priv->search_bar, "search-mode-enabled",
@@ -696,6 +700,7 @@ gtk_app_chooser_dialog_class_init (GtkAppChooserDialogClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserDialog, inner_box);
gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserDialog, search_bar);
gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserDialog, search_entry);
gtk_widget_class_bind_template_child_private (widget_class, GtkAppChooserDialog, buttons);
gtk_widget_class_bind_template_callback (widget_class, show_more_button_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, software_button_clicked_cb);
}

View File

@@ -147,6 +147,8 @@ static void gtk_button_state_changed (GtkWidget *widget,
GtkStateType previous_state);
static void gtk_button_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static void gtk_button_do_release (GtkButton *button,
gboolean emit_clicked);
static void gtk_button_actionable_iface_init (GtkActionableInterface *iface);
static void gtk_button_activatable_interface_init(GtkActivatableIface *iface);
@@ -644,6 +646,14 @@ multipress_gesture_update_cb (GtkGesture *gesture,
}
}
static void
multipress_gesture_cancel_cb (GtkGesture *gesture,
GdkEventSequence *sequence,
GtkButton *button)
{
gtk_button_do_release (button, FALSE);
}
static void
gtk_button_init (GtkButton *button)
{
@@ -683,6 +693,7 @@ gtk_button_init (GtkButton *button)
g_signal_connect (priv->gesture, "pressed", G_CALLBACK (multipress_pressed_cb), button);
g_signal_connect (priv->gesture, "released", G_CALLBACK (multipress_released_cb), button);
g_signal_connect (priv->gesture, "update", G_CALLBACK (multipress_gesture_update_cb), button);
g_signal_connect (priv->gesture, "cancel", G_CALLBACK (multipress_gesture_cancel_cb), button);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->gesture), GTK_PHASE_BUBBLE);
}

View File

@@ -201,6 +201,8 @@ struct _GtkFileChooserDialogPrivate
{
GtkWidget *widget;
GtkSizeGroup *buttons;
/* for use with GtkFileChooserEmbed */
gboolean response_requested;
gboolean search_setup;
@@ -256,6 +258,7 @@ gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class)
"/org/gtk/libgtk/ui/gtkfilechooserdialog.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserDialog, widget);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserDialog, buttons);
gtk_widget_class_bind_template_callback (widget_class, response_cb);
gtk_widget_class_bind_template_callback (widget_class, file_chooser_widget_file_activated);
gtk_widget_class_bind_template_callback (widget_class, file_chooser_widget_default_size_changed);
@@ -473,6 +476,15 @@ gtk_file_chooser_dialog_get_property (GObject *object,
g_object_get_property (G_OBJECT (priv->widget), pspec->name, value);
}
static void
add_button (GtkWidget *button, gpointer data)
{
GtkFileChooserDialog *dialog = data;
if (GTK_IS_BUTTON (button))
gtk_size_group_add_widget (dialog->priv->buttons, button);
}
static void
setup_search (GtkFileChooserDialog *dialog)
{
@@ -505,6 +517,8 @@ setup_search (GtkFileChooserDialog *dialog)
g_object_bind_property (button, "active",
dialog->priv->widget, "search-mode",
G_BINDING_BIDIRECTIONAL);
gtk_container_forall (GTK_CONTAINER (header), add_button, dialog);
}
}

View File

@@ -1373,8 +1373,9 @@ _gtk_gesture_cancel_sequence (GtkGesture *gesture,
return FALSE;
g_signal_emit (gesture, signals[CANCEL], 0, sequence);
_gtk_gesture_check_recognized (gesture, sequence);
_gtk_gesture_remove_point (gesture, data->event);
_gtk_gesture_check_recognized (gesture, sequence);
return TRUE;
}

View File

@@ -1567,12 +1567,6 @@ gtk_main_do_event (GdkEvent *event)
event_widget = gtk_get_event_widget (event);
}
if (GTK_IS_WINDOW (event_widget))
{
if (_gtk_window_check_handle_wm_event (event))
return;
}
window_group = gtk_main_get_window_group (event_widget);
device = gdk_event_get_device (event);
@@ -1583,6 +1577,14 @@ gtk_main_do_event (GdkEvent *event)
if (!grab_widget)
grab_widget = gtk_window_group_get_current_grab (window_group);
if (GTK_IS_WINDOW (event_widget) ||
(grab_widget && grab_widget != event_widget &&
!gtk_widget_is_ancestor (event_widget, grab_widget)))
{
if (_gtk_window_check_handle_wm_event (event))
return;
}
/* Find out the topmost widget where captured event propagation
* should start, which is the widget holding the GTK+ grab
* if any, otherwise it's left NULL and events are emitted

View File

@@ -132,6 +132,7 @@ struct _GtkMenuButtonPrivate
GtkWidget *arrow_widget;
GtkArrowType arrow_type;
gboolean use_popover;
guint press_handled : 1;
};
enum
@@ -405,60 +406,39 @@ popup_menu (GtkMenuButton *menu_button,
}
static void
gtk_menu_button_toggled (GtkToggleButton *button)
gtk_menu_button_clicked (GtkButton *button)
{
GtkMenuButton *menu_button = GTK_MENU_BUTTON (button);
GtkMenuButtonPrivate *priv = menu_button->priv;
gboolean active;
gboolean active = TRUE;
active = gtk_toggle_button_get_active (button);
if (priv->menu)
if (priv->menu && !gtk_widget_get_visible (priv->menu))
{
if (active)
{
if (!gtk_widget_get_visible (priv->menu))
{
/* we get here only when the menu is activated by a key
* press, so that we can select the first menu item
*/
popup_menu (menu_button, NULL);
gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
}
}
else
gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->menu));
GdkEvent *event;
event = gtk_get_current_event ();
popup_menu (menu_button,
(event && event->type != GDK_BUTTON_RELEASE) ?
(GdkEventButton *) event : NULL);
if (!event ||
event->type == GDK_KEY_PRESS ||
event->type == GDK_KEY_RELEASE)
gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
if (event)
gdk_event_free (event);
}
else if (priv->popover)
gtk_widget_set_visible (priv->popover, active);
}
else if (priv->popover && !gtk_widget_get_visible (priv->popover))
gtk_widget_show (priv->popover);
else
active = FALSE;
static gboolean
gtk_menu_button_button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
GtkMenuButton *menu_button = GTK_MENU_BUTTON (widget);
GtkMenuButtonPrivate *priv = menu_button->priv;
GTK_BUTTON_CLASS (gtk_menu_button_parent_class)->clicked (button);
if (event->button == GDK_BUTTON_PRIMARY)
{
/* Filter out double/triple clicks */
if (event->type != GDK_BUTTON_PRESS)
return TRUE;
if (priv->menu && !gtk_widget_get_visible (priv->menu))
popup_menu (menu_button, event);
else if (priv->popover && !gtk_widget_get_visible (priv->popover))
gtk_widget_show (priv->popover);
else
return TRUE;
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
return TRUE;
}
return GTK_WIDGET_CLASS (gtk_menu_button_parent_class)->button_press_event (widget, event);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (button));
}
static void
@@ -491,19 +471,18 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass);
GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
gobject_class->set_property = gtk_menu_button_set_property;
gobject_class->get_property = gtk_menu_button_get_property;
gobject_class->dispose = gtk_menu_button_dispose;
widget_class->state_flags_changed = gtk_menu_button_state_flags_changed;
widget_class->button_press_event = gtk_menu_button_button_press_event;
container_class->add = gtk_menu_button_add;
container_class->remove = gtk_menu_button_remove;
toggle_button_class->toggled = gtk_menu_button_toggled;
button_class->clicked = gtk_menu_button_clicked;
/**
* GtkMenuButton:popup:

View File

@@ -17278,3 +17278,32 @@ _gtk_widget_list_controllers (GtkWidget *widget,
return retval;
}
gboolean
_gtk_widget_consumes_motion (GtkWidget *widget)
{
EventControllerData *data;
GtkWidgetPrivate *priv;
gboolean consumes_motion = FALSE;
GList *l;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
if (data->controller == NULL)
continue;
if (!GTK_IS_GESTURE_SINGLE (data->controller))
consumes_motion = TRUE;
else if (GTK_IS_GESTURE_DRAG (data->controller) ||
GTK_IS_GESTURE_SWIPE (data->controller))
consumes_motion = TRUE;
}
return consumes_motion;
}

View File

@@ -160,6 +160,8 @@ void _gtk_widget_remove_controller (GtkWidget
GtkEventController *controller);
GList * _gtk_widget_list_controllers (GtkWidget *widget,
GtkPropagationPhase phase);
gboolean _gtk_widget_consumes_motion (GtkWidget *widget);
gboolean gtk_widget_has_tick_callback (GtkWidget *widget);
void gtk_widget_set_csd_input_shape (GtkWidget *widget,

View File

@@ -233,11 +233,10 @@ struct _GtkWindowPrivate
guint fullscreen : 1;
guint tiled : 1;
guint drag_possible : 1;
guint use_subsurface : 1;
GtkGesture *multipress_gesture;
GtkGesture *drag_gesture;
GdkWindow *hardcoded_window;
};
@@ -1435,8 +1434,10 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
if (!event)
return;
if (n_press > 1)
gtk_gesture_set_state (priv->drag_gesture, GTK_EVENT_SEQUENCE_DENIED);
region = get_active_region_type (window, (GdkEventAny*) event, x, y);
priv->drag_possible = FALSE;
if (button == GDK_BUTTON_SECONDARY && region == GTK_WINDOW_REGION_TITLE)
{
@@ -1476,8 +1477,6 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
case GTK_WINDOW_REGION_TITLE:
if (n_press == 2)
gtk_window_titlebar_action (window, event, button, n_press);
if (n_press == 1)
priv->drag_possible = TRUE;
if (gtk_widget_has_grab (widget))
gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
@@ -1502,41 +1501,71 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
}
static void
multipress_gesture_stopped_cb (GtkGestureMultiPress *gesture,
GtkWindow *window)
drag_gesture_begin_cb (GtkGestureDrag *gesture,
gdouble x,
gdouble y,
GtkWindow *window)
{
GdkEventSequence *sequence;
GtkWindowPrivate *priv;
GtkWindowRegion region;
const GdkEvent *event;
gdouble x, y;
GtkWidget *event_widget;
if (!gtk_gesture_is_active (GTK_GESTURE (gesture)))
return;
/* The gesture is active, but stopped, so a too long
* press happened, or one drifting out of the threshold
*/
priv = gtk_window_get_instance_private (window);
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, &x, &y);
if (priv->drag_possible)
if (!event)
return;
event_widget = gtk_get_event_widget ((GdkEvent *) event);
if (event_widget != GTK_WIDGET (window) &&
!gtk_widget_has_grab (event_widget) &&
_gtk_widget_consumes_motion (event_widget))
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
else
{
gdouble x_root, y_root;
region = get_active_region_type (window, (GdkEventAny*) event, x, y);
gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
sequence, GTK_EVENT_SEQUENCE_CLAIMED);
gdk_event_get_root_coords (event, &x_root, &y_root);
gdk_window_begin_move_drag_for_device (gtk_widget_get_window (GTK_WIDGET (window)),
gdk_event_get_device ((GdkEvent*) event),
GDK_BUTTON_PRIMARY,
x_root, y_root,
gdk_event_get_time (event));
if (region != GTK_WINDOW_REGION_TITLE)
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
}
}
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
priv->drag_possible = FALSE;
static void
drag_gesture_update_cb (GtkGestureDrag *gesture,
gdouble offset_x,
gdouble offset_y,
GtkWindow *window)
{
gint double_click_distance;
GtkSettings *settings;
settings = gtk_widget_get_settings (GTK_WIDGET (window));
g_object_get (settings,
"gtk-double-click-distance", &double_click_distance,
NULL);
if (ABS (offset_x) > double_click_distance ||
ABS (offset_y) > double_click_distance)
{
gdouble start_x, start_y;
gint x_root, y_root;
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
gdk_window_get_root_coords (gtk_widget_get_window (GTK_WIDGET (window)),
start_x, start_y, &x_root, &y_root);
gdk_window_begin_move_drag_for_device (gtk_widget_get_window (GTK_WIDGET (window)),
gtk_gesture_get_device (GTK_GESTURE (gesture)),
gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)),
x_root, y_root,
gtk_get_current_event_time ());
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
}
}
static void
@@ -1632,8 +1661,14 @@ gtk_window_constructed (GObject *object)
GTK_PHASE_NONE);
g_signal_connect (priv->multipress_gesture, "pressed",
G_CALLBACK (multipress_gesture_pressed_cb), object);
g_signal_connect (priv->multipress_gesture, "stopped",
G_CALLBACK (multipress_gesture_stopped_cb), object);
priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (object));
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture),
GTK_PHASE_CAPTURE);
g_signal_connect (priv->drag_gesture, "drag-begin",
G_CALLBACK (drag_gesture_begin_cb), object);
g_signal_connect (priv->drag_gesture, "drag-update",
G_CALLBACK (drag_gesture_update_cb), object);
}
}
@@ -7766,8 +7801,10 @@ get_active_region_type (GtkWindow *window, GdkEventAny *event, gint x, gint y)
static gboolean
gtk_window_handle_wm_event (GtkWindow *window,
GdkEvent *event)
GdkEvent *event,
gboolean run_drag)
{
gboolean retval = GDK_EVENT_PROPAGATE;
GtkWindowPrivate *priv;
if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE ||
@@ -7776,12 +7813,16 @@ gtk_window_handle_wm_event (GtkWindow *window,
{
priv = window->priv;
if (run_drag && priv->drag_gesture)
retval |= gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->drag_gesture),
(const GdkEvent*) event);
if (priv->multipress_gesture)
return gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->multipress_gesture),
(const GdkEvent*) event);
retval |= gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->multipress_gesture),
(const GdkEvent*) event);
}
return GDK_EVENT_PROPAGATE;
return retval;
}
gboolean
@@ -7792,6 +7833,9 @@ _gtk_window_check_handle_wm_event (GdkEvent *event)
widget = gtk_get_event_widget (event);
if (!GTK_IS_WINDOW (widget))
widget = gtk_widget_get_toplevel (widget);
if (!GTK_IS_WINDOW (widget))
return GDK_EVENT_PROPAGATE;
@@ -7808,7 +7852,7 @@ _gtk_window_check_handle_wm_event (GdkEvent *event)
if (gtk_widget_event (widget, event))
return GDK_EVENT_STOP;
return gtk_window_handle_wm_event (GTK_WINDOW (widget), event);
return gtk_window_handle_wm_event (GTK_WINDOW (widget), event, TRUE);
}
static gboolean
@@ -7816,7 +7860,7 @@ gtk_window_event (GtkWidget *widget,
GdkEvent *event)
{
if (widget != gtk_get_event_widget (event))
return gtk_window_handle_wm_event (GTK_WINDOW (widget), event);
return gtk_window_handle_wm_event (GTK_WINDOW (widget), event, FALSE);
return GDK_EVENT_PROPAGATE;
}

View File

@@ -108,4 +108,11 @@
<property name="receives_default">True</property>
<signal name="clicked" handler="software_button_clicked_cb" swapped="no"/>
</object>
<object class="GtkSizeGroup" id="buttons">
<property name="mode">vertical</property>
<widgets>
<widget name="cancel_button"/>
<widget name="ok_button"/>
</widgets>
</object>
</interface>

View File

@@ -53,4 +53,7 @@
</object>
</child>
</template>
<object class="GtkSizeGroup" id="buttons">
<property name="mode">vertical</property>
</object>
</interface>