Compare commits

...

69 Commits

Author SHA1 Message Date
Carlos Garnacho d94d6e560b gtk,range: Have slider jump to the pointer coordinates on touch devices
This widget is too narrow to make touch interaction tricky enough, so
don't add the penalty of having the slider run farther from the touch
coordinates if it happens to miss the slider.
2012-01-05 02:03:26 +01:00
Carlos Garnacho 33b160569f gtk,scrolledwindow: Ensure the view snaps back when overshooting
Instead of just stopping the acceleration source ID, check whether
it's overshooting, and let it snap back if needed after ::grab-notify
and ::button-release-event
2012-01-05 01:38:16 +01:00
Carlos Garnacho fd941bd764 gtk,scrolledwindow: set slower inverse acceleration on the overshoot area
This is so snapping back is more fluid and noticeable.
2012-01-05 01:37:13 +01:00
Carlos Garnacho 18e451da6e gtk,scrolledwindow: Improve initial velocity calculation
Velocity calculation has been refactored out of the captured motion events
handler, and also has more tolerance defore determining that a drag is actually
still.
2012-01-05 01:37:13 +01:00
Carlos Garnacho 0e05d33eee gtk,menu: Don't popdown submenus on button release for touch devices
This is so submenus stay open as the parent menu item is pressed/released,
as the user would typically lift the finger in order to select a submenu
item.
2012-01-05 01:37:12 +01:00
Carlos Garnacho e5b2e528ca gtk,settings: Deprecate gtk-touchscreen-mode
It's not used anywhere in GTK+ anymore.
2012-01-05 01:37:12 +01:00
Carlos Garnacho 355a4cdc14 gtk,range: Remove gtk-touchscreen-mode usage
Emulated crossing events with mode GDK_CROSSING_TOUCH_PRESS/RELEASE
already catter dinamically for the "don't prelight on touch devices"
usecase.
2012-01-05 01:37:12 +01:00
Carlos Garnacho d45dc8f3c3 gtk,togglebutton: Remove gtk-touchcreen-mode usage
Emulated crossing events with mode GDK_CROSSING_TOUCH_PRESS/RELEASE
already catter dinamically for the "don't prelight on touch devices"
usecase.
2012-01-05 01:37:12 +01:00
Carlos Garnacho dd8cf478b0 gtk,menushell: Remove gtk-touchscreen-mode usage
This usage in a keybinding signal is hardly related to touchscreens,
so just remove it.
2012-01-05 01:37:12 +01:00
Carlos Garnacho c33786446c gtk,menu: Remove gtk-touchscreen-mode from scrolling code
scrolling is handled via ::captured-event dynamically, so remove
this now unused code.
2012-01-05 01:37:12 +01:00
Carlos Garnacho c4b7848aeb gtk,menu: Select the first item for touch devices
This was done through gtk-touchscreen-mode, now is handled
dynamically on the current event source device.
2012-01-05 01:37:12 +01:00
Carlos Garnacho 2d88021014 gtk,menu: Implement scrolling through ::captured-event for touch devices
This makes overflown menus scrollable via direct manipulation. Once past
the threshold, the item below the pointer is unselected and scrolling
starts.
2012-01-05 01:37:12 +01:00
Carlos Garnacho 5cf832da40 gtk,menu: handle item selection for touch devices dynamically
Instead of using gtk-touchscreen-mode, the behavior changes depending
on the source device in use.
2012-01-05 01:37:12 +01:00
Carlos Garnacho bbbc5198a3 gtk,textview: Pop up context menu on press-and-hold 2012-01-05 01:37:12 +01:00
Carlos Garnacho 2e4eaa7cfd gtk,textview: Also cancel DnD on ::grab-notify
If is about to start when the drag device is grabbed
somewhere else, unset drag start x/y so DnD doesn't
happen anyway.
2012-01-05 01:37:12 +01:00
Carlos Garnacho 07a4c8174f gtk,entry: Pop up menu on press-and-hold 2012-01-05 01:37:12 +01:00
Carlos Garnacho 5f20868ec8 gtk,label: Pop up menu on press-and-hold 2012-01-05 01:37:12 +01:00
Carlos Garnacho 5d2c04b9d1 xi2: Get the effective group state by ORing the XIGroupState values 2012-01-05 01:37:12 +01:00
Carlos Garnacho 62c32d742e gdk: Ensure that GdkPointerWindowInfo is only generated for pointers 2012-01-05 01:37:11 +01:00
Carlos Garnacho 736438ecfb gtk: Only set widget under device on non-virtual crossing events
_gtk_widget_set_device_window() is suppose to make accounting of
the topmost widget under the device at each time, so avoid setting
it on virtual crossing events as the device is already in another
window.
2012-01-05 01:37:11 +01:00
Carlos Garnacho b02c3cf4a9 gtk,scrolledwindow: remove scrollbars auto-hide
This is in a really sorry state, and scrollbars need to be
thought out for the new design anyway.
2012-01-05 01:37:11 +01:00
Carlos Garnacho cf4b293b75 gtk,scrolledwindow: capture crossing events when dragging
Also, instead of connecting 2 more times to ::captured-event,
have it all go through a single handler.
2012-01-05 01:37:11 +01:00
Carlos Garnacho 64534d1fc2 gtk,scrolledwindow: Add GtkKineticScrollingFlags
gtk_scrolled_window_set_kinetic_scrolling() now takes a set of flags,
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS makes the "capture button
press and maybe replay later" vs "let button press go through, but
trust in ::grab-broken to undo things" an opt-in, by default that
flag is set, which is the most conservative approach.
2012-01-05 01:37:11 +01:00
Carlos Garnacho 380299681a gtk,scrolledwindow: Grab only after starting drag
This is so the grab doesn't break the implicit grab on the
child widget's window, which avoids that the button press and
release are possibly sent to different windows, and after the
grab was actually broken.
2012-01-05 01:37:11 +01:00
Carlos Garnacho bbe6075408 gdk: Generate crossing events around touch devices' press/release
Anytime a touch device interacts, the crossing events generation
will change to a touch mode where only events with mode
GDK_CROSSING_TOUCH_PRESS/RELEASE are handled, and those are sent
around button press/release. Those are virtual as the master
device may still stay on the window.

Whenever there is a switch of slave device (the user starts
using another non-touch device), a crossing event with mode
GDK_CROSSING_DEVICE_SWITCH may generated if needed, and the normal
crossing event handling is resumed.
2012-01-05 01:37:11 +01:00
Carlos Garnacho 9615fa61a0 gdk: Keep track of the last slave device used
This last slave device (stored per master) is used to fill
in the missing slave device in synthesized crossing events
not directly caused by a device event (ie. due to configure
events or grabs)
2012-01-05 01:37:11 +01:00
Carlos Garnacho 5a0ec9859e gtk,tooltips: Use the source device instead of gtk-touchscreen-mode
This makes tooltips behavior dynamic based on the interacting device.
2012-01-05 01:37:11 +01:00
Carlos Garnacho 03d89b557a gtk,scrolledwindow: Clamp early overshooting when snapping back
Fixes the situation where overshooting in scrolled windows with a
small viewport could end up overshooting right to the opposite
side, and then back again indefinitely.
2012-01-05 01:37:11 +01:00
Carlos Garnacho d12924f241 gtk: Handle motion hints for ::captured-event
Request automatically more motion events in behalf of
the original widget if it listens to motion hints. So
the capturing widget doesn't need to handle such
implementation details.
2012-01-05 01:37:11 +01:00
Carlos Garnacho e349e46abd gtk,scrolledwindow: capture motions until the kinetic scrolling cancellation
If the widget which events are captured listens to motion hints, there
are situations where neither the scrolled window or the child widget
request more motions.
2012-01-05 01:37:11 +01:00
Carlos Garnacho 888be799d9 gtk,pah: Show a bigger press-and-hold indicator on touchscreens
On touch devices it uses a 2.5cm diameter via gdk_screen_get_width_mm()
in the hope that it'll pop out of the finger area.
2012-01-05 01:37:11 +01:00
Carlos Garnacho 365a4f7535 gtk,scrolledwindow: Unset dragging device on ::grab-notify
The child widget may still call gtk_(device_)grab_add, which left
the scrolled window in an inconsistent state.
2012-01-05 01:37:10 +01:00
Carlos Garnacho 6468ae821f gtk,scrolledwindow: Rework physics behind kinetic scrolling
The maths being used didn't resemble much about velocities or
friction/deceleration, so reimplement it in terms of velocity
vectors and decelerations, measured in pixels/ms^2.

Overshooting is also handled within the deceleration effect,
turning into a constant acceleration vector in the opposite
direction so it returns elastically within the boundaries.
2012-01-05 01:37:10 +01:00
Carlos Garnacho 4f04075a8f gtk,scrolledwindow: Implement overshooting
An extra GdkWindow has been added, this window is the parent
of the child widget, and is the one getting resized/moved when
overshooting.

The unclamped adjustments' values are also stored in
GtkScrolledWindowPrivate as a separate value, overshooting is
pretty specific to GtkScrolledWindow and it isn't worth to
expose API in GtkAlignment for this single purpose.

This method allows GtkScrollable children to be blissfully
unaware of overshooting, as otherwise they'd have to handle
rather odd adjustment values themselves.
2012-01-05 01:37:10 +01:00
Carlos Garnacho ae058ae7ef gtk,scrolledwindow: Add window for overshooting
This window is the child widget's parent window, and will
be the one that's moved when overshooting.
2012-01-05 01:37:10 +01:00
Carlos Garnacho 7f0fe69b6e scrolledwindow: Handle nested scrolled windows in kinetic scrolling
The innermost scrolled window always gets to capture the events, all
scrolled windows above it just let the event go through. Ideally
reaching a limit on the innermost scrolled window would propagate
the dragging up the hierarchy in order to keep following the touch
coords, although that'd involve rather evil hacks just to cater
for broken UIs.
2012-01-05 01:37:10 +01:00
Carlos Garnacho ce879ae739 gtk: Add event storing/replaying to GtkWidget::captured-event
It now returns a GtkCapturedEventFlags which tells whether the
widget captured the event, and additionally whether the event
should be stored for later replay.

gtk_widget_release_captured_events() has been added too so
all stored events are released onto the widget that was initially
to receive the events.
2012-01-05 01:37:10 +01:00
Carlos Garnacho df2bb4a92d scrolledwindow: Use event times when calculating deceleration
Using g_get_current_time() isn't going to be realistic on lagging
events.
2012-01-05 01:37:10 +01:00
Carlos Garnacho e45d9994a4 scrolledwindow: bypass kinetic scrolling if no scrollbars are shown 2012-01-05 01:37:10 +01:00
Carlos Garnacho 447ddb08fc scrolledwindow: Remove priv->event_window
It looks like a leftover from pre-captured-event iterations of
the patch, it is completely unnecessary now.
2012-01-05 01:37:10 +01:00
Carlos Garnacho 0df96ac738 scrolledwindow: Don't use p-a-h for the "let event go through" timeout
Just use a timeout there, the press-and-hold feedback is undesirable
here.
2012-01-05 01:37:10 +01:00
Carlos Garnacho 688d8c0796 gtk,pah: Hook directly into gtk_main_do_event()
Press and hold couldn't reasonably work if nested widgets
handle ::captured-event, once the widget inits press-and-hold,
it'd better also handle possible cancellation on motion and
button release, which isn't guaranteed with ::capture-event.

Also, tentatively start press-and-hold by default on the
grab_widget, and before event capturing happens, this avoids
awkward situations like the scrolled window preventing/delaying
press-and-hold to happen on the child textview for example.
2012-01-05 01:37:10 +01:00
Carlos Garnacho fb7047d212 scrolledwindow: Enable kinetic scrolling by default 2012-01-05 01:37:10 +01:00
Carlos Garnacho 1bb03e26a4 scrolledwindow: add another shortcut to bypass event capture
When clicked again close to the previous button press location
(assuming it had ~0 movement), the scrolled window wil allow
the child to handle the events immediately.

This is so the user doesn't have to wait to the p-a-h timeout
in order to operate on the scrolledwindow child.
2012-01-05 01:37:10 +01:00
Carlos Garnacho 7df706715c scrolledwindow: Only do kinetic scrolling on touch devices
This is sort of meaningless on pointer devices, besides it implies
a different input event handling on child widgets that's unnecessary
there.
2012-01-05 01:37:09 +01:00
Carlos Garnacho 40455db2f5 scrolledwindow: Don't crash on 0-sized motion buffer 2012-01-05 01:37:09 +01:00
Carlos Garnacho 64ec631874 scrolledwindow: Set also a GTK+ grab on p-a-h scrolling
This is so the widget is ensured to receive the events
regardless of the pointer position.
2012-01-05 01:37:09 +01:00
Carlos Garnacho de593da052 scrolledwindow: Add GdkDevice parameter to ::press-and-hold handler 2012-01-05 01:37:09 +01:00
Carlos Garnacho a308c35112 gtk: Handle press-and-hold for touch devices
Also, only react to the first button
2012-01-05 01:37:09 +01:00
Carlos Garnacho 412ba5cc2d gtk: Add a GdkDevice parameter to ::press-and-hold
This would be useful when popping up menus, just so we
know what device to trigger it for.
2012-01-05 01:37:09 +01:00
Carlos Garnacho e4e11aa4a2 gtk: Clean up press-and-hold code
The press and hold animation now fully relies on style context
transitions, finishing the p-a-h operation right after it
finishes. There's also no need to connect to ::drag-begin as
::grab-notify will also tell when a grab begins.
2012-01-05 01:37:09 +01:00
Carlos Garnacho e48285f759 tests: Add an entry to testpressandhold
Useful for checking behavior while selecting,
starting drags, subwindows...
2012-01-05 01:37:09 +01:00
Carlos Garnacho dcd2dba384 entry: Handle ::grab-notify
Store the device, and unset private fields whenever the device
is shadowed by another GTK+ grab, so popping up menus while
selecting (i.e. press-and-hold) doesn't leave it in a confused
state.
2012-01-05 01:37:09 +01:00
Carlos Garnacho 1a6a1118a4 gtk: Do not use deprecated APIs on press-and-hold 2012-01-05 01:37:09 +01:00
Carlos Garnacho d6476eb99a tests: Update testkineticscrolling to use GtkGrid 2012-01-05 01:37:09 +01:00
Carlos Garnacho cdd97bbff7 gtk: connect to grab-notify for press and hold
This is so press and hold is cancelled if a click actually
causes an inner widget to do a GTK+ grab.
2012-01-05 01:37:09 +01:00
Carlos Garnacho a447b2fee2 gtk: Propagate ::captured-event up the hierarchy for crossing events 2012-01-05 01:37:09 +01:00
Carlos Garnacho 4af317307f gtk: emit ::captured-event starting from the GTK grab widget 2012-01-05 01:37:09 +01:00
Carlos Garcia Campos aa0c220f7d scrolledwindow: Allow selections and drag-and-drop when kinetic scrolling is enabled
If the scrolling doesn't start after a long press, the scrolling is
cancelled and events are handled by child widget normally.
2012-01-05 01:37:08 +01:00
Carlos Garcia Campos 39966dbaf8 Add GtkWidget::press-and-hold signal
Press-and-hold signal is emitted when the mouse button is pressed for a
given amount of time, specified in the new "press-and-hold-timeout"
GtkSetting. It's commonly used in mobile platforms to emulate a right
click to show a context menu. This patch is based on previous patches by
Kristian Rietveld and Danielle Madeley.

https://bugzilla.gnome.org/show_bug.cgi?id=315645
2012-01-05 01:37:08 +01:00
Carlos Garnacho fbb70becef gtksettings: Set animation for press-and-hold through GtkStyleProvider
The "gtk-press-and-hold-timeout" setting has also been added, to control
its duration.
2012-01-05 01:37:08 +01:00
Carlos Garnacho 388dcb4234 themingengine: Implement press-and-hold notification renderer
gtk_render_activity() uses it for the GTK_STYLE_CLASS_PRESS_AND_HOLD
style class.
2012-01-05 01:25:39 +01:00
Carlos Garcia Campos e8a9fad467 scrolledwindow: Add auto-hide-scrollbars style property
To hide the scrollbars in kinetic mode when not scrolling.
2012-01-05 01:25:38 +01:00
Carlos Garcia Campos ccb7da408e tests: Add new test for kinetic scrolling 2012-01-05 01:25:38 +01:00
Carlos Garcia Campos 1764558d8c test: Add checkbox to enable/disable kinetic scrolling in scrolled window test 2012-01-05 01:25:38 +01:00
Carlos Garcia Campos 90ee273c66 scrolledwindow: Initial kinetic scrolling support 2012-01-05 01:25:38 +01:00
Carlos Garcia Campos b0b26cc2e0 timeline: Add _gtk_timeline_get_elapsed_time()
To get the time in milliseconds since the last frame
2012-01-05 01:25:38 +01:00
Carlos Garcia Campos 990e049510 Add GtkWidget::captured-event signal
https://bugzilla.gnome.org/show_bug.cgi?id=641836
2012-01-05 01:25:38 +01:00
Carlos Garnacho 8f013f4055 gdk: Add GDK_SOURCE_TOUCH
This device source applies to touch capable devices, most
notably touchscreens.
2012-01-05 01:25:38 +01:00
39 changed files with 3005 additions and 537 deletions
+3
View File
@@ -2936,6 +2936,9 @@ gtk_scrolled_window_get_min_content_width
gtk_scrolled_window_set_min_content_width
gtk_scrolled_window_get_min_content_height
gtk_scrolled_window_set_min_content_height
GtkKineticScrollingFlags
gtk_scrolled_window_set_kinetic_scrolling
gtk_scrolled_window_get_kinetic_scrolling
<SUBSECTION Standard>
GTK_SCROLLED_WINDOW
+3 -1
View File
@@ -61,6 +61,7 @@ typedef enum
* of a stylus on a graphics tablet.
* @GDK_SOURCE_CURSOR: the device is a graphics tablet "puck" or similar device.
* @GDK_SOURCE_KEYBOARD: the device is a keyboard.
* @GDK_SOURCE_TOUCH: the device is a touch capable device.
*
* An enumeration describing the type of an input device in general terms.
*/
@@ -70,7 +71,8 @@ typedef enum
GDK_SOURCE_PEN,
GDK_SOURCE_ERASER,
GDK_SOURCE_CURSOR,
GDK_SOURCE_KEYBOARD
GDK_SOURCE_KEYBOARD,
GDK_SOURCE_TOUCH
} GdkInputSource;
/**
+22 -9
View File
@@ -899,15 +899,25 @@ switch_to_pointer_grab (GdkDisplay *display,
if (grab == NULL) /* Ungrabbed, send events */
{
pointer_window = NULL;
if (new_toplevel)
{
/* Find (possibly virtual) child window */
pointer_window =
_gdk_window_find_descendant_at (new_toplevel,
x, y,
NULL, NULL);
}
/* If the source device is a touch device, do not
* propagate any enter event yet, until one is
* synthesized when needed.
*/
if (source_device &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
info->need_touch_press_enter = TRUE;
pointer_window = NULL;
if (new_toplevel &&
!info->need_touch_press_enter)
{
/* Find (possibly virtual) child window */
pointer_window =
_gdk_window_find_descendant_at (new_toplevel,
x, y,
NULL, NULL);
}
if (pointer_window != last_grab->window)
synthesize_crossing_events (display, device, source_device,
@@ -1122,6 +1132,9 @@ _gdk_display_get_pointer_info (GdkDisplay *display,
{
GdkPointerWindowInfo *info;
if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
device = gdk_device_get_associated_device (device);
if (G_UNLIKELY (!device))
return NULL;
+2
View File
@@ -75,6 +75,8 @@ typedef struct
gdouble toplevel_x, toplevel_y;
guint32 state;
guint32 button;
GdkDevice *last_slave;
guint need_touch_press_enter : 1;
} GdkPointerWindowInfo;
typedef struct
+13 -1
View File
@@ -354,6 +354,15 @@ typedef enum
* @GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated.
* @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed
* state (e.g. sensitivity).
* @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed
* state (e.g. sensitivity).
* @GDK_CROSSING_TOUCH_PRESS: crossing because a touch device was pressed,
* this event is synthetic as the pointer might have not left the window.
* @GDK_CROSSING_TOUCH_RELEASE: crossing because a touch device was released.
* this event is synthetic as the pointer might have not left the window.
* @GDK_CROSSING_DEVICE_SWITCH: crossing because of a device switch (i.e.
* a mouse taking control of the pointer after a touch device), this event
* is synthetic as the pointer didn't leave the window.
*
* Specifies the crossing mode for #GdkEventCrossing.
*/
@@ -364,7 +373,10 @@ typedef enum
GDK_CROSSING_UNGRAB,
GDK_CROSSING_GTK_GRAB,
GDK_CROSSING_GTK_UNGRAB,
GDK_CROSSING_STATE_CHANGED
GDK_CROSSING_STATE_CHANGED,
GDK_CROSSING_TOUCH_PRESS,
GDK_CROSSING_TOUCH_RELEASE,
GDK_CROSSING_DEVICE_SWITCH
} GdkCrossingMode;
/**
+96 -14
View File
@@ -8340,9 +8340,11 @@ send_crossing_event (GdkDisplay *display,
GdkEvent *event;
guint32 window_event_mask, type_event_mask;
GdkDeviceGrabInfo *grab;
GdkPointerWindowInfo *pointer_info;
gboolean block_event = FALSE;
grab = _gdk_display_has_device_grab (display, device, serial);
pointer_info = _gdk_display_get_pointer_info (display, device);
if (grab != NULL &&
!grab->owner_events)
@@ -8355,7 +8357,13 @@ send_crossing_event (GdkDisplay *display,
else
window_event_mask = window->event_mask;
if (type == GDK_LEAVE_NOTIFY)
if (pointer_info->need_touch_press_enter &&
mode != GDK_CROSSING_TOUCH_PRESS &&
mode != GDK_CROSSING_TOUCH_RELEASE)
{
block_event = TRUE;
}
else if (type == GDK_LEAVE_NOTIFY)
{
type_event_mask = GDK_LEAVE_NOTIFY_MASK;
window->devices_inside = g_list_remove (window->devices_inside, device);
@@ -9064,7 +9072,7 @@ do_synthesize_crossing_event (gpointer data)
_gdk_synthesize_crossing_events (display,
pointer_info->window_under_pointer,
new_window_under_pointer,
device, NULL,
device, pointer_info->last_slave,
GDK_CROSSING_NORMAL,
pointer_info->toplevel_x,
pointer_info->toplevel_y,
@@ -9179,7 +9187,7 @@ proxy_pointer_event (GdkDisplay *display,
guint state;
gdouble toplevel_x, toplevel_y;
guint32 time_;
gboolean non_linear;
gboolean non_linear, need_synthetic_enter = FALSE;
event_window = source_event->any.window;
gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
@@ -9199,6 +9207,13 @@ proxy_pointer_event (GdkDisplay *display,
source_event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL))
non_linear = TRUE;
if (pointer_info->need_touch_press_enter &&
gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCH)
{
pointer_info->need_touch_press_enter = FALSE;
need_synthetic_enter = TRUE;
}
/* If we get crossing events with subwindow unexpectedly being NULL
that means there is a native subwindow that gdk doesn't know about.
We track these and forward them, with the correct virtual window
@@ -9322,6 +9337,18 @@ proxy_pointer_event (GdkDisplay *display,
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
/* The last device to interact with the window was a touch device,
* which synthesized a leave notify event, so synthesize another enter
* notify to tell the pointer is on the window.
*/
if (need_synthetic_enter)
_gdk_synthesize_crossing_events (display,
NULL, pointer_window,
device, source_device,
GDK_CROSSING_DEVICE_SWITCH,
toplevel_x, toplevel_y,
state, time_, NULL,
serial, FALSE);
is_hint = FALSE;
if (event_win &&
@@ -9380,6 +9407,7 @@ proxy_button_event (GdkEvent *source_event,
GdkWindow *pointer_window;
GdkWindow *parent;
GdkEvent *event;
GdkPointerWindowInfo *pointer_info;
guint state;
guint32 time_;
GdkEventType type;
@@ -9399,6 +9427,7 @@ proxy_button_event (GdkEvent *source_event,
toplevel_window = convert_native_coords_to_toplevel (event_window,
toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y);
pointer_info = _gdk_display_get_pointer_info (display, device);
if (type == GDK_BUTTON_PRESS &&
!source_event->any.send_event &&
@@ -9451,6 +9480,30 @@ proxy_button_event (GdkEvent *source_event,
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
if (type == GDK_BUTTON_PRESS &&
pointer_info->need_touch_press_enter)
{
GdkCrossingMode mode;
/* The last device to interact with the window was a touch device,
* which synthesized a leave notify event, so synthesize another enter
* notify to tell the pointer is on the window.
*/
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
mode = GDK_CROSSING_TOUCH_PRESS;
else
mode = GDK_CROSSING_DEVICE_SWITCH;
pointer_info->need_touch_press_enter = FALSE;
_gdk_synthesize_crossing_events (display,
NULL,
pointer_info->window_under_pointer,
device, source_device, mode,
toplevel_x, toplevel_y,
state, time_, source_event,
serial, FALSE);
}
event = _gdk_make_event (event_win, type, source_event, FALSE);
switch (type)
@@ -9471,7 +9524,23 @@ proxy_button_event (GdkEvent *source_event,
gdk_event_set_source_device (event, source_device);
if (type == GDK_BUTTON_PRESS)
_gdk_event_button_generate (display, event);
_gdk_event_button_generate (display, event);
else if (type == GDK_BUTTON_RELEASE &&
pointer_window == pointer_info->window_under_pointer &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
{
/* Synthesize a leave notify event
* whenever a touch device is released
*/
pointer_info->need_touch_press_enter = TRUE;
_gdk_synthesize_crossing_events (display,
pointer_window, NULL,
device, source_device,
GDK_CROSSING_TOUCH_RELEASE,
toplevel_x, toplevel_y,
state, time_, NULL,
serial, FALSE);
}
return TRUE;
case GDK_SCROLL:
@@ -9590,6 +9659,17 @@ _gdk_windowing_got_event (GdkDisplay *display,
{
GdkInputMode mode;
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
{
pointer_info = _gdk_display_get_pointer_info (display, device);
if (source_device != pointer_info->last_slave &&
gdk_device_get_device_type (source_device) == GDK_DEVICE_TYPE_SLAVE)
pointer_info->last_slave = source_device;
else
source_device = pointer_info->last_slave;
}
g_object_get (device, "input-mode", &mode, NULL);
_gdk_display_device_grab_update (display, device, source_device, serial);
@@ -9608,8 +9688,6 @@ _gdk_windowing_got_event (GdkDisplay *display,
if (!event_window)
return;
pointer_info = _gdk_display_get_pointer_info (display, device);
#ifdef DEBUG_WINDOW_PRINTING
if (event->type == GDK_KEY_PRESS &&
(event->key.keyval == 0xa7 ||
@@ -9694,15 +9772,19 @@ _gdk_windowing_got_event (GdkDisplay *display,
}
}
/* Store last pointer window and position/state */
old_state = pointer_info->state;
old_button = pointer_info->button;
if (pointer_info)
{
/* Store last pointer window and position/state */
old_state = pointer_info->state;
old_button = pointer_info->button;
gdk_event_get_coords (event, &x, &y);
convert_native_coords_to_toplevel (event_window, x, y, &x, &y);
pointer_info->toplevel_x = x;
pointer_info->toplevel_y = y;
gdk_event_get_state (event, &pointer_info->state);
}
gdk_event_get_coords (event, &x, &y);
convert_native_coords_to_toplevel (event_window, x, y, &x, &y);
pointer_info->toplevel_x = x;
pointer_info->toplevel_y = y;
gdk_event_get_state (event, &pointer_info->state);
if (event->type == GDK_BUTTON_PRESS ||
event->type == GDK_BUTTON_RELEASE)
pointer_info->button = event->button.button;
+1 -1
View File
@@ -742,7 +742,7 @@ _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state,
{
gint group;
group = group_state->base + group_state->latched + group_state->locked;
group = group_state->base | group_state->latched | group_state->locked;
/* FIXME: do we need the XKB complications for this ? */
group = CLAMP(group, 0, 3);
+4
View File
@@ -254,6 +254,10 @@ create_device (GdkDeviceManager *device_manager,
input_source = GDK_SOURCE_ERASER;
else if (strstr (tmp_name, "cursor"))
input_source = GDK_SOURCE_CURSOR;
else if (strstr (tmp_name, "finger") ||
(strstr (tmp_name, "touch") &&
!strstr (tmp_name, "touchpad")))
input_source = GDK_SOURCE_TOUCH;
else if (strstr (tmp_name, "wacom") ||
strstr (tmp_name, "pen"))
input_source = GDK_SOURCE_PEN;
+4 -1
View File
@@ -1579,9 +1579,12 @@ device_grab_update_callback (GdkDisplay *display,
gpointer data,
gulong serial)
{
GdkPointerWindowInfo *pointer_info;
GdkDevice *device = data;
_gdk_display_device_grab_update (display, device, NULL, serial);
pointer_info = _gdk_display_get_pointer_info (display, device);
_gdk_display_device_grab_update (display, device,
pointer_info->last_slave, serial);
}
#define XSERVER_TIME_IS_LATER(time1, time2) \
+6
View File
@@ -3057,6 +3057,12 @@ gtk_css_provider_get_default (void)
" border-width: 0;\n"
" padding: 2;\n"
"}\n"
"\n"
".press-and-hold {\n"
" background-color: alpha (@bg_color, 0.5);\n"
" color: alpha (lighter (@selected_bg_color), 0.8);\n"
" border-width: 10;\n"
"}\n"
"\n";
provider = gtk_css_provider_new ();
+76 -26
View File
@@ -147,6 +147,8 @@ struct _GtkEntryPrivate
GtkShadowType shadow_type;
GtkWidget *popup_menu;
GdkDevice *device;
GdkDevice *completion_device;
GdkWindow *text_area;
@@ -482,6 +484,11 @@ static void gtk_entry_toggle_overwrite (GtkEntry *entry);
static void gtk_entry_select_all (GtkEntry *entry);
static void gtk_entry_real_activate (GtkEntry *entry);
static gboolean gtk_entry_popup_menu (GtkWidget *widget);
static gboolean gtk_entry_press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y);
static void keymap_direction_changed (GdkKeymap *keymap,
GtkEntry *entry);
@@ -545,9 +552,13 @@ static void gtk_entry_paste (GtkEntry *entry,
GdkAtom selection);
static void gtk_entry_update_primary_selection (GtkEntry *entry);
static void gtk_entry_do_popup (GtkEntry *entry,
GdkEventButton *event);
GdkDevice *device,
guint32 _time,
guint button);
static gboolean gtk_entry_mnemonic_activate (GtkWidget *widget,
gboolean group_cycling);
static void gtk_entry_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static void gtk_entry_check_cursor_blink (GtkEntry *entry);
static void gtk_entry_pend_cursor_blink (GtkEntry *entry);
static void gtk_entry_reset_blink_time (GtkEntry *entry);
@@ -692,6 +703,7 @@ gtk_entry_class_init (GtkEntryClass *class)
widget_class->state_flags_changed = gtk_entry_state_flags_changed;
widget_class->screen_changed = gtk_entry_screen_changed;
widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
widget_class->grab_notify = gtk_entry_grab_notify;
widget_class->drag_drop = gtk_entry_drag_drop;
widget_class->drag_motion = gtk_entry_drag_motion;
@@ -701,6 +713,7 @@ gtk_entry_class_init (GtkEntryClass *class)
widget_class->drag_data_delete = gtk_entry_drag_data_delete;
widget_class->popup_menu = gtk_entry_popup_menu;
widget_class->press_and_hold = gtk_entry_press_and_hold;
class->move_cursor = gtk_entry_move_cursor;
class->insert_at_cursor = gtk_entry_insert_at_cursor;
@@ -3771,7 +3784,8 @@ gtk_entry_button_press (GtkWidget *widget,
gtk_entry_reset_blink_time (entry);
priv->button = event->button;
priv->device = gdk_event_get_device ((GdkEvent *) event);
if (!gtk_widget_has_focus (widget))
{
priv->in_click = TRUE;
@@ -3783,8 +3797,10 @@ gtk_entry_button_press (GtkWidget *widget,
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
{
gtk_entry_do_popup (entry, event);
gtk_entry_do_popup (entry, event->device,
event->time, event->button);
priv->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */
priv->device = NULL;
return TRUE;
}
@@ -3962,9 +3978,10 @@ gtk_entry_button_release (GtkWidget *widget,
priv->in_drag = 0;
}
priv->button = 0;
priv->device = NULL;
gtk_entry_update_primary_selection (entry);
return TRUE;
@@ -4073,7 +4090,8 @@ gtk_entry_motion_notify (GtkWidget *widget,
priv->in_drag = FALSE;
priv->button = 0;
priv->device = NULL;
gtk_target_list_unref (target_list);
}
}
@@ -8542,6 +8560,26 @@ gtk_entry_mnemonic_activate (GtkWidget *widget,
return TRUE;
}
static void
gtk_entry_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
GtkEntryPrivate *priv;
priv = GTK_ENTRY (widget)->priv;
if (priv->device &&
gtk_widget_device_is_shadowed (widget, priv->device))
{
/* Unset button so we don't expect
* a button release anymore
*/
priv->button = 0;
priv->device = NULL;
priv->in_drag = FALSE;
}
}
static void
append_action_signal (GtkEntry *entry,
GtkWidget *menu,
@@ -8744,15 +8782,17 @@ popup_targets_received (GtkClipboard *clipboard,
info_entry_priv->popup_menu);
if (info->device)
if (gdk_device_get_source (info->device) != GDK_SOURCE_KEYBOARD)
gtk_menu_popup_for_device (GTK_MENU (info_entry_priv->popup_menu),
info->device, NULL, NULL, NULL, NULL, NULL,
info->button, info->time);
else
{
gtk_menu_popup (GTK_MENU (info_entry_priv->popup_menu), NULL, NULL,
popup_position_func, entry,
0, gtk_get_current_event_time ());
gtk_menu_popup_for_device (GTK_MENU (info_entry_priv->popup_menu),
info->device, NULL, NULL,
popup_position_func,
entry, NULL,
0, info->time);
gtk_menu_shell_select_first (GTK_MENU_SHELL (info_entry_priv->popup_menu), FALSE);
}
}
@@ -8760,10 +8800,12 @@ popup_targets_received (GtkClipboard *clipboard,
g_object_unref (entry);
g_slice_free (PopupInfo, info);
}
static void
gtk_entry_do_popup (GtkEntry *entry,
GdkEventButton *event)
GdkDevice *device,
guint32 _time,
guint button)
{
PopupInfo *info = g_slice_new (PopupInfo);
@@ -8772,19 +8814,10 @@ gtk_entry_do_popup (GtkEntry *entry,
* we get them, then we actually pop up the menu.
*/
info->entry = g_object_ref (entry);
if (event)
{
info->button = event->button;
info->time = event->time;
info->device = event->device;
}
else
{
info->button = 0;
info->time = gtk_get_current_event_time ();
info->device = NULL;
}
info->button = button;
info->time = _time;
info->device = device;
gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD),
gdk_atom_intern_static_string ("TARGETS"),
@@ -8795,7 +8828,24 @@ gtk_entry_do_popup (GtkEntry *entry,
static gboolean
gtk_entry_popup_menu (GtkWidget *widget)
{
gtk_entry_do_popup (GTK_ENTRY (widget), NULL);
gtk_entry_do_popup (GTK_ENTRY (widget),
gtk_get_current_event_device (),
gtk_get_current_event_time (),
0);
return TRUE;
}
static gboolean
gtk_entry_press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y)
{
if (action == GTK_PRESS_AND_HOLD_TRIGGER)
gtk_entry_do_popup (GTK_ENTRY (widget),
device, GDK_CURRENT_TIME, 1);
return TRUE;
}
+27
View File
@@ -920,6 +920,33 @@ typedef enum {
GTK_BORDER_STYLE_OUTSET
} GtkBorderStyle;
typedef enum {
GTK_CAPTURED_EVENT_NONE = 0,
GTK_CAPTURED_EVENT_HANDLED = 1 << 0,
GTK_CAPTURED_EVENT_STORE = 1 << 1
} GtkCapturedEventFlags;
/**
* GtkKineticScrollingFlags:
* @GTK_KINETIC_SCROLLING_NONE: No kinetic scrolling.
* @GTK_KINETIC_SCROLLING_ENABLED: Kinetic scrolling is enabled.
* @GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS: The first button
* press is captured by the scrolled window, and then replayed
* if the button press is meant to go to the child widget. This
* flag should be enabled if the child widget(s) perform
* non-reversible actions on #GtkWidget::button-press-event.
* If the widget does not do so, and handles
* #GtkWidget::grab-broken-event, it might be better off without
* this flag.
*
* Describes the kinetic scrolling behavior of a #GtkScrolledWindow
*/
typedef enum {
GTK_KINETIC_SCROLLING_NONE = 0,
GTK_KINETIC_SCROLLING_ENABLED = 1 << 0,
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS = 1 << 1
} GtkKineticScrollingFlags;
G_END_DECLS
+45 -13
View File
@@ -439,6 +439,11 @@ static void gtk_label_hierarchy_changed (GtkWidget *widget,
static void gtk_label_screen_changed (GtkWidget *widget,
GdkScreen *old_screen);
static gboolean gtk_label_popup_menu (GtkWidget *widget);
static gboolean gtk_label_press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y);
static void gtk_label_create_window (GtkLabel *label);
static void gtk_label_destroy_window (GtkLabel *label);
@@ -492,7 +497,9 @@ static void gtk_label_move_cursor (GtkLabel *label,
static void gtk_label_copy_clipboard (GtkLabel *label);
static void gtk_label_select_all (GtkLabel *label);
static void gtk_label_do_popup (GtkLabel *label,
GdkEventButton *event);
GdkDevice *device,
guint32 _time,
guint button);
static gint gtk_label_move_forward_word (GtkLabel *label,
gint start);
static gint gtk_label_move_backward_word (GtkLabel *label,
@@ -588,6 +595,7 @@ gtk_label_class_init (GtkLabelClass *class)
widget_class->drag_data_get = gtk_label_drag_data_get;
widget_class->grab_focus = gtk_label_grab_focus;
widget_class->popup_menu = gtk_label_popup_menu;
widget_class->press_and_hold = gtk_label_press_and_hold;
widget_class->focus = gtk_label_focus;
widget_class->get_request_mode = gtk_label_get_request_mode;
widget_class->get_preferred_width = gtk_label_get_preferred_width;
@@ -4625,7 +4633,8 @@ gtk_label_button_press (GtkWidget *widget,
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
{
info->link_clicked = 1;
gtk_label_do_popup (label, event);
gtk_label_do_popup (label, event->device,
event->time, event->button);
return TRUE;
}
else if (event->button == 1)
@@ -4643,7 +4652,8 @@ gtk_label_button_press (GtkWidget *widget,
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
{
gtk_label_do_popup (label, event);
gtk_label_do_popup (label, event->device,
event->time, event->button);
return TRUE;
}
@@ -4908,6 +4918,8 @@ gtk_label_motion (GtkWidget *widget,
if ((event->state & GDK_BUTTON1_MASK) == 0)
return FALSE;
gdk_event_request_motions (event);
if (info->in_drag)
{
if (gtk_drag_check_threshold (widget,
@@ -6157,14 +6169,32 @@ copy_link_activate_cb (GtkMenuItem *menu_item,
static gboolean
gtk_label_popup_menu (GtkWidget *widget)
{
gtk_label_do_popup (GTK_LABEL (widget), NULL);
gtk_label_do_popup (GTK_LABEL (widget),
gtk_get_current_event_device (),
gtk_get_current_event_time (),
0);
return TRUE;
}
static gboolean
gtk_label_press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y)
{
if (action == GTK_PRESS_AND_HOLD_TRIGGER)
gtk_label_do_popup (GTK_LABEL (widget),
device, GDK_CURRENT_TIME, 1);
return TRUE;
}
static void
gtk_label_do_popup (GtkLabel *label,
GdkEventButton *event)
GdkDevice *device,
guint32 _time,
guint button)
{
GtkLabelPrivate *priv = label->priv;
GtkWidget *menuitem;
@@ -6186,7 +6216,7 @@ gtk_label_do_popup (GtkLabel *label,
have_selection =
priv->select_info->selection_anchor != priv->select_info->selection_end;
if (event)
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
{
if (priv->select_info->link_clicked)
link = priv->select_info->active_link;
@@ -6246,15 +6276,17 @@ gtk_label_do_popup (GtkLabel *label,
g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
if (event)
gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
NULL, NULL,
event->button, event->time);
if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
gtk_menu_popup_for_device (GTK_MENU (menu), device,
NULL, NULL, NULL, NULL, NULL,
button, _time);
else
{
gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
popup_position_func, label,
0, gtk_get_current_event_time ());
gtk_menu_popup_for_device (GTK_MENU (menu),
device, NULL, NULL,
popup_position_func,
label, NULL,
0, _time);
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
}
}
+187 -83
View File
@@ -129,6 +129,9 @@
#include "gtkwidgetprivate.h"
#include "gtkwindowprivate.h"
static gboolean gtk_propagate_captured_event (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost);
/* Private type definitions
*/
@@ -1469,6 +1472,7 @@ gtk_main_do_event (GdkEvent *event)
{
GtkWidget *event_widget;
GtkWidget *grab_widget = NULL;
GtkWidget *topmost_widget = NULL;
GtkWindowGroup *window_group;
GdkEvent *rewritten_event = NULL;
GdkDevice *device;
@@ -1528,6 +1532,14 @@ gtk_main_do_event (GdkEvent *event)
if (!grab_widget)
grab_widget = gtk_window_group_get_current_grab (window_group);
/* 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
* from the toplevel (or topmost parentless parent).
*/
if (grab_widget)
topmost_widget = grab_widget;
/* If the grab widget is an ancestor of the event widget
* then we send the event to the original event widget.
* This is the key to implementing modality.
@@ -1624,14 +1636,26 @@ gtk_main_do_event (GdkEvent *event)
case GDK_WINDOW_STATE:
case GDK_GRAB_BROKEN:
case GDK_DAMAGE:
gtk_widget_event (event_widget, event);
if (!_gtk_widget_captured_event (event_widget, event))
gtk_widget_event (event_widget, event);
break;
case GDK_SCROLL:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
gtk_propagate_event (grab_widget, event);
if ((event->type == GDK_BUTTON_PRESS) &&
event->button.button == 1)
{
/* Handle press and hold on the grab widget before propagating up,
* so a parent capturing events doesn't delay nor prevent a child
* from doing the press-and-hold action.
*/
_gtk_widget_press_and_hold_check_start (grab_widget, &event->button);
}
if (!gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;
case GDK_KEY_PRESS:
@@ -1683,22 +1707,36 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_RELEASE:
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
gtk_propagate_event (grab_widget, event);
if ((event->type == GDK_BUTTON_RELEASE) &&
event->button.button == 1)
_gtk_widget_press_and_hold_check_cancel (grab_widget, &event->button);
else if (event->type == GDK_MOTION_NOTIFY)
_gtk_widget_press_and_hold_check_threshold (grab_widget,
&event->motion);
if (!gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;
case GDK_ENTER_NOTIFY:
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
event->any.window);
if (gtk_widget_is_sensitive (grab_widget))
if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
event->any.window);
if (gtk_widget_is_sensitive (grab_widget) &&
!gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_widget_event (grab_widget, event);
break;
case GDK_LEAVE_NOTIFY:
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
NULL);
if (gtk_widget_is_sensitive (grab_widget))
if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
NULL);
if (gtk_widget_is_sensitive (grab_widget) &&
!gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_widget_event (grab_widget, event);
break;
@@ -2320,6 +2358,135 @@ gtk_get_event_widget (GdkEvent *event)
return widget;
}
static gboolean
propagate_event_up (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
gboolean handled_event = FALSE;
/* Propagate event up the widget tree so that
* parents can see the button and motion
* events of the children.
*/
while (TRUE)
{
GtkWidget *tmp;
g_object_ref (widget);
/* Scroll events are special cased here because it
* feels wrong when scrolling a GtkViewport, say,
* to have children of the viewport eat the scroll
* event
*/
if (!gtk_widget_is_sensitive (widget))
handled_event = event->type != GDK_SCROLL;
else
handled_event = gtk_widget_event (widget, event);
tmp = gtk_widget_get_parent (widget);
g_object_unref (widget);
if (widget == topmost)
break;
widget = tmp;
if (handled_event || !widget)
break;
}
return handled_event;
}
static gboolean
propagate_event_down (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
gint handled_event = FALSE;
GList *widgets = NULL;
GList *l;
widgets = g_list_prepend (widgets, g_object_ref (widget));
while (widget && widget != topmost)
{
widget = gtk_widget_get_parent (widget);
if (!widget)
break;
widgets = g_list_prepend (widgets, g_object_ref (widget));
if (widget == topmost)
break;
}
for (l = widgets; l && !handled_event; l = g_list_next (l))
{
widget = (GtkWidget *)l->data;
if (!gtk_widget_is_sensitive (widget))
handled_event = TRUE;
else
handled_event = _gtk_widget_captured_event (widget, event);
}
g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
return handled_event;
}
static gboolean
propagate_event (GtkWidget *widget,
GdkEvent *event,
gboolean captured,
GtkWidget *topmost)
{
gboolean handled_event = FALSE;
gboolean (* propagate_func) (GtkWidget *widget, GdkEvent *event);
propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
{
/* Only send key events within Window widgets to the Window
* The Window widget will in turn pass the
* key event on to the currently focused widget
* for that window.
*/
GtkWidget *window;
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
g_object_ref (widget);
/* If there is a grab within the window, give the grab widget
* a first crack at the key event
*/
if (widget != window && gtk_widget_has_grab (widget))
handled_event = propagate_func (widget, event);
if (!handled_event)
{
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
if (gtk_widget_is_sensitive (window))
handled_event = propagate_func (window, event);
}
}
g_object_unref (widget);
return handled_event;
}
}
/* Other events get propagated up/down the widget tree */
return captured ?
propagate_event_down (widget, event, topmost) :
propagate_event_up (widget, event, topmost);
}
/**
* gtk_propagate_event:
* @widget: a #GtkWidget
@@ -2348,79 +2515,16 @@ void
gtk_propagate_event (GtkWidget *widget,
GdkEvent *event)
{
gint handled_event;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (event != NULL);
handled_event = FALSE;
g_object_ref (widget);
if ((event->type == GDK_KEY_PRESS) ||
(event->type == GDK_KEY_RELEASE))
{
/* Only send key events within Window widgets to the Window
* The Window widget will in turn pass the
* key event on to the currently focused widget
* for that window.
*/
GtkWidget *window;
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
/* If there is a grab within the window, give the grab widget
* a first crack at the key event
*/
if (widget != window && gtk_widget_has_grab (widget))
handled_event = gtk_widget_event (widget, event);
if (!handled_event)
{
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
if (gtk_widget_is_sensitive (window))
gtk_widget_event (window, event);
}
}
handled_event = TRUE; /* don't send to widget */
}
}
/* Other events get propagated up the widget tree
* so that parents can see the button and motion
* events of the children.
*/
if (!handled_event)
{
while (TRUE)
{
GtkWidget *tmp;
/* Scroll events are special cased here because it
* feels wrong when scrolling a GtkViewport, say,
* to have children of the viewport eat the scroll
* event
*/
if (!gtk_widget_is_sensitive (widget))
handled_event = event->type != GDK_SCROLL;
else
handled_event = gtk_widget_event (widget, event);
tmp = gtk_widget_get_parent (widget);
g_object_unref (widget);
widget = tmp;
if (!handled_event && widget)
g_object_ref (widget);
else
break;
}
}
else
g_object_unref (widget);
propagate_event (widget, event, FALSE, NULL);
}
static gboolean
gtk_propagate_captured_event (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
return propagate_event (widget, event, TRUE, topmost);
}
+2
View File
@@ -36,6 +36,7 @@ BOOLEAN:OBJECT,BOXED,BOXED
BOOLEAN:OBJECT,OBJECT,OBJECT
BOOLEAN:OBJECT,STRING,STRING
BOOLEAN:OBJECT,ENUM
BOOLEAN:OBJECT,ENUM,INT,INT
BOOLEAN:INT
BOOLEAN:INT,INT
BOOLEAN:INT,INT,INT
@@ -47,6 +48,7 @@ BOOLEAN:BOOLEAN,BOOLEAN,BOOLEAN
BOOLEAN:STRING
ENUM:ENUM
ENUM:VOID
FLAGS:BOXED
INT:POINTER
OBJECT:VOID
STRING:DOUBLE
+230 -276
View File
@@ -110,6 +110,7 @@
#include "gtksettings.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkdnd.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
@@ -227,12 +228,14 @@ static void gtk_menu_scroll_to (GtkMenu *menu,
gint offset);
static void gtk_menu_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static GtkCapturedEventFlags
gtk_menu_captured_event (GtkWidget *widget,
GdkEvent *event);
static void gtk_menu_stop_scrolling (GtkMenu *menu);
static void gtk_menu_remove_scroll_timeout (GtkMenu *menu);
static gboolean gtk_menu_scroll_timeout (gpointer data);
static gboolean gtk_menu_scroll_timeout_initial (gpointer data);
static void gtk_menu_start_scrolling (GtkMenu *menu);
static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
GtkWidget *menu_item);
@@ -510,6 +513,7 @@ gtk_menu_class_init (GtkMenuClass *class)
widget_class->get_preferred_width = gtk_menu_get_preferred_width;
widget_class->get_preferred_height = gtk_menu_get_preferred_height;
widget_class->get_preferred_height_for_width = gtk_menu_get_preferred_height_for_width;
widget_class->captured_event = gtk_menu_captured_event;
container_class->remove = gtk_menu_remove;
container_class->get_child_property = gtk_menu_get_child_property;
@@ -1057,6 +1061,7 @@ gtk_menu_init (GtkMenu *menu)
priv->needs_destruction_ref = TRUE;
priv->monitor_num = -1;
priv->drag_start_y = -1;
context = gtk_widget_get_style_context (GTK_WIDGET (menu));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
@@ -1462,7 +1467,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
GtkMenuShell *menu_shell;
gboolean grab_keyboard;
GtkWidget *parent_toplevel;
GdkDevice *keyboard, *pointer;
GdkDevice *keyboard, *pointer, *source_device = NULL;
g_return_if_fail (GTK_IS_MENU (menu));
g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
@@ -1599,6 +1604,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
(current_event->type != GDK_ENTER_NOTIFY))
menu_shell->priv->ignore_enter = TRUE;
source_device = gdk_event_get_source_device (current_event);
gdk_event_free (current_event);
}
else
@@ -1668,17 +1674,9 @@ gtk_menu_popup_for_device (GtkMenu *menu,
gtk_menu_scroll_to (menu, priv->scroll_offset);
/* if no item is selected, select the first one */
if (!menu_shell->priv->active_menu_item)
{
gboolean touchscreen_mode;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (touchscreen_mode)
gtk_menu_shell_select_first (menu_shell, TRUE);
}
if (!menu_shell->priv->active_menu_item &&
source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
gtk_menu_shell_select_first (menu_shell, TRUE);
/* Once everything is set up correctly, map the toplevel */
gtk_widget_show (priv->toplevel);
@@ -3313,34 +3311,6 @@ gtk_menu_get_preferred_height_for_width (GtkWidget *widget,
g_free (nat_heights);
}
static gboolean
gtk_menu_button_scroll (GtkMenu *menu,
GdkEventButton *event)
{
GtkMenuPrivate *priv = menu->priv;
if (priv->upper_arrow_prelight || priv->lower_arrow_prelight)
{
gboolean touchscreen_mode;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (touchscreen_mode)
gtk_menu_handle_scrolling (menu,
event->x_root, event->y_root,
event->type == GDK_BUTTON_PRESS,
FALSE);
return TRUE;
}
return FALSE;
}
static gboolean
pointer_in_menu_window (GtkWidget *widget,
gdouble x_root,
@@ -3377,13 +3347,16 @@ static gboolean
gtk_menu_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GdkDevice *source_device;
GtkWidget *event_widget;
GtkMenu *menu;
if (event->type != GDK_BUTTON_PRESS)
return FALSE;
/* Don't pass down to menu shell for presses over scroll arrows
*/
if (gtk_menu_button_scroll (GTK_MENU (widget), event))
return TRUE;
source_device = gdk_event_get_source_device ((GdkEvent *) event);
event_widget = gtk_get_event_widget ((GdkEvent *) event);
menu = GTK_MENU (widget);
/* Don't pass down to menu shell if a non-menuitem part of the menu
* was clicked. The check for the event_widget being a GtkMenuShell
@@ -3392,10 +3365,16 @@ gtk_menu_button_press (GtkWidget *widget,
* the menu or on its border are delivered relative to
* menu_shell->window.
*/
if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
if (GTK_IS_MENU_SHELL (event_widget) &&
pointer_in_menu_window (widget, event->x_root, event->y_root))
return TRUE;
if (GTK_IS_MENU_ITEM (event_widget) &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH &&
GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
!gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
menu->priv->ignore_button_release = TRUE;
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
}
@@ -3414,11 +3393,6 @@ gtk_menu_button_release (GtkWidget *widget,
if (event->type != GDK_BUTTON_RELEASE)
return FALSE;
/* Don't pass down to menu shell for releases over scroll arrows
*/
if (gtk_menu_button_scroll (GTK_MENU (widget), event))
return TRUE;
/* Don't pass down to menu shell if a non-menuitem part of the menu
* was clicked (see comment in button_press()).
*/
@@ -3662,10 +3636,14 @@ gtk_menu_motion_notify (GtkWidget *widget,
GtkMenu *menu;
GtkMenuShell *menu_shell;
GtkWidget *parent;
GdkDevice *source_device;
gboolean need_enter;
if (GTK_IS_MENU (widget))
source_device = gdk_event_get_source_device ((GdkEvent *) event);
if (GTK_IS_MENU (widget) &&
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
{
GtkMenuPrivate *priv = GTK_MENU(widget)->priv;
@@ -3829,90 +3807,17 @@ gtk_menu_scroll_by (GtkMenu *menu,
gtk_menu_scroll_to (menu, offset);
}
static void
gtk_menu_do_timeout_scroll (GtkMenu *menu,
gboolean touchscreen_mode)
{
GtkMenuPrivate *priv = menu->priv;
gboolean upper_visible;
gboolean lower_visible;
upper_visible = priv->upper_arrow_visible;
lower_visible = priv->lower_arrow_visible;
gtk_menu_scroll_by (menu, priv->scroll_step);
if (touchscreen_mode &&
(upper_visible != priv->upper_arrow_visible ||
lower_visible != priv->lower_arrow_visible))
{
/* We are about to hide a scroll arrow while the mouse is pressed,
* this would cause the uncovered menu item to be activated on button
* release. Therefore we need to ignore button release here
*/
GTK_MENU_SHELL (menu)->priv->ignore_enter = TRUE;
priv->ignore_button_release = TRUE;
}
}
static gboolean
gtk_menu_scroll_timeout (gpointer data)
{
GtkMenu *menu;
gboolean touchscreen_mode;
menu = GTK_MENU (data);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
gtk_menu_scroll_by (menu, menu->priv->scroll_step);
return TRUE;
}
static gboolean
gtk_menu_scroll_timeout_initial (gpointer data)
{
GtkMenu *menu;
guint timeout;
gboolean touchscreen_mode;
menu = GTK_MENU (data);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-timeout-repeat", &timeout,
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
gtk_menu_remove_scroll_timeout (menu);
menu->priv->scroll_timeout =
gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout, menu);
return FALSE;
}
static void
gtk_menu_start_scrolling (GtkMenu *menu)
{
guint timeout;
gboolean touchscreen_mode;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-timeout-repeat", &timeout,
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
menu->priv->scroll_timeout =
gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout_initial, menu);
}
static gboolean
gtk_menu_scroll (GtkWidget *widget,
GdkEventScroll *event)
@@ -4036,14 +3941,9 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
gboolean in_arrow;
gboolean scroll_fast = FALSE;
gint top_x, top_y;
gboolean touchscreen_mode;
menu_shell = GTK_MENU_SHELL (menu);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
&top_x, &top_y);
x -= top_x;
@@ -4061,82 +3961,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
in_arrow = TRUE;
}
if (touchscreen_mode)
priv->upper_arrow_prelight = in_arrow;
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
if (priv->upper_arrow_visible && !priv->tearoff_active)
{
if (touchscreen_mode)
scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->upper_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
if (enter && priv->upper_arrow_prelight)
{
if (priv->scroll_timeout == 0)
{
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
priv->upper_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
if (!motion)
{
/* Only do stuff on click. */
gtk_menu_start_scrolling (menu);
arrow_pressed = TRUE;
}
}
else
{
arrow_pressed = TRUE;
}
}
else if (!enter)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? -MENU_SCROLL_STEP2
: -MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else /* !touchscreen_mode */
else if (!enter && !in_arrow && priv->upper_arrow_prelight)
{
scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->upper_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
priv->upper_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? -MENU_SCROLL_STEP2
: -MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else if (!enter && !in_arrow && priv->upper_arrow_prelight)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_stop_scrolling (menu);
}
}
/* gtk_menu_start_scrolling() might have hit the top of the
* menu, so check if the button isn't insensitive before
/* check if the button isn't insensitive before
* changing it to something else.
*/
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
@@ -4171,82 +4033,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
in_arrow = TRUE;
}
if (touchscreen_mode)
priv->lower_arrow_prelight = in_arrow;
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
if (priv->lower_arrow_visible && !priv->tearoff_active)
{
if (touchscreen_mode)
scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->lower_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
if (enter && priv->lower_arrow_prelight)
{
if (priv->scroll_timeout == 0)
{
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
priv->lower_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = MENU_SCROLL_STEP2; /* always fast */
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
if (!motion)
{
/* Only do stuff on click. */
gtk_menu_start_scrolling (menu);
arrow_pressed = TRUE;
}
}
else
{
arrow_pressed = TRUE;
}
}
else if (!enter)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? MENU_SCROLL_STEP2
: MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else /* !touchscreen_mode */
else if (!enter && !in_arrow && priv->lower_arrow_prelight)
{
scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->lower_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
priv->lower_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? MENU_SCROLL_STEP2
: MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else if (!enter && !in_arrow && priv->lower_arrow_prelight)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_stop_scrolling (menu);
}
}
/* gtk_menu_start_scrolling() might have hit the bottom of the
* menu, so check if the button isn't insensitive before
/* check if the button isn't insensitive before
* changing it to something else.
*/
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
@@ -4276,19 +4100,18 @@ gtk_menu_enter_notify (GtkWidget *widget,
{
GtkWidget *menu_item;
GtkWidget *parent;
gboolean touchscreen_mode;
GdkDevice *source_device;
if (event->mode == GDK_CROSSING_GTK_GRAB ||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
event->mode == GDK_CROSSING_STATE_CHANGED)
return TRUE;
g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
menu_item = gtk_get_event_widget ((GdkEvent*) event);
if (GTK_IS_MENU (widget))
if (GTK_IS_MENU (widget) &&
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
{
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
@@ -4297,7 +4120,8 @@ gtk_menu_enter_notify (GtkWidget *widget,
event->x_root, event->y_root, TRUE, TRUE);
}
if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item))
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH &&
GTK_IS_MENU_ITEM (menu_item))
{
GtkWidget *menu = gtk_widget_get_parent (menu_item);
@@ -4354,6 +4178,7 @@ gtk_menu_leave_notify (GtkWidget *widget,
GtkMenu *menu;
GtkMenuItem *menu_item;
GtkWidget *event_widget;
GdkDevice *source_device;
if (event->mode == GDK_CROSSING_GTK_GRAB ||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
@@ -4366,7 +4191,10 @@ gtk_menu_leave_notify (GtkWidget *widget,
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
return TRUE;
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH)
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
event_widget = gtk_get_event_widget ((GdkEvent*) event);
@@ -4401,6 +4229,138 @@ gtk_menu_leave_notify (GtkWidget *widget,
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
}
static gboolean
pointer_on_menu_widget (GtkMenu *menu,
gdouble x_root,
gdouble y_root)
{
GtkMenuPrivate *priv = menu->priv;
GtkAllocation allocation;
gint window_x, window_y;
gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
&window_x, &window_y);
if (x_root >= window_x && x_root < window_x + allocation.width &&
y_root >= window_y && y_root < window_y + allocation.height)
return TRUE;
return FALSE;
}
static GtkCapturedEventFlags
gtk_menu_captured_event (GtkWidget *widget,
GdkEvent *event)
{
GdkDevice *source_device;
GtkCapturedEventFlags flags;
GtkMenuPrivate *priv;
GtkMenu *menu;
menu = GTK_MENU (widget);
priv = menu->priv;
flags = GTK_CAPTURED_EVENT_NONE;
if (!priv->upper_arrow_visible && !priv->lower_arrow_visible)
return flags;
source_device = gdk_event_get_source_device (event);
switch (event->type)
{
case GDK_BUTTON_PRESS:
if (event->button.button == 1 &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH &&
pointer_on_menu_widget (menu, event->button.x_root, event->button.y_root))
{
priv->drag_start_y = event->button.y_root;
priv->initial_drag_offset = priv->scroll_offset;
priv->drag_scroll_started = FALSE;
}
else
priv->drag_start_y = -1;
priv->drag_already_pressed = TRUE;
break;
case GDK_BUTTON_RELEASE:
if (priv->drag_scroll_started)
{
flags = GTK_CAPTURED_EVENT_HANDLED;
priv->drag_scroll_started = FALSE;
priv->drag_start_y = -1;
priv->drag_already_pressed = FALSE;
}
break;
case GDK_MOTION_NOTIFY:
if (event->motion.state & GDK_BUTTON1_MASK &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
{
if (!priv->drag_already_pressed)
{
if (pointer_on_menu_widget (menu,
event->motion.x_root,
event->motion.y_root))
{
priv->drag_start_y = event->motion.y_root;
priv->initial_drag_offset = priv->scroll_offset;
priv->drag_scroll_started = FALSE;
}
else
priv->drag_start_y = -1;
priv->drag_already_pressed = TRUE;
}
if (priv->drag_start_y < 0 &&
!priv->drag_scroll_started)
break;
if (priv->drag_scroll_started)
{
gint offset, view_height;
GtkBorder arrow_border;
gdouble y_diff;
y_diff = event->motion.y_root - priv->drag_start_y;
offset = priv->initial_drag_offset - y_diff;
view_height = gdk_window_get_height (gtk_widget_get_window (widget));
get_arrows_border (menu, &arrow_border);
if (priv->upper_arrow_visible)
view_height -= arrow_border.top;
if (priv->lower_arrow_visible)
view_height -= arrow_border.bottom;
offset = CLAMP (offset, 0, priv->requested_height - view_height);
gtk_menu_scroll_to (menu, offset);
flags = GTK_CAPTURED_EVENT_HANDLED;
}
else if (gtk_drag_check_threshold (widget,
0, priv->drag_start_y,
0, event->motion.y_root))
{
priv->drag_scroll_started = TRUE;
flags = GTK_CAPTURED_EVENT_HANDLED;
gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
}
}
break;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
if (priv->drag_scroll_started)
flags = GTK_CAPTURED_EVENT_HANDLED;
break;
default:
break;
}
return flags;
}
static void
gtk_menu_stop_navigating_submenu (GtkMenu *menu)
{
@@ -4844,19 +4804,10 @@ static void
gtk_menu_stop_scrolling (GtkMenu *menu)
{
GtkMenuPrivate *priv = menu->priv;
gboolean touchscreen_mode;
gtk_menu_remove_scroll_timeout (menu);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (!touchscreen_mode)
{
priv->upper_arrow_prelight = FALSE;
priv->lower_arrow_prelight = FALSE;
}
priv->upper_arrow_prelight = FALSE;
priv->lower_arrow_prelight = FALSE;
}
static void
@@ -5662,7 +5613,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu,
}
}
/**
* gtk_menu_set_monitor:
* @menu: a #GtkMenu
@@ -5739,11 +5689,13 @@ static void
gtk_menu_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
GtkMenu *menu;
GtkWidget *toplevel;
GtkWindowGroup *group;
GtkWidget *grab;
GdkDevice *pointer;
menu = GTK_MENU (widget);
pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
if (!pointer ||
@@ -5760,6 +5712,8 @@ gtk_menu_grab_notify (GtkWidget *widget,
if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab))
gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
menu->priv->drag_scroll_started = FALSE;
}
/**
+12 -5
View File
@@ -1690,13 +1690,20 @@ static void
gtk_real_menu_item_select (GtkMenuItem *menu_item)
{
GtkMenuItemPrivate *priv = menu_item->priv;
gboolean touchscreen_mode;
GdkDevice *source_device = NULL;
GdkEvent *current_event;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
current_event = gtk_get_current_event ();
if (!touchscreen_mode && priv->submenu &&
if (current_event)
{
source_device = gdk_event_get_source_device (current_event);
gdk_event_free (current_event);
}
if ((!source_device ||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCH) &&
priv->submenu &&
(!gtk_widget_get_mapped (priv->submenu) ||
GTK_MENU (priv->submenu)->priv->tearoff_active))
{
+5
View File
@@ -100,6 +100,8 @@ struct _GtkMenuPrivate
guint seen_item_enter : 1;
guint ignore_button_release : 1;
guint no_toggle_size : 1;
guint drag_already_pressed : 1;
guint drag_scroll_started : 1;
/* info used for the table */
guint *heights;
@@ -126,6 +128,9 @@ struct _GtkMenuPrivate
gint navigation_height;
guint navigation_timeout;
gdouble drag_start_y;
gint initial_drag_offset;
};
G_END_DECLS
+4 -32
View File
@@ -1084,13 +1084,11 @@ gtk_menu_shell_enter_notify (GtkWidget *widget,
if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
{
gboolean touchscreen_mode;
GdkDevice *source_device;
g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
if (touchscreen_mode)
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
_gtk_menu_item_popup_submenu (menu_item, TRUE);
}
}
@@ -1612,45 +1610,19 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
GtkMenuShellPrivate *priv = menu_shell->priv;
GtkMenuShell *parent_menu_shell = NULL;
gboolean had_selection;
gboolean touchscreen_mode;
priv->in_unselectable_item = FALSE;
had_selection = priv->active_menu_item != NULL;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (priv->parent_menu_shell)
parent_menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
switch (direction)
{
case GTK_MENU_DIR_PARENT:
if (touchscreen_mode &&
priv->active_menu_item &&
GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu &&
gtk_widget_get_visible (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu))
if (parent_menu_shell)
{
/* if we are on a menu item that has an open submenu but the
* focus is not in that submenu (e.g. because it's empty or
* has only insensitive items), close that submenu instead of
* running into the code below which would close *this* menu.
*/
_gtk_menu_item_popdown_submenu (priv->active_menu_item);
_gtk_menu_shell_update_mnemonics (menu_shell);
}
else if (parent_menu_shell)
{
if (touchscreen_mode)
{
/* close menu when returning from submenu. */
_gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->priv->parent_menu_item);
_gtk_menu_shell_update_mnemonics (parent_menu_shell);
break;
}
if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
gtk_menu_shell_deselect (menu_shell);
+17
View File
@@ -32,6 +32,7 @@
#include "gdk/gdk.h"
#include "gtkprivate.h"
#include "gtkenums.h"
#if !defined G_OS_WIN32 && !(defined GDK_WINDOWING_QUARTZ && defined QUARTZ_RELOCATION)
@@ -159,6 +160,22 @@ _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
return continue_emission;
}
gboolean
_gtk_captured_enum_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy)
{
gboolean continue_emission;
GtkCapturedEventFlags flags;
flags = g_value_get_flags (handler_return);
g_value_set_flags (return_accu, flags);
continue_emission = (flags & GTK_CAPTURED_EVENT_HANDLED) == 0;
return continue_emission;
}
GdkModifierType
_gtk_replace_virtual_modifiers (GdkKeymap *keymap,
GdkModifierType modifiers)
+4
View File
@@ -59,6 +59,10 @@ gboolean _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
gboolean _gtk_captured_enum_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
GdkModifierType _gtk_replace_virtual_modifiers (GdkKeymap *keymap,
GdkModifierType modifiers);
+16 -15
View File
@@ -2005,14 +2005,10 @@ gtk_range_draw (GtkWidget *widget,
GtkStateFlags state = 0;
gint focus_line_width = 0;
gint focus_padding = 0;
gboolean touchscreen;
gboolean draw_trough = TRUE;
GtkStyleContext *context;
context = gtk_widget_get_style_context (widget);
g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen,
NULL);
if (GTK_IS_SCALE (widget) &&
gtk_adjustment_get_upper (priv->adjustment) == gtk_adjustment_get_lower (priv->adjustment))
@@ -2282,7 +2278,7 @@ gtk_range_draw (GtkWidget *widget,
if (!sensitive)
state = GTK_STATE_FLAG_INSENSITIVE;
else if (!touchscreen && priv->mouse_location == MOUSE_SLIDER)
else if (priv->mouse_location == MOUSE_SLIDER)
state = GTK_STATE_FLAG_PRELIGHT;
if (priv->grab_location == MOUSE_SLIDER)
@@ -2314,25 +2310,25 @@ gtk_range_draw (GtkWidget *widget,
draw_stepper (range, STEPPER_A, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
priv->grab_location == MOUSE_STEPPER_A,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_A);
priv->mouse_location == MOUSE_STEPPER_A);
if (priv->has_stepper_b)
draw_stepper (range, STEPPER_B, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
priv->grab_location == MOUSE_STEPPER_B,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_B);
priv->mouse_location == MOUSE_STEPPER_B);
if (priv->has_stepper_c)
draw_stepper (range, STEPPER_C, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
priv->grab_location == MOUSE_STEPPER_C,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_C);
priv->mouse_location == MOUSE_STEPPER_C);
if (priv->has_stepper_d)
draw_stepper (range, STEPPER_D, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
priv->grab_location == MOUSE_STEPPER_D,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_D);
priv->mouse_location == MOUSE_STEPPER_D);
return FALSE;
}
@@ -2533,7 +2529,8 @@ gtk_range_button_press (GtkWidget *widget,
{
GtkRange *range = GTK_RANGE (widget);
GtkRangePrivate *priv = range->priv;
GdkDevice *device;
GdkDevice *device, *source_device;
GdkInputSource source;
if (!gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
@@ -2543,13 +2540,17 @@ gtk_range_button_press (GtkWidget *widget,
return FALSE;
device = gdk_event_get_device ((GdkEvent *) event);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
source = gdk_device_get_source (source_device);
priv->mouse_x = event->x;
priv->mouse_y = event->y;
if (gtk_range_update_mouse_location (range))
gtk_widget_queue_draw (widget);
if (priv->mouse_location == MOUSE_TROUGH &&
if (source != GDK_SOURCE_TOUCH &&
priv->mouse_location == MOUSE_TROUGH &&
event->button == 1)
{
/* button 1 steps by page increment, as with button 2 on a stepper
@@ -2598,17 +2599,17 @@ gtk_range_button_press (GtkWidget *widget,
return TRUE;
}
else if ((priv->mouse_location == MOUSE_TROUGH &&
event->button == 2) ||
(source == GDK_SOURCE_TOUCH || event->button == 2)) ||
priv->mouse_location == MOUSE_SLIDER)
{
gboolean need_value_update = FALSE;
/* Any button can be used to drag the slider, but you can start
* dragging the slider with a trough click using button 2;
* On button 2 press, we warp the slider to mouse position,
* then begin the slider drag.
* On button 2 press and touch devices, we warp the slider to
* mouse position, then begin the slider drag.
*/
if (event->button == 2)
if (event->button == 2 || source == GDK_SOURCE_TOUCH)
{
gdouble slider_low_value, slider_high_value, new_value;
+1013 -13
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -117,6 +117,9 @@ void gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *sc
gint gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window);
void gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
gint height);
void gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
GtkKineticScrollingFlags flags);
GtkKineticScrollingFlags gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window);
gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);
+56 -5
View File
@@ -33,6 +33,7 @@
#include "gtkprivate.h"
#include "gtkcssproviderprivate.h"
#include "gtksymboliccolor.h"
#include "gtkanimationdescription.h"
#include "gtktypebuiltins.h"
#include "gtkversion.h"
@@ -96,9 +97,10 @@
*/
#define DEFAULT_TIMEOUT_INITIAL 200
#define DEFAULT_TIMEOUT_REPEAT 20
#define DEFAULT_TIMEOUT_EXPAND 500
#define DEFAULT_TIMEOUT_INITIAL 200
#define DEFAULT_TIMEOUT_REPEAT 20
#define DEFAULT_TIMEOUT_EXPAND 500
#define DEFAULT_TIMEOUT_PRESS_AND_HOLD 800
typedef struct _GtkSettingsPropertyValue GtkSettingsPropertyValue;
typedef struct _GtkSettingsValuePrivate GtkSettingsValuePrivate;
@@ -206,7 +208,8 @@ enum {
PROP_IM_PREEDIT_STYLE,
PROP_IM_STATUS_STYLE,
PROP_SHELL_SHOWS_APP_MENU,
PROP_SHELL_SHOWS_MENUBAR
PROP_SHELL_SHOWS_MENUBAR,
PROP_PRESS_AND_HOLD_TIMEOUT
};
/* --- prototypes --- */
@@ -697,13 +700,16 @@ gtk_settings_class_init (GtkSettingsClass *class)
* functionality.
*
* Since: 2.10
*
* Deprecated: 3.4. Generally the behavior touchscreen input should be
* performed dynamically based on gdk_event_get_source_device().
*/
result = settings_install_property_parser (class,
g_param_spec_boolean ("gtk-touchscreen-mode",
P_("Enable Touchscreen Mode"),
P_("When TRUE, there are no motion notify events delivered on this screen"),
FALSE,
GTK_PARAM_READWRITE),
GTK_PARAM_READWRITE | G_PARAM_DEPRECATED),
NULL);
g_assert (result == PROP_TOUCHSCREEN_MODE);
@@ -1346,6 +1352,24 @@ gtk_settings_class_init (GtkSettingsClass *class)
NULL);
g_assert (result == PROP_SHELL_SHOWS_MENUBAR);
/**
* GtkSettings:gtk-press-and-hold-timeout:
*
* The amount of time, in milliseconds, a button has to be pressed
* before the press-and-hold signal with the trigger action is emitted.
*
* Since: 3.2
*/
result = settings_install_property_parser (class,
g_param_spec_int ("gtk-press-and-hold-timeout",
P_("Press And Hold Timeout"),
P_("Timeout before press-and-hold action activates"),
0, G_MAXINT,
DEFAULT_TIMEOUT_PRESS_AND_HOLD,
GTK_PARAM_READWRITE),
NULL);
g_assert (result == PROP_PRESS_AND_HOLD_TIMEOUT);
g_type_class_add_private (class, sizeof (GtkSettingsPrivate));
}
@@ -1441,6 +1465,33 @@ gtk_settings_get_style (GtkStyleProvider *provider,
settings_ensure_style (settings);
/* Set animation for press and hold */
if (gtk_widget_path_iter_has_class (path, 0, GTK_STYLE_CLASS_PRESS_AND_HOLD))
{
GtkAnimationDescription *anim_desc;
GtkStyleProperties *copy;
gint duration;
copy = gtk_style_properties_new ();
gtk_style_properties_merge (copy, settings->priv->style, TRUE);
g_object_get (settings,
"gtk-press-and-hold-timeout", &duration,
NULL);
anim_desc = _gtk_animation_description_new (duration,
GTK_TIMELINE_PROGRESS_LINEAR,
FALSE);
gtk_style_properties_set (copy,
GTK_STATE_FLAG_ACTIVE,
"transition", anim_desc,
NULL);
_gtk_animation_description_unref (anim_desc);
return copy;
}
return g_object_ref (settings->priv->style);
}
+7
View File
@@ -647,6 +647,13 @@ struct _GtkStyleContextClass
*/
#define GTK_STYLE_CLASS_RIGHT "right"
/**
* GTK_STYLE_CLASS_PRESS_AND_HOLD:
*
* A CSS class for the press and hold activity indicator.
*/
#define GTK_STYLE_CLASS_PRESS_AND_HOLD "press-and-hold"
/* Predefined set of widget regions */
/**
+54 -26
View File
@@ -378,6 +378,11 @@ static void gtk_text_view_drag_data_received (GtkWidget *widget,
guint time);
static gboolean gtk_text_view_popup_menu (GtkWidget *widget);
static gboolean gtk_text_view_press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y);
static void gtk_text_view_move_cursor (GtkTextView *text_view,
GtkMovementStep step,
@@ -463,7 +468,9 @@ static void gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
gint y);
static void gtk_text_view_do_popup (GtkTextView *text_view,
GdkEventButton *event);
GdkDevice *device,
guint32 _time,
guint button);
static void cancel_pending_scroll (GtkTextView *text_view);
static void gtk_text_view_queue_scroll (GtkTextView *text_view,
@@ -631,7 +638,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
widget_class->drag_data_received = gtk_text_view_drag_data_received;
widget_class->popup_menu = gtk_text_view_popup_menu;
widget_class->press_and_hold = gtk_text_view_press_and_hold;
container_class->add = gtk_text_view_add;
container_class->remove = gtk_text_view_remove;
container_class->forall = gtk_text_view_forall;
@@ -4268,8 +4276,15 @@ gtk_text_view_grab_notify (GtkWidget *widget,
if (priv->grab_device &&
gtk_widget_device_is_shadowed (widget, priv->grab_device))
{
if (priv->drag_start_x >= 0)
{
priv->drag_start_x = -1;
priv->drag_start_y = -1;
}
gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget));
gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
priv->grab_device = NULL;
}
}
@@ -4547,7 +4562,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
{
gtk_text_view_do_popup (text_view, event);
gtk_text_view_do_popup (text_view, event->device,
event->time, event->button);
return TRUE;
}
else if (event->button == 1)
@@ -4570,6 +4586,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
gtk_widget_get_modifier_mask (widget,
GDK_MODIFIER_INTENT_EXTEND_SELECTION)))
{
priv->grab_device = event->device;
priv->drag_start_x = event->x;
priv->drag_start_y = event->y;
priv->pending_place_cursor_button = event->button;
@@ -8310,16 +8327,18 @@ popup_targets_received (GtkClipboard *clipboard,
signals[POPULATE_POPUP],
0,
priv->popup_menu);
if (info->device)
gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
info->device, NULL, NULL, NULL, NULL, NULL,
info->button, info->time);
if (gdk_device_get_source (info->device) != GDK_SOURCE_KEYBOARD)
gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
info->device, NULL, NULL, NULL, NULL, NULL,
info->button, info->time);
else
{
gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
popup_position_func, text_view,
0, gtk_get_current_event_time ());
gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
info->device, NULL, NULL,
popup_position_func,
text_view, NULL,
0, info->time);
gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
}
}
@@ -8330,7 +8349,9 @@ popup_targets_received (GtkClipboard *clipboard,
static void
gtk_text_view_do_popup (GtkTextView *text_view,
GdkEventButton *event)
GdkDevice *device,
guint32 _time,
guint button)
{
PopupInfo *info = g_new (PopupInfo, 1);
@@ -8339,19 +8360,9 @@ gtk_text_view_do_popup (GtkTextView *text_view,
* we get them, then we actually pop up the menu.
*/
info->text_view = g_object_ref (text_view);
if (event)
{
info->button = event->button;
info->time = event->time;
info->device = event->device;
}
else
{
info->button = 0;
info->time = gtk_get_current_event_time ();
info->device = NULL;
}
info->button = button;
info->time = _time;
info->device = device;
gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view),
GDK_SELECTION_CLIPBOARD),
@@ -8363,7 +8374,24 @@ gtk_text_view_do_popup (GtkTextView *text_view,
static gboolean
gtk_text_view_popup_menu (GtkWidget *widget)
{
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget),
gtk_get_current_event_device (),
gtk_get_current_event_time (),
0);
return TRUE;
}
static gboolean
gtk_text_view_press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y)
{
if (action == GTK_PRESS_AND_HOLD_TRIGGER)
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), device,
GDK_CURRENT_TIME, 1);
return TRUE;
}
+61
View File
@@ -2596,6 +2596,63 @@ render_spinner (GtkThemingEngine *engine,
cairo_restore (cr);
}
static void
render_press_and_hold (GtkThemingEngine *engine,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
gdouble progress, radius, border_width;
GdkRGBA color, bg_color;
GtkStateFlags flags;
GtkBorder border;
cairo_save (cr);
if (!gtk_theming_engine_state_is_running (engine,
GTK_STATE_FLAG_ACTIVE,
&progress))
progress = 0;
flags = gtk_theming_engine_get_state (engine);
gtk_theming_engine_get_background_color (engine, flags, &bg_color);
gtk_theming_engine_get_color (engine, flags, &color);
gtk_theming_engine_get_border (engine, flags, &border);
border_width = (gdouble) MAX (MAX (border.top, border.bottom),
MAX (border.left, border.right));
radius = MIN (width, height) / 2;
if (border_width == 0 ||
border_width >= radius - border_width)
border_width = MAX (1, radius / 4);
cairo_set_line_width (cr, border_width);
radius -= border_width;
/* Arcs start from the negative Y axis */
cairo_arc (cr,
width / 2, height / 2,
radius,
- G_PI_2, - G_PI_2 + (2 * G_PI));
gdk_cairo_set_source_rgba (cr, &bg_color);
cairo_stroke (cr);
cairo_arc (cr,
width / 2, height / 2,
radius,
- G_PI_2,
- G_PI_2 + (2 * G_PI * progress));
gdk_cairo_set_source_rgba (cr, &color);
cairo_stroke (cr);
cairo_restore (cr);
}
static void
gtk_theming_engine_render_activity (GtkThemingEngine *engine,
cairo_t *cr,
@@ -2608,6 +2665,10 @@ gtk_theming_engine_render_activity (GtkThemingEngine *engine,
{
render_spinner (engine, cr, x, y, width, height);
}
else if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_PRESS_AND_HOLD))
{
render_press_and_hold (engine, cr, x, y, width, height);
}
else
{
gtk_theming_engine_render_background (engine, cr, x, y, width, height);
+22 -3
View File
@@ -35,6 +35,7 @@ struct GtkTimelinePriv
guint source_id;
GTimer *timer;
gdouble elapsed_time;
gdouble progress;
gdouble last_progress;
@@ -309,16 +310,15 @@ gtk_timeline_run_frame (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
gdouble delta_progress, progress;
guint elapsed_time;
priv = timeline->priv;
elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
g_timer_start (priv->timer);
if (priv->animations_enabled)
{
delta_progress = (gdouble) elapsed_time / priv->duration;
delta_progress = (gdouble) priv->elapsed_time / priv->duration;
progress = priv->last_progress;
if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
@@ -509,6 +509,25 @@ _gtk_timeline_is_running (GtkTimeline *timeline)
return (priv->source_id != 0);
}
/**
* gtk_timeline_get_elapsed_time:
* @timeline: A #GtkTimeline
*
* Returns the elapsed time since the last GtkTimeline::frame signal
*
* Return Value: elapsed time in milliseconds since the last frame
**/
guint
_gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
priv = timeline->priv;
return priv->elapsed_time;
}
/**
* gtk_timeline_get_fps:
* @timeline: A #GtkTimeline
+1
View File
@@ -84,6 +84,7 @@ void _gtk_timeline_pause (GtkTimeline
void _gtk_timeline_rewind (GtkTimeline *timeline);
gboolean _gtk_timeline_is_running (GtkTimeline *timeline);
guint _gtk_timeline_get_elapsed_time (GtkTimeline *timeline);
guint _gtk_timeline_get_fps (GtkTimeline *timeline);
void _gtk_timeline_set_fps (GtkTimeline *timeline,
+2 -6
View File
@@ -658,13 +658,9 @@ gtk_toggle_button_update_state (GtkButton *button)
{
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButtonPrivate *priv = toggle_button->priv;
gboolean depressed, touchscreen;
gboolean depressed;
GtkStateFlags new_state = 0;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (button)),
"gtk-touchscreen-mode", &touchscreen,
NULL);
new_state = gtk_widget_get_state_flags (GTK_WIDGET (button)) &
~(GTK_STATE_FLAG_INCONSISTENT |
GTK_STATE_FLAG_PRELIGHT |
@@ -680,7 +676,7 @@ gtk_toggle_button_update_state (GtkButton *button)
else
depressed = priv->active;
if (!touchscreen && button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
if (button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
new_state |= GTK_STATE_FLAG_PRELIGHT;
if (depressed)
+17 -5
View File
@@ -1537,22 +1537,34 @@ _gtk_tooltip_hide (GtkWidget *widget)
}
static gboolean
tooltips_enabled (GdkWindow *window)
tooltips_enabled (GdkEvent *event)
{
GdkDevice *source_device;
GdkInputSource source;
GdkWindow *window;
gboolean enabled;
gboolean touchscreen;
GdkScreen *screen;
GtkSettings *settings;
window = event->any.window;
source_device = gdk_event_get_source_device (event);
if (!source_device)
return FALSE;
source = gdk_device_get_source (source_device);
screen = gdk_window_get_screen (window);
settings = gtk_settings_get_for_screen (screen);
g_object_get (settings,
"gtk-touchscreen-mode", &touchscreen,
"gtk-enable-tooltips", &enabled,
NULL);
return (!touchscreen && enabled);
if (enabled &&
source != GDK_SOURCE_TOUCH)
return TRUE;
return FALSE;
}
void
@@ -1564,7 +1576,7 @@ _gtk_tooltip_handle_event (GdkEvent *event)
GdkDisplay *display;
GtkTooltip *current_tooltip;
if (!tooltips_enabled (event->any.window))
if (!tooltips_enabled (event))
return;
/* Returns coordinates relative to has_tooltip_widget's allocation. */
+624
View File
@@ -398,6 +398,8 @@ struct _GtkWidgetPrivate
/* The widget's parent */
GtkWidget *parent;
GSList *captured_events;
#ifdef G_ENABLE_DEBUG
/* Number of gtk_widget_push_verify_invariants () */
guint verifying_invariants_count;
@@ -479,6 +481,8 @@ enum {
QUERY_TOOLTIP,
DRAG_FAILED,
STYLE_UPDATED,
CAPTURED_EVENT,
PRESS_AND_HOLD,
LAST_SIGNAL
};
@@ -534,6 +538,24 @@ struct _GtkStateData
guint operation : 2;
};
typedef struct
{
/* The widget */
GtkWidget *widget;
/* animation */
GtkWidget *popup;
guint delay_animation_id;
guint size;
gint start_x;
gint start_y;
gint current_x;
gint current_y;
GdkDevice *device;
} PressAndHoldData;
/* --- prototypes --- */
static void gtk_widget_base_class_init (gpointer g_class);
static void gtk_widget_class_init (GtkWidgetClass *klass);
@@ -700,6 +722,10 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
GdkDevice *device,
gboolean recurse,
gboolean enabled);
static gboolean event_window_is_still_viewable (GdkEvent *event);
static gboolean gtk_widget_press_and_hold_cancel (GtkWidget *widget);
/* --- variables --- */
static gpointer gtk_widget_parent_class = NULL;
@@ -728,6 +754,7 @@ static GQuark quark_visual = 0;
static GQuark quark_modifier_style = 0;
static GQuark quark_enabled_devices = 0;
static GQuark quark_size_groups = 0;
static GQuark quark_press_and_hold = 0;
GParamSpecPool *_gtk_widget_child_property_pool = NULL;
GObjectNotifyContext *_gtk_widget_child_property_notify_context = NULL;
@@ -852,6 +879,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
quark_modifier_style = g_quark_from_static_string ("gtk-widget-modifier-style");
quark_enabled_devices = g_quark_from_static_string ("gtk-widget-enabled-devices");
quark_size_groups = g_quark_from_static_string ("gtk-widget-size-groups");
quark_press_and_hold = g_quark_from_static_string ("gtk-widget-press-and-hold");
style_property_spec_pool = g_param_spec_pool_new (FALSE);
_gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
@@ -930,6 +958,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->grab_broken_event = NULL;
klass->query_tooltip = gtk_widget_real_query_tooltip;
klass->style_updated = gtk_widget_real_style_updated;
klass->press_and_hold = NULL;
klass->show_help = gtk_widget_real_show_help;
@@ -1793,6 +1822,9 @@ gtk_widget_class_init (GtkWidgetClass *klass)
* #GtkWidget::key-press-event) and finally a generic
* #GtkWidget::event-after signal.
*
* An event can be captured before ::event signal is emitted by connecting to
* ::captured-event event signal.
*
* Returns: %TRUE to stop other handlers from being invoked for the event
* and to cancel the emission of the second specific ::event signal.
* %FALSE to propagate the event further and to allow the emission of
@@ -1829,6 +1861,53 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_NONE, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GtkWidget::captured-event:
* @widget: the object which received the signal.
* @event: the #GdkEvent which triggered this signal
*
* The ::captured-event signal is emitted before the ::event signal to
* allow capturing an event before the specialized events are emitted.
* The event is propagated starting from the top-level container to
* the widget that received the event going down the hierarchy.
*
* This signal returns a #GtkCapturedEventFlags with the handling
* status of the event, if %GTK_CAPTURED_EVENT_HANDLED is enabled,
* the event will be considered to be handled, and thus not propagated
* further.
*
* If, additionally, %GTK_CAPTURED_EVENT_STORE is enabled, the event will
* be stored so it can possibly be re-sent at a later stage. See
* gtk_widget_release_captured_events().
*
* If no flags are enabled, the captured event will be propagated even
* further, eventually triggering the emission of the #GtkWidget::event
* signal on the widget that received the event if no parent widgets
* captured it.
*
* <note><para>Enabling %GTK_CAPTURED_EVENT_STORE without
* %GTK_CAPTURED_EVENT_HANDLED is not allowed to avoid doubly
* event emission.</para></note>
*
* <warning><para>%GTK_CAPTURED_EVENT_STORE will not keep any track of
* event parity (eg. ensuring that button/key presses and releases
* are paired, or focus/crossing events) nor consistency, so use with
* discretion.</para></warning>
*
* Returns: a #GtkCapturedEventFlags specifying what to do with the event.
*
* Since: 3.4
*/
widget_signals[CAPTURED_EVENT] =
g_signal_new (I_("captured-event"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, captured_event),
_gtk_captured_enum_accumulator, NULL,
_gtk_marshal_FLAGS__BOXED,
GTK_TYPE_CAPTURED_EVENT_FLAGS, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GtkWidget::button-press-event:
* @widget: the object which received the signal.
@@ -3000,6 +3079,66 @@ gtk_widget_class_init (GtkWidgetClass *klass)
_gtk_marshal_BOOLEAN__UINT,
G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
/**
* GtkWidget::press-and-hold:
* @widget: the object which received the signal
* @action: a #GtkPressAndHoldAction specifying the action
* @x: if the action is not %GTK_PRESS_AND_HOLD_CANCEL, the x coordinate
* of the cursor position where the request has been emitted, relative
* to widget->window, otherwise undefined
* @y: if the action is not %GTK_PRESS_AND_HOLD_CANCEL, the y coordinate
* of the cursor position where the request has been emitted, relative
* to widget->window, otherwise undefined
*
* Connect to this signal and correctly handle all of its actions if you
* want your widget to support the press-n-hold operation. The
* press-and-hold operation is defined as keeping a mouse button pressed
* for a given amount of time (specified in the "press-and-hold-timeout"
* GtkSetting); during this time the mouse is only allowed to move a little
* bit (not past the drag threshold), else the press-and-hold operation will
* be terminated.
*
* From the above passage we can distill three actions for which this
* signal will be emitted: query, emitted when the mouse button goes
* down; trigger, emitted if the mouse button has been kept down for the
* specified amount of time and movements did not pass the drag threshold;
* and cancel, emitted when the press-and-hold operation has been terminated
* before the trigger action has been emitted.
*
* For query, @action will be set to %GTK_PRESS_AND_HOLD_QUERY, @x and @y
* will be set to the cursor position.
* A return value of %FALSE means no press-and-hold action should occur
* for these coordinates on the given widget, when %TRUE is returned
* a trigger action may be emitted later on.
*
* The trigger action is emitted by setting @action to be
* %GTK_PRESS_AND_HOLD_TRIGGER, the @x and @y coordinates are set to the
* cursor's current location (this includes any movements made between
* the original query and this trigger). In this case the return value
* is ignored.
*
* When @action is %GTK_WIDGET_PRESS_AND_HOLD_CANCEL, @x and @y are both
* undefined. The return value is ignored too as
* this action is only there for informational purposes.
*
* Returns: a boolean indicating how to proceed based on the value of
* @action, as described above.
*
* Since: 3.2
*/
widget_signals[PRESS_AND_HOLD] =
g_signal_new (I_("press-and-hold"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, press_and_hold),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__OBJECT_ENUM_INT_INT,
G_TYPE_BOOLEAN, 4,
GDK_TYPE_DEVICE,
GTK_TYPE_PRESS_AND_HOLD_ACTION,
G_TYPE_INT,
G_TYPE_INT);
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_F10, GDK_SHIFT_MASK,
"popup-menu", 0);
@@ -4422,6 +4561,12 @@ gtk_widget_realize (GtkWidget *widget)
_gtk_widget_enable_device_events (widget);
gtk_widget_update_devices_mask (widget, TRUE);
/* Enable button motion events for press and hold */
if (!gtk_widget_get_has_window (widget))
gdk_window_set_events (priv->window,
gdk_window_get_events (priv->window) | GDK_BUTTON_MOTION_MASK);
gtk_widget_add_events (widget, GDK_BUTTON_MOTION_MASK);
gtk_widget_pop_verify_invariants (widget);
}
}
@@ -5863,6 +6008,68 @@ gtk_widget_event (GtkWidget *widget,
return gtk_widget_event_internal (widget, event);
}
gboolean
_gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event)
{
GtkCapturedEventFlags flags;
GtkWidgetPrivate *priv;
gboolean return_val;
g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
priv = widget->priv;
if (event->type == GDK_EXPOSE)
{
g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
"the same effect, call gdk_window_invalidate_rect/region(), "
"followed by gdk_window_process_updates().");
return TRUE;
}
if (!event_window_is_still_viewable (event))
return TRUE;
g_object_ref (widget);
g_signal_emit (widget, widget_signals[CAPTURED_EVENT], 0, event, &flags);
/* Only store events that have been handled, so we don't end up
* sending twice the same event.
*/
if (flags & GTK_CAPTURED_EVENT_STORE)
{
if ((flags & GTK_CAPTURED_EVENT_HANDLED) != 0)
priv->captured_events = g_slist_prepend (priv->captured_events,
gdk_event_copy (event));
else
g_warning ("Captured events can only be stored if they are claimed "
"to be handled by the capturing widget. The use of "
"GTK_CAPTURED_EVENT_HANDLED is mandatory if using "
"GTK_CAPTURED_EVENT_STORE.");
}
/* The widget that was originally to receive the event
* handles motion hints, but the capturing widget might
* not, so ensure we get further motion events.
*/
if ((flags & GTK_CAPTURED_EVENT_HANDLED) != 0 &&
event->type == GDK_MOTION_NOTIFY &&
event->motion.is_hint &&
(gdk_window_get_events (event->any.window) &
GDK_POINTER_MOTION_HINT_MASK) != 0)
gdk_event_request_motions (&event->motion);
return_val = (flags & GTK_CAPTURED_EVENT_HANDLED) != 0 ||
!WIDGET_REALIZED_FOR_EVENT (widget, event);
g_object_unref (widget);
return return_val;
}
/* Returns TRUE if a translation should be done */
gboolean
_gtk_widget_get_translation_to_window (GtkWidget *widget,
@@ -6400,6 +6607,14 @@ void
_gtk_widget_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
PressAndHoldData *data;
data = g_object_get_qdata (G_OBJECT (widget), quark_press_and_hold);
if (data && data->device &&
gtk_widget_device_is_shadowed (widget, data->device))
gtk_widget_press_and_hold_cancel (widget);
g_signal_emit (widget, widget_signals[GRAB_NOTIFY], 0, was_grabbed);
}
@@ -6714,6 +6929,344 @@ gtk_widget_has_focus (GtkWidget *widget)
return widget->priv->has_focus;
}
/* --- Press and hold --- */
static inline PressAndHoldData *
gtk_widget_peek_press_and_hold_data (GtkWidget *widget)
{
return g_object_get_qdata (G_OBJECT (widget), quark_press_and_hold);
}
static void
press_and_hold_data_free (PressAndHoldData *data)
{
if (data->popup)
gtk_widget_destroy (data->popup);
if (data->delay_animation_id)
g_source_remove (data->delay_animation_id);
g_slice_free (PressAndHoldData, data);
}
static inline void
gtk_widget_set_press_and_hold_data (GtkWidget *widget,
PressAndHoldData *data)
{
g_object_set_qdata_full (G_OBJECT (widget),
quark_press_and_hold,
data,
(GDestroyNotify) press_and_hold_data_free);
}
static inline PressAndHoldData *
gtk_widget_get_press_and_hold_data (GtkWidget *widget)
{
PressAndHoldData *data;
data = gtk_widget_peek_press_and_hold_data (widget);
if (!data)
{
data = g_slice_new0 (PressAndHoldData);
data->widget = widget;
gtk_widget_set_press_and_hold_data (widget, data);
}
return data;
}
static gboolean
gtk_widget_press_and_hold_cancel (GtkWidget *widget)
{
PressAndHoldData *data;
gboolean return_value;
data = gtk_widget_peek_press_and_hold_data (widget);
g_assert (data != NULL);
g_signal_emit (widget, widget_signals[PRESS_AND_HOLD],
0,
data->device,
GTK_PRESS_AND_HOLD_CANCEL,
-1, -1,
&return_value);
gtk_widget_set_press_and_hold_data (widget, NULL);
return FALSE;
}
gboolean
_gtk_widget_press_and_hold_check_cancel (GtkWidget *widget,
GdkEventButton *event)
{
PressAndHoldData *data;
if (event->type != GDK_BUTTON_RELEASE)
return FALSE;
data = gtk_widget_peek_press_and_hold_data (widget);
if (data &&
data->device == gdk_event_get_device ((GdkEvent *) event))
{
gtk_widget_press_and_hold_cancel (widget);
return TRUE;
}
return FALSE;
}
gboolean
_gtk_widget_press_and_hold_check_threshold (GtkWidget *widget,
GdkEventMotion *event)
{
PressAndHoldData *data;
GdkDevice *device;
if (event->type != GDK_MOTION_NOTIFY)
return FALSE;
data = gtk_widget_peek_press_and_hold_data (widget);
if (!data)
return FALSE;
device = gdk_event_get_device ((GdkEvent *) event);
if (data->device != device)
return FALSE;
_gtk_widget_find_at_coords (event->window, event->x, event->y,
&data->current_x, &data->current_y);
/* Stop press-and-hold if we dragged too far from the starting point */
if (gtk_drag_check_threshold (widget, data->start_x, data->start_y,
data->current_x, data->current_y))
{
gtk_widget_press_and_hold_cancel (widget);
return TRUE;
}
if (data->popup)
gtk_window_move (GTK_WINDOW (data->popup),
event->x_root - data->size / 2,
event->y_root - data->size / 2);
return FALSE;
}
static gboolean
gtk_widget_press_and_hold_timeout (gpointer user_data)
{
gboolean return_value;
GtkWidget *widget = GTK_WIDGET (user_data);
PressAndHoldData *data;
data = gtk_widget_peek_press_and_hold_data (widget);
g_assert (data != NULL);
/* Done, clean up and emit the trigger signal */
g_signal_emit (widget, widget_signals[PRESS_AND_HOLD],
0,
data->device,
GTK_PRESS_AND_HOLD_TRIGGER,
data->current_x, data->current_y,
&return_value);
gtk_widget_set_press_and_hold_data (widget, NULL);
return FALSE;
}
static gboolean
press_and_hold_animation_draw (GtkWidget *widget,
cairo_t *cr,
gpointer user_data)
{
PressAndHoldData *data;
GtkStyleContext *context;
GtkStateFlags state;
gint width, height;
data = gtk_widget_peek_press_and_hold_data (GTK_WIDGET (user_data));
g_assert (data != NULL);
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
gtk_style_context_set_state (context, state);
if (!gtk_style_context_state_is_running (context, GTK_STATE_FLAG_ACTIVE, NULL))
{
/* The animation just finished, so hide the widget
* and finish the press and hold operation.
*/
gdk_threads_add_idle (gtk_widget_press_and_hold_timeout,
user_data);
gtk_widget_hide (widget);
return FALSE;
}
if (!gtk_widget_is_composited (widget))
{
cairo_t *mask_cr;
cairo_region_t *region;
cairo_surface_t *mask;
mask = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
mask_cr = cairo_create (mask);
gtk_render_activity (context, mask_cr, 0, 0, width, height);
cairo_destroy (mask_cr);
region = gdk_cairo_region_create_from_surface (mask);
gdk_window_shape_combine_region (gtk_widget_get_window (widget), region, 0, 0);
cairo_region_destroy (region);
cairo_surface_destroy (mask);
}
else
{
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_restore (cr);
}
gtk_render_activity (context, cr, 0, 0, width, height);
return FALSE;
}
static gboolean
gtk_widget_press_and_hold_begin_animation_timeout (gpointer user_data)
{
GtkWidget *widget = GTK_WIDGET (user_data);
PressAndHoldData *data;
gint x, y;
data = gtk_widget_peek_press_and_hold_data (widget);
g_assert (data != NULL);
gdk_window_get_device_position (gdk_screen_get_root_window (gtk_widget_get_screen (widget)),
data->device, &x, &y, NULL);
gtk_window_move (GTK_WINDOW (data->popup),
x - data->size / 2,
y - data->size / 2);
gtk_widget_show (data->popup);
gtk_widget_set_state_flags (GTK_WIDGET (data->popup),
GTK_STATE_FLAG_ACTIVE, FALSE);
return FALSE;
}
static gboolean
gtk_widget_press_and_hold_query (GtkWidget *widget,
GdkDevice *device,
gint x,
gint y)
{
gboolean return_value = FALSE;
g_signal_emit (widget, widget_signals[PRESS_AND_HOLD],
0,
device,
GTK_PRESS_AND_HOLD_QUERY,
x, y,
&return_value);
return return_value;
}
gboolean
_gtk_widget_press_and_hold_check_start (GtkWidget *widget,
GdkEventButton *event)
{
PressAndHoldData *data = gtk_widget_peek_press_and_hold_data (widget);
if (event->type != GDK_BUTTON_PRESS)
return FALSE;
/* Press and hold already in process? */
if (data)
return FALSE;
data = gtk_widget_get_press_and_hold_data (widget);
if (gtk_widget_press_and_hold_query (widget, data->device,
event->x, event->y))
{
gint timeout, begin_ani_timeout;
GdkScreen *screen;
GdkVisual *visual;
GtkStyleContext *context;
cairo_region_t *region;
GdkDevice *source_device;
GdkInputSource source;
_gtk_widget_find_at_coords (event->window,
event->x, event->y,
&data->start_x, &data->start_y);
data->current_x = data->start_x;
data->current_y = data->start_y;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
"gtk-press-and-hold-timeout", &timeout,
"gtk-timeout-initial", &begin_ani_timeout,
NULL);
screen = gtk_widget_get_screen (widget);
visual = gdk_screen_get_rgba_visual (screen);
data->popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_screen (GTK_WINDOW (data->popup), screen);
if (visual)
gtk_widget_set_visual (data->popup, visual);
gtk_widget_set_app_paintable (data->popup, TRUE);
gtk_widget_realize (data->popup);
context = gtk_widget_get_style_context (data->popup);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_PRESS_AND_HOLD);
g_signal_connect (data->popup, "draw",
G_CALLBACK (press_and_hold_animation_draw),
widget);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
source = gdk_device_get_source (source_device);
if (source == GDK_SOURCE_TOUCH)
{
/* Have an indicator with 2.5cm of diameter */
data->size = (25 * gdk_screen_get_width (screen)) /
gdk_screen_get_width_mm (screen);
}
else
data->size = gdk_display_get_default_cursor_size (gtk_widget_get_display (widget));
gtk_window_resize (GTK_WINDOW (data->popup), data->size, data->size);
region = cairo_region_create ();
gdk_window_input_shape_combine_region (gtk_widget_get_window (data->popup), region, 0, 0);
cairo_region_destroy (region);
/* delay loading the animation by the double click timeout */
data->delay_animation_id =
gdk_threads_add_timeout (begin_ani_timeout,
gtk_widget_press_and_hold_begin_animation_timeout,
widget);
data->device = gdk_event_get_device ((GdkEvent *) event);
}
return FALSE;
}
/**
* gtk_widget_has_visible_focus:
* @widget: a #GtkWidget
@@ -10167,6 +10720,12 @@ gtk_widget_finalize (GObject *object)
_gtk_widget_free_cached_sizes (widget);
if (priv->captured_events)
{
g_slist_foreach (priv->captured_events, (GFunc) gdk_event_free, NULL);
g_slist_free (priv->captured_events);
}
if (g_object_is_floating (object))
g_warning ("A floating object was finalized. This means that someone\n"
"called g_object_unref() on an object that had only a floating\n"
@@ -13914,3 +14473,68 @@ _gtk_widget_set_style (GtkWidget *widget,
{
widget->priv->style = style;
}
/**
* gtk_widget_release_captured_events:
* @widget: the #GtkWidget holding the events
* @emit: #TRUE if the events must be emitted on the targed widget
*
* Releases the events that a widget has captured and stored
* in the #GtkWidget::captured-event signal. if @emit is #TRUE,
* the events will be emitted on the target widget (the widget
* that would receive the event if no signal capturing happened)
*
* Since: 3.4
**/
void
gtk_widget_release_captured_events (GtkWidget *widget,
gboolean emit)
{
GtkWidgetPrivate *priv;
GSList *l;
g_return_if_fail (GTK_IS_WIDGET (widget));
priv = widget->priv;
if (emit)
{
priv->captured_events = g_slist_reverse (priv->captured_events);
for (l = priv->captured_events; l; l = l->next)
{
GtkWidget *event_widget;
GdkEvent *event = l->data;
event_widget = gtk_get_event_widget (event);
switch (event->type)
{
case GDK_PROPERTY_NOTIFY:
case GDK_FOCUS_CHANGE:
case GDK_CONFIGURE:
case GDK_MAP:
case GDK_UNMAP:
case GDK_SELECTION_CLEAR:
case GDK_SELECTION_REQUEST:
case GDK_SELECTION_NOTIFY:
case GDK_CLIENT_EVENT:
case GDK_VISIBILITY_NOTIFY:
case GDK_WINDOW_STATE:
case GDK_GRAB_BROKEN:
case GDK_DAMAGE:
/* These events are capturable, but don't bubble up */
gtk_widget_event (event_widget, event);
break;
default:
/* All other capturable events do bubble up */
gtk_propagate_event (event_widget, event);
break;
}
}
}
g_slist_foreach (priv->captured_events, (GFunc) gdk_event_free, NULL);
g_slist_free (priv->captured_events);
priv->captured_events = NULL;
}
+17 -2
View File
@@ -49,6 +49,13 @@ typedef enum
GTK_WIDGET_HELP_WHATS_THIS
} GtkWidgetHelpType;
typedef enum
{
GTK_PRESS_AND_HOLD_QUERY,
GTK_PRESS_AND_HOLD_TRIGGER,
GTK_PRESS_AND_HOLD_CANCEL
} GtkPressAndHoldAction;
/* Macro for casting a pointer to a GtkWidget or GtkWidgetClass pointer.
* Macros for testing whether `widget' or `klass' are of type GTK_TYPE_WIDGET.
*/
@@ -425,6 +432,14 @@ struct _GtkWidgetClass
void (* style_updated) (GtkWidget *widget);
GtkCapturedEventFlags (* captured_event) (GtkWidget *widget,
GdkEvent *event);
gboolean (* press_and_hold) (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y);
/*< private >*/
GtkWidgetClassPrivate *priv;
@@ -435,8 +450,6 @@ struct _GtkWidgetClass
void (*_gtk_reserved4) (void);
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
void (*_gtk_reserved8) (void);
};
struct _GtkWidgetAuxInfo
@@ -892,6 +905,8 @@ GtkWidgetPath * gtk_widget_get_path (GtkWidget *widget);
GdkModifierType gtk_widget_get_modifier_mask (GtkWidget *widget,
GdkModifierIntent intent);
void gtk_widget_release_captured_events (GtkWidget *widget,
gboolean emit);
G_END_DECLS
+10
View File
@@ -163,6 +163,16 @@ void _gtk_widget_set_style (GtkWidget *widget,
GtkStyle *style);
gboolean _gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event);
gboolean _gtk_widget_press_and_hold_check_start (GtkWidget *widget,
GdkEventButton *event);
gboolean _gtk_widget_press_and_hold_check_cancel (GtkWidget *widget,
GdkEventButton *event);
gboolean _gtk_widget_press_and_hold_check_threshold (GtkWidget *widget,
GdkEventMotion *event);
G_END_DECLS
#endif /* __GTK_WIDGET_PRIVATE_H__ */
+10
View File
@@ -63,6 +63,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testicontheme \
testimage \
testinput \
testkineticscrolling \
testlockbutton \
testmenubars \
testmountoperation \
@@ -76,6 +77,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testorientable \
testoverlay \
testprint \
testpressandhold \
testrecentchooser \
testrecentchoosermenu \
testrichtext \
@@ -179,6 +181,7 @@ testgrid_DEPENDENCIES = $(TEST_DEPS)
testgtk_DEPENDENCIES = $(TEST_DEPS)
testinput_DEPENDENCIES = $(TEST_DEPS)
testimage_DEPENDENCIES = $(TEST_DEPS)
testkineticscrolling_DEPENDENCIES = $(TEST_DEPS)
testlockbutton_DEPENDENCIES = $(TEST_DEPS)
testmenubars_DEPENDENCIES = $(TEST_DEPS)
testmountoperation_DEPENDENCIES = $(TEST_DEPS)
@@ -194,6 +197,7 @@ testappchooserbutton_DEPENDENCIES = $(TEST_DEPS)
testorientable_DEPENDENCIES = $(TEST_DEPS)
testoverlay_DEPENDENCIES = $(TEST_DEPS)
testprint_DEPENDENCIES = $(TEST_DEPS)
testpressandhold_DEPENDENCIES = $(TEST_DEPS)
testrecentchooser_DEPENDENCIES = $(TEST_DEPS)
testrecentchoosermenu_DEPENDENCIES = $(TEST_DEPS)
testrichtext_DEPENDENCIES = $(TEST_DEPS)
@@ -277,6 +281,7 @@ testiconview_LDADD = $(LDADDS)
testiconview_keynav_LDADD = $(LDADDS)
testinput_LDADD = $(LDADDS)
testimage_LDADD = $(LDADDS)
testkineticscrolling_LDADD = $(LDADDS)
testlockbutton_LDADD = $(LDADDS)
testmenubars_LDADD = $(LDADDS)
testmountoperation_LDADD = $(LDADDS)
@@ -292,6 +297,7 @@ testappchooserbutton_LDADD = $(LDADDS)
testorientable_LDADD = $(LDADDS)
testoverlay_LDADD = $(LDADDS)
testprint_LDADD = $(LDADDS)
testpressandhold_LDADD = $(LDADDS)
testrecentchooser_LDADD = $(LDADDS)
testrecentchoosermenu_LDADD = $(LDADDS)
testrichtext_LDADD = $(LDADDS)
@@ -405,6 +411,9 @@ testprint_SOURCES = \
testprintfileoperation.h \
testprintfileoperation.c
testpressandhold_SOURCES = \
testpressandhold.c
testsocket_SOURCES = \
testsocket.c \
testsocket_common.c
@@ -521,6 +530,7 @@ testpixbuf_save_SOURCES = testpixbuf-save.c
widget_factory_SOURCES = widget-factory.c
testkineticscrolling_SOURCES = testkineticscrolling.c
EXTRA_DIST += \
gradient1.png \
+150
View File
@@ -0,0 +1,150 @@
#include <gtk/gtk.h>
enum
{
TARGET_GTK_TREE_MODEL_ROW
};
static GtkTargetEntry row_targets[] =
{
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_GTK_TREE_MODEL_ROW }
};
static void
on_button_clicked (GtkWidget *widget, gpointer data)
{
g_print ("Button %d clicked\n", GPOINTER_TO_INT (data));
}
static void
kinetic_scrolling (void)
{
GtkWidget *window, *swindow, *grid;
GtkWidget *label;
GtkWidget *button_grid, *button;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkListStore *store;
GtkWidget *textview;
gint i;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
g_signal_connect (window, "delete_event",
G_CALLBACK (gtk_main_quit), NULL);
grid = gtk_grid_new ();
label = gtk_label_new ("Non scrollable widget using viewport");
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_show (label);
label = gtk_label_new ("Scrollable widget: TreeView");
gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_show (label);
label = gtk_label_new ("Scrollable widget: TextView");
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_show (label);
button_grid = gtk_grid_new ();
for (i = 0; i < 80; i++)
{
gchar *label = g_strdup_printf ("Button number %d", i);
button = gtk_button_new_with_label (label);
gtk_grid_attach (GTK_GRID (button_grid), button, 0, i, 1, 1);
gtk_widget_set_hexpand (button, TRUE);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (on_button_clicked),
GINT_TO_POINTER (i));
g_free (label);
}
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow),
GTK_KINETIC_SCROLLING_ENABLED);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), button_grid);
gtk_widget_show (button_grid);
gtk_grid_attach (GTK_GRID (grid), swindow, 0, 1, 1, 1);
gtk_widget_show (swindow);
treeview = gtk_tree_view_new ();
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview),
GDK_BUTTON1_MASK,
row_targets,
G_N_ELEMENTS (row_targets),
GDK_ACTION_MOVE | GDK_ACTION_COPY);
gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview),
row_targets,
G_N_ELEMENTS (row_targets),
GDK_ACTION_MOVE | GDK_ACTION_COPY);
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer, "editable", TRUE, NULL);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
0, "Title",
renderer,
"text", 0,
NULL);
store = gtk_list_store_new (1, G_TYPE_STRING);
for (i = 0; i < 80; i++)
{
GtkTreeIter iter;
gchar *label = g_strdup_printf ("Row number %d", i);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, 0, label, -1);
g_free (label);
}
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
g_object_unref (store);
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow),
GTK_KINETIC_SCROLLING_ENABLED |
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS);
gtk_container_add (GTK_CONTAINER (swindow), treeview);
gtk_widget_show (treeview);
gtk_grid_attach (GTK_GRID (grid), swindow, 1, 1, 1, 1);
gtk_widget_set_hexpand (swindow, TRUE);
gtk_widget_set_vexpand (swindow, TRUE);
gtk_widget_show (swindow);
textview = gtk_text_view_new ();
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow),
GTK_KINETIC_SCROLLING_ENABLED |
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS);
gtk_container_add (GTK_CONTAINER (swindow), textview);
gtk_widget_show (textview);
gtk_grid_attach (GTK_GRID (grid), swindow, 2, 1, 1, 1);
gtk_widget_set_hexpand (swindow, TRUE);
gtk_widget_set_vexpand (swindow, TRUE);
gtk_widget_show (swindow);
gtk_container_add (GTK_CONTAINER (window), grid);
gtk_widget_show (grid);
gtk_widget_show (window);
}
int
main (int argc, char **argv)
{
gtk_init (NULL, NULL);
kinetic_scrolling ();
gtk_main ();
return 0;
}
+162
View File
@@ -0,0 +1,162 @@
/* testpressandhold.c: Test application for GTK+ >= 3.2 press-n-hold code
*
* Copyright (C) 2007,2008 Imendio AB
* Contact: Kristian Rietveld <kris@imendio.com>
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <gtk/gtk.h>
static void
press_and_hold_show_menu (GtkWidget *widget,
GdkDevice *device)
{
GtkWidget *menu;
GtkWidget *item;
menu = gtk_menu_new ();
item = gtk_menu_item_new_with_label ("Test 1");
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
item = gtk_menu_item_new_with_label ("Test 2");
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
item = gtk_menu_item_new_with_label ("Test 3");
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
gtk_menu_popup_for_device (GTK_MENU (menu), device,
NULL, NULL, NULL, NULL, NULL,
1,
GDK_CURRENT_TIME);
}
static gboolean
press_and_hold (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
gint x,
gint y,
gpointer user_data)
{
switch (action)
{
case GTK_PRESS_AND_HOLD_QUERY:
g_print ("press-and-hold-query on %s\n", gtk_widget_get_name (widget));
break;
case GTK_PRESS_AND_HOLD_TRIGGER:
g_print ("press-and-hold-trigger on %s\n", gtk_widget_get_name (widget));
press_and_hold_show_menu (widget, device);
break;
case GTK_PRESS_AND_HOLD_CANCEL:
g_print ("press-and-hold-cancel on %s\n", gtk_widget_get_name (widget));
break;
}
return TRUE;
}
static GtkTreeModel *
create_model (void)
{
GtkTreeStore *store;
GtkTreeIter iter;
store = gtk_tree_store_new (1, G_TYPE_STRING);
/* A tree store with some random words ... */
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
0, "File Manager", -1);
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
0, "Gossip", -1);
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
0, "System Settings", -1);
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
0, "The GIMP", -1);
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
0, "Terminal", -1);
gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
0, "Word Processor", -1);
return GTK_TREE_MODEL (store);
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *label, *checkbutton, *tree_view, *entry;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Press and Hold test");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
g_signal_connect (window, "delete_event",
G_CALLBACK (gtk_main_quit), NULL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
gtk_container_add (GTK_CONTAINER (window), box);
label = gtk_button_new_with_label ("Press-n-hold me!");
g_signal_connect (label, "press-and-hold",
G_CALLBACK (press_and_hold), NULL);
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
label = gtk_button_new_with_label ("No press and hold");
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
checkbutton = gtk_check_button_new_with_label ("Checkable check button");
g_signal_connect (checkbutton, "press-and-hold",
G_CALLBACK (press_and_hold), NULL);
gtk_box_pack_start (GTK_BOX (box), checkbutton, FALSE, FALSE, 0);
tree_view = gtk_tree_view_new_with_model (create_model ());
gtk_widget_set_size_request (tree_view, 200, 240);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
0, "Test",
gtk_cell_renderer_text_new (),
"text", 0,
NULL);
g_signal_connect (tree_view, "press-and-hold",
G_CALLBACK (press_and_hold), NULL);
gtk_box_pack_start (GTK_BOX (box), tree_view, FALSE, FALSE, 0);
entry = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (entry), "Press and hold me");
g_signal_connect (entry, "press-and-hold",
G_CALLBACK (press_and_hold), NULL);
gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
+17
View File
@@ -53,6 +53,16 @@ content_height_changed (GtkSpinButton *spin_button,
gtk_scrolled_window_set_min_content_height (swindow, (gint)value);
}
static void
kinetic_scrolling_changed (GtkToggleButton *toggle_button,
gpointer data)
{
GtkScrolledWindow *swindow = data;
gboolean enabled = gtk_toggle_button_get_active (toggle_button);
gtk_scrolled_window_set_kinetic_scrolling (swindow, enabled);
}
static void
scrollable_policy (void)
{
@@ -199,6 +209,13 @@ scrollable_policy (void)
g_signal_connect (G_OBJECT (widget), "changed",
G_CALLBACK (label_flip_changed), label);
/* Add Kinetic scrolling control here */
widget = gtk_check_button_new_with_label ("Kinetic scrolling");
gtk_widget_show (widget);
gtk_box_pack_start (GTK_BOX (cntl), widget, TRUE, TRUE, 0);
g_signal_connect (G_OBJECT (widget), "toggled",
G_CALLBACK (kinetic_scrolling_changed), swindow);
gtk_widget_show (window);
}