Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Key Sequences / Key Chords #1208

Closed
cyruseuros opened this issue Sep 13, 2018 · 23 comments
Closed

Key Sequences / Key Chords #1208

cyruseuros opened this issue Sep 13, 2018 · 23 comments

Comments

@cyruseuros
Copy link

I was just wondering if there was a way to get Qtile to respond to key sequences /chords such as

  • Emacs style C-c C-c
  • Vim style d i w

Further, is it possible to use any of the mod keys alone? E.g. using the Super key as a leader key that makes Qtile listen to the sequence to follow until it recognizes one / can't match it to any?

If not, I expect I'll finally get down to the "Hacking on Qtile" section of the manual, and start doing so. If that's the case, I'd really appreciate any pointers you guys might have.

Also thinking of implementing an emacs-which-key (https://github.com/justbur/emacs-which-key) kind of popup for when you bug out and forget shortcuts. Any pointers on that are more than welcome as well as I'll probably start working on that regardless of the answer to the above. The same goes for any preferences anyone might have, e.g. curses over GUI (and the case for either).

@Melkor333
Copy link
Contributor

Look at my issue #1190, I started doing this. Since opening the issue, I have hacked a bit more to have a widged display when I press something, but since I am about to build my own nixos desktop condiguration, I couldn't document it yet, open a PR here or something like that. But I have created a dotfiles repo containing the qtile config.

One thing to remember: I haven't found a way to assign both vim style AND normal/emacs style bindings to the same key. (like using super in super d w AND super+h). If you've got any question to my code, you can gladly open an issue on my gitlab or ask here..

Jfyi the way keychains are stored is a bit ugly right now. I couldn't come up with a good tree-like container to store the variables yet, but I will look into it again when I have a better working desktop.. My goal is to be able to create keychains in the config.py with minimal effort & knowledge, but right now it still includes setting up a root key, a home key, defining the home, etc. Just look at the files KeyChain.py for the feature and keys.py for how to use it in the config.

@cyruseuros
Copy link
Author

Oh, awesome! Thanks, @Melkor333! will take a look once hell (exam) week is over! Just as a first though, have you considered using a dict to store they key sequences instead of the objects themselves (and obviously writing up something pretty to access and modify it)? But I guess that would involve reworking the Key class completely... I guess it will have to wait a few days, but definitely expect me to be in touch.

@Melkor333
Copy link
Contributor

Yeah I have thought about using a dict and tried some ways to do it. But I wasn't able to come up with a useful way and just wanted to have something that works, so it's probably more of a proof of concept implementation at moment.

I am also thinking about a way to programmatically add new shortcuts and modifying them without having to rewrite my configuration file. This would allow for dynamic creation and change of shortcuts.

My longterm goal is to create a project/workspace based setup where I have global, project wide, group wide and window wide commands.
E.g. if I have a terminal in focus, I want to have a shortcut to get the pwd of the terminal and store it in a clipboard which is either globally or workspace specific.
Another thing is dynamic "workspace shortcut favourites": shortcuts which are just one key after pressing "super super" - like pressing "super super x" instead of "super o x" where I need to open xterm very often (it's a very bad example I know^^).

I don't know how other window managers handle shortcuts exactly, but it would be interesting if it was possible to create a separate tool which is completely independent. As far as I know, there doesn't exist anything like that and if not limited to qtile, there may be more guys interested in using and extending it..
I think it wouldn't take too much more effort to create a key mapping library which provides functions to get a list of shortcuts, go through the tree, add, modify and delete keys, etc.
Window manager specific plugins could then implement it and map the keys to the respective commands. An even more abstract approach would be a server-client model so that not only the window manager, but also other programs could talk to it with client implementations.

@cyruseuros
Copy link
Author

cyruseuros commented Sep 15, 2018

That's exactly what I was thinking! Though making it platform independent might be a bit more complex than it seems. I would take inspiration from either Doom Emacs (https://github.com/hlissner/doom-emacs) or Spacemacs(https://github.com/syl20bnr/spacemacs) - personally, I think the community driven nature of Spacemacs has made the key bindings more consistent and mnemonic, and since the pdumber branch appeared, there doesn't seem to be much of a speed advantage either. I don't know if you're an Emacs user, but I would map M-x/SPC SPC to dmenu, or even directly to the users preferred shell (fish would be amazing here, even though I prefer bash), and then use M-: to configure the super-dispatch (or whatever we'd call the project based on the leader key) in real time (and perhaps add a hotkey to add all the live changes you made to your config. Also, the major mode hotkey in Spacemacs - namely , - sounds a lot like what you're talking about. That could get re-bound every time, depending on the environment, (it's just one extra key) while the rest would remain stable so it's not disorienting. Obviously, we would use a modifier like Alt-R or something, but you get my drift.

@Melkor333
Copy link
Contributor

Actually I only tried spacemacs for a short time before I gave up on it because there were too many random bugs for me. I worked with spacevim for some time tho, so I know about the idea of SPC and it is actually why I wanted to have this concept in qtile, too. But idk what all this stuff like esuper-dispatch, major mode, etc. means. From what I understand you would like to create a Desktop environment on top of Qtile with predefined Keybindings?

@cyruseuros
Copy link
Author

Predefined only in the sane defaults sense. Of course, they could be freely rebound. Nothing as heavy as a full blow DE, no dependencies or anythingike that. More like a simplified way to launch commands from the console without so much typing and some window manager manipulation options for convenience. Kinda like magit in Emacs. It's such a thin porcelain over git it doesn't actually remove any options, just makes the flow better. A major mode in Emacs is a primary mode the editor enters when working on different kinds of files. E.g. python-mode, Shell-script, JavaScript-IDE, etc. In Spacemacs, all the bindings related to the current major mode are accessible through ,. The bindings themselves are dynamic. I'm just saying the concept of a separate leader key and a "major mode" leader key seems like a good idea.

@acuenat
Copy link

acuenat commented Sep 17, 2018

That would be great. I3 is doing something similar that i found very useful when i was using it
https://i3wm.org/docs/userguide.html#binding_modes

@cyruseuros
Copy link
Author

I should have a working implementation of this up by the end of next week. Maybe I'll manage to throw in a curses completion thingy as well, but I doubt it. Also, I was thinking of adding an interface to dump keymaps in json, so people can test them out and exchange them. Of course you could still dynamically configure them if you so choose. Thoughts?

@Melkor333
Copy link
Contributor

Thank you very much for doing this!
I really like the idea of using json (as long as it doesn't restrict dynamic configuration). This would also make it easier to test different presets :)

What exacty do you mean with curses completion? Something similar to emacs-which-key?
Because I thought i'd try to create a rofi interface for this lateron 😄

And are you trying to merge it directly into qtile or you will it be separate like my implementation?

@cyruseuros
Copy link
Author

cyruseuros commented Sep 26, 2018

No problem man. Time to give back to the FOSS community after all it has done for me (to the extent that I can). I think I will be merging it straight into Qtile because I am depending heavily on their hotkey daemon. After that, I will probably gut it and make it an independent thing as well (and hopefully let someone else maintain it, too much work to do right now) as there is no reason it couldn't be used with any other window manager.

Rofi completion sounds awesome, but I don't know enough about it to do anything but use it. After I upload this stuff (gimme another couple of days, I fell sick recently and couldn't work at all) I'd very much appreciate any help.

Update:
Reimplementing the keys in Qtile from the ground up to get around having to choose between Vi and Emacs keybindings. For example, M a C-a m would be a perfectly valid keybinding. Technically you would always be using Vi style, but if all your sequences are of length 1, (e.g. M-S-a), or your standard Emacs chords (C-c C-c), it's basically Emacs.

@alexdreptu
Copy link

I'm also interested in this feature, I can't work with mod+ keys alone, I need something like i3 mode to have my key combinations work in harmony across applications. Without this I can't go any further into qtile, it's a deal breaker for me.

@Melkor333
Copy link
Contributor

You can check out my config at Gitlab for a more or less working solution. The file KeyChain.py is imported in the keys.py file where the "modes" as you may call them are defined. If you have any questions to my config, I'll gladly answer them ☺️

Oh and excuse the mess in the files! 😄

@alexdreptu
Copy link

@Melkor333 thanks for sharing, at the moment I've put it on hold, but I'll be back at it soon. And no worries about the files 😁

@josefson
Copy link

Someone really sold the idea of a python twm for me on irc. However this is a dalbreaker as i currently have something similar with i3-modes

@Melkor333
Copy link
Contributor

Well it‘s currently not built in but I wrote a class which can do it, as said in my last comment.
I can‘t tell if it still works since I hopped to awesomewm for the time being but you can try it pretty easily.
download the file qtile_config/KeyChain.py from my config repo, import the classes KeyNode and KeyWidget (if you want to see a hint in your qtile-bar when you enter a keychain) in your config and configure it like I did in qtile_config/keys.py.
You should then be able to add the KeyWidget to any bar, for example:

screens = [
    Screen(
        bottom=bar.Bar(
            [
                Keywidget(),
                ...
            ]
))]

@josefson
Copy link

josefson commented Apr 20, 2020

@Melkor333 For the sake of others xcbq moved to libqtile.backend.x11

  File "/home/elodin/.config/qtile/KeyChain.py", line 4, in <module>
    from libqtile import xcbq
ImportError: cannot import name 'xcbq' from 'libqtile' (/usr/lib/python3.8/site-packages/libqtile/__init__.py)

Edit:
Couldn't make it work

2020-04-20 10:28:56,716 ERROR libqtile qtile.py:make_qtile():L129 Error while reading config file (No such key: Root)
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/libqtile/scripts/qtile.py", line 127, in make_qtile
    config = confreader.Config.from_file(kore, options.configfile)
  File "/usr/lib/python3.8/site-packages/libqtile/confreader.py", line 95, in from_file
    cnf.validate(kore)
  File "/usr/lib/python3.8/site-packages/libqtile/confreader.py", line 109, in validate
    raise ConfigError("No such key: %s" % k.key)
libqtile.confreader.ConfigError: No such key: Root

@oboingo
Copy link
Contributor

oboingo commented Apr 20, 2020

@Melkor333 First, thanks for writing this up!

Can you give me (us) a bit more explanation about what is going on here (so I can understand it versus just copy/paste)?

For example, what is this doing?

# set the custom keymap so that caps can be used with "Super_R"
@hook.subscribe.startup_once
def set_keymap():
    subprocess.run(['/home/sam/.nix-profile/bin/xmodmap', '/home/sam/.config/qtile/xmodmaprc'])

And what does "ishome" do? What is root and Super_R doing here?

root = KeyNode([], "Super_R", [], name="Root")
home = KeyNode([], returnkey, [], ishome=True)
home.addchildren(
    root,
    KeyNode([mousemod, 'shift'], 'r', [], lazy.restart()),
    KeyNode([mousemod, "shift"], 'q', [], lazy.shutdown()),
)
root.sethome(home)
keys = home.children

Maybe just a nudge in the right direction on understanding what is going on? Thanks in advance!

@josefson
Copy link

@oboingo I reckon the hook is for he to rearrange his keys on the physical keyboard thorugh xmodmap upon startup_once event.

The later, as far as I understood, which may be wrong:
ishome appears to set this KeyNode as the "default" way of bindings which he uses the returnkey variable with the Escape value to Escape/return to. So to exit completely or go back a mode.

@oboingo
Copy link
Contributor

oboingo commented Apr 23, 2020

I whipped up something that seems to work, using @Melkor333 's code as a basis.

https://gist.github.com/oboingo/67150b907d71dde00130cd2dcabf582c

@oboingo
Copy link
Contributor

oboingo commented Apr 25, 2020

I did get this to work, it is in my example config:
https://github.com/qtile/qtile-examples/blob/master/oboingo/keys.py

That being said, I am half way done re-writing it to integrate into Qtile directly (as opposed to being separate). Hopefully it will simplify it some.

I am curious though, if it is going in directly... what does everyone like for what we call this? Key Chains? Key Chords? Key Groups?

@tych0
Copy link
Member

tych0 commented Apr 25, 2020 via email

@Melkor333
Copy link
Contributor

In my opinion Key Sequence or Key Chain seems more intuitive, since in comparison to music a chord would rather reflect multiple keys pressed simultaneously. But I'm not native english so my intuition might be wrong.

tych0 pushed a commit that referenced this issue Jun 19, 2020
PR implements key chords(discussed here #1208) as core qtile functionality.
Chords are implemented in two ways:

    "immediate chord" after enter chord its any subbinding or Escape cause leave chord
    "mode chord" work more like VIM modes we stay in this mode till Escape key is hit

PR implements also widget indicator for "mode chord".

Closes #1777
fdev31 pushed a commit to fdev31/qtile that referenced this issue Jun 30, 2020
PR implements key chords(discussed here qtile#1208) as core qtile functionality.
Chords are implemented in two ways:

    "immediate chord" after enter chord its any subbinding or Escape cause leave chord
    "mode chord" work more like VIM modes we stay in this mode till Escape key is hit

PR implements also widget indicator for "mode chord".

Closes qtile#1777
@ramnes
Copy link
Member

ramnes commented Aug 19, 2020

#1777 has been merged, thanks everyone! It still needs some documentation if anyone wants to help.

@ramnes ramnes closed this as completed Aug 19, 2020
@ep12 ep12 mentioned this issue Mar 1, 2021
9 tasks
ep12 added a commit to ep12/qtile that referenced this issue Mar 17, 2021
This commit implements a chord stack to keep track of the chord modes
that were activated. When exiting a mode, a higher-level chord with
the mode attribute is searched for. If found, it is activated.
Otherwise the root keybindings are restored.
A new command is introduced: cmd_ungrab_all_chords.
Unlike before, cmd_ungrab_chord does not directly go to the root mode.
This can now be explicitly done with cmd_ungrab_all_chords.

Rationale:
+ emacs-like shortcut sequences have been requested: qtile#1208, qtile#470
+ they are available since qtile#1777 (mid-2020), but probably a niche
  application. Let's see how this develops.
+ people who wanted this feature (including me) probably gave up and
  maybe switched to another WM with this functionality,
  [e.g. this comment](qtile#1208 (comment)).
+ The functionality this PR provides cannot be achieved with user extensions,
  as stated in qtile#2241. The hooks are not called in the right places to create a
  work-around.
+ The pr was developed and tested extensively with [my config](https://github.com/ep12/qtileconf)
  (especially `xournal_bindings.py`). I was able to do everything I wanted.
  Before that I filtered the actions by program, see qtile#1661. With the `xournal`
  chord/mode I don't need all that complicated stuff.
+ Combining normal key bindings with custom modes is really quite ideal: the
  important stuff can be controlled with one keystroke, special stuff is in a
  separate mode.
+ The performance impact of this feature enhancement is ~0.
+ The config breakage impact of this feature is minimal: having
  `lazy.ungrab_chord()` bound to a key inside two nested `KeyChord`s with
  `mode` set behaves now differently; a suitable replacement
  (`lazy.ungrab_all_chords()`) is provided.

Furthermore, the current_chord variable is removed as it is not
necessary anymore.

The linter complained about an else block after a for without a break.
This superfluous else was removed.
tych0 pushed a commit that referenced this issue Mar 19, 2021
This commit implements a chord stack to keep track of the chord modes
that were activated. When exiting a mode, a higher-level chord with
the mode attribute is searched for. If found, it is activated.
Otherwise the root keybindings are restored.
A new command is introduced: cmd_ungrab_all_chords.
Unlike before, cmd_ungrab_chord does not directly go to the root mode.
This can now be explicitly done with cmd_ungrab_all_chords.

Rationale:
+ emacs-like shortcut sequences have been requested: #1208, #470
+ they are available since #1777 (mid-2020), but probably a niche
  application. Let's see how this develops.
+ people who wanted this feature (including me) probably gave up and
  maybe switched to another WM with this functionality,
  [e.g. this comment](#1208 (comment)).
+ The functionality this PR provides cannot be achieved with user extensions,
  as stated in #2241. The hooks are not called in the right places to create a
  work-around.
+ The pr was developed and tested extensively with [my config](https://github.com/ep12/qtileconf)
  (especially `xournal_bindings.py`). I was able to do everything I wanted.
  Before that I filtered the actions by program, see #1661. With the `xournal`
  chord/mode I don't need all that complicated stuff.
+ Combining normal key bindings with custom modes is really quite ideal: the
  important stuff can be controlled with one keystroke, special stuff is in a
  separate mode.
+ The performance impact of this feature enhancement is ~0.
+ The config breakage impact of this feature is minimal: having
  `lazy.ungrab_chord()` bound to a key inside two nested `KeyChord`s with
  `mode` set behaves now differently; a suitable replacement
  (`lazy.ungrab_all_chords()`) is provided.

Furthermore, the current_chord variable is removed as it is not
necessary anymore.

The linter complained about an else block after a for without a break.
This superfluous else was removed.
stonewell pushed a commit to stonewell/qtile that referenced this issue Aug 11, 2021
This commit implements a chord stack to keep track of the chord modes
that were activated. When exiting a mode, a higher-level chord with
the mode attribute is searched for. If found, it is activated.
Otherwise the root keybindings are restored.
A new command is introduced: cmd_ungrab_all_chords.
Unlike before, cmd_ungrab_chord does not directly go to the root mode.
This can now be explicitly done with cmd_ungrab_all_chords.

Rationale:
+ emacs-like shortcut sequences have been requested: qtile#1208, qtile#470
+ they are available since qtile#1777 (mid-2020), but probably a niche
  application. Let's see how this develops.
+ people who wanted this feature (including me) probably gave up and
  maybe switched to another WM with this functionality,
  [e.g. this comment](qtile#1208 (comment)).
+ The functionality this PR provides cannot be achieved with user extensions,
  as stated in qtile#2241. The hooks are not called in the right places to create a
  work-around.
+ The pr was developed and tested extensively with [my config](https://github.com/ep12/qtileconf)
  (especially `xournal_bindings.py`). I was able to do everything I wanted.
  Before that I filtered the actions by program, see qtile#1661. With the `xournal`
  chord/mode I don't need all that complicated stuff.
+ Combining normal key bindings with custom modes is really quite ideal: the
  important stuff can be controlled with one keystroke, special stuff is in a
  separate mode.
+ The performance impact of this feature enhancement is ~0.
+ The config breakage impact of this feature is minimal: having
  `lazy.ungrab_chord()` bound to a key inside two nested `KeyChord`s with
  `mode` set behaves now differently; a suitable replacement
  (`lazy.ungrab_all_chords()`) is provided.

Furthermore, the current_chord variable is removed as it is not
necessary anymore.

The linter complained about an else block after a for without a break.
This superfluous else was removed.
fdev31 pushed a commit to fdev31/qtile that referenced this issue May 30, 2022
This commit implements a chord stack to keep track of the chord modes
that were activated. When exiting a mode, a higher-level chord with
the mode attribute is searched for. If found, it is activated.
Otherwise the root keybindings are restored.
A new command is introduced: cmd_ungrab_all_chords.
Unlike before, cmd_ungrab_chord does not directly go to the root mode.
This can now be explicitly done with cmd_ungrab_all_chords.

Rationale:
+ emacs-like shortcut sequences have been requested: qtile#1208, qtile#470
+ they are available since qtile#1777 (mid-2020), but probably a niche
  application. Let's see how this develops.
+ people who wanted this feature (including me) probably gave up and
  maybe switched to another WM with this functionality,
  [e.g. this comment](qtile#1208 (comment)).
+ The functionality this PR provides cannot be achieved with user extensions,
  as stated in qtile#2241. The hooks are not called in the right places to create a
  work-around.
+ The pr was developed and tested extensively with [my config](https://github.com/ep12/qtileconf)
  (especially `xournal_bindings.py`). I was able to do everything I wanted.
  Before that I filtered the actions by program, see qtile#1661. With the `xournal`
  chord/mode I don't need all that complicated stuff.
+ Combining normal key bindings with custom modes is really quite ideal: the
  important stuff can be controlled with one keystroke, special stuff is in a
  separate mode.
+ The performance impact of this feature enhancement is ~0.
+ The config breakage impact of this feature is minimal: having
  `lazy.ungrab_chord()` bound to a key inside two nested `KeyChord`s with
  `mode` set behaves now differently; a suitable replacement
  (`lazy.ungrab_all_chords()`) is provided.

Furthermore, the current_chord variable is removed as it is not
necessary anymore.

The linter complained about an else block after a for without a break.
This superfluous else was removed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants