Code Monkey home page Code Monkey logo

keyboard-extra's Introduction

Keyboard Extra

Nice keyboard inputs in Elm.

It is quite tedious to find out the currently pressed down keys with just the Keyboard module, so this package aims to make it easier.

You can use Keyboard.Extra in two ways:

  1. The "Msg and Update" way, which has some setting up to do but has a bunch of ways to help you get the information you need.
  2. The "Plain Subscriptions" way, where you get subscriptions for keys' down and up events, and handle the rest on your own.

Full examples you can run and play with on Ellie

All of the examples are also in the example directory in the repository.

Msg and Update

If you use the "Msg and Update" way, you will get the most help, such as:

  • All keyboard keys are named values of the Key type, such as ArrowUp, CharA and Enter
  • You can find out whether e.g. Shift is pressed down when any kind of a Msg happens in your program
  • Arrow keys and WASD can be used as { x : Int, y : Int } or as a union type (e.g. South, NorthEast)
  • You can also get a full list of keys that are pressed down

When using Keyboard.Extra like this, it follows The Elm Architecture. Its model is a list of keys, and it has an update function and some subscriptions. Below are the necessary parts to wire things up. Once that is done, you can get useful information using the helper functions such as arrows and arrowsDirection.


Include the list of keys in your program's model

import Keyboard.Extra exposing (Key)

type alias Model =
    { pressedKeys : List Key
    -- ...
    }

init : ( Model, Cmd Msg )
init =
    ( { pressedKeys = []
      -- ...
      }
    , Cmd.none
    )

Add the message type in your messages

type Msg
    = KeyMsg Keyboard.Extra.Msg
    -- ...

Include the subscriptions for the events to come through (remember to add them in your main too)

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch
        [ Sub.map KeyMsg Keyboard.Extra.subscriptions
        -- ...
        ]

And finally, you can use update to have the list of keys be up to date

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        KeyMsg keyMsg ->
            ( { model | pressedKeys = Keyboard.Extra.update keyMsg model.pressedKeys }
            , Cmd.none
            )
        -- ...

Now you can get all the information anywhere where you have access to the model, for example like so:

calculateSpeed : Model -> Float
calculateSpeed model =
    let
        arrows =
            Keyboard.Extra.arrows model.pressedKeys
    in
        model.currentSpeed + arrows.x


isShooting : Model -> Bool
isShooting model =
    List.member Space model.pressedKeys

Have fun! :)


PS. The Tracking Key Changes example example shows how to use updateWithKeyChange to find out exactly which key was pressed down / released on that update cycle.

Plain Subscriptions

With the "plain subscriptions" way, you get the bare minimum:

  • All keyboard keys are named values of the Key type, such as ArrowUp, CharA and Enter

Setting up is very straight-forward:

type Msg
    = KeyDown Key
    | KeyUp Key
    -- ...


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch
        [ Keyboard.Extra.downs KeyDown
        , Keyboard.Extra.ups KeyUp
        -- ...
        ]

There's an example for this, too: Plain Subscriptions

keyboard-extra's People

Contributors

fredcy avatar jtojnar avatar ohanhi avatar rohanorton avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

keyboard-extra's Issues

no way to tell what key is being pressed/released

My use case is that i'm trying to see when the user has pressed one of the directions on the numpad + the shift key. So let's say they pressed shift, then they pressed 9 (PgUp) which represents NE.

To do this, ideally, I'd match on the keycode for Numpad 9, and do a test for isPressed on shift.

The problem is matching on keycode for Numpad 9. When a Msg comes in for a keypress, the Msg type is hidden, so I only have the current model and the new model. There is no easy way of finding out what just got pressed or released. I'd have to use pressedDown: Model -> List Key on the existing model and then diff that with the new model to work out what key was just pressed or released. Then I'd have to work out whether it was pressed or released.

If the Msg type was exposed, then I could get the KeyCode from the msg and use fromCode to turn that into a Key. That would solve my use case.

Consider exposing Msg to react on modified key

Hi,
I'm in a context of a game loop where I need to track up and down keys, but also to react when a given key has been pressed or released. I can do isPressed on old and new model but it's less practical than matching on the msg, furthermore if I have several keys to look after.

So:

    KeyboardMsg keyboardMsg ->
      let
        ( newKeyboard, keyboardCmd ) =
          Keyboard.update keyboardMsg model.keyboard

        escPressed =
          Keyboard.isPressed Keyboard.Escape

        chatCmd =
          if not (escPressed model.keyboard) && escPressed newKeyboard then
            Ports.setFocus chatFieldId
          else
            Cmd.none
      in
        res
          { model | keyboard = newKeyboard }
          (Cmd.batch [ chatCmd, Cmd.map KeyboardMsg keyboardCmd ])

would become:

    KeyboardMsg keyboardMsg ->
      let
        ( newKeyboard, keyboardCmd ) =
          Keyboard.update keyboardMsg model.keyboard

        chatCmd =
          case keyboardMsg of
            Keyboard.Down Keyboard.Escape ->
              Ports.setFocus chatFieldId
            _ ->
              Cmd.none
      in
        res
          { model | keyboard = newKeyboard }
          (Cmd.batch [ chatCmd, Cmd.map KeyboardMsg keyboardCmd ])

Cheers

A way to subscribe without boilerplate

I know it's the recommended way to achieve modularity, but to wire Keyboard.Extra's init, model, update and subscription into my own seems like a lot of wiring code just to get notified when arrowDirection changes.

I've tried making a module that exposes arrowDirection as a subscription, just like Keyboard exposes keypresses as a subscription, but it was very complicated, and there is not much documentation. Have you tried / considered it?

No way to do preventDefault

Hi,
Is there a way to turn on preventDefault for a keypress (or all keypresses)?

I know there's been some discussion about preventDefault at:
https://github.com/elm-lang/virtual-dom/issues/18
, but that discussion seems to be mainly about key presses that are sent to an Input component, etc.

Specifically, when I press Tab, it's handled by Keyboard.Extra, but Chrome also focuses elements on the page (including going to the URL bar); this messes up subsequent key presses being handled the right way.

Left shift not detected

Using Chromium 51.0.2704.79, isPressed Shift model will detect a right Shift but not a left Shift.
As far as I can tell, there is no way to detect a left Shift.

Using just the Keyboard module, both shifts are detected as the same code, 16.

Inefficient fromCode lookup

It looks like you're doing a linear search for your Key on every key press

fromCode : KeyCode -> Key
fromCode code =
    codeBook
        |> List.filter (((==) code) << fst)
        |> List.map snd
        |> List.head
        |> Maybe.withDefault Other

This code would be more efficient if codebook were a dict e.g.

codeDict = Dict.fromList codeBook

fromCode : KeyCode -> Key
fromCode code =
    codeDict
        |> Dict.get code
        |> Maybe.withDefault Other

This also has the benefit of making terser code!

Update misleading

Why have update return a pair with the Cmd if the cmd is always Cmd.none.

I get that it fits nicely into the Elm setup, but I find this misleading and think it makes more sense to just return the new model.

Constraints causing warning in 0.18-beta2

elm-repl 0.18.0-beta gives me this warning:

Problem in dependency ohanhi/keyboard-extra 1.3.0

The elm-package.json constraints of 'ohanhi/keyboard-extra' are probably
letting too much stuff through. Definitely open an issue on the relevant github
repo to get this fixed and save other people from this pain.

In the meantime, take a look through the direct dependencies of the broken
package and see if any of them have had releases recently. If you find the new
thing that is causing problems, you can artificially constrain things by adding
some extra constraints to your elm-package.json as a stopgap measure.

Our elm-package.json is:

{
    "version": "1.0.0",
    "summary": "foo",
    "repository": "https://github.com/foo/foo.git",
    "license": "BSD3",
    "source-directories": [
        "src"
    ],
    "exposed-modules": [],
    "dependencies": {
      "elm-lang/core": "5.0.0 <= v < 6.0.0",
      "elm-lang/html": "2.0.0 <= v < 3.0.0",
      "ohanhi/keyboard-extra": "1.3.0 <= v < 2.0.0",
      "rtfeldman/elm-css": "7.0.0 <= v < 8.0.0",
      "rtfeldman/elm-css-helpers": "2.0.1 <= v < 3.0.0"
    },
    "elm-version": "0.18.0 <= v < 0.19.0"
}

Keys are not released under certain circumstances

Hi

When I press e.g. "F1" in Chrome, or "Ctrl+F" in Firefox, this will trigger the browser-related command (help and search, respectively).

When returning to the browser, the "F1" is not being released in the first case, "Ctrl" in the second.

The effect is in short: There are "pressed" keys showing, when there aren't any keys pressed. This can also be reproduced by hitting multiple keys (arrows and others mixed)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.