From 5a83ef83252aa54e5b3230bcb7569a607a86c8bf Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 5 Oct 2010 10:09:40 -0400 Subject: [PATCH] St: add StFocusManager, to handle keyboard navigation StFocusManager allows setting up "focus groups" in which it will automatically handle navigation between can_focus widgets. https://bugzilla.gnome.org/show_bug.cgi?id=621671 --- src/Makefile-st.am | 2 + src/shell-global.c | 15 +++ src/st/st-focus-manager.c | 202 ++++++++++++++++++++++++++++++++++++++ src/st/st-focus-manager.h | 80 +++++++++++++++ 4 files changed, 299 insertions(+) create mode 100644 src/st/st-focus-manager.c create mode 100644 src/st/st-focus-manager.h diff --git a/src/Makefile-st.am b/src/Makefile-st.am index 74b023b75..0423da40a 100644 --- a/src/Makefile-st.am +++ b/src/Makefile-st.am @@ -77,6 +77,7 @@ st_source_h = \ st/st-container.h \ st/st-drawing-area.h \ st/st-entry.h \ + st/st-focus-manager.h \ st/st-group.h \ st/st-im-text.h \ st/st-label.h \ @@ -126,6 +127,7 @@ st_source_c = \ st/st-container.c \ st/st-drawing-area.c \ st/st-entry.c \ + st/st-focus-manager.c \ st/st-group.c \ st/st-im-text.c \ st/st-label.c \ diff --git a/src/shell-global.c b/src/shell-global.c index a1518b44a..40fa49008 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -7,6 +7,7 @@ #include "shell-perf-log.h" #include "shell-window-tracker.h" #include "shell-wm.h" +#include "st.h" #include "display.h" #include "util.h" @@ -58,6 +59,7 @@ struct _ShellGlobal { const char *datadir; const char *imagedir; const char *userdatadir; + StFocusManager *focus_manager; /* Displays the root window; see shell_global_create_root_pixmap_actor() */ ClutterActor *root_pixmap; @@ -89,6 +91,7 @@ enum { PROP_DATADIR, PROP_IMAGEDIR, PROP_USERDATADIR, + PROP_FOCUS_MANAGER, }; G_DEFINE_TYPE(ShellGlobal, shell_global, G_TYPE_OBJECT); @@ -172,6 +175,9 @@ shell_global_get_property(GObject *object, case PROP_USERDATADIR: g_value_set_string (value, global->userdatadir); break; + case PROP_FOCUS_MANAGER: + g_value_set_object (value, global->focus_manager); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -327,6 +333,13 @@ shell_global_class_init (ShellGlobalClass *klass) "Directory containing gnome-shell user data", NULL, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, + PROP_FOCUS_MANAGER, + g_param_spec_object ("focus-manager", + "Focus manager", + "The shell's StFocusManager", + ST_TYPE_FOCUS_MANAGER, + G_PARAM_READABLE)); } /** @@ -722,6 +735,8 @@ _shell_global_set_plugin (ShellGlobal *global, display = meta_screen_get_display (screen); g_signal_connect (display, "notify::focus-window", G_CALLBACK (focus_window_changed), global); + + global->focus_manager = st_focus_manager_get_for_stage (CLUTTER_STAGE (stage)); } void diff --git a/src/st/st-focus-manager.c b/src/st/st-focus-manager.c new file mode 100644 index 000000000..50cb0bdaa --- /dev/null +++ b/src/st/st-focus-manager.c @@ -0,0 +1,202 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * st-focus-manager.c: Keyboard focus manager + * + * Copyright (c) 2010 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * SECTION:st-focus-manager + * @short_description: Keyboard focus management + * + * #StFocusManager handles keyboard focus for all actors on the stage. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "st-focus-manager.h" + +#define ST_FOCUS_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_FOCUS_MANAGER, StFocusManagerPrivate)) + +struct _StFocusManagerPrivate +{ + GHashTable *groups; +}; + +G_DEFINE_TYPE (StFocusManager, st_focus_manager, G_TYPE_OBJECT) + +static void +st_focus_manager_dispose (GObject *object) +{ + StFocusManager *manager = ST_FOCUS_MANAGER (object); + + if (manager->priv->groups) + { + g_hash_table_destroy (manager->priv->groups); + manager->priv->groups = NULL; + } + + G_OBJECT_CLASS (st_focus_manager_parent_class)->dispose (object); +} + +static void +st_focus_manager_class_init (StFocusManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (StFocusManagerPrivate)); + + object_class->dispose = st_focus_manager_dispose; +} + +static void +st_focus_manager_init (StFocusManager *manager) +{ + manager->priv = ST_FOCUS_MANAGER_GET_PRIVATE (manager); + manager->priv->groups = g_hash_table_new_full (NULL, NULL, + g_object_unref, NULL); +} + +static gboolean +st_focus_manager_stage_event (ClutterActor *stage, + ClutterEvent *event, + gpointer user_data) +{ + StFocusManager *manager = user_data; + GtkDirectionType direction; + gboolean wrap_around = FALSE; + ClutterActor *focused, *group; + + if (event->type != CLUTTER_KEY_PRESS) + return FALSE; + + switch (event->key.keyval) + { + case CLUTTER_KEY_Up: + direction = GTK_DIR_UP; + break; + case CLUTTER_KEY_Down: + direction = GTK_DIR_DOWN; + break; + case CLUTTER_KEY_Left: + direction = GTK_DIR_LEFT; + break; + case CLUTTER_KEY_Right: + direction = GTK_DIR_RIGHT; + break; + case CLUTTER_KEY_Tab: + if (event->key.modifier_state & CLUTTER_SHIFT_MASK) + direction = GTK_DIR_TAB_BACKWARD; + else + direction = GTK_DIR_TAB_FORWARD; + wrap_around = TRUE; + break; + case CLUTTER_KEY_ISO_Left_Tab: + direction = GTK_DIR_TAB_BACKWARD; + wrap_around = TRUE; + break; + + default: + return FALSE; + } + + focused = clutter_stage_get_key_focus (CLUTTER_STAGE (stage)); + if (!focused) + return FALSE; + + for (group = focused; group != stage; group = clutter_actor_get_parent (group)) + { + if (g_hash_table_lookup (manager->priv->groups, group)) + { + return st_widget_navigate_focus (ST_WIDGET (group), focused, + direction, wrap_around); + } + } + return FALSE; +} + +/** + * st_focus_manager_get_for_stage: + * @stage: a #ClutterStage + * + * Gets the #StFocusManager for @stage, creating it if necessary. + * + * Return value: (transfer none): the focus manager for @stage + */ +StFocusManager * +st_focus_manager_get_for_stage (ClutterStage *stage) +{ + StFocusManager *manager; + + manager = g_object_get_data (G_OBJECT (stage), "st-focus-manager"); + if (!manager) + { + manager = g_object_new (ST_TYPE_FOCUS_MANAGER, NULL); + g_object_set_data_full (G_OBJECT (stage), "st-focus-manager", + manager, g_object_unref); + + g_signal_connect (stage, "event", + G_CALLBACK (st_focus_manager_stage_event), manager); + } + + return manager; +} + +static void +remove_destroyed_group (ClutterActor *actor, + gpointer user_data) +{ + StFocusManager *manager = user_data; + + st_focus_manager_remove_group (manager, ST_WIDGET (actor)); +} + +/** + * st_focus_manager_add_group: + * @manager: the #StFocusManager + * @root: the root container of the group + * + * Adds a new focus group to @manager. When the focus is in an actor + * that is a descendant of @root, @manager will handle moving focus + * from one actor to another within @root based on keyboard events. + */ +void +st_focus_manager_add_group (StFocusManager *manager, + StWidget *root) +{ + g_signal_connect (root, "destroy", + G_CALLBACK (remove_destroyed_group), + manager); + g_hash_table_insert (manager->priv->groups, root, GINT_TO_POINTER (1)); +} + +/** + * st_focus_manager_remove_group: + * @manager: the #StFocusManager + * @root: the root container of the group + * + * Removes the group rooted at @root from @manager + */ +void +st_focus_manager_remove_group (StFocusManager *manager, + StWidget *root) +{ + g_hash_table_remove (manager->priv->groups, root); +} diff --git a/src/st/st-focus-manager.h b/src/st/st-focus-manager.h new file mode 100644 index 000000000..365cf970f --- /dev/null +++ b/src/st/st-focus-manager.h @@ -0,0 +1,80 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * st-focus-manager.h: Keyboard focus manager + * + * Copyright 2010 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * Boston, MA 02111-1307, USA. + */ + +#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION) +#error "Only can be included directly.h" +#endif + +#ifndef __ST_FOCUS_MANAGER_H__ +#define __ST_FOCUS_MANAGER_H__ + +#include +#include + +G_BEGIN_DECLS + +#define ST_TYPE_FOCUS_MANAGER (st_focus_manager_get_type ()) +#define ST_FOCUS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_FOCUS_MANAGER, StFocusManager)) +#define ST_IS_FOCUS_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_FOCUS_MANAGER)) +#define ST_FOCUS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_FOCUS_MANAGER, StFocusManagerClass)) +#define ST_IS_FOCUS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_FOCUS_MANAGER)) +#define ST_FOCUS_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_FOCUS_MANAGER, StFocusManagerClass)) + +typedef struct _StFocusManager StFocusManager; +typedef struct _StFocusManagerPrivate StFocusManagerPrivate; +typedef struct _StFocusManagerClass StFocusManagerClass; + +/** + * StFocusManager: + * + * The #StFocusManager struct contains only private data + */ +struct _StFocusManager +{ + /*< private >*/ + GObject parent_instance; + + StFocusManagerPrivate *priv; +}; + +/** + * StFocusManagerClass: + * + * The #StFocusManagerClass struct contains only private data + */ +struct _StFocusManagerClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +GType st_focus_manager_get_type (void) G_GNUC_CONST; + +StFocusManager *st_focus_manager_get_for_stage (ClutterStage *stage); + +void st_focus_manager_add_group (StFocusManager *manager, + StWidget *root); +void st_focus_manager_remove_group (StFocusManager *manager, + StWidget *root); + +G_END_DECLS + +#endif /* __ST_FOCUS_MANAGER_H__ */