KeSp_controller/ui/tabs/tab_stats.slint
Mae PUGIN 32ee3a6d26 feat: Complete KeSp Controller — Slint UI port
Full port of the KaSe/KeSp split keyboard configurator from egui to Slint:
- 6 tabs: Keymap, Advanced, Macros, Stats, Settings, Flash
- Responsive keyboard view with scale-to-fit and key rotations
- Key selector popup with categorized grid, MT/LT builders, hex input
- Combo key picker with inline keyboard visual
- Macro step builder with visual tags
- Serial communication via background threads + mpsc polling
- Heatmap overlay with blue-yellow-red gradient
- OTA flasher with prog port VID filtering and partition selector
- WPM polling, Tamagotchi, Autoshift controls
- Dracula theme matching egui version

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 20:40:34 +02:00

332 lines
12 KiB
Text

import { Button, ScrollView } from "std-widgets.slint";
import { Theme } from "../theme.slint";
import { StatsBridge, AppState, ConnectionState } from "../globals.slint";
component BarChart inherits Rectangle {
in property <float> value; // 0-100
in property <string> label;
in property <color> bar-color: Theme.accent-purple;
height: 28px;
HorizontalLayout {
spacing: 8px;
Text {
width: 80px;
text: root.label;
color: Theme.fg-secondary;
font-size: 11px;
vertical-alignment: center;
horizontal-alignment: right;
}
Rectangle {
horizontal-stretch: 1;
background: Theme.bg-primary;
border-radius: 3px;
Rectangle {
x: 0;
width: parent.width * clamp(root.value / 100, 0, 1);
height: 100%;
background: root.bar-color;
border-radius: 3px;
}
}
Text {
width: 50px;
text: round(root.value * 10) / 10 + "%";
color: Theme.fg-primary;
font-size: 11px;
vertical-alignment: center;
}
}
}
export component TabStats inherits Rectangle {
background: Theme.bg-primary;
ScrollView {
VerticalLayout {
padding: 16px;
spacing: 12px;
// Header
HorizontalLayout {
spacing: 8px;
Text {
text: "Typing Statistics";
color: Theme.fg-primary;
font-size: 20px;
font-weight: 700;
vertical-alignment: center;
}
Rectangle { horizontal-stretch: 1; }
Button {
text: "Refresh";
enabled: AppState.connection == ConnectionState.connected;
clicked => { StatsBridge.refresh-stats(); }
}
}
// Content in two columns
HorizontalLayout {
spacing: 12px;
vertical-stretch: 1;
// Left column
VerticalLayout {
horizontal-stretch: 1;
spacing: 12px;
// Hand balance
Rectangle {
background: Theme.bg-secondary;
border-radius: 8px;
vertical-stretch: 0;
VerticalLayout {
padding: 12px;
spacing: 6px;
Text {
text: "Hand Balance";
color: Theme.accent-cyan;
font-size: 13px;
font-weight: 600;
}
HorizontalLayout {
spacing: 4px;
// Left bar
Rectangle {
horizontal-stretch: StatsBridge.hand-balance.left-pct > 0 ? round(StatsBridge.hand-balance.left-pct) : 1;
height: 24px;
background: Theme.accent-purple;
border-radius: 3px;
Text {
text: "L " + round(StatsBridge.hand-balance.left-pct) + "%";
color: Theme.fg-primary;
font-size: 11px;
horizontal-alignment: center;
vertical-alignment: center;
}
}
// Right bar
Rectangle {
horizontal-stretch: StatsBridge.hand-balance.right-pct > 0 ? round(StatsBridge.hand-balance.right-pct) : 1;
height: 24px;
background: Theme.accent-green;
border-radius: 3px;
Text {
text: "R " + round(StatsBridge.hand-balance.right-pct) + "%";
color: Theme.bg-primary;
font-size: 11px;
horizontal-alignment: center;
vertical-alignment: center;
}
}
}
Text {
text: "Total: " + StatsBridge.hand-balance.total + " presses";
color: Theme.fg-secondary;
font-size: 11px;
}
}
}
// Bigrams
if StatsBridge.bigrams.total > 0 : Rectangle {
background: Theme.bg-secondary;
border-radius: 8px;
vertical-stretch: 0;
VerticalLayout {
padding: 12px;
spacing: 6px;
Text {
text: "Bigram Analysis";
color: Theme.accent-cyan;
font-size: 13px;
font-weight: 600;
}
BarChart {
label: "Alt Hand";
value: StatsBridge.bigrams.alt-hand-pct;
bar-color: Theme.accent-green;
}
BarChart {
label: "Same Hand";
value: StatsBridge.bigrams.same-hand-pct;
bar-color: Theme.accent-orange;
}
BarChart {
label: "SFB";
value: StatsBridge.bigrams.sfb-pct;
bar-color: Theme.accent-red;
}
}
}
// Finger load
Rectangle {
background: Theme.bg-secondary;
border-radius: 8px;
vertical-stretch: 1;
VerticalLayout {
padding: 12px;
spacing: 4px;
Text {
text: "Finger Load";
color: Theme.accent-cyan;
font-size: 13px;
font-weight: 600;
}
for finger in StatsBridge.finger-load : BarChart {
label: finger.name;
value: finger.pct;
bar-color: Theme.accent-orange;
}
}
}
}
// Right column
VerticalLayout {
horizontal-stretch: 1;
spacing: 12px;
// Row usage
Rectangle {
background: Theme.bg-secondary;
border-radius: 8px;
vertical-stretch: 0;
VerticalLayout {
padding: 12px;
spacing: 4px;
Text {
text: "Row Usage";
color: Theme.accent-cyan;
font-size: 13px;
font-weight: 600;
}
for row in StatsBridge.row-usage : BarChart {
label: row.name;
value: row.pct;
bar-color: Theme.accent-green;
}
}
}
// Top keys
Rectangle {
background: Theme.bg-secondary;
border-radius: 8px;
vertical-stretch: 1;
VerticalLayout {
padding: 12px;
spacing: 4px;
Text {
text: "Top Keys";
color: Theme.accent-cyan;
font-size: 13px;
font-weight: 600;
}
for key in StatsBridge.top-keys : HorizontalLayout {
height: 22px;
spacing: 8px;
Text {
width: 80px;
text: key.name;
color: Theme.fg-primary;
font-size: 11px;
vertical-alignment: center;
}
Text {
width: 60px;
text: key.finger;
color: Theme.fg-secondary;
font-size: 10px;
vertical-alignment: center;
}
Rectangle {
horizontal-stretch: 1;
background: Theme.bg-primary;
border-radius: 3px;
Rectangle {
x: 0;
width: parent.width * clamp(key.pct / 20, 0, 1);
height: 100%;
background: Theme.accent-yellow;
border-radius: 3px;
}
}
Text {
width: 50px;
text: key.count;
color: Theme.fg-primary;
font-size: 11px;
vertical-alignment: center;
horizontal-alignment: right;
}
}
}
}
// Dead keys
if StatsBridge.dead-keys.length > 0 : Rectangle {
background: Theme.bg-secondary;
border-radius: 8px;
vertical-stretch: 0;
VerticalLayout {
padding: 12px;
spacing: 4px;
Text {
text: "Dead Keys (never pressed)";
color: Theme.accent-red;
font-size: 13px;
font-weight: 600;
}
HorizontalLayout {
spacing: 4px;
for dk in StatsBridge.dead-keys : Rectangle {
width: self.preferred-width + 12px;
preferred-width: dk-text.preferred-width;
height: 22px;
background: Theme.bg-primary;
border-radius: 3px;
dk-text := Text {
text: dk;
color: Theme.accent-red;
font-size: 10px;
horizontal-alignment: center;
vertical-alignment: center;
}
}
}
}
}
}
}
}
}
}