swelet
swelet

Reputation: 8722

Keyboard combinations in Elm 0.17 and later

How do I build keyboard combinations into my Elm app, eg. "shift + alt + enter"? You would do something like this to react to a single key pressed (for example the enter key):

import Keyboard

type Msg
  = KeyDown Keyboard.KeyCode

type alias Model =
  ...

update msg model =
    case msg of
        KeyDown key ->
          handleKeyDown key model

subscriptions model =
    Sub.batch
        [ 
          Keyboard.downs KeyDown
        ]

handleKeyDown key model =
  case key of
    13 -> -- Enter key
      Debug.log "Other key"
      model

    _ -> -- Any other key
      Debug.log "Other key"
      model


view model =
    ...

But how can you do the same for multiple keys pressed?

Upvotes: 4

Views: 738

Answers (2)

Zimm i48
Zimm i48

Reputation: 3081

From the documentation:

Longer-term, it makes sense to help track key combinations and a couple other common scenarios. If you have a particular problem you think this library could address more directly, please describe it as a SSCCE in an issue. Please do not suggest the solution. Just describe the scenario. Once it becomes clearer what folks are up to, it will be time to add support for those cases in a coherent way.

So, at least it means that it is not supported right now. I'm not fully sure whether it also means that you should open an issue to encourage this later addition or not.

Now, Chad's answer looks like a good temporary solution to me. It looks even better with swelet's suggestion of tracking only a few modifier keys. Note how if you take this approach you don't need to rely on any List or Set but you can instead have a little record with 4 booleans.

Finally, if you want for some reasons to track all the keys (if for instance you would like the user being able to link [F1] to some action, then performance-wise it would make sense to use a more efficient representation of sets. Any subset of a finite set for instance can be defined as a single number, each bit being a boolean meaning absence or presence of one particular element. You can do such things with the Bitwise library.

Upvotes: 1

Chad Gilbert
Chad Gilbert

Reputation: 36375

You could use Keyboard.downs as pdoherty926 mentioned as well as a Set to keep track of which keys are pressed. You'll need to look at Keyboard.ups as well to know when a key is released.

Here is a working example:

import Html exposing (..)
import Html.App exposing (program)
import Keyboard exposing (..)
import Set exposing (Set)
import Char

main =
  program { init = (initialModel, Cmd.none), view = view, update = update, subscriptions = subscriptions }

initialModel =
  { keysDown = Set.empty
  }

view : Model -> Html Msg
view model =
  text <| toString <| Set.map Char.fromCode model.keysDown

type Msg
  = KeyDown KeyCode
  | KeyUp KeyCode

type alias Model =
  { keysDown : Set KeyCode
  }

update msg model =
  case msg of
    KeyDown key ->
      ({ model | keysDown = Set.insert key model.keysDown }, Cmd.none)
    KeyUp key ->
      ({ model | keysDown = Set.remove key model.keysDown }, Cmd.none)

subscriptions _ =
    Sub.batch
        [ Keyboard.downs KeyDown
        , Keyboard.ups KeyUp
        ]

Upvotes: 5

Related Questions