├── .gitignore ├── examples ├── elm-package.json └── Main.elm ├── elm-package.json ├── LICENSE ├── README.md └── src └── Keyboard └── Combo.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | examples/elm-stuff 3 | documentation.json 4 | -------------------------------------------------------------------------------- /examples/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.0", 3 | "summary": "Examples for Keyboard Combo", 4 | "repository": "https://github.com/scottcorgan/keyboard-combo.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "../src" 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 13 | "ohanhi/keyboard-extra": "2.0.2 <= v < 3.0.0" 14 | }, 15 | "elm-version": "0.18.0 <= v < 0.19.0" 16 | } 17 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.0.0", 3 | "summary": "Handle keyboard key combinations with type safety in Elm", 4 | "repository": "https://github.com/scottcorgan/keyboard-combo.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "./src" 8 | ], 9 | "exposed-modules": [ 10 | "Keyboard.Combo" 11 | ], 12 | "dependencies": { 13 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 14 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 15 | "ohanhi/keyboard-extra": "2.0.2 <= v < 3.0.0" 16 | }, 17 | "elm-version": "0.18.0 <= v < 0.19.0" 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016, Scott Corgan 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /examples/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Html exposing (..) 4 | import Keyboard.Combo 5 | 6 | 7 | main : Program Never Model Msg 8 | main = 9 | Html.program 10 | { subscriptions = subscriptions 11 | , init = init 12 | , update = update 13 | , view = view 14 | } 15 | 16 | 17 | keyboardCombos : List (Keyboard.Combo.KeyCombo Msg) 18 | keyboardCombos = 19 | [ Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.s ) Save 20 | , Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.a ) SelectAll 21 | , Keyboard.Combo.combo3 ( Keyboard.Combo.control, Keyboard.Combo.alt, Keyboard.Combo.e ) RandomThing 22 | ] 23 | 24 | 25 | 26 | -- Init 27 | 28 | 29 | init : ( Model, Cmd Msg ) 30 | init = 31 | { combos = Keyboard.Combo.init keyboardCombos ComboMsg 32 | , content = "No combo yet" 33 | } 34 | ! [] 35 | 36 | 37 | subscriptions : Model -> Sub Msg 38 | subscriptions model = 39 | Keyboard.Combo.subscriptions model.combos 40 | 41 | 42 | 43 | -- Model 44 | 45 | 46 | type alias Model = 47 | { combos : Keyboard.Combo.Model Msg 48 | , content : String 49 | } 50 | 51 | 52 | 53 | -- Update 54 | 55 | 56 | type Msg 57 | = Save 58 | | SelectAll 59 | | RandomThing 60 | | ComboMsg Keyboard.Combo.Msg 61 | 62 | 63 | update : Msg -> Model -> ( Model, Cmd Msg ) 64 | update msg model = 65 | case msg of 66 | Save -> 67 | { model | content = "Saved" } ! [] 68 | 69 | SelectAll -> 70 | { model | content = "Select All" } ! [] 71 | 72 | RandomThing -> 73 | { model | content = "Random Thing" } ! [] 74 | 75 | ComboMsg msg -> 76 | let 77 | ( updatedKeys, comboCmd ) = 78 | Keyboard.Combo.update msg model.combos 79 | in 80 | ( { model | combos = updatedKeys }, comboCmd ) 81 | 82 | 83 | 84 | -- View 85 | 86 | 87 | view : Model -> Html Msg 88 | view model = 89 | div [] 90 | [ h1 [] [ text "Available Key Commands:" ] 91 | , ul [] 92 | [ li [] [ text "Save: Ctrl+s" ] 93 | , li [] [ text "Select All: Ctrl+a" ] 94 | , li [] [ text "Random Thing: Ctrl+Alt+e" ] 95 | ] 96 | , div [] 97 | [ strong [] [ text "Current command: " ] 98 | , span [] [ text model.content ] 99 | ] 100 | ] 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keyboard Combo 2 | 3 | Handle keyboard key combinations with type safety in Elm 4 | 5 | [Check out the demo](https://embed.ellie-app.com/n3mVWz5gzJ/0) 6 | 7 | **Note:** Not every key is implemented yet. Please open a PR if one is missing. 8 | 9 | ## Usage 10 | 11 | Usage example taken from [Example in _examples_ directory](https://github.com/scottcorgan/keyboard-combo/tree/master/examples) 12 | 13 | ```elm 14 | module Main exposing (..) 15 | 16 | import Html exposing (..) 17 | import Keyboard.Combo 18 | 19 | 20 | main : Program Never Model Msg 21 | main = 22 | Html.program 23 | { subscriptions = subscriptions 24 | , init = init 25 | , update = update 26 | , view = view 27 | } 28 | 29 | 30 | keyboardCombos : List (Keyboard.Combo.KeyCombo Msg) 31 | keyboardCombos = 32 | [ Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.s ) Save 33 | , Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.a ) SelectAll 34 | , Keyboard.Combo.combo3 ( Keyboard.Combo.control, Keyboard.Combo.alt, Keyboard.Combo.e ) RandomThing 35 | ] 36 | 37 | 38 | 39 | -- Init 40 | 41 | 42 | init : ( Model, Cmd Msg ) 43 | init = 44 | { keys = Keyboard.Combo.init keyboardCombos ComboMsg 45 | , content = "No combo yet" 46 | } 47 | ! [] 48 | 49 | 50 | subscriptions : Model -> Sub Msg 51 | subscriptions model = 52 | Keyboard.Combo.subscriptions model.keys 53 | 54 | 55 | 56 | -- Model 57 | 58 | 59 | type alias Model = 60 | { keys : Keyboard.Combo.Model Msg 61 | , content : String 62 | } 63 | 64 | 65 | 66 | -- Update 67 | 68 | 69 | type Msg 70 | = Save 71 | | SelectAll 72 | | RandomThing 73 | | ComboMsg Keyboard.Combo.Msg 74 | 75 | 76 | update : Msg -> Model -> ( Model, Cmd Msg ) 77 | update msg model = 78 | case msg of 79 | Save -> 80 | { model | content = "Saved" } ! [] 81 | 82 | SelectAll -> 83 | { model | content = "Select All" } ! [] 84 | 85 | RandomThing -> 86 | { model | content = "Random Thing" } ! [] 87 | 88 | ComboMsg msg -> 89 | let 90 | ( keys, cmd ) = 91 | Keyboard.Combo.update msg model.keys 92 | in 93 | ( { model | keys = keys }, cmd ) 94 | 95 | 96 | 97 | -- View 98 | 99 | 100 | view : Model -> Html Msg 101 | view model = 102 | div [] 103 | [ h1 [] [ text "Available Key Commands:" ] 104 | , ul [] 105 | [ li [] [ text "Save: Ctrl+s" ] 106 | , li [] [ text "Select All: Ctrl+a" ] 107 | , li [] [ text "Random Thing: Ctrl+Alt+e" ] 108 | ] 109 | , div [] 110 | [ strong [] [ text "Current command: " ] 111 | , span [] [ text model.content ] 112 | ] 113 | ] 114 | ``` 115 | -------------------------------------------------------------------------------- /src/Keyboard/Combo.elm: -------------------------------------------------------------------------------- 1 | module Keyboard.Combo 2 | exposing 3 | ( Key 4 | , KeyCombo 5 | , Model 6 | , Msg 7 | , a 8 | , alt 9 | , b 10 | , backSlash 11 | , backTick 12 | , backspace 13 | , c 14 | , closeBracket 15 | , combo1 16 | , combo2 17 | , combo3 18 | , combo4 19 | , comma 20 | , command 21 | , control 22 | , d 23 | , delete 24 | , down 25 | , e 26 | , eight 27 | , enter 28 | , equals 29 | , escape 30 | , f 31 | , five 32 | , forwardSlash 33 | , four 34 | , g 35 | , h 36 | , i 37 | , init 38 | , j 39 | , k 40 | , l 41 | , left 42 | , m 43 | , minus 44 | , n 45 | , nine 46 | , o 47 | , one 48 | , openBracket 49 | , option 50 | , p 51 | , period 52 | , q 53 | , r 54 | , right 55 | , s 56 | , semicolon 57 | , seven 58 | , shift 59 | , singleQuote 60 | , six 61 | , space 62 | , subscriptions 63 | , super 64 | , t 65 | , tab 66 | , three 67 | , two 68 | , u 69 | , up 70 | , update 71 | , v 72 | , w 73 | , x 74 | , y 75 | , z 76 | , zero 77 | ) 78 | 79 | {-| Provides helpers to call messages on the given key combinations 80 | 81 | 82 | ## Types 83 | 84 | @docs Model, Msg, KeyCombo, Key 85 | 86 | 87 | ## Setup 88 | 89 | import Keyboard.Combo 90 | 91 | type alias Model = 92 | { combos : Keyboard.Combo.Model Msg } 93 | 94 | type Msg 95 | = Save 96 | | SaveAll 97 | | RandomThing 98 | | ComboMsg Keyboard.Combo.Msg 99 | 100 | keyboardCombos : List (Keyboard.Combo.KeyCombo Msg) 101 | keyboardCombos = 102 | [ Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.s ) Save 103 | , Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.a ) SelectAll 104 | , Keyboard.Combo.combo3 ( Keyboard.Combo.control, Keyboard.Combo.alt, Keyboard.Combo.e ) RandomThing 105 | ] 106 | 107 | init : ( Model, Cmd Msg ) 108 | init = 109 | { combos = Keyboard.Combo.init keyboardCombos ComboMsg } 110 | } 111 | ! [] 112 | 113 | subscriptions : Model -> Sub Msg 114 | subscriptions model = 115 | Keyboard.Combo.subscriptions model.combos 116 | 117 | @docs init, subscriptions, update 118 | 119 | 120 | ## Combo Helpers 121 | 122 | import Keyboard.Combo 123 | 124 | type Msg 125 | = Save 126 | | SaveAll 127 | | RandomThing 128 | 129 | keyboardCombos : List (Keyboard.Combo.KeyCombo Msg) 130 | keyboardCombos = 131 | [ Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.s ) Save 132 | , Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.a ) SelectAll 133 | , Keyboard.Combo.combo3 ( Keyboard.Combo.control, Keyboard.Combo.alt, Keyboard.Combo.e ) RandomThing 134 | ] 135 | 136 | @docs combo1, combo2, combo3, combo4 137 | 138 | 139 | ## Modifiers 140 | 141 | @docs super, command, shift, control, alt, option, enter, tab, escape, space, backspace, delete 142 | 143 | 144 | ## Letters 145 | 146 | @docs a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 147 | 148 | 149 | ## Number Helpers 150 | 151 | @docs zero, one, two, three, four, five, six, seven, eight, nine 152 | 153 | 154 | ## Arrow Helpers 155 | 156 | @docs left, right, up, down 157 | 158 | 159 | ## Punctuation 160 | 161 | @docs period, comma, semicolon, singleQuote, minus, equals, openBracket, closeBracket, backSlash, forwardSlash, backTick 162 | 163 | -} 164 | 165 | import Keyboard.Extra 166 | import Task 167 | 168 | 169 | -- Model 170 | 171 | 172 | {-| Internal state that keeps track of keys currently pressed and key combos 173 | -} 174 | type alias Model msg = 175 | { keys : Keyboard.Extra.State 176 | , combos : List (KeyCombo msg) 177 | , toMsg : Msg -> msg 178 | , activeCombo : Maybe (KeyCombo msg) 179 | } 180 | 181 | 182 | {-| Each key uses this type 183 | -} 184 | type alias Key = 185 | Keyboard.Extra.Key 186 | 187 | 188 | {-| Combo length types 189 | -} 190 | type KeyCombo msg 191 | = KeyCombo Key msg 192 | | KeyCombo2 Key Key msg 193 | | KeyCombo3 Key Key Key msg 194 | | KeyCombo4 Key Key Key Key msg 195 | 196 | 197 | 198 | -- Init 199 | 200 | 201 | {-| Initialize the module 202 | 203 | import Keyboard.Combo 204 | 205 | keyboardCombos : List (Keyboard.Combo.KeyCombo Msg) 206 | keyboardCombos = 207 | [ Keyboard.Combo.combo2 ( Keyboard.Combo.control, Keyboard.Combo.s ) Save 208 | ] 209 | 210 | init : Keyboard.Combo.Model Msg 211 | init = 212 | Keyboard.Combo.init keyboardCombos ComboMsg 213 | 214 | -} 215 | init : List (KeyCombo msg) -> (Msg -> msg) -> Model msg 216 | init combos toMsg = 217 | { keys = Keyboard.Extra.initialState 218 | , combos = combos 219 | , toMsg = toMsg 220 | , activeCombo = Nothing 221 | } 222 | 223 | 224 | {-| Subscribe to module key events 225 | -} 226 | subscriptions : Model parentMsg -> Sub parentMsg 227 | subscriptions model = 228 | Sub.map model.toMsg Keyboard.Extra.subscriptions 229 | 230 | 231 | 232 | -- Update 233 | 234 | 235 | {-| Internal update messages 236 | -} 237 | type alias Msg = 238 | Keyboard.Extra.Msg 239 | 240 | 241 | {-| Update the internal model. The command should be forwarded by the parent `update`. 242 | 243 | update : Msg -> Model -> ( Model, Cmd Msg ) 244 | update msg model = 245 | case msg of 246 | ComboMsg msg -> 247 | let 248 | ( updatedKeys, comboCmd ) = 249 | Keyboard.Combo.update msg model.combos 250 | in 251 | ( { model | combos = updatedKeys }, comboCmd ) 252 | 253 | -} 254 | update : Msg -> Model msg -> ( Model msg, Cmd msg ) 255 | update msg model = 256 | updateActiveCombo 257 | { model | keys = Keyboard.Extra.update msg model.keys } 258 | 259 | 260 | 261 | -- Combo helpers 262 | 263 | 264 | {-| Helper to define a key combo of one key 265 | -} 266 | combo1 : Key -> msg -> KeyCombo msg 267 | combo1 key msg = 268 | KeyCombo key msg 269 | 270 | 271 | {-| Helper to define a key combo of two keys 272 | -} 273 | combo2 : ( Key, Key ) -> msg -> KeyCombo msg 274 | combo2 ( key1, key2 ) msg = 275 | KeyCombo2 key1 key2 msg 276 | 277 | 278 | {-| Helper to define a key combo of three keys 279 | -} 280 | combo3 : ( Key, Key, Key ) -> msg -> KeyCombo msg 281 | combo3 ( key1, key2, key3 ) msg = 282 | KeyCombo3 key1 key2 key3 msg 283 | 284 | 285 | {-| Helper to define a key combo of four keys 286 | -} 287 | combo4 : ( Key, Key, Key, Key ) -> msg -> KeyCombo msg 288 | combo4 ( key1, key2, key3, key4 ) msg = 289 | KeyCombo4 key1 key2 key3 key4 msg 290 | 291 | 292 | 293 | -- Modifier Keys 294 | 295 | 296 | {-| Helper for super key 297 | -} 298 | super : Key 299 | super = 300 | Keyboard.Extra.Super 301 | 302 | 303 | {-| Helper for macOS command key 304 | -} 305 | command : Key 306 | command = 307 | super 308 | 309 | 310 | {-| Helper for shift key 311 | -} 312 | shift : Key 313 | shift = 314 | Keyboard.Extra.Shift 315 | 316 | 317 | {-| Helper for alt key 318 | -} 319 | alt : Key 320 | alt = 321 | Keyboard.Extra.Alt 322 | 323 | 324 | {-| Helper for macOS option key 325 | -} 326 | option : Key 327 | option = 328 | alt 329 | 330 | 331 | {-| Helper for control key 332 | -} 333 | control : Key 334 | control = 335 | Keyboard.Extra.Control 336 | 337 | 338 | {-| Helper for enter key 339 | -} 340 | enter : Key 341 | enter = 342 | Keyboard.Extra.Enter 343 | 344 | 345 | {-| Helper for tab key 346 | -} 347 | tab : Key 348 | tab = 349 | Keyboard.Extra.Tab 350 | 351 | 352 | {-| Helper for escape key 353 | -} 354 | escape : Key 355 | escape = 356 | Keyboard.Extra.Escape 357 | 358 | 359 | {-| Helper for space key 360 | -} 361 | space : Key 362 | space = 363 | Keyboard.Extra.Space 364 | 365 | 366 | {-| Helper for backspace key 367 | -} 368 | backspace : Key 369 | backspace = 370 | Keyboard.Extra.BackSpace 371 | 372 | 373 | {-| Helper for delete key 374 | -} 375 | delete : Key 376 | delete = 377 | Keyboard.Extra.Delete 378 | 379 | 380 | 381 | -- Letter helpers 382 | 383 | 384 | {-| -} 385 | a : Key 386 | a = 387 | Keyboard.Extra.CharA 388 | 389 | 390 | {-| -} 391 | b : Key 392 | b = 393 | Keyboard.Extra.CharB 394 | 395 | 396 | {-| -} 397 | c : Key 398 | c = 399 | Keyboard.Extra.CharC 400 | 401 | 402 | {-| -} 403 | d : Key 404 | d = 405 | Keyboard.Extra.CharD 406 | 407 | 408 | {-| -} 409 | e : Key 410 | e = 411 | Keyboard.Extra.CharE 412 | 413 | 414 | {-| -} 415 | f : Key 416 | f = 417 | Keyboard.Extra.CharF 418 | 419 | 420 | {-| -} 421 | g : Key 422 | g = 423 | Keyboard.Extra.CharG 424 | 425 | 426 | {-| -} 427 | h : Key 428 | h = 429 | Keyboard.Extra.CharH 430 | 431 | 432 | {-| -} 433 | i : Key 434 | i = 435 | Keyboard.Extra.CharI 436 | 437 | 438 | {-| -} 439 | j : Key 440 | j = 441 | Keyboard.Extra.CharJ 442 | 443 | 444 | {-| -} 445 | k : Key 446 | k = 447 | Keyboard.Extra.CharK 448 | 449 | 450 | {-| -} 451 | l : Key 452 | l = 453 | Keyboard.Extra.CharL 454 | 455 | 456 | {-| -} 457 | m : Key 458 | m = 459 | Keyboard.Extra.CharM 460 | 461 | 462 | {-| -} 463 | n : Key 464 | n = 465 | Keyboard.Extra.CharN 466 | 467 | 468 | {-| -} 469 | o : Key 470 | o = 471 | Keyboard.Extra.CharO 472 | 473 | 474 | {-| -} 475 | p : Key 476 | p = 477 | Keyboard.Extra.CharP 478 | 479 | 480 | {-| -} 481 | q : Key 482 | q = 483 | Keyboard.Extra.CharQ 484 | 485 | 486 | {-| -} 487 | r : Key 488 | r = 489 | Keyboard.Extra.CharR 490 | 491 | 492 | {-| -} 493 | s : Key 494 | s = 495 | Keyboard.Extra.CharS 496 | 497 | 498 | {-| -} 499 | t : Key 500 | t = 501 | Keyboard.Extra.CharT 502 | 503 | 504 | {-| -} 505 | u : Key 506 | u = 507 | Keyboard.Extra.CharU 508 | 509 | 510 | {-| -} 511 | v : Key 512 | v = 513 | Keyboard.Extra.CharV 514 | 515 | 516 | {-| -} 517 | w : Key 518 | w = 519 | Keyboard.Extra.CharW 520 | 521 | 522 | {-| -} 523 | x : Key 524 | x = 525 | Keyboard.Extra.CharX 526 | 527 | 528 | {-| -} 529 | y : Key 530 | y = 531 | Keyboard.Extra.CharY 532 | 533 | 534 | {-| -} 535 | z : Key 536 | z = 537 | Keyboard.Extra.CharZ 538 | 539 | 540 | 541 | -- Number helpers 542 | 543 | 544 | {-| -} 545 | zero : Key 546 | zero = 547 | Keyboard.Extra.Number0 548 | 549 | 550 | {-| -} 551 | one : Key 552 | one = 553 | Keyboard.Extra.Number1 554 | 555 | 556 | {-| -} 557 | two : Key 558 | two = 559 | Keyboard.Extra.Number2 560 | 561 | 562 | {-| -} 563 | three : Key 564 | three = 565 | Keyboard.Extra.Number3 566 | 567 | 568 | {-| -} 569 | four : Key 570 | four = 571 | Keyboard.Extra.Number4 572 | 573 | 574 | {-| -} 575 | five : Key 576 | five = 577 | Keyboard.Extra.Number5 578 | 579 | 580 | {-| -} 581 | six : Key 582 | six = 583 | Keyboard.Extra.Number6 584 | 585 | 586 | {-| -} 587 | seven : Key 588 | seven = 589 | Keyboard.Extra.Number7 590 | 591 | 592 | {-| -} 593 | eight : Key 594 | eight = 595 | Keyboard.Extra.Number8 596 | 597 | 598 | {-| -} 599 | nine : Key 600 | nine = 601 | Keyboard.Extra.Number9 602 | 603 | 604 | 605 | -- Arrow helpers 606 | 607 | 608 | {-| -} 609 | left : Key 610 | left = 611 | Keyboard.Extra.ArrowLeft 612 | 613 | 614 | {-| -} 615 | right : Key 616 | right = 617 | Keyboard.Extra.ArrowRight 618 | 619 | 620 | {-| -} 621 | up : Key 622 | up = 623 | Keyboard.Extra.ArrowUp 624 | 625 | 626 | {-| -} 627 | down : Key 628 | down = 629 | Keyboard.Extra.ArrowDown 630 | 631 | 632 | 633 | -- Punctuation Helpers 634 | 635 | 636 | {-| Helper for a `.` 637 | -} 638 | period : Key 639 | period = 640 | Keyboard.Extra.Period 641 | 642 | 643 | {-| Helper for a `,` 644 | -} 645 | comma : Key 646 | comma = 647 | Keyboard.Extra.Comma 648 | 649 | 650 | {-| Helper for a `;` 651 | -} 652 | semicolon : Key 653 | semicolon = 654 | Keyboard.Extra.Semicolon 655 | 656 | 657 | {-| Helper for a `'` 658 | -} 659 | singleQuote : Key 660 | singleQuote = 661 | Keyboard.Extra.Quote 662 | 663 | 664 | {-| Helper for a `-` 665 | -} 666 | minus : Key 667 | minus = 668 | Keyboard.Extra.Minus 669 | 670 | 671 | {-| Helper for a `=` 672 | -} 673 | equals : Key 674 | equals = 675 | Keyboard.Extra.Equals 676 | 677 | 678 | {-| Helper for a `[` 679 | -} 680 | openBracket : Key 681 | openBracket = 682 | Keyboard.Extra.OpenBracket 683 | 684 | 685 | {-| Helper for a `]` 686 | -} 687 | closeBracket : Key 688 | closeBracket = 689 | Keyboard.Extra.CloseBracket 690 | 691 | 692 | {-| Helper for a `\` 693 | -} 694 | backSlash : Key 695 | backSlash = 696 | Keyboard.Extra.BackSlash 697 | 698 | 699 | {-| Helper for a `/` 700 | -} 701 | forwardSlash : Key 702 | forwardSlash = 703 | Keyboard.Extra.Slash 704 | 705 | 706 | {-| Helper for a ``` 707 | -} 708 | backTick : Key 709 | backTick = 710 | Keyboard.Extra.BackQuote 711 | 712 | 713 | 714 | -- Utils 715 | 716 | 717 | updateActiveCombo : Model msg -> ( Model msg, Cmd msg ) 718 | updateActiveCombo model = 719 | let 720 | possibleCombo = 721 | matchesCombo model 722 | in 723 | { model | activeCombo = possibleCombo } 724 | ! getComboCmd possibleCombo model 725 | 726 | 727 | getComboCmd : Maybe (KeyCombo msg) -> Model msg -> List (Cmd msg) 728 | getComboCmd possibleCombo model = 729 | if possibleCombo == model.activeCombo then 730 | [] 731 | else 732 | possibleCombo 733 | |> Maybe.map (\combo -> [ performComboTask combo ]) 734 | |> Maybe.withDefault [] 735 | 736 | 737 | performComboTask : KeyCombo msg -> Cmd msg 738 | performComboTask combo = 739 | getComboMsg combo 740 | |> Task.succeed 741 | |> Task.perform (\x -> x) 742 | 743 | 744 | arePressed : Keyboard.Extra.State -> List Key -> Bool 745 | arePressed keyTracker keysPressed = 746 | List.all 747 | (\key -> Keyboard.Extra.isPressed key keyTracker) 748 | keysPressed 749 | 750 | 751 | matchesCombo : Model msg -> Maybe (KeyCombo msg) 752 | matchesCombo model = 753 | find (\combo -> arePressed model.keys <| keyList combo) model.combos 754 | 755 | 756 | keyList : KeyCombo msg -> List Key 757 | keyList combo = 758 | case combo of 759 | KeyCombo key msg -> 760 | [ key ] 761 | 762 | KeyCombo2 key1 key2 msg -> 763 | [ key1, key2 ] 764 | 765 | KeyCombo3 key1 key2 key3 msg -> 766 | [ key1, key2, key3 ] 767 | 768 | KeyCombo4 key1 key2 key3 key4 msg -> 769 | [ key1, key2, key3, key4 ] 770 | 771 | 772 | getComboMsg : KeyCombo msg -> msg 773 | getComboMsg combo = 774 | case combo of 775 | KeyCombo _ msg -> 776 | msg 777 | 778 | KeyCombo2 _ _ msg -> 779 | msg 780 | 781 | KeyCombo3 _ _ _ msg -> 782 | msg 783 | 784 | KeyCombo4 _ _ _ _ msg -> 785 | msg 786 | 787 | 788 | find : (a -> Bool) -> List a -> Maybe a 789 | find predicate list = 790 | case list of 791 | [] -> 792 | Nothing 793 | 794 | first :: rest -> 795 | if predicate first then 796 | Just first 797 | else 798 | find predicate rest 799 | --------------------------------------------------------------------------------