Compare commits
36 Commits
v1.3.2
...
squeekboar
| Author | SHA1 | Date | |
|---|---|---|---|
| 3af10285b7 | |||
| 53997abc46 | |||
| 34765be22e | |||
| 8b4c643d3e | |||
| 358b25c431 | |||
| 2749fdb686 | |||
| 8e7909e877 | |||
| afaacd3f68 | |||
| 6a164d8119 | |||
| 3c45e3e53c | |||
| cdf263d984 | |||
| 2ddfcfaff0 | |||
| a901c85bcb | |||
| fdbbe8f126 | |||
| f284627beb | |||
| d45724c462 | |||
| 93d0dcdc99 | |||
| b252f7659b | |||
| af6ad1fce6 | |||
| 4ee8a91dfe | |||
| 6d5f793718 | |||
| 59f6173282 | |||
| 3aec821f92 | |||
| 3ac4caa3b9 | |||
| 80ac591535 | |||
| 034570bfa0 | |||
| 1abca0a44e | |||
| 23693521b7 | |||
| 0179507254 | |||
| 0c7e77a05f | |||
| 0adde1004f | |||
| 9b271a6919 | |||
| 1db561d33a | |||
| 9571adb107 | |||
| f834f174d8 | |||
| 3c0b142c4f |
@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0.*"
|
bitflags = "1.0.*"
|
||||||
|
clap = "2.32.*"
|
||||||
maplit = "1.0.*"
|
maplit = "1.0.*"
|
||||||
regex = "1.1.*"
|
regex = "1.1.*"
|
||||||
serde = { version = "1.0.*", features = ["derive"] }
|
serde = { version = "1.0.*", features = ["derive"] }
|
||||||
|
|||||||
12
HACKING.md
12
HACKING.md
@ -30,7 +30,7 @@ Most common testing is done in CI. Occasionally, and for each release, do perfor
|
|||||||
- the application draws correctly
|
- the application draws correctly
|
||||||
- it shows when relevant
|
- it shows when relevant
|
||||||
- it changes layouts
|
- it changes layouts
|
||||||
- it changes levels
|
- it changes views
|
||||||
|
|
||||||
Testing with an application:
|
Testing with an application:
|
||||||
|
|
||||||
@ -50,10 +50,8 @@ Testing layouts:
|
|||||||
Layouts can be selected using the GNOME Settings application.
|
Layouts can be selected using the GNOME Settings application.
|
||||||
|
|
||||||
```
|
```
|
||||||
# define all available layouts
|
# define all available layouts. First one is currently selected.
|
||||||
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
|
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'de')]"
|
||||||
# choose the active layout
|
|
||||||
$ gsettings set org.gnome.desktop.input-sources current 1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Coding
|
Coding
|
||||||
@ -114,7 +112,7 @@ Use the `cargo.sh` script for maintaining the Cargo part of the build. The scrip
|
|||||||
|
|
||||||
```
|
```
|
||||||
cd build_dir
|
cd build_dir
|
||||||
sh /source_path/cargo.sh '' test
|
sh /source_path/cargo.sh test
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cargo dependencies
|
### Cargo dependencies
|
||||||
@ -125,6 +123,6 @@ Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Si
|
|||||||
|
|
||||||
```
|
```
|
||||||
cd build_dir
|
cd build_dir
|
||||||
sh /source_path/cargo.sh '' update
|
sh /source_path/cargo.sh update
|
||||||
ninja test
|
ninja test
|
||||||
```
|
```
|
||||||
|
|||||||
7
cargo.sh
7
cargo.sh
@ -11,14 +11,7 @@ SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
|
|||||||
|
|
||||||
CARGO_TARGET_DIR="$(pwd)"
|
CARGO_TARGET_DIR="$(pwd)"
|
||||||
export CARGO_TARGET_DIR
|
export CARGO_TARGET_DIR
|
||||||
if [ -n "${1}" ]; then
|
|
||||||
OUT_PATH="$(realpath "$1")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$SOURCE_DIR"
|
cd "$SOURCE_DIR"
|
||||||
shift
|
|
||||||
cargo "$@"
|
cargo "$@"
|
||||||
|
|
||||||
if [ -n "${OUT_PATH}" ]; then
|
|
||||||
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
|
|
||||||
fi
|
|
||||||
|
|||||||
34
cargo_build.sh
Executable file
34
cargo_build.sh
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This script manages Cargo builds
|
||||||
|
# while keeping the artifact directory within the build tree
|
||||||
|
# instead of the source tree
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_PATH="$(realpath "$0")"
|
||||||
|
SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
|
||||||
|
|
||||||
|
RELEASE=""
|
||||||
|
BINARY_DIR="debug"
|
||||||
|
if [ "${1}" = "--release" ]; then
|
||||||
|
shift
|
||||||
|
BINARY_DIR="release"
|
||||||
|
RELEASE="--release"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${1}" = "--rename" ]; then
|
||||||
|
shift
|
||||||
|
FILENAME="${1}"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
OUT_PATH="$(realpath "${1}")"
|
||||||
|
shift
|
||||||
|
OUT_BASENAME="$(basename "${OUT_PATH}")"
|
||||||
|
FILENAME="${FILENAME:-"${OUT_BASENAME}"}"
|
||||||
|
|
||||||
|
sh "$SOURCE_DIR"/cargo.sh build $RELEASE "$@"
|
||||||
|
|
||||||
|
if [ -n "${OUT_PATH}" ]; then
|
||||||
|
cp -a ./"${BINARY_DIR}"/"${FILENAME}" "${OUT_PATH}"
|
||||||
|
fi
|
||||||
@ -1,5 +1,4 @@
|
|||||||
# German layout by Mark Müller
|
# Maintained by: Mark Müller <markmueller86@gmail.com>
|
||||||
# Version 2019111700
|
|
||||||
---
|
---
|
||||||
bounds: { x: 0, y: 1, width: 360, height: 208 }
|
bounds: { x: 0, y: 1, width: 360, height: 208 }
|
||||||
|
|
||||||
@ -83,7 +82,7 @@ buttons:
|
|||||||
space:
|
space:
|
||||||
outline: "spaceline"
|
outline: "spaceline"
|
||||||
label: " "
|
label: " "
|
||||||
keysym: "space"
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
|||||||
88
data/keyboards/de_wide.yaml
Normal file
88
data/keyboards/de_wide.yaml
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# Maintained by: Mark Müller <markmueller86@gmail.com>
|
||||||
|
---
|
||||||
|
bounds: { x: 0, y: 1, width: 540, height: 168 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
bounds: { x: 0, y: 0, width: 48, height: 42 }
|
||||||
|
altline:
|
||||||
|
bounds: { x: 0, y: 0, width: 81, height: 42 }
|
||||||
|
wide:
|
||||||
|
bounds: { x: 0, y: 0, width: 108, height: 42 }
|
||||||
|
spaceline:
|
||||||
|
bounds: { x: 0, y: 0, width: 216, height: 42 }
|
||||||
|
special:
|
||||||
|
bounds: { x: 0, y: 0, width: 48, height: 42 }
|
||||||
|
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "q w e r t z u i o p ü"
|
||||||
|
- "a s d f g h j k l ö ä"
|
||||||
|
- "Shift_L y x c v b n m BackSpace"
|
||||||
|
- "show_numbers preferences space , . Return"
|
||||||
|
upper:
|
||||||
|
- "Q W E R T Z U I O P Ü"
|
||||||
|
- "A S D F G H J K L Ö Ä"
|
||||||
|
- "Shift_L Y X C V B N M BackSpace"
|
||||||
|
- "show_numbers preferences space ! ? Return"
|
||||||
|
numbers:
|
||||||
|
- "1 2 3 4 5 6 7 8 9 0"
|
||||||
|
- "@ # % & - _ + ( ) ß"
|
||||||
|
- "show_symbols , \" ' : = < > BackSpace"
|
||||||
|
- "show_letters preferences space , . Return"
|
||||||
|
symbols:
|
||||||
|
- "~ ` ´ · © ® ÷ × ¶"
|
||||||
|
- "€ £ $ ¥ ^ ° * { } |"
|
||||||
|
- "show_numbers \\ / § π τ [ ] BackSpace"
|
||||||
|
- "show_letters preferences space , . Return"
|
||||||
|
eschars:
|
||||||
|
- "ä è é ö ü Ä È É Ö Ü"
|
||||||
|
- "à â ê î ô À Â È Î Ô"
|
||||||
|
- "show_numbers « » ç Ç æ œ ß BackSpace"
|
||||||
|
- "show_letters preferences space „ “ Return"
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
Shift_L:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "upper"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-shift"
|
||||||
|
BackSpace:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
|
preferences:
|
||||||
|
action: "show_prefs"
|
||||||
|
outline: "special"
|
||||||
|
icon: "keyboard-mode-symbolic"
|
||||||
|
show_numbers:
|
||||||
|
action:
|
||||||
|
set_view: "numbers"
|
||||||
|
outline: "altline"
|
||||||
|
label: "123"
|
||||||
|
show_letters:
|
||||||
|
action:
|
||||||
|
set_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "abc"
|
||||||
|
show_symbols:
|
||||||
|
action:
|
||||||
|
set_view: "symbols"
|
||||||
|
outline: "altline"
|
||||||
|
label: "*/="
|
||||||
|
show_eschars:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "eschars"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "äÄ"
|
||||||
|
space:
|
||||||
|
outline: "spaceline"
|
||||||
|
text: " "
|
||||||
|
Return:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
@ -53,6 +53,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
@ -86,111 +87,112 @@ buttons:
|
|||||||
label: "αι"
|
label: "αι"
|
||||||
period:
|
period:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
label: "."
|
text: "."
|
||||||
space:
|
space:
|
||||||
outline: spaceline
|
outline: spaceline
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "wide"
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
aring:
|
aring:
|
||||||
label: "å"
|
text: "å"
|
||||||
Aring:
|
Aring:
|
||||||
label: "Å"
|
text: "Å"
|
||||||
oslash:
|
oslash:
|
||||||
label: "ø"
|
text: "ø"
|
||||||
Oslash:
|
Oslash:
|
||||||
label: "Ø"
|
text: "Ø"
|
||||||
ae:
|
ae:
|
||||||
label: "æ"
|
text: "æ"
|
||||||
AE:
|
AE:
|
||||||
label: "Æ"
|
text: "Æ"
|
||||||
asterisk:
|
asterisk:
|
||||||
label: "*"
|
text: "*"
|
||||||
asciitilde:
|
asciitilde:
|
||||||
label: "~"
|
text: "~"
|
||||||
quoteleft:
|
quoteleft:
|
||||||
label: "`"
|
text: "`"
|
||||||
bar:
|
bar:
|
||||||
label: "|"
|
text: "|"
|
||||||
U00B7:
|
U00B7:
|
||||||
label: "·"
|
text: "·"
|
||||||
squareroot:
|
squareroot:
|
||||||
label: "√"
|
text: "√"
|
||||||
Greek_pi:
|
Greek_pi:
|
||||||
label: "π"
|
text: "π"
|
||||||
division:
|
division:
|
||||||
label: "÷"
|
text: "÷"
|
||||||
multiply:
|
multiply:
|
||||||
label: "×"
|
text: "×"
|
||||||
paragraph:
|
paragraph:
|
||||||
label: "¶"
|
text: "¶"
|
||||||
Greek_tau:
|
Greek_tau:
|
||||||
label: "τ"
|
text: "τ"
|
||||||
copyright:
|
copyright:
|
||||||
label: "©"
|
text: "©"
|
||||||
numbersign:
|
numbersign:
|
||||||
label: "#"
|
text: "#"
|
||||||
U00AE:
|
U00AE:
|
||||||
label: "®"
|
text: "®"
|
||||||
at:
|
at:
|
||||||
label: "@"
|
text: "@"
|
||||||
dollar:
|
dollar:
|
||||||
label: "$"
|
text: "$"
|
||||||
U00A3:
|
U00A3:
|
||||||
label: "£"
|
text: "£"
|
||||||
percent:
|
percent:
|
||||||
label: "%"
|
text: "%"
|
||||||
EuroSign:
|
EuroSign:
|
||||||
label: "€"
|
text: "€"
|
||||||
ampersand:
|
ampersand:
|
||||||
label: "&"
|
text: "&"
|
||||||
U00A5:
|
U00A5:
|
||||||
label: "¥"
|
text: "¥"
|
||||||
minus:
|
minus:
|
||||||
label: "-"
|
text: "-"
|
||||||
asciicircum:
|
asciicircum:
|
||||||
label: "^"
|
text: "^"
|
||||||
underscore:
|
underscore:
|
||||||
label: "_"
|
text: "_"
|
||||||
degree:
|
degree:
|
||||||
label: "°"
|
text: "°"
|
||||||
plus:
|
plus:
|
||||||
label: "+"
|
text: "+"
|
||||||
equal:
|
equal:
|
||||||
label: "="
|
text: "="
|
||||||
parenleft:
|
parenleft:
|
||||||
label: "("
|
text: "("
|
||||||
parenright:
|
parenright:
|
||||||
label: ")"
|
text: ")"
|
||||||
braceleft:
|
braceleft:
|
||||||
label: "{"
|
text: "{"
|
||||||
braceright:
|
braceright:
|
||||||
label: "}"
|
text: "}"
|
||||||
comma:
|
comma:
|
||||||
label: ","
|
text: ","
|
||||||
backslash:
|
backslash:
|
||||||
label: "\\"
|
text: "\\"
|
||||||
slash:
|
slash:
|
||||||
label: "/"
|
text: "/"
|
||||||
quotedbl:
|
quotedbl:
|
||||||
label: "\""
|
text: "\""
|
||||||
quoteright:
|
quoteright:
|
||||||
label: "'"
|
text: "'"
|
||||||
less:
|
less:
|
||||||
label: "<"
|
text: "<"
|
||||||
greater:
|
greater:
|
||||||
label: ">"
|
text: ">"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
text: ":"
|
||||||
semicolon:
|
semicolon:
|
||||||
label: ";"
|
text: ";"
|
||||||
exclam:
|
exclam:
|
||||||
label: "!"
|
text: "!"
|
||||||
question:
|
question:
|
||||||
label: "?"
|
text: "?"
|
||||||
bracketleft:
|
bracketleft:
|
||||||
label: "["
|
text: "["
|
||||||
bracketright:
|
bracketright:
|
||||||
label: "]"
|
text: "]"
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "default"
|
outline: "default"
|
||||||
@ -80,14 +81,14 @@ buttons:
|
|||||||
|
|
||||||
period:
|
period:
|
||||||
outline: "default"
|
outline: "default"
|
||||||
label: "."
|
text: "."
|
||||||
space:
|
space:
|
||||||
outline: "spaceline"
|
outline: "spaceline"
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
text: ":"
|
||||||
"\"":
|
|
||||||
keysym: "quotedbl"
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
@ -69,108 +70,109 @@ buttons:
|
|||||||
outline: altline
|
outline: altline
|
||||||
space:
|
space:
|
||||||
outline: spaceline
|
outline: spaceline
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "wide"
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
aring:
|
aring:
|
||||||
label: "å"
|
text: "å"
|
||||||
Aring:
|
Aring:
|
||||||
label: "Å"
|
text: "Å"
|
||||||
ouml:
|
ouml:
|
||||||
label: "ö"
|
text: "ö"
|
||||||
Ouml:
|
Ouml:
|
||||||
label: "Ö"
|
text: "Ö"
|
||||||
auml:
|
auml:
|
||||||
label: "ä"
|
text: "ä"
|
||||||
Auml:
|
Auml:
|
||||||
label: "Ä"
|
text: "Ä"
|
||||||
asterisk:
|
asterisk:
|
||||||
label: "*"
|
text: "*"
|
||||||
asciitilde:
|
asciitilde:
|
||||||
label: "~"
|
text: "~"
|
||||||
quoteleft:
|
quoteleft:
|
||||||
label: "`"
|
text: "`"
|
||||||
bar:
|
bar:
|
||||||
label: "|"
|
text: "|"
|
||||||
U00B7:
|
U00B7:
|
||||||
label: "·"
|
text: "·"
|
||||||
squareroot:
|
squareroot:
|
||||||
label: "√"
|
text: "√"
|
||||||
Greek_pi:
|
Greek_pi:
|
||||||
label: "π"
|
text: "π"
|
||||||
division:
|
division:
|
||||||
label: "÷"
|
text: "÷"
|
||||||
multiply:
|
multiply:
|
||||||
label: "×"
|
text: "×"
|
||||||
paragraph:
|
paragraph:
|
||||||
label: "¶"
|
text: "¶"
|
||||||
Greek_tau:
|
Greek_tau:
|
||||||
label: "τ"
|
text: "τ"
|
||||||
copyright:
|
copyright:
|
||||||
label: "©"
|
text: "©"
|
||||||
numbersign:
|
numbersign:
|
||||||
label: "#"
|
text: "#"
|
||||||
U00AE:
|
U00AE:
|
||||||
label: "®"
|
text: "®"
|
||||||
at:
|
at:
|
||||||
label: "@"
|
text: "@"
|
||||||
dollar:
|
dollar:
|
||||||
label: "$"
|
text: "$"
|
||||||
U00A3:
|
U00A3:
|
||||||
label: "£"
|
text: "£"
|
||||||
percent:
|
percent:
|
||||||
label: "%"
|
text: "%"
|
||||||
EuroSign:
|
EuroSign:
|
||||||
label: "€"
|
text: "€"
|
||||||
ampersand:
|
ampersand:
|
||||||
label: "&"
|
text: "&"
|
||||||
U00A5:
|
U00A5:
|
||||||
label: "¥"
|
text: "¥"
|
||||||
minus:
|
minus:
|
||||||
label: "-"
|
text: "-"
|
||||||
asciicircum:
|
asciicircum:
|
||||||
label: "^"
|
text: "^"
|
||||||
underscore:
|
underscore:
|
||||||
label: "_"
|
text: "_"
|
||||||
degree:
|
degree:
|
||||||
label: "°"
|
text: "°"
|
||||||
plus:
|
plus:
|
||||||
label: "+"
|
text: "+"
|
||||||
equal:
|
equal:
|
||||||
label: "="
|
text: "="
|
||||||
parenleft:
|
parenleft:
|
||||||
label: "("
|
text: "("
|
||||||
parenright:
|
parenright:
|
||||||
label: ")"
|
text: ")"
|
||||||
braceleft:
|
braceleft:
|
||||||
label: "{"
|
text: "{"
|
||||||
braceright:
|
braceright:
|
||||||
label: "}"
|
text: "}"
|
||||||
comma:
|
comma:
|
||||||
label: ","
|
text: ","
|
||||||
backslash:
|
backslash:
|
||||||
label: "\\"
|
text: "\\"
|
||||||
slash:
|
slash:
|
||||||
label: "/"
|
text: "/"
|
||||||
quotedbl:
|
quotedbl:
|
||||||
label: "\""
|
text: "\""
|
||||||
quoteright:
|
quoteright:
|
||||||
label: "'"
|
text: "'"
|
||||||
less:
|
less:
|
||||||
label: "<"
|
text: "<"
|
||||||
greater:
|
greater:
|
||||||
label: ">"
|
text: ">"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
text: ":"
|
||||||
semicolon:
|
semicolon:
|
||||||
label: ";"
|
text: ";"
|
||||||
exclam:
|
exclam:
|
||||||
label: "!"
|
text: "!"
|
||||||
question:
|
question:
|
||||||
label: "?"
|
text: "?"
|
||||||
bracketleft:
|
bracketleft:
|
||||||
label: "["
|
text: "["
|
||||||
bracketright:
|
bracketright:
|
||||||
label: "]"
|
text: "]"
|
||||||
|
|
||||||
|
|||||||
@ -53,6 +53,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "default"
|
outline: "default"
|
||||||
@ -91,6 +92,7 @@ buttons:
|
|||||||
Return:
|
Return:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
label: ":"
|
||||||
"\"":
|
"\"":
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
# Japanese Kana layout by Mark Müller
|
# Maintained by: Mark Müller <markmueller86@gmail.com>
|
||||||
# Version 2019111800
|
|
||||||
---
|
---
|
||||||
bounds: { x: 0, y: 1, width: 360, height: 208 }
|
bounds: { x: 0, y: 1, width: 360, height: 208 }
|
||||||
|
|
||||||
@ -221,11 +220,11 @@ buttons:
|
|||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "special"
|
outline: "special"
|
||||||
icon: "keyboard-mode-symbolic"
|
icon: "keyboard-mode-symbolic"
|
||||||
# space button with unicode keysym for ideographic space
|
# space button using text tag for ideographic space
|
||||||
space:
|
space:
|
||||||
outline: "default-wide"
|
outline: "default-wide"
|
||||||
label: "␣"
|
label: "␣"
|
||||||
keysym: "U3000"
|
text: " "
|
||||||
# switch to number view
|
# switch to number view
|
||||||
numbers:
|
numbers:
|
||||||
action:
|
action:
|
||||||
|
|||||||
@ -26,13 +26,13 @@ views:
|
|||||||
- "show_numbers preferences space . Return"
|
- "show_numbers preferences space . Return"
|
||||||
numbers:
|
numbers:
|
||||||
- "1 2 3 4 5 6 7 8 9 0"
|
- "1 2 3 4 5 6 7 8 9 0"
|
||||||
- "at numbersign dollar percent ampersand minus underscore plus parenleft parenright"
|
- "@ # $ % & - _ + ( )"
|
||||||
- "show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace"
|
- "show_symbols , \" ' : ; ! ? BackSpace"
|
||||||
- "show_letters preferences space . Return"
|
- "show_letters preferences space . Return"
|
||||||
symbols:
|
symbols:
|
||||||
- "asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
|
- "~ ` | U00B7 squareroot Greek_pi Greek_tau division multiply paragraph"
|
||||||
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright"
|
- "copyright U00AE U00A3 EuroSign U00A5 asciicircum degree * { }"
|
||||||
- "show_numbers backslash slash less greater equal bracketleft bracketright BackSpace"
|
- "show_numbers \\ / < > = [ ] BackSpace"
|
||||||
- "show_letters preferences space . Return"
|
- "show_letters preferences space . Return"
|
||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
@ -46,6 +46,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
@ -69,108 +70,37 @@ buttons:
|
|||||||
outline: altline
|
outline: altline
|
||||||
space:
|
space:
|
||||||
outline: spaceline
|
outline: spaceline
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "wide"
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
aring:
|
keysym: "Return"
|
||||||
label: "å"
|
|
||||||
Aring:
|
|
||||||
label: "Å"
|
|
||||||
oslash:
|
|
||||||
label: "ø"
|
|
||||||
Oslash:
|
|
||||||
label: "Ø"
|
|
||||||
ae:
|
|
||||||
label: "æ"
|
|
||||||
AE:
|
|
||||||
label: "Æ"
|
|
||||||
asterisk:
|
|
||||||
label: "*"
|
|
||||||
asciitilde:
|
|
||||||
label: "~"
|
|
||||||
quoteleft:
|
|
||||||
label: "`"
|
|
||||||
bar:
|
|
||||||
label: "|"
|
|
||||||
U00B7:
|
U00B7:
|
||||||
label: "·"
|
text: "·"
|
||||||
squareroot:
|
squareroot:
|
||||||
label: "√"
|
text: "√"
|
||||||
Greek_pi:
|
Greek_pi:
|
||||||
label: "π"
|
text: "π"
|
||||||
division:
|
division:
|
||||||
label: "÷"
|
text: "÷"
|
||||||
multiply:
|
multiply:
|
||||||
label: "×"
|
text: "×"
|
||||||
paragraph:
|
paragraph:
|
||||||
label: "¶"
|
text: "¶"
|
||||||
Greek_tau:
|
Greek_tau:
|
||||||
label: "τ"
|
text: "τ"
|
||||||
copyright:
|
copyright:
|
||||||
label: "©"
|
text: "©"
|
||||||
numbersign:
|
|
||||||
label: "#"
|
|
||||||
U00AE:
|
U00AE:
|
||||||
label: "®"
|
text: "®"
|
||||||
at:
|
|
||||||
label: "@"
|
|
||||||
dollar:
|
|
||||||
label: "$"
|
|
||||||
U00A3:
|
U00A3:
|
||||||
label: "£"
|
text: "£"
|
||||||
percent:
|
|
||||||
label: "%"
|
|
||||||
EuroSign:
|
EuroSign:
|
||||||
label: "€"
|
text: "€"
|
||||||
ampersand:
|
|
||||||
label: "&"
|
|
||||||
U00A5:
|
U00A5:
|
||||||
label: "¥"
|
text: "¥"
|
||||||
minus:
|
|
||||||
label: "-"
|
|
||||||
asciicircum:
|
asciicircum:
|
||||||
label: "^"
|
text: "^"
|
||||||
underscore:
|
|
||||||
label: "_"
|
|
||||||
degree:
|
degree:
|
||||||
label: "°"
|
text: "°"
|
||||||
plus:
|
|
||||||
label: "+"
|
|
||||||
equal:
|
|
||||||
label: "="
|
|
||||||
parenleft:
|
|
||||||
label: "("
|
|
||||||
parenright:
|
|
||||||
label: ")"
|
|
||||||
braceleft:
|
|
||||||
label: "{"
|
|
||||||
braceright:
|
|
||||||
label: "}"
|
|
||||||
comma:
|
|
||||||
label: ","
|
|
||||||
backslash:
|
|
||||||
label: "\\"
|
|
||||||
slash:
|
|
||||||
label: "/"
|
|
||||||
quotedbl:
|
|
||||||
label: "\""
|
|
||||||
quoteright:
|
|
||||||
label: "'"
|
|
||||||
less:
|
|
||||||
label: "<"
|
|
||||||
greater:
|
|
||||||
label: ">"
|
|
||||||
colon:
|
|
||||||
label: ":"
|
|
||||||
semicolon:
|
|
||||||
label: ";"
|
|
||||||
exclam:
|
|
||||||
label: "!"
|
|
||||||
question:
|
|
||||||
label: "?"
|
|
||||||
bracketleft:
|
|
||||||
label: "["
|
|
||||||
bracketright:
|
|
||||||
label: "]"
|
|
||||||
|
|
||||||
|
|||||||
@ -22,22 +22,24 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
space:
|
space:
|
||||||
outline: spaceline
|
outline: spaceline
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: outline7
|
outline: outline7
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "BackSpace"
|
||||||
asterisk:
|
asterisk:
|
||||||
label: "*"
|
text: "*"
|
||||||
numbersign:
|
numbersign:
|
||||||
label: "#"
|
text: "#"
|
||||||
minus:
|
minus:
|
||||||
label: "-"
|
text: "-"
|
||||||
plus:
|
plus:
|
||||||
label: "+"
|
text: "+"
|
||||||
parenleft:
|
parenleft:
|
||||||
label: "("
|
text: "("
|
||||||
parenright:
|
parenright:
|
||||||
label: ")"
|
text: ")"
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
@ -69,96 +70,97 @@ buttons:
|
|||||||
outline: altline
|
outline: altline
|
||||||
space:
|
space:
|
||||||
outline: spaceline
|
outline: spaceline
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "wide"
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
asterisk:
|
asterisk:
|
||||||
label: "*"
|
text: "*"
|
||||||
asciitilde:
|
asciitilde:
|
||||||
label: "~"
|
text: "~"
|
||||||
quoteleft:
|
quoteleft:
|
||||||
label: "`"
|
text: "`"
|
||||||
bar:
|
bar:
|
||||||
label: "|"
|
text: "|"
|
||||||
U00B7:
|
U00B7:
|
||||||
label: "·"
|
text: "·"
|
||||||
squareroot:
|
squareroot:
|
||||||
label: "√"
|
text: "√"
|
||||||
Greek_pi:
|
Greek_pi:
|
||||||
label: "π"
|
text: "π"
|
||||||
division:
|
division:
|
||||||
label: "÷"
|
text: "÷"
|
||||||
multiply:
|
multiply:
|
||||||
label: "×"
|
text: "×"
|
||||||
paragraph:
|
paragraph:
|
||||||
label: "¶"
|
text: "¶"
|
||||||
Greek_tau:
|
Greek_tau:
|
||||||
label: "τ"
|
text: "τ"
|
||||||
copyright:
|
copyright:
|
||||||
label: "©"
|
text: "©"
|
||||||
numbersign:
|
numbersign:
|
||||||
label: "#"
|
text: "#"
|
||||||
U00AE:
|
U00AE:
|
||||||
label: "®"
|
text: "®"
|
||||||
at:
|
at:
|
||||||
label: "@"
|
text: "@"
|
||||||
dollar:
|
dollar:
|
||||||
label: "$"
|
text: "$"
|
||||||
U00A3:
|
U00A3:
|
||||||
label: "£"
|
text: "£"
|
||||||
percent:
|
percent:
|
||||||
label: "%"
|
text: "%"
|
||||||
EuroSign:
|
EuroSign:
|
||||||
label: "€"
|
text: "€"
|
||||||
ampersand:
|
ampersand:
|
||||||
label: "&"
|
text: "&"
|
||||||
U00A5:
|
U00A5:
|
||||||
label: "¥"
|
text: "¥"
|
||||||
minus:
|
minus:
|
||||||
label: "-"
|
text: "-"
|
||||||
asciicircum:
|
asciicircum:
|
||||||
label: "^"
|
text: "^"
|
||||||
underscore:
|
underscore:
|
||||||
label: "_"
|
text: "_"
|
||||||
degree:
|
degree:
|
||||||
label: "°"
|
text: "°"
|
||||||
plus:
|
plus:
|
||||||
label: "+"
|
text: "+"
|
||||||
equal:
|
equal:
|
||||||
label: "="
|
text: "="
|
||||||
parenleft:
|
parenleft:
|
||||||
label: "("
|
text: "("
|
||||||
parenright:
|
parenright:
|
||||||
label: ")"
|
text: ")"
|
||||||
braceleft:
|
braceleft:
|
||||||
label: "{"
|
text: "{"
|
||||||
braceright:
|
braceright:
|
||||||
label: "}"
|
text: "}"
|
||||||
comma:
|
comma:
|
||||||
label: ","
|
text: ","
|
||||||
backslash:
|
backslash:
|
||||||
label: "\\"
|
text: "\\"
|
||||||
slash:
|
slash:
|
||||||
label: "/"
|
text: "/"
|
||||||
quotedbl:
|
quotedbl:
|
||||||
label: "\""
|
text: "\""
|
||||||
quoteright:
|
quoteright:
|
||||||
label: "'"
|
text: "'"
|
||||||
less:
|
less:
|
||||||
label: "<"
|
text: "<"
|
||||||
greater:
|
greater:
|
||||||
label: ">"
|
text: ">"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
text: ":"
|
||||||
semicolon:
|
semicolon:
|
||||||
label: ";"
|
text: ";"
|
||||||
exclam:
|
exclam:
|
||||||
label: "!"
|
text: "!"
|
||||||
question:
|
question:
|
||||||
label: "?"
|
text: "?"
|
||||||
bracketleft:
|
bracketleft:
|
||||||
label: "["
|
text: "["
|
||||||
bracketright:
|
bracketright:
|
||||||
label: "]"
|
text: "]"
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "special"
|
outline: "special"
|
||||||
@ -72,14 +73,13 @@ buttons:
|
|||||||
label: "*/="
|
label: "*/="
|
||||||
period:
|
period:
|
||||||
outline: "special"
|
outline: "special"
|
||||||
label: "."
|
text: "."
|
||||||
space:
|
space:
|
||||||
outline: "spaceline"
|
outline: "spaceline"
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "wide"
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
text: ":"
|
||||||
"\"":
|
|
||||||
keysym: "quotedbl"
|
|
||||||
|
|||||||
@ -18,22 +18,22 @@ views:
|
|||||||
- "q w e r t y u i o p"
|
- "q w e r t y u i o p"
|
||||||
- "a s d f g h j k l"
|
- "a s d f g h j k l"
|
||||||
- "Shift_L z x c v b n m BackSpace"
|
- "Shift_L z x c v b n m BackSpace"
|
||||||
- "show_numbers preferences space period Return"
|
- "show_numbers preferences space . Return"
|
||||||
upper:
|
upper:
|
||||||
- "Q W E R T Y U I O P"
|
- "Q W E R T Y U I O P"
|
||||||
- "A S D F G H J K L"
|
- "A S D F G H J K L"
|
||||||
- "Shift_L Z X C V B N M BackSpace"
|
- "Shift_L Z X C V B N M BackSpace"
|
||||||
- "show_numbers preferences space period Return"
|
- "show_numbers preferences space . Return"
|
||||||
numbers:
|
numbers:
|
||||||
- "1 2 3 4 5 6 7 8 9 0"
|
- "1 2 3 4 5 6 7 8 9 0"
|
||||||
- "@ # $ % & - _ + ( )"
|
- "@ # $ % & - _ + ( )"
|
||||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||||
- "show_letters preferences space period Return"
|
- "show_letters preferences space . Return"
|
||||||
symbols:
|
symbols:
|
||||||
- "~ ` | · √ π τ ÷ × ¶"
|
- "~ ` | · √ π τ ÷ × ¶"
|
||||||
- "© ® £ € ¥ ^ ° * { }"
|
- "© ® £ € ¥ ^ ° * { }"
|
||||||
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||||
- "show_letters preferences space period Return"
|
- "show_letters preferences space . Return"
|
||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
Shift_L:
|
Shift_L:
|
||||||
@ -46,6 +46,7 @@ buttons:
|
|||||||
BackSpace:
|
BackSpace:
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
|
keysym: "BackSpace"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "special"
|
outline: "special"
|
||||||
@ -70,16 +71,15 @@ buttons:
|
|||||||
set_view: "symbols"
|
set_view: "symbols"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
label: "*/="
|
label: "*/="
|
||||||
period:
|
".":
|
||||||
outline: "special"
|
outline: "special"
|
||||||
label: "."
|
text: "."
|
||||||
space:
|
space:
|
||||||
outline: "spaceline"
|
outline: "spaceline"
|
||||||
label: " "
|
text: " "
|
||||||
Return:
|
Return:
|
||||||
outline: "wide"
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
|
keysym: "Return"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
text: ":"
|
||||||
"\"":
|
|
||||||
keysym: "quotedbl"
|
|
||||||
|
|||||||
8
data/langs/ja-JP.txt
Normal file
8
data/langs/ja-JP.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
us 英語 (US)
|
||||||
|
de ドイツ語
|
||||||
|
el ギリシャ語
|
||||||
|
es スペイン語
|
||||||
|
it イタリア語
|
||||||
|
jp+kana 日本語 (かな)
|
||||||
|
nb ノルウェー語
|
||||||
|
|
||||||
@ -2,6 +2,7 @@
|
|||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/sm/puri/squeekboard">
|
<gresource prefix="/sm/puri/squeekboard">
|
||||||
<file compressed="true">style.css</file>
|
<file compressed="true">style.css</file>
|
||||||
|
<file compressed="true">style-Adwaita:dark.css</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">popup.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">popup.ui</file>
|
||||||
<file>icons/key-enter.svg</file>
|
<file>icons/key-enter.svg</file>
|
||||||
<file>icons/key-shift.svg</file>
|
<file>icons/key-shift.svg</file>
|
||||||
|
|||||||
46
data/style-Adwaita:dark.css
Normal file
46
data/style-Adwaita:dark.css
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
sq_view {
|
||||||
|
background-color: rgba(0, 0, 0, 255);
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: cantarell, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_view sq_button {
|
||||||
|
color: #deddda;
|
||||||
|
background: #464448;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #5e5c64;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 4px 2px 4px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_view.wide sq_button {
|
||||||
|
margin: 1px 1px 1px 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_button:active {
|
||||||
|
background: #747077;
|
||||||
|
border-color: #96949d;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_button.altline,
|
||||||
|
sq_button.special,
|
||||||
|
sq_button.wide {
|
||||||
|
background: #2b292f;
|
||||||
|
border-color: #3e3a44;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_button.locked {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #2b292f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Return {
|
||||||
|
background: #1c71d8;
|
||||||
|
border-color: #1a5fb4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Return:active {
|
||||||
|
background: #1c71d8;
|
||||||
|
border-color: #3584e4;
|
||||||
|
}
|
||||||
@ -1,15 +1,15 @@
|
|||||||
sq_view {
|
sq_view {
|
||||||
background-color: rgba(0, 0, 0, 255);
|
background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/
|
||||||
color: #ffffff;
|
color: @theme_text_color; /*#ffffff;*/
|
||||||
font-family: cantarell, sans-serif;
|
font-family: cantarell, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
sq_view sq_button {
|
sq_view sq_button {
|
||||||
color: #deddda;
|
color: @theme_fg_color; /*#deddda;*/
|
||||||
background: #464448;
|
background: mix(@theme_bg_color, @theme_base_color, -0.5); /* #464448; */
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-color: #5e5c64;
|
border-color: @borders; /* #5e5c64;*/
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin: 4px 2px 4px 2px;
|
margin: 4px 2px 4px 2px;
|
||||||
}
|
}
|
||||||
@ -18,29 +18,32 @@ sq_view.wide sq_button {
|
|||||||
margin: 1px 1px 1px 1px;
|
margin: 1px 1px 1px 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
sq_button:active {
|
sq_button:active,
|
||||||
background: #747077;
|
sq_button.altline:active,
|
||||||
border-color: #96949d;
|
sq_button.special:active,
|
||||||
|
sq_button.wide:active {
|
||||||
|
background: mix(@theme_bg_color, @theme_selected_bg_color, 0.4);/* #747077; */
|
||||||
|
border-color: mix(@borders, @theme_selected_fg_color, 0.5);/* #96949d; */
|
||||||
}
|
}
|
||||||
|
|
||||||
sq_button.altline,
|
sq_button.altline,
|
||||||
sq_button.special,
|
sq_button.special,
|
||||||
sq_button.wide {
|
sq_button.wide {
|
||||||
background: #2b292f;
|
background: mix(@theme_bg_color, @theme_base_color, 0.5); /*#2b292f;*/
|
||||||
border-color: #3e3a44;
|
border-color: @borders; /* #3e3a44; */
|
||||||
}
|
}
|
||||||
|
|
||||||
sq_button.locked {
|
sq_button.locked {
|
||||||
background: #ffffff;
|
background: @theme_fg_color; /*#ffffff;*/
|
||||||
color: #2b292f;
|
color: @theme_bg_color; /*#2b292f;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#Return {
|
#Return {
|
||||||
background: #1c71d8;
|
background: @theme_selected_bg_color; /* #1c71d8; */
|
||||||
border-color: #1a5fb4;
|
border-color: @borders; /*#1a5fb4;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#Return:active {
|
#Return:active {
|
||||||
background: #1c71d8;
|
background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/
|
||||||
border-color: #3584e4;
|
border-color: @borders; /*#3584e4;*/
|
||||||
}
|
}
|
||||||
|
|||||||
7
debian/changelog
vendored
7
debian/changelog
vendored
@ -1,3 +1,10 @@
|
|||||||
|
squeekboard (1.4.0) amber-phone; urgency=medium
|
||||||
|
|
||||||
|
* "text" property in layouts
|
||||||
|
* Adjusts to user's color scheme
|
||||||
|
|
||||||
|
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 02 Dec 2019 19:37:01 +0000
|
||||||
|
|
||||||
squeekboard (1.3.2) amber-phone; urgency=medium
|
squeekboard (1.3.2) amber-phone; urgency=medium
|
||||||
|
|
||||||
* Make sure all key presses get accepted by the compositor
|
* Make sure all key presses get accepted by the compositor
|
||||||
|
|||||||
11
debian/control
vendored
11
debian/control
vendored
@ -12,6 +12,7 @@ Build-Depends:
|
|||||||
libgtk-3-dev,
|
libgtk-3-dev,
|
||||||
libcroco3-dev,
|
libcroco3-dev,
|
||||||
librust-bitflags-1-dev (>= 1.0),
|
librust-bitflags-1-dev (>= 1.0),
|
||||||
|
librust-clap-2+default-dev (>= 2.32),
|
||||||
librust-gio+v2-44-dev,
|
librust-gio+v2-44-dev,
|
||||||
librust-glib+v2-44-dev,
|
librust-glib+v2-44-dev,
|
||||||
librust-glib-sys-dev,
|
librust-glib-sys-dev,
|
||||||
@ -40,3 +41,13 @@ Depends:
|
|||||||
${misc:Depends}
|
${misc:Depends}
|
||||||
Description: On-screen keyboard for Wayland
|
Description: On-screen keyboard for Wayland
|
||||||
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
|
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.
|
||||||
|
|
||||||
|
Package: squeekboard-devel
|
||||||
|
Architecture: linux-any
|
||||||
|
Depends:
|
||||||
|
${shlibs:Depends}
|
||||||
|
${misc:Depends}
|
||||||
|
Description: Resources for making Squeekboard layouts
|
||||||
|
Tools for creating Squeekboard layouts:
|
||||||
|
.
|
||||||
|
* squeekboard-test-layout
|
||||||
|
|||||||
1
debian/squeekboard-devel.install
vendored
Normal file
1
debian/squeekboard-devel.install
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
usr/bin/squeekboard-test-layout /usr/bin
|
||||||
2
debian/squeekboard.install
vendored
Normal file
2
debian/squeekboard.install
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
tools/squeekboard-restyled usr/bin
|
||||||
|
usr/bin/squeekboard /usr/bin
|
||||||
2
debian/squeekboard.lintian-overrides
vendored
2
debian/squeekboard.lintian-overrides
vendored
@ -1,2 +1,2 @@
|
|||||||
# yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive
|
# yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive
|
||||||
squeekboard binary: embedded-library usr/bin/squeekboard-real: libyaml
|
squeekboard binary: embedded-library usr/bin/squeekboard: libyaml
|
||||||
|
|||||||
@ -158,6 +158,8 @@ eek_gtk_keyboard_real_button_press_event (GtkWidget *self,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render
|
// TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render
|
||||||
static gboolean
|
static gboolean
|
||||||
eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
|
eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
|
||||||
@ -170,6 +172,18 @@ eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
eek_gtk_keyboard_leave_event (GtkWidget *self,
|
||||||
|
GdkEventCrossing *event)
|
||||||
|
{
|
||||||
|
if (event->type == GDK_LEAVE_NOTIFY) {
|
||||||
|
// TODO: can the event have different coords than the previous move event?
|
||||||
|
release(EEK_GTK_KEYBOARD(self), event->time);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
|
eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
|
||||||
GdkEventMotion *event)
|
GdkEventMotion *event)
|
||||||
@ -279,6 +293,9 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
|
|||||||
eek_gtk_keyboard_real_button_release_event;
|
eek_gtk_keyboard_real_button_release_event;
|
||||||
widget_class->motion_notify_event =
|
widget_class->motion_notify_event =
|
||||||
eek_gtk_keyboard_real_motion_notify_event;
|
eek_gtk_keyboard_real_motion_notify_event;
|
||||||
|
widget_class->leave_notify_event =
|
||||||
|
eek_gtk_keyboard_leave_event;
|
||||||
|
|
||||||
widget_class->touch_event = handle_touch_event;
|
widget_class->touch_event = handle_touch_event;
|
||||||
|
|
||||||
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
#include "eek-renderer.h"
|
#include "eek-renderer.h"
|
||||||
|
#include "src/style.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
@ -623,10 +624,7 @@ eek_renderer_init (EekRenderer *self)
|
|||||||
|
|
||||||
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
|
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
|
||||||
|
|
||||||
/* Create a default CSS provider and load a style sheet */
|
priv->css_provider = squeek_load_style();
|
||||||
priv->css_provider = gtk_css_provider_new ();
|
|
||||||
gtk_css_provider_load_from_resource (priv->css_provider,
|
|
||||||
"/sm/puri/squeekboard/style.css");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
@ -207,22 +207,8 @@ static void
|
|||||||
settings_get_layout(GSettings *settings, char **type, char **layout)
|
settings_get_layout(GSettings *settings, char **type, char **layout)
|
||||||
{
|
{
|
||||||
GVariant *inputs = g_settings_get_value(settings, "sources");
|
GVariant *inputs = g_settings_get_value(settings, "sources");
|
||||||
guint32 index;
|
// current layout is always first
|
||||||
g_settings_get(settings, "current", "u", &index);
|
g_variant_get_child(inputs, 0, "(ss)", type, layout);
|
||||||
|
|
||||||
GVariantIter *iter;
|
|
||||||
g_variant_get(inputs, "a(ss)", &iter);
|
|
||||||
|
|
||||||
for (unsigned i = 0;
|
|
||||||
g_variant_iter_loop(iter, "(ss)", type, layout);
|
|
||||||
i++) {
|
|
||||||
if (i == index) {
|
|
||||||
//printf("Found layout %s %s\n", *type, *layout);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_variant_iter_free(iter);
|
|
||||||
g_variant_unref(inputs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@ -1,52 +1,10 @@
|
|||||||
extern crate rs;
|
extern crate rs;
|
||||||
extern crate xkbcommon;
|
|
||||||
|
|
||||||
|
use rs::tests::check_builtin_layout;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use rs::data::{ Layout, LoadError };
|
|
||||||
|
|
||||||
use xkbcommon::xkb;
|
|
||||||
|
|
||||||
|
|
||||||
fn check_layout(name: &str) {
|
|
||||||
let layout = Layout::from_resource(name)
|
|
||||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
|
||||||
.expect("layout broken");
|
|
||||||
|
|
||||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
|
||||||
|
|
||||||
let keymap_str = layout.keymap_str
|
|
||||||
.clone()
|
|
||||||
.into_string().expect("Failed to decode keymap string");
|
|
||||||
|
|
||||||
let keymap = xkb::Keymap::new_from_string(
|
|
||||||
&context,
|
|
||||||
keymap_str.clone(),
|
|
||||||
xkb::KEYMAP_FORMAT_TEXT_V1,
|
|
||||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
|
||||||
).expect("Failed to create keymap");
|
|
||||||
|
|
||||||
let state = xkb::State::new(&keymap);
|
|
||||||
|
|
||||||
// "Press" each button with keysyms
|
|
||||||
for view in layout.views.values() {
|
|
||||||
for row in &view.rows {
|
|
||||||
for button in &row.buttons {
|
|
||||||
let keystate = button.state.borrow();
|
|
||||||
for keycode in &keystate.keycodes {
|
|
||||||
match state.key_get_one_sym(*keycode) {
|
|
||||||
xkb::KEY_NoSymbol => {
|
|
||||||
eprintln!("{}", keymap_str);
|
|
||||||
panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name);
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> () {
|
fn main() -> () {
|
||||||
check_layout(env::args().nth(1).expect("No argument given").as_str());
|
check_builtin_layout(
|
||||||
|
env::args().nth(1).expect("No argument given").as_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
10
meson.build
10
meson.build
@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'squeekboard',
|
'squeekboard',
|
||||||
'c', 'rust',
|
'c', 'rust',
|
||||||
version: '1.3.2',
|
version: '1.4.0',
|
||||||
license: 'GPLv3',
|
license: 'GPLv3',
|
||||||
meson_version: '>=0.51.0',
|
meson_version: '>=0.51.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
@ -33,6 +33,11 @@ endif
|
|||||||
if get_option('buildtype') != 'plain'
|
if get_option('buildtype') != 'plain'
|
||||||
add_project_arguments('-fstack-protector-strong', language: 'c')
|
add_project_arguments('-fstack-protector-strong', language: 'c')
|
||||||
endif
|
endif
|
||||||
|
if get_option('buildtype') == 'release'
|
||||||
|
cargo_build_flags = ['--release'] # for artifacts
|
||||||
|
else
|
||||||
|
cargo_build_flags = []
|
||||||
|
endif
|
||||||
|
|
||||||
prefix = get_option('prefix')
|
prefix = get_option('prefix')
|
||||||
datadir = join_paths(prefix, get_option('datadir'))
|
datadir = join_paths(prefix, get_option('datadir'))
|
||||||
@ -54,8 +59,9 @@ summary = [
|
|||||||
]
|
]
|
||||||
message('\n'.join(summary))
|
message('\n'.join(summary))
|
||||||
|
|
||||||
cargo = find_program('cargo')
|
dep_cargo = find_program('cargo')
|
||||||
cargo_script = find_program('cargo.sh')
|
cargo_script = find_program('cargo.sh')
|
||||||
|
cargo_build = find_program('cargo_build.sh')
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
subdir('protocols')
|
subdir('protocols')
|
||||||
|
|||||||
16
src/bin/test_layout.rs
Normal file
16
src/bin/test_layout.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
extern crate rs;
|
||||||
|
|
||||||
|
use rs::tests::check_layout_file;
|
||||||
|
|
||||||
|
fn main() -> () {
|
||||||
|
let matches = clap_app!(test_layout =>
|
||||||
|
(name: "squeekboard-test-layout")
|
||||||
|
(about: "Test keyboard layout for errors. Returns OK or an error message containing further information.")
|
||||||
|
(@arg INPUT: +required "Yaml keyboard layout file to test")
|
||||||
|
).get_matches();
|
||||||
|
if check_layout_file(matches.value_of("INPUT").unwrap()) == () {
|
||||||
|
println!("Test result: OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
282
src/data.rs
282
src/data.rs
@ -26,8 +26,8 @@ use ::xdg;
|
|||||||
// traits, derives
|
// traits, derives
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use util::WarningHandler;
|
||||||
|
|
||||||
|
|
||||||
/// Gathers stuff defined in C or called by C
|
/// Gathers stuff defined in C or called by C
|
||||||
@ -151,21 +151,30 @@ fn list_layout_sources(
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PrintWarnings;
|
||||||
|
|
||||||
|
impl WarningHandler for PrintWarnings {
|
||||||
|
fn handle(&mut self, warning: &str) {
|
||||||
|
println!("{}", warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn load_layout_data(source: DataSource)
|
fn load_layout_data(source: DataSource)
|
||||||
-> Result<::layout::LayoutData, LoadError>
|
-> Result<::layout::LayoutData, LoadError>
|
||||||
{
|
{
|
||||||
|
let handler = PrintWarnings{};
|
||||||
match source {
|
match source {
|
||||||
DataSource::File(path) => {
|
DataSource::File(path) => {
|
||||||
Layout::from_file(path.clone())
|
Layout::from_file(path.clone())
|
||||||
.map_err(LoadError::BadData)
|
.map_err(LoadError::BadData)
|
||||||
.and_then(|layout|
|
.and_then(|layout|
|
||||||
layout.build().map_err(LoadError::BadKeyMap)
|
layout.build(handler).0.map_err(LoadError::BadKeyMap)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
DataSource::Resource(name) => {
|
DataSource::Resource(name) => {
|
||||||
Layout::from_resource(&name)
|
Layout::from_resource(&name)
|
||||||
.and_then(|layout|
|
.and_then(|layout|
|
||||||
layout.build().map_err(LoadError::BadKeyMap)
|
layout.build(handler).0.map_err(LoadError::BadKeyMap)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -225,22 +234,28 @@ struct Bounds {
|
|||||||
/// Buttons are embedded in a single string
|
/// Buttons are embedded in a single string
|
||||||
type ButtonIds = String;
|
type ButtonIds = String;
|
||||||
|
|
||||||
|
/// All info about a single button
|
||||||
|
/// Buttons can have multiple instances though.
|
||||||
#[derive(Debug, Default, Deserialize, PartialEq)]
|
#[derive(Debug, Default, Deserialize, PartialEq)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct ButtonMeta {
|
struct ButtonMeta {
|
||||||
/// Action other than keysym (conflicts with keysym)
|
/// Special action to perform on activation. Conflicts with keysym, text.
|
||||||
action: Option<Action>,
|
action: Option<Action>,
|
||||||
/// The name of the outline. If not present, will be "default"
|
/// The name of the XKB keysym to emit on activation.
|
||||||
outline: Option<String>,
|
/// Conflicts with action, text
|
||||||
/// FIXME: start using it
|
|
||||||
keysym: Option<String>,
|
keysym: Option<String>,
|
||||||
/// If not present, will be derived from the button ID
|
/// The text to submit on activation. Will be derived from ID if not present
|
||||||
|
/// Conflicts with action, keysym
|
||||||
|
text: Option<String>,
|
||||||
|
/// If not present, will be derived from text or the button ID
|
||||||
label: Option<String>,
|
label: Option<String>,
|
||||||
/// Conflicts with label
|
/// Conflicts with label
|
||||||
icon: Option<String>,
|
icon: Option<String>,
|
||||||
|
/// The name of the outline. If not present, will be "default"
|
||||||
|
outline: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
enum Action {
|
enum Action {
|
||||||
#[serde(rename="locking")]
|
#[serde(rename="locking")]
|
||||||
@ -296,7 +311,7 @@ impl Layout {
|
|||||||
.map_err(LoadError::BadResource)
|
.map_err(LoadError::BadResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_file(path: PathBuf) -> Result<Layout, Error> {
|
pub fn from_file(path: PathBuf) -> Result<Layout, Error> {
|
||||||
let infile = BufReader::new(
|
let infile = BufReader::new(
|
||||||
fs::OpenOptions::new()
|
fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
@ -305,8 +320,8 @@ impl Layout {
|
|||||||
serde_yaml::from_reader(infile).map_err(Error::Yaml)
|
serde_yaml::from_reader(infile).map_err(Error::Yaml)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self)
|
pub fn build<H: WarningHandler>(self, mut warning_handler: H)
|
||||||
-> Result<::layout::LayoutData, FormattingError>
|
-> (Result<::layout::LayoutData, FormattingError>, H)
|
||||||
{
|
{
|
||||||
let button_names = self.views.values()
|
let button_names = self.views.values()
|
||||||
.flat_map(|rows| {
|
.flat_map(|rows| {
|
||||||
@ -323,7 +338,8 @@ impl Layout {
|
|||||||
create_action(
|
create_action(
|
||||||
&self.buttons,
|
&self.buttons,
|
||||||
name,
|
name,
|
||||||
self.views.keys().collect()
|
self.views.keys().collect(),
|
||||||
|
&mut warning_handler,
|
||||||
)
|
)
|
||||||
)}).collect();
|
)}).collect();
|
||||||
|
|
||||||
@ -368,13 +384,15 @@ impl Layout {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let button_states
|
let button_states = HashMap::<String, KeyState>::from_iter(
|
||||||
= HashMap::<String, KeyState>::from_iter(
|
button_states
|
||||||
button_states
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: generate from symbols
|
// TODO: generate from symbols
|
||||||
let keymap_str = generate_keymap(&button_states)?;
|
let keymap_str = match generate_keymap(&button_states) {
|
||||||
|
Err(e) => { return (Err(e), warning_handler) },
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
|
||||||
let button_states_cache = hash_map_map(
|
let button_states_cache = hash_map_map(
|
||||||
button_states,
|
button_states,
|
||||||
@ -405,7 +423,8 @@ impl Layout {
|
|||||||
name,
|
name,
|
||||||
button_states_cache.get(name.into())
|
button_states_cache.get(name.into())
|
||||||
.expect("Button state not created")
|
.expect("Button state not created")
|
||||||
.clone()
|
.clone(),
|
||||||
|
&mut warning_handler,
|
||||||
))
|
))
|
||||||
}).collect(),
|
}).collect(),
|
||||||
})
|
})
|
||||||
@ -414,116 +433,148 @@ impl Layout {
|
|||||||
)})
|
)})
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(::layout::LayoutData {
|
(
|
||||||
views: views,
|
Ok(::layout::LayoutData {
|
||||||
keymap_str: {
|
views: views,
|
||||||
CString::new(keymap_str)
|
keymap_str: {
|
||||||
.expect("Invalid keymap string generated")
|
CString::new(keymap_str)
|
||||||
},
|
.expect("Invalid keymap string generated")
|
||||||
})
|
},
|
||||||
|
}),
|
||||||
|
warning_handler,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_action(
|
fn create_action<H: WarningHandler>(
|
||||||
button_info: &HashMap<String, ButtonMeta>,
|
button_info: &HashMap<String, ButtonMeta>,
|
||||||
name: &str,
|
name: &str,
|
||||||
view_names: Vec<&String>,
|
view_names: Vec<&String>,
|
||||||
|
warning_handler: &mut H,
|
||||||
) -> ::action::Action {
|
) -> ::action::Action {
|
||||||
let default_meta = ButtonMeta::default();
|
let default_meta = ButtonMeta::default();
|
||||||
let symbol_meta = button_info.get(name)
|
let symbol_meta = button_info.get(name)
|
||||||
.unwrap_or(&default_meta);
|
.unwrap_or(&default_meta);
|
||||||
|
|
||||||
fn filter_view_name(
|
|
||||||
button_name: &str,
|
|
||||||
view_name: String,
|
|
||||||
view_names: &Vec<&String>
|
|
||||||
) -> String {
|
|
||||||
if view_names.contains(&&view_name) {
|
|
||||||
view_name
|
|
||||||
} else {
|
|
||||||
eprintln!(
|
|
||||||
"Button {} switches to missing view {}",
|
|
||||||
button_name,
|
|
||||||
view_name
|
|
||||||
);
|
|
||||||
"base".into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keysym_valid(name: &str) -> bool {
|
fn keysym_valid(name: &str) -> bool {
|
||||||
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
|
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
|
||||||
}
|
}
|
||||||
|
|
||||||
let keysyms = match &symbol_meta.action {
|
enum SubmitData {
|
||||||
// Non-submit action
|
Action(Action),
|
||||||
Some(_) => Vec::new(),
|
Text(String),
|
||||||
// Submit action
|
Keysym(String),
|
||||||
None => match &symbol_meta.keysym {
|
|
||||||
// Keysym given explicitly
|
|
||||||
Some(keysym) => vec!(match keysym_valid(keysym.as_str()) {
|
|
||||||
true => keysym.clone(),
|
|
||||||
false => {
|
|
||||||
eprintln!("Keysym name invalid: {}", keysym);
|
|
||||||
"space".into() // placeholder
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
// Keysyms left open to derive
|
|
||||||
// TODO: when button name is meant diretly as xkb keysym name,
|
|
||||||
// mark it so, e.g. with a "#"
|
|
||||||
None => match keysym_valid(name) {
|
|
||||||
// Button name is actually a valid xkb name
|
|
||||||
true => vec!(String::from(name)),
|
|
||||||
// Button name is not a valid xkb name,
|
|
||||||
// so assume it's a literal string to be submitted
|
|
||||||
false => {
|
|
||||||
if name.chars().count() == 0 {
|
|
||||||
// A name read from yaml with no valid Unicode.
|
|
||||||
// Highly improbable, but let's be safe.
|
|
||||||
eprintln!("Key {} doesn't have any characters", name);
|
|
||||||
vec!("space".into()) // placeholder
|
|
||||||
} else {
|
|
||||||
name.chars().map(|codepoint| {
|
|
||||||
let codepoint_string = codepoint.to_string();
|
|
||||||
match keysym_valid(codepoint_string.as_str()) {
|
|
||||||
true => codepoint_string,
|
|
||||||
false => format!("U{:04X}", codepoint as u32),
|
|
||||||
}
|
|
||||||
}).collect()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match &symbol_meta.action {
|
let submission = match (
|
||||||
Some(Action::SetView(view_name)) => ::action::Action::SetLevel(
|
&symbol_meta.action,
|
||||||
filter_view_name(name, view_name.clone(), &view_names)
|
&symbol_meta.keysym,
|
||||||
|
&symbol_meta.text
|
||||||
|
) {
|
||||||
|
(Some(action), None, None) => SubmitData::Action(action.clone()),
|
||||||
|
(None, Some(keysym), None) => SubmitData::Keysym(keysym.clone()),
|
||||||
|
(None, None, Some(text)) => SubmitData::Text(text.clone()),
|
||||||
|
(None, None, None) => SubmitData::Text(name.into()),
|
||||||
|
_ => {
|
||||||
|
warning_handler.handle(&format!(
|
||||||
|
"Button {} has more than one of (action, keysym, text)",
|
||||||
|
name
|
||||||
|
));
|
||||||
|
SubmitData::Text("".into())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn filter_view_name<H: WarningHandler>(
|
||||||
|
button_name: &str,
|
||||||
|
view_name: String,
|
||||||
|
view_names: &Vec<&String>,
|
||||||
|
warning_handler: &mut H,
|
||||||
|
) -> String {
|
||||||
|
if view_names.contains(&&view_name) {
|
||||||
|
view_name
|
||||||
|
} else {
|
||||||
|
warning_handler.handle(&format!("Button {} switches to missing view {}",
|
||||||
|
button_name,
|
||||||
|
view_name,
|
||||||
|
));
|
||||||
|
"base".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match submission {
|
||||||
|
SubmitData::Action(
|
||||||
|
Action::SetView(view_name)
|
||||||
|
) => ::action::Action::SetLevel(
|
||||||
|
filter_view_name(
|
||||||
|
name, view_name.clone(), &view_names,
|
||||||
|
warning_handler,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
Some(Action::Locking {
|
SubmitData::Action(Action::Locking {
|
||||||
lock_view, unlock_view
|
lock_view, unlock_view
|
||||||
}) => ::action::Action::LockLevel {
|
}) => ::action::Action::LockLevel {
|
||||||
lock: filter_view_name(name, lock_view.clone(), &view_names),
|
lock: filter_view_name(
|
||||||
|
name,
|
||||||
|
lock_view.clone(),
|
||||||
|
&view_names,
|
||||||
|
warning_handler,
|
||||||
|
),
|
||||||
unlock: filter_view_name(
|
unlock: filter_view_name(
|
||||||
name,
|
name,
|
||||||
unlock_view.clone(),
|
unlock_view.clone(),
|
||||||
&view_names
|
&view_names,
|
||||||
|
warning_handler,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Some(Action::ShowPrefs) => ::action::Action::ShowPreferences,
|
SubmitData::Action(
|
||||||
None => ::action::Action::Submit {
|
Action::ShowPrefs
|
||||||
|
) => ::action::Action::ShowPreferences,
|
||||||
|
SubmitData::Keysym(keysym) => ::action::Action::Submit {
|
||||||
text: None,
|
text: None,
|
||||||
keys: keysyms.into_iter().map(::action::KeySym).collect(),
|
keys: vec!(::action::KeySym(
|
||||||
|
match keysym_valid(keysym.as_str()) {
|
||||||
|
true => keysym.clone(),
|
||||||
|
false => {
|
||||||
|
warning_handler.handle(&format!(
|
||||||
|
"Keysym name invalid: {}",
|
||||||
|
keysym,
|
||||||
|
));
|
||||||
|
"space".into() // placeholder
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
|
SubmitData::Text(text) => ::action::Action::Submit {
|
||||||
|
text: {
|
||||||
|
CString::new(text.clone())
|
||||||
|
.map_err(|e| {
|
||||||
|
warning_handler.handle(&format!(
|
||||||
|
"Text {} contains problems: {:?}",
|
||||||
|
text,
|
||||||
|
e
|
||||||
|
));
|
||||||
|
e
|
||||||
|
}).ok()
|
||||||
|
},
|
||||||
|
keys: text.chars().map(|codepoint| {
|
||||||
|
let codepoint_string = codepoint.to_string();
|
||||||
|
::action::KeySym(match keysym_valid(codepoint_string.as_str()) {
|
||||||
|
true => codepoint_string,
|
||||||
|
false => format!("U{:04X}", codepoint as u32),
|
||||||
|
})
|
||||||
|
}).collect(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: Since this will receive user-provided data,
|
/// TODO: Since this will receive user-provided data,
|
||||||
/// all .expect() on them should be turned into soft fails
|
/// all .expect() on them should be turned into soft fails
|
||||||
fn create_button(
|
fn create_button<H: WarningHandler>(
|
||||||
button_info: &HashMap<String, ButtonMeta>,
|
button_info: &HashMap<String, ButtonMeta>,
|
||||||
outlines: &HashMap<String, Outline>,
|
outlines: &HashMap<String, Outline>,
|
||||||
name: &str,
|
name: &str,
|
||||||
state: Rc<RefCell<KeyState>>,
|
state: Rc<RefCell<KeyState>>,
|
||||||
|
warning_handler: &mut H,
|
||||||
) -> ::layout::Button {
|
) -> ::layout::Button {
|
||||||
let cname = CString::new(name.clone())
|
let cname = CString::new(name.clone())
|
||||||
.expect("Bad name");
|
.expect("Bad name");
|
||||||
@ -539,6 +590,18 @@ fn create_button(
|
|||||||
} else if let Some(icon) = &button_meta.icon {
|
} else if let Some(icon) = &button_meta.icon {
|
||||||
::layout::Label::IconName(CString::new(icon.as_str())
|
::layout::Label::IconName(CString::new(icon.as_str())
|
||||||
.expect("Bad icon"))
|
.expect("Bad icon"))
|
||||||
|
} else if let Some(text) = &button_meta.text {
|
||||||
|
::layout::Label::Text(
|
||||||
|
CString::new(text.as_str())
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
warning_handler.handle(&format!(
|
||||||
|
"Text {} is invalid: {}",
|
||||||
|
text,
|
||||||
|
e,
|
||||||
|
));
|
||||||
|
CString::new("").unwrap()
|
||||||
|
})
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
::layout::Label::Text(cname.clone())
|
::layout::Label::Text(cname.clone())
|
||||||
};
|
};
|
||||||
@ -548,7 +611,7 @@ fn create_button(
|
|||||||
if outlines.contains_key(outline) {
|
if outlines.contains_key(outline) {
|
||||||
outline.clone()
|
outline.clone()
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Outline named {} does not exist! Using default for button {}", outline, name);
|
warning_handler.handle(&format!("Outline named {} does not exist! Using default for button {}", outline, name));
|
||||||
"default".into()
|
"default".into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,7 +621,9 @@ fn create_button(
|
|||||||
let outline = outlines.get(&outline_name)
|
let outline = outlines.get(&outline_name)
|
||||||
.map(|outline| (*outline).clone())
|
.map(|outline| (*outline).clone())
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
eprintln!("No default outline defied Using 1x1!");
|
warning_handler.handle(
|
||||||
|
&format!("No default outline defined! Using 1x1!")
|
||||||
|
);
|
||||||
Outline {
|
Outline {
|
||||||
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
||||||
}
|
}
|
||||||
@ -585,6 +650,14 @@ mod tests {
|
|||||||
|
|
||||||
use std::error::Error as ErrorTrait;
|
use std::error::Error as ErrorTrait;
|
||||||
|
|
||||||
|
struct PanicWarn;
|
||||||
|
|
||||||
|
impl WarningHandler for PanicWarn {
|
||||||
|
fn handle(&mut self, warning: &str) {
|
||||||
|
panic!("{}", warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_path() {
|
fn test_parse_path() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -599,6 +672,7 @@ mod tests {
|
|||||||
icon: None,
|
icon: None,
|
||||||
keysym: None,
|
keysym: None,
|
||||||
action: None,
|
action: None,
|
||||||
|
text: None,
|
||||||
label: Some("test".into()),
|
label: Some("test".into()),
|
||||||
outline: None,
|
outline: None,
|
||||||
}
|
}
|
||||||
@ -656,7 +730,7 @@ mod tests {
|
|||||||
fn test_layout_punctuation() {
|
fn test_layout_punctuation() {
|
||||||
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
|
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build(PanicWarn).0
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out.views["base"]
|
out.views["base"]
|
||||||
@ -671,7 +745,7 @@ mod tests {
|
|||||||
fn test_layout_unicode() {
|
fn test_layout_unicode() {
|
||||||
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
|
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build(PanicWarn).0
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out.views["base"]
|
out.views["base"]
|
||||||
@ -687,7 +761,7 @@ mod tests {
|
|||||||
fn test_layout_unicode_multi() {
|
fn test_layout_unicode_multi() {
|
||||||
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
|
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build(PanicWarn).0
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out.views["base"]
|
out.views["base"]
|
||||||
@ -702,7 +776,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parsing_fallback() {
|
fn parsing_fallback() {
|
||||||
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
|
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
|
||||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
.map(|layout| layout.build(PanicWarn).0.unwrap())
|
||||||
.is_ok()
|
.is_ok()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -742,18 +816,20 @@ mod tests {
|
|||||||
".".into() => ButtonMeta {
|
".".into() => ButtonMeta {
|
||||||
icon: None,
|
icon: None,
|
||||||
keysym: None,
|
keysym: None,
|
||||||
|
text: None,
|
||||||
action: None,
|
action: None,
|
||||||
label: Some("test".into()),
|
label: Some("test".into()),
|
||||||
outline: None,
|
outline: None,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
".",
|
".",
|
||||||
Vec::new()
|
Vec::new(),
|
||||||
|
&mut PanicWarn,
|
||||||
),
|
),
|
||||||
::action::Action::Submit {
|
::action::Action::Submit {
|
||||||
text: None,
|
text: Some(CString::new(".").unwrap()),
|
||||||
keys: vec!(::action::KeySym("U002E".into())),
|
keys: vec!(::action::KeySym("U002E".into())),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,15 @@ pub struct KeyState {
|
|||||||
pub action: Action,
|
pub action: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sorts an iterator by converting it to a Vector and back
|
||||||
|
fn sorted<'a, I: Iterator<Item=&'a str>>(
|
||||||
|
iter: I
|
||||||
|
) -> impl Iterator<Item=&'a str> {
|
||||||
|
let mut v: Vec<&'a str> = iter.collect();
|
||||||
|
v.sort();
|
||||||
|
v.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
/// Generates a mapping where each key gets a keycode, starting from ~~8~~
|
||||||
/// HACK: starting from 9, because 8 results in keycode 0,
|
/// HACK: starting from 9, because 8 results in keycode 0,
|
||||||
/// which the compositor likes to discard
|
/// which the compositor likes to discard
|
||||||
@ -58,7 +67,8 @@ pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
|
|||||||
key_names: C
|
key_names: C
|
||||||
) -> HashMap<String, u32> {
|
) -> HashMap<String, u32> {
|
||||||
HashMap::from_iter(
|
HashMap::from_iter(
|
||||||
key_names.into_iter()
|
// sort to remove a source of indeterminism in keycode assignment
|
||||||
|
sorted(key_names.into_iter())
|
||||||
.map(|name| String::from(name))
|
.map(|name| String::from(name))
|
||||||
.zip(9..)
|
.zip(9..)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -24,5 +24,7 @@ mod outputs;
|
|||||||
mod popover;
|
mod popover;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod submission;
|
mod submission;
|
||||||
mod util;
|
mod style;
|
||||||
|
pub mod tests;
|
||||||
|
pub mod util;
|
||||||
mod xdg;
|
mod xdg;
|
||||||
|
|||||||
@ -58,7 +58,7 @@ rslibs = custom_target(
|
|||||||
output: ['librs.a'],
|
output: ['librs.a'],
|
||||||
install: false,
|
install: false,
|
||||||
console: true,
|
console: true,
|
||||||
command: [cargo_script, '@OUTPUT@', 'build']
|
command: [cargo_build] + cargo_build_flags + ['@OUTPUT@', '--lib']
|
||||||
)
|
)
|
||||||
|
|
||||||
build_rstests = custom_target(
|
build_rstests = custom_target(
|
||||||
@ -72,14 +72,14 @@ build_rstests = custom_target(
|
|||||||
output: ['src'],
|
output: ['src'],
|
||||||
install: false,
|
install: false,
|
||||||
console: true,
|
console: true,
|
||||||
command: [cargo_script, '', 'test', '--no-run'],
|
command: [cargo_script, 'test', '--no-run'],
|
||||||
depends: rslibs, # no point building tests if the code itself fails
|
depends: rslibs, # no point building tests if the code itself fails
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'rstest',
|
'rstest',
|
||||||
cargo_script,
|
cargo_script,
|
||||||
args: ['', 'test'],
|
args: ['test'],
|
||||||
# this is a whole Carg-based test suite, let it run for a while
|
# this is a whole Carg-based test suite, let it run for a while
|
||||||
timeout: 900,
|
timeout: 900,
|
||||||
depends: build_rstests,
|
depends: build_rstests,
|
||||||
@ -97,20 +97,7 @@ libsqueekboard = static_library('libsqueekboard',
|
|||||||
'-DEEK_COMPILATION=1'],
|
'-DEEK_COMPILATION=1'],
|
||||||
)
|
)
|
||||||
|
|
||||||
# the straight binary needs to be demoted in favor of the wrapper script
|
squeekboard = executable('squeekboard',
|
||||||
# due to styling being inconsistent
|
|
||||||
bindir = join_paths(prefix, get_option('bindir'))
|
|
||||||
wrapper_conf = configuration_data()
|
|
||||||
wrapper_conf.set('bindir', bindir)
|
|
||||||
configure_file(
|
|
||||||
input: '../tools/squeekboard.in',
|
|
||||||
output: 'squeekboard',
|
|
||||||
install_dir: bindir,
|
|
||||||
configuration: wrapper_conf,
|
|
||||||
install: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
squeekboard = executable('squeekboard-real',
|
|
||||||
'server-main.c',
|
'server-main.c',
|
||||||
wl_proto_sources,
|
wl_proto_sources,
|
||||||
squeekboard_resources,
|
squeekboard_resources,
|
||||||
@ -124,3 +111,17 @@ squeekboard = executable('squeekboard-real',
|
|||||||
'-DEEKBOARD_COMPILATION=1',
|
'-DEEKBOARD_COMPILATION=1',
|
||||||
'-DEEK_COMPILATION=1'],
|
'-DEEK_COMPILATION=1'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
bindir = join_paths(prefix, get_option('bindir'))
|
||||||
|
|
||||||
|
test_layout = custom_target('squeekboard-test-layout',
|
||||||
|
build_by_default: true,
|
||||||
|
# meson doesn't track all inputs, cargo does
|
||||||
|
build_always_stale: true,
|
||||||
|
output: ['squeekboard-test-layout'],
|
||||||
|
console: true,
|
||||||
|
command: [cargo_build] + cargo_build_flags
|
||||||
|
+ ['--rename', 'test_layout', '@OUTPUT@', '--bin', 'test_layout'],
|
||||||
|
install: true,
|
||||||
|
install_dir: bindir,
|
||||||
|
)
|
||||||
|
|||||||
115
src/popover.rs
115
src/popover.rs
@ -7,9 +7,9 @@ use ::locale::compare_current_locale;
|
|||||||
use ::locale_config::system_locale;
|
use ::locale_config::system_locale;
|
||||||
use ::resources;
|
use ::resources;
|
||||||
|
|
||||||
use gio::ActionExt;
|
|
||||||
use gio::ActionMapExt;
|
use gio::ActionMapExt;
|
||||||
use gio::SettingsExt;
|
use gio::SettingsExt;
|
||||||
|
use gio::SimpleActionExt;
|
||||||
use glib::translate::FromGlibPtrNone;
|
use glib::translate::FromGlibPtrNone;
|
||||||
use glib::variant::ToVariant;
|
use glib::variant::ToVariant;
|
||||||
use gtk::PopoverExt;
|
use gtk::PopoverExt;
|
||||||
@ -18,8 +18,11 @@ use std::io::Write;
|
|||||||
|
|
||||||
mod variants {
|
mod variants {
|
||||||
use glib;
|
use glib;
|
||||||
|
use glib::Variant;
|
||||||
use glib_sys;
|
use glib_sys;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
use glib::ToVariant;
|
||||||
use glib::translate::FromGlibPtrFull;
|
use glib::translate::FromGlibPtrFull;
|
||||||
use glib::translate::ToGlibPtr;
|
use glib::translate::ToGlibPtr;
|
||||||
|
|
||||||
@ -49,6 +52,44 @@ mod variants {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// "a(ss)" variant
|
||||||
|
/// Rust doesn't allow implementing existing traits for existing types
|
||||||
|
pub struct ArrayPairString(pub Vec<(String, String)>);
|
||||||
|
|
||||||
|
impl ToVariant for ArrayPairString {
|
||||||
|
fn to_variant(&self) -> Variant {
|
||||||
|
let tspec = "a(ss)".to_glib_none();
|
||||||
|
let builder = unsafe {
|
||||||
|
let vtype = glib_sys::g_variant_type_checked_(tspec.0);
|
||||||
|
glib_sys::g_variant_builder_new(vtype)
|
||||||
|
};
|
||||||
|
let ispec = "(ss)".to_glib_none();
|
||||||
|
for (a, b) in &self.0 {
|
||||||
|
let a = a.to_glib_none();
|
||||||
|
let b = b.to_glib_none();
|
||||||
|
// string pointers are weak references
|
||||||
|
// and will get silently invalidated
|
||||||
|
// as soon as the source is out of scope
|
||||||
|
{
|
||||||
|
let a: *const c_char = a.0;
|
||||||
|
let b: *const c_char = b.0;
|
||||||
|
unsafe {
|
||||||
|
glib_sys::g_variant_builder_add(
|
||||||
|
builder,
|
||||||
|
ispec.0,
|
||||||
|
a, b
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let ret = glib_sys::g_variant_builder_end(builder);
|
||||||
|
glib_sys::g_variant_builder_unref(builder);
|
||||||
|
glib::Variant::from_glib_full(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
||||||
@ -88,12 +129,15 @@ fn make_menu_builder(inputs: Vec<(&str, &str)>) -> gtk::Builder {
|
|||||||
fn set_layout(kind: String, name: String) {
|
fn set_layout(kind: String, name: String) {
|
||||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||||
let inputs = settings.get_value("sources").unwrap();
|
let inputs = settings.get_value("sources").unwrap();
|
||||||
let inputs = variants::get_tuples(inputs).into_iter();
|
let current = (kind.clone(), name.clone());
|
||||||
for (index, (ikind, iname)) in inputs.enumerate() {
|
let inputs = variants::get_tuples(inputs).into_iter()
|
||||||
if (&ikind, &iname) == (&kind, &name) {
|
.filter(|t| t != ¤t);
|
||||||
settings.set_uint("current", index as u32);
|
let inputs = vec![(kind, name)].into_iter()
|
||||||
}
|
.chain(inputs).collect();
|
||||||
}
|
settings.set_value(
|
||||||
|
"sources",
|
||||||
|
&variants::ArrayPairString(inputs).to_variant()
|
||||||
|
);
|
||||||
settings.apply();
|
settings.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +147,6 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
|||||||
|
|
||||||
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
let settings = gio::Settings::new("org.gnome.desktop.input-sources");
|
||||||
let inputs = settings.get_value("sources").unwrap();
|
let inputs = settings.get_value("sources").unwrap();
|
||||||
let current = settings.get_uint("current") as usize;
|
|
||||||
let inputs = variants::get_tuples(inputs);
|
let inputs = variants::get_tuples(inputs);
|
||||||
|
|
||||||
let input_names: Vec<&str> = inputs.iter()
|
let input_names: Vec<&str> = inputs.iter()
|
||||||
@ -152,39 +195,35 @@ pub fn show(window: EekGtkKeyboard, position: ::layout::c::Bounds) {
|
|||||||
height: position.width.floor() as i32,
|
height: position.width.floor() as i32,
|
||||||
});
|
});
|
||||||
|
|
||||||
let initial_state = input_names[current].to_variant();
|
if let Some(current_name) = input_names.get(0) {
|
||||||
|
let current_name = current_name.to_variant();
|
||||||
|
|
||||||
let layout_action = gio::SimpleAction::new_stateful(
|
let layout_action = gio::SimpleAction::new_stateful(
|
||||||
"layout",
|
"layout",
|
||||||
Some(initial_state.type_()),
|
Some(current_name.type_()),
|
||||||
&initial_state,
|
¤t_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
let action_group = gio::SimpleActionGroup::new();
|
layout_action.connect_change_state(|_action, state| {
|
||||||
action_group.add_action(&layout_action);
|
match state {
|
||||||
|
Some(v) => {
|
||||||
|
v.get::<String>()
|
||||||
|
.or_else(|| {
|
||||||
|
eprintln!("Variant is not string: {:?}", v);
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.map(|state| set_layout("xkb".into(), state));
|
||||||
|
},
|
||||||
|
None => eprintln!("No variant selected"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let action_group = gio::SimpleActionGroup::new();
|
||||||
|
action_group.add_action(&layout_action);
|
||||||
|
|
||||||
|
menu.insert_action_group("popup", Some(&action_group));
|
||||||
|
};
|
||||||
|
|
||||||
menu.insert_action_group("popup", Some(&action_group));
|
|
||||||
menu.bind_model(Some(&model), Some("popup"));
|
menu.bind_model(Some(&model), Some("popup"));
|
||||||
|
|
||||||
menu.connect_closed(move |_menu| {
|
|
||||||
let state = match layout_action.get_state() {
|
|
||||||
Some(v) => {
|
|
||||||
let s = v.get::<String>().or_else(|| {
|
|
||||||
eprintln!("Variant is not string: {:?}", v);
|
|
||||||
None
|
|
||||||
});
|
|
||||||
// FIXME: the `get_state` docs call for unrefing,
|
|
||||||
// but the function is nowhere to be found
|
|
||||||
// glib::Variant::unref(v);
|
|
||||||
s
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
eprintln!("No variant selected");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
};
|
|
||||||
set_layout("xkb".into(), state.unwrap_or("us".into()));
|
|
||||||
});
|
|
||||||
|
|
||||||
menu.popup();
|
menu.popup();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const KEYBOARDS: &[(*const str, *const str)] = &[
|
|||||||
("us", include_str!("../data/keyboards/us.yaml")),
|
("us", include_str!("../data/keyboards/us.yaml")),
|
||||||
("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
|
("us_wide", include_str!("../data/keyboards/us_wide.yaml")),
|
||||||
("de", include_str!("../data/keyboards/de.yaml")),
|
("de", include_str!("../data/keyboards/de.yaml")),
|
||||||
|
("de_wide", include_str!("../data/keyboards/de_wide.yaml")),
|
||||||
("el", include_str!("../data/keyboards/el.yaml")),
|
("el", include_str!("../data/keyboards/el.yaml")),
|
||||||
("es", include_str!("../data/keyboards/es.yaml")),
|
("es", include_str!("../data/keyboards/es.yaml")),
|
||||||
("fi", include_str!("../data/keyboards/fi.yaml")),
|
("fi", include_str!("../data/keyboards/fi.yaml")),
|
||||||
@ -42,6 +43,7 @@ const LAYOUT_NAMES: &[(*const str, *const str)] = &[
|
|||||||
("de-DE", include_str!("../data/langs/de-DE.txt")),
|
("de-DE", include_str!("../data/langs/de-DE.txt")),
|
||||||
("en-US", include_str!("../data/langs/en-US.txt")),
|
("en-US", include_str!("../data/langs/en-US.txt")),
|
||||||
("es-ES", include_str!("../data/langs/es-ES.txt")),
|
("es-ES", include_str!("../data/langs/es-ES.txt")),
|
||||||
|
("ja-JP", include_str!("../data/langs/ja-JP.txt")),
|
||||||
("pl-PL", include_str!("../data/langs/pl-PL.txt")),
|
("pl-PL", include_str!("../data/langs/pl-PL.txt")),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
7
src/style.h
Normal file
7
src/style.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef __STYLE_H
|
||||||
|
#define __STYLE_H
|
||||||
|
#include "gtk/gtk.h"
|
||||||
|
|
||||||
|
GtkCssProvider *squeek_load_style();
|
||||||
|
|
||||||
|
#endif
|
||||||
124
src/style.rs
Normal file
124
src/style.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2000 Red Hat, Inc.
|
||||||
|
* Copyright (C) 2019 Purism, SPC
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! CSS data loading */
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use glib::object::ObjectExt;
|
||||||
|
use util::Warn;
|
||||||
|
|
||||||
|
/// Gathers stuff defined in C or called by C
|
||||||
|
pub mod c {
|
||||||
|
use super::*;
|
||||||
|
use gio;
|
||||||
|
use gtk;
|
||||||
|
use gtk_sys;
|
||||||
|
|
||||||
|
use gtk::CssProviderExt;
|
||||||
|
use glib::translate::ToGlibPtr;
|
||||||
|
|
||||||
|
/// Loads the layout style based on current theme
|
||||||
|
/// without having to worry about string allocation
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_load_style() -> *const gtk_sys::GtkCssProvider {
|
||||||
|
unsafe { gtk::set_initialized() };
|
||||||
|
let theme = gtk::Settings::get_default()
|
||||||
|
.map(|settings| get_theme_name(&settings));
|
||||||
|
|
||||||
|
let css_name = path_from_theme(theme);
|
||||||
|
|
||||||
|
let resource_name = if gio::resources_get_info(
|
||||||
|
&css_name,
|
||||||
|
gio::ResourceLookupFlags::NONE
|
||||||
|
).is_ok() {
|
||||||
|
css_name
|
||||||
|
} else { // use default if this path doesn't exist
|
||||||
|
path_from_theme(None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let provider = gtk::CssProvider::new();
|
||||||
|
provider.load_from_resource(&resource_name);
|
||||||
|
provider.to_glib_full()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not Adwaita, but rather fall back to default
|
||||||
|
const DEFAULT_THEME_NAME: &str = "";
|
||||||
|
|
||||||
|
struct GtkTheme {
|
||||||
|
name: String,
|
||||||
|
variant: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets theme as determined by the toolkit
|
||||||
|
/// Ported from GTK's gtksettings.c
|
||||||
|
fn get_theme_name(settings: >k::Settings) -> GtkTheme {
|
||||||
|
let env_theme = env::var("GTK_THEME")
|
||||||
|
.map(|theme| {
|
||||||
|
let mut parts = theme.splitn(2, ":");
|
||||||
|
GtkTheme {
|
||||||
|
// guaranteed at least empty string
|
||||||
|
// as the first result from splitting a string
|
||||||
|
name: parts.next().unwrap().into(),
|
||||||
|
variant: parts.next().map(String::from)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| {
|
||||||
|
match &e {
|
||||||
|
env::VarError::NotPresent => {},
|
||||||
|
e => eprintln!("GTK_THEME variable invalid: {}", e),
|
||||||
|
};
|
||||||
|
e
|
||||||
|
}).ok();
|
||||||
|
|
||||||
|
match env_theme {
|
||||||
|
Some(theme) => theme,
|
||||||
|
None => GtkTheme {
|
||||||
|
name: {
|
||||||
|
settings.get_property("gtk-theme-name")
|
||||||
|
.ok_warn("No theme name")
|
||||||
|
.and_then(|value| value.get::<String>())
|
||||||
|
.unwrap_or(DEFAULT_THEME_NAME.into())
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
settings.get_property("gtk-application-prefer-dark-theme")
|
||||||
|
.ok_warn("No settings key")
|
||||||
|
.and_then(|value| value.get::<bool>())
|
||||||
|
.and_then(|dark_preferred| match dark_preferred {
|
||||||
|
true => Some("dark".into()),
|
||||||
|
false => None,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_from_theme(theme: Option<GtkTheme>) -> String {
|
||||||
|
format!(
|
||||||
|
"/sm/puri/squeekboard/style{}.css",
|
||||||
|
match theme {
|
||||||
|
Some(GtkTheme { name, variant: Some(variant) }) => {
|
||||||
|
format!("-{}:{}", name, variant)
|
||||||
|
},
|
||||||
|
Some(GtkTheme { name, variant: None }) => format!("-{}", name),
|
||||||
|
None => "".into(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
78
src/tests.rs
Normal file
78
src/tests.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*! Testing functionality */
|
||||||
|
|
||||||
|
use ::data::Layout;
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
use ::util::WarningHandler;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct CountAndPrint(u32);
|
||||||
|
|
||||||
|
impl WarningHandler for CountAndPrint {
|
||||||
|
fn handle(&mut self, warning: &str) {
|
||||||
|
self.0 = self.0 + 1;
|
||||||
|
println!("{}", warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CountAndPrint {
|
||||||
|
fn new() -> CountAndPrint {
|
||||||
|
CountAndPrint(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_builtin_layout(name: &str) {
|
||||||
|
check_layout(Layout::from_resource(name).expect("Invalid layout data"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_layout_file(path: &str) {
|
||||||
|
check_layout(Layout::from_file(path.into()).expect("Invalid layout file"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_layout(layout: Layout) {
|
||||||
|
let handler = CountAndPrint::new();
|
||||||
|
let (layout, handler) = layout.build(handler);
|
||||||
|
|
||||||
|
if handler.0 > 0 {
|
||||||
|
println!("{} mistakes in layout", handler.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = layout.expect("layout broken");
|
||||||
|
|
||||||
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
|
|
||||||
|
let keymap_str = layout.keymap_str
|
||||||
|
.clone()
|
||||||
|
.into_string().expect("Failed to decode keymap string");
|
||||||
|
|
||||||
|
let keymap = xkb::Keymap::new_from_string(
|
||||||
|
&context,
|
||||||
|
keymap_str.clone(),
|
||||||
|
xkb::KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||||
|
).expect("Failed to create keymap");
|
||||||
|
|
||||||
|
let state = xkb::State::new(&keymap);
|
||||||
|
|
||||||
|
// "Press" each button with keysyms
|
||||||
|
for view in layout.views.values() {
|
||||||
|
for row in &view.rows {
|
||||||
|
for button in &row.buttons {
|
||||||
|
let keystate = button.state.borrow();
|
||||||
|
for keycode in &keystate.keycodes {
|
||||||
|
match state.key_get_one_sym(*keycode) {
|
||||||
|
xkb::KEY_NoSymbol => {
|
||||||
|
eprintln!("{}", keymap_str);
|
||||||
|
panic!("Keysym {} on key {:?} can't be resolved", keycode, button.name);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler.0 > 0 {
|
||||||
|
panic!("Layout contains mistakes");
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/util.rs
21
src/util.rs
@ -177,6 +177,27 @@ impl<T> Borrow<Rc<T>> for Pointer<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sugar for logging errors in results
|
||||||
|
pub trait Warn {
|
||||||
|
type Value;
|
||||||
|
fn ok_warn(self, msg: &str) -> Option<Self::Value>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: std::error::Error> Warn for Result<T, E> {
|
||||||
|
type Value = T;
|
||||||
|
fn ok_warn(self, msg: &str) -> Option<T> {
|
||||||
|
self.map_err(|e| {
|
||||||
|
eprintln!("{}: {}", msg, e);
|
||||||
|
e
|
||||||
|
}).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WarningHandler {
|
||||||
|
/// Handle a warning
|
||||||
|
fn handle(&mut self, warning: &str);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@ -49,7 +49,7 @@ endforeach
|
|||||||
# and the need to call it manually
|
# and the need to call it manually
|
||||||
foreach layout : [
|
foreach layout : [
|
||||||
'us', 'us_wide',
|
'us', 'us_wide',
|
||||||
'de',
|
'de', 'de_wide',
|
||||||
'el',
|
'el',
|
||||||
'es',
|
'es',
|
||||||
'fi',
|
'fi',
|
||||||
@ -62,7 +62,7 @@ foreach layout : [
|
|||||||
test(
|
test(
|
||||||
'test_layout_' + layout,
|
'test_layout_' + layout,
|
||||||
cargo_script,
|
cargo_script,
|
||||||
args: ['', 'run', '--example', 'test_layout', layout]
|
args: ['run', '--example', 'test_layout', layout]
|
||||||
)
|
)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
|
|||||||
@ -12,4 +12,4 @@ for DIR in ${DIRS}; do
|
|||||||
fi;
|
fi;
|
||||||
done;
|
done;
|
||||||
|
|
||||||
exec @bindir@/squeekboard-real
|
exec $(which squeekboard)
|
||||||
Reference in New Issue
Block a user