Compare commits

..

7 Commits

Author SHA1 Message Date
eneller
8aca73cd9c doc: begin sweave 2026-01-26 12:59:36 +01:00
eneller
e8bfcacacb doc: init R 2026-01-26 12:29:00 +01:00
eneller
86b67f36e4 refactor: enum for keyboards 2026-01-26 11:50:43 +01:00
eneller
93041a370e begin cli args 2026-01-26 11:14:28 +01:00
eneller
2b6ea24d5e chore: bump version 2026-01-25 20:12:29 +01:00
eneller
02e6b033c2 chore: remove demo 2026-01-25 20:00:23 +01:00
eneller
076816bd12 doc: readme 2026-01-24 18:52:01 +01:00
19 changed files with 3054 additions and 66 deletions

1
.gitignore vendored
View File

@@ -42,3 +42,4 @@ testem.log
Thumbs.db
TextTestExe/
data/

View File

@@ -15,6 +15,6 @@ Keyboard emulation on most systems requires workarounds for two issues:
- not receiving focus when clicked
- sending input
## Logging Setup
either use [TextTestExe](https://depts.washington.edu/acelab/proj/texttest/) (local program) or [TextTestPP](https://drustz.com/TextTestPP/) (online).
## Logging and Statistics
For logging, either use [TextTestExe](https://depts.washington.edu/acelab/proj/texttest/) (local program) or [TextTestPP](https://drustz.com/TextTestPP/) (online).
The theoretical framework is inspired by https://www.yorku.ca/mack/bit95.html.

1
doc/.Rprofile Normal file
View File

@@ -0,0 +1 @@
source("renv/activate.R")

374
doc/.gitignore vendored Normal file
View File

@@ -0,0 +1,374 @@
# Created by https://www.toptal.com/developers/gitignore/api/tex,r
# Edit at https://www.toptal.com/developers/gitignore?templates=tex,r
### R ###
# History files
.Rhistory
.Rapp.history
# Session Data files
.RData
.RDataTmp
# User-specific files
.Ruserdata
# Example code in package build process
*-Ex.R
# Output files from R CMD build
/*.tar.gz
# Output files from R CMD check
/*.Rcheck/
# RStudio files
.Rproj.user/
# produced vignettes
vignettes/*.html
vignettes/*.pdf
# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
.httr-oauth
# knitr and R markdown default cache directories
*_cache/
/cache/
# Temporary files created by R markdown
*.utf8.md
*.knit.md
# R Environment Variables
.Renviron
# pkgdown site
docs/
# translation temp files
po/*~
# RStudio Connect folder
rsconnect/
### R.Bookdown Stack ###
# R package: bookdown caching files
/*_files/
### TeX ###
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
*.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
*.lzo
*.lzs
*.slg
*.slo
*.sls
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplot
*.gnuplot
*.table
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.glog
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
# *.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# newpax
*.newpax
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# svg
svg-inkscape/
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# titletoc
*.ptc
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices and outlines
*.xyc
*.xyd
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# gummi
.*.swp
# KBibTeX
*~[0-9]*
# TeXnicCenter
*.tps
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
# Makeindex log files
*.lpz
# xwatermark package
*.xwm
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
# Uncomment the next line to have this generated file ignored.
#*Notes.bib
### TeX Patch ###
# LIPIcs / OASIcs
*.vtc
# glossaries
*.glstex
# End of https://www.toptal.com/developers/gitignore/api/tex,r
*.html
bib
# Sweave-specific
*.tex

13
doc/hci-doc.Rproj Normal file
View File

@@ -0,0 +1,13 @@
Version: 1.0
RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default
EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8
RnwWeave: knitr
LaTeX: pdfLaTeX

962
doc/renv.lock Normal file

File diff suppressed because one or more lines are too long

7
doc/renv/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
library/
local/
cellar/
lock/
python/
sandbox/
staging/

1403
doc/renv/activate.R Normal file

File diff suppressed because it is too large Load Diff

20
doc/renv/settings.json Normal file
View File

@@ -0,0 +1,20 @@
{
"bioconductor.version": null,
"external.libraries": [],
"ignored.packages": [],
"package.dependency.fields": [
"Imports",
"Depends",
"LinkingTo"
],
"ppm.enabled": null,
"ppm.ignored.urls": [],
"r.version": null,
"snapshot.dev": false,
"snapshot.type": "implicit",
"use.cache": true,
"vcs.ignore.cellar": true,
"vcs.ignore.library": true,
"vcs.ignore.local": true,
"vcs.manage.ignores": true
}

34
doc/report.Rnw Normal file
View File

@@ -0,0 +1,34 @@
\documentclass{article}
\begin{document}
\section{Abstract}\label{abstract}
\section{Introduction}\label{introduction}
\section{Keyboard Designs}\label{keyboard-designs}
\section{Experiment}\label{experiment}
\subsection{Participants}\label{participants}
\subsection{Apparatus}\label{apparatus}
\subsection{Procedure}\label{procedure}
\section{Results}\label{results}
\subsection{Descriptive Statistics}\label{descriptive-statistics}
\subsubsection{Objective Measures}\label{objective-measures}
\subsubsection{Subjective Measures}\label{subjective-measures}
\subsection{Inferential Statistics}\label{inferential-statistics}
\subsubsection{Objective Measures}\label{objective-measures-1}
\subsubsection{Subjective Measures}\label{subjective-measures-1}
\section{Discussion}\label{discussion}
\end{document}

16
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "keeb",
"version": "0.1.0",
"version": "1.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "keeb",
"version": "0.1.0",
"version": "1.0.1",
"dependencies": {
"@angular/animations": "^18.2.14",
"@angular/common": "^18.2.14",
@@ -17,6 +17,7 @@
"@angular/platform-browser-dynamic": "^18.2.14",
"@angular/router": "^18.2.14",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-cli": "^2.4.1",
"@tauri-apps/plugin-opener": "^2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
@@ -4581,6 +4582,15 @@
"node": ">= 10"
}
},
"node_modules/@tauri-apps/plugin-cli": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-cli/-/plugin-cli-2.4.1.tgz",
"integrity": "sha512-8JXofQFI5cmiGolh1PlU4hzE2YJgrgB1lyaztyBYiiMCy13luVxBXaXChYPeqMkUo46J1UadxvYdjRjj0E8zaw==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@tauri-apps/api": "^2.8.0"
}
},
"node_modules/@tauri-apps/plugin-opener": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz",
@@ -13399,6 +13409,7 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -14047,6 +14058,7 @@
"integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/bonjour": "^3.5.13",
"@types/connect-history-api-fallback": "^1.5.4",

View File

@@ -1,6 +1,6 @@
{
"name": "keeb",
"version": "0.1.0",
"version": "1.0.1",
"scripts": {
"ng": "ng",
"start": "ng serve",
@@ -18,16 +18,18 @@
"@angular/platform-browser": "^18.2.14",
"@angular/platform-browser-dynamic": "^18.2.14",
"@angular/router": "^18.2.14",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-cli": "^2.4.1",
"@tauri-apps/plugin-opener": "^2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.2",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2"
"zone.js": "~0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.21",
"@angular/cli": "^18.2.21",
"@angular/compiler-cli": "^18.2.14",
"@tauri-apps/cli": "^2",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
@@ -35,7 +37,6 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.4.0",
"@tauri-apps/cli": "^2"
"typescript": "~5.4.0"
}
}

117
src-tauri/Cargo.lock generated
View File

@@ -41,6 +41,56 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
version = "1.0.100"
@@ -456,6 +506,39 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "clap"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "combine"
version = "4.6.7"
@@ -1750,6 +1833,12 @@ dependencies = [
"once_cell",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.15"
@@ -1843,6 +1932,7 @@ dependencies = [
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-cli",
"tauri-plugin-opener",
"windows 0.62.2",
"x11",
@@ -2434,6 +2524,12 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "open"
version = "5.3.2"
@@ -3754,6 +3850,21 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-cli"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28e78fb2c09a81546bcd376d34db4bda5769270d00990daa9f0d6e7ac1107e25"
dependencies = [
"clap",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.17",
]
[[package]]
name = "tauri-plugin-opener"
version = "2.5.0"
@@ -4320,6 +4431,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.18.1"

View File

@@ -34,6 +34,9 @@ windows = { version = "0.62.2", features = [
[target.'cfg(target_os = "linux")'.dependencies]
x11 = { version = "2.21.0", features = ["xlib"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-cli = "2"
[profile.dev]
incremental = true # Compile your binary in smaller steps.

View File

@@ -0,0 +1,14 @@
{
"identifier": "desktop-capability",
"platforms": [
"macOS",
"windows",
"linux"
],
"windows": [
"main"
],
"permissions": [
"cli:default"
]
}

View File

@@ -19,25 +19,35 @@ fn send_key(key: String) -> Result<(), String> {
// Check if uppercase letter
if ch.is_uppercase() && ch.is_alphabetic() {
// Send Shift + lowercase letter
enigo.key(Key::Shift, Direction::Press)
enigo
.key(Key::Shift, Direction::Press)
.map_err(|e| format!("Shift press error: {}", e))?;
enigo.key(Key::Unicode(ch.to_lowercase().next().unwrap()), Direction::Click)
enigo
.key(
Key::Unicode(ch.to_lowercase().next().unwrap()),
Direction::Click,
)
.map_err(|e| format!("Key error: {}", e))?;
enigo.key(Key::Shift, Direction::Release)
enigo
.key(Key::Shift, Direction::Release)
.map_err(|e| format!("Shift release error: {}", e))?;
} else {
// Send character as-is (lowercase or non-letter)
enigo.key(Key::Unicode(ch), Direction::Click)
enigo
.key(Key::Unicode(ch), Direction::Click)
.map_err(|e| format!("Key error: {}", e))?;
}
} else {
// Special keys
match key.as_str() {
"Enter" => enigo.key(Key::Return, Direction::Click)
"Enter" => enigo
.key(Key::Return, Direction::Click)
.map_err(|e| format!("Key error: {}", e))?,
"Space" => enigo.key(Key::Space, Direction::Click)
"Space" => enigo
.key(Key::Space, Direction::Click)
.map_err(|e| format!("Key error: {}", e))?,
"Backspace" => enigo.key(Key::Backspace, Direction::Click)
"Backspace" => enigo
.key(Key::Backspace, Direction::Click)
.map_err(|e| format!("Key error: {}", e))?,
_ => return Err("Unknown key".to_string()),
}
@@ -48,14 +58,16 @@ fn send_key(key: String) -> Result<(), String> {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_cli::init())
.setup(|app| {
let window = app.get_webview_window("main").unwrap();
#[cfg(target_os = "windows")]
{
use windows::Win32::UI::WindowsAndMessaging::{SetWindowPos, HWND_TOPMOST, SWP_NOACTIVATE, GetWindowLongPtrW, SetWindowLongPtrW, GWL_EXSTYLE, WS_EX_NOACTIVATE};
use windows::Win32::Foundation::HWND;
#![allow(deprecated)]
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::{GetWindowLongPtrW, SetWindowLongPtrW, GWL_EXSTYLE, WS_EX_NOACTIVATE,};
unsafe {
if let Ok(RawWindowHandle::Win32(handle)) = window.raw_window_handle() {
@@ -82,8 +94,8 @@ pub fn run() {
#[cfg(target_os = "linux")]
{
use x11::xlib;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use x11::xlib;
unsafe {
let handle = window.raw_window_handle();

View File

@@ -26,6 +26,29 @@
"csp": null
}
},
"plugins": {
"cli":{
"description": "On-Screen keyboard for Human-Computer Interface studies",
"args": [
{
"name": "order",
"short": "o",
"takesValue": true,
"multiple": true
},
{
"name": "limit",
"short": "l",
"takesValue": true
},
{
"name": "demo",
"short": "d",
"takesValue": true
}
]
}
},
"bundle": {
"active": true,
"targets": "all",

View File

@@ -1,35 +1,28 @@
<main class="container">
<form class="row" (submit)="greet($event, greetInput.value)">
<input #greetInput id="greet-input" placeholder="Enter a name..." />
<button type="submit">Greet</button>
</form>
<p>{{ greetingMessage }}</p>
<!-- Layout Switcher -->
<div class="layout-controls">
<button (click)="switchLayout()" class="layout-button">
Switch Layout: {{ currentLayout.toUpperCase() }}
Switch Layout: {{ currentLayout.toString() }}
</button>
</div>
<!-- Conditional Rendering basierend auf currentLayout -->
<app-qwerty-keyboard
*ngIf="currentLayout === 'qwerty'"
*ngIf="currentLayout === Keyboards.QWERTY"
[shiftActive]="shiftActive"
(keyPressed)="handleKeyPress($event)"
(shiftToggled)="toggleShift()">
</app-qwerty-keyboard>
<app-dvorak-keyboard
*ngIf="currentLayout === 'dvorak'"
*ngIf="currentLayout === Keyboards.DVORAK"
[shiftActive]="shiftActive"
(keyPressed)="handleKeyPress($event)"
(shiftToggled)="toggleShift()">
</app-dvorak-keyboard>
<app-circle-keyboard
*ngIf="currentLayout === 'circle'"
*ngIf="currentLayout === Keyboards.CIRCLE"
[shiftActive]="shiftActive"
(keyPressed)="handleKeyPress($event)"
(shiftToggled)="toggleShift()">

View File

@@ -1,7 +1,8 @@
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { Component, ViewChild, ElementRef, AfterViewInit, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { invoke } from "@tauri-apps/api/core";
import { getMatches } from '@tauri-apps/plugin-cli';
import { QwertyKeyboardComponent } from './keyboards/qwerty-keyboard.component';
import { DvorakKeyboardComponent } from './keyboards/dvorak-keyboard.component';
import { CircleKeyboardComponent } from './keyboards/circle-keyboard.component';
@@ -17,42 +18,34 @@ import { CircleKeyboardComponent } from './keyboards/circle-keyboard.component';
CircleKeyboardComponent
],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
styleUrl: './app.component.css',
})
export class AppComponent implements AfterViewInit {
greetingMessage = "";
currentLayout: 'qwerty' | 'dvorak' | 'circle' = 'qwerty';
export class AppComponent implements OnInit {
Keyboards = Keyboards;
currentLayout: Keyboards = Keyboards.QWERTY;
shiftActive = false;
async ngOnInit() {
const cli = await getMatches();
}
@ViewChild('greetInput') inputElement!: ElementRef;
ngAfterViewInit() {
this.inputElement.nativeElement.focus();
}
greet(event: SubmitEvent, name: string): void {
event.preventDefault();
invoke<string>("greet", { name }).then((text) => {
this.greetingMessage = text;
});
}
toggleShift(): void {
this.shiftActive = !this.shiftActive;
}
switchLayout(): void {
if (this.currentLayout == 'qwerty'){
this.currentLayout = 'dvorak';
} else if (this.currentLayout == 'dvorak'){
this.currentLayout = 'circle';
} else if (this.currentLayout == 'circle'){
this.currentLayout = 'qwerty';
if (this.currentLayout === Keyboards.QWERTY){
this.currentLayout = Keyboards.DVORAK;
} else if (this.currentLayout === Keyboards.DVORAK){
this.currentLayout = Keyboards.CIRCLE;
} else if (this.currentLayout === Keyboards.CIRCLE){
this.currentLayout = Keyboards.QWERTY;
}
}
async handleKeyPress(key: string): Promise<void> {
this.inputElement.nativeElement.focus();
let finalKey = key;
if (key.length === 1) {
@@ -66,3 +59,8 @@ export class AppComponent implements AfterViewInit {
}
}
}
enum Keyboards{
QWERTY,
DVORAK,
CIRCLE
}