46 Commits

Author SHA1 Message Date
László Monda
123cab5724 Bump Agent version to 1.2.9 2018-09-13 20:42:07 +02:00
Róbert Kiss
c16365a0e5 fix: Alt+Tab triggers "Remap on all layers" (#773) 2018-09-11 22:29:30 +02:00
Róbert Kiss
a21d278c0c fix: modifier button layout (#772) 2018-09-11 21:56:30 +02:00
Róbert Kiss
0466916be1 fix: key modifier reordering (#771) 2018-09-11 00:29:22 +02:00
László Monda
9a845d8f6a Fix eeprom.js 2018-09-10 19:29:58 +02:00
Róbert Kiss
9ae1673499 feat: secondary role visualisation (#767)
* feat: secondary role visualisation

* fix: recalculate the text position of the secondary role if changes

* fix: recalculate the text position of SvgKeystrokeKeyComponent

* fix: recalculate the text position when changes anything

* fix: two line text key position calculation

* fix: fix space positioning

* fix: visualize second character of complex key

* style: remove extra line
2018-09-08 12:09:16 +02:00
László Monda
2d5a5e7aef Exchange Alt and Super modifiers in the key action popover. 2018-09-03 03:58:03 +02:00
Róbert Kiss
3e4d439852 feat: display OS-specific modifiers (#764)
* chore: git ignore "out-tsc/" folder in uhk-web package

* feat: add OperationSystem calculation to the app reducer

* feat: create os specific key modifier

* feat: Os specific texts

* revert: KeyModifierValues and getKeyModifiers selector

* refactor: remove unnecessary return

* refactor: rename OperationSystem => OperatingSystem
2018-09-03 00:21:55 +02:00
László Monda
aba0b09109 Bump Agent to version 1.2.8 and update changelog. 2018-08-26 23:42:31 +02:00
László Monda
af608ee17d Downgrade to firmware 8.2.5 2018-08-26 23:35:54 +02:00
Róbert Kiss
df817e86d6 fix: use OnPush change detection on the keymap edit and add pages (#760)
* fix: use OnPush change detection on the keymap edit and add pages

* fix: font-size
2018-08-26 22:08:29 +02:00
Róbert Kiss
a7d3b62512 chore: upgrade many dependencies and restructure tsconfigs (#757)
* chore: upgrade many dependencies and restructure tsconfigs

Summary:
- upgrade many dependencies
- remove dev dependencies from test-serializer and uhk-common
- created root tsconfig.json and test-serializer and uhk-common
  tsconfigs extends it
- fixed e2e test

* chore: upgrade more dependencies
2018-08-22 00:34:16 +02:00
Róbert Kiss
b41f14192a chore: upgrade stylelint => 9.5.0 (#756)
it is fix "Unknown CSS word" error in IntelliJ
2018-08-21 20:43:18 +02:00
Róbert Kiss
475ec71983 fix: uncheck 'Remap on all keyboard' and 'Remap on all layer' checkbox by default (#754)
* fix: uncheck 'Remap on all keyboard' and 'Remap on all layer' checkbox by default

* fix: popover checkbox checked state
2018-08-21 20:41:54 +02:00
László Monda
b5cff2fa93 Fix the padding of the secondary role tooltip. 2018-08-14 04:10:30 +02:00
László Monda
80e8c014ec Bind left and right Shift on the Mouse layer of all keymaps in the default configuration. 2018-08-08 17:00:32 +02:00
László Monda
67d42f666c Simplify the output of get-debug-info.js 2018-08-04 02:29:39 +02:00
László Monda
fa32f95438 Add note to the LED brightness page saying that current UHK versions are not backlit. Also include spacing-bootstrap-3 2018-07-30 00:05:04 +02:00
László Monda
06878dd56a Remove the redundant scrollbar from the LED brightness page. 2018-07-29 00:28:34 +02:00
Róbert Kiss
374f6a3e6e chore: downgrade electron builder => 20.14.7 (#741)
* chore: downgrade electron builder => 20.14.7

* chore: downgrade electron builder => 20.8.1
2018-07-26 23:30:20 +02:00
László Monda
0b9c804a3d Bump Agent version to 1.2.7 and update changelog. 2018-07-26 22:20:28 +02:00
László Monda
365a459d61 Merge branch 'master' of github.com:UltimateHackingKeyboard/agent 2018-07-26 22:16:33 +02:00
László Monda
cec891a2c0 Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap. 2018-07-26 22:15:21 +02:00
Róbert Kiss
8eb8aa3032 chore: upgrade electron builder => 20.26.0 (#740) 2018-07-26 22:06:32 +02:00
László Monda
38184e7968 Bump Agent version to 1.2.6 and firmware version to 8.4.0. Update changelog. 2018-07-26 06:04:47 +02:00
Róbert Kiss
f6092ea195 fix: popover components use OnPush change detection (#727)
* fix: popover components use OnPush change detection

* fix: select2 selection bug

* chore: upgrade @ert78gb/ngx-select-ex => 3.7.0
2018-07-26 05:37:58 +02:00
László Monda
ac7d66e338 Add keyboard shortcut for enabling the USB stack test mode of the firmware. Resolves #735. 2018-07-26 05:34:48 +02:00
László Monda
b82a1da92a Remove usbVariables in favor of variableNameToId. 2018-07-25 11:44:57 +02:00
László Monda
b8859f7b64 Make {get,set}-variable.js expect variable name and use async/await. 2018-07-25 02:08:36 +02:00
László Monda
a04fa67446 Comment out the Settings menu until auto update is implemented. 2018-07-24 16:40:34 +02:00
Eric Tang
ac89aff018 Add scripts for getting/setting firmware variables (#734) 2018-07-22 16:07:21 +02:00
Róbert Kiss
e7cf8dc966 fix: no scroll when macro tab selected on popover (#731) 2018-07-17 22:08:45 +02:00
Róbert Kiss
d0102f5bdb feat: add help page (#728)
* feat: add help page

* feat: add help page content
2018-07-16 23:05:41 +02:00
László Monda
eb0daadf98 Overwrite the blhost binary with a statically compiled version that doesn't use special instructions.
See https://github.com/UltimateHackingKeyboard/agent/issues/681#issuecomment-403057795
2018-07-14 14:43:11 +02:00
László Monda
49d6ca173d Remove the blhost-old x86-64 Linux blhost binary. 2018-07-14 14:41:53 +02:00
László Monda
a3eb6a6b7e Fix typo. 2018-07-11 14:48:28 +02:00
László Monda
144ed57b20 Fix epic typo. 2018-07-08 14:44:41 +02:00
Róbert Kiss
6086ddabf0 build: build only AppImage for Linux (#719)
upgrade:
 - electron-builder => 20.15.0
 - electron-log => 2.2.16
 - electron-rebuild => 1.8.1
2018-07-08 14:34:38 +02:00
Róbert Kiss
84f378a276 feat: add save to keyboard and remap shortcut keys (#712)
* feat: add save to keyboard and remap shortcut keys

* feat: Alt and Shift keys set the remapOnAllKeymap and remapOnAllLayer

* fix: control + enter trigger remap keymap
2018-07-08 14:31:48 +02:00
Róbert Kiss
648e8d5f2c feat: keep current selected layer when changing keymap (#714) 2018-07-05 23:58:44 +02:00
Róbert Kiss
15df8d7129 WIP feat: replace ng2-select2 => ngx-select-ex (#706)
* feat: replace ng2-select2 => ngx-select-ex

* feat: style the ngrx-select

* feat: replace secondary role select2

* feat: replace Select2OptionData => SelectOptionData

* feat: replace select2 => ngx-select in macro-tab component

* feat: replace select2 => ngx-select in keymap-tab component

* feat: fix styles

* chore: remove select2 from dependencies

* fix: macro editor overflow

* fix: set the same font size for the toggle button

* fix: overflow

* chore: use @ert78gb/ngx-select-ex version of ngx-select-ex
2018-07-02 23:44:39 +02:00
Sylvain Benner
cfc0af9655 Fix Sleep key for macOS users in Mac keymaps and Fn layers (#707)
Reference: https://support.apple.com/en-us/HT201236
2018-06-29 12:19:59 +02:00
László Monda
f02e3181a6 Improve the phrasing of the firmware update error message. 2018-06-28 18:49:01 +02:00
Róbert Kiss
3d59bcf97e feat: Tweak unsupported Windows firmware update notification (#705)
* feat: Tweak unsupported Windows firmware update notification

* feat: Display firmware update status

* feat: throw error when left half not connected under firmware upgrade
2018-06-28 18:41:16 +02:00
László Monda
5e4fc983fb Rename the "Remap Key" button to "Remay key". 2018-06-27 21:28:16 +02:00
László Monda
32d9635b34 Tone down the color of the separator line. 2018-06-26 06:05:03 +02:00
122 changed files with 13965 additions and 6237 deletions

View File

@@ -6,12 +6,56 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md). Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
## [1.2.9] - 2018-09-13
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Display OS-specific modifiers.
- Display secondary roles.
- Don't trigger "Remap on all layers" after leaving Agent with Alt+Tab.
## [1.2.8] - 2018-08-26
Firmware: 8.**2.5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Uncheck the "Remap on all keymaps" and "Remap on all layers" checkboxes of the key action popover by default.
- Bind left and right Shift on the Mouse layer of all keymaps in the default configuration.
- Make ng2-select2 widgets faster.
- Add note to the LED brightness page saying that current UHK versions are not backlit.
- Fix the padding of the secondary role tooltip.
- Remove the redundant scrollbar from the LED brightness page.
## [1.2.7] - 2018-07-26
Firmware: 8.4.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
- Fix Agent startup exception on Linux by upgrading Electron builder.
- Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap.
## [1.2.6] - 2018-07-26
Firmware: 8.**4.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.**4.0** | User Config: 4.0.1 | Hardware Config: 1.0.0
- Replace the Linux blhost binary with a statically compiled version that doesn't use special instructions and shouldn't segfault.
- Keep the current layer when changing keymaps.
- Fix the sleep key of Mac keymaps.
- Add help page.
- Add "save to keyboard" and "remap key" shortcuts.
- Build only AppImages for Linux.
- Replace ng2-select2 widgets with ngx-select-ex that always shows up in the correct position.
- Improve the phrasing of the firmware update error message.
- Tweak unsupported Windows firmware update notification.
- Hide the Settings menu until auto update is implemented.
- Don't scroll when the macro tab of the key action popover gets selected.
- Add keyboard shortcut for enabling the USB stack test mode of the firmware. `DEVICEPROTOCOL:MINOR`
- Tone down the color of the separator line.
## [1.2.5] - 2018-06-26 ## [1.2.5] - 2018-06-26
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0 Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
- When remapping a switch keymap action on all keymaps, don't set it on its own keymap. - When remapping a switch keymap action on all keymaps, don't set it on its own keymap.
- Make the key action popver always contain the action of the current key, even after cancelled. - Make the key action popover always contain the action of the current key, even after cancelled.
- Include the firmware version to be updated to the firmware update log. - Include the firmware version to be updated to the firmware update log.
- Update the Agent icon of the side menu and the about page. - Update the Agent icon of the side menu and the about page.
- When remapping a key, only flash the affected key instead of all keys. - When remapping a key, only flash the affected key instead of all keys.

9753
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,9 +3,9 @@
"private": true, "private": true,
"author": "Ultimate Gadget Laboratories", "author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js", "main": "electron/dist/electron-main.js",
"version": "1.2.5", "version": "1.2.9",
"firmwareVersion": "8.2.5", "firmwareVersion": "8.2.5",
"deviceProtocolVersion": "4.3.1", "deviceProtocolVersion": "4.4.0",
"userConfigVersion": "4.0.1", "userConfigVersion": "4.0.1",
"hardwareConfigVersion": "1.0.0", "hardwareConfigVersion": "1.0.0",
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.", "description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
@@ -22,7 +22,7 @@
"@types/electron-devtools-installer": "2.0.2", "@types/electron-devtools-installer": "2.0.2",
"@types/electron-settings": "3.0.0", "@types/electron-settings": "3.0.0",
"@types/fs-extra": "5.0.1", "@types/fs-extra": "5.0.1",
"@types/jasmine": "2.6.0", "@types/jasmine": "2.8.8",
"@types/jquery": "3.3.1", "@types/jquery": "3.3.1",
"@types/jsonfile": "4.0.1", "@types/jsonfile": "4.0.1",
"@types/lodash-es": "4.17.0", "@types/lodash-es": "4.17.0",
@@ -38,37 +38,41 @@
"core-js": "2.4.1", "core-js": "2.4.1",
"cross-env": "5.0.5", "cross-env": "5.0.5",
"decompress": "4.2.0", "decompress": "4.2.0",
"decompress-tarbz2": "^4.1.1", "decompress-tarbz2": "4.1.1",
"devtron": "1.4.0", "devtron": "1.4.0",
"electron": "1.8.4", "electron": "1.8.7",
"electron-builder": "20.8.1", "electron-builder": "20.8.1",
"electron-debug": "1.5.0", "electron-debug": "1.5.0",
"electron-devtools-installer": "2.2.3", "electron-devtools-installer": "2.2.3",
"electron-log": "2.2.14", "electron-log": "2.2.16",
"electron-rebuild": "1.7.3", "electron-rebuild": "1.8.2",
"electron-settings": "3.1.4", "electron-settings": "3.1.4",
"electron-updater": "2.21.4", "electron-updater": "2.21.4",
"exports-loader": "0.6.3", "exports-loader": "0.6.3",
"file-loader": "0.10.0", "file-loader": "0.10.0",
"fs-extra": "5.0.0", "fs-extra": "5.0.0",
"gh-pages": "1.1.0", "gh-pages": "1.1.0",
"jasmine": "2.8.0",
"jasmine-core": "2.8.0",
"jasmine-node": "2.0.1",
"jasmine-ts": "0.2.1",
"jsonfile": "4.0.0", "jsonfile": "4.0.0",
"lerna": "2.9.0", "lerna": "3.1.4",
"lodash-es": "4.17.4", "lodash-es": "4.17.4",
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"node-hid": "0.5.7", "node-hid": "0.5.7",
"npm-run-all": "4.0.2", "npm-run-all": "4.0.2",
"pre-commit": "1.2.2", "pre-commit": "1.2.2",
"request": "2.83.0", "request": "2.88.0",
"rimraf": "2.6.1", "rimraf": "2.6.1",
"standard-version": "4.2.0", "standard-version": "4.2.0",
"stylelint": "7.13.0", "stylelint": "9.5.0",
"svg-sprite": "1.3.7", "svg-sprite": "1.4.0",
"ts-loader": "2.3.1", "ts-loader": "2.3.1",
"ts-node": "3.0.4", "ts-node": "7.0.1",
"tslint": "5.9.1", "tslint": "5.9.1",
"typescript": "2.6.2", "typescript": "2.6.2",
"webpack": "3.10.0" "webpack": "3.12.0"
}, },
"pre-commit": [ "pre-commit": [
"precommit-msg" "precommit-msg"
@@ -88,6 +92,7 @@
"lint:ts:test-serializer": "tslint --project ./packages/test-serializer/tsconfig.json", "lint:ts:test-serializer": "tslint --project ./packages/test-serializer/tsconfig.json",
"lint:ts:uhk-usb": "tslint --project ./packages/uhk-usb/tsconfig.json", "lint:ts:uhk-usb": "tslint --project ./packages/uhk-usb/tsconfig.json",
"lint:style": "stylelint \"packages/uhk-agent/src/**/*.scss\" \"packages/uhk-web/src/**/*.scss\" --syntax scss", "lint:style": "stylelint \"packages/uhk-agent/src/**/*.scss\" \"packages/uhk-web/src/**/*.scss\" --syntax scss",
"e2e": "lerna run e2e --scope uhk-web",
"prebuild": "check-node-version --package", "prebuild": "check-node-version --package",
"build": "run-s build:common build:usb build:web build:electron", "build": "run-s build:common build:usb build:web build:electron",
"build:web": "lerna exec --scope uhk-web npm run build", "build:web": "lerna exec --scope uhk-web npm run build",

View File

@@ -201,11 +201,11 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
}, },
"gaze": { "gaze": {
"version": "0.5.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
"integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
"requires": { "requires": {
"globule": "0.1.0" "globule": "1.2.1"
} }
}, },
"get-caller-file": { "get-caller-file": {
@@ -219,41 +219,37 @@
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}, },
"glob": { "glob": {
"version": "3.1.21", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": { "requires": {
"graceful-fs": "1.2.3", "fs.realpath": "1.0.0",
"inherits": "1.0.2", "inflight": "1.0.6",
"minimatch": "0.2.14" "inherits": "2.0.3",
}, "minimatch": "3.0.4",
"dependencies": { "once": "1.4.0",
"inherits": { "path-is-absolute": "1.0.1"
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
"integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js="
}
} }
}, },
"globule": { "globule": {
"version": "0.1.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
"integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
"requires": { "requires": {
"glob": "3.1.21", "glob": "7.1.2",
"lodash": "1.0.2", "lodash": "4.17.10",
"minimatch": "0.2.14" "minimatch": "3.0.4"
} }
}, },
"graceful-fs": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
"integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q="
},
"growl": { "growl": {
"version": "1.7.0", "version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
},
"grunt-exec": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz",
"integrity": "sha1-QAUf+k6wyWV+BTuV6I1ENSocLCU="
}, },
"has-flag": { "has-flag": {
"version": "2.0.0", "version": "2.0.0",
@@ -362,31 +358,34 @@
"integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=" "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4="
}, },
"jasmine-growl-reporter": { "jasmine-growl-reporter": {
"version": "0.2.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-0.2.1.tgz", "resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-1.0.1.tgz",
"integrity": "sha1-1fCje5L2qD/VxkgrgJSVyQqLVf4=", "integrity": "sha512-dh7VjP3l0OLxL9+sw5vK6RrdH4gdHCNkTnUd9orViHDPr7Fe8LsXY+IObWauS2hX5khMFtjKRZCfTcDHKAjm/A==",
"requires": { "requires": {
"growl": "1.7.0" "growl": "1.10.5"
} }
}, },
"jasmine-node": { "jasmine-node": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-2.0.0.tgz", "resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-2.0.1.tgz",
"integrity": "sha1-gXUacjJfVJdJCxQYGlUIfxsDcf8=", "integrity": "sha512-1S5Z4Mof5yxwqLIApzyo2pV5WN2kRpTSgICvEo3+rJmKve9P94kolzC9eS0u5cyiT+gxBY2mwOQdxLbkhwKzoA==",
"requires": { "requires": {
"coffee-script": "1.7.1", "coffee-script": "1.7.1",
"gaze": "0.5.2", "gaze": "1.1.3",
"jasmine-growl-reporter": "0.2.1", "grunt-exec": "0.4.7",
"minimist": "0.0.8", "jasmine-growl-reporter": "1.0.1",
"jasmine-reporters": "git://github.com/larrymyers/jasmine-reporters.git#2c7242dc11c15c2f156169bc704798568b8cb50d",
"minimist": "0.0.10",
"mkdirp": "0.3.5", "mkdirp": "0.3.5",
"underscore": "1.6.0", "underscore": "1.6.0",
"walkdir": "0.0.12" "walkdir": "0.0.12",
"xml2js": "0.4.19"
}, },
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
}, },
"mkdirp": { "mkdirp": {
"version": "0.3.5", "version": "0.3.5",
@@ -395,6 +394,19 @@
} }
} }
}, },
"jasmine-reporters": {
"version": "git://github.com/larrymyers/jasmine-reporters.git#2c7242dc11c15c2f156169bc704798568b8cb50d",
"requires": {
"mkdirp": "0.3.5"
},
"dependencies": {
"mkdirp": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
"integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc="
}
}
},
"jasmine-ts": { "jasmine-ts": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/jasmine-ts/-/jasmine-ts-0.2.1.tgz", "resolved": "https://registry.npmjs.org/jasmine-ts/-/jasmine-ts-0.2.1.tgz",
@@ -442,14 +454,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "1.0.2", "version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=" "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lru-cache": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
"integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
}, },
"make-error": { "make-error": {
"version": "1.3.0", "version": "1.3.0",
@@ -470,12 +477,11 @@
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=" "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg="
}, },
"minimatch": { "minimatch": {
"version": "0.2.14", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": { "requires": {
"lru-cache": "2.7.3", "brace-expansion": "1.1.8"
"sigmund": "1.0.1"
} }
}, },
"minimist": { "minimist": {
@@ -633,6 +639,11 @@
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
}, },
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": { "semver": {
"version": "5.4.1", "version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
@@ -656,11 +667,6 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
}, },
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
},
"signal-exit": { "signal-exit": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@@ -854,6 +860,20 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}, },
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"requires": {
"sax": "1.2.4",
"xmlbuilder": "9.0.7"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
},
"y18n": { "y18n": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",

View File

@@ -14,16 +14,9 @@
"npm": ">=5.1.0 <6.0.0" "npm": ">=5.1.0 <6.0.0"
}, },
"dependencies": { "dependencies": {
"uhk-common": "1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "2.6.0",
"@types/node": "8.0.30",
"jasmine": "2.8.0",
"jasmine-core": "2.8.0",
"jasmine-node": "2.0.0",
"jasmine-ts": "0.2.1",
"ts-node": "3.3.0",
"uhk-common": "1.0.0"
}, },
"scripts": { "scripts": {
"test": "jasmine-ts --config=jasmine.json" "test": "jasmine-ts --config=jasmine.json"

View File

@@ -1,11 +1,9 @@
{ {
"extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "es6",
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"experimentalDecorators": true, "experimentalDecorators": true
"typeRoots": [
"./node_modules/@types"
]
} }
} }

View File

@@ -1,11 +1,14 @@
{ {
"requires": true, "name": "uhk-agent",
"version": "0.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true,
"dependencies": { "dependencies": {
"@types/decompress": { "@types/decompress": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/decompress/-/decompress-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/decompress/-/decompress-4.2.0.tgz",
"integrity": "sha512-f7bLkRy09Z+1kid9ylcWMRlJrEWe4ceqtoGvlFQ/hnxL7WIr+AEy5Bv8SCIRDm8FLqTv4VF+hTUCpSd7rHgtkQ==", "integrity": "sha512-f7bLkRy09Z+1kid9ylcWMRlJrEWe4ceqtoGvlFQ/hnxL7WIr+AEy5Bv8SCIRDm8FLqTv4VF+hTUCpSd7rHgtkQ==",
"dev": true,
"requires": { "requires": {
"@types/node": "8.0.33" "@types/node": "8.0.33"
} }
@@ -13,12 +16,14 @@
"@types/node": { "@types/node": {
"version": "8.0.33", "version": "8.0.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.33.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.33.tgz",
"integrity": "sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==" "integrity": "sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==",
"dev": true
}, },
"@types/tmp": { "@types/tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz", "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=" "integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=",
"dev": true
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",

View File

@@ -87,6 +87,15 @@ export class DeviceService {
}); });
}); });
ipcMain.on(IpcEvents.device.enableUsbStackTest, (...args: any[]) => {
this.queueManager.add({
method: this.enableUsbStackTest,
bind: this,
params: args,
asynchronous: true
});
});
logService.debug('[DeviceService] init success'); logService.debug('[DeviceService] init success');
} }
@@ -224,6 +233,10 @@ export class DeviceService {
event.sender.send(IpcEvents.device.updateFirmwareReply, response); event.sender.send(IpcEvents.device.updateFirmwareReply, response);
} }
public async enableUsbStackTest(event: Electron.Event) {
await this.device.enableUsbStackTest();
}
/** /**
* HID API not support device attached and detached event. * HID API not support device attached and detached event.
* This method check the keyboard is attached to the computer or not. * This method check the keyboard is attached to the computer or not.

View File

@@ -19,16 +19,6 @@
"coverage": "nyc jasmine-ts --config=jasmine.json" "coverage": "nyc jasmine-ts --config=jasmine.json"
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"devDependencies": {
"@types/jasmine": "2.6.0",
"@types/node": "8.0.30",
"jasmine": "2.8.0",
"jasmine-core": "2.8.0",
"jasmine-node": "2.0.0",
"jasmine-ts": "0.2.1",
"nyc": "11.2.1",
"ts-node": "3.3.0"
},
"nyc": { "nyc": {
"extension": [ "extension": [
".ts" ".ts"

View File

@@ -30,6 +30,7 @@ export class Device {
public static readonly updateFirmwareReply = 'device-update-firmware-reply'; public static readonly updateFirmwareReply = 'device-update-firmware-reply';
public static readonly startConnectionPoller = 'device-start-connection-poller'; public static readonly startConnectionPoller = 'device-start-connection-poller';
public static readonly recoveryDevice = 'device-recovery'; public static readonly recoveryDevice = 'device-recovery';
public static readonly enableUsbStackTest = 'enable-usb-stack-test';
} }
export class IpcEvents { export class IpcEvents {

View File

@@ -1,4 +1,5 @@
{ {
"extends": "../../tsconfig.json",
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"sourceMap": true, "sourceMap": true,
@@ -9,9 +10,6 @@
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"target": "es5", "target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [ "lib": [
"es2015.iterable", "es2015.iterable",
"dom", "dom",

View File

@@ -1,11 +1,14 @@
{ {
"requires": true, "name": "uhk-usb",
"version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true,
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "8.0.28", "version": "8.0.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==" "integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
"dev": true
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",

View File

@@ -23,7 +23,12 @@ export enum UsbCommand {
GetDebugBuffer = 0x0b, GetDebugBuffer = 0x0b,
GetAdcValue = 0x0c, GetAdcValue = 0x0c,
SetLedPwmBrightness = 0x0d, SetLedPwmBrightness = 0x0d,
GetModuleProperty = 0x0e GetModuleProperty = 0x0e,
GetSlaveI2cErrors = 0x0f,
SetI2cBaudRate = 0x10,
SwitchKeymap = 0x11,
GetVariable = 0x12,
SetVariable = 0x13
} }
export enum EepromOperation { export enum EepromOperation {
@@ -86,3 +91,10 @@ export enum KbootCommands {
export enum ModulePropertyId { export enum ModulePropertyId {
protocolVersions = 0 protocolVersions = 0
} }
export enum UsbVariables {
testSwitches = 0,
testUsbStack = 1,
debounceTimePress = 2,
debounceTimeRelease = 3
}

View File

@@ -10,7 +10,8 @@ import {
KbootCommands, KbootCommands,
ModuleSlotToI2cAddress, ModuleSlotToI2cAddress,
ModuleSlotToId, ModuleSlotToId,
UsbCommand UsbCommand,
UsbVariables
} from './constants'; } from './constants';
import { bufferToString, getTransferData, isUhkDevice, retry, snooze } from './util'; import { bufferToString, getTransferData, isUhkDevice, retry, snooze } from './util';
@@ -133,6 +134,11 @@ export class UhkHidDevice {
await this.waitUntilKeyboardBusy(); await this.waitUntilKeyboardBusy();
} }
public async enableUsbStackTest(): Promise<void> {
await this.write(new Buffer([UsbCommand.SetVariable, UsbVariables.testUsbStack, 1]));
await this.waitUntilKeyboardBusy();
}
/** /**
* Close the communication chanel with UHK Device * Close the communication chanel with UHK Device
*/ */

View File

@@ -59,8 +59,9 @@ export class UhkOperations {
const leftModuleBricked = await this.waitForKbootIdle(); const leftModuleBricked = await this.waitForKbootIdle();
if (!leftModuleBricked) { if (!leftModuleBricked) {
this.logService.error('[UhkOperations] Couldn\'t connect to the left keyboard half.'); const msg = '[UhkOperations] Couldn\'t connect to the left keyboard half.';
return; this.logService.error(msg);
throw new Error(msg);
} }
await this.device.reenumerate(EnumerationModes.Buspal); await this.device.reenumerate(EnumerationModes.Buspal);

View File

@@ -26,7 +26,6 @@
], ],
"scripts": [ "scripts": [
"../node_modules/bootstrap/dist/js/bootstrap.js", "../node_modules/bootstrap/dist/js/bootstrap.js",
"../node_modules/select2/dist/js/select2.full.js",
"../node_modules/nouislider/distribute/nouislider.js" "../node_modules/nouislider/distribute/nouislider.js"
], ],
"environmentSource": "environments/environment.ts", "environmentSource": "environments/environment.ts",

1
packages/uhk-web/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
out-tsc/

View File

@@ -7,8 +7,8 @@ describe('web App', () => {
page = new WebPage(); page = new WebPage();
}); });
it('should display welcome message', () => { it('should display default device name', () => {
page.navigateTo(); page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!'); expect(page.getDeviceName()).toEqual('My UHK');
}); });
}); });

View File

@@ -5,7 +5,8 @@ export class WebPage {
return browser.get('/'); return browser.get('/');
} }
getParagraphText() { getDeviceName() {
return element(by.css('app-root h1')).getText(); return element(by.css('body > main-app > side-menu > ul > li:nth-child(1) > div > auto-grow-input > input'))
.getAttribute('value');
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -36,8 +36,8 @@
"@types/electron-devtools-installer": "2.0.2", "@types/electron-devtools-installer": "2.0.2",
"@types/electron-settings": "3.0.0", "@types/electron-settings": "3.0.0",
"@types/file-saver": "0.0.1", "@types/file-saver": "0.0.1",
"@types/jasmine": "2.5.53", "@types/jasmine": "2.8.8",
"@types/jasminewd2": "2.0.2", "@types/jasminewd2": "2.0.3",
"@types/jquery": "3.2.9", "@types/jquery": "3.2.9",
"@types/usb": "1.1.3", "@types/usb": "1.1.3",
"angular-confirmation-popover": "3.2.0", "angular-confirmation-popover": "3.2.0",
@@ -50,26 +50,25 @@
"dragula": "3.7.2", "dragula": "3.7.2",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"html-webpack-plugin": "^2.29.0", "html-webpack-plugin": "^2.29.0",
"jasmine-core": "2.6.2", "jasmine-core": "3.2.1",
"jasmine-spec-reporter": "4.1.0", "jasmine-spec-reporter": "4.2.1",
"jquery": "3.2.1", "jquery": "3.2.1",
"jsonfile": "3.0.1", "jsonfile": "3.0.1",
"karma": "1.7.0", "karma": "3.0.0",
"karma-chrome-launcher": "2.1.1", "karma-chrome-launcher": "2.2.0",
"karma-coverage-istanbul-reporter": "1.2.1", "karma-coverage-istanbul-reporter": "2.0.1",
"karma-jasmine": "1.1.0", "karma-jasmine": "1.1.2",
"karma-jasmine-html-reporter": "0.2.2", "karma-jasmine-html-reporter": "1.3.1",
"ng2-dragula": "1.5.0", "ng2-dragula": "1.5.0",
"ng2-nouislider": "^1.7.7", "ng2-nouislider": "^1.7.7",
"ng2-select2": "1.0.0-beta.10",
"ngx-clipboard": "10.0.0", "ngx-clipboard": "10.0.0",
"@ert78gb/ngx-select-ex": "3.7.2",
"ngrx-store-freeze": "0.1.9", "ngrx-store-freeze": "0.1.9",
"nouislider": "^11.1.0", "nouislider": "^11.1.0",
"postcss-url": "^7.1.2", "postcss-url": "^7.1.2",
"protractor": "5.1.2", "protractor": "5.4.0",
"reselect": "3.0.1", "reselect": "3.0.1",
"rxjs": "5.5.8", "rxjs": "5.5.8",
"select2": "4.0.3",
"typescript": "2.6.2", "typescript": "2.6.2",
"uhk-common": "1.0.0", "uhk-common": "1.0.0",
"xml-loader": "1.2.1", "xml-loader": "1.2.1",
@@ -79,6 +78,7 @@
}, },
"dependencies": { "dependencies": {
"classlist.js": "1.1.20150312", "classlist.js": "1.1.20150312",
"file-saver": "1.3.3" "file-saver": "1.3.3",
"spacing-bootstrap-3": "^1.0.0"
} }
} }

View File

@@ -12,7 +12,7 @@ exports.config = {
'browserName': 'chrome' 'browserName': 'chrome'
}, },
directConnect: true, directConnect: true,
baseUrl: 'http://localhost:4200/', baseUrl: 'http://localhost:8080/',
framework: 'jasmine', framework: 'jasmine',
jasmineNodeOpts: { jasmineNodeOpts: {
showColors: true, showColors: true,

View File

@@ -9,7 +9,7 @@
</div> </div>
<notifier-container></notifier-container> <notifier-container></notifier-container>
<progress-button class="save-to-keyboard-button" <progress-button class="save-to-keyboard-button"
*ngIf="(saveToKeyboardState$ | async).showButton" *ngIf="saveToKeyboardState.showButton"
[@showSaveToKeyboardButton] [@showSaveToKeyboardButton]
[state]="saveToKeyboardState$ | async" [state]="saveToKeyboardState"
(clicked)="clickedOnProgressButton($event)"></progress-button> (clicked)="clickedOnProgressButton($event)"></progress-button>

View File

@@ -1,11 +1,13 @@
import { Component, ViewEncapsulation } from '@angular/core'; import { Component, HostListener, ViewEncapsulation, OnDestroy } from '@angular/core';
import { animate, style, transition, trigger } from '@angular/animations'; import { animate, style, transition, trigger } from '@angular/animations';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { Action, Store } from '@ngrx/store'; import { Action, Store } from '@ngrx/store';
import 'rxjs/add/operator/last'; import 'rxjs/add/operator/last';
import { DoNotUpdateAppAction, UpdateAppAction } from './store/actions/app-update.action'; import { DoNotUpdateAppAction, UpdateAppAction } from './store/actions/app-update.action';
import { EnableUsbStackTestAction } from './store/actions/device';
import { import {
AppState, AppState,
getShowAppUpdateAvailable, getShowAppUpdateAvailable,
@@ -34,17 +36,44 @@ import { ProgressButtonState } from './store/reducers/progress-button-state';
]) ])
] ]
}) })
export class MainAppComponent { export class MainAppComponent implements OnDestroy {
showUpdateAvailable$: Observable<boolean>; showUpdateAvailable$: Observable<boolean>;
deviceConfigurationLoaded$: Observable<boolean>; deviceConfigurationLoaded$: Observable<boolean>;
runningInElectron$: Observable<boolean>; runningInElectron$: Observable<boolean>;
saveToKeyboardState$: Observable<ProgressButtonState>; saveToKeyboardState: ProgressButtonState;
private saveToKeyboardStateSubscription: Subscription;
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable); this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable);
this.deviceConfigurationLoaded$ = store.select(deviceConfigurationLoaded); this.deviceConfigurationLoaded$ = store.select(deviceConfigurationLoaded);
this.runningInElectron$ = store.select(runningInElectron); this.runningInElectron$ = store.select(runningInElectron);
this.saveToKeyboardState$ = store.select(saveToKeyboardState); this.saveToKeyboardStateSubscription = store.select(saveToKeyboardState)
.subscribe(data => this.saveToKeyboardState = data);
}
ngOnDestroy(): void {
this.saveToKeyboardStateSubscription.unsubscribe();
}
@HostListener('document:keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.saveToKeyboardState.showButton &&
event.ctrlKey &&
event.key === 's' &&
!event.defaultPrevented) {
this.clickedOnProgressButton(this.saveToKeyboardState.action);
event.preventDefault();
}
if (event.shiftKey &&
event.ctrlKey &&
event.metaKey &&
event.key === '|' &&
!event.defaultPrevented) {
this.enableUsbStackTest();
event.preventDefault();
}
} }
updateApp() { updateApp() {
@@ -58,4 +87,8 @@ export class MainAppComponent {
clickedOnProgressButton(action: Action) { clickedOnProgressButton(action: Action) {
return this.store.dispatch(action); return this.store.dispatch(action);
} }
enableUsbStackTest() {
this.store.dispatch(new EnableUsbStackTestAction());
}
} }

View File

@@ -4,7 +4,7 @@
<span>About</span> <span>About</span>
</h1> </h1>
<div class="col-xs-12"> <div class="col-xs-12">
<div class="agent-version">Agent version: <span class="text-bold">{{version}}</span></div> <div class="agent-version">Agent version: <span class="text-bold">{{ version }}</span></div>
<div><a class="link-github" (click)="openAgentGitHubPage($event)">Agent on GitHub</a></div> <div><a class="link-github" [href]="agentGithubUrl" externalUrl>Agent on GitHub</a></div>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Constants } from 'uhk-common'; import { Constants } from 'uhk-common';
import { AppState } from '../../../store';
import { getVersions } from '../../../util'; import { getVersions } from '../../../util';
import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
@Component({ @Component({
selector: 'about-page', selector: 'about-page',
@@ -16,12 +13,5 @@ import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
}) })
export class AboutComponent { export class AboutComponent {
version: string = getVersions().version; version: string = getVersions().version;
agentGithubUrl = Constants.AGENT_GITHUB_URL;
constructor(private store: Store<AppState>) {
}
openAgentGitHubPage(event) {
event.preventDefault();
this.store.dispatch(new OpenUrlInNewWindowAction(Constants.AGENT_GITHUB_URL));
}
} }

View File

@@ -2,12 +2,17 @@ import { Routes } from '@angular/router';
import { SettingsComponent } from './settings/settings.component'; import { SettingsComponent } from './settings/settings.component';
import { AboutComponent } from './about/about.component'; import { AboutComponent } from './about/about.component';
import { HelpPageComponent } from './help-page/help-page.component';
export const agentRoutes: Routes = [ export const agentRoutes: Routes = [
{ {
path: 'settings', path: 'settings',
component: SettingsComponent component: SettingsComponent
}, },
{
path: 'help',
component: HelpPageComponent
},
{ {
path: 'about', path: 'about',
component: AboutComponent component: AboutComponent

View File

@@ -0,0 +1,28 @@
<div class="row">
<h1 class="col-xs-12 pane-title">
<i class="fa fa-question-circle"></i>
<span class="macro__name pane-title__name">Help</span>
</h1>
</div>
<div class="row">
<div class="col-xs-12">
Frequently asked questions
<ul>
<li><a href="https://ultimatehackingkeyboard.com/blog/2018/06/23/how-can-i-type-accented-characters-with-my-uhk" externalUrl>How can I type accented characters with my UHK?</a></li>
</ul>
</div>
</div>
<div class="row">
<div class="col-xs-12">
Keyboard shortcuts
<ul>
<li><kbd>CTRL</kbd> + <kbd>Enter</kbd> = Remap key</li>
<li><kbd>CTRL</kbd> + <kbd>S</kbd> = Save to keyboard</li>
<li>Right click on a key = Capture key</li>
<li>Hold Shift while clicking on a key = Remap on all keymaps</li>
<li>Hold Alt while clicking on a key = Remap on all layers</li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,5 @@
:host {
width: 100%;
height: 100%;
display: block;
}

View File

@@ -0,0 +1,13 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'help-page',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './help-page.component.html',
styleUrls: ['./help-page.component.scss'],
host: {
'class': 'container-fluid'
}
})
export class HelpPageComponent {
}

View File

@@ -12,13 +12,10 @@
Firmware {{ hardwareModules.rightModuleInfo.firmwareVersion }} is running on the right keyboard half. Firmware {{ hardwareModules.rightModuleInfo.firmwareVersion }} is running on the right keyboard half.
</p> </p>
<p *ngIf="showUnsupportedOsToFirmwareUpgrade$ | async">Firmware update doesn't work on Windows 7, Windows Vista, and Windows XP. Use Windows 10, Windows 8, Linux, or OSX instead.</p> <p *ngIf="runningOnNotSupportedWindows$ | async">Firmware update doesn't work on Windows 7, Windows Vista,
and Windows XP. Use Windows 10, Windows 8, Linux, or OSX instead.</p>
<p>If the update process fails, disconnect every USB device from your computer including USB hubs, KVM switches, and every USB device. Then connect only your UHK and retry.</p> <p *ngIf="firmwareUpgradeAllowed$ | async">
<p>If you tried the above and the update still keeps failing, please <a class="link-github" (click)="openFirmwareGitHubIssuePage($event)">create a GitHub issue</a>, and attach the update log.</p>
<p>
<button class="btn btn-primary" <button class="btn btn-primary"
[disabled]="flashFirmwareButtonDisbabled$ | async" [disabled]="flashFirmwareButtonDisbabled$ | async"
(click)="onUpdateFirmware()"> (click)="onUpdateFirmware()">
@@ -29,9 +26,23 @@
accept=".tar.bz2" accept=".tar.bz2"
label="Choose firmware file and flash it"></file-upload> label="Choose firmware file and flash it"></file-upload>
</p> </p>
<div *ngIf="firmwareUpgradeFailed$ | async"
class="alert alert-danger"
role="alert">
<p>Firmware update failed. Disconnect every USB device from your computer (including USB hubs, KVM switches, USB dongles, and everything else), then connect only your UHK and retry.</p>
<p>If you've tried the above and the update still keeps failing, please <a class="link-github" (click)="openFirmwareGitHubIssuePage($event)">create a GitHub issue</a>, and attach the update log.</p>
</div> </div>
<div class="flex-grow"> <div *ngIf="firmwareUpgradeSuccess$ | async"
class="alert alert-success"
role="alert">
<p>Firmware update succeeded.</p>
</div>
</div>
<div class="flex-grow" *ngIf="firmwareUpgradeAllowed$ | async">
<xterm [logs]="xtermLog$ | async"></xterm> <xterm [logs]="xtermLog$ | async"></xterm>
</div> </div>
<div class="flex-footer"> <div class="flex-footer">

View File

@@ -7,10 +7,13 @@ import { Constants, HardwareModules, VersionInformation } from 'uhk-common';
import { OpenUrlInNewWindowAction } from '../../../store/actions/app'; import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
import { import {
AppState, AppState,
firmwareUpgradeAllowed,
firmwareUpgradeFailed,
firmwareUpgradeSuccess,
flashFirmwareButtonDisbabled, flashFirmwareButtonDisbabled,
getAgentVersionInfo, getAgentVersionInfo,
getHardwareModules, getHardwareModules,
showUnsupportedOsToFirmwareUpgrade, runningOnNotSupportedWindows,
xtermLog xtermLog
} from '../../../store'; } from '../../../store';
import { UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device'; import { UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
@@ -31,7 +34,10 @@ export class DeviceFirmwareComponent implements OnDestroy {
getAgentVersionInfo$: Observable<VersionInformation>; getAgentVersionInfo$: Observable<VersionInformation>;
hardwareModulesSubscription: Subscription; hardwareModulesSubscription: Subscription;
hardwareModules: HardwareModules; hardwareModules: HardwareModules;
showUnsupportedOsToFirmwareUpgrade$: Observable<boolean>; runningOnNotSupportedWindows$: Observable<boolean>;
firmwareUpgradeAllowed$: Observable<boolean>;
firmwareUpgradeFailed$: Observable<boolean>;
firmwareUpgradeSuccess$: Observable<boolean>;
constructor(private store: Store<AppState>) { constructor(private store: Store<AppState>) {
this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled); this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled);
@@ -40,7 +46,10 @@ export class DeviceFirmwareComponent implements OnDestroy {
this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => { this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => {
this.hardwareModules = data; this.hardwareModules = data;
}); });
this.showUnsupportedOsToFirmwareUpgrade$ = store.select(showUnsupportedOsToFirmwareUpgrade); this.runningOnNotSupportedWindows$ = store.select(runningOnNotSupportedWindows);
this.firmwareUpgradeAllowed$ = store.select(firmwareUpgradeAllowed);
this.firmwareUpgradeFailed$ = store.select(firmwareUpgradeFailed);
this.firmwareUpgradeSuccess$ = store.select(firmwareUpgradeSuccess);
} }
ngOnDestroy(): void { ngOnDestroy(): void {

View File

@@ -29,7 +29,7 @@
<div class="row led-setting"> <div class="row led-setting">
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<slider-wrapper <slider-wrapper
label="Key backlight brightness" label="Key backlight brightness <span class='text-muted pl-1'>Please note that current UHK versions are not backlit.</span>"
[min]="0" [min]="0"
[max]="255" [max]="255"
[step]="1" [step]="1"

View File

@@ -1,5 +1,4 @@
:host { :host {
overflow-y: auto;
display: block; display: block;
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@@ -9,6 +9,7 @@
[keyboardLayout]="keyboardLayout" [keyboardLayout]="keyboardLayout"
[description]="description" [description]="description"
[showDescription]="true" [showDescription]="true"
oncontextmenu="return false;"
(keyClick)="keyClick.emit($event)" (keyClick)="keyClick.emit($event)"
(keyHover)="keyHover.emit($event)" (keyHover)="keyHover.emit($event)"
(capture)="capture.emit($event)" (capture)="capture.emit($event)"

Before

Width:  |  Height:  |  Size: 809 B

After

Width:  |  Height:  |  Size: 853 B

View File

@@ -3,6 +3,11 @@ import { animate, keyframes, state, style, transition, trigger } from '@angular/
import { Layer } from 'uhk-common'; import { Layer } from 'uhk-common';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum'; import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import {
SvgKeyboardCaptureEvent,
SvgKeyboardKeyClickEvent,
SvgKeyHoverEvent
} from '../../../models/svg-key-events';
type AnimationKeyboard = type AnimationKeyboard =
'init' | 'init' |
@@ -82,9 +87,9 @@ export class KeyboardSliderComponent implements OnChanges {
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number }; @Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
@Input() keyboardLayout = KeyboardLayout.ANSI; @Input() keyboardLayout = KeyboardLayout.ANSI;
@Input() description: string; @Input() description: string;
@Output() keyClick = new EventEmitter(); @Output() keyClick = new EventEmitter<SvgKeyboardKeyClickEvent>();
@Output() keyHover = new EventEmitter(); @Output() keyHover = new EventEmitter<SvgKeyHoverEvent>();
@Output() capture = new EventEmitter(); @Output() capture = new EventEmitter<SvgKeyboardCaptureEvent>();
@Output() descriptionChanged = new EventEmitter<string>(); @Output() descriptionChanged = new EventEmitter<string>();
layerAnimationState: AnimationKeyboard[]; layerAnimationState: AnimationKeyboard[];

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'; import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Keymap } from 'uhk-common'; import { Keymap } from 'uhk-common';
@@ -14,6 +14,7 @@ import { KeymapActions } from '../../../store/actions';
selector: 'keymap-add', selector: 'keymap-add',
templateUrl: './keymap-add.component.html', templateUrl: './keymap-add.component.html',
styleUrls: ['./keymap-add.component.scss'], styleUrls: ['./keymap-add.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { host: {
'class': 'container-fluid' 'class': 'container-fluid'
} }

View File

@@ -1,4 +1,4 @@
import { Component, HostListener } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostListener } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Keymap } from 'uhk-common'; import { Keymap } from 'uhk-common';
@@ -24,6 +24,7 @@ import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription
selector: 'keymap-edit', selector: 'keymap-edit',
templateUrl: './keymap-edit.component.html', templateUrl: './keymap-edit.component.html',
styleUrls: ['./keymap-edit.component.scss'], styleUrls: ['./keymap-edit.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { host: {
'class': 'container-fluid' 'class': 'container-fluid'
} }

View File

@@ -8,8 +8,10 @@
<icon *ngIf="deletable" name="trash" (click)="deleteAction()"></icon> <icon *ngIf="deletable" name="trash" (click)="deleteAction()"></icon>
</div> </div>
<div class="list-group-item macro-action-editor__container" <div class="list-group-item macro-action-editor__container"
[@toggler]="((editable && editing) || newItem) ? 'active' : 'inactive'"> [@toggler]="((editable && editing) || newItem) ? 'active' : 'inactive'"
[style.overflow]="overflow">
<macro-action-editor <macro-action-editor
*ngIf="editable || newItem"
[macroAction]="macroAction" [macroAction]="macroAction"
(cancel)="cancelEdit()" (cancel)="cancelEdit()"
(save)="saveEditedAction($event)"> (save)="saveEditedAction($event)">

View File

@@ -1,7 +1,7 @@
@import '../../../../styles/variables'; @import '../../../../styles/variables';
:host { :host {
overflow: hidden; overflow: visible;
display: block; display: block;
&.macro-item:first-of-type { &.macro-item:first-of-type {

View File

@@ -45,6 +45,7 @@ export class MacroItemComponent implements OnInit, OnChanges {
iconName: string; iconName: string;
editing: boolean; editing: boolean;
newItem: boolean = false; newItem: boolean = false;
overflow = 'hidden';
constructor(private mapper: MapperService) { } constructor(private mapper: MapperService) { }
@@ -53,6 +54,7 @@ export class MacroItemComponent implements OnInit, OnChanges {
if (!this.macroAction) { if (!this.macroAction) {
this.editing = true; this.editing = true;
this.newItem = true; this.newItem = true;
this.overflow = 'visible';
} }
} }
@@ -65,6 +67,7 @@ export class MacroItemComponent implements OnInit, OnChanges {
saveEditedAction(editedAction: MacroAction): void { saveEditedAction(editedAction: MacroAction): void {
this.macroAction = editedAction; this.macroAction = editedAction;
this.editing = false; this.editing = false;
this.overflow = 'hidden';
this.updateView(); this.updateView();
this.save.emit(editedAction); this.save.emit(editedAction);
} }
@@ -77,10 +80,12 @@ export class MacroItemComponent implements OnInit, OnChanges {
this.editing = true; this.editing = true;
this.edit.emit(); this.edit.emit();
this.setOverflow('visible');
} }
cancelEdit(): void { cancelEdit(): void {
this.editing = false; this.editing = false;
this.overflow = 'hidden';
this.cancel.emit(); this.cancel.emit();
} }
@@ -202,4 +207,12 @@ export class MacroItemComponent implements OnInit, OnChanges {
}); });
this.title += selectedButtonLabels.join(', '); this.title += selectedButtonLabels.join(', ');
} }
private setOverflow(value: string): void {
// tslint:disable: align
setTimeout(() => {
this.overflow = value;
}, 600);
// tslint:enable: align
}
} }

View File

@@ -47,7 +47,7 @@
</ul> </ul>
</div> </div>
<div [ngSwitch]="activeTab"> <div [ngSwitch]="activeTab">
<keypress-tab #tab *ngSwitchCase="tabName.Keypress" class="popover-content" <keypress-tab #tab *ngSwitchCase="tabName.Keypress" class="popover-content pr-10"
[defaultKeyAction]="defaultKeyAction" [defaultKeyAction]="defaultKeyAction"
[secondaryRoleEnabled]="true" [secondaryRoleEnabled]="true"
(validAction)="keyActionValid=$event" (validAction)="keyActionValid=$event"
@@ -81,14 +81,14 @@
<label> <label>
<input type="checkbox" <input type="checkbox"
name="remapOnAllKeymap" name="remapOnAllKeymap"
[(ngModel)]="remapOnAllKeymap"> Remap on all keymaps [(ngModel)]="remapInfo.remapOnAllKeymap"> Remap on all keymaps
</label> </label>
</div> </div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" <input type="checkbox"
name="remapOnAllLayer" name="remapOnAllLayer"
[(ngModel)]="remapOnAllLayer"> Remap on all layers [(ngModel)]="remapInfo.remapOnAllLayer"> Remap on all layers
</label> </label>
</div> </div>
<div class="d-inline-block"> <div class="d-inline-block">
@@ -109,7 +109,7 @@
<div class="d-inline-block pull-right"> <div class="d-inline-block pull-right">
<button class="btn btn-sm btn-default" type="button" (click)="onCancelClick()"> Cancel</button> <button class="btn btn-sm btn-default" type="button" (click)="onCancelClick()"> Cancel</button>
<button class="btn btn-sm btn-primary" [class.disabled]="!keyActionValid" type="button" <button class="btn btn-sm btn-primary" [class.disabled]="!keyActionValid" type="button"
(click)="onRemapKey()"> Remap Key (click)="onRemapKey()"> Remap key
</button> </button>
</div> </div>
</div> </div>

View File

@@ -99,6 +99,10 @@
padding: 10px 24px; padding: 10px 24px;
} }
.pr-10 {
padding-right: 10px;
}
.popover-overlay { .popover-overlay {
position: fixed; position: fixed;
width: 100%; width: 100%;
@@ -116,23 +120,6 @@
} }
} }
.select2-item {
position: relative;
font-size: 1.5rem;
&.keymap-name--wrapper {
padding-left: 50px;
}
.layout-segment-code {
height: 2rem;
position: absolute;
left: 0;
top: 50%;
margin-top: -1rem;
}
}
.popover-action-form { .popover-action-form {
margin-top: 4px; margin-top: 4px;

View File

@@ -1,4 +1,5 @@
import { import {
ChangeDetectionStrategy,
Component, Component,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
@@ -32,6 +33,7 @@ import { Tab } from './tab';
import { AppState } from '../../store'; import { AppState } from '../../store';
import { getKeymaps } from '../../store/reducers/user-configuration'; import { getKeymaps } from '../../store/reducers/user-configuration';
import { KeyActionRemap } from '../../models/key-action-remap'; import { KeyActionRemap } from '../../models/key-action-remap';
import { RemapInfo } from '../../models/remap-info';
enum TabName { enum TabName {
Keypress, Keypress,
@@ -46,6 +48,7 @@ enum TabName {
selector: 'popover', selector: 'popover',
templateUrl: './popover.component.html', templateUrl: './popover.component.html',
styleUrls: ['./popover.component.scss'], styleUrls: ['./popover.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [ animations: [
trigger('popover', [ trigger('popover', [
state('closed', style({ state('closed', style({
@@ -84,6 +87,7 @@ export class PopoverComponent implements OnChanges {
@Input() wrapPosition: any; @Input() wrapPosition: any;
@Input() visible: boolean; @Input() visible: boolean;
@Input() allowLayerDoubleTap: boolean; @Input() allowLayerDoubleTap: boolean;
@Input() remapInfo: RemapInfo;
@Output() cancel = new EventEmitter<any>(); @Output() cancel = new EventEmitter<any>();
@Output() remap = new EventEmitter<KeyActionRemap>(); @Output() remap = new EventEmitter<KeyActionRemap>();
@@ -101,9 +105,6 @@ export class PopoverComponent implements OnChanges {
leftPosition: number = 0; leftPosition: number = 0;
animationState: string; animationState: string;
remapOnAllKeymap: boolean;
remapOnAllLayer: boolean;
private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined); private readonly currentKeymap$ = new BehaviorSubject<Keymap>(undefined);
constructor(store: Store<AppState>) { constructor(store: Store<AppState>) {
@@ -143,8 +144,6 @@ export class PopoverComponent implements OnChanges {
if (change['visible']) { if (change['visible']) {
if (change['visible'].currentValue) { if (change['visible'].currentValue) {
this.animationState = 'opened'; this.animationState = 'opened';
this.remapOnAllKeymap = false;
this.remapOnAllLayer = false;
} else { } else {
this.animationState = 'closed'; this.animationState = 'closed';
} }
@@ -163,8 +162,8 @@ export class PopoverComponent implements OnChanges {
if (this.keyActionValid) { if (this.keyActionValid) {
try { try {
this.remap.emit({ this.remap.emit({
remapOnAllKeymap: this.remapOnAllKeymap, remapOnAllKeymap: this.remapInfo.remapOnAllKeymap,
remapOnAllLayer: this.remapOnAllLayer, remapOnAllLayer: this.remapInfo.remapOnAllLayer,
action: this.selectedTab.toKeyAction() action: this.selectedTab.toKeyAction()
}); });
} catch (e) { } catch (e) {
@@ -179,6 +178,14 @@ export class PopoverComponent implements OnChanges {
this.cancel.emit(); this.cancel.emit();
} }
@HostListener('document:keydown.control.enter', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.visible) {
this.onRemapKey();
event.preventDefault();
}
}
selectTab(tab: TabName): void { selectTab(tab: TabName): void {
this.activeTab = tab; this.activeTab = tab;
} }

View File

@@ -4,12 +4,23 @@
<ng-template [ngIf]="keymapOptions.length > 0"> <ng-template [ngIf]="keymapOptions.length > 0">
<div> <div>
<b>Switch to keymap:</b> <b>Switch to keymap:</b>
<select2 <ngx-select [items]="keymapOptions"
[data]="keymapOptions" [ngModel]="selectedKeymap?.abbreviation || -1"
[value]="selectedKeymap?.abbreviation || -1" [autoActiveOnMouseEnter]="false"
(valueChanged)="onChange($event)" size="small"
[width]="'100%'" optionValueField="id"
></select2> optionTextField="text"
(select)="onChange($event)">
<ng-template ngx-select-option let-option>
<span [ngClass]="{'indent-dropdown-item':option.data.id !== '-1'}">
<span>{{ option.text }}</span>
<span class="scancode--searchterm">
{{ option.data.additional?.explanation}}
</span>
</span>
</ng-template>
</ngx-select>
</div> </div>
<div> <div>
<div class="empty" *ngIf="!selectedKeymap?.abbreviation"> <div class="empty" *ngIf="!selectedKeymap?.abbreviation">

View File

@@ -16,7 +16,7 @@
margin-right: 7px; margin-right: 7px;
} }
select2 { ngx-select {
flex: 1; flex: 1;
} }
} }

View File

@@ -1,8 +1,8 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Select2OptionData } from 'ng2-select2/ng2-select2';
import { Keymap, KeyAction, SwitchKeymapAction } from 'uhk-common'; import { Keymap, KeyAction, SwitchKeymapAction } from 'uhk-common';
import { Tab } from '../tab'; import { Tab } from '../tab';
import { SelectOptionData } from '../../../../models/select-option-data';
@Component({ @Component({
selector: 'keymap-tab', selector: 'keymap-tab',
@@ -14,7 +14,7 @@ export class KeymapTabComponent extends Tab implements OnChanges {
@Input() defaultKeyAction: KeyAction; @Input() defaultKeyAction: KeyAction;
@Input() keymaps: Keymap[]; @Input() keymaps: Keymap[];
keymapOptions: Array<Select2OptionData>; keymapOptions: Array<SelectOptionData>;
selectedKeymap: Keymap; selectedKeymap: Keymap;
constructor() { constructor() {
@@ -25,7 +25,7 @@ export class KeymapTabComponent extends Tab implements OnChanges {
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes.keymaps) { if (changes.keymaps) {
this.keymapOptions = this.keymaps this.keymapOptions = this.keymaps
.map((keymap: Keymap): Select2OptionData => { .map((keymap: Keymap): SelectOptionData => {
return { return {
id: keymap.abbreviation, id: keymap.abbreviation,
text: keymap.name text: keymap.name
@@ -40,12 +40,11 @@ export class KeymapTabComponent extends Tab implements OnChanges {
this.validAction.emit(true); this.validAction.emit(true);
} }
// TODO: change to the correct type when the wrapper has added it. onChange(event: string) {
onChange(event: any) { if (event === '-1') {
if (event.value === '-1') {
this.selectedKeymap = undefined; this.selectedKeymap = undefined;
} else { } else {
this.selectedKeymap = this.keymaps.find((keymap: Keymap) => keymap.abbreviation === event.value); this.selectedKeymap = this.keymaps.find((keymap: Keymap) => keymap.abbreviation === event);
} }
} }

View File

@@ -1,12 +1,27 @@
<div class="scancode-options"> <div class="scancode-options">
<b class="setting-label">Scancode:</b> <b class="setting-label">Scancode:</b>
<select2 <div class="scancode-container">
[data]="scanCodeGroups" <ngx-select [items]="scanCodeGroups"
[value]="selectedScancodeOption.id" [ngModel]="selectedScancodeOption?.id"
(valueChanged)="onScancodeChange($event)" [autoActiveOnMouseEnter]="false"
[width]="200" size="small"
[options]="options" optionValueField="id"
></select2> optionTextField="text"
optGroupLabelField="text"
optGroupOptionsField="children"
(select)="onScancodeChange($event)">
<ng-template ngx-select-option let-option>
<span [ngClass]="{'indent-dropdown-item':option.data.id !== '0'}">
<span>{{ option.text }}</span>
<span class="scancode--searchterm">
{{ option.data.additional?.explanation}}
</span>
</span>
</ng-template>
</ngx-select>
</div>
<icon name="question-circle" <icon name="question-circle"
data-toggle="tooltip" data-toggle="tooltip"
html="true" html="true"
@@ -21,32 +36,48 @@
<div class="btn-toolbar modifiers"> <div class="btn-toolbar modifiers">
<div class="btn-group btn-group-sm modifiers__left"> <div class="btn-group btn-group-sm modifiers__left">
<button type="button" class="btn btn-default" <button type="button" class="btn btn-default"
*ngFor="let modifier of leftModifiers; let index = index" *ngFor="let modifier of leftModifiers; trackBy:modifiersTrackBy"
[class.btn-primary]="leftModifierSelects[index]" [class.btn-primary]="modifier.checked"
(click)="toggleModifier(false, index)" (click)="toggleModifier(modifier)"
> >
{{modifier}} {{ modifier.text }}
</button> </button>
</div> </div>
<div class="btn-group btn-group-sm modifiers__right"> <div class="btn-group btn-group-sm modifiers__right">
<button type="button" class="btn btn-default" <button type="button" class="btn btn-default"
*ngFor="let modifier of rightModifiers; let index = index" *ngFor="let modifier of rightModifiers; trackBy:modifiersTrackBy"
[class.btn-primary]="rightModifierSelects[index]" [class.btn-primary]="modifier.checked"
(click)="toggleModifier(true, index)" (click)="toggleModifier(modifier)"
> >
{{modifier}} {{ modifier.text }}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div class="long-press-container" *ngIf="secondaryRoleEnabled"> <div class="long-press-container" *ngIf="secondaryRoleEnabled">
<b class="setting-label">Secondary role:</b> <b class="setting-label">Secondary role:</b>
<select2 #secondaryRoleSelect <div class="secondary-role-groups-container">
[data]="secondaryRoleGroups" <ngx-select [items]="secondaryRoleGroups"
[value]="selectedSecondaryRoleIndex.toString()" [ngModel]="selectedSecondaryRoleIndex.toString()"
(valueChanged)="onSecondaryRoleChange($event)" [autoActiveOnMouseEnter]="false"
[width]="140" size="small"
></select2> optionValueField="id"
optionTextField="text"
optGroupLabelField="text"
optGroupOptionsField="children"
(select)="onSecondaryRoleChange($event)">
<ng-template ngx-select-option let-option>
<span [ngClass]="{'indent-dropdown-item':option.data.id !== '-1'}">
<span>{{ option.text }}</span>
<span class="scancode--searchterm">
{{ option.data.additional?.explanation}}
</span>
</span>
</ng-template>
</ngx-select>
</div>
<icon name="question-circle" <icon name="question-circle"
data-toggle="tooltip" data-toggle="tooltip"
html="true" html="true"
@@ -57,7 +88,7 @@
<li>Tap this key to trigger Escape. <i>(Primary role)</i></li> <li>Tap this key to trigger Escape. <i>(Primary role)</i></li>
<li>Hold this key and press another key to activate the relevant key of the Mouse layer. <i>(Secondary role)</i></li> <li>Hold this key and press another key to activate the relevant key of the Mouse layer. <i>(Secondary role)</i></li>
</ul> </ul>
<p class='text-left pt-3'>The secondary role can be any layer or modifier.</p>" <p class='text-left'>The secondary role can be any layer or modifier.</p>"
data-placement="bottom"></icon> data-placement="bottom"></icon>
</div> </div>

View File

@@ -79,4 +79,14 @@
display: block; display: block;
} }
} }
.scancode-container {
display: inline-block;
width: 200px;
}
.secondary-role-groups-container {
display: inline-block;
width: 140px;
}
} }

View File

@@ -1,12 +1,15 @@
import { Component, Input, OnChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { Select2OptionData, Select2TemplateFunction } from 'ng2-select2';
import { KeyAction, KeystrokeAction, KeystrokeType, SCANCODES, SECONDARY_ROLES } from 'uhk-common'; import { KeyAction, KeystrokeAction, KeystrokeType, SCANCODES, SECONDARY_ROLES } from 'uhk-common';
import { Tab } from '../tab'; import { Tab } from '../tab';
import { MapperService } from '../../../../services/mapper.service'; import { MapperService } from '../../../../services/mapper.service';
import { SelectOptionData } from '../../../../models/select-option-data';
import { KeyModifierModel } from '../../../../models/key-modifier-model';
import { mapLeftRigthModifierToKeyActionModifier } from '../../../../util';
@Component({ @Component({
selector: 'keypress-tab', selector: 'keypress-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './keypress-tab.component.html', templateUrl: './keypress-tab.component.html',
styleUrls: ['./keypress-tab.component.scss'] styleUrls: ['./keypress-tab.component.scss']
}) })
@@ -14,45 +17,28 @@ export class KeypressTabComponent extends Tab implements OnChanges {
@Input() defaultKeyAction: KeyAction; @Input() defaultKeyAction: KeyAction;
@Input() secondaryRoleEnabled: boolean; @Input() secondaryRoleEnabled: boolean;
leftModifiers: string[]; leftModifiers: KeyModifierModel[];
rightModifiers: string[]; rightModifiers: KeyModifierModel[];
leftModifierSelects: boolean[]; scanCodeGroups: Array<SelectOptionData>;
rightModifierSelects: boolean[]; secondaryRoleGroups: Array<SelectOptionData>;
scanCodeGroups: Array<Select2OptionData>; selectedScancodeOption: SelectOptionData;
secondaryRoleGroups: Array<Select2OptionData>;
options: Select2Options;
selectedScancodeOption: Select2OptionData;
selectedSecondaryRoleIndex: number; selectedSecondaryRoleIndex: number;
constructor(private mapper: MapperService) { constructor(private mapper: MapperService) {
super(); super();
this.leftModifiers = ['LShift', 'LCtrl', 'LSuper', 'LAlt']; this.leftModifiers = mapper.getLeftKeyModifiers();
this.rightModifiers = ['RShift', 'RCtrl', 'RSuper', 'RAlt']; this.rightModifiers = mapper.getRightKeyModifiers();
this.scanCodeGroups = [{ this.scanCodeGroups = [{
id: '0', id: '0',
text: 'None' text: 'None'
}]; }];
this.scanCodeGroups = this.scanCodeGroups.concat(SCANCODES); this.scanCodeGroups = this.scanCodeGroups.concat(SCANCODES);
this.secondaryRoleGroups = SECONDARY_ROLES; this.secondaryRoleGroups = SECONDARY_ROLES;
this.leftModifierSelects = Array(this.leftModifiers.length).fill(false);
this.rightModifierSelects = Array(this.rightModifiers.length).fill(false);
this.selectedScancodeOption = this.scanCodeGroups[0]; this.selectedScancodeOption = this.scanCodeGroups[0];
this.selectedSecondaryRoleIndex = -1; this.selectedSecondaryRoleIndex = -1;
this.options = {
templateResult: this.scanCodeTemplateResult,
matcher: (term: string, text: string, data: Select2OptionData) => {
let found = text.toUpperCase().indexOf(term.toUpperCase()) > -1;
if (!found && data.additional && data.additional.explanation) {
found = data.additional.explanation.toUpperCase().indexOf(term.toUpperCase()) > -1;
}
return found;
}
};
} }
ngOnChanges() { ngOnChanges() {
@@ -68,15 +54,15 @@ export class KeypressTabComponent extends Tab implements OnChanges {
return (keystrokeAction) ? (keystrokeAction.scancode > 0 || keystrokeAction.modifierMask > 0) : false; return (keystrokeAction) ? (keystrokeAction.scancode > 0 || keystrokeAction.modifierMask > 0) : false;
} }
onKeysCapture(event: { code: number, left: boolean[], right: boolean[] }) { onKeysCapture(event: { code: number, left: KeyModifierModel[], right: KeyModifierModel[] }) {
if (event.code) { if (event.code) {
this.selectedScancodeOption = this.findScancodeOptionByScancode(event.code, KeystrokeType.basic); this.selectedScancodeOption = this.findScancodeOptionByScancode(event.code, KeystrokeType.basic);
} else { } else {
this.selectedScancodeOption = this.scanCodeGroups[0]; this.selectedScancodeOption = this.scanCodeGroups[0];
} }
this.leftModifierSelects = event.left; this.leftModifiers = event.left;
this.rightModifierSelects = event.right; this.rightModifiers = event.right;
this.validAction.emit(this.keyActionValid()); this.validAction.emit(this.keyActionValid());
} }
@@ -88,16 +74,12 @@ export class KeypressTabComponent extends Tab implements OnChanges {
// Restore selectedScancodeOption // Restore selectedScancodeOption
this.selectedScancodeOption = this.findScancodeOptionByScancode(keystrokeAction.scancode || 0, keystrokeAction.type); this.selectedScancodeOption = this.findScancodeOptionByScancode(keystrokeAction.scancode || 0, keystrokeAction.type);
const leftModifiersLength: number = this.leftModifiers.length; for (const modifier of this.leftModifiers) {
modifier.checked = (keystrokeAction.modifierMask & modifier.value) > 0;
// Restore modifiers
for (let i = 0; i < leftModifiersLength; ++i) {
this.leftModifierSelects[this.mapper.modifierMapper(i)] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
} }
for (let i = leftModifiersLength; i < leftModifiersLength + this.rightModifierSelects.length; ++i) { for (const modifier of this.rightModifiers) {
const index: number = this.mapper.modifierMapper(i) - leftModifiersLength; modifier.checked = (keystrokeAction.modifierMask & modifier.value) > 0;
this.rightModifierSelects[index] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
} }
// Restore secondaryRoleAction // Restore secondaryRoleAction
@@ -119,11 +101,7 @@ export class KeypressTabComponent extends Tab implements OnChanges {
} else { } else {
keystrokeAction.type = KeystrokeType[scTypePair[1]]; keystrokeAction.type = KeystrokeType[scTypePair[1]];
} }
keystrokeAction.modifierMask = 0; keystrokeAction.modifierMask = mapLeftRigthModifierToKeyActionModifier(this.leftModifiers, this.rightModifiers);
const modifiers = this.leftModifierSelects.concat(this.rightModifierSelects).map(x => x ? 1 : 0);
for (let i = 0; i < modifiers.length; ++i) {
keystrokeAction.modifierMask |= modifiers[i] << this.mapper.modifierMapper(i);
}
keystrokeAction.secondaryRoleAction = this.selectedSecondaryRoleIndex === -1 keystrokeAction.secondaryRoleAction = this.selectedSecondaryRoleIndex === -1
? undefined ? undefined
@@ -134,50 +112,30 @@ export class KeypressTabComponent extends Tab implements OnChanges {
} }
} }
scanCodeTemplateResult: Select2TemplateFunction = (state: Select2OptionData): JQuery | string => { toggleModifier(modifier: KeyModifierModel): void {
if (!state.id) { modifier.checked = !modifier.checked;
return state.text;
}
if (state.additional && state.additional.explanation) {
return jQuery(
'<span class="select2-item">'
+ '<span>' + state.text + '</span>'
+ '<span class="scancode--searchterm"> '
+ state.additional.explanation
+ '</span>' +
'</span>'
);
} else {
return jQuery('<span class="select2-item">' + state.text + '</span>');
}
}
toggleModifier(right: boolean, index: number) {
const modifierSelects: boolean[] = right ? this.rightModifierSelects : this.leftModifierSelects;
modifierSelects[index] = !modifierSelects[index];
this.validAction.emit(this.keyActionValid()); this.validAction.emit(this.keyActionValid());
} }
onSecondaryRoleChange(event: { value: string }) { onSecondaryRoleChange(id: string) {
this.selectedSecondaryRoleIndex = +event.value; this.selectedSecondaryRoleIndex = +id;
} }
onScancodeChange(event: { value: string }) { onScancodeChange(id: string) {
const id: string = event.value;
// ng2-select2 should provide the selectedOption in an upcoming release
// TODO: change this when it has become available
this.selectedScancodeOption = this.findScancodeOptionById(id); this.selectedScancodeOption = this.findScancodeOptionById(id);
this.validAction.emit(this.keyActionValid()); this.validAction.emit(this.keyActionValid());
} }
private findScancodeOptionBy(predicate: (option: Select2OptionData) => boolean): Select2OptionData { modifiersTrackBy(index: number, modifier: KeyModifierModel): string {
let selectedOption: Select2OptionData; return `${modifier.value}${modifier.checked}`;
}
const scanCodeGroups: Select2OptionData[] = [...this.scanCodeGroups]; private findScancodeOptionBy(predicate: (option: SelectOptionData) => boolean): SelectOptionData {
let selectedOption: SelectOptionData;
const scanCodeGroups: SelectOptionData[] = [...this.scanCodeGroups];
while (scanCodeGroups.length > 0) { while (scanCodeGroups.length > 0) {
const scanCodeGroup = scanCodeGroups.shift(); const scanCodeGroup = scanCodeGroups.shift();
if (predicate(scanCodeGroup)) { if (predicate(scanCodeGroup)) {
@@ -192,14 +150,14 @@ export class KeypressTabComponent extends Tab implements OnChanges {
return selectedOption; return selectedOption;
} }
private findScancodeOptionById(id: string): Select2OptionData { private findScancodeOptionById(id: string): SelectOptionData {
return this.findScancodeOptionBy(option => option.id === id); return this.findScancodeOptionBy(option => option.id === id);
} }
private findScancodeOptionByScancode(scancode: number, type: KeystrokeType): Select2OptionData { private findScancodeOptionByScancode(scancode: number, type: KeystrokeType): SelectOptionData {
const typeToFind: string = const typeToFind: string =
(type === KeystrokeType.shortMedia || type === KeystrokeType.longMedia) ? 'media' : KeystrokeType[type]; (type === KeystrokeType.shortMedia || type === KeystrokeType.longMedia) ? 'media' : KeystrokeType[type];
return this.findScancodeOptionBy((option: Select2OptionData) => { return this.findScancodeOptionBy((option: SelectOptionData) => {
const additional = option.additional; const additional = option.additional;
if (additional && additional.scancode === scancode && additional.type === typeToFind) { if (additional && additional.scancode === scancode && additional.type === typeToFind) {
return true; return true;
@@ -211,7 +169,11 @@ export class KeypressTabComponent extends Tab implements OnChanges {
}); });
} }
private toScancodeTypePair(option: Select2OptionData): [number, string] { private toScancodeTypePair(option: SelectOptionData): [number, string] {
if (!option) {
return [0, 'basic'];
}
let scanCode: number; let scanCode: number;
let type: string; let type: string;
if (option.additional) { if (option.additional) {

View File

@@ -1,4 +1,4 @@
import { Component, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core';
import { KeyAction, LayerName, SwitchLayerAction, SwitchLayerMode } from 'uhk-common'; import { KeyAction, LayerName, SwitchLayerAction, SwitchLayerMode } from 'uhk-common';
import { Tab } from '../tab'; import { Tab } from '../tab';
@@ -7,6 +7,7 @@ export type toggleType = 'active' | 'toggle';
@Component({ @Component({
selector: 'layer-tab', selector: 'layer-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './layer-tab.component.html', templateUrl: './layer-tab.component.html',
styleUrls: ['./layer-tab.component.scss'] styleUrls: ['./layer-tab.component.scss']
}) })

View File

@@ -5,7 +5,23 @@
<p><i>Please note that macro playback is not implemented yet. You can bind macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p> <p><i>Please note that macro playback is not implemented yet. You can bind macros, but they won't have any effect until firmware support is implemented. We're working on this.</i></p>
<div class="macro-selector"> <div class="macro-selector">
<b> Play macro: </b> <b> Play macro: </b>
<select2 [data]="macroOptions" [value]="macroOptions[selectedMacroIndex].id" (valueChanged)="onChange($event)" [width]="'100%'"></select2> <ngx-select [items]="macroOptions"
[ngModel]="macroOptions[selectedMacroIndex]?.id"
[autoActiveOnMouseEnter]="false"
size="small"
optionValueField="id"
optionTextField="text"
(select)="onChange($event)">
<ng-template ngx-select-option let-option>
<span [ngClass]="{'indent-dropdown-item':option.data.id !== '-1'}">
<span>{{ option.text }}</span>
<span class="scancode--searchterm">
{{ option.data.additional?.explanation}}
</span>
</span>
</ng-template>
</ngx-select>
</div> </div>
<div class="macro-action-container"> <div class="macro-action-container">
<div class="list-group"> <div class="list-group">

View File

@@ -16,7 +16,7 @@
margin-right: 7px; margin-right: 7px;
} }
select2 { ngx-select {
flex: 1; flex: 1;
} }
} }

View File

@@ -1,16 +1,17 @@
import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs/Subscription';
import { Select2OptionData } from 'ng2-select2/ng2-select2';
import { KeyAction, Macro, PlayMacroAction } from 'uhk-common'; import { KeyAction, Macro, PlayMacroAction } from 'uhk-common';
import { Tab } from '../tab'; import { Tab } from '../tab';
import { AppState } from '../../../../store/index'; import { AppState } from '../../../../store';
import { getMacros } from '../../../../store/reducers/user-configuration'; import { getMacros } from '../../../../store/reducers/user-configuration';
import { SelectOptionData } from '../../../../models/select-option-data';
@Component({ @Component({
selector: 'macro-tab', selector: 'macro-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './macro-tab.component.html', templateUrl: './macro-tab.component.html',
styleUrls: ['./macro-tab.component.scss'] styleUrls: ['./macro-tab.component.scss']
}) })
@@ -18,7 +19,7 @@ export class MacroTabComponent extends Tab implements OnInit, OnChanges, OnDestr
@Input() defaultKeyAction: KeyAction; @Input() defaultKeyAction: KeyAction;
macros: Macro[]; macros: Macro[];
macroOptions: Array<Select2OptionData>; macroOptions: Array<SelectOptionData>;
selectedMacroIndex: number; selectedMacroIndex: number;
private subscription: Subscription; private subscription: Subscription;
@@ -31,7 +32,7 @@ export class MacroTabComponent extends Tab implements OnInit, OnChanges, OnDestr
} }
ngOnInit() { ngOnInit() {
this.macroOptions = this.macros.map(function (macro: Macro, index: number): Select2OptionData { this.macroOptions = this.macros.map(function (macro: Macro, index: number): SelectOptionData {
return { return {
id: index.toString(), id: index.toString(),
text: macro.name text: macro.name
@@ -44,9 +45,8 @@ export class MacroTabComponent extends Tab implements OnInit, OnChanges, OnDestr
this.validAction.emit(true); this.validAction.emit(true);
} }
// TODO: change to the correct type when the wrapper has added it. onChange(id: string) {
onChange(event: any) { this.selectedMacroIndex = +id;
this.selectedMacroIndex = +event.value;
} }
keyActionValid(): boolean { keyActionValid(): boolean {

View File

@@ -1,10 +1,11 @@
import { Component, Input, OnChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { KeyAction, MouseAction, MouseActionParam } from 'uhk-common'; import { KeyAction, MouseAction, MouseActionParam } from 'uhk-common';
import { Tab } from '../tab'; import { Tab } from '../tab';
@Component({ @Component({
selector: 'mouse-tab', selector: 'mouse-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './mouse-tab.component.html', templateUrl: './mouse-tab.component.html',
styleUrls: ['./mouse-tab.component.scss'] styleUrls: ['./mouse-tab.component.scss']
}) })

View File

@@ -1,9 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Tab } from '../tab'; import { Tab } from '../tab';
@Component({ @Component({
selector: 'none-tab', selector: 'none-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './none-tab.component.html', templateUrl: './none-tab.component.html',
styleUrls: ['./none-tab.component.scss'] styleUrls: ['./none-tab.component.scss']
}) })

View File

@@ -1,10 +1,12 @@
import { Component, EventEmitter, HostListener, Output, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, Output } from '@angular/core';
import { CaptureService } from '../../../../services/capture.service'; import { CaptureService } from '../../../../services/capture.service';
import { KeyModifierModel } from '../../../../models/key-modifier-model';
@Component({ @Component({
selector: 'capture-keystroke-button', selector: 'capture-keystroke-button',
templateUrl: './capture-keystroke-button.component.html', templateUrl: './capture-keystroke-button.component.html',
styleUrls: ['./capture-keystroke-button.component.scss'] styleUrls: ['./capture-keystroke-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class CaptureKeystrokeButtonComponent { export class CaptureKeystrokeButtonComponent {
@Input() isLink = false; @Input() isLink = false;
@@ -67,8 +69,8 @@ export class CaptureKeystrokeButtonComponent {
private saveScanCode(code?: number) { private saveScanCode(code?: number) {
this.record = false; this.record = false;
const left: boolean[] = this.captureService.getModifiers(true); const left: KeyModifierModel[] = this.captureService.getModifiers(true);
const right: boolean[] = this.captureService.getModifiers(false); const right: KeyModifierModel[] = this.captureService.getModifiers(false);
this.capture.emit({ this.capture.emit({
code, code,

View File

@@ -1,9 +1,10 @@
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'icon', selector: 'icon',
templateUrl: './icon.component.html', templateUrl: './icon.component.html',
styleUrls: ['./icon.component.scss'] styleUrls: ['./icon.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class IconComponent implements OnInit { export class IconComponent implements OnInit {

View File

@@ -136,11 +136,17 @@
(click)="toggleHide($event, 'agent')"></i> (click)="toggleHide($event, 'agent')"></i>
</div> </div>
<ul [@toggler]="animation['agent']"> <ul [@toggler]="animation['agent']">
<li class="sidebar__level-2--item"> <!--li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']"> <div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/settings']" <a [routerLink]="['/settings']"
[class.disabled]="state.updatingFirmware">Settings</a> [class.disabled]="state.updatingFirmware">Settings</a>
</div> </div>
</li-->
<li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/help']"
[class.disabled]="state.updatingFirmware">Help</a>
</div>
</li> </li>
<li class="sidebar__level-2--item"> <li class="sidebar__level-2--item">
<div class="sidebar__level-2" [routerLinkActive]="['active']"> <div class="sidebar__level-2" [routerLinkActive]="['active']">

View File

@@ -1,5 +1,5 @@
<label *ngIf="label"> <label *ngIf="label">
<span>{{label}}</span> <span [innerHtml]="label"></span>
<icon name="question-circle" <icon name="question-circle"
data-toggle="tooltip" data-toggle="tooltip"
[title]="tooltip" [title]="tooltip"

View File

@@ -0,0 +1 @@
export const SECONDARY_ROLE_BOTTOM_MARGIN = 1;

View File

@@ -13,9 +13,9 @@
[selectedKey]="selectedKey" [selectedKey]="selectedKey"
[@split]="moduleAnimationStates[i]" [@split]="moduleAnimationStates[i]"
[selected]="selectedKey?.moduleId === i" [selected]="selectedKey?.moduleId === i"
(keyClick)="onKeyClick(i, $event.index, $event.keyTarget)" (keyClick)="onKeyClick(i, $event)"
(keyHover)="onKeyHover($event.index, $event.event, $event.over, i)" (keyHover)="onKeyHover($event.index, $event.event, $event.over, i)"
(capture)="onCapture(i, $event.index, $event.captured)" /> (capture)="onCapture(i, $event)" />
<svg:path [@fadeSeparator]="separatorAnimation" <svg:path [@fadeSeparator]="separatorAnimation"
[attr.d]="separator.d" [attr.d]="separator.d"

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -7,6 +7,12 @@ import { SvgModule } from '../module';
import { SvgModuleProviderService } from '../../../services/svg-module-provider.service'; import { SvgModuleProviderService } from '../../../services/svg-module-provider.service';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum'; import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import { SvgSeparator } from '../separator'; import { SvgSeparator } from '../separator';
import {
SvgKeyHoverEvent,
SvgKeyboardKeyClickEvent,
SvgKeyboardCaptureEvent,
SvgModuleKeyClickEvent
} from '../../../models/svg-key-events';
@Component({ @Component({
selector: 'svg-keyboard', selector: 'svg-keyboard',
@@ -45,9 +51,9 @@ export class SvgKeyboardComponent implements OnInit {
@Input() keyboardLayout = KeyboardLayout.ANSI; @Input() keyboardLayout = KeyboardLayout.ANSI;
@Input() description: string; @Input() description: string;
@Input() showDescription = false; @Input() showDescription = false;
@Output() keyClick = new EventEmitter(); @Output() keyClick = new EventEmitter<SvgKeyboardKeyClickEvent>();
@Output() keyHover = new EventEmitter(); @Output() keyHover = new EventEmitter<SvgKeyHoverEvent>();
@Output() capture = new EventEmitter(); @Output() capture = new EventEmitter<SvgKeyboardCaptureEvent>();
@Output() descriptionChanged = new EventEmitter<string>(); @Output() descriptionChanged = new EventEmitter<string>();
modules: SvgModule[]; modules: SvgModule[];
@@ -79,19 +85,17 @@ export class SvgKeyboardComponent implements OnInit {
} }
} }
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void { onKeyClick(moduleId: number, event: SvgModuleKeyClickEvent): void {
this.keyClick.emit({ this.keyClick.emit({
moduleId, ...event,
keyId, moduleId
keyTarget
}); });
} }
onCapture(moduleId: number, keyId: number, captured: { code: number, left: boolean[], right: boolean[] }): void { onCapture(moduleId: number, event: SvgKeyboardCaptureEvent): void {
this.capture.emit({ this.capture.emit({
moduleId, ...event,
keyId, moduleId
captured
}); });
} }

View File

@@ -11,3 +11,4 @@ export { SvgSingleIconKeyComponent } from './svg-single-icon-key';
export { SvgSwitchKeymapKeyComponent } from './svg-switch-keymap-key'; export { SvgSwitchKeymapKeyComponent } from './svg-switch-keymap-key';
export { SvgTextIconKeyComponent } from './svg-text-icon-key'; export { SvgTextIconKeyComponent } from './svg-text-icon-key';
export { SvgTwoLineTextKeyComponent } from './svg-two-line-text-key'; export { SvgTwoLineTextKeyComponent } from './svg-two-line-text-key';
export { SvgSecondaryRoleComponent } from './svg-secondary-role';

View File

@@ -11,3 +11,10 @@
[attr.font-size]="11"> [attr.font-size]="11">
<tspan [attr.x]="spanX">{{ text }}</tspan> <tspan [attr.x]="spanX">{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 499 B

View File

@@ -1,15 +1,19 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-icon-text-key]', selector: 'g[svg-icon-text-key]',
templateUrl: './svg-icon-text-key.component.html', templateUrl: './svg-icon-text-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgIconTextKeyComponent implements OnInit { export class SvgIconTextKeyComponent implements OnChanges {
@Input() width: number; @Input() width: number;
@Input() height: number; @Input() height: number;
@Input() icon: string; @Input() icon: string;
@Input() text: string; @Input() text: string;
@Input() secondaryText: string;
useWidth: number; useWidth: number;
useHeight: number; useHeight: number;
@@ -17,16 +21,33 @@ export class SvgIconTextKeyComponent implements OnInit {
useY: number; useY: number;
textY: number; textY: number;
spanX: number; spanX: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { constructor() {
} }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.calculatePositions();
}
private calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.useWidth = this.width / 3; this.useWidth = this.width / 3;
this.useHeight = this.height / 3; this.useHeight = this.height / 3;
this.useX = (this.width > 2 * this.height) ? 0 : this.width / 3; this.useX = (this.width > 2 * this.height) ? 0 : this.width / 3;
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 10; this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 10;
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height * 0.6; this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height * 0.6;
this.spanX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 2; this.spanX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 2;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -25,34 +25,40 @@
<svg:g svg-keystroke-key *ngSwitchCase="enumLabelTypes.KeystrokeKey" <svg:g svg-keystroke-key *ngSwitchCase="enumLabelTypes.KeystrokeKey"
[height]="height" [height]="height"
[width]="width" [width]="width"
[keystrokeAction]="labelSource"> [keystrokeAction]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-one-line-text-key *ngSwitchCase="enumLabelTypes.OneLineText" <svg:g svg-one-line-text-key *ngSwitchCase="enumLabelTypes.OneLineText"
[height]="height" [height]="height"
[width]="width" [width]="width"
[text]="labelSource"> [text]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-two-line-text-key *ngSwitchCase="enumLabelTypes.TwoLineText" <svg:g svg-two-line-text-key *ngSwitchCase="enumLabelTypes.TwoLineText"
[height]="height" [height]="height"
[width]="width" [width]="width"
[texts]="labelSource"> [texts]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-text-icon-key *ngSwitchCase="enumLabelTypes.TextIcon" <svg:g svg-text-icon-key *ngSwitchCase="enumLabelTypes.TextIcon"
[height]="height" [height]="height"
[width]="width" [width]="width"
[text]="labelSource.text" [text]="labelSource.text"
[icon]="labelSource.icon"> [icon]="labelSource.icon"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-icon-text-key *ngSwitchCase="enumLabelTypes.IconText" <svg:g svg-icon-text-key *ngSwitchCase="enumLabelTypes.IconText"
[height]="height" [height]="height"
[width]="width" [width]="width"
[icon]="labelSource.icon" [icon]="labelSource.icon"
[text]="labelSource.text"> [text]="labelSource.text"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-single-icon-key *ngSwitchCase="enumLabelTypes.SingleIcon" <svg:g svg-single-icon-key *ngSwitchCase="enumLabelTypes.SingleIcon"
[height]="height" [height]="height"
[width]="width" [width]="width"
[icon]="labelSource"> [icon]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-switch-keymap-key *ngSwitchCase="enumLabelTypes.SwitchKeymap" <svg:g svg-switch-keymap-key *ngSwitchCase="enumLabelTypes.SwitchKeymap"
[height]="height" [height]="height"

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -26,6 +26,9 @@ import { MapperService } from '../../../../services/mapper.service';
import { AppState } from '../../../../store'; import { AppState } from '../../../../store';
import { getMacros } from '../../../../store/reducers/user-configuration'; import { getMacros } from '../../../../store/reducers/user-configuration';
import { SvgKeyCaptureEvent, SvgKeyClickEvent } from '../../../../models/svg-key-events';
import { OperatingSystem } from '../../../../models/operating-system';
import { KeyModifierModel } from '../../../../models/key-modifier-model';
enum LabelTypes { enum LabelTypes {
KeystrokeKey, KeystrokeKey,
@@ -82,8 +85,8 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
@Input() capturingEnabled: boolean; @Input() capturingEnabled: boolean;
@Input() active: boolean; @Input() active: boolean;
@Output() keyClick = new EventEmitter(); @Output() keyClick = new EventEmitter<SvgKeyClickEvent>();
@Output() capture = new EventEmitter(); @Output() capture = new EventEmitter<SvgKeyCaptureEvent>();
enumLabelTypes = LabelTypes; enumLabelTypes = LabelTypes;
@@ -93,9 +96,14 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
labelType: LabelTypes; labelType: LabelTypes;
labelSource: any; labelSource: any;
secondaryText: string;
macros: Macro[]; macros: Macro[];
private subscription: Subscription; private subscription: Subscription;
private scanCodePressed: boolean; private scanCodePressed: boolean;
private pressedShiftLocation = -1;
private pressedAltLocation = -1;
private altPressed = false;
private shiftPressed = false;
constructor( constructor(
private mapper: MapperService, private mapper: MapperService,
@@ -112,15 +120,21 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
this.scanCodePressed = false; this.scanCodePressed = false;
} }
@HostListener('click') @HostListener('click', ['$event'])
onClick() { onClick(e: MouseEvent) {
this.reset(); this.reset();
this.keyClick.emit(this.element.nativeElement); this.keyClick.emit({
keyTarget: this.element.nativeElement,
shiftPressed: e.shiftKey,
altPressed: e.altKey
});
this.pressedShiftLocation = -1;
this.pressedAltLocation = -1;
} }
@HostListener('mousedown', ['$event']) @HostListener('mousedown', ['$event'])
onMouseDown(e: MouseEvent) { onMouseDown(e: MouseEvent) {
if ((e.which === 2 || e.button === 1) && this.capturingEnabled) { if ((e.which === 2 || e.button === 2) && this.capturingEnabled) {
e.preventDefault(); e.preventDefault();
this.renderer.invokeElementMethod(this.element.nativeElement, 'focus'); this.renderer.invokeElementMethod(this.element.nativeElement, 'focus');
@@ -129,13 +143,23 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} else { } else {
this.recording = true; this.recording = true;
this.recordAnimation = 'active'; this.recordAnimation = 'active';
this.shiftPressed = e.shiftKey;
this.altPressed = e.altKey;
} }
} }
} }
@HostListener('keyup', ['$event']) @HostListener('document:keyup', ['$event'])
onKeyUp(e: KeyboardEvent) { onKeyUp(e: KeyboardEvent) {
if (this.scanCodePressed) { if (e.keyCode === 18 && this.pressedAltLocation > -1) {
this.pressedAltLocation = -1;
e.preventDefault();
}
else if (e.keyCode === 16 && this.pressedShiftLocation > -1) {
this.pressedShiftLocation = -1;
e.preventDefault();
}
else if (this.scanCodePressed) {
e.preventDefault(); e.preventDefault();
this.scanCodePressed = false; this.scanCodePressed = false;
} else if (this.recording) { } else if (this.recording) {
@@ -144,7 +168,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
@HostListener('keydown', ['$event']) @HostListener('document:keydown', ['$event'])
onKeyDown(e: KeyboardEvent) { onKeyDown(e: KeyboardEvent) {
const code: number = e.keyCode; const code: number = e.keyCode;
@@ -152,11 +176,29 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
e.preventDefault(); e.preventDefault();
if (this.captureService.hasMap(code)) { if (this.captureService.hasMap(code)) {
// If the Alt or Shift key not released after start the capturing
// then add them as a modifier
if (this.pressedShiftLocation > -1) {
this.captureService.setModifier((this.pressedShiftLocation === 1), 16);
}
if (this.pressedAltLocation > -1) {
this.captureService.setModifier((this.pressedAltLocation === 1), 18);
}
this.saveScanCode(this.captureService.getMap(code)); this.saveScanCode(this.captureService.getMap(code));
this.scanCodePressed = true; this.scanCodePressed = true;
} else { } else {
this.captureService.setModifier((e.location === 1), code); this.captureService.setModifier((e.location === 1), code);
} }
} else {
if (e.keyCode === 16) {
this.pressedShiftLocation = e.location;
}
if (e.keyCode === 18) {
this.pressedAltLocation = e.location;
}
} }
} }
@@ -198,36 +240,40 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
this.recording = false; this.recording = false;
this.changeAnimation = 'inactive'; this.changeAnimation = 'inactive';
this.captureService.initModifiers(); this.captureService.initModifiers();
this.shiftPressed = false;
this.altPressed = false;
} }
private saveScanCode(code = 0) { private saveScanCode(code = 0) {
this.recording = false; const left: KeyModifierModel[] = this.captureService.getModifiers(true);
this.changeAnimation = 'inactive'; const right: KeyModifierModel[] = this.captureService.getModifiers(false);
const left: boolean[] = this.captureService.getModifiers(true);
const right: boolean[] = this.captureService.getModifiers(false);
this.capture.emit({ this.capture.emit({
captured: {
code, code,
left, left,
right right
},
shiftPressed: this.shiftPressed,
altPressed: this.altPressed
}); });
this.captureService.initModifiers(); this.reset();
} }
private setLabels(): void { private setLabels(): void {
if (!this.keyAction) {
this.labelSource = undefined;
this.labelType = LabelTypes.OneLineText; this.labelType = LabelTypes.OneLineText;
this.labelSource = undefined;
this.secondaryText = undefined;
if (!this.keyAction) {
return; return;
} }
this.labelType = LabelTypes.OneLineText;
if (this.keyAction instanceof KeystrokeAction) { if (this.keyAction instanceof KeystrokeAction) {
const keyAction: KeystrokeAction = this.keyAction as KeystrokeAction; const keyAction: KeystrokeAction = this.keyAction as KeystrokeAction;
let newLabelSource: string[]; let newLabelSource: string[];
this.secondaryText = this.mapper.getSecondaryRoleText(keyAction.secondaryRoleAction);
if (!keyAction.hasActiveModifier() && keyAction.hasScancode()) { if (!keyAction.hasActiveModifier() && keyAction.hasScancode()) {
const scancode: number = keyAction.scancode; const scancode: number = keyAction.scancode;
@@ -245,29 +291,32 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
} else if (keyAction.hasOnlyOneActiveModifier() && !keyAction.hasScancode()) { } else if (keyAction.hasOnlyOneActiveModifier() && !keyAction.hasScancode()) {
newLabelSource = [];
switch (keyAction.modifierMask) { switch (keyAction.modifierMask) {
case KeyModifiers.leftCtrl: case KeyModifiers.leftCtrl:
case KeyModifiers.rightCtrl: case KeyModifiers.rightCtrl:
newLabelSource.push('Ctrl'); this.labelSource = ['Ctrl'];
break; break;
case KeyModifiers.leftShift: case KeyModifiers.leftShift:
case KeyModifiers.rightShift: case KeyModifiers.rightShift:
newLabelSource.push('Shift'); this.labelSource = ['Shift'];
break; break;
case KeyModifiers.leftAlt: case KeyModifiers.leftAlt:
case KeyModifiers.rightAlt: case KeyModifiers.rightAlt:
newLabelSource.push('Alt'); this.labelSource = [this.mapper.getOsSpecificText('Alt')];
break; break;
case KeyModifiers.leftGui: case KeyModifiers.leftGui:
case KeyModifiers.rightGui: case KeyModifiers.rightGui:
newLabelSource.push('Super'); if (this.mapper.getOperatingSystem() === OperatingSystem.Windows) {
this.labelSource = this.mapper.getIcon('command');
this.labelType = LabelTypes.SingleIcon;
} else {
this.labelSource = [this.mapper.getOsSpecificText('Super')];
}
break; break;
default: default:
newLabelSource.push('Undefined'); this.labelSource = ['Undefined'];
break; break;
} }
this.labelSource = newLabelSource;
} else { } else {
this.labelType = LabelTypes.KeystrokeKey; this.labelType = LabelTypes.KeystrokeKey;
this.labelSource = this.keyAction; this.labelSource = this.keyAction;

View File

@@ -13,7 +13,8 @@
<svg:g svg-two-line-text-key *ngSwitchCase="'two-line'" <svg:g svg-two-line-text-key *ngSwitchCase="'two-line'"
[height]="height" [height]="height"
[width]="width" [width]="width"
[texts]="labelSource"> [texts]="labelSource"
[secondaryText]="subComponentSecondaryRoleText">
</svg:g> </svg:g>
</svg> </svg>
<svg [attr.viewBox]="viewBox" [attr.width]="modifierContainer.width" [attr.height]="modifierContainer.height" [attr.x]="modifierContainer.x" <svg [attr.viewBox]="viewBox" [attr.width]="modifierContainer.width" [attr.height]="modifierContainer.height" [attr.x]="modifierContainer.x"
@@ -28,10 +29,19 @@
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y" <svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y"
preserveAspectRatio="none" [class.disabled]="option.disabled"> preserveAspectRatio="none" [class.disabled]="option.disabled">
<svg:use [attr.xlink:href]="modifierIconNames.option" /> <svg:use *ngIf="modifierIconNames.option" [attr.xlink:href]="modifierIconNames.option" />
<svg:text *ngIf="!modifierIconNames.option" [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">A</svg:text>
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y" <svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y"
preserveAspectRatio="none" [class.disabled]="command.disabled"> preserveAspectRatio="none" [class.disabled]="command.disabled">
<svg:use [attr.xlink:href]="modifierIconNames.command" /> <svg:use *ngIf="modifierIconNames.command" [attr.xlink:href]="modifierIconNames.command" />
<svg:text *ngIf="!modifierIconNames.command" [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">S</svg:text>
</svg> </svg>
</svg> </svg>
<svg:g svg-secondary-role
*ngIf="thisSecondaryRoleText"
[height]="20"
[width]="secondaryTextWidth"
[y]="secondaryTextY"
[text]="thisSecondaryRoleText">
</svg:g>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,7 +1,9 @@
import { Component, Input, OnChanges, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { KeyModifiers, KeystrokeAction } from 'uhk-common'; import { KeyModifiers, KeystrokeAction } from 'uhk-common';
import { MapperService } from '../../../../services/mapper.service'; import { MapperService } from '../../../../services/mapper.service';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
class SvgAttributes { class SvgAttributes {
width: number; width: number;
@@ -25,10 +27,11 @@ class SvgAttributes {
styleUrls: ['./svg-keystroke-key.component.scss'], styleUrls: ['./svg-keystroke-key.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgKeystrokeKeyComponent implements OnInit, OnChanges { export class SvgKeystrokeKeyComponent implements OnChanges {
@Input() height: number; @Input() height: number;
@Input() width: number; @Input() width: number;
@Input() keystrokeAction: KeystrokeAction; @Input() keystrokeAction: KeystrokeAction;
@Input() secondaryText: string;
viewBox: string; viewBox: string;
textContainer: SvgAttributes; textContainer: SvgAttributes;
@@ -46,6 +49,11 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
option?: string, option?: string,
command?: string command?: string
}; };
secondaryTextY: number;
secondaryTextWidth: number;
secondaryHeight: number;
thisSecondaryRoleText: string;
subComponentSecondaryRoleText: string;
constructor(private mapper: MapperService) { constructor(private mapper: MapperService) {
this.modifierIconNames = {}; this.modifierIconNames = {};
@@ -57,15 +65,75 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
this.command = new SvgAttributes(); this.command = new SvgAttributes();
} }
ngOnInit() { ngOnChanges() {
this.calculatePositions();
}
private calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
this.thisSecondaryRoleText = this.secondaryText;
this.subComponentSecondaryRoleText = null;
const bottomSideMode: boolean = this.width < this.height * 1.8;
const isRectangleAsSecondaryRole = isRectangleAsSecondaryRoleKey(this.width, this.height);
if (this.secondaryText && isRectangleAsSecondaryRole) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
if (this.keystrokeAction.hasScancode()) {
const scancode: number = this.keystrokeAction.scancode;
this.labelSource = this.mapper.scanCodeToSvgImagePath(scancode, this.keystrokeAction.type);
if (this.labelSource) {
this.labelType = 'icon';
} else {
let newLabelSource: string[];
newLabelSource = this.mapper.scanCodeToText(scancode, this.keystrokeAction.type);
if (newLabelSource) {
if (this.secondaryText && newLabelSource.length === 2) {
if (isRectangleAsSecondaryRole || bottomSideMode) {
this.labelSource = newLabelSource[0];
this.labelType = 'one-line';
} else {
this.labelSource = newLabelSource;
this.labelType = 'two-line';
this.thisSecondaryRoleText = null;
this.subComponentSecondaryRoleText = this.secondaryText;
}
}
else {
if (newLabelSource.length === 1) {
this.labelSource = newLabelSource[0];
this.labelType = 'one-line';
} else {
this.labelSource = newLabelSource;
this.labelType = 'two-line';
}
}
}
}
} else {
this.labelType = 'empty';
}
this.shift.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftShift | KeyModifiers.rightShift);
this.control.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftCtrl | KeyModifiers.rightCtrl);
this.option.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftAlt | KeyModifiers.rightAlt);
this.command.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftGui | KeyModifiers.rightGui);
this.secondaryHeight = this.secondaryText ? this.height / 4 : 0;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
this.viewBox = [0, 0, this.width, this.height].join(' '); this.viewBox = [0, 0, this.width, this.height].join(' ');
this.modifierIconNames.shift = this.mapper.getIcon('shift'); this.modifierIconNames.shift = this.mapper.getIcon('shift');
this.modifierIconNames.option = this.mapper.getIcon('option'); this.modifierIconNames.option = this.mapper.getIcon('option');
this.modifierIconNames.command = this.mapper.getIcon('command'); this.modifierIconNames.command = this.mapper.getIcon('command');
this.textContainer.y = 0;
const bottomSideMode: boolean = this.width < this.height * 1.8;
const heightWidthRatio = this.height / this.width; const heightWidthRatio = this.height / this.width;
this.secondaryTextWidth = this.width;
if (bottomSideMode) { if (bottomSideMode) {
const maxIconWidth = this.width / 4; const maxIconWidth = this.width / 4;
@@ -75,7 +143,7 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
const iconHeight = iconScalingFactor * maxIconHeight; const iconHeight = iconScalingFactor * maxIconHeight;
this.modifierContainer.width = this.width; this.modifierContainer.width = this.width;
this.modifierContainer.height = this.height / 5; this.modifierContainer.height = this.height / 5;
this.modifierContainer.y = this.height - this.modifierContainer.height; this.modifierContainer.y = this.height - this.modifierContainer.height - this.secondaryHeight;
this.shift.width = iconWidth; this.shift.width = iconWidth;
this.shift.height = iconHeight; this.shift.height = iconHeight;
this.shift.x = (maxIconWidth - iconWidth) / 2; this.shift.x = (maxIconWidth - iconWidth) / 2;
@@ -92,7 +160,7 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
this.command.height = iconHeight; this.command.height = iconHeight;
this.command.x = this.option.x + maxIconWidth; this.command.x = this.option.x + maxIconWidth;
this.command.y = this.shift.y; this.command.y = this.shift.y;
this.textContainer.y = -this.modifierContainer.height / 2; this.textContainer.y = -this.modifierContainer.height / 2 - this.secondaryHeight / 2;
} else { } else {
this.modifierContainer.width = this.width / 4; this.modifierContainer.width = this.width / 4;
this.modifierContainer.height = this.height; this.modifierContainer.height = this.height;
@@ -120,40 +188,11 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
this.command.x = this.option.x + this.width / 2; this.command.x = this.option.x + this.width / 2;
this.command.y = this.option.y; this.command.y = this.option.y;
this.textContainer.x = -this.modifierContainer.width / 2; this.textContainer.x = -this.modifierContainer.width / 2;
this.secondaryTextWidth = this.width - this.modifierContainer.width;
} }
this.textContainer.y -= textYModifier;
this.textContainer.width = this.width; this.textContainer.width = this.width;
this.textContainer.height = this.height; this.textContainer.height = this.height;
} }
ngOnChanges() {
if (this.keystrokeAction.hasScancode()) {
const scancode: number = this.keystrokeAction.scancode;
this.labelSource = this.mapper.scanCodeToSvgImagePath(scancode, this.keystrokeAction.type);
if (this.labelSource) {
this.labelType = 'icon';
} else {
let newLabelSource: string[];
newLabelSource = this.mapper.scanCodeToText(scancode, this.keystrokeAction.type);
if (newLabelSource) {
if (newLabelSource.length === 1) {
this.labelSource = newLabelSource[0];
this.labelType = 'one-line';
} else {
this.labelSource = newLabelSource;
this.labelType = 'two-line';
}
}
}
} else {
this.labelType = 'empty';
}
this.shift.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftShift | KeyModifiers.rightShift);
this.control.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftCtrl | KeyModifiers.rightCtrl);
this.option.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftAlt | KeyModifiers.rightAlt);
this.command.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftGui | KeyModifiers.rightGui);
}
} }

View File

@@ -1,6 +1,13 @@
<svg:text <svg:text
[attr.x]="0" [attr.x]="0"
[attr.y]="textY" [attr.y]="textY"
[attr.text-anchor]="'middle'"> [attr.text-anchor]="'middle'">
<tspan [attr.x]="spanX" dy="0">{{ text }}</tspan> <tspan [attr.x]="spanX" dy="0">{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -1,22 +1,43 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-one-line-text-key]', selector: 'g[svg-one-line-text-key]',
templateUrl: './svg-one-line-text-key.component.html', templateUrl: './svg-one-line-text-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgOneLineTextKeyComponent implements OnInit { export class SvgOneLineTextKeyComponent implements OnChanges {
@Input() height: number; @Input() height: number;
@Input() width: number; @Input() width: number;
@Input() text: string; @Input() text: string;
@Input() secondaryText: string;
textY: number; textY: number;
spanX: number; spanX: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { } constructor() { }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.textY = this.height / 2; this.calculatePositions();
}
calculatePositions() {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.textY = this.height / 2 - textYModifier;
this.spanX = this.width / 2; this.spanX = this.width / 2;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -0,0 +1 @@
export * from './svg-secondary-role.component';

View File

@@ -0,0 +1,15 @@
<svg [attr.viewBox]="viewBox" [attr.width]="width" [attr.height]="height" [attr.y]="y">
<g id="secondaryContent" [attr.transform]="transform">
<svg viewBox="0 0 14 14" width="12" height="12" x="2" [attr.y]="textY / 3.5">
<ellipse stroke="#fff" rx="6.5" ry="6.5" cy="7" cx="7" stroke-width="1" fill-opacity="0"/>
<text text-anchor="start" font-family="Helvetica" font-size="12" y="7.8" x="4" stroke-width="0">2
</text>
</svg>
<text [attr.y]="textY"
[attr.x]="textIndent"
font-size="12"
text-anchor="start">
{{ text }}
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 667 B

View File

@@ -0,0 +1,52 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Input,
OnChanges,
OnInit,
SimpleChanges,
ViewChild
} from '@angular/core';
import { getContentWidth } from '../../../../util';
const SECONDARY_STYLE: CSSStyleDeclaration = {
font: '12px Helvetica'
} as any;
@Component({
selector: 'g[svg-secondary-role]',
templateUrl: './svg-secondary-role.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SvgSecondaryRoleComponent implements OnInit, OnChanges {
@Input() height: number;
@Input() width: number;
@Input() y: number;
@Input() text: string;
@ViewChild('secondary') svgElement: ElementRef;
viewBox: string;
textY: number;
transform: string;
textIndent = 16;
ngOnInit(): void {
this.viewBox = [0, 0, this.width, this.height].join(' ');
this.textY = this.height / 2 - 2;
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.text) {
this.calculateTextPosition();
}
}
private calculateTextPosition(): void {
const textWidth = getContentWidth(SECONDARY_STYLE, this.text) + this.textIndent;
const translateValue = Math.max(0, (this.width - textWidth) / 2);
this.transform = `translate(${ translateValue },0)`;
}
}

View File

@@ -4,3 +4,10 @@
[attr.x]="svgWidth" [attr.x]="svgWidth"
[attr.y]="svgHeight"> [attr.y]="svgHeight">
</svg:use> </svg:use>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 173 B

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -1,22 +1,42 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-single-icon-key]', selector: 'g[svg-single-icon-key]',
templateUrl: './svg-single-icon-key.component.html', templateUrl: './svg-single-icon-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgSingleIconKeyComponent implements OnInit { export class SvgSingleIconKeyComponent implements OnChanges {
@Input() width: number; @Input() width: number;
@Input() height: number; @Input() height: number;
@Input() icon: string; @Input() icon: string;
@Input() secondaryText: string;
svgHeight: number; svgHeight: number;
svgWidth: number; svgWidth: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { } constructor() { }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.calculatePositions();
}
calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.svgWidth = this.width / 3; this.svgWidth = this.width / 3;
this.svgHeight = this.height / 3; this.svgHeight = this.height / 3;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -10,3 +10,10 @@
[attr.x]="useX" [attr.x]="useX"
[attr.y]="useY"> [attr.y]="useY">
</svg:use> </svg:use>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -1,15 +1,19 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-text-icon-key]', selector: 'g[svg-text-icon-key]',
templateUrl: './svg-text-icon-key.component.html', templateUrl: './svg-text-icon-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgTextIconKeyComponent implements OnInit { export class SvgTextIconKeyComponent implements OnChanges {
@Input() width: number; @Input() width: number;
@Input() height: number; @Input() height: number;
@Input() text: string; @Input() text: string;
@Input() icon: string; @Input() icon: string;
@Input() secondaryText: string;
useWidth: number; useWidth: number;
useHeight: number; useHeight: number;
@@ -18,16 +22,33 @@ export class SvgTextIconKeyComponent implements OnInit {
textY: number; textY: number;
textAnchor: string; textAnchor: string;
spanX: number; spanX: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { } constructor() { }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.calculatePositions();
}
calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.useWidth = this.width / 3; this.useWidth = this.width / 3;
this.useHeight = this.height / 3; this.useHeight = this.height / 3;
this.useX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 3; this.useX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 3;
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2; this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2;
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height / 3; this.textY = ((this.width > 2 * this.height) ? this.height / 2 : this.height / 3) - textYModifier;
this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle'; this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle';
this.spanX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2; this.spanX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -9,3 +9,10 @@
dy="0" dy="0"
>{{ text }}</tspan> >{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -1,28 +1,51 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, SimpleChanges, OnChanges } from '@angular/core';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-two-line-text-key]', selector: 'g[svg-two-line-text-key]',
templateUrl: './svg-two-line-text-key.component.html', templateUrl: './svg-two-line-text-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgTwoLineTextKeyComponent implements OnInit { export class SvgTwoLineTextKeyComponent implements OnChanges {
@Input() height: number; @Input() height: number;
@Input() width: number; @Input() width: number;
@Input() texts: string[]; @Input() texts: string[];
@Input() secondaryText: string;
textY: number; textY: number;
spanX: number; spanX: number;
spanYs: number[]; spanYs: number[];
secondaryTextY: number;
secondaryHeight: number;
constructor() { constructor() {
this.spanYs = []; this.spanYs = [];
} }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.textY = this.height / 2; this.calculatePositions();
this.spanX = this.width / 2;
for (let i = 0; i < this.texts.length; ++i) {
this.spanYs.push((0.75 - i * 0.5) * this.height);
} }
calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
this.secondaryHeight = 0;
let textContainerHeight = this.height;
if (this.secondaryText) {
textYModifier = this.height / 5;
secondaryYModifier = 0;
this.secondaryHeight = this.height / 4;
textContainerHeight -= this.secondaryHeight;
}
this.textY = textContainerHeight / 2;
this.spanX = this.width / 2;
this.spanYs = [];
for (let i = 0; i < this.texts.length; ++i) {
this.spanYs.push((0.75 - i * 0.5) * textContainerHeight);
}
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -0,0 +1,3 @@
export const isRectangleAsSecondaryRoleKey = (width: number, height: number): boolean => {
return width > height * 2.4;
};

View File

@@ -2,6 +2,12 @@ import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from
import { KeyAction } from 'uhk-common'; import { KeyAction } from 'uhk-common';
import { SvgKeyboardKey } from '../keys'; import { SvgKeyboardKey } from '../keys';
import {
SvgKeyCaptureEvent,
SvgKeyClickEvent,
SvgModuleCaptureEvent,
SvgModuleKeyClickEvent
} from '../../../models/svg-key-events';
@Component({ @Component({
selector: 'g[svg-module]', selector: 'g[svg-module]',
@@ -17,18 +23,18 @@ export class SvgModuleComponent {
@Input() selected: boolean; @Input() selected: boolean;
@Input() keybindAnimationEnabled: boolean; @Input() keybindAnimationEnabled: boolean;
@Input() capturingEnabled: boolean; @Input() capturingEnabled: boolean;
@Output() keyClick = new EventEmitter(); @Output() keyClick = new EventEmitter<SvgModuleKeyClickEvent>();
@Output() keyHover = new EventEmitter(); @Output() keyHover = new EventEmitter();
@Output() capture = new EventEmitter(); @Output() capture = new EventEmitter<SvgModuleCaptureEvent>();
constructor() { constructor() {
this.keyboardKeys = []; this.keyboardKeys = [];
} }
onKeyClick(index: number, keyTarget: HTMLElement): void { onKeyClick(keyId: number, event: SvgKeyClickEvent): void {
this.keyClick.emit({ this.keyClick.emit({
index, ...event,
keyTarget keyId
}); });
} }
@@ -40,10 +46,10 @@ export class SvgModuleComponent {
}); });
} }
onCapture(index: number, captured: {code: number, left: boolean[], right: boolean[]}) { onCapture(keyId: number, event: SvgKeyCaptureEvent) {
this.capture.emit({ this.capture.emit({
index, ...event,
captured keyId
}); });
} }
} }

View File

@@ -8,9 +8,9 @@
[halvesSplit]="halvesSplit" [halvesSplit]="halvesSplit"
[keyboardLayout]="keyboardLayout" [keyboardLayout]="keyboardLayout"
[description]="keymap.description" [description]="keymap.description"
(keyClick)="onKeyClick($event.moduleId, $event.keyId, $event.keyTarget)" (keyClick)="onKeyClick($event)"
(keyHover)="onKeyHover($event.moduleId, $event.event, $event.over, $event.keyId)" (keyHover)="onKeyHover($event)"
(capture)="onCapture($event.moduleId, $event.keyId, $event.captured)" (capture)="onCapture($event)"
(descriptionChanged)="onDescriptionChanged($event)" (descriptionChanged)="onDescriptionChanged($event)"
></keyboard-slider> ></keyboard-slider>
@@ -22,6 +22,7 @@
[currentKeymap]="keymap" [currentKeymap]="keymap"
[currentLayer]="currentLayer" [currentLayer]="currentLayer"
[allowLayerDoubleTap]="allowLayerDoubleTap" [allowLayerDoubleTap]="allowLayerDoubleTap"
[remapInfo]="remapInfo"
(cancel)="hidePopover()" (cancel)="hidePopover()"
(remap)="onRemap($event)"></popover> (remap)="onRemap($event)"></popover>

View File

@@ -43,6 +43,13 @@ import { PopoverComponent } from '../../popover';
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum'; import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription'; import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
import { KeyActionRemap } from '../../../models/key-action-remap'; import { KeyActionRemap } from '../../../models/key-action-remap';
import {
SvgKeyboardCaptureEvent,
SvgKeyboardKeyClickEvent,
SvgKeyHoverEvent
} from '../../../models/svg-key-events';
import { RemapInfo } from '../../../models/remap-info';
import { mapLeftRigthModifierToKeyActionModifier } from '../../../util';
interface NameValuePair { interface NameValuePair {
name: string; name: string;
@@ -65,7 +72,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
@Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>(); @Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>();
@ViewChild(PopoverComponent, { read: ElementRef }) popover: ElementRef; @ViewChild(PopoverComponent, {read: ElementRef}) popover: ElementRef;
popoverShown: boolean; popoverShown: boolean;
keyEditConfig: { moduleId: number, keyId: number }; keyEditConfig: { moduleId: number, keyId: number };
@@ -82,6 +89,11 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
layers: Layer[]; layers: Layer[];
keyPosition: ClientRect; keyPosition: ClientRect;
wrapPosition: ClientRect; wrapPosition: ClientRect;
remapInfo: RemapInfo = {
remapOnAllKeymap: false,
remapOnAllLayer: false
};
private wrapHost: HTMLElement; private wrapHost: HTMLElement;
private keyElement: HTMLElement; private keyElement: HTMLElement;
@@ -131,7 +143,6 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
this.layers = this.keymap.layers; this.layers = this.keymap.layers;
if (keymapChanges.isFirstChange() || if (keymapChanges.isFirstChange() ||
keymapChanges.previousValue.abbreviation !== keymapChanges.currentValue.abbreviation) { keymapChanges.previousValue.abbreviation !== keymapChanges.currentValue.abbreviation) {
this.currentLayer = 0;
this.keybindAnimationEnabled = keymapChanges.isFirstChange(); this.keybindAnimationEnabled = keymapChanges.isFirstChange();
} else { } else {
this.keybindAnimationEnabled = true; this.keybindAnimationEnabled = true;
@@ -140,51 +151,50 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
} }
onKeyClick(moduleId: number, keyId: number, keyTarget: HTMLElement): void { onKeyClick(event: SvgKeyboardKeyClickEvent): void {
if (!this.popoverShown && this.popoverEnabled) { if (!this.popoverShown && this.popoverEnabled) {
this.keyEditConfig = { this.keyEditConfig = {
moduleId, moduleId: event.moduleId,
keyId keyId: event.keyId
};
this.selectedKey = {layerId: this.currentLayer, moduleId: event.moduleId, keyId: event.keyId};
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[event.moduleId].keyActions[event.keyId];
this.keyElement = event.keyTarget;
this.remapInfo = {
remapOnAllKeymap: event.shiftPressed,
remapOnAllLayer: event.altPressed
}; };
this.selectedKey = { layerId: this.currentLayer, moduleId, keyId };
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[moduleId].keyActions[keyId];
this.keyElement = keyTarget;
this.showPopover(keyActionToEdit); this.showPopover(keyActionToEdit);
} }
} }
onKeyHover(moduleId: number, event: MouseEvent, over: boolean, keyId: number): void { onKeyHover(event: SvgKeyHoverEvent): void {
if (this.tooltipEnabled) { if (this.tooltipEnabled) {
const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[moduleId].keyActions[keyId]; const keyActionToEdit: KeyAction = this.layers[this.currentLayer].modules[event.moduleId].keyActions[event.keyId];
if (over) { if (event.over) {
this.showTooltip(keyActionToEdit, event); this.showTooltip(keyActionToEdit, event.event);
} else { } else {
this.hideTooltip(); this.hideTooltip();
} }
} }
} }
onCapture(moduleId: number, keyId: number, captured: { code: number, left: boolean[], right: boolean[] }): void { onCapture(event: SvgKeyboardCaptureEvent): void {
const keystrokeAction: KeystrokeAction = new KeystrokeAction(); const keystrokeAction: KeystrokeAction = new KeystrokeAction();
const modifiers = captured.left.concat(captured.right).map(x => x ? 1 : 0);
keystrokeAction.scancode = captured.code; keystrokeAction.scancode = event.captured.code;
keystrokeAction.modifierMask = 0; keystrokeAction.modifierMask = mapLeftRigthModifierToKeyActionModifier(event.captured.left, event.captured.right);
for (let i = 0; i < modifiers.length; ++i) {
keystrokeAction.modifierMask |= modifiers[i] << this.mapper.modifierMapper(i);
}
this.store.dispatch( this.store.dispatch(
KeymapActions.saveKey( KeymapActions.saveKey(
this.keymap, this.keymap,
this.currentLayer, this.currentLayer,
moduleId, event.moduleId,
keyId, event.keyId,
{ {
remapOnAllKeymap: false, remapOnAllKeymap: event.shiftPressed,
remapOnAllLayer: false, remapOnAllLayer: event.altPressed,
action: keystrokeAction action: keystrokeAction
}) })
); );

View File

@@ -0,0 +1,25 @@
import { Directive, ElementRef, HostListener } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '../../store';
import { OpenUrlInNewWindowAction } from '../../store/actions/app';
@Directive({
selector: 'a[externalUrl]'
})
export class ExternalUrlDirective {
constructor(private el: ElementRef,
private store: Store<AppState>) {
}
@HostListener('click', ['$event'])
onClick($event: MouseEvent): void {
$event.preventDefault();
$event.stopPropagation();
const anchor = this.el.nativeElement as HTMLAnchorElement;
if (anchor.href) {
this.store.dispatch(new OpenUrlInNewWindowAction(anchor.href));
}
}
}

View File

@@ -0,0 +1 @@
export * from './external-url.directive';

View File

@@ -1,2 +1,3 @@
export * from './cancelable'; export * from './cancelable';
export * from './tooltip'; export * from './tooltip';
export * from './external-url';

View File

@@ -1,3 +1,4 @@
///<reference path="../../../../node_modules/@types/jquery/index.d.ts"/>
import { AfterContentInit, Directive, ElementRef, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core'; import { AfterContentInit, Directive, ElementRef, HostBinding, Input, OnChanges, SimpleChanges } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';

View File

@@ -0,0 +1,7 @@
import { KeyModifiers } from 'uhk-common';
export interface KeyModifierModel {
text: string;
value: KeyModifiers;
checked: boolean;
}

View File

@@ -0,0 +1,5 @@
export enum OperatingSystem {
Linux,
Mac,
Windows
}

View File

@@ -0,0 +1,4 @@
export interface RemapInfo {
remapOnAllKeymap: boolean;
remapOnAllLayer: boolean;
}

View File

@@ -0,0 +1,7 @@
export interface SelectOptionData {
id: string;
text: string;
disabled?: boolean;
children?: Array<SelectOptionData>;
additional?: any;
}

View File

@@ -0,0 +1,44 @@
import { KeyModifierModel } from './key-modifier-model';
export interface SvgKeyClickEvent {
keyTarget: HTMLElement;
shiftPressed?: boolean;
altPressed?: boolean;
}
export interface SvgModuleKeyClickEvent extends SvgKeyClickEvent {
keyId: number;
}
export interface SvgKeyboardKeyClickEvent extends SvgModuleKeyClickEvent {
moduleId: number;
}
export interface KeyCaptureData {
code: number;
left: KeyModifierModel[];
right: KeyModifierModel[];
}
export interface SvgKeyCaptureEvent {
captured: KeyCaptureData;
shiftPressed?: boolean;
altPressed?: boolean;
}
export interface SvgModuleCaptureEvent extends SvgKeyCaptureEvent {
keyId: number;
}
export interface SvgKeyboardCaptureEvent extends SvgModuleCaptureEvent {
moduleId: number;
}
export interface SvgKeyHoverEvent {
keyId: number;
event: MouseEvent;
over: boolean;
moduleId: number;
shiftPressed?: boolean;
altPressed?: boolean;
}

View File

@@ -1,14 +1,18 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MapperService } from './mapper.service';
import { KeyModifiers } from 'uhk-common';
import { KeyModifierModel } from '../models/key-modifier-model';
@Injectable() @Injectable()
export class CaptureService { export class CaptureService {
private mapping: Map<number, number>; private mapping: Map<number, number>;
private leftModifiers: Map<number, boolean>; private readonly leftModifiers: Map<number, KeyModifierModel>;
private rightModifiers: Map<number, boolean>; private readonly rightModifiers: Map<number, KeyModifierModel>;
constructor() { constructor(private mapper: MapperService) {
this.leftModifiers = new Map<number, boolean>(); this.leftModifiers = new Map<number, KeyModifierModel>();
this.rightModifiers = new Map<number, boolean>(); this.rightModifiers = new Map<number, KeyModifierModel>();
this.mapping = new Map<number, number>(); this.mapping = new Map<number, number>();
} }
@@ -21,26 +25,61 @@ export class CaptureService {
} }
public setModifier(left: boolean, code: number) { public setModifier(left: boolean, code: number) {
return left ? this.leftModifiers.set(code, true) : this.rightModifiers.set(code, true); const map = left ? this.leftModifiers : this.rightModifiers;
map.get(code).checked = true;
} }
public getModifiers(left: boolean) { public getModifiers(left: boolean) {
return left ? this.reMap(this.leftModifiers) : this.reMap(this.rightModifiers); const map = left ? this.leftModifiers : this.rightModifiers;
return Array.from(map.values());
} }
public initModifiers() { public initModifiers() {
this.leftModifiers.set(16, false); // Shift this.leftModifiers.set(16, {
this.leftModifiers.set(17, false); // Ctrl text: 'LShift',
this.leftModifiers.set(18, false); // Alt value: KeyModifiers.leftShift,
this.leftModifiers.set(91, false); // Super checked: false
});
this.leftModifiers.set(17, {
text: 'LCtrl',
value: KeyModifiers.leftCtrl,
checked: false
});
this.leftModifiers.set(18, {
text: this.mapper.getOsSpecificText('LAlt'),
value: KeyModifiers.leftAlt,
checked: false
});
this.leftModifiers.set(91, {
text: this.mapper.getOsSpecificText('LSuper'),
value: KeyModifiers.leftGui,
checked: false
});
this.rightModifiers.set(16, false); // Shift this.rightModifiers.set(16, {
this.rightModifiers.set(17, false); // Ctrl text: 'RShift',
this.rightModifiers.set(18, false); // Alt value: KeyModifiers.rightShift,
this.rightModifiers.set(91, false); // Super checked: false
});
this.rightModifiers.set(17, {
text: 'RCtrl',
value: KeyModifiers.rightCtrl,
checked: false
});
this.rightModifiers.set(18, {
text: this.mapper.getOsSpecificText('RAlt'),
value: KeyModifiers.rightAlt,
checked: false
});
this.rightModifiers.set(91, {
text: this.mapper.getOsSpecificText('RSuper'),
value: KeyModifiers.rightGui,
checked: false
});
} }
public populateMapping () { public populateMapping() {
this.mapping.set(8, 42); // Backspace this.mapping.set(8, 42); // Backspace
this.mapping.set(9, 43); // Tab this.mapping.set(9, 43); // Tab
this.mapping.set(13, 40); // Enter this.mapping.set(13, 40); // Enter
@@ -136,8 +175,4 @@ export class CaptureService {
this.mapping.set(221, 48); // Close bracket this.mapping.set(221, 48); // Close bracket
this.mapping.set(222, 52); // Single quote this.mapping.set(222, 52); // Single quote
} }
private reMap(value: Map<number, boolean>): boolean[] {
return [value.get(16), value.get(17), value.get(91), value.get(18)];
}
} }

View File

@@ -50,6 +50,10 @@ export class DeviceRendererService {
this.ipcRenderer.send(IpcEvents.device.recoveryDevice); this.ipcRenderer.send(IpcEvents.device.recoveryDevice);
} }
enableUsbStackTest(): void {
this.ipcRenderer.send(IpcEvents.device.enableUsbStackTest);
}
private registerEvents(): void { private registerEvents(): void {
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => { this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => {
this.dispachStoreAction(new ConnectionStateChangedAction(arg)); this.dispachStoreAction(new ConnectionStateChangedAction(arg));

View File

@@ -1,22 +1,40 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { KeystrokeType } from 'uhk-common'; import { Store } from '@ngrx/store';
import { KeyModifiers, KeystrokeType, SecondaryRoleAction } from 'uhk-common';
import { Subscription } from 'rxjs/Subscription';
import { AppState, getOperatingSystem } from '../store';
import { OperatingSystem } from '../models/operating-system';
import { KeyModifierModel } from '../models/key-modifier-model';
@Injectable() @Injectable()
export class MapperService { export class MapperService {
private basicScanCodeTextMap: Map<number, string[]>; private basicScanCodeTextMap: Map<number, string[]>;
private mediaScanCodeTextMap: Map<number, string[]>; private mediaScanCodeTextMap: Map<number, string[]>;
private sytemScanCodeTextMap: Map<number, string[]>; private systemScanCodeTextMap: Map<number, string[]>;
private basicScancodeIcons: Map<number, string>; private basicScancodeIcons: Map<number, string>;
private mediaScancodeIcons: Map<number, string>; private mediaScancodeIcons: Map<number, string>;
private systemScancodeIcons: Map<number, string>; private systemScancodeIcons: Map<number, string>;
private nameToFileName: Map<string, string>; private nameToFileName: Map<string, string>;
private osSpecificTexts: Map<string, string>;
private secondaryRoleTexts: Map<number, string>;
constructor() { private operatingSystem: OperatingSystem;
private osSubscription: Subscription;
constructor(private store: Store<AppState>) {
this.osSubscription = store
.select(getOperatingSystem)
.subscribe(os => {
this.operatingSystem = os;
this.initOsSpecificText();
this.initScanCodeTextMap(); this.initScanCodeTextMap();
this.initScancodeIcons(); this.initScancodeIcons();
this.initNameToFileNames(); this.initNameToFileNames();
this.initSecondaryRoleTexts();
});
} }
public scanCodeToText(scanCode: number, type: KeystrokeType = KeystrokeType.basic): string[] { public scanCodeToText(scanCode: number, type: KeystrokeType = KeystrokeType.basic): string[] {
@@ -27,7 +45,7 @@ export class MapperService {
map = this.mediaScanCodeTextMap; map = this.mediaScanCodeTextMap;
break; break;
case KeystrokeType.system: case KeystrokeType.system:
map = this.sytemScanCodeTextMap; map = this.systemScanCodeTextMap;
break; break;
default: default:
map = this.basicScanCodeTextMap; map = this.basicScanCodeTextMap;
@@ -79,7 +97,10 @@ export class MapperService {
} }
public getIcon(iconName: string): string { public getIcon(iconName: string): string {
return 'assets/compiled_sprite.svg#' + this.nameToFileName.get(iconName); const mappedIconName = this.nameToFileName.get(iconName);
if (mappedIconName) {
return 'assets/compiled_sprite.svg#' + mappedIconName;
}
} }
public modifierMapper(x: number) { public modifierMapper(x: number) {
@@ -90,6 +111,87 @@ export class MapperService {
} }
} }
public getOperatingSystem(): OperatingSystem {
return this.operatingSystem;
}
public getOsSpecificText(key: string): string {
const text = this.osSpecificTexts.get(key);
return text ? text : key;
}
public getSecondaryRoleText(secondaryRoleAction: SecondaryRoleAction): string {
return this.secondaryRoleTexts.get(secondaryRoleAction);
}
public getLeftKeyModifiers(): KeyModifierModel[] {
return [
{
text: 'LShift',
value: KeyModifiers.leftShift,
checked: false
},
{
text: 'LCtrl',
value: KeyModifiers.leftCtrl,
checked: false
},
{
text: this.getOsSpecificText('LAlt'),
value: KeyModifiers.leftAlt,
checked: false
},
{
text: this.getOsSpecificText('LSuper'),
value: KeyModifiers.leftGui,
checked: false
}
];
}
public getRightKeyModifiers(): KeyModifierModel[] {
return [
{
text: 'RShift',
value: KeyModifiers.rightShift,
checked: false
},
{
text: 'RCtrl',
value: KeyModifiers.rightCtrl,
checked: false
},
{
text: this.getOsSpecificText('RAlt'),
value: KeyModifiers.rightAlt,
checked: false
},
{
text: this.getOsSpecificText('RSuper'),
value: KeyModifiers.rightGui,
checked: false
}
];
}
private initOsSpecificText(): void {
this.osSpecificTexts = new Map<string, string>();
if (this.operatingSystem === OperatingSystem.Mac) {
this.osSpecificTexts.set('Enter', 'Return');
this.osSpecificTexts.set('Alt', 'Option');
this.osSpecificTexts.set('Super', 'Cmd');
this.osSpecificTexts.set('LSuper', 'LCmd');
this.osSpecificTexts.set('RSuper', 'RCmd');
this.osSpecificTexts.set('LAlt', 'LOption');
this.osSpecificTexts.set('RAlt', 'ROption');
} else if (this.operatingSystem === OperatingSystem.Windows) {
this.osSpecificTexts.set('LSuper', 'LWindows');
this.osSpecificTexts.set('RSuper', 'RWindows');
}
}
// TODO: read the mapping from JSON // TODO: read the mapping from JSON
private initScanCodeTextMap(): void { private initScanCodeTextMap(): void {
this.basicScanCodeTextMap = new Map<number, string[]>(); this.basicScanCodeTextMap = new Map<number, string[]>();
@@ -129,7 +231,7 @@ export class MapperService {
this.basicScanCodeTextMap.set(37, ['8', '*']); this.basicScanCodeTextMap.set(37, ['8', '*']);
this.basicScanCodeTextMap.set(38, ['9', '(']); this.basicScanCodeTextMap.set(38, ['9', '(']);
this.basicScanCodeTextMap.set(39, ['0', ')']); this.basicScanCodeTextMap.set(39, ['0', ')']);
this.basicScanCodeTextMap.set(40, ['Enter']); this.basicScanCodeTextMap.set(40, [this.getOsSpecificText('Enter')]);
this.basicScanCodeTextMap.set(41, ['Esc']); this.basicScanCodeTextMap.set(41, ['Esc']);
this.basicScanCodeTextMap.set(42, ['Backspace']); this.basicScanCodeTextMap.set(42, ['Backspace']);
this.basicScanCodeTextMap.set(43, ['Tab']); this.basicScanCodeTextMap.set(43, ['Tab']);
@@ -177,7 +279,7 @@ export class MapperService {
this.basicScanCodeTextMap.set(85, ['*']); this.basicScanCodeTextMap.set(85, ['*']);
this.basicScanCodeTextMap.set(86, ['-']); this.basicScanCodeTextMap.set(86, ['-']);
this.basicScanCodeTextMap.set(87, ['+']); this.basicScanCodeTextMap.set(87, ['+']);
this.basicScanCodeTextMap.set(88, ['Enter']); this.basicScanCodeTextMap.set(88, [this.getOsSpecificText('Enter')]);
this.basicScanCodeTextMap.set(89, ['end', '1']); this.basicScanCodeTextMap.set(89, ['end', '1']);
this.basicScanCodeTextMap.set(90, ['2']); this.basicScanCodeTextMap.set(90, ['2']);
this.basicScanCodeTextMap.set(91, ['pgdn', '3']); this.basicScanCodeTextMap.set(91, ['pgdn', '3']);
@@ -224,10 +326,10 @@ export class MapperService {
this.mediaScanCodeTextMap.set(394, ['Launch Email Client']); this.mediaScanCodeTextMap.set(394, ['Launch Email Client']);
this.mediaScanCodeTextMap.set(402, ['Launch Calculator']); this.mediaScanCodeTextMap.set(402, ['Launch Calculator']);
this.sytemScanCodeTextMap = new Map<number, string[]>(); this.systemScanCodeTextMap = new Map<number, string[]>();
this.sytemScanCodeTextMap.set(129, ['Power Down']); this.systemScanCodeTextMap.set(129, ['Power Down']);
this.sytemScanCodeTextMap.set(130, ['Sleep']); this.systemScanCodeTextMap.set(130, ['Sleep']);
this.sytemScanCodeTextMap.set(131, ['Wake Up']); this.systemScanCodeTextMap.set(131, ['Wake Up']);
} }
private initScancodeIcons(): void { private initScancodeIcons(): void {
@@ -266,8 +368,12 @@ export class MapperService {
this.nameToFileName.set('switch-keymap', 'icon-kbd__mod--switch-keymap'); this.nameToFileName.set('switch-keymap', 'icon-kbd__mod--switch-keymap');
this.nameToFileName.set('macro', 'icon-icon__macro'); this.nameToFileName.set('macro', 'icon-icon__macro');
this.nameToFileName.set('shift', 'icon-kbd__default--modifier-shift'); this.nameToFileName.set('shift', 'icon-kbd__default--modifier-shift');
if (this.operatingSystem === OperatingSystem.Mac) {
this.nameToFileName.set('option', 'icon-kbd__default--modifier-option'); this.nameToFileName.set('option', 'icon-kbd__default--modifier-option');
this.nameToFileName.set('command', 'icon-kbd__default--modifier-command'); this.nameToFileName.set('command', 'icon-kbd__default--modifier-command');
} else if (this.operatingSystem === OperatingSystem.Windows) {
this.nameToFileName.set('command', 'icon-kbd__default--modifier-windows');
}
this.nameToFileName.set('mouse', 'icon-kbd__mouse'); this.nameToFileName.set('mouse', 'icon-kbd__mouse');
this.nameToFileName.set('left-arrow', 'icon-kbd__mod--arrow-left'); this.nameToFileName.set('left-arrow', 'icon-kbd__mod--arrow-left');
this.nameToFileName.set('right-arrow', 'icon-kbd__mod--arrow-right'); this.nameToFileName.set('right-arrow', 'icon-kbd__mod--arrow-right');
@@ -279,4 +385,18 @@ export class MapperService {
this.nameToFileName.set('scroll-up', 'icon-kbd__mouse--scroll-up'); this.nameToFileName.set('scroll-up', 'icon-kbd__mouse--scroll-up');
} }
private initSecondaryRoleTexts(): void {
this.secondaryRoleTexts = new Map<number, string>();
this.secondaryRoleTexts.set(0, 'LCtrl');
this.secondaryRoleTexts.set(1, 'LShift');
this.secondaryRoleTexts.set(2, 'LAlt');
this.secondaryRoleTexts.set(3, 'LSuper');
this.secondaryRoleTexts.set(4, 'RCtrl');
this.secondaryRoleTexts.set(5, 'RShift');
this.secondaryRoleTexts.set(6, 'RAlt');
this.secondaryRoleTexts.set(7, 'RSuper');
this.secondaryRoleTexts.set(8, 'Mod');
this.secondaryRoleTexts.set(9, 'Fn');
this.secondaryRoleTexts.set(10, 'Mouse');
}
} }

View File

@@ -689,8 +689,9 @@
null, null,
{ {
"keyActionType": "keystroke", "keyActionType": "keystroke",
"type": "system", "type": "media",
"scancode": 130 "scancode": 184,
"modifierMask": 12
}, },
null, null,
{ {
@@ -887,7 +888,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -958,7 +963,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,
@@ -1874,7 +1883,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -1945,7 +1958,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,
@@ -2653,8 +2670,9 @@
null, null,
{ {
"keyActionType": "keystroke", "keyActionType": "keystroke",
"type": "system", "type": "media",
"scancode": 130 "scancode": 184,
"modifierMask": 12
}, },
null, null,
{ {
@@ -2854,7 +2872,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -2919,7 +2941,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,
@@ -3838,7 +3864,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -3903,7 +3933,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,
@@ -4611,8 +4645,9 @@
null, null,
{ {
"keyActionType": "keystroke", "keyActionType": "keystroke",
"type": "system", "type": "media",
"scancode": 130 "scancode": 184,
"modifierMask": 12
}, },
null, null,
{ {
@@ -4806,7 +4841,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -4871,7 +4910,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,
@@ -5787,7 +5830,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -5852,7 +5899,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,
@@ -6752,7 +6803,11 @@
null, null,
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 32
},
null, null,
{ {
"keyActionType": "mouse", "keyActionType": "mouse",
@@ -6813,7 +6868,11 @@
}, },
null, null,
null, null,
null, {
"keyActionType": "keystroke",
"type": "basic",
"modifierMask": 2
},
null, null,
null, null,
null, null,

View File

@@ -6,7 +6,7 @@ import { NotifierModule } from 'angular-notifier';
import { ConfirmationPopoverModule } from 'angular-confirmation-popover'; import { ConfirmationPopoverModule } from 'angular-confirmation-popover';
import { DragulaModule } from 'ng2-dragula/ng2-dragula'; import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { Select2Module } from 'ng2-select2/ng2-select2'; import { NgxSelectModule } from '@ert78gb/ngx-select-ex';
import { NouisliderModule } from 'ng2-nouislider'; import { NouisliderModule } from 'ng2-nouislider';
import { ClipboardModule } from 'ngx-clipboard'; import { ClipboardModule } from 'ngx-clipboard';
@@ -59,6 +59,7 @@ import {
SvgMouseScrollKeyComponent, SvgMouseScrollKeyComponent,
SvgMouseSpeedKeyComponent, SvgMouseSpeedKeyComponent,
SvgOneLineTextKeyComponent, SvgOneLineTextKeyComponent,
SvgSecondaryRoleComponent,
SvgSingleIconKeyComponent, SvgSingleIconKeyComponent,
SvgSwitchKeymapKeyComponent, SvgSwitchKeymapKeyComponent,
SvgTextIconKeyComponent, SvgTextIconKeyComponent,
@@ -68,7 +69,7 @@ import { SvgModuleComponent } from './components/svg/module';
import { SvgKeyboardWrapComponent } from './components/svg/wrap'; import { SvgKeyboardWrapComponent } from './components/svg/wrap';
import { appRoutingProviders, routing } from './app.routes'; import { appRoutingProviders, routing } from './app.routes';
import { CancelableDirective, TooltipDirective } from './directives'; import { CancelableDirective, ExternalUrlDirective, TooltipDirective } from './directives';
import { SafeStylePipe } from './pipes'; import { SafeStylePipe } from './pipes';
import { CaptureService } from './services/capture.service'; import { CaptureService } from './services/capture.service';
@@ -109,6 +110,7 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard'; import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard';
import { FileUploadComponent } from './components/file-upload'; import { FileUploadComponent } from './components/file-upload';
import { AutoGrowInputComponent } from './components/auto-grow-input'; import { AutoGrowInputComponent } from './components/auto-grow-input';
import { HelpPageComponent } from './components/agent/help-page/help-page.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -183,7 +185,10 @@ import { AutoGrowInputComponent } from './components/auto-grow-input';
RestoreConfigurationComponent, RestoreConfigurationComponent,
RecoveryModeComponent, RecoveryModeComponent,
FileUploadComponent, FileUploadComponent,
AutoGrowInputComponent AutoGrowInputComponent,
HelpPageComponent,
ExternalUrlDirective,
SvgSecondaryRoleComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@@ -191,7 +196,7 @@ import { AutoGrowInputComponent } from './components/auto-grow-input';
FormsModule, FormsModule,
DragulaModule, DragulaModule,
routing, routing,
Select2Module, NgxSelectModule,
NouisliderModule, NouisliderModule,
NotifierModule.withConfig(angularNotifierConfig), NotifierModule.withConfig(angularNotifierConfig),
ConfirmationPopoverModule.forRoot({ ConfirmationPopoverModule.forRoot({

View File

@@ -28,7 +28,8 @@ export const ActionTypes = {
HAS_BACKUP_USER_CONFIGURATION: type(PREFIX + 'Store backup user configuration'), HAS_BACKUP_USER_CONFIGURATION: type(PREFIX + 'Store backup user configuration'),
RESTORE_CONFIGURATION_FROM_BACKUP: type(PREFIX + 'Restore configuration from backup'), RESTORE_CONFIGURATION_FROM_BACKUP: type(PREFIX + 'Restore configuration from backup'),
RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS: type(PREFIX + 'Restore configuration from backup success'), RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS: type(PREFIX + 'Restore configuration from backup success'),
RECOVERY_DEVICE: type(PREFIX + 'Recovery device') RECOVERY_DEVICE: type(PREFIX + 'Recovery device'),
ENABLE_USB_STACK_TEST: type(PREFIX + 'USB stack test')
}; };
export class SetPrivilegeOnLinuxAction implements Action { export class SetPrivilegeOnLinuxAction implements Action {
@@ -144,6 +145,10 @@ export class RecoveryDeviceAction implements Action {
type = ActionTypes.RECOVERY_DEVICE; type = ActionTypes.RECOVERY_DEVICE;
} }
export class EnableUsbStackTestAction implements Action {
type = ActionTypes.ENABLE_USB_STACK_TEST;
}
export type Actions export type Actions
= SetPrivilegeOnLinuxAction = SetPrivilegeOnLinuxAction
| SetPrivilegeOnLinuxReplyAction | SetPrivilegeOnLinuxReplyAction
@@ -166,4 +171,5 @@ export type Actions
| HasBackupUserConfigurationAction | HasBackupUserConfigurationAction
| RestoreUserConfigurationFromBackupSuccessAction | RestoreUserConfigurationFromBackupSuccessAction
| RecoveryDeviceAction | RecoveryDeviceAction
| EnableUsbStackTestAction
; ;

Some files were not shown because too many files have changed in this diff Show More