st-widget: Copy get_focus_chain and navigate_focus from StContainer
We can't get rid of the implementations in StContainer just yet, as StContainer still keeps its own child list. But this should lower the amount of code that has to be moved around when we remove StContainer. https://bugzilla.gnome.org/show_bug.cgi?id=670034
This commit is contained in:
parent
fbcea03ab3
commit
3736d81d8f
@ -163,13 +163,13 @@ shell_generic_container_pick (ClutterActor *actor,
|
||||
}
|
||||
|
||||
static GList *
|
||||
shell_generic_container_get_focus_chain (StContainer *container)
|
||||
shell_generic_container_get_focus_chain (StWidget *widget)
|
||||
{
|
||||
ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (container);
|
||||
ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (widget);
|
||||
GList *children, *focus_chain;
|
||||
|
||||
focus_chain = NULL;
|
||||
for (children = st_container_get_children_list (container); children; children = children->next)
|
||||
for (children = st_container_get_children_list (ST_CONTAINER (widget)); children; children = children->next)
|
||||
{
|
||||
ClutterActor *child = children->data;
|
||||
|
||||
@ -252,7 +252,7 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StContainerClass *container_class = ST_CONTAINER_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = shell_generic_container_finalize;
|
||||
|
||||
@ -262,7 +262,7 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass)
|
||||
actor_class->paint = shell_generic_container_paint;
|
||||
actor_class->pick = shell_generic_container_pick;
|
||||
|
||||
container_class->get_focus_chain = shell_generic_container_get_focus_chain;
|
||||
widget_class->get_focus_chain = shell_generic_container_get_focus_chain;
|
||||
|
||||
/**
|
||||
* ShellGenericContainer::get-preferred-width:
|
||||
|
@ -183,8 +183,9 @@ st_container_get_children_list (StContainer *container)
|
||||
}
|
||||
|
||||
static GList *
|
||||
st_container_real_get_focus_chain (StContainer *container)
|
||||
st_container_get_focus_chain (StWidget *widget)
|
||||
{
|
||||
StContainer *container = ST_CONTAINER (widget);
|
||||
GList *chain, *children;
|
||||
|
||||
chain = NULL;
|
||||
@ -199,23 +200,6 @@ st_container_real_get_focus_chain (StContainer *container)
|
||||
return g_list_reverse (chain);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_container_get_focus_chain:
|
||||
* @container: An #StContainer
|
||||
*
|
||||
* Gets a list of the focusable children of @container, in "Tab"
|
||||
* order. By default, this returns all visible
|
||||
* (as in CLUTTER_ACTOR_IS_VISIBLE()) children of @container.
|
||||
*
|
||||
* Returns: (element-type Clutter.Actor) (transfer container):
|
||||
* @container's focusable children
|
||||
*/
|
||||
GList *
|
||||
st_container_get_focus_chain (StContainer *container)
|
||||
{
|
||||
return ST_CONTAINER_GET_CLASS (container)->get_focus_chain (container);
|
||||
}
|
||||
|
||||
static gint
|
||||
sort_z_order (gconstpointer a,
|
||||
gconstpointer b)
|
||||
@ -649,7 +633,7 @@ st_container_navigate_focus (StWidget *widget,
|
||||
* "first" being determined by @direction.)
|
||||
*/
|
||||
|
||||
children = st_container_get_focus_chain (container);
|
||||
children = st_widget_get_focus_chain (ST_WIDGET (container));
|
||||
if (direction == GTK_DIR_TAB_FORWARD ||
|
||||
direction == GTK_DIR_TAB_BACKWARD)
|
||||
{
|
||||
@ -752,7 +736,6 @@ st_container_class_init (StContainerClass *klass)
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||
StContainerClass *container_class = ST_CONTAINER_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StContainerPrivate));
|
||||
|
||||
@ -760,7 +743,6 @@ st_container_class_init (StContainerClass *klass)
|
||||
|
||||
actor_class->get_paint_volume = st_container_get_paint_volume;
|
||||
|
||||
widget_class->get_focus_chain = st_container_get_focus_chain;
|
||||
widget_class->navigate_focus = st_container_navigate_focus;
|
||||
|
||||
container_class->get_focus_chain = st_container_real_get_focus_chain;
|
||||
}
|
||||
|
@ -47,16 +47,12 @@ struct _StContainer {
|
||||
|
||||
struct _StContainerClass {
|
||||
StWidgetClass parent_class;
|
||||
|
||||
GList * (*get_focus_chain) (StContainer *container);
|
||||
};
|
||||
|
||||
GType st_container_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void st_container_destroy_children (StContainer *container);
|
||||
|
||||
GList * st_container_get_focus_chain (StContainer *container);
|
||||
|
||||
/* Only to be used by subclasses of StContainer */
|
||||
void st_container_move_child (StContainer *container,
|
||||
ClutterActor *actor,
|
||||
|
@ -677,6 +677,12 @@ st_widget_get_paint_volume (ClutterActor *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GList *
|
||||
st_widget_real_get_focus_chain (StWidget *widget)
|
||||
{
|
||||
return clutter_actor_get_children (CLUTTER_ACTOR (widget));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
st_widget_class_init (StWidgetClass *klass)
|
||||
@ -711,6 +717,7 @@ st_widget_class_init (StWidgetClass *klass)
|
||||
klass->style_changed = st_widget_real_style_changed;
|
||||
klass->navigate_focus = st_widget_real_navigate_focus;
|
||||
klass->get_accessible_type = st_widget_accessible_get_type;
|
||||
klass->get_focus_chain = st_widget_real_get_focus_chain;
|
||||
|
||||
/**
|
||||
* StWidget:pseudo-class:
|
||||
@ -1581,20 +1588,278 @@ st_widget_get_can_focus (StWidget *widget)
|
||||
return widget->priv->can_focus;
|
||||
}
|
||||
|
||||
/* filter @children to contain only only actors that overlap @rbox
|
||||
* when moving in @direction. (Assuming no transformations.)
|
||||
*/
|
||||
static GList *
|
||||
filter_by_position (GList *children,
|
||||
ClutterActorBox *rbox,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
ClutterActorBox cbox;
|
||||
GList *l, *ret;
|
||||
ClutterActor *child;
|
||||
|
||||
for (l = children, ret = NULL; l; l = l->next)
|
||||
{
|
||||
child = l->data;
|
||||
clutter_actor_get_allocation_box (child, &cbox);
|
||||
|
||||
/* Filter out children if they are in the wrong direction from
|
||||
* @rbox, or if they don't overlap it. To account for floating-
|
||||
* point imprecision, an actor is "down" (etc.) from an another
|
||||
* actor even if it overlaps it by up to 0.1 pixels.
|
||||
*/
|
||||
switch (direction)
|
||||
{
|
||||
case GTK_DIR_UP:
|
||||
if (cbox.y2 > rbox->y1 + 0.1)
|
||||
continue;
|
||||
if (cbox.x1 >= rbox->x2 || cbox.x2 <= rbox->x1)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case GTK_DIR_DOWN:
|
||||
if (cbox.y1 < rbox->y2 - 0.1)
|
||||
continue;
|
||||
if (cbox.x1 >= rbox->x2 || cbox.x2 <= rbox->x1)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case GTK_DIR_LEFT:
|
||||
if (cbox.x2 > rbox->x1 + 0.1)
|
||||
continue;
|
||||
if (cbox.y1 >= rbox->y2 || cbox.y2 <= rbox->y1)
|
||||
continue;
|
||||
break;
|
||||
|
||||
case GTK_DIR_RIGHT:
|
||||
if (cbox.x1 < rbox->x2 - 0.1)
|
||||
continue;
|
||||
if (cbox.y1 >= rbox->y2 || cbox.y2 <= rbox->y1)
|
||||
continue;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
ret = g_list_prepend (ret, child);
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
GtkDirectionType direction;
|
||||
ClutterActorBox box;
|
||||
} StWidgetChildSortData;
|
||||
|
||||
static int
|
||||
sort_by_position (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActor *actor_a = (ClutterActor *)a;
|
||||
ClutterActor *actor_b = (ClutterActor *)b;
|
||||
StWidgetChildSortData *sort_data = user_data;
|
||||
GtkDirectionType direction = sort_data->direction;
|
||||
ClutterActorBox abox, bbox;
|
||||
int ax, ay, bx, by;
|
||||
int cmp, fmid;
|
||||
|
||||
/* Determine the relationship, relative to motion in @direction, of
|
||||
* the center points of the two actors. Eg, for %GTK_DIR_UP, we
|
||||
* return a negative number if @actor_a's center is below @actor_b's
|
||||
* center, and postive if vice versa, which will result in an
|
||||
* overall list sorted bottom-to-top.
|
||||
*/
|
||||
|
||||
clutter_actor_get_allocation_box (actor_a, &abox);
|
||||
ax = (int)(abox.x1 + abox.x2) / 2;
|
||||
ay = (int)(abox.y1 + abox.y2) / 2;
|
||||
clutter_actor_get_allocation_box (actor_b, &bbox);
|
||||
bx = (int)(bbox.x1 + bbox.x2) / 2;
|
||||
by = (int)(bbox.y1 + bbox.y2) / 2;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case GTK_DIR_UP:
|
||||
cmp = by - ay;
|
||||
break;
|
||||
case GTK_DIR_DOWN:
|
||||
cmp = ay - by;
|
||||
break;
|
||||
case GTK_DIR_LEFT:
|
||||
cmp = bx - ax;
|
||||
break;
|
||||
case GTK_DIR_RIGHT:
|
||||
cmp = ax - bx;
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached (0);
|
||||
}
|
||||
|
||||
if (cmp)
|
||||
return cmp;
|
||||
|
||||
/* If two actors have the same center on the axis being sorted,
|
||||
* prefer the one that is closer to the center of the current focus
|
||||
* actor on the other axis. Eg, for %GTK_DIR_UP, prefer whichever
|
||||
* of @actor_a and @actor_b has a horizontal center closest to the
|
||||
* current focus actor's horizontal center.
|
||||
*
|
||||
* (This matches GTK's behavior.)
|
||||
*/
|
||||
switch (direction)
|
||||
{
|
||||
case GTK_DIR_UP:
|
||||
case GTK_DIR_DOWN:
|
||||
fmid = (int)(sort_data->box.x1 + sort_data->box.x2) / 2;
|
||||
return abs (ax - fmid) - abs (bx - fmid);
|
||||
case GTK_DIR_LEFT:
|
||||
case GTK_DIR_RIGHT:
|
||||
fmid = (int)(sort_data->box.y1 + sort_data->box.y2) / 2;
|
||||
return abs (ay - fmid) - abs (by - fmid);
|
||||
default:
|
||||
g_return_val_if_reached (0);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_widget_real_navigate_focus (StWidget *widget,
|
||||
ClutterActor *from,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
if (widget->priv->can_focus &&
|
||||
CLUTTER_ACTOR (widget) != from)
|
||||
ClutterActor *widget_actor, *focus_child;
|
||||
GList *children, *l;
|
||||
|
||||
widget_actor = CLUTTER_ACTOR (widget);
|
||||
if (from == widget_actor)
|
||||
return FALSE;
|
||||
|
||||
/* Figure out if @from is a descendant of @widget, and if so,
|
||||
* set @focus_child to the immediate child of @widget that
|
||||
* contains (or *is*) @from.
|
||||
*/
|
||||
focus_child = from;
|
||||
while (focus_child && clutter_actor_get_parent (focus_child) != widget_actor)
|
||||
focus_child = clutter_actor_get_parent (focus_child);
|
||||
|
||||
if (widget->priv->can_focus)
|
||||
{
|
||||
clutter_actor_grab_key_focus (CLUTTER_ACTOR (widget));
|
||||
return TRUE;
|
||||
if (!focus_child)
|
||||
{
|
||||
/* Accept focus from outside */
|
||||
clutter_actor_grab_key_focus (widget_actor);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yield focus from within: since @widget itself is
|
||||
* focusable we don't allow the focus to be navigated
|
||||
* within @widget.
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we can navigate within @focus_child */
|
||||
if (focus_child && ST_IS_WIDGET (focus_child))
|
||||
{
|
||||
if (st_widget_navigate_focus (ST_WIDGET (focus_child), from, direction, FALSE))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* At this point we know that we want to navigate focus to one of
|
||||
* @widget's immediate children; the next one after @focus_child,
|
||||
* or the first one if @focus_child is %NULL. (With "next" and
|
||||
* "first" being determined by @direction.)
|
||||
*/
|
||||
|
||||
children = st_widget_get_focus_chain (widget);
|
||||
if (direction == GTK_DIR_TAB_FORWARD ||
|
||||
direction == GTK_DIR_TAB_BACKWARD)
|
||||
{
|
||||
if (direction == GTK_DIR_TAB_BACKWARD)
|
||||
children = g_list_reverse (children);
|
||||
|
||||
if (focus_child)
|
||||
{
|
||||
/* Remove focus_child and any earlier children */
|
||||
while (children && children->data != focus_child)
|
||||
children = g_list_delete_link (children, children);
|
||||
if (children)
|
||||
children = g_list_delete_link (children, children);
|
||||
}
|
||||
}
|
||||
else /* direction is an arrow key, not tab */
|
||||
{
|
||||
StWidgetChildSortData sort_data;
|
||||
|
||||
/* Compute the allocation box of the previous focused actor, in
|
||||
* @widget's coordinate space. If there was no previous focus,
|
||||
* use the coordinates of the appropriate edge of @widget.
|
||||
*
|
||||
* Note that all of this code assumes the actors are not
|
||||
* transformed (or at most, they are all scaled by the same
|
||||
* amount). If @widget or any of its children is rotated, or
|
||||
* any child is inconsistently scaled, then the focus chain will
|
||||
* probably be unpredictable.
|
||||
*/
|
||||
if (focus_child)
|
||||
{
|
||||
clutter_actor_get_allocation_box (focus_child, &sort_data.box);
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_actor_get_allocation_box (CLUTTER_ACTOR (widget), &sort_data.box);
|
||||
switch (direction)
|
||||
{
|
||||
case GTK_DIR_UP:
|
||||
sort_data.box.y1 = sort_data.box.y2;
|
||||
break;
|
||||
case GTK_DIR_DOWN:
|
||||
sort_data.box.y2 = sort_data.box.y1;
|
||||
break;
|
||||
case GTK_DIR_LEFT:
|
||||
sort_data.box.x1 = sort_data.box.x2;
|
||||
break;
|
||||
case GTK_DIR_RIGHT:
|
||||
sort_data.box.x2 = sort_data.box.x1;
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
}
|
||||
}
|
||||
sort_data.direction = direction;
|
||||
|
||||
if (focus_child)
|
||||
children = filter_by_position (children, &sort_data.box, direction);
|
||||
if (children)
|
||||
children = g_list_sort_with_data (children, sort_by_position, &sort_data);
|
||||
}
|
||||
|
||||
/* Now try each child in turn */
|
||||
for (l = children; l; l = l->next)
|
||||
{
|
||||
if (ST_IS_WIDGET (l->data))
|
||||
{
|
||||
if (st_widget_navigate_focus (l->data, from, direction, FALSE))
|
||||
{
|
||||
g_list_free (children);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* st_widget_navigate_focus:
|
||||
* @widget: the "top level" container
|
||||
@ -2087,3 +2352,20 @@ check_labels (StWidgetAccessible *widget_accessible,
|
||||
ATK_OBJECT (widget_accessible));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_get_focus_chain:
|
||||
* @widget: An #StWidget
|
||||
*
|
||||
* Gets a list of the focusable children of @widget, in "Tab"
|
||||
* order. By default, this returns all visible
|
||||
* (as in CLUTTER_ACTOR_IS_VISIBLE()) children of @widget.
|
||||
*
|
||||
* Returns: (element-type Clutter.Actor) (transfer container):
|
||||
* @widget's focusable children
|
||||
*/
|
||||
GList *
|
||||
st_widget_get_focus_chain (StWidget *widget)
|
||||
{
|
||||
return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
|
||||
}
|
||||
|
@ -86,6 +86,8 @@ struct _StWidgetClass
|
||||
ClutterActor *from,
|
||||
GtkDirectionType direction);
|
||||
GType (* get_accessible_type) (void);
|
||||
|
||||
GList * (* get_focus_chain) (StWidget *widget);
|
||||
};
|
||||
|
||||
GType st_widget_get_type (void) G_GNUC_CONST;
|
||||
@ -151,6 +153,9 @@ void st_widget_style_changed (StWidget *widg
|
||||
StThemeNode * st_widget_get_theme_node (StWidget *widget);
|
||||
StThemeNode * st_widget_peek_theme_node (StWidget *widget);
|
||||
|
||||
GList * st_widget_get_focus_chain (StWidget *widget);
|
||||
|
||||
|
||||
/* debug methods */
|
||||
char *st_describe_actor (ClutterActor *actor);
|
||||
void st_set_slow_down_factor (gfloat factor);
|
||||
|
Loading…
Reference in New Issue
Block a user