This commit is contained in:
2026-01-26 23:40:05 +08:00
parent 1ec60e3cbb
commit 24bf9dc3dd
7 changed files with 163 additions and 188 deletions

56
Cargo.lock generated
View File

@@ -1147,27 +1147,6 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "femtovg"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be5d925785ad33d7b0ae2b445d9f157c3ab42ff3c515fff0b46d53d4a86c43c5"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
"fnv",
"glow",
"image",
"imgref",
"itertools 0.14.0",
"log",
"rgb",
"slotmap",
"ttf-parser 0.25.1",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "field-offset" name = "field-offset"
version = "0.3.6" version = "0.3.6"
@@ -1211,12 +1190,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.1.5"
@@ -1659,8 +1632,9 @@ dependencies = [
"glutin", "glutin",
"i-slint-common", "i-slint-common",
"i-slint-core", "i-slint-core",
"i-slint-renderer-femtovg", "i-slint-renderer-skia",
"input", "input",
"memmap2",
"nix", "nix",
"raw-window-handle", "raw-window-handle",
"xkbcommon", "xkbcommon",
@@ -1696,7 +1670,6 @@ dependencies = [
"i-slint-common", "i-slint-common",
"i-slint-core", "i-slint-core",
"i-slint-core-macros", "i-slint-core-macros",
"i-slint-renderer-femtovg",
"i-slint-renderer-skia", "i-slint-renderer-skia",
"lyon_path", "lyon_path",
"muda", "muda",
@@ -1811,29 +1784,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "i-slint-renderer-femtovg"
version = "1.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d85d77f899ecb1f05c50c659a6d356fba44463686713f1f925c8be48de6afb"
dependencies = [
"cfg-if",
"const-field-offset",
"derive_more",
"femtovg",
"glow",
"i-slint-common",
"i-slint-core",
"i-slint-core-macros",
"imgref",
"lyon_path",
"pin-weak",
"rgb",
"ttf-parser 0.25.1",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "i-slint-renderer-skia" name = "i-slint-renderer-skia"
version = "1.14.1" version = "1.14.1"
@@ -3779,7 +3729,6 @@ dependencies = [
"i-slint-backend-selector", "i-slint-backend-selector",
"i-slint-core", "i-slint-core",
"i-slint-core-macros", "i-slint-core-macros",
"i-slint-renderer-femtovg",
"num-traits", "num-traits",
"once_cell", "once_cell",
"pin-weak", "pin-weak",
@@ -3925,6 +3874,7 @@ checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3"
dependencies = [ dependencies = [
"as-raw-xcb-connection", "as-raw-xcb-connection",
"bytemuck", "bytemuck",
"drm",
"fastrand", "fastrand",
"js-sys", "js-sys",
"memmap2", "memmap2",

View File

@@ -4,11 +4,14 @@ version = "0.0.1"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
slint = { version = "1.14", default-features = false, features = ["backend-winit", "renderer-femtovg", "compat-1-2"] } slint = { version = "1.14", default-features = false, features = ["backend-winit", "renderer-winit-skia", "compat-1-2"] }
[build-dependencies] [build-dependencies]
slint-build = "1.14" slint-build = "1.14"
[features]
default = []
lite = [] # Lightweight demo with minimal memory footprint
[profile.release] [profile.release]
opt-level = 'z' # 优化代码体积 opt-level = 'z' # 优化代码体积

View File

@@ -1,3 +1,12 @@
fn main() { fn main() {
slint_build::compile("ui/demo.slint").unwrap(); // Use SLINT_DEMO environment variable to choose demo version
// cargo build -> full demo
// cargo build --features lite -> lite demo
let demo_file = if cfg!(feature = "lite") {
"ui/demo-lite.slint"
} else {
"ui/demo.slint"
};
slint_build::compile(demo_file).unwrap();
} }

View File

@@ -1,9 +1,21 @@
slint::include_modules!(); slint::include_modules!();
#[cfg(not(feature = "lite"))]
use slint::{ComponentHandle, Model, ModelRc, VecModel}; use slint::{ComponentHandle, Model, ModelRc, VecModel};
#[cfg(not(feature = "lite"))]
use std::rc::Rc; use std::rc::Rc;
fn main() -> Result<(), slint::PlatformError> { fn main() -> Result<(), slint::PlatformError> {
#[cfg(feature = "lite")]
{
// Lite version - minimal memory footprint
let ui = DemoLite::new()?;
ui.run()
}
#[cfg(not(feature = "lite"))]
{
// Full version with all features
let ui = Demo::new()?; let ui = Demo::new()?;
// Toast management // Toast management
@@ -56,4 +68,5 @@ fn main() -> Result<(), slint::PlatformError> {
}); });
ui.run() ui.run()
}
} }

View File

@@ -14,10 +14,10 @@ export component Button {
// Callbacks // Callbacks
callback clicked(); callback clicked();
// Calculate text color based on variant // Calculate text color based on variant (shadcn design tokens)
private property <color> text-color: { private property <color> text-color: {
if disabled { Theme.colors.muted-foreground } if disabled { Theme.colors.muted-foreground }
else if variant == "destructive" { Theme.colors.destructive-foreground } else if variant == "destructive" { Theme.colors.destructive } // destructive uses destructive color for text
else if variant == "outline" { Theme.colors.foreground } else if variant == "outline" { Theme.colors.foreground }
else if variant == "secondary" { Theme.colors.secondary-foreground } else if variant == "secondary" { Theme.colors.secondary-foreground }
else if variant == "ghost" { Theme.colors.foreground } else if variant == "ghost" { Theme.colors.foreground }
@@ -48,8 +48,9 @@ export component Button {
// Main container // Main container
container := Rectangle { container := Rectangle {
border-radius: SpacingSystem.radius.md; border-radius: SpacingSystem.radius.md;
border-width: variant == "outline" ? 1px : 0px; // Outline variant always has border
border-color: Theme.colors.border; border-width: root.variant == "outline" ? 1px : 0px;
border-color: root.variant == "outline" ? Theme.colors.border : Colors.transparent;
// Touch interaction area (must be defined before using touch.pressed/has-hover) // Touch interaction area (must be defined before using touch.pressed/has-hover)
touch := TouchArea { touch := TouchArea {
@@ -61,36 +62,34 @@ export component Button {
} }
} }
// Background with state-based colors (shadcn style) // Calculate background color based on variant and state (shadcn official design)
background-rect := Rectangle {
width: 100%;
height: 100%;
border-radius: parent.border-radius;
// Calculate background color based on variant and state
background: { background: {
if root.disabled { if root.disabled {
Theme.colors.muted Theme.colors.muted
} else if root.variant == "destructive" { } else if root.variant == "destructive" {
if touch.pressed { Theme.colors.destructive.darker(0.1) } // destructive: bg-destructive/10 hover:bg-destructive/20
else if touch.has-hover { Theme.colors.destructive.darker(0.05) } if touch.pressed { Theme.colors.destructive.with-alpha(0.25) }
else { Theme.colors.destructive } else if touch.has-hover { Theme.colors.destructive.with-alpha(0.2) }
else { Theme.colors.destructive.with-alpha(0.1) }
} else if root.variant == "outline" { } else if root.variant == "outline" {
if touch.pressed { Theme.colors.accent.darker(0.05) } // outline: bg-background hover:bg-muted
else if touch.has-hover { Theme.colors.accent } if touch.pressed { Theme.colors.muted.darker(0.05) }
else { Colors.transparent } else if touch.has-hover { Theme.colors.muted }
else { Theme.colors.background }
} else if root.variant == "secondary" { } else if root.variant == "secondary" {
if touch.pressed { Theme.colors.secondary.darker(0.1) } // secondary: bg-secondary hover:bg-secondary/80
else if touch.has-hover { Theme.colors.secondary.darker(0.05) } if touch.pressed { Theme.colors.secondary.with-alpha(0.7) }
else if touch.has-hover { Theme.colors.secondary.with-alpha(0.8) }
else { Theme.colors.secondary } else { Theme.colors.secondary }
} else if root.variant == "ghost" { } else if root.variant == "ghost" {
if touch.pressed { Theme.colors.accent.darker(0.05) } // ghost: transparent hover:bg-muted
else if touch.has-hover { Theme.colors.accent } if touch.pressed { Theme.colors.muted.darker(0.05) }
else if touch.has-hover { Theme.colors.muted }
else { Colors.transparent } else { Colors.transparent }
} else { } else {
// default variant // default (primary): bg-primary hover:bg-primary/80
if touch.pressed { Theme.colors.primary.darker(0.1) } if touch.pressed { Theme.colors.primary.with-alpha(0.7) }
else if touch.has-hover { Theme.colors.primary.darker(0.05) } else if touch.has-hover { Theme.colors.primary.with-alpha(0.8) }
else { Theme.colors.primary } else { Theme.colors.primary }
} }
}; };
@@ -100,7 +99,6 @@ export component Button {
duration: Animations.durations.fast; duration: Animations.durations.fast;
easing: Animations.ease-in-out; easing: Animations.ease-in-out;
} }
}
// Content layout // Content layout
HorizontalLayout { HorizontalLayout {

View File

@@ -15,11 +15,11 @@ export component Demo inherits Window {
title: "Slint Shadcn UI - Task Manager Demo"; title: "Slint Shadcn UI - Task Manager Demo";
background: Theme.colors.background; background: Theme.colors.background;
// Window size settings - reduced for lower memory usage // Window size settings - optimized for lower memory usage
min-width: 800px; min-width: 600px;
min-height: 600px; min-height: 400px;
preferred-width: 1000px; preferred-width: 800px;
preferred-height: 700px; preferred-height: 600px;
// Application state // Application state
in-out property <string> new-task-title: ""; in-out property <string> new-task-title: "";

View File

@@ -1,5 +1,6 @@
// Shadcn-style Color System // Shadcn-style Color System
// Based on shadcn/ui zinc theme with light/dark mode support // Based on shadcn/ui official neutral theme with light/dark mode support
// Source: https://github.com/shadcn-ui/ui/blob/main/apps/v4/styles/globals.css
// Color palette structure // Color palette structure
export struct ColorPalette { export struct ColorPalette {
@@ -24,52 +25,53 @@ export struct ColorPalette {
ring: color, ring: color,
} }
// Light mode colors - shadcn zinc theme // Light mode colors - shadcn official neutral theme
// oklch values converted to hex for Slint compatibility
export global LightColors { export global LightColors {
out property <ColorPalette> palette: { out property <ColorPalette> palette: {
background: #ffffff, background: #ffffff, // oklch(1 0 0)
foreground: #09090b, foreground: #252525, // oklch(0.145 0 0)
card: #ffffff, card: #ffffff, // oklch(1 0 0)
card-foreground: #09090b, card-foreground: #252525, // oklch(0.145 0 0)
popover: #ffffff, popover: #ffffff, // oklch(1 0 0)
popover-foreground: #09090b, popover-foreground: #252525, // oklch(0.145 0 0)
primary: #18181b, primary: #343434, // oklch(0.205 0 0)
primary-foreground: #fafafa, primary-foreground: #fbfbfb, // oklch(0.985 0 0)
secondary: #f4f4f5, secondary: #f7f7f7, // oklch(0.97 0 0)
secondary-foreground: #18181b, secondary-foreground: #343434, // oklch(0.205 0 0)
muted: #f4f4f5, muted: #f7f7f7, // oklch(0.97 0 0)
muted-foreground: #71717a, muted-foreground: #8e8e8e, // oklch(0.556 0 0)
accent: #f4f4f5, accent: #f7f7f7, // oklch(0.97 0 0)
accent-foreground: #18181b, accent-foreground: #343434, // oklch(0.205 0 0)
destructive: #ef4444, destructive: #ef4444, // oklch(0.577 0.245 27.325)
destructive-foreground: #fafafa, destructive-foreground: #f7f7f7, // oklch(0.97 0.01 17) - light red/pink, not white!
border: #e4e4e7, border: #ebebeb, // oklch(0.922 0 0)
input: #e4e4e7, input: #ebebeb, // oklch(0.922 0 0)
ring: #18181b, ring: #b4b4b4, // oklch(0.708 0 0)
}; };
} }
// Dark mode colors - shadcn zinc theme // Dark mode colors - shadcn official neutral theme
export global DarkColors { export global DarkColors {
out property <ColorPalette> palette: { out property <ColorPalette> palette: {
background: #09090b, background: #252525, // oklch(0.145 0 0)
foreground: #fafafa, foreground: #fbfbfb, // oklch(0.985 0 0)
card: #09090b, card: #343434, // oklch(0.205 0 0)
card-foreground: #fafafa, card-foreground: #fbfbfb, // oklch(0.985 0 0)
popover: #09090b, popover: #444444, // oklch(0.269 0 0)
popover-foreground: #fafafa, popover-foreground: #fbfbfb, // oklch(0.985 0 0)
primary: #fafafa, primary: #ebebeb, // oklch(0.922 0 0)
primary-foreground: #18181b, primary-foreground: #343434, // oklch(0.205 0 0)
secondary: #27272a, secondary: #444444, // oklch(0.269 0 0)
secondary-foreground: #fafafa, secondary-foreground: #fbfbfb, // oklch(0.985 0 0)
muted: #27272a, muted: #444444, // oklch(0.269 0 0)
muted-foreground: #a1a1aa, muted-foreground: #b4b4b4, // oklch(0.708 0 0)
accent: #27272a, accent: #5e5e5e, // oklch(0.371 0 0)
accent-foreground: #fafafa, accent-foreground: #fbfbfb, // oklch(0.985 0 0)
destructive: #7f1d1d, destructive: #dc2626, // oklch(0.704 0.191 22.216)
destructive-foreground: #fafafa, destructive-foreground: #ef4444, // oklch(0.58 0.22 27) - red color, not white!
border: #27272a, border: #1a1a1a, // oklch(1 0 0 / 10%) - 10% white on dark
input: #27272a, input: #262626, // oklch(1 0 0 / 15%) - 15% white on dark
ring: #d4d4d8, ring: #8e8e8e, // oklch(0.556 0 0)
}; };
} }