cdepillabout / termonad

Terminal emulator configurable in Haskell.
https://hackage.haskell.org/package/termonad
BSD 3-Clause "New" or "Revised" License
398 stars 48 forks source link

figure out a way for the user to set default key bindings #83

Open cdepillabout opened 5 years ago

cdepillabout commented 5 years ago

It would be nice for the user to be able to set default key bindings in their termonad.hs configuration file.

Currently there are two types of key bindings:

It would be nice to give the user the ability to change both of these key bindings.

That is to say, the user should be able to change the key associated with the menu actions (like new tab). The user should also be able to change key bindings that aren't associated with a menu action (like Alt-1 for switching to the first terminal). Finally, the user should be able to add new key bindings that aren't associated with a menu action to call arbitrary functions they define.

burumaj commented 5 years ago

To start of with I really like the idea for this feature. I recently came across Termonad and like using it. This should be fairly simple I imagine. An easy way to do this, I think, is via similar construction like the keyMap allready present. After looking at the source code letting the user define their own Map Key (TMState -> IO Bool) and adding it to the TMConfig should not be to much effort.

Then in App.hs we could do something like union keyMap userKeyMap to generate the final final key map. This will keep the Alt-n combinations, should the user try to define them, and adds all bindings.

The last thing to do is let the user define their own bindings for menu actions. This could be done by exposing a function like createMenuAction :: MonadIO m => Action -> [Accelarator] -> (Application -> m () )

Where Action is something like

data Action = NewTab
            | CloseTab 
            | ...

toText :: Action -> Text
toText NewTab = "app.newtab"
toText CloseTab = "app.closetab"
...

This way I assume it is fairly easy to write a function that adds all accelerators to the application.

However keep in mind I'm quite new to Haskell.

cdepillabout commented 5 years ago

@burumaj Thanks for thinking about this!

To start of with I really like the idea for this feature. I recently came across Termonad and like using it. This should be fairly simple I imagine. An easy way to do this, I think, is via similar construction like the keyMap allready present. After looking at the source code letting the user define their own Map Key (TMState -> IO Bool) and adding it to the TMConfig should not be to much effort.

Then in App.hs we could do something like union keyMap userKeyMap to generate the final final key map. This will keep the Alt-n combinations, should the user try to define them, and adds all bindings.

This sounds like a good idea. Here are a couple small additions:

The last thing to do is let the user define their own bindings for menu actions. This could be done by exposing a function like createMenuAction :: MonadIO m => Action -> [Accelarator] -> (Application -> m ())

I think this is a little more tricky. If all we want to do is to let the user change key bindings for menu actions, then we need a mapping like the following:

data MenuAction = NewTab | CloseTab | Quit | Copy | Paste

defaultMenuActionMap :: Map MenuAction String
defaultMenuActionMap =
  mapFromList
    [ (NewTab, "<Shift><Ctrl>T")
    , (CloseTab, "<Shift><Ctrl>W")
    , ...
    ]

This should be relatively easy to implement.

However, if we also want to let the user override the actual action that is called, maybe we could have additional hooks:

https://github.com/cdepillabout/termonad/blob/8d6548b51f91db19795537d1a1871d1856b7c8c3/src/Termonad/Types.hs#L414-L422

For instance, instead of:

https://github.com/cdepillabout/termonad/blob/8d6548b51f91db19795537d1a1871d1856b7c8c3/src/Termonad/App.hs#L314-L317

we could have the following code:

newTabAction <- simpleActionNew "newtab" Nothing
void $ onSimpleActionActivate newTabAction $ \_ -> newTabHook
actionMapAddAction app newTabAction
applicationSetAccelsForAction app "app.newtab" ["<Shift><Ctrl>T"]

I guess we could also have a function like you suggest (createMenuAction :: MonadIO m => Action -> [Accelarator] -> (Application -> m () )), but I feel like it might just be simpler to have different hooks for each action.

This would be a little more involved then the previous change, but it would make it much more customizable for the end-user.


The next step would be making it fully customizable, so that the end-user could even define which menu options are shown.

Currently the menu is defined in XML here:

https://github.com/cdepillabout/termonad/blob/8d6548b51f91db19795537d1a1871d1856b7c8c3/src/Termonad/XML.hs#L45-L91

This would have to be removed and redone completely in Haskell code. Then it could be made customizable and exposed to the user. I haven't given this a lot of thought, but eventually it would be nice to do.