accelerators
- From: Tim Janik <timj gtk org>
- To: Gtk+ Developers <gtk-devel-list gnome org>
- Subject: accelerators
- Date: Tue, 30 Oct 2001 00:17:37 +0100 (CET)
hi all,
one of the outstanding API issues is fixing accelerator behaviour.
i'll start with the basic rationale and then come to the planned changes.
accelerators are the basic mechamism through which certain key combinations
can be assigned to menu items, to emit those menu item's ::activate signal
through keyboard shortcuts. we've also had a couple of requests to bind
arbitrary callbacks to accelerators, so accelerators are supposed to
accomplish that functionality in the future as well.
in order to support runtime changable accelerators persistently (through
loading of accelerator assignments at program start and saving them upon
exit), gtk needs some kind of association of keyboard accelerator to
functionality. this currently works through item factory's menu paths,
e.g. we have a set of paths and accelerator pairs:
("<DocumentWindow>/File/Quit", "<Ctrl>W")
("<DocumentWindow>/File/Open", "<Ctrl>O")
("<DocumentWindow>/Edit/Cut", "<Ctrl>X")
("<ImageWindow>/Edit/Cut", "<Ctrl>X")
("<ImageWindow>/Select/All", "<Ctrl>A")
the paths consist of a window type in angle brackets and categorized
functionality. that's, so that changing e.g. <DocumentWindow>/Edit/Cut
to "<Alt>C" propagates to all <DocumentWindow>s of an application,
but the accelerator for <ImageWindow>/Edit/Cut for all windows which
display images can be changed independently from that.
since a window can have multiple menus that come with different
sets of accelerators, we also have accelerator groups per menu
which get attached to the toplevel that uses this menu (e.g. in
a menu bar).
certain menus (e.g. right-click popups) may not be associated with
a single window, but are used for a set of similar windows, in those
cases, the menu's accelerator group needs to be added to all toplevels
of that set.
what we end up with is a set of accelerator assignments, a set
of accelerator groups and a set of toplevels with n:n relations, e.g.:
Paths and accelerators:
(<A>/Bar,<Ctrl>X) (<C>/Foo,<Shft>Y) (<B>/Baz,<Alt>Z)
\ /|\ /
\ / | \ /
M*=MenuItems M1 / M3 \ M2
C*=Callbacks \ C1 M4 C2 /
\ / M5 \ /
\ / | \ /
AccelGroups: Group1 Group2 Group3
| /|\ |
| / | \ |
| / | \ |
| / | \ |
| / | \ |
AcceleratableObjects: Window1 Window2 Window3
based on this observation, it makes most sense to maintain:
1) a global map of (path,shortcut) pairs which tracks what
accel groups use what pair
2) accelerator changed notification on accel groups (not widgets
like we currently do)
3) a GtkAcceleratable interface that object types can implement which
whish to support shortcuts
so in terms of what we currently have, i plan to change things like this:
- remove the GtkWidget::add_accelerator and GtkWidget::remove_accelerator
signals
- remove: gtk_widget_remove_accelerators(), gtk_widget_accelerator_signal(),
gtk_widget_lock_accelerators(), gtk_widget_unlock_accelerators(),
gtk_widget_accelerators_locked(),
and probably also:
gtk_widget_add_accelerator(), gtk_widget_remove_accelerator()
though the latter two could be left for convenience if people feel overly
annoyed by their removal
- remove: gtk_item_factory_print_func, gtk_item_factory_parse_rc,
gtk_item_factory_parse_rc_string, gtk_item_factory_parse_rc_scanner,
gtk_item_factory_add_foreign, gtk_item_factory_dump_items,
gtk_item_factory_dump_rc
- implement a global (path,shortcut) map with the public API:
/* save/load accelerators to/from a file */
void gtk_accelerator_map_load (const gchar *file_name);
void gtk_accelerator_map_save (const gchar *file_name);
/* in case someone needs to operate on partial files (e.g. beast
* combines the accelerator dump with its rc file):
*/
void gtk_accelerator_map_load_from_fd (gint fd);
void gtk_accelerator_map_save_to_fd (gint fd);
/* if saving is not possible/desired, this prevents runtime changable
* accelerators
*/
void gtk_accelerator_map_lock (void);
void gtk_accelerator_map_unlock (void);
/* in case someone needs to implement his own parser (iirc, gsumi
* wanted to save in xml syntax or somesuch):
*/
void gtk_accelerator_map_foreach (GtkAccelPoolForeach foreach_func,
gpointer data,
gboolean ignore_filters);
void gtk_accelerator_map_enter (const gchar *accel_path,
const gchar *accel_key);
/* semi public API, only needed for GtkModules that create their own
* menus, but don't want it to be saved into the application's
* accelerator map dump (e.g. GLE)
*/
void gtk_accelerator_map_install_filter (const gchar *pattern);
- introduce, and for toplevels implement, the new interface:
struct GtkAccelKey {
guint keyval;
guint modifier;
guint fixed : 1; /* !removable */
};
struct GtkAcceleratableClass {
GTypeInterface base_iface;
gboolean (*remove_accel) (GtkAcceleratable *acceleratable,
guint keyval,
guint modifier);
/* list accels and mnemonics */
GtkAccelKey* (*list_accels) (GtkAcceleratable *acceleratable,
guint *n_accels);
void (*queue_changed) (GtkAcceleratable *acceleratable,
guint keyval,
guint modifier);
/* signals */
void (*accels_changed) (GtkAcceleratable *acceleratable);
};
gboolean gtk_acceleratable_remove_accel (GtkAcceleratable *acceleratable,
guint keyval,
guint modifier);
GtkAccelKey* gtk_acceleratable_list_accels (GtkAcceleratable *acceleratable,
guint *n_accels);
void gtk_acceleratable_queue_changed (GtkAcceleratable *acceleratable,
guint keyval,
guint modifier);
a few notes on the purpose of the above functions:
listing all accelerators and getting changed notification is required for
plug/socket code to figure what key combos need to be passed between
embedded apps. removing accelerators (and returning whether removal was
successfull, i.e. the accelerator was unlocked) is needed to change and
override accelerators at runtime.
for GtkWindow's GtkAcceleratable implementation, removal and listing will
also account for mnemonics (they are basically treated as locked
accelerators)
- nuke gtk_accel_group_get_default(), there's no point in having a default
accelerator group, it just enables unmaintained shortcuts in the wrong
cases, which is one of the major bugs that drove some people into always
locking accelerators
- for accelerator installation/removal, offer as public API:
void gtk_accel_group_add (GtkAccelGroup *accel_group,
const gchar *path,
guint accel_key,
GdkModifierType accel_mods,
GtkAccelFlags accel_flags,
GObject *object,
const gchar *accel_signal);
void gtk_accel_group_remove (GtkAccelGroup *accel_group,
guint accel_key,
GdkModifierType accel_mods,
GObject *object);
void gtk_accel_group_connect (GtkAccelGroup *accel_group,
const gchar *path,
guint accel_key,
GdkModifierType accel_mods,
GClosure *closure);
void gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
guint accel_key,
GdkModifierType accel_mods,
GClosure *closure);
(NULL paths are allowed, they'll instantly lock the accelerators though,
as we can't save/load accelerators without path specification)
- for internal changed notification from GtkAccelGroup->GtkAcceleratable,
introduce:
void (*GtkAccelGroup::accel_changed) (GtkAccelGroup *group,
guint accel_key,
GdkModifierType accel_mods);
- change GtkAccelGroup functions that currently take a GObject towards
taking GtkAcceleratable (not the above _add/_remove of course). a few
functions might also get deprecated and move into the GtkAcceleratable
interface, such as:
gboolean gtk_accel_group_activate (GtkAccelGroup *accel_group,
guint accel_key,
GdkModifierType accel_mods);
- add
void gtk_menu_set_accel_path (GtkMenu *menu,
const gchar *accel_path);
this is for menus on which accelerators should be runtime-changable,
but which aren't created by GtkItemFactory. the accel_path is used to
create accelerator paths for all those menu items that have a label.
though this looks like a lot of changes, internally this is mostly moving
code from GtkMenu and GtkItemFactory into GtkAccelGroup and the accelerator
map. in terms of the public API changes, the expected amount of adaption
for third party code is also moderately small:
- apps that currently save/load accelerators through the item factory should
simply use gtk_accelerator_map_load/gtk_accelerator_map_save instead
(GNOME applications do this automatically through gnome-libs)
- apps that do not bother saving/loading should call
gtk_accelerator_map_lock() after gtk_init()
- explicit accelerator installation (most people just do this through
GtkItemFactory and won't notice) should be done through
gtk_accel_group_add() and gtk_accel_group_remove() instead of
gtk_widget_add_accelerator() and gtk_widget_remove_accelerator()
unless we decide to keep those
- monitoring accelerator changes will work through a GtkAccelGroup
signal now and not GtkWidget signals, i highly doubt people are
actually doing that in their apps though (if so, i'd be interested
in hearing why)
---
ciaoTJ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]