Files

96 lines
4.0 KiB
Plaintext

import { HorizontalBox } from "std-widgets.slint";
import { AppTheme, LayoutMetrics, ThemeUtilities } from "../design-system.slint";
import { TypeScale } from "../typography.slint";
import { FocusVisibleScope } from "focus-visible-scope.slint";
// Application-owned button with deterministic geometry across compositors.
export component Button inherits Rectangle {
in property <string> text;
in property <image> icon;
in property <bool> has-icon: false;
in property <bool> colorize-icon: true;
in property <length> icon-size: LayoutMetrics.icon-size;
in property <bool> primary: false;
in property <bool> destructive: false;
in property <bool> selected: false;
in property <bool> enabled: true;
// A button that fills a fixed-width column may elide a label that does
// not fit there; other buttons are given room for the whole label.
in property <TextOverflow> label-overflow: clip;
// The width that shows the whole label, for buttons sized to it. The
// label row moves when pressed, so it does not size the button itself.
out property <length> content-width: label-row.preferred-width;
out property <bool> focus-visible: keyboard.focus-visible;
callback clicked;
height: 32px;
min-width: 64px;
border-radius: LayoutMetrics.corner-radius - 2px;
forward-focus: keyboard;
border-width: keyboard.focus-visible ? 2px : root.primary || root.destructive ? 0px : 1px;
border-color: keyboard.focus-visible || root.selected ? AppTheme.accent : AppTheme.control-border;
private property <color> fill-color: !root.enabled ? AppTheme.disabled-bg
: root.destructive ? (button-area.pressed ? AppTheme.destructive-pressed : button-area.has-hover ? AppTheme.destructive-hover : AppTheme.destructive)
: root.primary ? (button-area.pressed ? AppTheme.primary-pressed : button-area.has-hover ? AppTheme.primary-hover : AppTheme.primary)
: root.selected ? AppTheme.accent-soft
: button-area.pressed ? AppTheme.muted-control-bg
: button-area.has-hover ? AppTheme.control-bg.brighter(AppTheme.dark ? 8% : 2%)
: AppTheme.surface;
private property <color> foreground: root.destructive ? white
: root.primary ? ThemeUtilities.contrasting-foreground(root.fill-color)
: root.selected ? AppTheme.accent : AppTheme.heading;
background: root.fill-color;
opacity: root.enabled ? 1 : 0.5;
animate background { duration: 120ms; easing: ease-out; }
accessible-role: button;
accessible-label: root.text;
accessible-action-default => {
if root.enabled {
root.clicked();
}
}
label-row := HorizontalBox {
y: button-area.pressed ? 1px : 0px;
animate y { duration: 140ms; easing: spring(0); }
padding-left: 12px;
padding-right: 12px;
spacing: 7px;
alignment: center;
cross-axis-alignment: center;
if root.has-icon : Image {
width: root.icon-size;
height: root.icon-size;
source: root.icon;
colorize: root.colorize-icon ? (root.primary || root.destructive ? root.foreground : AppTheme.body) : transparent;
image-fit: contain;
accessible-role: none;
}
Text {
text: root.text;
color: root.foreground;
font-size: TypeScale.body;
font-weight: root.primary || root.destructive || root.selected ? 600 : 500;
horizontal-alignment: center;
vertical-alignment: center;
overflow: root.label-overflow;
}
}
keyboard := FocusVisibleScope {
enabled: root.enabled;
accessible-role: none;
key-pressed(event) => {
if event.text == Key.Return || event.text == Key.Space { root.clicked(); return accept; }
reject
}
}
button-area := TouchArea {
enabled: root.enabled;
clicked => { keyboard.focus(); keyboard.pointer-activated(); root.clicked(); }
mouse-cursor: pointer;
}
}