This commit is contained in:
2026-06-19 03:09:53 +08:00
Unverified
commit d32882443f
49 changed files with 19399 additions and 0 deletions

174
ui/widgets.slint Normal file
View File

@@ -0,0 +1,174 @@
import { Theme } from "theme.slint";
// --- IconButton ------------------------------------------------------------
// A small square action button that accepts a single-character glyph (or short text).
// Keeps visual language consistent across the app.
export component IconButton inherits Rectangle {
in property <string> glyph;
in property <string> tooltip;
in property <length> size: 28px;
in property <brush> fg: Theme.text-secondary;
in property <brush> fg-hover: Theme.text-primary;
callback clicked();
width: size;
height: size;
border-radius: Theme.radius-sm;
background: touch.has-hover ? Theme.bg-hover : transparent;
animate background { duration: 120ms; }
Text {
width: parent.width;
height: parent.height;
text: glyph;
color: touch.has-hover ? fg-hover : fg;
font-size: Theme.fs-md;
horizontal-alignment: center;
vertical-alignment: center;
}
touch := TouchArea {
mouse-cursor: pointer;
clicked => { root.clicked(); }
}
}
// --- PrimaryButton ---------------------------------------------------------
export component PrimaryButton inherits Rectangle {
in property <string> text;
in property <bool> disabled: false;
callback clicked();
height: 30px;
min-width: 80px;
border-radius: Theme.radius-sm;
background: disabled
? Theme.bg-elevated
: (touch.pressed ? Theme.accent-pressed
: (touch.has-hover ? Theme.accent-hover : Theme.accent));
animate background { duration: 120ms; }
HorizontalLayout {
height: parent.height;
padding-left: 14px;
padding-right: 14px;
alignment: center;
Text {
height: parent.height;
text: root.text;
color: disabled ? Theme.text-muted : white;
font-size: Theme.fs-md;
font-weight: 600;
horizontal-alignment: center;
vertical-alignment: center;
}
}
touch := TouchArea {
enabled: !disabled;
mouse-cursor: disabled ? default : pointer;
clicked => { root.clicked(); }
}
}
// --- GhostButton -----------------------------------------------------------
export component GhostButton inherits Rectangle {
in property <string> text;
callback clicked();
height: 30px;
min-width: 80px;
border-radius: Theme.radius-sm;
border-width: 1px;
border-color: touch.has-hover ? Theme.border-strong : Theme.border-subtle;
background: touch.pressed ? Theme.bg-active
: (touch.has-hover ? Theme.bg-hover : Theme.bg-panel);
animate background, border-color { duration: 120ms; }
HorizontalLayout {
height: parent.height;
padding-left: 14px;
padding-right: 14px;
alignment: center;
Text {
height: parent.height;
text: root.text;
color: Theme.text-primary;
font-size: Theme.fs-md;
horizontal-alignment: center;
vertical-alignment: center;
}
}
touch := TouchArea {
mouse-cursor: pointer;
clicked => { root.clicked(); }
}
}
// --- LabeledInput ----------------------------------------------------------
export component LabeledInput inherits VerticalLayout {
in property <string> label;
in property <string> placeholder;
in-out property <string> value;
in property <bool> password: false;
spacing: 4px;
Text {
text: label;
color: Theme.text-secondary;
font-size: Theme.fs-sm;
}
Rectangle {
height: 32px;
border-radius: Theme.radius-sm;
border-width: 1px;
border-color: input.has-focus ? Theme.accent : Theme.border-subtle;
background: Theme.bg-root;
input := TextInput {
x: 10px;
y: 0px;
width: parent.width - 20px;
height: parent.height;
text <=> root.value;
color: Theme.text-primary;
font-size: Theme.fs-md;
vertical-alignment: center;
single-line: true;
input-type: root.password ? InputType.password : InputType.text;
}
// Lightweight placeholder — shown when value is empty.
if value == "" : Text {
x: 10px;
y: 0px;
height: parent.height;
text: placeholder;
color: Theme.text-muted;
font-size: Theme.fs-md;
vertical-alignment: center;
}
}
}
// --- Sparkline -------------------------------------------------------------
// Compact horizontal bar chart used in the stats sidebar.
export component Sparkline inherits Rectangle {
in property <[float]> values;
in property <brush> line-color: Theme.accent;
background: Theme.bg-root;
border-radius: Theme.radius-sm;
clip: true;
for v[i] in values : Rectangle {
x: i * (parent.width / max(1, values.length));
y: parent.height - (parent.height * clamp(v, 0.0, 1.0));
width: (parent.width / max(1, values.length)) - 1px;
height: parent.height * clamp(v, 0.0, 1.0);
background: line-color.with-alpha(0.85);
border-radius: 1px;
}
}