// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: MIT import { Button, Palette } from "std-widgets.slint"; import { Icons } from "icons.slint"; component VirtualKeyboardButton { in property key; in property icon; callback key-pressed(/* key */ string); min-width: 32px; min-height: 32px; horizontal-stretch: 0; states [ pressed when i-touch-area.pressed : { i-state-area.opacity: 0.5; } ] i-container := Rectangle { border-radius: 4px; background: Palette.color-scheme == ColorScheme.dark ? #373737 : #ffffff; HorizontalLayout { padding: 8px; if (root.key != "") : Text { text: root.key; color: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000; font-size: 12px; vertical-alignment: center; horizontal-alignment: center; } if (root.key == "") : Image { y: (parent.height - self.height) / 2; source: root.icon; height: 18px; colorize: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000; } } } i-state-area := Rectangle { border-radius: i-container.border-radius; opacity: 0; background: #000000; animate opacity { duration: 150ms; } } i-touch-area := TouchArea { pointer-event(event) => { if(event.kind == PointerEventKind.down) { root.key-pressed(key); } } } } export struct KeyModel { key: string, shift-key: string, } export global VirtualKeyboardHandler { in property enabled; in property <[[[KeyModel]]]> default-key-sets: [ [ [ { key: "q", shift-key: "Q" }, { key: "w", shift-key: "W" }, { key: "e", shift-key: "E" }, { key: "r", shift-key: "R" }, { key: "t", shift-key: "T" }, { key: "y", shift-key: "Y" }, { key: "u", shift-key: "U" }, { key: "i", shift-key: "I" }, { key: "o", shift-key: "O" }, { key: "p", shift-key: "P" } ], [ { key: "a", shift-key: "A" }, { key: "s", shift-key: "S" }, { key: "d", shift-key: "D" }, { key: "f", shift-key: "F" }, { key: "g", shift-key: "G" }, { key: "h", shift-key: "H" }, { key: "j", shift-key: "J" }, { key: "k", shift-key: "K" }, { key: "l", shift-key: "L" } ], [ { key: "z", shift-key: "Z" }, { key: "x", shift-key: "X" }, { key: "c", shift-key: "C" }, { key: "v", shift-key: "V" }, { key: "b", shift-key: "B" }, { key: "n", shift-key: "N" }, { key: "m", shift-key: "M" }, { key: ",", shift-key: ";" }, { key: ".", shift-key: ":" }, { key: "?", shift-key: "?" } ], ], [ [ { key: "1", shift-key: "[" }, { key: "2", shift-key: "]" }, { key: "3", shift-key: "{" }, { key: "4", shift-key: "}" }, { key: "5", shift-key: "#" }, { key: "6", shift-key: "%" }, { key: "7", shift-key: "^" }, { key: "8", shift-key: "*" }, { key: "9", shift-key: "+" }, { key: "0", shift-key: "=" } ], [ { key: "-", shift-key: "_" }, { key: "/", shift-key: "\\" }, { key: ":", shift-key: "|" }, { key: ";", shift-key: "~" }, { key: "(", shift-key: "<" }, { key: ")", shift-key: ">" }, { key: "€", shift-key: "$" }, { key: "&", shift-key: "€" }, { key: "@", shift-key: "°" }, { key: "'", shift-key: "#" }, ], [ { key: ".", shift-key: "." }, { key: ",", shift-key: "," }, { key: "?", shift-key: "?" }, { key: "!", shift-key: "!" }, { key: "'", shift-key: "'" }, ], ] ]; out property current-key-set; out property <[[KeyModel]]> keys: default-key-sets[self.current-key-set]; in-out property open; callback key_pressed(/* key */ string); public function switch-keyboard() { if (self.current-key-set < self.default-key-sets.length - 1) { self.current-key-set += 1; } else { self.current-key-set -= 1; } self.current-key-set = min(self.default-key-sets.length - 1, max(0, self.current-key-set)) } } export component VirtualKeyboard { private property shift; callback close(); preferred-width: 100%; TouchArea {} Rectangle { background: Palette.color-scheme == ColorScheme.dark ? #1c1c1c : #d4d4d4; height: 100%; } i-layout := VerticalLayout { padding: 8px; spacing: 4px; for row[index] in VirtualKeyboardHandler.keys : HorizontalLayout { spacing: 4px; if (index == 0) : VirtualKeyboardButton { key: "ESC"; key-pressed => { VirtualKeyboardHandler.key-pressed(Key.Escape); } } if (index == 1) : VirtualKeyboardButton { key: "Tab"; key-pressed => { VirtualKeyboardHandler.key-pressed(Key.Tab); } } // shift if (index == 2) : VirtualKeyboardButton { icon: Icons.arrow-up; key-pressed => { root.shift = !root.shift; } } for km in row : VirtualKeyboardButton { key: root.shift ? km.shift-key : km.key; key-pressed(key) => { VirtualKeyboardHandler.key-pressed(key); root.shift = false; } } if (index == 0) : VirtualKeyboardButton { icon: Icons.chevron-left; key-pressed => { VirtualKeyboardHandler.key-pressed(Key.Backspace); } } if (index == 1) : VirtualKeyboardButton { icon: Icons.arrow-circle-o-left; key-pressed => { VirtualKeyboardHandler.key-pressed(Key.Return); } } // shift if (index == 2) : VirtualKeyboardButton { icon: Icons.arrow-up; key-pressed => { root.shift = !root.shift; } } } HorizontalLayout { spacing: 4px; VirtualKeyboardButton { icon: Icons.expand-more; key-pressed(key) => { root.close(); } } VirtualKeyboardButton { icon: Icons.globe; key-pressed(key) => { VirtualKeyboardHandler.switch-keyboard(); } } VirtualKeyboardButton { horizontal-stretch: 1; key: " "; key-pressed(key) => { root.shift = false; VirtualKeyboardHandler.key-pressed(key); } } VirtualKeyboardButton { icon: Icons.arrow-left; key-pressed(key) => { VirtualKeyboardHandler.key-pressed(Key.LeftArrow); } } VirtualKeyboardButton { icon: Icons.arrow-right; key-pressed(key) => { VirtualKeyboardHandler.key-pressed(Key.RightArrow); } } } } animate y { duration: 500ms; easing: cubic-bezier(0.05, 0.7, 0.1, 1.0); } }