discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

New keybinding system


From: Nicola Pero
Subject: New keybinding system
Date: Mon, 25 Feb 2002 19:25:55 +0000 (GMT)

Spent most of yesterday writing a new keybinding engine.

The new engine already does all what I was told that openstep can do ...
but I think it can do more :-)

I'd like comments about functionality people would like to have in the
keybindings engine.

I still want to improve the core engine, before actually working on the
public API of how real users would actually configure it and how
application programmers would actually configure it.  Anyway, there will
be a system-wide default setting, and it will be possible to modify
keybindings on a per-user or per-application basis.  I also want to
provide something which Apple MacOS X doesn't provide, which is the
ability to load different keybindings in different textviews at the same
time (for example, an integrated editor might have different keybindings
for code editing and for normal text editing).  That's pretty easy - we
just attach a particular NSInputManager to a NSTextView (instead of using
the shared one), and load different keybindings in that particular
NSInputManager.

Anyway - back to the core engine.

Keybindings are specified in files, holding dictionaries.  

The dictionary can contain a simple keybinding, such as 

 "Control-f" = "moveForward:";

this binds Control-f to call the selector 'moveForward:'.

OpenStep used some complicated and confusing encoding for keystrokes,
which I don't remember.  I think ^ was for Control, $ for Alternate, and
similar, then there were no names so you needed to put the unicode number
for most function keys (like Home or such).  That was very ugly,
unreadable.  We use an extremely user friendly description of keystrokes.

I've put into the NSInputManager functionality to read keystroke
descriptions in user-friendly format, and also to produce them.  The API
is public so it can be used by any other piece of code needing it.

Anyway, NSInputManager uses stuff like 'Control-g', 'Control-Alternate-H',
'Shift-LeftArrow', and similar very nice strings.  It can recognize a lot
of variants, such as 'Ctrl-g', 'C-g', or 'C-M-H', 'Control-Meta-H',
'Ctrl-Alt-H', 'C-Alternate-H' etc

Then, there are keybindings which bind a keystroke to multiple actions,
such as

 "Control-f" = ("selectAll:", "delete:");

this binds Control-f to invoke the selectors selectAll:, then delete: (in
this order).  As you see, the list of keybindings is described by an
array. (this is as on openstep)


Then, there are the multi-stroke keybindings, in which you bind a sequence
of keystrokes to an action (/multiple actions), such as

 "Control-f" = { 
                  "Control-a" = "moveForward:";
                  "Control-e" = "moveBackward:";
               };

If you press Control-f followed by Control-a, 'moveForward:' is invoked.
If you press Control-f followed by Control-e, 'moveBackward:' is invoked.

(this is as on openstep)
                 

Then, there is the abort keybinding.  Say that you press Control-f.  Then
you change your mind, and no longer want to actually input a keybinding
starting with Control-f.  You can abort your keybinding sequence by
entering Control-g, the abort key (same as emacs or bash, and as it was on
openstep).

While the interpretation of all other keystrokes depends on the context
(ie, which keystrokes came before, because of the existence of
multi-stroke keybindings), the interpretation of Control-g does not depend
on the context.  It's a completely special case.  In whatever context you
are, pressing Control-g should reset the input manager machinery.

You can change the abort keybinding by setting the GSAbortKey user
defaults (might change without notice till the API is stable).


Then, there is the quote keybinding.  Say that you know you never use the
^ sign, and you bind some silly action to it - for example, you bind
selectAll: to ^.  Now whenever you press ^, selectAll: is executed.  If
you once in your life actually need to enter ^, then you need the quote
keybinding, which is normally Control-q.  Pressing Control-q causes the
next keystroke to be interpreted literally, disabling its meaning as a
keybinding.  So, pressing Control-q ^ will insert a literal ^ into the
text.

You can change the quote keybinding by setting the GSQuoteKey user
defautls (might change without notice till the API is stable).

==

Now come the things I still need to fix and think about.

 1. the abort keybinding.  If you open a save panel, and want to abort it,
    you currently press 'Esc'.  I think the abort key should be the same
    in all context - it's actually very important for consistency.

    I personally like being able to type Control-g and would like that to
    abort panels in the same way as Esc.  At the same time, I think people
    who are used to typing Esc to abort panels, would benefit if the text
    system recognized Esc as being an abort key.

    Anyway - I think we should use the same abort key(s) everywhere.  It
    should be possible to configure it (them), but it (they) should be the
    same everywhere.

    Perhaps by default we might accept both Control-g and Esc as abort
    keys.

    Power users with an emacs background will probably disable the Esc
    abort key since to reproduce emacs Meta keybindings and not interfere
    with internationalized input management, you need to use Esc to mean
    Meta - basically you add a lot of multi-stroke keybindings all
    beginning with Esc.

    Anyway - this is just to say that I think I will allow up to two
    abort keys defined - by default they will be Esc and Control-g.
    I'll add an NSInputManager method which tells you if a key event
    represents an abort key or not.  All gnustep-gui code which now only
    recognizes an hardcoded Esc as abort key will be changed to use that
    method instead, so it will automatically recognize Control-g 
    (consistently with the text system), and changing the abort keys in
    the defaults will have a global effect.

 2. Currently, it works as on openstep - if you load the

     "Control-f" = { 
                  "Control-a" = "moveForward:";
                  "Control-e" = "moveBackward:";
               };

     keybindings, and later on you load 

     "Control-f" = { 
                  "Control-k" = "moveUpsideDown:";
               };

     this will completely override the previous dictionary for Control-f,
     so you loose all previous keybindings starting with Control-f.

     this is going to be an issue, since users will have their keybindings
     loaded after the system ones, but normally just want to add a few
     of them, while keeping the system ones.

     I want to change this so that the new keybindings are added to the
     existing ones (replacing them if it's the case), but the old ones
     are not discarded.
     [of course this is more difficult to implement than the current
     behaviour]

  3. I want to add a new different method of adding multi-stroke
     keybindings, which is by putting into the dictionary

     ("Control-f", "Control-k") = "moveUpsideDown:";

     Now this would be very pretty in my opinion.  This would bind
     the sequence Control-f followed by Control-k to 'moveUpsideDown:'.
     [of course this is more difficult to implement but we can manage]

  4. Many people complain that if they type, say, Control-x, and that is
     bound to nothing, garbage is inserted into the textview.

     What can I do about that ... I don't know.  I've already modified the
     NSInputManager so that if you press a function key, say, F8, or
     ScrollLock, or End, and that is not bound to a keybinding, the 
     corresponding unicode character is not inserted into the text.
     Which is quite good.

     But this is easy, because I check the NSFunctionKeyMask.  I don't
     think I can ignore the other keybindings as easily, considering that
     internationalized text input might require people to use strange
     modifiers when typing in.
     My solution would be to have a user defaults which determines if
     keystrokes having NSControlKeyMask set should be inserted or not
     in the text.
     By default it's NO.


  5. What to do when the user starts what looks like a sequence of
     keystrokes corresponding to a certain keybinding, but then it turns
     out that it isn't like that.  For example, say that you bind
     Control-x Control-s to save: - a classical unmissable Emacs
     keybinding.  You also bind Control-x Control-f to other stuff, etc
     a lot of Control-x Control-? keybindings.

     Now the user types Control-x into a textview.  The input manager
     determines it could be the start of a multi-stroke keybinding, so
     it saves the key event, updates its internal parser state-machine,
     and waits eagerly for further input.  Disappointingly enough, the
     user types in 'f', and Control-x f is not bound to anything.  What
     should the input manager do ?

     The current implementation implements the most complicated and
     refined mechanism - it takes the first key which was saved -
     Control-x in this case - and inserts it literally into the text
     (since it has determined that it wasn't a keybinding).
     It then starts again keystrokes processing from scratch from the
     second stored keystroke.  For example, if the user types
     Control-x Control-x Control-s, currently the layout manager
     would determine that the first Control-x is not part of a keybinding,
     so it inserts it literally, then process again from the second
     keystroke, and recognizes the Control-x Control-s sequence
     and executes it as save:.

     The other option is the very simple one - once the input manager
     determines the the user has inserted a meaningless keybinding
     combination, it simply discards all input up to that point.  I don't
     like this option at all.  It's plainly wrong in my opinion.

     The refined option would work very well ... except that stuff like
     Control-x, when is interpreted literally, inserts garbage into the
     text rather than being ignored.

     Perhaps a user defaults option might control whether keystrokes
     with NSControlKeyMask defined are inserted or not into the text
     if they are not keybindings ?  The default would be not to insert
     them.  Does this make sense ?

     Another thing - should the textview beep in response to meaningless
     multi-stroke keybindings ?  Or perhaps when a control keystroke
     happens to be inserted literally, should it beep ?  Another user
     default ?  I don't like software to beep, but since there don't
     seem to be much ways in which the input manager can communicate
     with the user, perhaps it's not that bad.


Ok - sorry for the long email - please let me know any comments and
suggestions.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]