Compare commits

...

70 Commits

Author SHA1 Message Date
Matthias Clasen 5ddbfef8c9 notebook: Reinstate previous focus behavior
We want the overall tab order to be
start-action
current tab
end-action
page
To achieve this, make the tabs_widget have only
a single focus location, for the current tab.
2019-03-11 15:35:51 -04:00
Matthias Clasen 115c7b6dba gizmo: Make it possible to override focus
This will be used in GtkNotebook.
2019-03-11 15:35:13 -04:00
Matthias Clasen 6a54cb3d3b box: Add a next-focus-chain implementation
This just uses the child order for Tab/Shift-Tab.
2019-03-11 15:08:12 -04:00
Matthias Clasen 998215942c notebook: Remove can-focus
This is not really necessary, and removing it makes
the focus chain work better.
2019-03-11 13:34:00 -04:00
Matthias Clasen 2511c4586b toolbar: Fix keynav
Reinstate the only-arrows-move inside behavior.
2019-03-09 21:39:44 -05:00
Matthias Clasen 5ce164ce8b focus test: Verify the focus chain
Make sure that the focus chain always leads from
the toplevel to the focus widget. We don't verify
that it is properly unset everywhere else, atm.
2019-03-08 13:55:38 -05:00
Matthias Clasen 469f038c00 Make gtk_widget_get_focus_child public
I want to use it in a test, if nothing else.
2019-03-08 13:55:38 -05:00
Matthias Clasen 0de3ac9bd8 Move maintaining the focus chain
Put this code in the same place where we generate
the crossing events.
2019-03-08 13:55:38 -05:00
Matthias Clasen 42c73a3495 Add a ::set_focus_child vfunc back
This will allow widgets to update state when a child
is focused. An example for where this is needed is
updating the selection in listbox or flowbox.
2019-03-08 13:55:08 -05:00
Matthias Clasen 0021b02380 Add key controller API to the docs
A bunch of API was missing here.
2019-03-08 13:55:08 -05:00
Matthias Clasen a93f939468 key controller: Enforce limitations of key forwarding
We now set current_event for focus-change as well,
so make sure to check the event type.
2019-03-08 13:55:08 -05:00
Matthias Clasen c389e1fea6 key controller: Improve the docs
Mention the limitations of the key forwarding api.
2019-03-08 13:55:08 -05:00
Matthias Clasen 23d98a956d focus test: test origin and targets
Check that these functions return the expected
widgets during crossing event generation.
2019-03-08 13:55:08 -05:00
Matthias Clasen c55d538447 key controller: Add getters for focus event targets
This information can be needed in signal handlers,
so make it available.
2019-03-08 13:55:08 -05:00
Matthias Clasen 8e6985bdc0 focus test: check the new properties
Test that is-focus and contains-focus have the
expected values, as focus moves around.
2019-03-08 13:55:08 -05:00
Matthias Clasen 1960f44fe3 key controller: Add focus properties
Add boolean properties, is-focus and contains-focus, that
track whether the focus is in the widget itself or in
one of its descendants.
2019-03-08 13:55:08 -05:00
Matthias Clasen 53ae3f777e key controller: Fix a copy/paste error
Prefix the finalize function properly.
2019-03-08 13:55:08 -05:00
Matthias Clasen b408a2674a Update the focus test
Check that we get the expected sequences of focus
change events for the nonlinear, inferior and ancestor
cases.

It would be nice to do the same checks for crossing
events, but we have no gtk_window_set_hover().
2019-03-08 13:55:08 -05:00
Matthias Clasen 7cc045666a Some fixes to crossing event generation
In the inferior and ancestor cases, we were missing
the initial/final event.
2019-03-08 13:55:08 -05:00
Matthias Clasen 7b084d05fa Use gdk_event_set_related_target 2019-03-08 13:55:07 -05:00
Matthias Clasen 8dc276b20b Pass mode and detail to focus-in/out signals
This information is useful when maintaining a
'last focus' field.

Update all users.
2019-03-08 13:54:45 -05:00
Matthias Clasen 56c862dc1f window: Use gtk_synthesize_crossing_events
Emit focus change events in the same way as crossing events.
Also change the code to only emit focus change events for
the master keyboard - we only maintain a single focus location,
so sending multiple focus change events for different devices
seems confusing.
2019-03-08 13:54:45 -05:00
Matthias Clasen 1ba7ff4ae2 Export gtk_synthesize_crossing_events internally
We want to use this for focus changes, now that
it can emit focus change events.
2019-03-08 13:54:45 -05:00
Matthias Clasen 162122133c Avoid a critical 2019-03-08 13:54:45 -05:00
Matthias Clasen 364b3ad8de drop gtk_widget_send_focus_change
Replace this with gtk_widget_set_has_focus + gtk_widget_event.
2019-03-08 13:54:45 -05:00
Matthias Clasen 54fd25a6a1 Share crossing and focus change event code
Make a single function that can emit both
enter/leave and focus change events.
2019-03-08 13:54:44 -05:00
Matthias Clasen 3ef019be6a widget: Add a private setter for has-focus 2019-03-08 12:32:04 -05:00
Matthias Clasen a6c1784662 gdk: Add a related_target field to some events
Crossing and focus change events are emitted between
two widgets, and want to associate both with the
events.
2019-03-08 12:32:04 -05:00
Matthias Clasen 684468f3d2 gdk: Rename gdk_event_set_user_data
Lets call it what it is, the target.
All of this is private api anyway.

Update all callers.
2019-03-08 12:32:04 -05:00
Matthias Clasen da73ff62dd gdk: Add crossing mode and detail to focus events
We want focus events more similar to crossing events.
2019-03-08 12:32:04 -05:00
Matthias Clasen 7d7288eedb Add a nested boxes focus test 2019-03-08 12:32:04 -05:00
Matthias Clasen 4bc6ca2a83 Add some stack focus tests 2019-03-08 12:32:04 -05:00
Matthias Clasen 8db0ba6745 Add a focus-chain test
This test takes ui files and compares the focus chain
with the expected output, as usual. ui files go in
the testsuite/gtk/focus-chain directory, and expected
output in the form of one widget ID per line goes
in .txt files.
2019-03-08 12:32:04 -05:00
Matthias Clasen 87d2aa74d3 Clarify grab_focus docs 2019-03-08 12:31:53 -05:00
Matthias Clasen 12456d71b1 window: Drop next_focus_child implementation
Not needed anymore, now that the cycling is enforced.
2019-03-08 12:29:28 -05:00
Matthias Clasen ed6830a2f0 Force focus chain to be cyclic
Move the enforcement of focus chain wraparound to
gtk_widget_get_next_focus. This lets next_focus_child
implementation be simpler.
2019-03-08 12:29:28 -05:00
Matthias Clasen 865cd272bc root: Don't allow setting nonsense focus
Don't allow setting focus-widget to something
thats not inside the root.
2019-03-08 12:29:28 -05:00
Matthias Clasen ead299b12d Add a missing include
If we don't include config.h, GDK_AVAILABLE_...
does not have the desired effect, and your symbols
are not exported. And you spend hours tearing out
your hair.
2019-03-08 12:29:28 -05:00
Matthias Clasen 767a478919 Factor out a common function
Use the same helper function to determine if a widget
can be made the focus-widget in all the places where
it is what we want to know.

The helper function takes into account can-focus,
sensitive and child-focusable.
2019-03-08 12:29:28 -05:00
Matthias Clasen 81783abd52 label: Bring back link focus
Handle key-pressed ourselves to maintain the intra-label
focus location for links. This works almost as well as
it did previously. One issue is that our ::focus-in
handler does not know which direction the focus is coming
from, so we always select the first link, whereas previously
we would select the last link for Shift-Tab.
2019-03-08 12:29:28 -05:00
Matthias Clasen 6c5d4fbe32 label: Redo select-on-focus handling
Similar to GtkEntry, move this from grab_focus to focus-in.
2019-03-08 12:29:28 -05:00
Matthias Clasen a844c9d6ca entry: Redo select-on-focus handling
GtkText was handling this just on grab_focus, which is
not the only way anymore that focus can reach it. Instead,
shift this to focus-in, and add a property that can be
used to override the select-on-focus setting. Remove
the gtk_text_grab_focus_without_selecting(), but keep
the GtkEntry variant of the function, reimplemented
to use the properties. GtkSearchEntry sets the select-on-focus
property of its text to FALSE, to match expectations.
2019-03-08 12:29:28 -05:00
Matthias Clasen 4fa651a03f window: Remove initial-focus builder support
With focus-widget now a property, this is no longer needed.
2019-03-08 12:29:28 -05:00
Matthias Clasen 73cad163ad window: Remove ::set-focus
The focus-widget is now a property, so we don't need
this signal anymore.
2019-03-08 12:29:28 -05:00
Matthias Clasen 53598e5e44 popover: Stop using ::set-focus
This will change completely when GtkPopover becomes a
root. For now, stop using ::set-focus and just use the
focus-widget property.
2019-03-08 12:29:28 -05:00
Matthias Clasen 3b5a6b656a filechooser: Stop using ::set-focus
We can achieve the same with the focus-widget property.
2019-03-08 12:29:28 -05:00
Matthias Clasen 9991f1a0e8 inspector: Stop using ::set-focus
The focus-widget is just a regular property now that shows
up on the property page. No need to special-case it on
the misc page anymore.
2019-03-08 12:29:28 -05:00
Matthias Clasen a7f564f04f Protect against focus cycles
Now that next_focus_child wraps around for windows,
GtkMenus cause an infinite lookp in next_Focus_child.
Prevent that.
2019-03-08 12:29:28 -05:00
Matthias Clasen e015ee0c00 root: Add a ::focus-widget property 2019-03-08 12:29:28 -05:00
Matthias Clasen e15c4703bc color chooser: Prevent focus leaks
The color chooser switches from the palette to
the editor by just showing and hiding. Use
child-focusable to ensure that focus does not
wander off into the hidden parts.
2019-03-08 12:29:28 -05:00
Matthias Clasen 1ce4fee9cd window: Check for child-focusable
Don't allow gtk_window_set_focus to focus a widget
that is !can-focus or has an ancestor that is !child-focusable.
2019-03-08 12:29:27 -05:00
Matthias Clasen 07ce97d500 Add a child-focusable flag
This gives a convenient way to exclude children from
the focus chain.
2019-03-08 12:29:27 -05:00
Matthias Clasen 0b78524ccd Remove ::focus implementations
We no longer call the ::focus vfunc, so remove
all existing implementations. This needs careful
review to determine how to bring the functionality
in these handlers back.
2019-03-08 12:29:13 -05:00
Matthias Clasen 3994623969 Remove gtk_widget_send_focus_change from public API
It should be dropped altogether. Until that happens,
keep it private.
2019-03-08 11:17:11 -05:00
Matthias Clasen 8e3f3cfa07 Remove gtk_widget_focus_move
This is no longer used.
2019-03-08 11:17:11 -05:00
Matthias Clasen 4a56b1883b Exclude non-drawable widgets from focus
This was showing up as confusion between palette
and editor in the color chooser.
2019-03-08 11:17:11 -05:00
Matthias Clasen 71b9821bcf window: Make focus wrap again
This is the traditional GTK behavior: wrap around when
the focus reaches the end of the toplevel's focus chain.
2019-03-08 11:17:11 -05:00
Matthias Clasen 974af6773f inspector: Add a focus overlay
Add an overlay that shows a segment of the focus chain.
2019-03-08 11:17:11 -05:00
Matthias Clasen fdc7231bfd expander: remove the ::focus implementation 2019-03-08 11:17:11 -05:00
Matthias Clasen 4588950a43 stack: Implement next_focus_child
We only return the visible page here, to avoid
focus moving into other pages.
2019-03-08 11:17:11 -05:00
Matthias Clasen 9e0c4ed24b widget: Add a next-focus-child API
Move things around to make the focus chain introspectable.
Use the next-focus-child api in the move-focus and
focus implementations.

We add a next_focus_child vfunc that containers can
override to tweak the behavior.
2019-03-08 11:17:11 -05:00
Matthias Clasen d7d05fe06e widget: Drop the ::focus signal
The focus vfunc is an implementation detail
of GTK focus handling, and having external
signal handlers interfere with it is not
a good idea.
2019-03-08 11:17:11 -05:00
Matthias Clasen 3bb19eb91b Simplify the move_focus api
No need to pass the array in from the outside.
2019-03-08 11:17:11 -05:00
Matthias Clasen 5af98031e5 Move a check to gtk_window_set_focus
gtk_widget_grab_focus is just a wrapper for
gtk_window_set_focus. We should do all the enforcement
there.
2019-03-08 11:17:11 -05:00
Matthias Clasen a8a5e65d0f Port widgets to the root focus API 2019-03-08 11:17:11 -05:00
Matthias Clasen 37cc26b794 window: Implement the root focus api
This just uses the existing get/set_focus functions.
We keep them public for now.
2019-03-08 11:17:11 -05:00
Matthias Clasen 9b3206a2ca root: Add focus
Add a getter and a setter for the focus widget.
The default implementations do nothing.
2019-03-08 11:17:11 -05:00
Matthias Clasen 9e859a1964 paned: Stop using set_focus_child
We can instead use focus_child to find the last focus location
inside a child.
2019-03-08 11:17:11 -05:00
Matthias Clasen 835ee90c0d notebook: Stop using set_focus_child
We can instead use focus_child to find the last focus
location inside a page.
2019-03-08 11:17:11 -05:00
Matthias Clasen ac5040ef8a Redo focus handling
GtkWindow has a focus_widget that points to the current input focus.
GtkWidget has a focus_child that points to the child that contains
the input focus. Following the focus_child chain from the toplevel
always leads to the focus_widget. We never unset focus_child, we only
set it. We bubble focus change events.
2019-03-08 11:17:11 -05:00
67 changed files with 2312 additions and 2576 deletions
+6 -1
View File
@@ -904,7 +904,6 @@ gtk_text_set_attributes
gtk_text_get_attributes
gtk_text_set_tabs
gtk_text_get_tabs
gtk_text_grab_focus_without_selecting
<SUBSECTION Private>
gtk_text_get_type
</SECTION>
@@ -6700,6 +6699,12 @@ gtk_event_controller_motion_get_type
<TITLE>GtkEventControllerKey</TITLE>
GtkEventControllerKey
gtk_event_controller_key_new
gtk_event_controller_key_set_im_context
gtk_event_controller_key_get_im_context
gtk_event_controller_key_forward
gtk_event_controller_key_get_group
gtk_event_controller_key_get_focus_origin
gtk_event_controller_key_get_focus_target
<SUBSECTION Standard>
GTK_TYPE_EVENT_CONTROLLER_KEY
-2
View File
@@ -20,8 +20,6 @@ void gdk_surface_thaw_toplevel_updates (GdkSurface *surface);
gboolean gdk_surface_supports_edge_constraints (GdkSurface *surface);
GObject * gdk_event_get_user_data (const GdkEvent *event);
guint32 gdk_display_get_last_seen_time (GdkDisplay *display);
void gdk_display_set_double_click_time (GdkDisplay *display,
+53 -8
View File
@@ -564,8 +564,8 @@ gdk_event_copy (const GdkEvent *event)
g_object_ref (new_event->any.device);
if (new_event->any.source_device)
g_object_ref (new_event->any.source_device);
if (new_event->any.user_data)
g_object_ref (new_event->any.user_data);
if (new_event->any.target)
g_object_ref (new_event->any.target);
switch ((guint) event->any.type)
{
@@ -573,6 +573,13 @@ gdk_event_copy (const GdkEvent *event)
case GDK_LEAVE_NOTIFY:
if (event->crossing.child_surface != NULL)
g_object_ref (event->crossing.child_surface);
if (event->crossing.related_target)
g_object_ref (event->crossing.related_target);
break;
case GDK_FOCUS_CHANGE:
if (event->focus_change.related_target)
g_object_ref (event->focus_change.related_target);
break;
case GDK_DRAG_ENTER:
@@ -634,6 +641,11 @@ gdk_event_finalize (GObject *object)
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
g_clear_object (&event->crossing.child_surface);
g_clear_object (&event->crossing.related_target);
break;
case GDK_FOCUS_CHANGE:
g_clear_object (&event->focus_change.related_target);
break;
case GDK_DRAG_ENTER:
@@ -675,7 +687,7 @@ gdk_event_finalize (GObject *object)
g_clear_object (&event->any.device);
g_clear_object (&event->any.source_device);
g_clear_object (&event->any.user_data);
g_clear_object (&event->any.target);
G_OBJECT_CLASS (gdk_event_parent_class)->finalize (object);
}
@@ -1904,16 +1916,39 @@ gdk_event_get_scancode (GdkEvent *event)
}
void
gdk_event_set_user_data (GdkEvent *event,
GObject *user_data)
gdk_event_set_target (GdkEvent *event,
GObject *target)
{
g_set_object (&event->any.user_data, user_data);
g_set_object (&event->any.target, target);
}
GObject *
gdk_event_get_user_data (const GdkEvent *event)
gdk_event_get_target (const GdkEvent *event)
{
return event->any.user_data;
return event->any.target;
}
void
gdk_event_set_related_target (GdkEvent *event,
GObject *target)
{
if (event->any.type == GDK_ENTER_NOTIFY ||
event->any.type == GDK_LEAVE_NOTIFY)
g_set_object (&event->crossing.related_target, target);
else if (event->any.type == GDK_FOCUS_CHANGE)
g_set_object (&event->focus_change.related_target, target);
}
GObject *
gdk_event_get_related_target (const GdkEvent *event)
{
if (event->any.type == GDK_ENTER_NOTIFY ||
event->any.type == GDK_LEAVE_NOTIFY)
return event->crossing.related_target;
else if (event->any.type == GDK_FOCUS_CHANGE)
return event->focus_change.related_target;
return NULL;
}
/**
@@ -1980,6 +2015,11 @@ gdk_event_get_crossing_mode (const GdkEvent *event,
*mode = event->crossing.mode;
return TRUE;
}
else if (event->any.type == GDK_FOCUS_CHANGE)
{
*mode = event->focus_change.mode;
return TRUE;
}
return FALSE;
}
@@ -2006,6 +2046,11 @@ gdk_event_get_crossing_detail (const GdkEvent *event,
*detail = event->crossing.detail;
return TRUE;
}
else if (event->any.type == GDK_FOCUS_CHANGE)
{
*detail = event->focus_change.detail;
return TRUE;
}
return FALSE;
}
+13 -3
View File
@@ -61,7 +61,7 @@ struct _GdkEventAny
GdkDevice *device;
GdkDevice *source_device;
GdkDisplay *display;
GObject *user_data;
GObject *target;
};
/*
@@ -303,6 +303,7 @@ struct _GdkEventCrossing
GdkNotifyType detail;
gboolean focus;
guint state;
GObject *related_target;
};
/*
@@ -312,6 +313,8 @@ struct _GdkEventCrossing
* @send_event: %TRUE if the event was sent explicitly.
* @in: %TRUE if the surface has gained the keyboard focus, %FALSE if
* it has lost the focus.
* @mode: the crossing mode
* @detail: the kind of crossing that happened
*
* Describes a change of keyboard focus.
*/
@@ -319,6 +322,9 @@ struct _GdkEventFocus
{
GdkEventAny any;
gint16 in;
GdkCrossingMode mode;
GdkNotifyType detail;
GObject *related_target;
};
/*
@@ -632,8 +638,12 @@ union _GdkEvent
GdkEventPadGroupMode pad_group_mode;
};
void gdk_event_set_user_data (GdkEvent *event,
GObject *user_data);
void gdk_event_set_target (GdkEvent *event,
GObject *user_data);
GObject * gdk_event_get_target (const GdkEvent *event);
void gdk_event_set_related_target (GdkEvent *event,
GObject *user_data);
GObject * gdk_event_get_related_target (const GdkEvent *event);
#endif /* __GDK_EVENTS_PRIVATE_H__ */
+27
View File
@@ -123,6 +123,9 @@ static void gtk_box_measure (GtkWidget *widget,
int *natural,
int *minimum_baseline,
int *natural_baseline);
GtkWidget * gtk_box_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction);
G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
G_ADD_PRIVATE (GtkBox)
@@ -140,6 +143,7 @@ gtk_box_class_init (GtkBoxClass *class)
widget_class->size_allocate = gtk_box_size_allocate;
widget_class->measure = gtk_box_measure;
widget_class->next_focus_child = gtk_box_next_focus_child;
container_class->add = gtk_box_add;
container_class->remove = gtk_box_remove;
@@ -1149,3 +1153,26 @@ gtk_box_reorder_child_after (GtkBox *box,
gtk_widget_get_css_node (child),
sibling ? gtk_widget_get_css_node (sibling) : NULL);
}
GtkWidget *
gtk_box_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction)
{
if (direction == GTK_DIR_TAB_FORWARD)
{
if (child)
return gtk_widget_get_next_sibling (child);
else
return gtk_widget_get_first_child (widget);
}
else if (direction == GTK_DIR_TAB_BACKWARD)
{
if (child)
return gtk_widget_get_prev_sibling (child);
else
return gtk_widget_get_last_child (widget);
}
return GTK_WIDGET_CLASS (gtk_box_parent_class)->next_focus_child (widget, child, direction);
}
+4
View File
@@ -295,6 +295,8 @@ static gboolean gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *
GdkModifierType state,
GtkWidget *widget);
static void gtk_calendar_key_controller_focus (GtkEventControllerKey *controller,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkWidget *widget);
static void gtk_calendar_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
@@ -2854,6 +2856,8 @@ gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller,
static void
gtk_calendar_key_controller_focus (GtkEventControllerKey *key,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkWidget *widget)
{
GtkCalendar *calendar = GTK_CALENDAR (widget);
+22 -10
View File
@@ -129,6 +129,22 @@ swatch_activate (GtkColorSwatch *swatch,
_gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color);
}
static void
show_editor (GtkColorChooserWidget *cc,
gboolean show)
{
gtk_widget_set_visible (cc->priv->palette, !show);
gtk_widget_set_visible (cc->priv->editor, show);
gtk_widget_set_child_focusable (cc->priv->palette, !show);
gtk_widget_set_child_focusable (cc->priv->editor, show);
if (show)
gtk_widget_child_focus (cc->priv->editor, GTK_DIR_TAB_FORWARD);
else
gtk_widget_child_focus (cc->priv->palette, GTK_DIR_TAB_FORWARD);
g_object_notify (G_OBJECT (cc), "show-editor");
}
static void
swatch_customize (GtkColorSwatch *swatch,
GtkColorChooserWidget *cc)
@@ -138,9 +154,7 @@ swatch_customize (GtkColorSwatch *swatch,
gtk_color_swatch_get_rgba (swatch, &color);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
gtk_widget_hide (cc->priv->palette);
gtk_widget_show (cc->priv->editor);
g_object_notify (G_OBJECT (cc), "show-editor");
show_editor (cc, TRUE);
}
static void
@@ -174,9 +188,7 @@ button_activate (GtkColorSwatch *swatch,
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
gtk_widget_hide (cc->priv->palette);
gtk_widget_show (cc->priv->editor);
g_object_notify (G_OBJECT (cc), "show-editor");
show_editor (cc, TRUE);
}
static void
@@ -260,9 +272,9 @@ gtk_color_chooser_widget_set_use_alpha (GtkColorChooserWidget *cc,
static void
gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc,
gboolean show_editor)
gboolean show)
{
if (show_editor)
if (show)
{
GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 };
@@ -271,8 +283,7 @@ gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc,
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
}
gtk_widget_set_visible (cc->priv->editor, show_editor);
gtk_widget_set_visible (cc->priv->palette, !show_editor);
show_editor (cc, show);
}
static void
@@ -592,6 +603,7 @@ gtk_color_chooser_widget_init (GtkColorChooserWidget *cc)
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc), &color);
gtk_widget_hide (GTK_WIDGET (cc->priv->editor));
gtk_widget_set_child_focusable (GTK_WIDGET (cc->priv->editor), FALSE);
cc->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
gtk_size_group_add_widget (cc->priv->size_group, cc->priv->palette);
+2 -1
View File
@@ -36,6 +36,7 @@
#include "gtkspinbutton.h"
#include "gtkstylecontext.h"
#include "gtkeventcontrollerkey.h"
#include "gtkroot.h"
#include <math.h>
@@ -224,7 +225,7 @@ popup_edit (GtkWidget *widget,
{
dismiss_current_popup (editor);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editor));
g_set_object (&editor->priv->popdown_focus, gtk_window_get_focus (GTK_WINDOW (toplevel)));
g_set_object (&editor->priv->popdown_focus, gtk_root_get_focus (GTK_ROOT (toplevel)));
editor->priv->current_popup = popup;
editor->priv->popup_position = position;
gtk_widget_show (popup);
+15 -2
View File
@@ -1652,10 +1652,23 @@ gtk_entry_snapshot (GtkWidget *widget,
void
gtk_entry_grab_focus_without_selecting (GtkEntry *entry)
{
g_return_if_fail (GTK_IS_ENTRY (entry));
GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
gboolean select, set;
gtk_text_grab_focus_without_selecting (GTK_TEXT (priv->text));
g_return_if_fail (GTK_IS_ENTRY (entry));
g_object_get (priv->text,
"select-on-focus", &select,
"select-on-focus-set", &set,
NULL);
g_object_set (priv->text, "select-on-focus", FALSE, NULL);
gtk_widget_grab_focus (priv->text);
g_object_set (priv->text,
"select-on-focus", select,
"select-on-focus-set", set,
NULL);
}
static void
+226 -16
View File
@@ -36,6 +36,7 @@
#include "gtkeventcontrollerkey.h"
#include "gtkbindings.h"
#include "gtkenums.h"
#include "gtkmain.h"
#include <gdk/gdk.h>
@@ -46,6 +47,9 @@ struct _GtkEventControllerKey
GHashTable *pressed_keys;
const GdkEvent *current_event;
guint is_focus : 1;
guint contains_focus : 1;
};
struct _GtkEventControllerKeyClass
@@ -65,11 +69,19 @@ enum {
static guint signals[N_SIGNALS] = { 0 };
enum {
PROP_IS_FOCUS = 1,
PROP_CONTAINS_FOCUS,
NUM_PROPERTIES
};
static GParamSpec *props[NUM_PROPERTIES] = { NULL, };
G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key,
GTK_TYPE_EVENT_CONTROLLER)
static void
gtk_event_controller_finalize (GObject *object)
gtk_event_controller_key_finalize (GObject *object)
{
GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object);
@@ -79,6 +91,50 @@ gtk_event_controller_finalize (GObject *object)
G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object);
}
static void
update_focus (GtkEventControllerKey *key,
gboolean focus_in,
GdkNotifyType detail)
{
gboolean is_focus;
gboolean contains_focus;
switch (detail)
{
case GDK_NOTIFY_VIRTUAL:
case GDK_NOTIFY_NONLINEAR_VIRTUAL:
is_focus = FALSE;
contains_focus = focus_in;
break;
case GDK_NOTIFY_ANCESTOR:
case GDK_NOTIFY_NONLINEAR:
is_focus = focus_in;
contains_focus = FALSE;
break;
case GDK_NOTIFY_INFERIOR:
is_focus = focus_in;
contains_focus = !focus_in;
break;
case GDK_NOTIFY_UNKNOWN:
default:
g_warning ("Unknown focus change detail");
return;
}
g_object_freeze_notify (G_OBJECT (key));
if (key->is_focus != is_focus)
{
key->is_focus = is_focus;
g_object_notify (G_OBJECT (key), "is-focus");
}
if (key->contains_focus != contains_focus)
{
key->contains_focus = contains_focus;
g_object_notify (G_OBJECT (key), "contains-focus");
}
g_object_thaw_notify (G_OBJECT (key));
}
static gboolean
gtk_event_controller_key_handle_event (GtkEventController *controller,
const GdkEvent *event)
@@ -93,11 +149,23 @@ gtk_event_controller_key_handle_event (GtkEventController *controller,
if (event_type == GDK_FOCUS_CHANGE)
{
gboolean focus_in;
GdkCrossingMode mode;
GdkNotifyType detail;
if (gdk_event_get_focus_in (event, &focus_in) && focus_in)
g_signal_emit (controller, signals[FOCUS_IN], 0);
gdk_event_get_focus_in (event, &focus_in);
gdk_event_get_crossing_mode (event, &mode);
gdk_event_get_crossing_detail (event, &detail);
update_focus (key, focus_in, detail);
key->current_event = event;
if (focus_in)
g_signal_emit (controller, signals[FOCUS_IN], 0, mode, detail);
else
g_signal_emit (controller, signals[FOCUS_OUT], 0);
g_signal_emit (controller, signals[FOCUS_OUT], 0, mode, detail);
key->current_event = NULL;
return FALSE;
}
@@ -158,15 +226,76 @@ gtk_event_controller_key_handle_event (GtkEventController *controller,
return handled;
}
static void
gtk_event_controller_key_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkEventControllerKey *controller = GTK_EVENT_CONTROLLER_KEY (object);
switch (prop_id)
{
case PROP_IS_FOCUS:
g_value_set_boolean (value, controller->is_focus);
break;
case PROP_CONTAINS_FOCUS:
g_value_set_boolean (value, controller->contains_focus);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
{
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gtk_event_controller_finalize;
object_class->finalize = gtk_event_controller_key_finalize;
object_class->get_property = gtk_event_controller_key_get_property;
controller_class->handle_event = gtk_event_controller_key_handle_event;
/**
* GtkEventControllerKey:is-focus:
*
* Whether focus is in the controllers widget itself,
* as opposed to in a descendent widget. See
* #GtkEventControllerKey:contains-focus.
*
* When handling focus events, this property is updated
* before #GtkEventControllerKey::focus-in or
* #GtkEventControllerKey::focus-out are emitted.
*/
props[PROP_IS_FOCUS] =
g_param_spec_boolean ("is-focus",
P_("Is Focus"),
P_("Whether the focus is in the controllers widget"),
FALSE,
G_PARAM_READABLE);
/**
* GtkEventControllerKey:contains-focus:
*
* Whether focus is in a descendant of the controllers widget.
* See #GtkEventControllerKey:is-focus.
*
* When handling focus events, this property is updated
* before #GtkEventControllerKey::focus-in or
* #GtkEventControllerKey::focus-out are emitted.
*/
props[PROP_CONTAINS_FOCUS] =
g_param_spec_boolean ("contains-focus",
P_("Contains Focus"),
P_("Whether the focus is in a descendant of the controllers widget"),
FALSE,
G_PARAM_READABLE);
g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
/**
* GtkEventControllerKey::key-pressed:
* @controller: the object which received the signal.
@@ -233,38 +362,50 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
GTK_TYPE_EVENT_CONTROLLER_KEY,
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
NULL,
G_TYPE_NONE, 0);
/**
* GtkEventControllerKey::focus-in:
* @controller: the object which received the signal.
* @mode: crossing mode indicating what caused this change
* @detail: detail indication where the focus is coming from
*
* This signal is emitted whenever the #GtkEventController:widget controlled
* by the @controller is given the keyboard focus.
* This signal is emitted whenever the widget controlled
* by the @controller or one of its descendants) is given
* the keyboard focus.
*/
signals[FOCUS_IN] =
g_signal_new (I_("focus-in"),
GTK_TYPE_EVENT_CONTROLLER_KEY,
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
NULL,
G_TYPE_NONE,
2,
GDK_TYPE_CROSSING_MODE,
GDK_TYPE_NOTIFY_TYPE);
/**
* GtkEventControllerKey::focus-out:
* @controller: the object which received the signal.
* @mode: crossing mode indicating what caused this change
* @detail: detail indication where the focus is going
*
* This signal is emitted whenever the #GtkEventController:widget controlled
* by the @controller loses the keyboard focus.
* This signal is emitted whenever the widget controlled
* by the @controller (or one of its descendants) loses
* the keyboard focus.
*/
signals[FOCUS_OUT] =
g_signal_new (I_("focus-out"),
GTK_TYPE_EVENT_CONTROLLER_KEY,
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
NULL,
G_TYPE_NONE,
2,
GDK_TYPE_CROSSING_MODE,
GDK_TYPE_NOTIFY_TYPE);
}
static void
@@ -283,8 +424,7 @@ gtk_event_controller_key_init (GtkEventControllerKey *controller)
GtkEventController *
gtk_event_controller_key_new (void)
{
return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY,
NULL);
return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, NULL);
}
/**
@@ -330,6 +470,13 @@ gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller)
*
* Forwards the current event of this @controller to a @widget.
*
* This function can only be used in handlers for the
* #GtkEventControllerKey::key-pressed,
* #GtkEventControllerKey::key-released
* or
* #GtkEventControllerKey::modifiers
* signals.
*
* Returns: whether the @widget handled the event
**/
gboolean
@@ -339,6 +486,8 @@ gtk_event_controller_key_forward (GtkEventControllerKey *controller,
g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (controller->current_event != NULL, FALSE);
g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_KEY_PRESS ||
gdk_event_get_event_type (controller->current_event) == GDK_KEY_RELEASE, FALSE);
if (!gtk_widget_get_realized (widget))
gtk_widget_realize (widget);
@@ -377,3 +526,64 @@ gtk_event_controller_key_get_group (GtkEventControllerKey *controller)
return group;
}
/**
* gtk_event_controller_key_get_focus_origin:
* @controller: a #GtkEventControllerKey
*
* Returns the widget that was holding focus before.
*
* This function can only be used in handlers for the
* #GtkEventControllerKey::focus-in and
* #GtkEventControllerKey::focus-out signals.
*
* Returns: (transfer none): the previous focus
*/
GtkWidget *
gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller)
{
gboolean focus_in;
GtkWidget *origin;
g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
g_return_val_if_fail (controller->current_event != NULL, NULL);
g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_FOCUS_CHANGE, NULL);
gdk_event_get_focus_in (controller->current_event, &focus_in);
if (focus_in)
origin = (GtkWidget *)gdk_event_get_related_target (controller->current_event);
else
origin = (GtkWidget *)gdk_event_get_target (controller->current_event);
return origin;
}
/**
* gtk_event_controller_key_get_focus_target:
* @controller: a #GtkEventControllerKey
*
* Returns the widget that will be holding focus afterwards.
*
* This function can only be used in handlers for the
* #GtkEventControllerKey::focus-in and
* #GtkEventControllerKey::focus-out signals.
*
* Returns: (transfer none): the next focus
*/
GtkWidget *
gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller)
{
gboolean focus_in;
g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
g_return_val_if_fail (controller->current_event != NULL, NULL);
g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_FOCUS_CHANGE, NULL);
gdk_event_get_focus_in (controller->current_event, &focus_in);
if (focus_in)
return (GtkWidget *)gdk_event_get_target (controller->current_event);
else
return (GtkWidget *)gdk_event_get_related_target (controller->current_event);
}
+5
View File
@@ -58,6 +58,11 @@ gboolean gtk_event_controller_key_forward (GtkEventControllerK
GDK_AVAILABLE_IN_ALL
guint gtk_event_controller_key_get_group (GtkEventControllerKey *controller);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller);
G_END_DECLS
#endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */
-174
View File
@@ -177,8 +177,6 @@ static void gtk_expander_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline);
static gboolean gtk_expander_focus (GtkWidget *widget,
GtkDirectionType direction);
static gboolean gtk_expander_drag_motion (GtkWidget *widget,
GdkDrop *drop,
gint x,
@@ -253,7 +251,6 @@ gtk_expander_class_init (GtkExpanderClass *klass)
widget_class->destroy = gtk_expander_destroy;
widget_class->size_allocate = gtk_expander_size_allocate;
widget_class->focus = gtk_expander_focus;
widget_class->drag_motion = gtk_expander_drag_motion;
widget_class->drag_leave = gtk_expander_drag_leave;
widget_class->measure = gtk_expander_measure;
@@ -562,142 +559,6 @@ gtk_expander_drag_leave (GtkWidget *widget,
}
}
typedef enum
{
FOCUS_NONE,
FOCUS_WIDGET,
FOCUS_LABEL,
FOCUS_CHILD
} FocusSite;
static gboolean
focus_current_site (GtkExpander *expander,
GtkDirectionType direction)
{
GtkWidget *current_focus;
current_focus = gtk_widget_get_focus_child (GTK_WIDGET (expander));
if (!current_focus)
return FALSE;
return gtk_widget_child_focus (current_focus, direction);
}
static gboolean
focus_in_site (GtkExpander *expander,
FocusSite site,
GtkDirectionType direction)
{
GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander);
switch (site)
{
case FOCUS_WIDGET:
gtk_widget_grab_focus (GTK_WIDGET (expander));
return TRUE;
case FOCUS_LABEL:
if (priv->label_widget)
return gtk_widget_child_focus (priv->label_widget, direction);
else
return FALSE;
case FOCUS_CHILD:
{
GtkWidget *child = priv->child;
if (child && gtk_widget_get_child_visible (child))
return gtk_widget_child_focus (child, direction);
else
return FALSE;
}
case FOCUS_NONE:
default:
break;
}
g_assert_not_reached ();
return FALSE;
}
static FocusSite
get_next_site (GtkExpander *expander,
FocusSite site,
GtkDirectionType direction)
{
gboolean ltr;
ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
switch (site)
{
case FOCUS_NONE:
switch (direction)
{
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_LEFT:
case GTK_DIR_UP:
return FOCUS_CHILD;
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_DOWN:
case GTK_DIR_RIGHT:
default:
return FOCUS_WIDGET;
}
break;
case FOCUS_WIDGET:
switch (direction)
{
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_UP:
return FOCUS_NONE;
case GTK_DIR_LEFT:
return ltr ? FOCUS_NONE : FOCUS_LABEL;
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_DOWN:
default:
return FOCUS_LABEL;
case GTK_DIR_RIGHT:
return ltr ? FOCUS_LABEL : FOCUS_NONE;
}
break;
case FOCUS_LABEL:
switch (direction)
{
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_UP:
return FOCUS_WIDGET;
case GTK_DIR_LEFT:
return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_DOWN:
default:
return FOCUS_CHILD;
case GTK_DIR_RIGHT:
return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
}
break;
case FOCUS_CHILD:
switch (direction)
{
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_LEFT:
case GTK_DIR_UP:
return FOCUS_LABEL;
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_DOWN:
case GTK_DIR_RIGHT:
default:
return FOCUS_NONE;
}
break;
default:
g_assert_not_reached ();
break;
}
return FOCUS_NONE;
}
static void
gtk_expander_resize_toplevel (GtkExpander *expander)
{
@@ -731,41 +592,6 @@ gtk_expander_resize_toplevel (GtkExpander *expander)
}
}
static gboolean
gtk_expander_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkExpander *expander = GTK_EXPANDER (widget);
GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander);
if (!focus_current_site (expander, direction))
{
GtkWidget *old_focus_child;
gboolean widget_is_focus;
FocusSite site = FOCUS_NONE;
widget_is_focus = gtk_widget_is_focus (widget);
old_focus_child = gtk_widget_get_focus_child (GTK_WIDGET (widget));
if (old_focus_child && old_focus_child == priv->label_widget)
site = FOCUS_LABEL;
else if (old_focus_child)
site = FOCUS_CHILD;
else if (widget_is_focus)
site = FOCUS_WIDGET;
while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
{
if (focus_in_site (expander, site, direction))
return TRUE;
}
return FALSE;
}
return TRUE;
}
static void
gtk_expander_add (GtkContainer *container,
GtkWidget *widget)
+14 -9
View File
@@ -82,6 +82,7 @@
#include "gtkdebug.h"
#include "gtkfilechoosererrorstackprivate.h"
#include "gtkentryprivate.h"
#include "gtkroot.h"
#include <cairo-gobject.h>
@@ -340,6 +341,7 @@ struct _GtkFileChooserWidgetPrivate {
GSource *focus_entry_idle;
gulong toplevel_set_focus_id;
GtkWidget *toplevel_current_focus_widget;
GtkWidget *toplevel_last_focus_widget;
gint sort_column;
@@ -1361,7 +1363,7 @@ key_press_cb (GtkEventController *controller,
GtkWidget *default_widget, *focus_widget;
default_widget = gtk_window_get_default_widget (window);
focus_widget = gtk_window_get_focus (window);
focus_widget = gtk_root_get_focus (GTK_ROOT (window));
if (widget != default_widget &&
!(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
@@ -2709,7 +2711,7 @@ location_mode_set (GtkFileChooserWidget *impl,
switch_to_file_list = FALSE;
if (toplevel)
{
current_focus = gtk_window_get_focus (toplevel);
current_focus = gtk_root_get_focus (GTK_ROOT (toplevel));
if (!current_focus || current_focus == priv->location_entry)
switch_to_file_list = TRUE;
}
@@ -3560,13 +3562,14 @@ gtk_file_chooser_widget_dispose (GObject *object)
* widget on our toplevel. See gtk_file_chooser_widget_hierarchy_changed()
*/
static void
toplevel_set_focus_cb (GtkWindow *window,
GtkWidget *focus,
toplevel_set_focus_cb (GtkWindow *window,
GParamSpec *pspec,
GtkFileChooserWidget *impl)
{
GtkFileChooserWidgetPrivate *priv = impl->priv;
priv->toplevel_last_focus_widget = gtk_window_get_focus (window);
priv->toplevel_last_focus_widget = priv->toplevel_current_focus_widget;
priv->toplevel_current_focus_widget = gtk_root_get_focus (GTK_ROOT (window));
}
/* We monitor the focus widget on our toplevel to be able to know which widget
@@ -3584,9 +3587,10 @@ gtk_file_chooser_widget_root (GtkWidget *widget)
toplevel = gtk_widget_get_toplevel (widget);
g_assert (priv->toplevel_set_focus_id == 0);
priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
priv->toplevel_set_focus_id = g_signal_connect (toplevel, "notify::focus-widget",
G_CALLBACK (toplevel_set_focus_cb), impl);
priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
priv->toplevel_last_focus_widget = NULL;
priv->toplevel_current_focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel));
}
static void
@@ -3602,6 +3606,7 @@ gtk_file_chooser_widget_unroot (GtkWidget *widget)
g_signal_handler_disconnect (toplevel, priv->toplevel_set_focus_id);
priv->toplevel_set_focus_id = 0;
priv->toplevel_last_focus_widget = NULL;
priv->toplevel_current_focus_widget = NULL;
}
GTK_WIDGET_CLASS (gtk_file_chooser_widget_parent_class)->unroot (widget);
@@ -5806,7 +5811,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
toplevel = get_toplevel (GTK_WIDGET (impl));
if (toplevel)
current_focus = gtk_window_get_focus (toplevel);
current_focus = gtk_root_get_focus (GTK_ROOT (toplevel));
else
current_focus = NULL;
@@ -6655,7 +6660,7 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserEmbed *chooser_embed)
retval = FALSE;
current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
current_focus = gtk_root_get_focus (GTK_ROOT (toplevel));
if (current_focus == priv->browse_files_tree_view)
{
-157
View File
@@ -303,92 +303,8 @@ gtk_flow_box_child_get_box (GtkFlowBoxChild *child)
return NULL;
}
static void
gtk_flow_box_child_set_focus (GtkFlowBoxChild *child)
{
GtkFlowBox *box = gtk_flow_box_child_get_box (child);
gboolean modify;
gboolean extend;
get_current_selection_modifiers (GTK_WIDGET (box), &modify, &extend);
if (modify)
gtk_flow_box_update_cursor (box, child);
else
gtk_flow_box_update_selection (box, child, FALSE, FALSE);
}
/* GtkWidget implementation {{{2 */
static gboolean
gtk_flow_box_child_focus (GtkWidget *widget,
GtkDirectionType direction)
{
gboolean had_focus = FALSE;
GtkWidget *child;
child = gtk_bin_get_child (GTK_BIN (widget));
/* Without "can-focus" flag try to pass the focus to the child immediately */
if (!gtk_widget_get_can_focus (widget))
{
if (child)
{
if (gtk_widget_child_focus (child, direction))
{
GtkFlowBox *box;
box = gtk_flow_box_child_get_box (GTK_FLOW_BOX_CHILD (widget));
if (box)
gtk_flow_box_update_cursor (box, GTK_FLOW_BOX_CHILD (widget));
return TRUE;
}
}
return FALSE;
}
g_object_get (widget, "has-focus", &had_focus, NULL);
if (had_focus)
{
/* If on row, going right, enter into possible container */
if (child &&
(direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD))
{
if (gtk_widget_child_focus (GTK_WIDGET (child), direction))
return TRUE;
}
return FALSE;
}
else if (gtk_widget_get_focus_child (widget) != NULL)
{
/* Child has focus, always navigate inside it first */
if (gtk_widget_child_focus (child, direction))
return TRUE;
/* If exiting child container to the left, select child */
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
{
gtk_flow_box_child_set_focus (GTK_FLOW_BOX_CHILD (widget));
return TRUE;
}
return FALSE;
}
else
{
/* If coming from the left, enter into possible container */
if (child &&
(direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD))
{
if (gtk_widget_child_focus (child, direction))
return TRUE;
}
gtk_flow_box_child_set_focus (GTK_FLOW_BOX_CHILD (widget));
return TRUE;
}
}
static void
gtk_flow_box_child_activate (GtkFlowBoxChild *child)
{
@@ -422,7 +338,6 @@ gtk_flow_box_child_class_init (GtkFlowBoxChildClass *class)
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
widget_class->get_request_mode = gtk_flow_box_child_get_request_mode;
widget_class->focus = gtk_flow_box_child_focus;
class->activate = gtk_flow_box_child_activate;
@@ -2893,77 +2808,6 @@ gtk_flow_box_child_type (GtkContainer *container)
/* Keynav {{{2 */
static gboolean
gtk_flow_box_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkFlowBox *box = GTK_FLOW_BOX (widget);
GtkWidget *focus_child;
GSequenceIter *iter;
GtkFlowBoxChild *next_focus_child;
/* Without "can-focus" flag fall back to the default behavior immediately */
if (!gtk_widget_get_can_focus (widget))
{
return GTK_WIDGET_CLASS (gtk_flow_box_parent_class)->focus (widget, direction);
}
focus_child = gtk_widget_get_focus_child (widget);
next_focus_child = NULL;
if (focus_child != NULL)
{
if (gtk_widget_child_focus (focus_child, direction))
return TRUE;
iter = CHILD_PRIV (focus_child)->iter;
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
iter = gtk_flow_box_get_previous_focusable (box, iter);
else if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
iter = gtk_flow_box_get_next_focusable (box, iter);
else if (direction == GTK_DIR_UP)
iter = gtk_flow_box_get_above_focusable (box, iter);
else if (direction == GTK_DIR_DOWN)
iter = gtk_flow_box_get_below_focusable (box, iter);
if (iter != NULL)
next_focus_child = g_sequence_get (iter);
}
else
{
if (BOX_PRIV (box)->selected_child)
next_focus_child = BOX_PRIV (box)->selected_child;
else
{
if (direction == GTK_DIR_UP || direction == GTK_DIR_TAB_BACKWARD)
iter = gtk_flow_box_get_last_focusable (box);
else
iter = gtk_flow_box_get_first_focusable (box);
if (iter != NULL)
next_focus_child = g_sequence_get (iter);
}
}
if (next_focus_child == NULL)
{
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN ||
direction == GTK_DIR_LEFT || direction == GTK_DIR_RIGHT)
{
if (gtk_widget_keynav_failed (GTK_WIDGET (box), direction))
return TRUE;
}
return FALSE;
}
if (gtk_widget_child_focus (GTK_WIDGET (next_focus_child), direction))
return TRUE;
return TRUE;
}
static void
gtk_flow_box_add_move_binding (GtkBindingSet *binding_set,
guint keyval,
@@ -3373,7 +3217,6 @@ gtk_flow_box_class_init (GtkFlowBoxClass *class)
widget_class->size_allocate = gtk_flow_box_size_allocate;
widget_class->unmap = gtk_flow_box_unmap;
widget_class->focus = gtk_flow_box_focus;
widget_class->snapshot = gtk_flow_box_snapshot;
widget_class->get_request_mode = gtk_flow_box_get_request_mode;
widget_class->measure = gtk_flow_box_measure;
+25
View File
@@ -46,6 +46,19 @@ gtk_gizmo_snapshot (GtkWidget *widget,
GTK_WIDGET_CLASS (gtk_gizmo_parent_class)->snapshot (widget, snapshot);
}
static GtkWidget *
gtk_gizmo_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction)
{
GtkGizmo *self = GTK_GIZMO (widget);
if (self->focus_func)
return self->focus_func (self, child, direction);
else
return GTK_WIDGET_CLASS (gtk_gizmo_parent_class)->next_focus_child (widget, child, direction);
}
static void
gtk_gizmo_finalize (GObject *object)
{
@@ -76,6 +89,7 @@ gtk_gizmo_class_init (GtkGizmoClass *klass)
widget_class->measure = gtk_gizmo_measure;
widget_class->size_allocate = gtk_gizmo_size_allocate;
widget_class->snapshot = gtk_gizmo_snapshot;
widget_class->next_focus_child = gtk_gizmo_next_focus_child;
}
static void
@@ -89,6 +103,16 @@ gtk_gizmo_new (const char *css_name,
GtkGizmoMeasureFunc measure_func,
GtkGizmoAllocateFunc allocate_func,
GtkGizmoSnapshotFunc snapshot_func)
{
return gtk_gizmo_new_with_focus (css_name, measure_func, allocate_func, snapshot_func, NULL);
}
GtkWidget *
gtk_gizmo_new_with_focus (const char *css_name,
GtkGizmoMeasureFunc measure_func,
GtkGizmoAllocateFunc allocate_func,
GtkGizmoSnapshotFunc snapshot_func,
GtkGizmoFocusFunc focus_func)
{
GtkGizmo *gizmo = GTK_GIZMO (g_object_new (GTK_TYPE_GIZMO,
"css-name", css_name,
@@ -97,6 +121,7 @@ gtk_gizmo_new (const char *css_name,
gizmo->measure_func = measure_func;
gizmo->allocate_func = allocate_func;
gizmo->snapshot_func = snapshot_func;
gizmo->focus_func = focus_func;
return GTK_WIDGET (gizmo);
}
+10 -1
View File
@@ -27,7 +27,9 @@ typedef void (* GtkGizmoAllocateFunc) (GtkGizmo *gizmo,
int baseline);
typedef void (* GtkGizmoSnapshotFunc) (GtkGizmo *gizmo,
GtkSnapshot *snapshot);
typedef GtkWidget * (* GtkGizmoFocusFunc) (GtkGizmo *gizmo,
GtkWidget *child,
GtkDirectionType direction);
struct _GtkGizmo
{
@@ -36,6 +38,7 @@ struct _GtkGizmo
GtkGizmoMeasureFunc measure_func;
GtkGizmoAllocateFunc allocate_func;
GtkGizmoSnapshotFunc snapshot_func;
GtkGizmoFocusFunc focus_func;
};
struct _GtkGizmoClass
@@ -50,5 +53,11 @@ GtkWidget *gtk_gizmo_new (const char *css_name,
GtkGizmoAllocateFunc allocate_func,
GtkGizmoSnapshotFunc snapshot_func);
GtkWidget *gtk_gizmo_new_with_focus (const char *css_name,
GtkGizmoMeasureFunc measure_func,
GtkGizmoAllocateFunc allocate_func,
GtkGizmoSnapshotFunc snapshot_func,
GtkGizmoFocusFunc focus_func);
#endif
+183 -178
View File
@@ -33,6 +33,7 @@
#include "gtkcssshadowsvalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkdnd.h"
#include "gtkeventcontrollerkey.h"
#include "gtkeventcontrollermotion.h"
#include "gtkgesturedrag.h"
#include "gtkgesturemultipress.h"
@@ -413,8 +414,6 @@ static void gtk_label_state_flags_changed (GtkWidget *widget,
static void gtk_label_style_updated (GtkWidget *widget);
static void gtk_label_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot);
static gboolean gtk_label_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_label_realize (GtkWidget *widget);
static void gtk_label_unrealize (GtkWidget *widget);
@@ -525,6 +524,10 @@ static void gtk_label_activate_current_link (GtkLabel *label);
static GtkLabelLink *gtk_label_get_current_link (GtkLabel *label);
static void emit_activate_link (GtkLabel *label,
GtkLabelLink *link);
static gboolean range_is_in_ellipsis (GtkLabel *label,
gint range_start,
gint range_end);
static GtkLabelLink *gtk_label_get_focus_link (GtkLabel *label);
/* Event controller callbacks */
static void gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
@@ -612,7 +615,6 @@ 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->focus = gtk_label_focus;
widget_class->get_request_mode = gtk_label_get_request_mode;
widget_class->measure = gtk_label_measure;
@@ -1122,6 +1124,177 @@ gtk_label_class_init (GtkLabelClass *class)
quark_link = g_quark_from_static_string ("link");
}
static void
focus_in_cb (GtkEventControllerKey *controller,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkWidget *widget)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelSelectionInfo *info = priv->select_info;
if (!info)
return;
if (info->selectable)
{
gboolean select_on_focus;
g_object_get (gtk_widget_get_settings (widget),
"gtk-label-select-on-focus",
&select_on_focus,
NULL);
if (select_on_focus && !priv->in_click)
gtk_label_select_region (label, 0, -1);
}
else
{
if (info->links && !priv->in_click)
{
GList *l;
int i;
for (l = info->links, i = 0; l; l = l->next, i++)
{
GtkLabelLink *link = l->data;
if (!range_is_in_ellipsis (label, link->start, link->end))
{
info->selection_anchor = link->start;
info->selection_end = link->start;
_gtk_label_accessible_focus_link_changed (label);
gtk_widget_queue_draw (widget);
break;
}
}
}
}
}
static gboolean
key_press_cb (GtkEventControllerKey *controller,
guint keyval,
guint keycode,
GdkModifierType modifiers,
GtkWidget *widget)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelSelectionInfo *info = priv->select_info;
GtkDirectionType direction;
GtkLabelLink *focus_link;
GList *l;
if (keyval != GDK_KEY_Tab && keyval != GDK_KEY_KP_Tab)
return FALSE;
if ((modifiers & GDK_SHIFT_MASK) != 0)
direction = GTK_DIR_TAB_BACKWARD;
else
direction = GTK_DIR_TAB_FORWARD;
if (!info)
return FALSE;
if (info->selectable)
{
int index;
int i;
if (info->selection_anchor != info->selection_end)
return FALSE;
index = info->selection_anchor;
if (direction == GTK_DIR_TAB_FORWARD)
{
for (l = info->links, i = 0; l; l = l->next, i++)
{
GtkLabelLink *link = l->data;
if (link->start > index)
{
if (!range_is_in_ellipsis (label, link->start, link->end))
{
gtk_label_select_region_index (label, link->start, link->start);
_gtk_label_accessible_focus_link_changed (label);
gtk_widget_queue_draw (widget);
return TRUE;
}
}
}
}
else
{
for (l = g_list_last (info->links), i = g_list_length (info->links) - 1; l; l = l->prev, i--)
{
GtkLabelLink *link = l->data;
if (link->end < index)
{
if (!range_is_in_ellipsis (label, link->start, link->end))
{
gtk_label_select_region_index (label, link->start, link->start);
_gtk_label_accessible_focus_link_changed (label);
gtk_widget_queue_draw (widget);
return TRUE;
}
}
}
}
}
else
{
focus_link = gtk_label_get_focus_link (label);
if (direction == GTK_DIR_TAB_FORWARD)
{
if (focus_link)
{
l = g_list_find (info->links, focus_link);
l = l->next;
}
else
l = info->links;
for (; l; l = l->next)
{
GtkLabelLink *link = l->data;
if (!range_is_in_ellipsis (label, link->start, link->end))
break;
}
}
else
{
if (focus_link)
{
l = g_list_find (info->links, focus_link);
l = l->prev;
}
else
l = g_list_last (info->links);
for (; l; l = l->prev)
{
GtkLabelLink *link = l->data;
if (!range_is_in_ellipsis (label, link->start, link->end))
break;
}
}
if (l)
{
focus_link = l->data;
info->selection_anchor = focus_link->start;
info->selection_end = focus_link->start;
_gtk_label_accessible_focus_link_changed (label);
gtk_widget_queue_draw (widget);
return TRUE;
}
}
return FALSE;
}
static void
gtk_label_set_property (GObject *object,
guint prop_id,
@@ -1273,6 +1446,7 @@ static void
gtk_label_init (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkEventController *controller;
gtk_widget_set_has_surface (GTK_WIDGET (label), FALSE);
@@ -1303,6 +1477,11 @@ gtk_label_init (GtkLabel *label)
priv->mnemonic_window = NULL;
priv->mnemonics_visible = TRUE;
controller = gtk_event_controller_key_new ();
g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in_cb), label);
g_signal_connect (controller, "key-pressed", G_CALLBACK (key_press_cb), label);
gtk_widget_add_controller (GTK_WIDGET (label), controller);
}
@@ -4260,185 +4439,11 @@ gtk_label_grab_focus (GtkWidget *widget)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
gboolean select_on_focus;
GtkLabelLink *link;
GList *l;
if (priv->select_info == NULL)
return;
GTK_WIDGET_CLASS (gtk_label_parent_class)->grab_focus (widget);
if (priv->select_info->selectable)
{
g_object_get (gtk_widget_get_settings (widget),
"gtk-label-select-on-focus",
&select_on_focus,
NULL);
if (select_on_focus && !priv->in_click)
gtk_label_select_region (label, 0, -1);
}
else
{
if (priv->select_info->links && !priv->in_click)
{
for (l = priv->select_info->links; l; l = l->next)
{
link = l->data;
if (!range_is_in_ellipsis (label, link->start, link->end))
{
priv->select_info->selection_anchor = link->start;
priv->select_info->selection_end = link->start;
_gtk_label_accessible_focus_link_changed (label);
break;
}
}
}
}
}
static gboolean
gtk_label_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelSelectionInfo *info = priv->select_info;
GtkLabelLink *focus_link;
GList *l;
if (!gtk_widget_is_focus (widget))
{
gtk_widget_grab_focus (widget);
if (info)
{
focus_link = gtk_label_get_focus_link (label);
if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
{
for (l = g_list_last (info->links); l; l = l->prev)
{
focus_link = l->data;
if (!range_is_in_ellipsis (label, focus_link->start, focus_link->end))
{
info->selection_anchor = focus_link->start;
info->selection_end = focus_link->start;
_gtk_label_accessible_focus_link_changed (label);
}
}
}
return TRUE;
}
return FALSE;
}
if (!info)
return FALSE;
if (info->selectable)
{
gint index;
if (info->selection_anchor != info->selection_end)
goto out;
index = info->selection_anchor;
if (direction == GTK_DIR_TAB_FORWARD)
for (l = info->links; l; l = l->next)
{
GtkLabelLink *link = l->data;
if (link->start > index)
{
if (!range_is_in_ellipsis (label, link->start, link->end))
{
gtk_label_select_region_index (label, link->start, link->start);
_gtk_label_accessible_focus_link_changed (label);
return TRUE;
}
}
}
else if (direction == GTK_DIR_TAB_BACKWARD)
for (l = g_list_last (info->links); l; l = l->prev)
{
GtkLabelLink *link = l->data;
if (link->end < index)
{
if (!range_is_in_ellipsis (label, link->start, link->end))
{
gtk_label_select_region_index (label, link->start, link->start);
_gtk_label_accessible_focus_link_changed (label);
return TRUE;
}
}
}
goto out;
}
else
{
focus_link = gtk_label_get_focus_link (label);
switch (direction)
{
case GTK_DIR_TAB_FORWARD:
if (focus_link)
{
l = g_list_find (info->links, focus_link);
l = l->next;
}
else
l = info->links;
for (; l; l = l->next)
{
GtkLabelLink *link = l->data;
if (!range_is_in_ellipsis (label, link->start, link->end))
break;
}
break;
case GTK_DIR_TAB_BACKWARD:
if (focus_link)
{
l = g_list_find (info->links, focus_link);
l = l->prev;
}
else
l = g_list_last (info->links);
for (; l; l = l->prev)
{
GtkLabelLink *link = l->data;
if (!range_is_in_ellipsis (label, link->start, link->end))
break;
}
break;
default:
case GTK_DIR_UP:
case GTK_DIR_DOWN:
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
goto out;
}
if (l)
{
focus_link = l->data;
info->selection_anchor = focus_link->start;
info->selection_end = focus_link->start;
_gtk_label_accessible_focus_link_changed (label);
gtk_widget_queue_draw (widget);
return TRUE;
}
}
out:
return FALSE;
}
static void
@@ -6222,7 +6227,7 @@ gtk_label_activate_current_link (GtkLabel *label)
if (window)
{
default_widget = gtk_window_get_default_widget (window);
focus_widget = gtk_window_get_focus (window);
focus_widget = gtk_root_get_focus (GTK_ROOT (window));
if (default_widget != widget &&
!(widget == focus_widget && (!default_widget || !gtk_widget_is_sensitive (default_widget))))
-198
View File
@@ -208,8 +208,6 @@ static void gtk_list_box_update_cursor (GtkListBo
GtkListBoxRow *row,
gboolean grab_focus);
static void gtk_list_box_show (GtkWidget *widget);
static gboolean gtk_list_box_focus (GtkWidget *widget,
GtkDirectionType direction);
static GSequenceIter* gtk_list_box_get_previous_visible (GtkListBox *box,
GSequenceIter *iter);
static GtkListBoxRow *gtk_list_box_get_first_focusable (GtkListBox *box);
@@ -407,7 +405,6 @@ gtk_list_box_class_init (GtkListBoxClass *klass)
object_class->set_property = gtk_list_box_set_property;
object_class->finalize = gtk_list_box_finalize;
widget_class->show = gtk_list_box_show;
widget_class->focus = gtk_list_box_focus;
widget_class->compute_expand = gtk_list_box_compute_expand;
widget_class->get_request_mode = gtk_list_box_get_request_mode;
widget_class->measure = gtk_list_box_measure;
@@ -1870,129 +1867,6 @@ gtk_list_box_show (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_list_box_parent_class)->show (widget);
}
static gboolean
gtk_list_box_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkListBox *box = GTK_LIST_BOX (widget);
GtkListBoxPrivate *priv = BOX_PRIV (box);
GtkWidget *focus_child;
GtkListBoxRow *next_focus_row;
GtkWidget *row;
GtkWidget *header;
focus_child = gtk_widget_get_focus_child (widget);
next_focus_row = NULL;
if (focus_child != NULL)
{
GSequenceIter *i;
if (gtk_widget_child_focus (focus_child, direction))
return TRUE;
if (direction == GTK_DIR_UP || direction == GTK_DIR_TAB_BACKWARD)
{
if (GTK_IS_LIST_BOX_ROW (focus_child))
{
header = ROW_PRIV (GTK_LIST_BOX_ROW (focus_child))->header;
if (header && gtk_widget_child_focus (header, direction))
return TRUE;
}
if (GTK_IS_LIST_BOX_ROW (focus_child))
row = focus_child;
else
row = g_hash_table_lookup (priv->header_hash, focus_child);
if (GTK_IS_LIST_BOX_ROW (row))
i = gtk_list_box_get_previous_visible (box, ROW_PRIV (GTK_LIST_BOX_ROW (row))->iter);
else
i = NULL;
while (i != NULL)
{
if (gtk_widget_get_sensitive (g_sequence_get (i)))
{
next_focus_row = g_sequence_get (i);
break;
}
i = gtk_list_box_get_previous_visible (box, i);
}
}
else if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD)
{
if (GTK_IS_LIST_BOX_ROW (focus_child))
i = gtk_list_box_get_next_visible (box, ROW_PRIV (GTK_LIST_BOX_ROW (focus_child))->iter);
else
{
row = g_hash_table_lookup (priv->header_hash, focus_child);
if (GTK_IS_LIST_BOX_ROW (row))
i = ROW_PRIV (GTK_LIST_BOX_ROW (row))->iter;
else
i = NULL;
}
while (!g_sequence_iter_is_end (i))
{
if (gtk_widget_get_sensitive (g_sequence_get (i)))
{
next_focus_row = g_sequence_get (i);
break;
}
i = gtk_list_box_get_next_visible (box, i);
}
}
}
else
{
/* No current focus row */
switch (direction)
{
case GTK_DIR_UP:
case GTK_DIR_TAB_BACKWARD:
next_focus_row = priv->selected_row;
if (next_focus_row == NULL)
next_focus_row = gtk_list_box_get_last_focusable (box);
break;
case GTK_DIR_DOWN:
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
default:
next_focus_row = priv->selected_row;
if (next_focus_row == NULL)
next_focus_row = gtk_list_box_get_first_focusable (box);
break;
}
}
if (next_focus_row == NULL)
{
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
{
if (gtk_widget_keynav_failed (GTK_WIDGET (box), direction))
return TRUE;
}
return FALSE;
}
if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD)
{
header = ROW_PRIV (next_focus_row)->header;
if (header && gtk_widget_child_focus (header, direction))
return TRUE;
}
if (gtk_widget_child_focus (GTK_WIDGET (next_focus_row), direction))
return TRUE;
return FALSE;
}
static void
list_box_add_visible_rows (GtkListBox *box,
gint n)
@@ -2882,77 +2756,6 @@ gtk_list_box_row_new (void)
return g_object_new (GTK_TYPE_LIST_BOX_ROW, NULL);
}
static void
gtk_list_box_row_set_focus (GtkListBoxRow *row)
{
GtkListBox *box = gtk_list_box_row_get_box (row);
gboolean modify;
gboolean extend;
if (!box)
return;
get_current_selection_modifiers (GTK_WIDGET (row), &modify, &extend);
if (modify)
gtk_list_box_update_cursor (box, row, TRUE);
else
gtk_list_box_update_selection (box, row, FALSE, FALSE);
}
static gboolean
gtk_list_box_row_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
gboolean had_focus = FALSE;
GtkWidget *child;
child = gtk_bin_get_child (GTK_BIN (widget));
g_object_get (widget, "has-focus", &had_focus, NULL);
if (had_focus)
{
/* If on row, going right, enter into possible container */
if (child &&
(direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD))
{
if (gtk_widget_child_focus (GTK_WIDGET (child), direction))
return TRUE;
}
return FALSE;
}
else if (gtk_widget_get_focus_child (widget) != NULL)
{
/* Child has focus, always navigate inside it first */
if (gtk_widget_child_focus (gtk_widget_get_focus_child (widget), direction))
return TRUE;
/* If exiting child container to the left, select row */
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
{
gtk_list_box_row_set_focus (row);
return TRUE;
}
return FALSE;
}
else
{
/* If coming from the left, enter into possible container */
if (child &&
(direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD))
{
if (gtk_widget_child_focus (child, direction))
return TRUE;
}
gtk_list_box_row_set_focus (row);
return TRUE;
}
}
static void
gtk_list_box_row_activate (GtkListBoxRow *row)
{
@@ -3384,7 +3187,6 @@ gtk_list_box_row_class_init (GtkListBoxRowClass *klass)
widget_class->show = gtk_list_box_row_show;
widget_class->hide = gtk_list_box_row_hide;
widget_class->focus = gtk_list_box_row_focus;
widget_class->grab_focus = gtk_list_box_row_grab_focus;
klass->activate = gtk_list_box_row_activate;
+105 -48
View File
@@ -1407,62 +1407,83 @@ static void
synth_crossing (GtkWidget *widget,
GtkWidget *toplevel,
gboolean enter,
GtkWidget *other_widget,
GtkWidget *target,
GtkWidget *related_target,
GdkEvent *source,
GdkNotifyType notify_type,
GdkCrossingMode crossing_mode)
{
GdkEvent *event;
gdouble x, y;
GtkStateFlags flags;
event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY);
gdk_event_set_user_data (event, G_OBJECT (widget));
if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE)
{
event = gdk_event_new (GDK_FOCUS_CHANGE);
event->focus_change.in = enter;
event->focus_change.mode = crossing_mode;
event->focus_change.detail = notify_type;
flags = GTK_STATE_FLAG_FOCUSED;
if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel)))
flags |= GTK_STATE_FLAG_FOCUS_VISIBLE;
}
else
{
gdouble x, y;
event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY);
if (related_target)
event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (related_target));
gdk_event_get_coords (source, &x, &y);
event->crossing.x = x;
event->crossing.y = y;
event->crossing.mode = crossing_mode;
event->crossing.detail = notify_type;
flags = GTK_STATE_FLAG_PRELIGHT;
}
gdk_event_set_target (event, G_OBJECT (target));
gdk_event_set_related_target (event, G_OBJECT (related_target));
gdk_event_set_device (event, gdk_event_get_device (source));
gdk_event_set_source_device (event, gdk_event_get_source_device (source));
event->any.surface = g_object_ref (gtk_widget_get_surface (toplevel));
if (other_widget)
event->crossing.child_surface = g_object_ref (gtk_widget_get_surface (other_widget));
event->any.surface = gtk_widget_get_surface (toplevel);
if (event->any.surface)
g_object_ref (event->any.surface);
if (enter)
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
gtk_widget_set_state_flags (widget, flags, FALSE);
else
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
gtk_widget_unset_state_flags (widget, flags);
gdk_event_get_coords (source, &x, &y);
event->crossing.x = x;
event->crossing.y = y;
event->crossing.mode = crossing_mode;
event->crossing.detail = notify_type;
if (gdk_event_get_event_type (source) == GDK_FOCUS_CHANGE)
{
/* maintain focus chain */
if (enter || notify_type == GDK_NOTIFY_INFERIOR)
{
GtkWidget *parent = gtk_widget_get_parent (widget);
if (parent)
gtk_widget_set_focus_child (parent, widget);
}
else if (!enter && notify_type != GDK_NOTIFY_INFERIOR)
{
GtkWidget *parent = gtk_widget_get_parent (widget);
if (parent)
gtk_widget_set_focus_child (parent, NULL);
}
/* maintain widget state */
if (notify_type == GDK_NOTIFY_ANCESTOR ||
notify_type == GDK_NOTIFY_INFERIOR ||
notify_type == GDK_NOTIFY_NONLINEAR)
gtk_widget_set_has_focus (widget, enter);
}
gtk_widget_event (widget, event);
g_object_unref (event);
}
static GtkWidget *
update_pointer_focus_state (GtkWindow *toplevel,
GdkEvent *event,
GtkWidget *new_target)
{
GtkWidget *old_target = NULL;
GdkEventSequence *sequence;
GdkDevice *device;
gdouble x, y;
device = gdk_event_get_device (event);
sequence = gdk_event_get_event_sequence (event);
old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence);
if (old_target == new_target)
return old_target;
gdk_event_get_coords (event, &x, &y);
gtk_window_update_pointer_focus (toplevel, device, sequence,
new_target, x, y);
return old_target;
}
static void
void
gtk_synthesize_crossing_events (GtkWindow *toplevel,
GtkWidget *old_target,
GtkWidget *new_target,
@@ -1492,13 +1513,16 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel,
{
widget = old_target;
while (widget != ancestor)
while (widget)
{
notify_type = (widget == old_target) ?
leave_type : get_virtual_notify_type (leave_type);
synth_crossing (widget, GTK_WIDGET (toplevel), FALSE,
new_target, event, notify_type, mode);
if (widget != ancestor || widget == old_target)
synth_crossing (widget, GTK_WIDGET (toplevel), FALSE,
old_target, new_target, event, notify_type, mode);
if (widget == ancestor)
break;
widget = gtk_widget_get_parent (widget);
}
}
@@ -1509,9 +1533,11 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel,
widget = new_target;
while (widget != ancestor)
while (widget)
{
widgets = g_slist_prepend (widgets, widget);
if (widget == ancestor)
break;
widget = gtk_widget_get_parent (widget);
}
@@ -1522,12 +1548,37 @@ gtk_synthesize_crossing_events (GtkWindow *toplevel,
notify_type = (widget == new_target) ?
enter_type : get_virtual_notify_type (enter_type);
synth_crossing (widget, GTK_WIDGET (toplevel), TRUE,
old_target, event, notify_type, mode);
if (widget != ancestor || widget == new_target)
synth_crossing (widget, GTK_WIDGET (toplevel), TRUE,
new_target, old_target, event, notify_type, mode);
}
}
}
static GtkWidget *
update_pointer_focus_state (GtkWindow *toplevel,
GdkEvent *event,
GtkWidget *new_target)
{
GtkWidget *old_target = NULL;
GdkEventSequence *sequence;
GdkDevice *device;
gdouble x, y;
device = gdk_event_get_device (event);
sequence = gdk_event_get_event_sequence (event);
old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence);
if (old_target == new_target)
return old_target;
gdk_event_get_coords (event, &x, &y);
gtk_window_update_pointer_focus (toplevel, device, sequence,
new_target, x, y);
return old_target;
}
static gboolean
is_pointing_event (GdkEvent *event)
{
@@ -1761,7 +1812,7 @@ gtk_main_do_event (GdkEvent *event)
if (is_pointing_event (event))
target_widget = handle_pointing_event (event);
else if (GTK_IS_WINDOW (target_widget) &&
else if (GTK_IS_ROOT (target_widget) &&
(event->any.type == GDK_KEY_PRESS ||
event->any.type == GDK_KEY_RELEASE))
{
@@ -1771,7 +1822,7 @@ gtk_main_do_event (GdkEvent *event)
gtk_window_activate_key (GTK_WINDOW (target_widget), (GdkEventKey *) event))
goto cleanup;
focus_widget = gtk_window_get_focus (GTK_WINDOW (target_widget));
focus_widget = gtk_root_get_focus (GTK_ROOT (target_widget));
if (focus_widget)
target_widget = focus_widget;
}
@@ -1779,7 +1830,7 @@ gtk_main_do_event (GdkEvent *event)
if (!target_widget)
goto cleanup;
gdk_event_set_user_data (event, G_OBJECT (target_widget));
gdk_event_set_target (event, G_OBJECT (target_widget));
window_group = gtk_main_get_window_group (target_widget);
device = gdk_event_get_device (event);
@@ -2398,7 +2449,13 @@ gtk_get_event_widget (const GdkEvent *event)
GtkWidget *
gtk_get_event_target (const GdkEvent *event)
{
return GTK_WIDGET (gdk_event_get_user_data (event));
return GTK_WIDGET (gdk_event_get_target (event));
}
GtkWidget *
gtk_get_event_related_target (const GdkEvent *event)
{
return GTK_WIDGET (gdk_event_get_related_target (event));
}
/**
+3
View File
@@ -155,6 +155,9 @@ GtkWidget *gtk_get_event_widget (const GdkEvent *event);
GDK_AVAILABLE_IN_ALL
GtkWidget *gtk_get_event_target (const GdkEvent *event);
GDK_AVAILABLE_IN_ALL
GtkWidget *gtk_get_event_related_target (const GdkEvent *event);
GDK_AVAILABLE_IN_ALL
GtkWidget *gtk_get_event_target_with_type (GdkEvent *event,
GType type);
+9 -7
View File
@@ -251,8 +251,9 @@ static void gtk_menu_handle_scrolling (GtkMenu *menu,
gint event_y,
gboolean enter,
gboolean motion);
static gboolean gtk_menu_focus (GtkWidget *widget,
GtkDirectionType direction);
static GtkWidget *gtk_menu_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction);
static gint gtk_menu_get_popup_delay (GtkMenuShell *menu_shell);
static void gtk_menu_move_current (GtkMenuShell *menu_shell,
GtkMenuDirectionType direction);
@@ -492,7 +493,7 @@ gtk_menu_class_init (GtkMenuClass *class)
widget_class->size_allocate = gtk_menu_size_allocate;
widget_class->show = gtk_menu_show;
widget_class->snapshot = gtk_menu_snapshot;
widget_class->focus = gtk_menu_focus;
widget_class->next_focus_child = gtk_menu_next_focus_child;
widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
widget_class->grab_notify = gtk_menu_grab_notify;
widget_class->measure = gtk_menu_measure;
@@ -2254,12 +2255,13 @@ gtk_menu_realize (GtkWidget *widget)
GTK_MENU_SHELL (widget)->priv->active_menu_item);
}
static gboolean
gtk_menu_focus (GtkWidget *widget,
GtkDirectionType direction)
static GtkWidget *
gtk_menu_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction)
{
/* A menu or its menu items cannot have focus */
return FALSE;
return NULL;
}
static GdkSurface *
+62 -329
View File
@@ -211,7 +211,6 @@ struct _GtkNotebookPrivate
guint32 timer;
guint child_has_focus : 1;
guint click_child : 3;
guint remove_in_detach : 1;
guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
@@ -303,7 +302,6 @@ struct _GtkNotebookPage
GtkWidget *child;
GtkWidget *tab_label;
GtkWidget *menu_label;
GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
GtkWidget *tab_widget; /* widget used for the tab itself */
@@ -645,8 +643,6 @@ static void gtk_notebook_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static void gtk_notebook_state_flags_changed (GtkWidget *widget,
GtkStateFlags previous_state);
static gboolean gtk_notebook_focus (GtkWidget *widget,
GtkDirectionType direction);
/*** Drag and drop Methods ***/
static void gtk_notebook_drag_begin (GtkWidget *widget,
@@ -678,8 +674,6 @@ static void gtk_notebook_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_notebook_remove (GtkContainer *container,
GtkWidget *widget);
static void gtk_notebook_set_focus_child (GtkContainer *container,
GtkWidget *child);
static GType gtk_notebook_child_type (GtkContainer *container);
static void gtk_notebook_forall (GtkContainer *container,
GtkCallback callback,
@@ -710,6 +704,9 @@ static void gtk_notebook_allocate_tabs (GtkGizmo *gizmo,
int baseline);
static void gtk_notebook_snapshot_tabs (GtkGizmo *gizmo,
GtkSnapshot *snapshot);
static GtkWidget *gtk_notebook_next_focus_child_tabs (GtkGizmo *gizmo,
GtkWidget *child,
GtkDirectionType direction);
/*** GtkNotebook Private Functions ***/
static void gtk_notebook_real_remove (GtkNotebook *notebook,
@@ -923,7 +920,6 @@ gtk_notebook_class_init (GtkNotebookClass *class)
widget_class->popup_menu = gtk_notebook_popup_menu;
widget_class->grab_notify = gtk_notebook_grab_notify;
widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
widget_class->focus = gtk_notebook_focus;
widget_class->drag_begin = gtk_notebook_drag_begin;
widget_class->drag_end = gtk_notebook_drag_end;
widget_class->drag_motion = gtk_notebook_drag_motion;
@@ -937,7 +933,6 @@ gtk_notebook_class_init (GtkNotebookClass *class)
container_class->add = gtk_notebook_add;
container_class->remove = gtk_notebook_remove;
container_class->forall = gtk_notebook_forall;
container_class->set_focus_child = gtk_notebook_set_focus_child;
container_class->child_type = gtk_notebook_child_type;
class->switch_page = gtk_notebook_real_switch_page;
@@ -1240,7 +1235,6 @@ gtk_notebook_init (GtkNotebook *notebook)
GtkEventController *controller;
GtkGesture *gesture;
gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
gtk_widget_set_has_surface (GTK_WIDGET (notebook), FALSE);
notebook->priv = gtk_notebook_get_instance_private (notebook);
@@ -1258,7 +1252,6 @@ gtk_notebook_init (GtkNotebook *notebook)
priv->scrollable = FALSE;
priv->click_child = ARROW_NONE;
priv->need_timer = 0;
priv->child_has_focus = FALSE;
priv->focus_out = FALSE;
priv->group = 0;
@@ -1291,10 +1284,11 @@ gtk_notebook_init (GtkNotebook *notebook)
gtk_widget_hide (priv->header_widget);
gtk_container_add (GTK_CONTAINER (priv->box), priv->header_widget);
priv->tabs_widget = gtk_gizmo_new ("tabs",
gtk_notebook_measure_tabs,
gtk_notebook_allocate_tabs,
gtk_notebook_snapshot_tabs);
priv->tabs_widget = gtk_gizmo_new_with_focus ("tabs",
gtk_notebook_measure_tabs,
gtk_notebook_allocate_tabs,
gtk_notebook_snapshot_tabs,
gtk_notebook_next_focus_child_tabs);
gtk_widget_set_hexpand (priv->tabs_widget, TRUE);
gtk_container_add (GTK_CONTAINER (priv->header_widget), priv->tabs_widget);
@@ -3215,13 +3209,7 @@ gtk_notebook_switch_tab_timeout (gpointer data)
priv->switch_tab = NULL;
if (switch_tab)
{
/* FIXME: hack, we don't want the
* focus to move fom the source widget
*/
priv->child_has_focus = FALSE;
gtk_notebook_switch_focus_tab (notebook, switch_tab);
}
gtk_notebook_switch_focus_tab (notebook, switch_tab);
return FALSE;
}
@@ -3480,8 +3468,6 @@ gtk_notebook_drag_data_received (GtkWidget *widget,
*
* gtk_notebook_add
* gtk_notebook_remove
* gtk_notebook_focus
* gtk_notebook_set_focus_child
* gtk_notebook_child_type
* gtk_notebook_forall
*/
@@ -3546,7 +3532,6 @@ focus_tabs_in (GtkNotebook *notebook)
if (priv->show_tabs && gtk_notebook_has_current_page (notebook))
{
gtk_widget_grab_focus (GTK_WIDGET (notebook));
gtk_notebook_set_focus_child (GTK_CONTAINER (notebook), NULL);
gtk_notebook_switch_focus_tab (notebook,
g_list_find (priv->children,
priv->cur_page));
@@ -3557,30 +3542,6 @@ focus_tabs_in (GtkNotebook *notebook)
return FALSE;
}
static gboolean
focus_tabs_move (GtkNotebook *notebook,
GtkDirectionType direction,
gint search_direction)
{
GtkNotebookPrivate *priv = notebook->priv;
GList *new_page;
new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
search_direction, TRUE);
if (!new_page)
{
new_page = gtk_notebook_search_page (notebook, NULL,
search_direction, TRUE);
}
if (new_page)
gtk_notebook_switch_focus_tab (notebook, new_page);
else
gtk_widget_error_bell (GTK_WIDGET (notebook));
return TRUE;
}
static gboolean
focus_child_in (GtkNotebook *notebook,
GtkDirectionType direction)
@@ -3593,269 +3554,6 @@ focus_child_in (GtkNotebook *notebook,
return FALSE;
}
static gboolean
focus_action_in (GtkNotebook *notebook,
gint action,
GtkDirectionType direction)
{
GtkNotebookPrivate *priv = notebook->priv;
if (priv->action_widget[action] &&
gtk_widget_get_visible (priv->action_widget[action]))
return gtk_widget_child_focus (priv->action_widget[action], direction);
else
return FALSE;
}
/* Focus in the notebook can either be on the pages, or on
* the tabs or on the action_widgets.
*/
static gboolean
gtk_notebook_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
GtkNotebookPrivate *priv = notebook->priv;
GtkWidget *old_focus_child;
GtkDirectionType effective_direction;
gint first_action;
gint last_action;
gboolean widget_is_focus;
if (priv->tab_pos == GTK_POS_TOP ||
priv->tab_pos == GTK_POS_LEFT)
{
first_action = ACTION_WIDGET_START;
last_action = ACTION_WIDGET_END;
}
else
{
first_action = ACTION_WIDGET_END;
last_action = ACTION_WIDGET_START;
}
if (priv->focus_out)
{
priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
return FALSE;
}
widget_is_focus = gtk_widget_is_focus (widget);
old_focus_child = gtk_widget_get_focus_child (widget);
effective_direction = get_effective_direction (notebook, direction);
if (old_focus_child) /* Focus on page child or action widget */
{
if (gtk_widget_child_focus (old_focus_child, direction))
return TRUE;
if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
{
switch ((guint) effective_direction)
{
case GTK_DIR_DOWN:
return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
case GTK_DIR_RIGHT:
return focus_tabs_in (notebook);
case GTK_DIR_LEFT:
return FALSE;
case GTK_DIR_UP:
return FALSE;
default:
switch ((guint) direction)
{
case GTK_DIR_TAB_FORWARD:
if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
focus_child_in (notebook, direction))
return TRUE;
return focus_tabs_in (notebook);
case GTK_DIR_TAB_BACKWARD:
return FALSE;
default:
g_assert_not_reached ();
break;
}
}
}
else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
{
switch ((guint) effective_direction)
{
case GTK_DIR_DOWN:
return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
case GTK_DIR_RIGHT:
return FALSE;
case GTK_DIR_LEFT:
return focus_tabs_in (notebook);
case GTK_DIR_UP:
return FALSE;
default:
switch ((guint) direction)
{
case GTK_DIR_TAB_FORWARD:
return FALSE;
case GTK_DIR_TAB_BACKWARD:
if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
focus_child_in (notebook, direction))
return TRUE;
return focus_tabs_in (notebook);
default:
g_assert_not_reached ();
break;
}
}
}
else
{
switch ((guint) effective_direction)
{
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_UP:
/* Focus onto the tabs */
return focus_tabs_in (notebook);
case GTK_DIR_DOWN:
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
return FALSE;
case GTK_DIR_TAB_FORWARD:
return focus_action_in (notebook, last_action, direction);
default:
break;
}
}
}
else if (widget_is_focus) /* Focus was on tabs */
{
switch ((guint) effective_direction)
{
case GTK_DIR_TAB_BACKWARD:
return focus_action_in (notebook, first_action, direction);
case GTK_DIR_UP:
return FALSE;
case GTK_DIR_TAB_FORWARD:
if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
return TRUE;
return focus_action_in (notebook, last_action, direction);
case GTK_DIR_DOWN:
/* We use TAB_FORWARD rather than direction so that we focus a more
* predictable widget for the user; users may be using arrow focusing
* in this situation even if they don't usually use arrow focusing.
*/
return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
case GTK_DIR_LEFT:
return focus_tabs_move (notebook, direction, STEP_PREV);
case GTK_DIR_RIGHT:
return focus_tabs_move (notebook, direction, STEP_NEXT);
default:
break;
}
}
else /* Focus was not on widget */
{
switch ((guint) effective_direction)
{
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_DOWN:
if (focus_action_in (notebook, first_action, direction))
return TRUE;
if (focus_tabs_in (notebook))
return TRUE;
if (focus_action_in (notebook, last_action, direction))
return TRUE;
if (focus_child_in (notebook, direction))
return TRUE;
return FALSE;
case GTK_DIR_TAB_BACKWARD:
if (focus_action_in (notebook, last_action, direction))
return TRUE;
if (focus_child_in (notebook, direction))
return TRUE;
if (focus_tabs_in (notebook))
return TRUE;
if (focus_action_in (notebook, first_action, direction))
return TRUE;
case GTK_DIR_UP:
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
return focus_child_in (notebook, direction);
default:
break;
}
}
g_assert_not_reached ();
return FALSE;
}
static void
gtk_notebook_set_focus_child (GtkContainer *container,
GtkWidget *child)
{
GtkNotebook *notebook = GTK_NOTEBOOK (container);
GtkNotebookPrivate *priv = notebook->priv;
GtkWidget *page_child;
GtkWidget *toplevel;
/* If the old focus widget was within a page of the notebook,
* (child may either be NULL or not in this case), record it
* for future use if we switch to the page with a mnemonic.
*/
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
if (toplevel && gtk_widget_is_toplevel (toplevel))
{
page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
while (page_child)
{
if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
{
GList *list = gtk_notebook_find_child (notebook, page_child);
if (list != NULL)
{
GtkNotebookPage *page = list->data;
if (page->last_focus_child)
g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
break;
}
}
page_child = gtk_widget_get_parent (page_child);
}
}
if (child)
{
g_return_if_fail (GTK_IS_WIDGET (child));
priv->child_has_focus = TRUE;
if (!priv->focus_tab)
{
GList *children;
GtkNotebookPage *page;
children = priv->children;
while (children)
{
page = children->data;
if (page->child == child || page->tab_label == child)
gtk_notebook_switch_focus_tab (notebook, children);
children = children->next;
}
}
}
else
priv->child_has_focus = FALSE;
GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
}
static void
gtk_notebook_forall (GtkContainer *container,
GtkCallback callback,
@@ -4305,12 +4003,6 @@ gtk_notebook_real_remove (GtkNotebook *notebook,
g_list_free (list);
if (page->last_focus_child)
{
g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
page->last_focus_child = NULL;
}
gtk_widget_unparent (page->tab_widget);
g_object_unref (page);
@@ -4550,6 +4242,27 @@ gtk_notebook_snapshot_tabs (GtkGizmo *gizmo,
gtk_widget_snapshot_child (GTK_WIDGET (gizmo), priv->cur_page->tab_widget, snapshot);
}
static GtkWidget *
gtk_notebook_next_focus_child_tabs (GtkGizmo *gizmo,
GtkWidget *child,
GtkDirectionType direction)
{
GtkWidget *widget = gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (gizmo)));
GtkNotebook *notebook = GTK_NOTEBOOK (gtk_widget_get_parent (widget));
GtkNotebookPrivate *priv = notebook->priv;
if (direction == GTK_DIR_TAB_FORWARD ||
direction == GTK_DIR_TAB_BACKWARD)
{
if (child == NULL)
return priv->cur_page->tab_widget;
else
return NULL;
}
return gtk_widget_next_focus_child (GTK_WIDGET (gizmo), child, direction);
}
/* Private GtkNotebook Size Allocate Functions:
*
* gtk_notebook_calculate_shown_tabs
@@ -5405,6 +5118,25 @@ gtk_notebook_calc_tabs (GtkNotebook *notebook,
}
}
static GtkWidget *
find_last_focus (GtkWidget *widget)
{
GtkWidget *f = widget;
while (f)
{
GtkWidget *focus_child = gtk_widget_get_focus_child (f);
if (focus_child == NULL)
break;
f = focus_child;
}
if (f != widget)
return f;
return NULL;
}
/* Private GtkNotebook Page Switch Methods:
*
* gtk_notebook_real_switch_page
@@ -5417,16 +5149,19 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook,
GtkNotebookPrivate *priv = notebook->priv;
GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child));
GtkNotebookPage *page = GTK_NOTEBOOK_PAGE_FROM_LIST (list);
gboolean child_has_focus;
gboolean child_has_focus = FALSE;
if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
return;
/* save the value here, changing visibility changes focus */
child_has_focus = priv->child_has_focus;
if (priv->cur_page)
gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED);
{
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (notebook));
GtkWidget *focus = gtk_root_get_focus (root);
if (focus)
child_has_focus = gtk_widget_is_ancestor (focus, priv->cur_page->child);
gtk_widget_unset_state_flags (priv->cur_page->tab_widget, GTK_STATE_FLAG_CHECKED);
}
priv->cur_page = page;
gtk_widget_set_state_flags (page->tab_widget, GTK_STATE_FLAG_CHECKED, FALSE);
@@ -5446,12 +5181,11 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook,
*/
if (child_has_focus)
{
if (priv->cur_page->last_focus_child &&
gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
gtk_widget_grab_focus (priv->cur_page->last_focus_child);
else
if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
gtk_widget_grab_focus (GTK_WIDGET (notebook));
GtkWidget *last_focus = find_last_focus (priv->cur_page->child);
if (last_focus)
gtk_widget_grab_focus (last_focus);
else if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
gtk_widget_grab_focus (GTK_WIDGET (notebook));
}
update_arrow_state (notebook);
@@ -7331,4 +7065,3 @@ gtk_notebook_get_pages (GtkNotebook *notebook)
return priv->pages;
}
+26 -137
View File
@@ -132,8 +132,6 @@ typedef struct
GtkPaned *first_paned;
GtkWidget *child1;
GtkWidget *child2;
GtkWidget *last_child1_focus;
GtkWidget *last_child2_focus;
GtkWidget *saved_focus;
GtkOrientation orientation;
@@ -218,8 +216,6 @@ static void gtk_paned_size_allocate (GtkWidget *widget,
int height,
int baseline);
static void gtk_paned_unrealize (GtkWidget *widget);
static gboolean gtk_paned_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_paned_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_paned_remove (GtkContainer *container,
@@ -231,16 +227,10 @@ static void gtk_paned_calc_position (GtkPaned *paned,
gint allocation,
gint child1_req,
gint child2_req);
static void gtk_paned_set_focus_child (GtkContainer *container,
GtkWidget *child);
static void gtk_paned_set_saved_focus (GtkPaned *paned,
GtkWidget *widget);
static void gtk_paned_set_first_paned (GtkPaned *paned,
GtkPaned *first_paned);
static void gtk_paned_set_last_child1_focus (GtkPaned *paned,
GtkWidget *widget);
static void gtk_paned_set_last_child2_focus (GtkPaned *paned,
GtkWidget *widget);
static gboolean gtk_paned_cycle_child_focus (GtkPaned *paned,
gboolean reverse);
static gboolean gtk_paned_cycle_handle_focus (GtkPaned *paned,
@@ -365,14 +355,12 @@ gtk_paned_class_init (GtkPanedClass *class)
widget_class->measure = gtk_paned_measure;
widget_class->size_allocate = gtk_paned_size_allocate;
widget_class->unrealize = gtk_paned_unrealize;
widget_class->focus = gtk_paned_focus;
widget_class->pick = gtk_paned_pick;
container_class->add = gtk_paned_add;
container_class->remove = gtk_paned_remove;
container_class->forall = gtk_paned_forall;
container_class->child_type = gtk_paned_child_type;
container_class->set_focus_child = gtk_paned_set_focus_child;
container_class->set_child_property = gtk_paned_set_child_property;
container_class->get_child_property = gtk_paned_get_child_property;
@@ -1353,8 +1341,6 @@ gtk_paned_unrealize (GtkWidget *widget)
{
GtkPaned *paned = GTK_PANED (widget);
gtk_paned_set_last_child1_focus (paned, NULL);
gtk_paned_set_last_child2_focus (paned, NULL);
gtk_paned_set_saved_focus (paned, NULL);
gtk_paned_set_first_paned (paned, NULL);
@@ -1410,8 +1396,6 @@ gtk_paned_init (GtkPaned *paned)
priv->position_set = FALSE;
priv->last_allocation = -1;
priv->last_child1_focus = NULL;
priv->last_child2_focus = NULL;
priv->in_recursion = FALSE;
priv->original_position = -1;
priv->max_position = G_MAXINT;
@@ -1498,24 +1482,6 @@ update_drag (GtkPaned *paned,
gtk_paned_set_position (paned, size);
}
static gboolean
gtk_paned_focus (GtkWidget *widget,
GtkDirectionType direction)
{
gboolean retval;
/* This is a hack, but how can this be done without
* excessive cut-and-paste from gtkcontainer.c?
*/
gtk_widget_set_can_focus (widget, FALSE);
retval = GTK_WIDGET_CLASS (gtk_paned_parent_class)->focus (widget, direction);
gtk_widget_set_can_focus (widget, TRUE);
return retval;
}
/**
* gtk_paned_new:
* @orientation: the paneds orientation.
@@ -1860,86 +1826,23 @@ gtk_paned_set_first_paned (GtkPaned *paned, GtkPaned *first_paned)
(gpointer *)&(priv->first_paned));
}
static void
gtk_paned_set_last_child1_focus (GtkPaned *paned, GtkWidget *widget)
{
GtkPanedPrivate *priv = gtk_paned_get_instance_private (paned);
if (priv->last_child1_focus)
g_object_remove_weak_pointer (G_OBJECT (priv->last_child1_focus),
(gpointer *)&(priv->last_child1_focus));
priv->last_child1_focus = widget;
if (priv->last_child1_focus)
g_object_add_weak_pointer (G_OBJECT (priv->last_child1_focus),
(gpointer *)&(priv->last_child1_focus));
}
static void
gtk_paned_set_last_child2_focus (GtkPaned *paned, GtkWidget *widget)
{
GtkPanedPrivate *priv = gtk_paned_get_instance_private (paned);
if (priv->last_child2_focus)
g_object_remove_weak_pointer (G_OBJECT (priv->last_child2_focus),
(gpointer *)&(priv->last_child2_focus));
priv->last_child2_focus = widget;
if (priv->last_child2_focus)
g_object_add_weak_pointer (G_OBJECT (priv->last_child2_focus),
(gpointer *)&(priv->last_child2_focus));
}
static GtkWidget *
paned_get_focus_widget (GtkPaned *paned)
find_last_focus (GtkWidget *widget)
{
GtkWidget *toplevel;
GtkWidget *f = widget;
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
if (gtk_widget_is_toplevel (toplevel))
return gtk_window_get_focus (GTK_WINDOW (toplevel));
return NULL;
}
static void
gtk_paned_set_focus_child (GtkContainer *container,
GtkWidget *focus_child)
{
GtkPaned *paned = GTK_PANED (container);
GtkPanedPrivate *priv = gtk_paned_get_instance_private (paned);
GtkWidget *container_focus_child;
g_return_if_fail (GTK_IS_PANED (container));
if (focus_child == NULL)
while (f)
{
GtkWidget *last_focus;
GtkWidget *w;
last_focus = paned_get_focus_widget (paned);
if (last_focus)
{
/* If there is one or more paned widgets between us and the
* focus widget, we want the topmost of those as last_focus
*/
for (w = last_focus; w != GTK_WIDGET (paned); w = gtk_widget_get_parent (w))
if (GTK_IS_PANED (w))
last_focus = w;
container_focus_child = gtk_widget_get_focus_child (GTK_WIDGET (container));
if (container_focus_child == priv->child1)
gtk_paned_set_last_child1_focus (paned, last_focus);
else if (container_focus_child == priv->child2)
gtk_paned_set_last_child2_focus (paned, last_focus);
}
GtkWidget *focus_child = gtk_widget_get_focus_child (f);
if (focus_child == NULL)
break;
f = focus_child;
}
if (GTK_CONTAINER_CLASS (gtk_paned_parent_class)->set_focus_child)
GTK_CONTAINER_CLASS (gtk_paned_parent_class)->set_focus_child (container, focus_child);
if (f != widget)
return f;
return NULL;
}
static void
@@ -1954,24 +1857,14 @@ gtk_paned_get_cycle_chain (GtkPaned *paned,
GtkWidget *widget = GTK_WIDGET (paned);
GList *temp_list = NULL;
GList *list;
GtkWidget *last_child1_focus;
GtkWidget *last_child2_focus;
if (priv->in_recursion)
return;
g_assert (widgets != NULL);
if (priv->last_child1_focus &&
!gtk_widget_is_ancestor (priv->last_child1_focus, widget))
{
gtk_paned_set_last_child1_focus (paned, NULL);
}
if (priv->last_child2_focus &&
!gtk_widget_is_ancestor (priv->last_child2_focus, widget))
{
gtk_paned_set_last_child2_focus (paned, NULL);
}
parent = gtk_widget_get_parent (widget);
if (parent)
ancestor = gtk_widget_get_ancestor (parent, GTK_TYPE_PANED);
@@ -1984,26 +1877,28 @@ gtk_paned_get_cycle_chain (GtkPaned *paned,
* priv->last_child?_focus before priv->child?, both when we
* are going forward and backward.
*/
last_child1_focus = find_last_focus (priv->child1);
last_child2_focus = find_last_focus (priv->child2);
focus_child = gtk_widget_get_focus_child (GTK_WIDGET (paned));
if (direction == GTK_DIR_TAB_FORWARD)
{
if (focus_child == priv->child1)
{
temp_list = g_list_append (temp_list, priv->last_child2_focus);
temp_list = g_list_append (temp_list, last_child2_focus);
temp_list = g_list_append (temp_list, priv->child2);
temp_list = g_list_append (temp_list, ancestor);
}
else if (focus_child == priv->child2)
{
temp_list = g_list_append (temp_list, ancestor);
temp_list = g_list_append (temp_list, priv->last_child1_focus);
temp_list = g_list_append (temp_list, last_child1_focus);
temp_list = g_list_append (temp_list, priv->child1);
}
else
{
temp_list = g_list_append (temp_list, priv->last_child1_focus);
temp_list = g_list_append (temp_list, last_child1_focus);
temp_list = g_list_append (temp_list, priv->child1);
temp_list = g_list_append (temp_list, priv->last_child2_focus);
temp_list = g_list_append (temp_list, last_child2_focus);
temp_list = g_list_append (temp_list, priv->child2);
temp_list = g_list_append (temp_list, ancestor);
}
@@ -2013,20 +1908,20 @@ gtk_paned_get_cycle_chain (GtkPaned *paned,
if (focus_child == priv->child1)
{
temp_list = g_list_append (temp_list, ancestor);
temp_list = g_list_append (temp_list, priv->last_child2_focus);
temp_list = g_list_append (temp_list, last_child2_focus);
temp_list = g_list_append (temp_list, priv->child2);
}
else if (focus_child == priv->child2)
{
temp_list = g_list_append (temp_list, priv->last_child1_focus);
temp_list = g_list_append (temp_list, last_child1_focus);
temp_list = g_list_append (temp_list, priv->child1);
temp_list = g_list_append (temp_list, ancestor);
}
else
{
temp_list = g_list_append (temp_list, priv->last_child2_focus);
temp_list = g_list_append (temp_list, last_child2_focus);
temp_list = g_list_append (temp_list, priv->child2);
temp_list = g_list_append (temp_list, priv->last_child1_focus);
temp_list = g_list_append (temp_list, last_child1_focus);
temp_list = g_list_append (temp_list, priv->child1);
temp_list = g_list_append (temp_list, ancestor);
}
@@ -2256,10 +2151,8 @@ gtk_paned_restore_focus (GtkPaned *paned)
if (!gtk_widget_child_focus (GTK_WIDGET (paned), GTK_DIR_TAB_FORWARD))
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
if (GTK_IS_WINDOW (toplevel))
gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (paned));
gtk_root_set_focus (root, NULL);
}
}
@@ -2364,7 +2257,6 @@ gtk_paned_cycle_handle_focus (GtkPaned *paned,
{
GtkPaned *focus;
GtkPaned *first;
GtkWidget *toplevel;
GtkWidget *focus_child;
gtk_paned_find_neighbours (paned, &next, &prev);
@@ -2410,10 +2302,7 @@ gtk_paned_cycle_handle_focus (GtkPaned *paned,
first = next;
}
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned));
if (GTK_IS_WINDOW (toplevel))
gtk_paned_set_saved_focus (focus, gtk_window_get_focus (GTK_WINDOW (toplevel)));
gtk_paned_set_saved_focus (focus, gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (paned))));
gtk_paned_set_first_paned (focus, first);
priv->original_position = gtk_paned_get_position (focus);
+1 -1
View File
@@ -1766,7 +1766,7 @@ on_key_press_event (GtkEventController *controller,
if (!toplevel)
return FALSE;
focus_widget = gtk_window_get_focus (toplevel);
focus_widget = gtk_root_get_focus (GTK_ROOT (toplevel));
if (!GTK_IS_PLACES_VIEW_ROW (focus_widget))
return FALSE;
+3 -36
View File
@@ -614,10 +614,11 @@ window_active_changed (GtkWindow *window,
static void
window_set_focus (GtkWindow *window,
GtkWidget *widget,
GParamSpec *pspec,
GtkPopover *popover)
{
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
GtkWidget *widget = gtk_root_get_focus (GTK_ROOT (window));
if (!priv->modal || !widget || !gtk_widget_is_drawable (GTK_WIDGET (popover)))
return;
@@ -673,7 +674,7 @@ gtk_popover_apply_modality (GtkPopover *popover,
g_signal_connect (priv->window, "notify::is-active",
G_CALLBACK (window_active_changed), popover);
g_signal_connect (priv->window, "set-focus",
g_signal_connect (priv->window, "notify::focus-widget",
G_CALLBACK (window_set_focus), popover);
}
else
@@ -1455,39 +1456,6 @@ gtk_popover_grab_focus (GtkWidget *widget)
gtk_widget_child_focus (child, GTK_DIR_TAB_FORWARD);
}
static gboolean
gtk_popover_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkPopover *popover = GTK_POPOVER (widget);
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
if (!priv->visible)
return FALSE;
if (!GTK_WIDGET_CLASS (gtk_popover_parent_class)->focus (widget, direction))
{
GtkWidget *focus;
focus = gtk_window_get_focus (priv->window);
focus = gtk_widget_get_parent (focus);
/* Unset focus child through children, so it is next stepped from
* scratch.
*/
while (focus && focus != widget)
{
gtk_widget_set_focus_child (focus, NULL);
focus = gtk_widget_get_parent (focus);
}
return gtk_widget_child_focus (gtk_bin_get_child (GTK_BIN (widget)),
direction);
}
return TRUE;
}
static void
gtk_popover_show (GtkWidget *widget)
{
@@ -1582,7 +1550,6 @@ gtk_popover_class_init (GtkPopoverClass *klass)
widget_class->size_allocate = gtk_popover_size_allocate;
widget_class->snapshot = gtk_popover_snapshot;
widget_class->grab_focus = gtk_popover_grab_focus;
widget_class->focus = gtk_popover_focus;
widget_class->show = gtk_popover_show;
widget_class->hide = gtk_popover_hide;
widget_class->state_flags_changed = gtk_popover_state_flags_changed;
-96
View File
@@ -149,8 +149,6 @@ static GParamSpec *radio_button_props[LAST_PROP] = { NULL, };
static guint signals[N_SIGNALS] = { 0 };
static void gtk_radio_button_destroy (GtkWidget *widget);
static gboolean gtk_radio_button_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_radio_button_clicked (GtkButton *button);
static void gtk_radio_button_set_property (GObject *object,
guint prop_id,
@@ -192,7 +190,6 @@ gtk_radio_button_class_init (GtkRadioButtonClass *class)
g_object_class_install_properties (gobject_class, LAST_PROP, radio_button_props);
widget_class->destroy = gtk_radio_button_destroy;
widget_class->focus = gtk_radio_button_focus;
button_class->clicked = gtk_radio_button_clicked;
@@ -615,99 +612,6 @@ gtk_radio_button_destroy (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->destroy (widget);
}
static gboolean
gtk_radio_button_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
GtkRadioButtonPrivate *priv = gtk_radio_button_get_instance_private (radio_button);
GSList *tmp_slist;
/* Radio buttons with draw_indicator unset focus "normally", since
* they look like buttons to the user.
*/
if (!gtk_check_button_get_draw_indicator (GTK_CHECK_BUTTON (widget)))
return GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->focus (widget, direction);
if (gtk_widget_is_focus (widget))
{
GPtrArray *child_array;
GtkWidget *new_focus = NULL;
GSList *l;
guint index;
gboolean found;
guint i;
if (direction == GTK_DIR_TAB_FORWARD ||
direction == GTK_DIR_TAB_BACKWARD)
return FALSE;
child_array = g_ptr_array_sized_new (g_slist_length (priv->group));
for (l = priv->group; l; l = l->next)
g_ptr_array_add (child_array, l->data);
gtk_widget_focus_sort (widget, direction, child_array);
found = g_ptr_array_find (child_array, widget, &index);
if (found)
{
/* Start at the *next* widget in the list */
if (index < child_array->len - 1)
index ++;
}
else
{
/* Search from the start of the list */
index = 0;
}
for (i = index; i < child_array->len; i ++)
{
GtkWidget *child = g_ptr_array_index (child_array, i);
if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
{
new_focus = child;
break;
}
}
if (new_focus)
{
gtk_widget_grab_focus (new_focus);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
}
g_ptr_array_free (child_array, TRUE);
return TRUE;
}
else
{
GtkRadioButton *selected_button = NULL;
/* We accept the focus if, we don't have the focus and
* - we are the currently active button in the group
* - there is no currently active radio button.
*/
tmp_slist = priv->group;
while (tmp_slist)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tmp_slist->data)) &&
gtk_widget_get_visible (tmp_slist->data))
selected_button = tmp_slist->data;
tmp_slist = tmp_slist->next;
}
if (selected_button && selected_button != radio_button)
return FALSE;
gtk_widget_grab_focus (widget);
return TRUE;
}
}
static void
gtk_radio_button_clicked (GtkButton *button)
{
+70
View File
@@ -21,6 +21,8 @@
#include "gtkrootprivate.h"
#include "gdk/gdk-private.h"
#include "gtkprivate.h"
#include "gtkintl.h"
/**
* SECTION:gtkroot
@@ -65,6 +67,13 @@ gtk_root_default_init (GtkRootInterface *iface)
iface->get_display = gtk_root_default_get_display;
iface->get_renderer = gtk_root_default_get_renderer;
iface->get_surface_transform = gtk_root_default_get_surface_transform;
g_object_interface_install_property (iface,
g_param_spec_object ("focus-widget",
P_("Focus widget"),
P_("The focus widget"),
GTK_TYPE_WIDGET,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
}
GdkDisplay *
@@ -124,3 +133,64 @@ gtk_root_get_for_surface (GdkSurface *surface)
return NULL;
}
/**
* gtk_root_set_focus:
* @self: a #GtkRoot
* @focus: (allow-none): widget to be the new focus widget, or %NULL
* to unset the focus widget
*
* If @focus is not the current focus widget, and is focusable, sets
* it as the focus widget for the root. If @focus is %NULL, unsets
* the focus widget for the root.
*
* To set the focus to a particular widget in the root, it is usually
* more convenient to use gtk_widget_grab_focus() instead of this function.
*/
void
gtk_root_set_focus (GtkRoot *self,
GtkWidget *focus)
{
g_return_if_fail (GTK_IS_ROOT (self));
g_return_if_fail (focus == NULL ||
(GTK_IS_WIDGET (focus) && gtk_widget_get_root (focus) == self));
g_object_set (self, "focus-widget", focus, NULL);
}
/**
* gtk_root_get_focus:
* @self: a #GtkRoot
*
* Retrieves the current focused widget within the root.
*
* Note that this is the widget that would have the focus
* if the root is active; if the root is not focused then
* `gtk_widget_has_focus (widget)` will be %FALSE for the
* widget.
*
* Returns: (nullable) (transfer none): the currently focused widget,
* or %NULL if there is none.
*/
GtkWidget *
gtk_root_get_focus (GtkRoot *self)
{
GtkWidget *focus;
g_return_val_if_fail (GTK_IS_ROOT (self), NULL);
g_object_get (self, "focus-widget", &focus, NULL);
if (focus)
g_object_unref (focus);
return focus;
}
guint
gtk_root_install_properties (GObjectClass *object_class,
guint first_prop)
{
g_object_class_override_property (object_class, first_prop + GTK_ROOT_PROP_FOCUS_WIDGET, "focus-widget");
return GTK_ROOT_NUM_PROPERTIES;
}
+6
View File
@@ -56,6 +56,12 @@ struct _GtkRootInterface
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_root_get_for_surface (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
void gtk_root_set_focus (GtkRoot *self,
GtkWidget *focus);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_root_get_focus (GtkRoot *self);
G_END_DECLS
#endif /* __GTK_ROOT_H__ */
+8
View File
@@ -11,6 +11,14 @@ GskRenderer * gtk_root_get_renderer (GtkRoot
void gtk_root_get_surface_transform (GtkRoot *self,
int *x,
int *y);
enum {
GTK_ROOT_PROP_FOCUS_WIDGET,
GTK_ROOT_NUM_PROPERTIES
} GtkRootProperties;
guint gtk_root_install_properties (GObjectClass *object_class,
guint first_prop);
G_END_DECLS
#endif /* __GTK_ROOT_PRIVATE_H__ */
-42
View File
@@ -322,8 +322,6 @@ static void gtk_scrolled_window_size_allocate (GtkWidget *widge
int width,
int height,
int baseline);
static gboolean gtk_scrolled_window_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_scrolled_window_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_scrolled_window_remove (GtkContainer *container,
@@ -507,7 +505,6 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
widget_class->destroy = gtk_scrolled_window_destroy;
widget_class->snapshot = gtk_scrolled_window_snapshot;
widget_class->size_allocate = gtk_scrolled_window_size_allocate;
widget_class->focus = gtk_scrolled_window_focus;
widget_class->measure = gtk_scrolled_window_measure;
widget_class->map = gtk_scrolled_window_map;
widget_class->unmap = gtk_scrolled_window_unmap;
@@ -3330,45 +3327,6 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
(GDestroyNotify) kinetic_scroll_data_free);
}
static gboolean
gtk_scrolled_window_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
GtkWidget *child;
gboolean had_focus_child;
had_focus_child = gtk_widget_get_focus_child (widget) != NULL;
if (priv->focus_out)
{
priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
return FALSE;
}
if (gtk_widget_is_focus (widget))
return FALSE;
/* We only put the scrolled window itself in the focus chain if it
* isn't possible to focus any children.
*/
child = gtk_bin_get_child (GTK_BIN (widget));
if (child)
{
if (gtk_widget_child_focus (child, direction))
return TRUE;
}
if (!had_focus_child && gtk_widget_get_can_focus (widget))
{
gtk_widget_grab_focus (widget);
return TRUE;
}
else
return FALSE;
}
static void
gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
gpointer data)
+2 -1
View File
@@ -255,7 +255,7 @@ gtk_search_entry_grab_focus (GtkWidget *widget)
GtkSearchEntry *entry = GTK_SEARCH_ENTRY (widget);
GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
gtk_text_grab_focus_without_selecting (GTK_TEXT (priv->entry));
gtk_widget_grab_focus (priv->entry);
}
static gboolean
@@ -523,6 +523,7 @@ gtk_search_entry_init (GtkSearchEntry *entry)
gtk_widget_set_vexpand (priv->box, FALSE);
priv->entry = gtk_text_new ();
g_object_set (priv->entry, "select-on-focus", FALSE, NULL);
gtk_widget_set_hexpand (priv->entry, TRUE);
gtk_widget_set_vexpand (priv->entry, TRUE);
gtk_container_add (GTK_CONTAINER (priv->box), GTK_WIDGET (priv->entry));
+2
View File
@@ -824,6 +824,8 @@ key_controller_key_released (GtkEventControllerKey *key,
static void
key_controller_focus_out (GtkEventControllerKey *key,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkSpinButton *spin_button)
{
GtkSpinButtonPrivate *priv = gtk_spin_button_get_instance_private (spin_button);
+32 -17
View File
@@ -533,6 +533,9 @@ static void gtk_stack_forall (GtkContainer *contain
static void gtk_stack_compute_expand (GtkWidget *widget,
gboolean *hexpand,
gboolean *vexpand);
static GtkWidget *gtk_stack_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction);
static void gtk_stack_size_allocate (GtkWidget *widget,
int width,
int height,
@@ -724,6 +727,7 @@ gtk_stack_class_init (GtkStackClass *klass)
widget_class->snapshot = gtk_stack_snapshot;
widget_class->measure = gtk_stack_measure;
widget_class->compute_expand = gtk_stack_compute_expand;
widget_class->next_focus_child = gtk_stack_next_focus_child;
container_class->add = gtk_stack_add;
container_class->remove = gtk_stack_remove;
@@ -1123,7 +1127,6 @@ set_visible_child (GtkStack *stack,
GtkStackPage *info;
GtkWidget *widget = GTK_WIDGET (stack);
GList *l;
GtkWidget *toplevel;
GtkWidget *focus;
gboolean contains_focus = FALSE;
guint old_pos = GTK_INVALID_LIST_POSITION;
@@ -1165,24 +1168,23 @@ set_visible_child (GtkStack *stack,
}
}
toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
if (gtk_widget_get_root (widget))
focus = gtk_root_get_focus (gtk_widget_get_root (widget));
else
focus = NULL;
if (focus &&
priv->visible_child &&
priv->visible_child->widget &&
gtk_widget_is_ancestor (focus, priv->visible_child->widget))
{
focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
if (focus &&
priv->visible_child &&
priv->visible_child->widget &&
gtk_widget_is_ancestor (focus, priv->visible_child->widget))
{
contains_focus = TRUE;
contains_focus = TRUE;
if (priv->visible_child->last_focus)
g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
(gpointer *)&priv->visible_child->last_focus);
priv->visible_child->last_focus = focus;
g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
(gpointer *)&priv->visible_child->last_focus);
}
if (priv->visible_child->last_focus)
g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
(gpointer *)&priv->visible_child->last_focus);
priv->visible_child->last_focus = focus;
g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
(gpointer *)&priv->visible_child->last_focus);
}
if (priv->last_visible_child)
@@ -2436,6 +2438,19 @@ gtk_stack_measure (GtkWidget *widget,
}
}
static GtkWidget *
gtk_stack_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction)
{
GtkStackPrivate *priv = gtk_stack_get_instance_private (GTK_STACK (widget));
if (child == NULL)
return priv->visible_child->widget;
return NULL;
}
static void
gtk_stack_init (GtkStack *stack)
{
+70 -44
View File
@@ -225,6 +225,8 @@ struct _GtkTextPrivate
guint cursor_handle_dragged : 1;
guint selection_handle_dragged : 1;
guint populate_all : 1;
guint select_on_focus : 1;
guint select_on_focus_set : 1;
};
struct _GtkTextPasswordHint
@@ -268,6 +270,8 @@ enum {
PROP_POPULATE_ALL,
PROP_TABS,
PROP_ENABLE_EMOJI_COMPLETION,
PROP_SELECT_ON_FOCUS,
PROP_SELECT_ON_FOCUS_SET,
NUM_PROPERTIES
};
@@ -321,7 +325,6 @@ static void gtk_text_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot);
static void gtk_text_focus_in (GtkWidget *widget);
static void gtk_text_focus_out (GtkWidget *widget);
static void gtk_text_grab_focus (GtkWidget *widget);
static void gtk_text_style_updated (GtkWidget *widget);
static void gtk_text_direction_changed (GtkWidget *widget,
GtkTextDirection previous_dir);
@@ -675,7 +678,6 @@ gtk_text_class_init (GtkTextClass *class)
widget_class->measure = gtk_text_measure;
widget_class->size_allocate = gtk_text_size_allocate;
widget_class->snapshot = gtk_text_snapshot;
widget_class->grab_focus = gtk_text_grab_focus;
widget_class->style_updated = gtk_text_style_updated;
widget_class->drag_begin = gtk_text_drag_begin;
widget_class->drag_end = gtk_text_drag_end;
@@ -898,6 +900,26 @@ gtk_text_class_init (GtkTextClass *class)
TRUE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkEntry::select-on-focus:
*
* Whether to select the contents of the text when focus enters it.
* When set, this property overrides the system-widget setting for
* this feature. See #GtkEntry::select-on-enter-set
*/
text_props[PROP_SELECT_ON_FOCUS] =
g_param_spec_boolean ("select-on-focus",
P_("Selet on focus"),
P_("Whether to select the text on focus"),
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
text_props[PROP_SELECT_ON_FOCUS_SET] =
g_param_spec_boolean ("select-on-focus-set",
P_("Select on focus set"),
P_("Whether the select-on-focus property has been set"),
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, text_props);
gtk_editable_install_properties (gobject_class, NUM_PROPERTIES);
@@ -1515,6 +1537,27 @@ gtk_text_set_property (GObject *object,
set_enable_emoji_completion (self, g_value_get_boolean (value));
break;
case PROP_SELECT_ON_FOCUS:
if (priv->select_on_focus != g_value_get_boolean (value))
{
priv->select_on_focus = g_value_get_boolean (value);
g_object_notify_by_pspec (object, pspec);
}
if (!priv->select_on_focus_set)
{
priv->select_on_focus_set = TRUE;
g_object_notify_by_pspec (object, text_props[PROP_SELECT_ON_FOCUS_SET]);
}
break;
case PROP_SELECT_ON_FOCUS_SET:
if (priv->select_on_focus_set != g_value_get_boolean (value))
{
priv->select_on_focus_set = g_value_get_boolean (value);
g_object_notify_by_pspec (object, pspec);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1630,6 +1673,14 @@ gtk_text_get_property (GObject *object,
g_value_set_boolean (value, priv->enable_emoji_completion);
break;
case PROP_SELECT_ON_FOCUS:
g_value_set_boolean (value, priv->select_on_focus);
break;
case PROP_SELECT_ON_FOCUS_SET:
g_value_set_boolean (value, priv->select_on_focus_set);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2896,6 +2947,22 @@ gtk_text_focus_in (GtkWidget *widget)
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GdkKeymap *keymap;
if (priv->editable && !priv->in_click)
{
gboolean select_on_focus;
if (priv->select_on_focus_set)
select_on_focus = priv->select_on_focus;
else
g_object_get (gtk_widget_get_settings (widget),
"gtk-entry-select-on-focus",
&select_on_focus,
NULL);
if (select_on_focus)
gtk_text_set_selection_bounds (self, 0, -1);
}
gtk_widget_queue_draw (widget);
keymap = gdk_display_get_keymap (gtk_widget_get_display (widget));
@@ -2941,47 +3008,6 @@ gtk_text_focus_out (GtkWidget *widget)
g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self);
}
static void
gtk_text_grab_focus (GtkWidget *widget)
{
GtkText *self = GTK_TEXT (widget);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
gboolean select_on_focus;
GTK_WIDGET_CLASS (gtk_text_parent_class)->grab_focus (GTK_WIDGET (self));
if (priv->editable && !priv->in_click)
{
g_object_get (gtk_widget_get_settings (widget),
"gtk-entry-select-on-focus",
&select_on_focus,
NULL);
if (select_on_focus)
gtk_text_set_selection_bounds (self, 0, -1);
}
}
/**
* gtk_text_grab_focus_without_selecting:
* @self: a #GtkText
*
* Causes @self to have keyboard focus.
*
* It behaves like gtk_widget_grab_focus(),
* except that it doesn't select the contents of the self.
* You only want to call this on some special entries
* which the user usually doesn't want to replace all text in,
* such as search-as-you-type entries.
*/
void
gtk_text_grab_focus_without_selecting (GtkText *self)
{
g_return_if_fail (GTK_IS_TEXT (self));
GTK_WIDGET_CLASS (gtk_text_parent_class)->grab_focus (GTK_WIDGET (self));
}
static void
gtk_text_direction_changed (GtkWidget *widget,
GtkTextDirection previous_dir)
@@ -3827,7 +3853,7 @@ gtk_text_real_activate (GtkText *self)
if (window)
{
default_widget = gtk_window_get_default_widget (window);
focus_widget = gtk_window_get_focus (window);
focus_widget = gtk_root_get_focus (GTK_ROOT (window));
if (widget != default_widget &&
!(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
gtk_window_activate_default (window);
-3
View File
@@ -131,9 +131,6 @@ void gtk_text_set_tabs (GtkText *self,
GDK_AVAILABLE_IN_ALL
PangoTabArray * gtk_text_get_tabs (GtkText *self);
GDK_AVAILABLE_IN_ALL
void gtk_text_grab_focus_without_selecting (GtkText *self);
G_END_DECLS
#endif /* __GTK_TEXT_H__ */
-36
View File
@@ -407,8 +407,6 @@ static void gtk_text_view_motion (GtkEventController *controller,
gpointer user_data);
static void gtk_text_view_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot);
static gboolean gtk_text_view_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_text_view_select_all (GtkWidget *widget,
gboolean select);
static gboolean get_middle_click_paste (GtkTextView *text_view);
@@ -694,7 +692,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
widget_class->measure = gtk_text_view_measure;
widget_class->size_allocate = gtk_text_view_size_allocate;
widget_class->snapshot = gtk_text_view_snapshot;
widget_class->focus = gtk_text_view_focus;
widget_class->drag_begin = gtk_text_view_drag_begin;
widget_class->drag_end = gtk_text_view_drag_end;
widget_class->drag_data_get = gtk_text_view_drag_data_get;
@@ -5447,39 +5444,6 @@ gtk_text_view_snapshot (GtkWidget *widget,
}
}
static gboolean
gtk_text_view_focus (GtkWidget *widget,
GtkDirectionType direction)
{
gboolean result;
if (!gtk_widget_is_focus (widget) &&
gtk_widget_get_focus_child (widget) == NULL)
{
if (gtk_widget_get_can_focus (widget))
{
gtk_widget_grab_focus (widget);
return TRUE;
}
return FALSE;
}
else
{
gboolean can_focus;
/*
* Unset CAN_FOCUS flag so that gtk_container_focus() allows
* children to get the focus
*/
can_focus = gtk_widget_get_can_focus (widget);
gtk_widget_set_can_focus (widget, FALSE);
result = GTK_WIDGET_CLASS (gtk_text_view_parent_class)->focus (widget, direction);
gtk_widget_set_can_focus (widget, can_focus);
return result;
}
}
/*
* Container
*/
+14 -40
View File
@@ -189,8 +189,6 @@ static void gtk_toolbar_size_allocate (GtkWidget *widget,
int height,
int baseline);
static void gtk_toolbar_style_updated (GtkWidget *widget);
static gboolean gtk_toolbar_focus (GtkWidget *widget,
GtkDirectionType dir);
static void gtk_toolbar_move_focus (GtkWidget *widget,
GtkDirectionType dir);
static void gtk_toolbar_display_changed (GtkWidget *widget,
@@ -347,6 +345,19 @@ add_ctrl_tab_bindings (GtkBindingSet *binding_set,
GTK_TYPE_DIRECTION_TYPE, direction);
}
static GtkWidget *
gtk_toolbar_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction)
{
if (child &&
(direction == GTK_DIR_TAB_FORWARD ||
direction == GTK_DIR_TAB_BACKWARD))
return NULL;
return GTK_WIDGET_CLASS (gtk_toolbar_parent_class)->next_focus_child (widget, child, direction);
}
static void
gtk_toolbar_class_init (GtkToolbarClass *klass)
{
@@ -368,7 +379,7 @@ gtk_toolbar_class_init (GtkToolbarClass *klass)
widget_class->measure = gtk_toolbar_measure;
widget_class->size_allocate = gtk_toolbar_size_allocate;
widget_class->style_updated = gtk_toolbar_style_updated;
widget_class->focus = gtk_toolbar_focus;
widget_class->next_focus_child = gtk_toolbar_next_focus_child;
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TOOL_BAR);
@@ -1611,43 +1622,6 @@ gtk_toolbar_move_focus (GtkWidget *widget,
g_list_free (children);
}
/* The focus handler for the toolbar. It called when the user presses
* TAB or otherwise tries to focus the toolbar.
*/
static gboolean
gtk_toolbar_focus (GtkWidget *widget,
GtkDirectionType dir)
{
GtkToolbar *toolbar = GTK_TOOLBAR (widget);
GList *children, *list;
gboolean result = FALSE;
/* if focus is already somewhere inside the toolbar then return FALSE.
* The only way focus can stay inside the toolbar is when the user presses
* arrow keys or Ctrl TAB (both of which are handled by the
* gtk_toolbar_move_focus() keybinding function.
*/
if (gtk_widget_get_focus_child (widget))
return FALSE;
children = gtk_toolbar_list_children_in_focus_order (toolbar, dir);
for (list = children; list != NULL; list = list->next)
{
GtkWidget *child = list->data;
if (gtk_widget_get_mapped (child) && gtk_widget_child_focus (child, dir))
{
result = TRUE;
break;
}
}
g_list_free (children);
return result;
}
static GtkSettings *
toolbar_get_settings (GtkToolbar *toolbar)
{
+6 -221
View File
@@ -614,10 +614,10 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey
GdkModifierType state,
GtkTreeView *tree_view);
static void gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkTreeView *tree_view);
static gint gtk_tree_view_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_tree_view_grab_focus (GtkWidget *widget);
static void gtk_tree_view_style_updated (GtkWidget *widget);
@@ -985,7 +985,6 @@ gtk_tree_view_class_init (GtkTreeViewClass *class)
widget_class->drag_motion = gtk_tree_view_drag_motion;
widget_class->drag_drop = gtk_tree_view_drag_drop;
widget_class->drag_data_received = gtk_tree_view_drag_data_received;
widget_class->focus = gtk_tree_view_focus;
widget_class->grab_focus = gtk_tree_view_grab_focus;
widget_class->style_updated = gtk_tree_view_style_updated;
@@ -5475,6 +5474,8 @@ gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller,
static void
gtk_tree_view_key_controller_focus_out (GtkEventControllerKey *key,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkTreeView *tree_view)
{
gtk_widget_queue_draw (GTK_WIDGET (tree_view));
@@ -7709,170 +7710,6 @@ gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view)
return tree_view->priv->fixed_height_mode;
}
/* Returns TRUE if the focus is within the headers, after the focus operation is
* done
*/
static gboolean
gtk_tree_view_header_focus (GtkTreeView *tree_view,
GtkDirectionType dir,
gboolean clamp_column_visible)
{
GtkTreeViewColumn *column;
GtkWidget *button;
GtkWidget *focus_child;
GList *last_column, *first_column;
GList *tmp_list;
gboolean rtl;
if (! tree_view->priv->headers_visible)
return FALSE;
focus_child = gtk_widget_get_focus_child (GTK_WIDGET (tree_view));
first_column = tree_view->priv->columns;
while (first_column)
{
column = GTK_TREE_VIEW_COLUMN (first_column->data);
button = gtk_tree_view_column_get_button (column);
if (gtk_widget_get_can_focus (button) &&
gtk_tree_view_column_get_visible (column) &&
(gtk_tree_view_column_get_clickable (column) ||
gtk_tree_view_column_get_reorderable (column)))
break;
first_column = first_column->next;
}
/* No headers are visible, or are focusable. We can't focus in or out.
*/
if (first_column == NULL)
return FALSE;
last_column = g_list_last (tree_view->priv->columns);
while (last_column)
{
column = GTK_TREE_VIEW_COLUMN (last_column->data);
button = gtk_tree_view_column_get_button (column);
if (gtk_widget_get_can_focus (button) &&
gtk_tree_view_column_get_visible (column) &&
(gtk_tree_view_column_get_clickable (column) ||
gtk_tree_view_column_get_reorderable (column)))
break;
last_column = last_column->prev;
}
rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
switch (dir)
{
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_UP:
case GTK_DIR_DOWN:
if (focus_child == NULL)
{
if (tree_view->priv->focus_column != NULL)
button = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
else
button = NULL;
if (button && gtk_widget_get_can_focus (button))
focus_child = button;
else
focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
gtk_widget_grab_focus (focus_child);
break;
}
return FALSE;
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
if (focus_child == NULL)
{
if (tree_view->priv->focus_column != NULL)
focus_child = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
else if (dir == GTK_DIR_LEFT)
focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data));
else
focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
gtk_widget_grab_focus (focus_child);
break;
}
if (gtk_widget_child_focus (focus_child, dir))
{
/* The focus moves inside the button. */
/* This is probably a great example of bad UI */
break;
}
/* We need to move the focus among the row of buttons. */
for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
break;
if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
|| (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
{
gtk_widget_error_bell (GTK_WIDGET (tree_view));
break;
}
while (tmp_list)
{
if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
tmp_list = tmp_list->next;
else
tmp_list = tmp_list->prev;
if (tmp_list == NULL)
{
g_warning ("Internal button not found");
break;
}
column = tmp_list->data;
button = gtk_tree_view_column_get_button (column);
if (button &&
gtk_tree_view_column_get_visible (column) &&
gtk_widget_get_can_focus (button))
{
focus_child = button;
gtk_widget_grab_focus (button);
break;
}
}
break;
default:
g_assert_not_reached ();
break;
}
/* if focus child is non-null, we assume it's been set to the current focus child
*/
if (focus_child)
{
for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
{
_gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (tmp_list->data));
break;
}
if (clamp_column_visible)
{
gtk_tree_view_clamp_column_visible (tree_view,
tree_view->priv->focus_column,
FALSE);
}
}
return (focus_child != NULL);
}
/* This function returns in 'path' the first focusable path, if the given path
* is already focusable, its the returned one.
*/
@@ -7919,59 +7756,6 @@ search_first_focusable_path (GtkTreeView *tree_view,
return (*path != NULL);
}
static gint
gtk_tree_view_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
GtkWidget *focus_child;
if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
return FALSE;
focus_child = gtk_widget_get_focus_child (widget);
gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE);
/* Case 1. Headers currently have focus. */
if (focus_child)
{
switch (direction)
{
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
gtk_tree_view_header_focus (tree_view, direction, TRUE);
return TRUE;
case GTK_DIR_TAB_BACKWARD:
case GTK_DIR_UP:
return FALSE;
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_DOWN:
gtk_widget_grab_focus (widget);
return TRUE;
default:
g_assert_not_reached ();
return FALSE;
}
}
/* Case 2. We don't have focus at all. */
if (!gtk_widget_has_focus (widget))
{
gtk_widget_grab_focus (widget);
return TRUE;
}
/* Case 3. We have focus already. */
if (direction == GTK_DIR_TAB_BACKWARD)
return (gtk_tree_view_header_focus (tree_view, direction, FALSE));
else if (direction == GTK_DIR_TAB_FORWARD)
return FALSE;
/* Other directions caught by the keybindings */
gtk_widget_grab_focus (widget);
return TRUE;
}
static void
gtk_tree_view_grab_focus (GtkWidget *widget)
{
@@ -10151,7 +9935,8 @@ send_focus_change (GtkWidget *widget,
fevent->focus_change.in = in;
gdk_event_set_device (fevent, device);
gtk_widget_send_focus_change (widget, fevent);
gtk_widget_set_has_focus (widget, in);
gtk_widget_event (widget, fevent);
g_object_unref (fevent);
}
+4 -5
View File
@@ -814,6 +814,8 @@ gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout)
static void
focus_in (GtkEventControllerKey *controller,
GdkCrossingMode mode,
GdkNotifyType detail,
GtkTreeViewColumn *column)
{
_gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column);
@@ -1016,11 +1018,8 @@ gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
gtk_widget_set_can_focus (priv->button, FALSE);
if (gtk_widget_has_focus (priv->button))
{
GtkWidget *toplevel = gtk_widget_get_toplevel (priv->tree_view);
if (gtk_widget_is_toplevel (toplevel))
{
gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
}
GtkRoot *root = gtk_widget_get_root (priv->tree_view);
gtk_root_set_focus (root, NULL);
}
}
/* Queue a resize on the assumption that we always want to catch all changes
+131 -251
View File
@@ -484,7 +484,6 @@ enum {
GRAB_NOTIFY,
CHILD_NOTIFY,
MNEMONIC_ACTIVATE,
FOCUS,
MOVE_FOCUS,
KEYNAV_FAILED,
DRAG_BEGIN,
@@ -589,7 +588,9 @@ static void gtk_widget_real_size_allocate (GtkWidget *widget,
static void gtk_widget_real_direction_changed(GtkWidget *widget,
GtkTextDirection previous_direction);
static void gtk_widget_real_grab_focus (GtkWidget *focus_widget);
static void gtk_widget_real_grab_focus (GtkWidget *focus_widget);
static void gtk_widget_real_set_focus_child (GtkWidget *widget,
GtkWidget *child);
static gboolean gtk_widget_real_query_tooltip (GtkWidget *widget,
gint x,
gint y,
@@ -600,8 +601,6 @@ static void gtk_widget_real_style_updated (GtkWidget *widget);
static void gtk_widget_dispatch_child_properties_changed (GtkWidget *object,
guint n_pspecs,
GParamSpec **pspecs);
static gboolean gtk_widget_real_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_widget_real_move_focus (GtkWidget *widget,
GtkDirectionType direction);
static gboolean gtk_widget_real_keynav_failed (GtkWidget *widget,
@@ -949,7 +948,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->snapshot = gtk_widget_real_snapshot;
klass->mnemonic_activate = gtk_widget_real_mnemonic_activate;
klass->grab_focus = gtk_widget_real_grab_focus;
klass->focus = gtk_widget_real_focus;
klass->set_focus_child = gtk_widget_real_set_focus_child;
klass->next_focus_child = gtk_widget_next_focus_child;
klass->move_focus = gtk_widget_real_move_focus;
klass->keynav_failed = gtk_widget_real_keynav_failed;
klass->drag_begin = NULL;
@@ -1670,23 +1670,6 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_BOOLEAN, 1,
G_TYPE_BOOLEAN);
/**
* GtkWidget::focus:
* @widget: the object which received the signal.
* @direction:
*
* Returns: %TRUE to stop other handlers from being invoked for the event. %FALSE to propagate the event further.
*/
widget_signals[FOCUS] =
g_signal_new (I_("focus"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, focus),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__ENUM,
G_TYPE_BOOLEAN, 1,
GTK_TYPE_DIRECTION_TYPE);
/**
* GtkWidget::move-focus:
* @widget: the object which received the signal.
@@ -2843,6 +2826,7 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class)
priv->visible = gtk_widget_class_get_visible_by_default (g_class);
priv->child_visible = TRUE;
priv->child_focusable = TRUE;
priv->name = NULL;
priv->user_alpha = 255;
priv->alpha = 255;
@@ -3130,10 +3114,11 @@ gtk_widget_unparent (GtkWidget *widget)
toplevel = NULL;
/* Removing a widget from a container restores the child visible
* flag to the default state, so it doesn't affect the child
* in the next parent.
* and focusable flags to the default state, so they don't affect
* the child in the next parent.
*/
priv->child_visible = TRUE;
priv->child_focusable = TRUE;
old_parent = priv->parent;
if (old_parent)
@@ -5321,107 +5306,25 @@ _gtk_widget_grab_notify (GtkWidget *widget,
* gtk_widget_grab_focus:
* @widget: a #GtkWidget
*
* Causes @widget to have the keyboard focus for the #GtkWindow it's
* inside. @widget must be a focusable widget, such as a #GtkEntry;
* something like #GtkFrame wont work.
* Causes @widget (or one of its descendents) to have the keyboard focus
* for the #GtkWindow it's inside.
*
* More precisely, it must have the %GTK_CAN_FOCUS flag set. Use
* gtk_widget_set_can_focus() to modify that flag.
*
* The widget also needs to be realized and mapped. This is indicated by the
* related signals. Grabbing the focus immediately after creating the widget
* will likely fail and cause critical warnings.
* @widget must be focusable, or have a ::grab_focus implementation that
* transfers the focus to a descendant of @widget that is focusable.
**/
void
gtk_widget_grab_focus (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!gtk_widget_is_sensitive (widget))
return;
g_object_ref (widget);
GTK_WIDGET_GET_CLASS (widget)->grab_focus (widget);
g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]);
g_object_unref (widget);
}
static void
reset_focus_recurse (GtkWidget *widget,
gpointer data)
{
gtk_widget_set_focus_child (widget, NULL);
gtk_widget_forall (widget,
reset_focus_recurse,
NULL);
}
static void
gtk_widget_real_grab_focus (GtkWidget *focus_widget)
{
GtkWidget *toplevel;
GtkWidget *widget;
/* clear the current focus setting, break if the current widget
* is the focus widget's parent, since containers above that will
* be set by the next loop.
*/
toplevel = _gtk_widget_get_toplevel (focus_widget);
if (_gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
{
widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
if (widget == focus_widget)
{
/* We call _gtk_window_internal_set_focus() here so that the
* toplevel window can request the focus if necessary.
* This is needed when the toplevel is a GtkPlug
*/
if (!gtk_widget_has_focus (widget))
_gtk_window_internal_set_focus (GTK_WINDOW (toplevel), focus_widget);
return;
}
if (widget)
{
GtkWidget *common_ancestor = gtk_widget_common_ancestor (widget, focus_widget);
if (widget != common_ancestor)
{
while (widget->priv->parent)
{
widget = widget->priv->parent;
gtk_widget_set_focus_child (widget, NULL);
if (widget == common_ancestor)
break;
}
}
}
}
else if (toplevel != focus_widget)
{
/* gtk_widget_grab_focus() operates on a tree without window...
* actually, this is very questionable behavior.
*/
gtk_widget_forall (toplevel,
reset_focus_recurse,
NULL);
}
/* now propagate the new focus up the widget tree and finally
* set it on the window
*/
widget = focus_widget;
while (widget->priv->parent)
{
gtk_widget_set_focus_child (widget->priv->parent, widget);
widget = widget->priv->parent;
}
if (GTK_IS_WINDOW (widget))
_gtk_window_internal_set_focus (GTK_WINDOW (widget), focus_widget);
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (focus_widget);
gtk_root_set_focus (priv->root, focus_widget);
}
static gboolean
@@ -5517,53 +5420,22 @@ gtk_widget_real_style_updated (GtkWidget *widget)
}
}
static gboolean
gtk_widget_real_focus (GtkWidget *widget,
GtkDirectionType direction)
{
if (gtk_widget_get_can_focus (widget))
{
if (!gtk_widget_is_focus (widget))
{
gtk_widget_grab_focus (widget);
return TRUE;
}
}
else if (_gtk_widget_get_first_child (widget) == NULL)
{
/* No children, no possibility to focus anything */
return FALSE;
}
else
{
GPtrArray *focus_order = g_ptr_array_new ();
gboolean ret = FALSE;
/* Try focusing any of the child widgets, depending on the given @direction */
gtk_widget_focus_sort (widget, direction, focus_order);
ret = gtk_widget_focus_move (widget, direction, focus_order);
g_ptr_array_unref (focus_order);
if (ret)
return TRUE;
}
return FALSE;
}
static void
gtk_widget_real_move_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkWidget *toplevel = _gtk_widget_get_toplevel (widget);
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
GtkWidget *focus_child;
GtkWidget *next_focus;
if (widget != toplevel && GTK_IS_WINDOW (toplevel))
{
g_signal_emit (toplevel, widget_signals[MOVE_FOCUS], 0,
direction);
}
focus_child = gtk_root_get_focus (gtk_widget_get_root (widget));
if (focus_child)
next_focus = gtk_widget_get_next_focus (focus_child, direction);
else
next_focus = gtk_widget_get_next_focus (widget, direction);
if (next_focus)
gtk_root_set_focus (priv->root, next_focus);
}
static gboolean
@@ -5594,9 +5466,15 @@ gtk_widget_real_keynav_failed (GtkWidget *widget,
* @widget: a #GtkWidget
* @can_focus: whether or not @widget can own the input focus.
*
* Specifies whether @widget can own the input focus. See
* gtk_widget_grab_focus() for actually setting the input focus on a
* widget.
* Specifies whether @widget can own the input focus.
*
* Note that having @can_focus be %TRUE is only one of the
* necessary conditions for being focusable. A widget must
* also be sensitive and not have a ancestor that is marked
* as not child-focusable in order to receive input focus.
*
* See gtk_widget_grab_focus() for actually setting the input
* focus on a widget.
**/
void
gtk_widget_set_can_focus (GtkWidget *widget,
@@ -5709,16 +5587,14 @@ gtk_widget_has_visible_focus (GtkWidget *widget)
gboolean
gtk_widget_is_focus (GtkWidget *widget)
{
GtkWidget *toplevel;
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
toplevel = _gtk_widget_get_toplevel (widget);
if (priv->root)
return widget == gtk_root_get_focus (priv->root);
if (GTK_IS_WINDOW (toplevel))
return widget == gtk_window_get_focus (GTK_WINDOW (toplevel));
else
return FALSE;
return FALSE;
}
/**
@@ -7580,25 +7456,27 @@ gboolean
gtk_widget_child_focus (GtkWidget *widget,
GtkDirectionType direction)
{
gboolean return_val;
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
GtkWidget *focus_child;
GtkWidget *next_focus;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
if (!_gtk_widget_get_visible (widget) ||
!gtk_widget_is_sensitive (widget))
if (!gtk_widget_get_root (widget))
return FALSE;
/* Emit ::focus in any case, even if can-focus is FALSE,
* since any widget might have child widgets that will take
* focus
*/
focus_child = gtk_root_get_focus (gtk_widget_get_root (widget));
if (focus_child)
next_focus = gtk_widget_get_next_focus (focus_child, direction);
else
next_focus = gtk_widget_get_next_focus (widget, direction);
if (next_focus && gtk_widget_is_ancestor (next_focus, widget))
{
gtk_root_set_focus (priv->root, next_focus);
return TRUE;
}
g_signal_emit (widget,
widget_signals[FOCUS],
0,
direction, &return_val);
return return_val;
return FALSE;
}
/**
@@ -8924,14 +8802,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
priv->state_flags |= GTK_STATE_FLAG_INSENSITIVE;
if (gtk_widget_is_focus (widget) && !gtk_widget_is_sensitive (widget))
{
GtkWidget *window;
window = _gtk_widget_get_toplevel (widget);
if (window && _gtk_widget_is_toplevel (window))
gtk_window_set_focus (GTK_WINDOW (window), NULL);
}
gtk_root_set_focus (priv->root, NULL);
new_flags = priv->state_flags;
@@ -11696,59 +11567,17 @@ gtk_widget_get_overflow (GtkWidget *widget)
return priv->overflow;
}
/**
* gtk_widget_send_focus_change:
* @widget: a #GtkWidget
* @event: a #GdkEvent of type GDK_FOCUS_CHANGE
*
* Sends the focus change @event to @widget
*
* This function is not meant to be used by applications. The only time it
* should be used is when it is necessary for a #GtkWidget to assign focus
* to a widget that is semantically owned by the first widget even though
* its not a direct child - for instance, a search entry in a floating
* window similar to the quick search in #GtkTreeView.
*
* An example of its usage is:
*
* |[<!-- language="C" -->
* GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
*
* fevent->focus_change.type = GDK_FOCUS_CHANGE;
* fevent->focus_change.in = TRUE;
* fevent->focus_change.surface = _gtk_widget_get_surface (widget);
* if (fevent->focus_change.surface != NULL)
* g_object_ref (fevent->focus_change.surface);
*
* gtk_widget_send_focus_change (widget, fevent);
*
* g_object_unref (event);
* ]|
*
* Returns: the return value from the event signal emission: %TRUE
* if the event was handled, and %FALSE otherwise
*/
gboolean
gtk_widget_send_focus_change (GtkWidget *widget,
GdkEvent *event)
void
gtk_widget_set_has_focus (GtkWidget *widget,
gboolean has_focus)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
gboolean res;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (event != NULL && event->any.type == GDK_FOCUS_CHANGE, FALSE);
g_object_ref (widget);
priv->has_focus = event->focus_change.in;
res = gtk_widget_event (widget, event);
if (priv->has_focus == has_focus)
return;
priv->has_focus = has_focus;
g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_HAS_FOCUS]);
g_object_unref (widget);
return res;
}
/**
@@ -13460,8 +13289,6 @@ void
gtk_widget_set_focus_child (GtkWidget *widget,
GtkWidget *child)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_return_if_fail (GTK_IS_WIDGET (widget));
if (child != NULL)
@@ -13470,26 +13297,16 @@ gtk_widget_set_focus_child (GtkWidget *widget,
g_return_if_fail (gtk_widget_get_parent (child) == widget);
}
if (priv->focus_child)
gtk_widget_unset_state_flags (priv->focus_child,
GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_FOCUS_VISIBLE);
GTK_WIDGET_GET_CLASS (widget)->set_focus_child (widget, child);
}
if (child)
{
GtkWidget *toplevel;
GtkStateFlags flags = GTK_STATE_FLAG_FOCUSED;
toplevel = _gtk_widget_get_toplevel (widget);
if (!GTK_IS_WINDOW (toplevel) || gtk_window_get_focus_visible (GTK_WINDOW (toplevel)))
flags |= GTK_STATE_FLAG_FOCUS_VISIBLE;
gtk_widget_set_state_flags (child, flags, FALSE);
}
static void
gtk_widget_real_set_focus_child (GtkWidget *widget,
GtkWidget *child)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_set_object (&priv->focus_child, child);
if (GTK_IS_CONTAINER (widget))
gtk_container_set_focus_child (GTK_CONTAINER (widget), child);
}
/**
@@ -13676,3 +13493,66 @@ gtk_widget_get_height (GtkWidget *widget)
return priv->height;
}
/**
* gtk_widget_get_child_focusable:
* @widget: a #GtkWidget
*
* Gets the value set with gtk_widget_set_child_focusable().
* If you feel a need to use this function, your code probably
* needs reorganization.
*
* This function is only useful for widget implementations and
* never should be called by an application.
*
* Returns: %TRUE if the widget is focusable
**/
gboolean
gtk_widget_get_child_focusable (GtkWidget *widget)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
return priv->child_focusable;
}
/**
* gtk_widget_set_child_focusable:
* @widget: a #GtkWidget
* @focusable: if %TRUE, @widget should be focusable
*
* Sets whether @widget should be able to receive focus
*
* Child focusability will be reset to its default state of %TRUE
* when the widget is removed from its parent.
*
* This function is only useful for widget implementations and
* never should be called by an application.
**/
void
gtk_widget_set_child_focusable (GtkWidget *widget,
gboolean focusable)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_return_if_fail (GTK_IS_WIDGET (widget));
focusable = !!focusable;
if (priv->child_focusable == focusable)
return;
if (focusable)
priv->child_focusable = TRUE;
else
{
GtkWidget *toplevel;
priv->child_focusable = FALSE;
toplevel = _gtk_widget_get_toplevel (widget);
if (toplevel != widget && _gtk_widget_is_toplevel (toplevel))
_gtk_window_unset_focus_and_default (GTK_WINDOW (toplevel), widget);
}
}
+23 -6
View File
@@ -185,7 +185,12 @@ struct _GtkWidget
* %FALSE, and just grabs the focus if @group_cycling is %TRUE.
* @grab_focus: Causes @widget to have the keyboard focus for the
* #GtkWindow its inside.
* @focus:
* @set_focus_child: Allows widget implementations to do extra actions when
* the child containing the focus is changed. Must chain up
* @next_focus_child: Returns the next child that is a candidate for receiving focus.
* Note that the returned child does not have to be focusable itself, it might just
* contain focusable children. The default implementation returns all children, in
* their logical order.
* @move_focus: Signal emitted when a change of focus is requested
* @keynav_failed: Signal emitted if keyboard navigation fails.
* @drag_begin: Signal emitted on the drag source when a drag is
@@ -277,8 +282,11 @@ struct _GtkWidgetClass
gboolean group_cycling);
/* explicit focus */
void (* grab_focus) (GtkWidget *widget);
gboolean (* focus) (GtkWidget *widget,
void (* grab_focus) (GtkWidget *widget);
void (* set_focus_child) (GtkWidget *widget,
GtkWidget *child);
GtkWidget * (* next_focus_child) (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction);
/* keyboard navigation */
@@ -451,9 +459,6 @@ gboolean gtk_widget_mnemonic_activate (GtkWidget *widget,
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_event (GtkWidget *widget,
const GdkEvent *event);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_send_focus_change (GtkWidget *widget,
GdkEvent *event);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_activate (GtkWidget *widget);
@@ -480,6 +485,9 @@ gboolean gtk_widget_has_visible_focus (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_widget_grab_focus (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
GtkWidget *gtk_widget_get_next_focus (GtkWidget *widget,
GtkDirectionType direction);
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_focus_on_click (GtkWidget *widget,
gboolean focus_on_click);
GDK_AVAILABLE_IN_ALL
@@ -1062,6 +1070,15 @@ void gtk_widget_insert_before (GtkWidget *widget,
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_focus_child (GtkWidget *widget,
GtkWidget *child);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_widget_get_focus_child (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_child_focusable (GtkWidget *widget,
gboolean focusable);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_get_child_focusable (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_widget_snapshot_child (GtkWidget *widget,
GtkWidget *child,
+101 -26
View File
@@ -15,6 +15,8 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkwidgetprivate.h"
typedef struct _CompareInfo CompareInfo;
@@ -155,15 +157,11 @@ static gboolean
old_focus_coords (GtkWidget *widget,
graphene_rect_t *old_focus_bounds)
{
GtkWidget *toplevel = _gtk_widget_get_toplevel (widget);
GtkWidget *old_focus;
if (GTK_IS_WINDOW (toplevel))
{
old_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
if (old_focus)
return gtk_widget_compute_bounds (old_focus, widget, old_focus_bounds);
}
old_focus = gtk_root_get_focus (gtk_widget_get_root (widget));
if (old_focus)
return gtk_widget_compute_bounds (old_focus, widget, old_focus_bounds);
return FALSE;
}
@@ -426,7 +424,9 @@ gtk_widget_focus_sort (GtkWidget *widget,
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
if (_gtk_widget_get_realized (child))
if (_gtk_widget_get_realized (child) &&
gtk_widget_get_child_focusable (child) &&
gtk_widget_get_sensitive (child))
g_ptr_array_add (focus_order, child);
}
}
@@ -451,36 +451,111 @@ gtk_widget_focus_sort (GtkWidget *widget,
}
}
gboolean
gtk_widget_focus_move (GtkWidget *widget,
GtkDirectionType direction,
GPtrArray *focus_order)
gtk_widget_can_take_focus (GtkWidget *widget)
{
GtkWidget *focus_child = gtk_widget_get_focus_child (widget);
int i;
GtkWidget *w;
for (i = 0; i < focus_order->len; i ++)
if (!gtk_widget_is_sensitive (widget) ||
!gtk_widget_get_can_focus (widget))
return FALSE;
w = widget;
do {
if (!gtk_widget_get_child_focusable (w))
return FALSE;
w = gtk_widget_get_parent (w);
} while (w != NULL);
return TRUE;
}
/**
* gtk_widget_get_next_focus:
* @widget: a #GtkWidget
* @direction: diretion to move in
*
* Finds the widget that would get focused if @widget was
* the focus widget, and focus was moved in @direcion.
*
* Returns: (transfer none): the next focus widget
*/
GtkWidget *
gtk_widget_get_next_focus (GtkWidget *widget,
GtkDirectionType dir)
{
GtkWidget *prev;
GtkWidget *next;
GHashTable *seen;
seen = g_hash_table_new (g_direct_hash, g_direct_equal);
prev = NULL;
do {
next = GTK_WIDGET_GET_CLASS (widget)->next_focus_child (widget, prev, dir);
if (next == NULL)
{
if (GTK_IS_ROOT (widget))
{
prev = NULL;
}
else
{
prev = widget;
widget = gtk_widget_get_parent (widget);
}
}
else if (gtk_widget_can_take_focus (next))
{
break;
}
else if (g_hash_table_contains (seen, next))
{
next = NULL;
break;
}
else
{
g_hash_table_add (seen, next);
widget = next;
prev = NULL;
}
} while (widget);
g_hash_table_unref (seen);
return next;
}
GtkWidget *
gtk_widget_next_focus_child (GtkWidget *widget,
GtkWidget *focus_child,
GtkDirectionType direction)
{
GPtrArray *focus_order;
int i;
GtkWidget *next_child = NULL;
focus_order = g_ptr_array_new ();
gtk_widget_focus_sort (widget, direction, focus_order);
for (i = 0; i < focus_order->len; i++)
{
GtkWidget *child = g_ptr_array_index (focus_order, i);
if (focus_child)
{
if (focus_child == child)
{
focus_child = NULL;
if (gtk_widget_child_focus (child, direction))
return TRUE;
}
focus_child = NULL;
}
else if (_gtk_widget_is_drawable (child) &&
gtk_widget_is_ancestor (child, widget))
else
{
if (gtk_widget_child_focus (child, direction))
return TRUE;
next_child = child;
break;
}
}
return FALSE;
g_ptr_array_unref (focus_order);
return next_child;
}
+15 -5
View File
@@ -72,6 +72,7 @@ struct _GtkWidgetPrivate
guint has_grab : 1;
guint shadowed : 1;
guint child_visible : 1;
guint child_focusable : 1;
guint multidevice : 1;
guint can_pick : 1;
@@ -242,6 +243,12 @@ GdkSurface * _gtk_widget_get_device_surface (GtkWidget *widget,
GdkDevice *device);
GList * _gtk_widget_list_devices (GtkWidget *widget);
void gtk_synthesize_crossing_events (GtkWindow *toplevel,
GtkWidget *from,
GtkWidget *to,
GdkEvent *event,
GdkCrossingMode mode);
void _gtk_widget_synthesize_crossing (GtkWidget *from,
GtkWidget *to,
GdkDevice *device,
@@ -311,14 +318,14 @@ void gtk_widget_forall (GtkWidget
GtkCallback callback,
gpointer user_data);
GtkWidget *gtk_widget_get_focus_child (GtkWidget *widget);
void gtk_widget_focus_sort (GtkWidget *widget,
GtkDirectionType direction,
GPtrArray *focus_order);
gboolean gtk_widget_focus_move (GtkWidget *widget,
GtkDirectionType direction,
GPtrArray *focus_order);
GtkWidget * gtk_widget_next_focus_child (GtkWidget *widget,
GtkWidget *child,
GtkDirectionType direction);
void gtk_widget_set_has_focus (GtkWidget *widget,
gboolean has_focus);
void gtk_widget_get_surface_allocation (GtkWidget *widget,
GtkAllocation *allocation);
@@ -335,6 +342,9 @@ gboolean gtk_widget_run_controllers (GtkWidget
const GdkEvent *event,
GtkPropagationPhase phase);
gboolean gtk_widget_can_take_focus (GtkWidget *widget);
/* inline getters */
static inline GtkWidget *
+63 -361
View File
@@ -122,16 +122,12 @@
* elements representing the #GtkAccelGroup objects you want to add to
* your window (synonymous with gtk_window_add_accel_group().
*
* It also supports the <initial-focus> element, whose name property names
* the widget to receive the focus when the window is mapped.
*
* An example of a UI definition fragment with accel groups:
* |[
* <object class="GtkWindow">
* <accel-groups>
* <group name="accelgroup1"/>
* </accel-groups>
* <initial-focus name="thunderclap"/>
* </object>
*
* ...
@@ -299,7 +295,6 @@ static const char *dnd_dest_targets [] = {
#endif
enum {
SET_FOCUS,
ACTIVATE_FOCUS,
ACTIVATE_DEFAULT,
KEYS_CHANGED,
@@ -436,12 +431,6 @@ static void gtk_window_remove (GtkContainer *container,
static void gtk_window_forall (GtkContainer *container,
GtkCallback callback,
gpointer callback_data);
static gint gtk_window_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_window_move_focus (GtkWidget *widget,
GtkDirectionType dir);
static void gtk_window_real_set_focus (GtkWindow *window,
GtkWidget *focus);
static void gtk_window_real_activate_default (GtkWindow *window);
static void gtk_window_real_activate_focus (GtkWindow *window);
@@ -804,8 +793,6 @@ gtk_window_class_init (GtkWindowClass *klass)
widget_class->realize = gtk_window_realize;
widget_class->unrealize = gtk_window_unrealize;
widget_class->size_allocate = gtk_window_size_allocate;
widget_class->focus = gtk_window_focus;
widget_class->move_focus = gtk_window_move_focus;
widget_class->measure = gtk_window_measure;
widget_class->state_flags_changed = gtk_window_state_flags_changed;
widget_class->style_updated = gtk_window_style_updated;
@@ -816,8 +803,6 @@ gtk_window_class_init (GtkWindowClass *klass)
container_class->remove = gtk_window_remove;
container_class->forall = gtk_window_forall;
klass->set_focus = gtk_window_real_set_focus;
klass->activate_default = gtk_window_real_activate_default;
klass->activate_focus = gtk_window_real_activate_focus;
klass->keys_changed = gtk_window_keys_changed;
@@ -1118,24 +1103,7 @@ gtk_window_class_init (GtkWindowClass *klass)
GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, LAST_ARG, window_props);
/**
* GtkWindow:set-focus:
* @window: the window which received the signal
* @widget: (nullable): the newly focused widget (or %NULL for no focus)
*
* This signal is emitted whenever the currently focused widget in
* this window changes.
*/
window_signals[SET_FOCUS] =
g_signal_new (I_("set-focus"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWindowClass, set_focus),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
gtk_root_install_properties (gobject_class, LAST_ARG);
/**
* GtkWindow::activate-focus:
@@ -2095,6 +2063,9 @@ gtk_window_set_property (GObject *object,
case PROP_FOCUS_VISIBLE:
gtk_window_set_focus_visible (window, g_value_get_boolean (value));
break;
case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET:
gtk_window_set_focus (window, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2210,6 +2181,9 @@ gtk_window_get_property (GObject *object,
case PROP_IS_MAXIMIZED:
g_value_set_boolean (value, gtk_window_is_maximized (window));
break;
case LAST_ARG + GTK_ROOT_PROP_FOCUS_WIDGET:
g_value_set_object (value, gtk_window_get_focus (window));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2365,55 +2339,6 @@ static const GMarkupParser window_parser =
window_start_element
};
typedef struct {
GObject *object;
GtkBuilder *builder;
gchar *name;
gint line;
gint col;
} NameSubParserData;
static void
focus_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **names,
const gchar **values,
gpointer user_data,
GError **error)
{
NameSubParserData *data = (NameSubParserData*)user_data;
if (strcmp (element_name, "initial-focus") == 0)
{
const gchar *name;
if (!_gtk_builder_check_parent (data->builder, context, "object", error))
return;
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "name", &name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, context, error);
return;
}
data->name = g_strdup (name);
g_markup_parse_context_get_position (context, &data->line, &data->col);
}
else
{
_gtk_builder_error_unhandled_tag (data->builder, context,
"GtkWindow", element_name,
error);
}
}
static const GMarkupParser focus_parser =
{
focus_start_element
};
static gboolean
gtk_window_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
@@ -2441,21 +2366,6 @@ gtk_window_buildable_custom_tag_start (GtkBuildable *buildable,
return TRUE;
}
if (strcmp (tagname, "initial-focus") == 0)
{
NameSubParserData *data;
data = g_slice_new0 (NameSubParserData);
data->name = NULL;
data->object = G_OBJECT (buildable);
data->builder = builder;
*parser = focus_parser;
*parser_data = data;
return TRUE;
}
return FALSE;
}
@@ -2478,23 +2388,6 @@ gtk_window_buildable_custom_finished (GtkBuildable *buildable,
g_slice_free (GSListSubParserData, data);
}
if (strcmp (tagname, "initial-focus") == 0)
{
NameSubParserData *data = (NameSubParserData*)user_data;
if (data->name)
{
GObject *object;
object = _gtk_builder_lookup_object (builder, data->name, data->line, data->col);
if (object)
gtk_window_set_focus (GTK_WINDOW (buildable), GTK_WIDGET (object));
g_free (data->name);
}
g_slice_free (NameSubParserData, data);
}
}
static GdkDisplay *
@@ -2771,70 +2664,6 @@ gtk_window_get_role (GtkWindow *window)
return priv->wm_role;
}
/**
* gtk_window_set_focus:
* @window: a #GtkWindow
* @focus: (allow-none): widget to be the new focus widget, or %NULL to unset
* any focus widget for the toplevel window.
*
* If @focus is not the current focus widget, and is focusable, sets
* it as the focus widget for the window. If @focus is %NULL, unsets
* the focus widget for this window. To set the focus to a particular
* widget in the toplevel, it is usually more convenient to use
* gtk_widget_grab_focus() instead of this function.
**/
void
gtk_window_set_focus (GtkWindow *window,
GtkWidget *focus)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
GtkWidget *parent;
g_return_if_fail (GTK_IS_WINDOW (window));
if (focus)
{
g_return_if_fail (GTK_IS_WIDGET (focus));
g_return_if_fail (gtk_widget_get_can_focus (focus));
if (!gtk_widget_get_visible (GTK_WIDGET (window)))
priv->initial_focus = focus;
else
gtk_widget_grab_focus (focus);
}
else
{
/* Clear the existing focus chain, so that when we focus into
* the window again, we start at the beginnning.
*/
GtkWidget *widget = priv->focus_widget;
if (widget)
{
while ((parent = _gtk_widget_get_parent (widget)))
{
widget = parent;
gtk_widget_set_focus_child (widget, NULL);
}
}
_gtk_window_internal_set_focus (window, NULL);
}
}
void
_gtk_window_internal_set_focus (GtkWindow *window,
GtkWidget *focus)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
g_return_if_fail (GTK_IS_WINDOW (window));
priv->initial_focus = NULL;
if ((priv->focus_widget != focus) ||
(focus && !gtk_widget_has_focus (focus)))
g_signal_emit (window, window_signals[SET_FOCUS], 0, focus);
}
/**
* gtk_window_set_default:
* @window: a #GtkWindow
@@ -5774,7 +5603,7 @@ gtk_window_show (GtkWidget *widget)
if (priv->initial_focus)
gtk_window_set_focus (window, priv->initial_focus);
else
gtk_window_move_focus (widget, GTK_DIR_TAB_FORWARD);
gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
}
if (priv->modal)
@@ -7072,42 +6901,31 @@ gtk_window_real_activate_focus (GtkWindow *window)
static void
do_focus_change (GtkWidget *widget,
gboolean in)
gboolean in)
{
GdkSeat *seat;
GList *devices, *d;
g_object_ref (widget);
GdkDevice *device;
GdkEvent *event;
seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_KEYBOARD);
devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat));
device = gdk_seat_get_keyboard (seat);
for (d = devices; d; d = d->next)
{
GdkDevice *dev = d->data;
GdkEvent *fevent;
GdkSurface *surface;
event = gdk_event_new (GDK_FOCUS_CHANGE);
gdk_event_set_display (event, gtk_widget_get_display (widget));
gdk_event_set_device (event, device);
surface = _gtk_widget_get_surface (widget);
event->any.type = GDK_FOCUS_CHANGE;
event->any.surface = _gtk_widget_get_surface (widget);
if (event->any.surface)
g_object_ref (event->any.surface);
event->focus_change.in = in;
event->focus_change.mode = GDK_CROSSING_STATE_CHANGED;
event->focus_change.detail = GDK_NOTIFY_ANCESTOR;
fevent = gdk_event_new (GDK_FOCUS_CHANGE);
gdk_event_set_display (fevent, gtk_widget_get_display (widget));
gtk_widget_set_has_focus (widget, in);
gtk_widget_event (widget, event);
fevent->any.type = GDK_FOCUS_CHANGE;
fevent->any.surface = surface;
if (surface)
g_object_ref (surface);
fevent->focus_change.in = in;
gdk_event_set_device (fevent, dev);
gtk_widget_send_focus_change (widget, fevent);
g_object_unref (fevent);
}
g_list_free (devices);
g_object_unref (widget);
g_object_unref (event);
}
static gboolean
@@ -7233,172 +7051,56 @@ gtk_window_forall (GtkContainer *container,
(* callback) (priv->title_box, callback_data);
}
static gboolean
gtk_window_focus (GtkWidget *widget,
GtkDirectionType direction)
{
GtkWindow *window = GTK_WINDOW (widget);
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
GtkBin *bin;
GtkContainer *container;
GtkWidget *child;
GtkWidget *old_focus_child;
GtkWidget *parent;
if (!_gtk_widget_is_toplevel (widget))
return GTK_WIDGET_CLASS (gtk_window_parent_class)->focus (widget, direction);
container = GTK_CONTAINER (widget);
bin = GTK_BIN (widget);
old_focus_child = gtk_widget_get_focus_child (widget);
/* We need a special implementation here to deal properly with wrapping
* around in the tab chain without the danger of going into an
* infinite loop.
*/
if (old_focus_child)
{
if (gtk_widget_child_focus (old_focus_child, direction))
return TRUE;
}
if (priv->focus_widget)
{
if (direction == GTK_DIR_LEFT ||
direction == GTK_DIR_RIGHT ||
direction == GTK_DIR_UP ||
direction == GTK_DIR_DOWN)
{
return FALSE;
}
/* Wrapped off the end, clear the focus setting for the toplpevel */
parent = _gtk_widget_get_parent (priv->focus_widget);
while (parent)
{
gtk_widget_set_focus_child (parent, NULL);
parent = _gtk_widget_get_parent (parent);
}
gtk_window_set_focus (GTK_WINDOW (container), NULL);
}
/* Now try to focus the first widget in the window,
* taking care to hook titlebar widgets into the
* focus chain.
*/
if (priv->title_box != NULL &&
old_focus_child != NULL &&
priv->title_box != old_focus_child)
child = priv->title_box;
else
child = gtk_bin_get_child (bin);
if (child)
{
if (gtk_widget_child_focus (child, direction))
return TRUE;
else if (priv->title_box != NULL &&
priv->title_box != child &&
gtk_widget_child_focus (priv->title_box, direction))
return TRUE;
}
return FALSE;
}
static void
gtk_window_move_focus (GtkWidget *widget,
GtkDirectionType dir)
{
if (!_gtk_widget_is_toplevel (widget))
{
GTK_WIDGET_CLASS (gtk_window_parent_class)->move_focus (widget, dir);
return;
}
gtk_widget_child_focus (widget, dir);
if (!gtk_widget_get_focus_child (widget))
gtk_window_set_focus (GTK_WINDOW (widget), NULL);
}
static void
gtk_window_real_set_focus (GtkWindow *window,
GtkWidget *focus)
/**
* gtk_window_set_focus:
* @window: a #GtkWindow
* @focus: (allow-none): widget to be the new focus widget, or %NULL to unset
* any focus widget for the toplevel window.
*
* If @focus is not the current focus widget, and is focusable, sets
* it as the focus widget for the window. If @focus is %NULL, unsets
* the focus widget for this window. To set the focus to a particular
* widget in the toplevel, it is usually more convenient to use
* gtk_widget_grab_focus() instead of this function.
**/
void
gtk_window_set_focus (GtkWindow *window,
GtkWidget *focus)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
GtkWidget *old_focus = priv->focus_widget;
GtkWidget *old_focus = NULL;
GdkSeat *seat;
GdkDevice *device;
GdkEvent *event;
if (old_focus)
{
g_object_ref (old_focus);
g_object_freeze_notify (G_OBJECT (old_focus));
}
if (focus)
{
g_object_ref (focus);
g_object_freeze_notify (G_OBJECT (focus));
}
g_return_if_fail (GTK_IS_WINDOW (window));
if (focus && !gtk_widget_can_take_focus (focus))
return;
if (priv->focus_widget)
{
if (gtk_widget_get_receives_default (priv->focus_widget) &&
(priv->focus_widget != priv->default_widget))
{
_gtk_widget_set_has_default (priv->focus_widget, FALSE);
old_focus = g_object_ref (priv->focus_widget);
g_set_object (&priv->focus_widget, NULL);
if (priv->default_widget)
_gtk_widget_set_has_default (priv->default_widget, TRUE);
}
seat = gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (window)));
device = gdk_seat_get_keyboard (seat);
priv->focus_widget = NULL;
event = gdk_event_new (GDK_FOCUS_CHANGE);
gdk_event_set_display (event, gtk_widget_get_display (GTK_WIDGET (window)));
gdk_event_set_device (event, device);
event->any.surface = _gtk_widget_get_surface (GTK_WIDGET (window));
if (event->any.surface)
g_object_ref (event->any.surface);
if (priv->is_active)
do_focus_change (old_focus, FALSE);
gtk_synthesize_crossing_events (window, old_focus, focus, event, GDK_CROSSING_NORMAL);
g_object_notify (G_OBJECT (old_focus), "is-focus");
}
g_object_unref (event);
/* The above notifications may have set a new focus widget,
* if so, we don't want to override it.
*/
if (focus && !priv->focus_widget)
{
priv->focus_widget = focus;
g_set_object (&priv->focus_widget, focus);
if (gtk_widget_get_receives_default (priv->focus_widget) &&
(priv->focus_widget != priv->default_widget))
{
if (gtk_widget_get_can_default (priv->focus_widget))
_gtk_widget_set_has_default (priv->focus_widget, TRUE);
g_clear_object (&old_focus);
if (priv->default_widget)
_gtk_widget_set_has_default (priv->default_widget, FALSE);
}
if (priv->is_active)
do_focus_change (priv->focus_widget, TRUE);
/* It's possible for do_focus_change() above to have callbacks
* that clear priv->focus_widget here.
*/
if (priv->focus_widget)
g_object_notify (G_OBJECT (priv->focus_widget), "is-focus");
}
if (old_focus)
{
g_object_thaw_notify (G_OBJECT (old_focus));
g_object_unref (old_focus);
}
if (focus)
{
g_object_thaw_notify (G_OBJECT (focus));
g_object_unref (focus);
}
g_object_notify (G_OBJECT (window), "focus-widget");
}
static void
-5
View File
@@ -70,11 +70,6 @@ struct _GtkWindowClass
{
GtkBinClass parent_class;
/*< public >*/
void (* set_focus) (GtkWindow *window,
GtkWidget *focus);
/* G_SIGNAL_ACTION signals for keybindings */
void (* activate_focus) (GtkWindow *window);
+135
View File
@@ -0,0 +1,135 @@
#include "config.h"
#include "focusoverlay.h"
#include "gtkwidgetprivate.h"
struct _GtkFocusOverlay
{
GtkInspectorOverlay parent_instance;
};
struct _GtkFocusOverlayClass
{
GtkInspectorOverlayClass parent_class;
};
G_DEFINE_TYPE (GtkFocusOverlay, gtk_focus_overlay, GTK_TYPE_INSPECTOR_OVERLAY)
static void
draw_focus_location (GtkWidget *widget,
GtkWidget *child,
GtkSnapshot *snapshot,
const char *color,
const char *text)
{
GtkAllocation allocation;
graphene_rect_t bounds;
PangoLayout *layout;
PangoRectangle rect;
GskRoundedRect rrect;
int x, y;
GdkRGBA rgba;
if (child == NULL)
return;
gdk_rgba_parse (&rgba, color);
gtk_widget_get_allocation (child, &allocation);
gtk_snapshot_save (snapshot);
gtk_widget_translate_coordinates (child, widget, 0, 0, &x, &y);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
gtk_snapshot_push_debug (snapshot, "Widget focus debugging");
layout = gtk_widget_create_pango_layout (widget, NULL);
pango_layout_set_markup (layout, text, -1);
pango_layout_get_extents (layout, NULL, &rect);
graphene_rect_init (&bounds,
0, 0,
rect.width / PANGO_SCALE + 20,
rect.height / PANGO_SCALE + 20);
gsk_rounded_rect_init_from_rect (&rrect, &bounds, 10.0f);
gtk_snapshot_push_rounded_clip (snapshot, &rrect);
gtk_snapshot_append_color (snapshot, &rgba, &bounds);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (10, 10));
gtk_snapshot_append_layout (snapshot, layout, &(GdkRGBA) { 1, 1, 1, 1 });
g_object_unref (layout);
gtk_snapshot_pop (snapshot);
gtk_snapshot_pop (snapshot);
gtk_snapshot_restore (snapshot);
}
static void
draw_focus_locations (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkWidget *focus;
GtkWidget *next;
int i;
char text[20];
if (!gtk_widget_get_mapped (widget))
return;
focus = gtk_root_get_focus (GTK_ROOT (widget));
if (focus == NULL)
return;
draw_focus_location (widget, focus, snapshot, "rgba(0,0,255,0.4)", "Focus");
next = focus;
for (i = 1; i < 4; i++)
{
g_snprintf (text, 20, "<big>⇥</big> %d", i);
next = gtk_widget_get_next_focus (next, GTK_DIR_TAB_FORWARD);
if (!next)
break;
draw_focus_location (widget, next, snapshot, "rgba(255,0,255,0.4)", text);
}
next = focus;
for (i = 1; i < 4; i++)
{
g_snprintf (text, 20, "<big>⇤</big> %d", i);
next = gtk_widget_get_next_focus (next, GTK_DIR_TAB_BACKWARD);
if (!next)
break;
draw_focus_location (widget, next, snapshot, "rgba(255,0,255,0.4)", text);
}
}
static void
gtk_focus_overlay_snapshot (GtkInspectorOverlay *overlay,
GtkSnapshot *snapshot,
GskRenderNode *node,
GtkWidget *widget)
{
draw_focus_locations (widget, snapshot);
}
static void
gtk_focus_overlay_init (GtkFocusOverlay *self)
{
}
static void
gtk_focus_overlay_class_init (GtkFocusOverlayClass *klass)
{
GtkInspectorOverlayClass *overlay_class = (GtkInspectorOverlayClass *)klass;
overlay_class->snapshot = gtk_focus_overlay_snapshot;
}
GtkInspectorOverlay *
gtk_focus_overlay_new (void)
{
return g_object_new (GTK_TYPE_FOCUS_OVERLAY, NULL);
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef __GTK_FOCUS_OVERLAY_H__
#define __GTK_FOCUS_OVERLAY_H__
#include "inspectoroverlay.h"
G_BEGIN_DECLS
#define GTK_TYPE_FOCUS_OVERLAY (gtk_focus_overlay_get_type ())
G_DECLARE_FINAL_TYPE (GtkFocusOverlay, gtk_focus_overlay, GTK, FOCUS_OVERLAY, GtkInspectorOverlay)
GtkInspectorOverlay * gtk_focus_overlay_new (void);
G_END_DECLS
#endif
+1
View File
@@ -6,6 +6,7 @@ inspector_sources = files(
'css-editor.c',
'css-node-tree.c',
'data-list.c',
'focusoverlay.c',
'fpsoverlay.c',
'general.c',
'graphdata.c',
-50
View File
@@ -46,9 +46,6 @@ struct _GtkInspectorMiscInfoPrivate {
GtkWidget *default_widget_row;
GtkWidget *default_widget;
GtkWidget *default_widget_button;
GtkWidget *focus_widget_row;
GtkWidget *focus_widget;
GtkWidget *focus_widget_button;
GtkWidget *mnemonic_label_row;
GtkWidget *mnemonic_label;
GtkWidget *request_mode_row;
@@ -217,43 +214,6 @@ show_default_widget (GtkWidget *button, GtkInspectorMiscInfo *sl)
show_object (sl, G_OBJECT (widget), "properties");
}
static void
update_focus_widget (GtkInspectorMiscInfo *sl)
{
GtkWidget *widget;
widget = gtk_window_get_focus (GTK_WINDOW (sl->priv->object));
if (widget)
{
gchar *tmp;
tmp = g_strdup_printf ("%p", widget);
gtk_label_set_label (GTK_LABEL (sl->priv->focus_widget), tmp);
g_free (tmp);
gtk_widget_set_sensitive (sl->priv->focus_widget_button, TRUE);
}
else
{
gtk_label_set_label (GTK_LABEL (sl->priv->focus_widget), "NULL");
gtk_widget_set_sensitive (sl->priv->focus_widget_button, FALSE);
}
}
static void
set_focus_cb (GtkWindow *window, GtkWidget *focus, GtkInspectorMiscInfo *sl)
{
update_focus_widget (sl);
}
static void
show_focus_widget (GtkWidget *button, GtkInspectorMiscInfo *sl)
{
GtkWidget *widget;
widget = gtk_window_get_focus (GTK_WINDOW (sl->priv->object));
if (widget)
show_object (sl, G_OBJECT (widget), "properties");
}
static void
show_mnemonic_label (GtkWidget *button, GtkInspectorMiscInfo *sl)
{
@@ -358,7 +318,6 @@ update_info (gpointer data)
if (GTK_IS_WINDOW (sl->priv->object))
{
update_default_widget (sl);
update_focus_widget (sl);
}
if (GDK_IS_FRAME_CLOCK (sl->priv->object))
@@ -408,7 +367,6 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl,
if (sl->priv->object)
{
g_signal_handlers_disconnect_by_func (sl->priv->object, state_flags_changed, sl);
g_signal_handlers_disconnect_by_func (sl->priv->object, set_focus_cb, sl);
g_signal_handlers_disconnect_by_func (sl->priv->object, allocation_changed, sl);
disconnect_each_other (sl->priv->object, G_OBJECT (sl));
disconnect_each_other (sl, sl->priv->object);
@@ -475,14 +433,10 @@ gtk_inspector_misc_info_set_object (GtkInspectorMiscInfo *sl,
if (GTK_IS_WINDOW (object))
{
gtk_widget_show (sl->priv->default_widget_row);
gtk_widget_show (sl->priv->focus_widget_row);
g_signal_connect_object (object, "set-focus", G_CALLBACK (set_focus_cb), sl, G_CONNECT_AFTER);
}
else
{
gtk_widget_hide (sl->priv->default_widget_row);
gtk_widget_hide (sl->priv->focus_widget_row);
}
if (GDK_IS_FRAME_CLOCK (object))
@@ -595,9 +549,6 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget_row);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, default_widget_button);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget_row);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, focus_widget_button);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, mnemonic_label_row);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, mnemonic_label);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, request_mode_row);
@@ -631,7 +582,6 @@ gtk_inspector_misc_info_class_init (GtkInspectorMiscInfoClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMiscInfo, child_visible);
gtk_widget_class_bind_template_callback (widget_class, show_default_widget);
gtk_widget_class_bind_template_callback (widget_class, show_focus_widget);
gtk_widget_class_bind_template_callback (widget_class, show_frame_clock);
}
-37
View File
@@ -157,42 +157,6 @@
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow" id="focus_widget_row">
<property name="activatable">0</property>
<child>
<object class="GtkBox">
<property name="margin">10</property>
<property name="spacing">40</property>
<child>
<object class="GtkLabel" id="focus_widget_label">
<property name="label" translatable="yes">Focus Widget</property>
<property name="halign">start</property>
<property name="valign">baseline</property>
<property name="xalign">0.0</property>
<property name="hexpand">1</property>
</object>
</child>
<child>
<object class="GtkLabel" id="focus_widget">
<property name="selectable">1</property>
<property name="halign">end</property>
<property name="valign">baseline</property>
<property name="ellipsize">end</property>
</object>
</child>
<child>
<object class="GtkButton" id="focus_widget_button">
<property name="halign">end</property>
<property name="valign">baseline</property>
<property name="label" translatable="yes">Properties</property>
<signal name="clicked" handler="show_focus_widget"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow" id="mnemonic_label_row">
<property name="activatable">0</property>
@@ -616,7 +580,6 @@
<widget name="state_label"/>
<widget name="buildable_id_label"/>
<widget name="default_widget_label"/>
<widget name="focus_widget_label"/>
<widget name="frame_clock_label"/>
</widgets>
</object>
+38
View File
@@ -20,6 +20,7 @@
#include "visual.h"
#include "focusoverlay.h"
#include "fpsoverlay.h"
#include "updatesoverlay.h"
#include "layoutoverlay.h"
@@ -83,6 +84,7 @@ struct _GtkInspectorVisualPrivate
GtkInspectorOverlay *fps_overlay;
GtkInspectorOverlay *updates_overlay;
GtkInspectorOverlay *layout_overlay;
GtkInspectorOverlay *focus_overlay;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorVisual, gtk_inspector_visual, GTK_TYPE_SCROLLED_WINDOW)
@@ -298,6 +300,41 @@ updates_activate (GtkSwitch *sw,
redraw_everything ();
}
static void
focus_activate (GtkSwitch *sw,
GParamSpec *pspec,
GtkInspectorVisual *vis)
{
GtkInspectorVisualPrivate *priv = vis->priv;
GtkInspectorWindow *iw;
gboolean focus;
focus = gtk_switch_get_active (sw);
iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (vis)));
if (iw == NULL)
return;
if (focus)
{
if (priv->focus_overlay == NULL)
{
priv->focus_overlay = gtk_focus_overlay_new ();
gtk_inspector_window_add_overlay (iw, priv->focus_overlay);
g_object_unref (priv->focus_overlay);
}
}
else
{
if (priv->focus_overlay != NULL)
{
gtk_inspector_window_remove_overlay (iw, priv->focus_overlay);
priv->focus_overlay = NULL;
}
}
redraw_everything ();
}
static void
baselines_activate (GtkSwitch *sw)
{
@@ -952,6 +989,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_entry);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_adjustment);
gtk_widget_class_bind_template_callback (widget_class, focus_activate);
gtk_widget_class_bind_template_callback (widget_class, fps_activate);
gtk_widget_class_bind_template_callback (widget_class, updates_activate);
gtk_widget_class_bind_template_callback (widget_class, direction_changed);
+27
View File
@@ -371,6 +371,33 @@
<child>
<object class="GtkListBox" id="debug_box">
<property name="selection-mode">none</property>
<child>
<object class="GtkListBoxRow">
<property name="activatable">0</property>
<child>
<object class="GtkBox">
<property name="margin">10</property>
<property name="spacing">40</property>
<child>
<object class="GtkLabel" id="focus_label">
<property name="label" translatable="yes">Show focus overlay</property>
<property name="halign">start</property>
<property name="valign">baseline</property>
<property name="xalign">0.0</property>
</object>
</child>
<child>
<object class="GtkSwitch" id="focus_switch">
<property name="halign">end</property>
<property name="valign">baseline</property>
<property name="hexpand">1</property>
<signal name="notify::active" handler="focus_activate"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow">
<property name="activatable">0</property>
-1
View File
@@ -239,6 +239,5 @@
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="apply">confirm_button</action-widget>
</action-widgets>
<initial-focus name="name_entry"/>
</object>
</interface>
+1 -1
View File
@@ -344,7 +344,7 @@ popup_context_menu (GtkToolbar *toolbar, gint x, gint y, gint button_number)
GtkWidget *widget;
window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (toolbar)));
widget = gtk_window_get_focus (window);
widget = gtk_root_get_focus (GTK_ROOT (window));
if (!widget)
widget = GTK_WIDGET (toolbar);
+9
View File
@@ -0,0 +1,9 @@
button1
button2
button3
button4
button5
button6
button7
button8
button9
+60
View File
@@ -0,0 +1,60 @@
<interface>
<object class="GtkWindow" id="window1">
<child>
<object class="GtkBox" id="box">
<property name="orientation">horizontal</property>
<child>
<object class="GtkBox" id="box1">
<property name="orientation">vertical</property>
<child>
<object class="GtkButton" id="button1">
</object>
</child>
<child>
<object class="GtkButton" id="button2">
</object>
</child>
<child>
<object class="GtkButton" id="button3">
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="orientation">vertical</property>
<child>
<object class="GtkButton" id="button4">
</object>
</child>
<child>
<object class="GtkButton" id="button5">
</object>
</child>
<child>
<object class="GtkButton" id="button6">
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="box3">
<property name="orientation">vertical</property>
<child>
<object class="GtkButton" id="button7">
</object>
</child>
<child>
<object class="GtkButton" id="button8">
</object>
</child>
<child>
<object class="GtkButton" id="button9">
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
+4
View File
@@ -0,0 +1,4 @@
[Test]
Exec=@libexecdir@/installed-tests/gtk-4.0/gtk/focus/test-focus --tap -k
Type=session
Output=TAP
+34
View File
@@ -0,0 +1,34 @@
testexecdir = join_paths(installed_test_bindir, 'gtk', 'focus-chain')
testdatadir = join_paths(installed_test_datadir, 'gtk', 'focus-chain')
test_nodes = executable('test-focus', 'test-focus.c',
install: get_option('install-tests'),
install_dir: testexecdir,
dependencies: libgtk_dep)
test('nodes', test_nodes,
args: [ '--tap', '-k' ],
env: [ 'GIO_USE_VOLUME_MONITOR=unix',
'GSETTINGS_BACKEND=memory',
'GTK_CSD=1',
'G_ENABLE_DIAGNOSTIC=0',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
],
suite: 'css')
test_data = [
'widget-factory.ui',
'widget-factory.txt',
]
if get_option('install-tests')
conf = configuration_data()
conf.set('libexecdir', gtk_libexecdir)
configure_file(input: 'focus.test.in',
output: 'focus.test',
configuration: conf,
install_dir: testdatadir)
install_data(test_data, install_dir: testexecdir)
endif
+2
View File
@@ -0,0 +1,2 @@
notebook1
page1
+37
View File
@@ -0,0 +1,37 @@
<interface>
<object class="GtkWindow" id="window1">
<property name="type">popup</property>
<child>
<object class="GtkNotebook" id="notebook1">
<child>
<object class="GtkNotebookPage">
<property name="child">
<object class="GtkButton" id="page1">
<property name="label" translatable="yes">Yes</property>
</object>
</property>
<property name="tab">
<object class="GtkLabel" id="tab1">
<property name="label" translatable="yes">Tab 1</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkNotebookPage">
<property name="child">
<object class="GtkButton" id="page2">
<property name="label" translatable="yes">No</property>
</object>
</property>
<property name="tab">
<object class="GtkLabel" id="tab2">
<property name="label" translatable="yes">Tab 2</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</interface>
+3
View File
@@ -0,0 +1,3 @@
GtkToggleButton
GtkToggleButton
page1
+40
View File
@@ -0,0 +1,40 @@
<interface>
<object class="GtkWindow" id="window1">
<property name="type">popup</property>
<child>
<object class="GtkBox" id="box1">
<child>
<object class="GtkStackSwitcher" id="stackswitcher1">
<property name="stack">stack1</property>
</object>
</child>
<child>
<object class="GtkStack" id="stack1">
<child>
<object class="GtkStackPage">
<property name="name">page1</property>
<property name="title">Page 1</property>
<property name="child">
<object class="GtkButton" id="page1">
<property name="label" translatable="yes">Yes</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">page2</property>
<property name="title">Page 2</property>
<property name="child">
<object class="GtkButton" id="page2">
<property name="label" translatable="yes">No</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
+3
View File
@@ -0,0 +1,3 @@
page1
GtkToggleButton
GtkToggleButton
+40
View File
@@ -0,0 +1,40 @@
<interface>
<object class="GtkWindow" id="window1">
<property name="type">popup</property>
<child>
<object class="GtkBox" id="box1">
<child>
<object class="GtkStack" id="stack1">
<child>
<object class="GtkStackPage">
<property name="name">page1</property>
<property name="title">Page 1</property>
<property name="child">
<object class="GtkButton" id="page1">
<property name="label" translatable="yes">Yes</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">page2</property>
<property name="title">Page 2</property>
<property name="child">
<object class="GtkButton" id="page2">
<property name="label" translatable="yes">No</property>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkStackSwitcher" id="stackswitcher1">
<property name="stack">stack1</property>
</object>
</child>
</object>
</child>
</object>
</interface>
+297
View File
@@ -0,0 +1,297 @@
/*
* Copyright (C) 2019 Red Hat Inc.
*
* Author:
* Matthias Clasen <mclasen@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#ifdef G_OS_WIN32
# include <io.h>
#endif
static char *
test_get_reference_file (const char *ui_file)
{
GString *file = g_string_new (NULL);
if (g_str_has_suffix (ui_file, ".ui"))
g_string_append_len (file, ui_file, strlen (ui_file) - 3);
else
g_string_append (file, ui_file);
g_string_append (file, ".txt");
if (!g_file_test (file->str, G_FILE_TEST_EXISTS))
{
g_string_free (file, TRUE);
return g_strdup (ui_file);
}
return g_string_free (file, FALSE);
}
static char *
diff_with_file (const char *file1,
char *text,
gssize len,
GError **error)
{
const char *command[] = { "diff", "-u", file1, NULL, NULL };
char *diff, *tmpfile;
int fd;
diff = NULL;
if (len < 0)
len = strlen (text);
/* write the text buffer to a temporary file */
fd = g_file_open_tmp (NULL, &tmpfile, error);
if (fd < 0)
return NULL;
if (write (fd, text, len) != (int) len)
{
close (fd);
g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Could not write data to temporary file '%s'", tmpfile);
goto done;
}
close (fd);
command[3] = tmpfile;
/* run diff command */
g_spawn_sync (NULL,
(char **) command,
NULL,
G_SPAWN_SEARCH_PATH,
NULL, NULL,
&diff,
NULL, NULL,
error);
done:
g_unlink (tmpfile);
g_free (tmpfile);
return diff;
}
static char *
generate_output (GtkWidget *window)
{
GString *s = g_string_new ("");
GtkWidget *initial_focus;
GtkWidget *f;
initial_focus = gtk_root_get_focus (GTK_ROOT (window));
if (initial_focus == NULL)
{
g_string_append (s, "NULL\n");
initial_focus = gtk_widget_get_next_focus (window, GTK_DIR_TAB_FORWARD);
}
f = initial_focus;
while (f)
{
const char *name = gtk_buildable_get_name (GTK_BUILDABLE (f));
if (name == NULL)
name = G_OBJECT_TYPE_NAME (f);
g_string_append_printf (s, "%s\n", name);
f = gtk_widget_get_next_focus (f, GTK_DIR_TAB_FORWARD);
if (f == initial_focus)
break;
}
if (f == NULL)
g_string_append (s, "NULL\n");
return g_string_free (s, FALSE);
}
static void
load_ui_file (GFile *file, gboolean generate)
{
GtkBuilder *builder;
GtkWidget *window;
char *output, *diff;
char *ui_file, *reference_file;
GError *error = NULL;
ui_file = g_file_get_path (file);
builder = gtk_builder_new_from_file (ui_file);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window1"));
g_assert (window != NULL);
gtk_widget_show (window);
output = generate_output (window);
if (generate)
{
g_print ("%s", output);
goto out;
}
reference_file = test_get_reference_file (ui_file);
diff = diff_with_file (reference_file, output, -1, &error);
g_assert_no_error (error);
if (diff && diff[0])
{
g_test_message ("Resulting output doesn't match reference:\n%s", diff);
g_test_fail ();
}
g_free (reference_file);
g_free (diff);
out:
g_free (output);
g_free (ui_file);
}
static void
test_ui_file (GFile *file)
{
load_ui_file (file, FALSE);
}
static void
add_test_for_file (GFile *file)
{
char *path;
path = g_file_get_path (file);
g_test_add_vtable (path,
0,
g_object_ref (file),
NULL,
(GTestFixtureFunc) test_ui_file,
(GTestFixtureFunc) g_object_unref);
g_free (path);
}
static int
compare_files (gconstpointer a, gconstpointer b)
{
GFile *file1 = G_FILE (a);
GFile *file2 = G_FILE (b);
char *path1, *path2;
int result;
path1 = g_file_get_path (file1);
path2 = g_file_get_path (file2);
result = strcmp (path1, path2);
g_free (path1);
g_free (path2);
return result;
}
static void
add_tests_for_files_in_directory (GFile *dir)
{
GFileEnumerator *enumerator;
GFileInfo *info;
GList *files;
GError *error = NULL;
enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error);
g_assert_no_error (error);
files = NULL;
while ((info = g_file_enumerator_next_file (enumerator, NULL, &error)))
{
const char *filename;
filename = g_file_info_get_name (info);
if (!g_str_has_suffix (filename, ".ui") ||
g_str_has_suffix (filename, ".txt"))
{
g_object_unref (info);
continue;
}
files = g_list_prepend (files, g_file_get_child (dir, filename));
g_object_unref (info);
}
g_assert_no_error (error);
g_object_unref (enumerator);
files = g_list_sort (files, compare_files);
g_list_foreach (files, (GFunc) add_test_for_file, NULL);
g_list_free_full (files, g_object_unref);
}
int
main (int argc, char *argv[])
{
gtk_test_init (&argc, &argv);
if (argc < 2)
{
const char *basedir;
GFile *dir;
basedir = g_test_get_dir (G_TEST_DIST);
dir = g_file_new_for_path (basedir);
add_tests_for_files_in_directory (dir);
g_object_unref (dir);
}
else if (strcmp (argv[1], "--generate") == 0)
{
if (argc >= 3)
{
GFile *file = g_file_new_for_commandline_arg (argv[2]);
load_ui_file (file, TRUE);
g_object_unref (file);
}
}
else
{
guint i;
for (i = 1; i < argc; i++)
{
GFile *file = g_file_new_for_commandline_arg (argv[i]);
add_test_for_file (file);
g_object_unref (file);
}
}
return g_test_run ();
}
+199 -10
View File
@@ -1,41 +1,230 @@
#include <gtk/gtk.h>
const char *
widget_name (GtkWidget *widget)
{
if (!widget)
return NULL;
else if (gtk_widget_get_name (widget))
return gtk_widget_get_name (widget);
else if (GTK_IS_LABEL (widget))
return gtk_label_get_label (GTK_LABEL (widget));
else if (GTK_IS_EDITABLE (widget))
return gtk_editable_get_text (GTK_EDITABLE (widget));
else
return G_OBJECT_TYPE_NAME (widget);
}
static char *
mode_to_string (GdkCrossingMode mode)
{
return g_enum_to_string (GDK_TYPE_CROSSING_MODE, mode);
}
static char *
detail_to_string (GdkNotifyType detail)
{
return g_enum_to_string (GDK_TYPE_NOTIFY_TYPE, detail);
}
static void
add_event (GtkEventController *controller,
gboolean in,
GdkCrossingMode mode,
GdkNotifyType detail,
GString *s)
{
GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
gboolean is_focus;
gboolean contains_focus;
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GtkWidget *origin = gtk_event_controller_key_get_focus_origin (key);
GtkWidget *target = gtk_event_controller_key_get_focus_target (key);
g_object_get (controller,
"is-focus", &is_focus,
"contains-focus", &contains_focus,
NULL);
g_string_append_printf (s, "%s: %s %s %s is-focus: %d contains-focus: %d origin: %s target: %s\n",
widget_name (widget),
in ? "focus-in" : "focus-out",
mode_to_string (mode),
detail_to_string (detail),
is_focus,
contains_focus,
widget_name (origin),
widget_name (target));
}
static void
focus_in (GtkEventController *controller,
GdkCrossingMode mode,
GdkNotifyType detail,
GString *s)
{
add_event (controller, TRUE, mode, detail, s);
}
static void
focus_out (GtkEventController *controller,
GdkCrossingMode mode,
GdkNotifyType detail,
GString *s)
{
add_event (controller, FALSE, mode, detail, s);
}
static void
verify_focus_chain (GtkWidget *window)
{
GtkWidget *child, *last;
child = window;
while (child)
{
last = child;
child = gtk_widget_get_focus_child (child);
}
g_assert (last == gtk_root_get_focus (GTK_ROOT (window)));
}
static void
add_controller (GtkWidget *widget, GString *s)
{
GtkEventController *controller;
controller = gtk_event_controller_key_new ();
g_signal_connect (controller, "focus-in", G_CALLBACK (focus_in), s);
g_signal_connect (controller, "focus-out", G_CALLBACK (focus_out), s);
gtk_widget_add_controller (widget, controller);
}
static void
test_window_focus (void)
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *box1;
GtkWidget *box2;
GtkWidget *label1;
GtkWidget *label2;
GtkWidget *entry1;
GtkWidget *entry2;
GString *s = g_string_new ("");
/*
* The tree look like this, with [] indicating
* focus locations:
*
* window
* |
* +----[box]-----+
* | | |
* label1 box1 box2------+
* | | |
* [entry1] label2 [entry2]
*/
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window, "window");
add_controller (window, s);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_can_focus (box, TRUE);
gtk_widget_set_name (box, "box");
add_controller (box, s);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("label1"));
label1 = gtk_label_new ("label1");
gtk_widget_set_name (label1, "label1");
add_controller (label1, s);
gtk_container_add (GTK_CONTAINER (box), label1);
box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_name (box1, "box1");
add_controller (box1, s);
gtk_container_add (GTK_CONTAINER (box), box1);
entry1 = gtk_text_new ();
gtk_container_add (GTK_CONTAINER (box), entry1);
gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("label2"));
gtk_widget_set_name (entry1, "entry1");
add_controller (entry1, s);
gtk_container_add (GTK_CONTAINER (box1), entry1);
box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_name (box2, "box2");
add_controller (box2, s);
gtk_container_add (GTK_CONTAINER (box), box2);
label2 = gtk_label_new ("label2");
gtk_widget_set_name (label2, "label2");
add_controller (label2, s);
gtk_container_add (GTK_CONTAINER (box2), label2);
entry2 = gtk_text_new ();
gtk_container_add (GTK_CONTAINER (box), entry2);
gtk_widget_set_name (entry2, "entry2");
add_controller (entry2, s);
gtk_container_add (GTK_CONTAINER (box2), entry2);
g_assert_null (gtk_window_get_focus (GTK_WINDOW (window)));
gtk_window_set_focus (GTK_WINDOW (window), entry1);
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1);
gtk_widget_show (window);
/* show puts the initial focus on box */
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box);
verify_focus_chain (window);
if (g_test_verbose ())
g_print ("-> box\n%s\n", s->str);
g_assert_cmpstr (s->str, ==,
"window: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1 origin: (null) target: box\n"
"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0 origin: (null) target: box\n");
g_string_truncate (s, 0);
gtk_widget_grab_focus (entry1);
if (g_test_verbose ())
g_print ("box -> entry1\n%s\n", s->str);
g_assert_cmpstr (s->str, ==,
"box: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 0 contains-focus: 1 origin: box target: entry1\n"
"box1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 1 origin: box target: entry1\n"
"entry1: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 1 contains-focus: 0 origin: box target: entry1\n");
g_string_truncate (s, 0);
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1);
verify_focus_chain (window);
gtk_widget_grab_focus (entry2);
if (g_test_verbose ())
g_print ("entry1 -> entry2\n%s\n", s->str);
g_assert_cmpstr (s->str, ==,
"entry1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 0 contains-focus: 0 origin: entry1 target: entry2\n"
"box1: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 0 origin: entry1 target: entry2\n"
"box2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR_VIRTUAL is-focus: 0 contains-focus: 1 origin: entry1 target: entry2\n"
"entry2: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_NONLINEAR is-focus: 1 contains-focus: 0 origin: entry1 target: entry2\n");
g_string_truncate (s, 0);
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2);
verify_focus_chain (window);
gtk_widget_grab_focus (box);
if (g_test_verbose ())
g_print ("entry2 -> box\n%s", s->str);
g_assert_cmpstr (s->str, ==,
"entry2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_ANCESTOR is-focus: 0 contains-focus: 0 origin: entry2 target: box\n"
"box2: focus-out GDK_CROSSING_NORMAL GDK_NOTIFY_VIRTUAL is-focus: 0 contains-focus: 0 origin: entry2 target: box\n"
"box: focus-in GDK_CROSSING_NORMAL GDK_NOTIFY_INFERIOR is-focus: 1 contains-focus: 0 origin: entry2 target: box\n");
g_string_truncate (s, 0);
gtk_widget_hide (window);
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry2);
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == box);
verify_focus_chain (window);
gtk_window_set_focus (GTK_WINDOW (window), entry1);
g_assert (gtk_window_get_focus (GTK_WINDOW (window)) == entry1);
+2
View File
@@ -1,3 +1,5 @@
subdir('focus-chain')
testexecdir = join_paths(installed_test_bindir, 'gtk')
testdatadir = join_paths(installed_test_datadir, 'gtk')