Files
ltk/themes/default/theme.json
Pedro M. de Echanove Pasquin bfe27b6fef event_loop, widget, input: pointer-dwell tooltips, global drag coords, foreign-toplevel name cascade
`Button::tooltip( text )` registers a hint string that fires after a 600 ms pointer dwell. `LaidOutWidget` gains a `tooltip: Option<String>` field, `Element::tooltip()` exposes it to the input layer, and the existing pointer-hover path now calls `arm_tooltip` on hover-enter and `cancel_tooltip` on hover-leave / touch. The deadline is polled alongside `next_long_press_wakeup` in `try_run` so an idle pointer still gets a wake-up at the firing instant; on fire, `tooltip_overlay()` synthesises an `OverlaySpec` — a rounded `text_primary @ 95%` pill drawn with `bg` text — anchored above the hovered widget, flipping below or clamping inside the screen if it would clip, and pushed alongside the app's own overlays both in the redraw path and in `reconcile_overlays` so the layer surface is created the same frame the tooltip becomes visible. Pointer-only by design: touch events explicitly cancel because a tap-and-release should never linger into a hint. The `showcase` example wires `.tooltip(..)` on the three button variants as a smoke test.
The drag pipeline now reports positions in main-surface (global) coordinates instead of per-surface. `surface_offset_for( focus )` derives the top-left of an overlay surface from `SurfaceState::layer_anchor` — newly stored at `reconcile_overlays` time from `OverlaySpec::anchor` — combined with the main surface's dimensions; `on_drag_move`, `on_drop`, the synthetic move emitted on drag-promotion in `pointer.rs`, and the `pending_drag_inits` push site in `gesture::start_drag` all translate before handing coordinates to the app. The motion and release paths additionally `request_redraw()` every overlay so a dock-style drop target painted on an `Anchor::Bottom` layer surface gets repainted as the drag moves — without that, the visible drop indicator only updates when the cursor re-enters the main surface. Drops still target whichever surface fired the release; only the coordinates are unified.
`ForeignToplevelListHandler` previously read `app_id` directly via `ForeignToplevelList::info()`. Clients that never set `app_id` (some winit-windowed compositors, simple test clients) were silently invisible to crustace-style docks because the empty string fell through `unwrap_or_default()` and the dock then keyed entries off `""`. `toplevel_display_id()` cascades: prefer `app_id` for desktop-entry matching, fall back to `title` for human-readable identification, and finally to the protocol-issued `identifier` which is always present and unique per handle. Applied to both `new_toplevel` and `update_toplevel`.
`theme::system_fontdb()` lazily loads the system font database once via `OnceLock` and reuses the `Arc` for every `decode_svg_bytes` call. resvg's default `Options::fontdb` is empty, so any SVG containing `<text>` rendered with the built-in fallback font or no font at all; with the system DB attached, icons and decorative SVGs with embedded labels now resolve glyphs correctly. Cached because `load_system_fonts()` walks every font path on the system and is comfortably tens of milliseconds on a cold cache — not something to repeat per icon decode.
`themes/default/theme.json` tweaks one variant's slot palette: `surface-alt` from `@indigo/D9` to `@white/D9` and `text-primary` from `@white` to `@navy`, plus a cosmetic re-alignment of the `"value"` columns in the same slots block.
2026-05-14 22:36:17 +02:00

311 lines
9.8 KiB
JSON

{
"theme": {
"id": "default",
"name": "Default"
},
"fonts": {
"sora": {
"name": "Sora",
"fallbacks": ["system-ui", "sans-serif"],
"sources": [
{ "weight": 300, "path": "/usr/share/fonts/opentype/sora/Sora-Light.otf" },
{ "weight": 400, "path": "/usr/share/fonts/opentype/sora/Sora-Regular.otf" },
{ "weight": 600, "path": "/usr/share/fonts/opentype/sora/Sora-SemiBold.otf" },
{ "weight": 700, "path": "/usr/share/fonts/opentype/sora/Sora-Bold.otf" }
]
}
},
"colors": {
"navy": "#0A032E",
"indigo": "#221A5A",
"midnight": "#181045",
"window-bar-dark": "#2B2838",
"white": "#FFFFFF",
"off-white": "#EAE9EB",
"sky": "#2AA7F7",
"sky-mist": "#E6F0FB",
"cyan": "#04D9FE",
"cyan-soft": "#02CFFF",
"teal": "#00CEB1",
"teal-deep": "#006073",
"danger": "#E5484D",
"slate": "#5F5F68",
"silver": "#D8D7DE",
"glass-hi": "#555555",
"glass-elev": "#212121",
"ink": "#000000",
"cyan-deep": "#0091AD",
"teal-fill-end": "#00687C"
},
"gradients": {
"fill-cyan-slider": {
"type": "linear",
"angle_deg": 0,
"space": "linear-rgb",
"stops": [
{ "pos": 0, "color": "@teal-fill-end" },
{ "pos": 1, "color": "@cyan" }
]
},
"fill-cyan-toggle": {
"type": "linear",
"angle_deg": 0,
"space": "linear-rgb",
"stops": [
{ "pos": 0, "color": "@cyan" },
{ "pos": 1, "color": "@cyan-deep" }
]
},
"track-glass-light": {
"type": "linear",
"angle_deg": 180,
"space": "linear-rgb",
"stops": [
{ "pos": 0, "color": "@white/80" },
{ "pos": 1.1781, "color": "@off-white/80" }
]
},
"surface-glass-dark": {
"type": "linear",
"angle_deg": 180,
"space": "linear-rgb",
"stops": [
{ "pos": 0, "color": "@sky/80" },
{ "pos": 1.1781, "color": "@navy/80" }
]
}
},
"inset_stacks": {
"glass-insets": [
{ "offset": [ 0, 1 ], "blur": 4, "color": "@ink/0F", "blend": "normal" }
],
"glass-insets-rich": [
{ "offset": [-3.6, -3.6], "blur": 13.5, "color": "@white", "blend": "normal" },
{ "offset": [ 1.8, 1.8], "blur": 1.8, "color": "@white", "blend": "normal" },
{ "offset": [ 1.8, 1.8], "blur": 7.2, "color": "@ink/26", "blend": "normal" }
]
},
"modes": {
"light": {
"launcher": { "background": "@white/E6", "border_radius": 24.0 },
"window_controls": {
"bar_bg": "@off-white",
"icon": "@slate",
"hover_bg": "@navy/14",
"pressed_bg": "@navy/24",
"close_hover_bg": "@danger",
"close_icon": "@white",
"focus_ring": "@teal"
},
"slots": {
"bg-page": { "type": "color", "value": "@sky-mist", "meta": { "semantic": "palette/bg" } },
"surface": { "type": "color", "value": "@white/EE", "meta": { "semantic": "palette/surface" } },
"surface-alt": { "type": "color", "value": "@white/D9", "meta": { "semantic": "palette/surface_alt" } },
"text-primary": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/text_primary" } },
"text-secondary": { "type": "color", "value": "@navy/99", "meta": { "semantic": "palette/text_secondary" } },
"accent": { "type": "color", "value": "@cyan", "meta": { "semantic": "palette/accent" } },
"divider": { "type": "color", "value": "@navy/14", "meta": { "semantic": "palette/divider" } },
"icon": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/icon" } },
"shadows-glass": {
"type": "shadows",
"shadows": [
{ "offset": [0, 0], "blur": 9, "color": "@glass-elev/40" }
],
"meta": { "semantic": "effect/glass/elevation" }
},
"surface-slider-track": {
"type": "surface",
"fill": "@track-glass-light",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/slider-track" }
},
"surface-slider-fill": {
"type": "surface",
"fill": "@fill-cyan-slider",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/slider-fill" }
},
"surface-card": {
"type": "surface",
"fill": {
"type": "linear",
"angle_deg": 180,
"space": "linear-rgb",
"stops": [
{ "pos": 0, "color": "@white/80" },
{ "pos": 1, "color": "@off-white/80" }
]
},
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/card" }
},
"surface-panel": {
"type": "surface",
"fill": { "type": "solid", "color": "@white/40" },
"meta": { "semantic": "effect/glass/panel" }
},
"surface-card-flat": {
"type": "surface",
"fill": {
"type": "linear",
"angle_deg": 180,
"space": "linear-rgb",
"stops": [
{ "pos": 0, "color": "@white/B3" },
{ "pos": 1, "color": "@off-white/B3" }
]
},
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/card-flat" }
},
"surface-toggle-active": {
"type": "surface",
"fill": "@fill-cyan-toggle",
"shadows": "shadows-glass",
"inset_shadows": [
{ "offset": [0, 0], "blur": 0, "spread": 0.6, "color": "@teal-deep", "blend": "normal" }
],
"meta": { "semantic": "effect/toggle/active" }
},
"surface-slider-track-flat": {
"type": "surface",
"fill": "@track-glass-light",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets-rich",
"meta": { "semantic": "effect/glass/slider-track-flat" }
},
"surface-slider-fill-flat": {
"type": "surface",
"fill": "@fill-cyan-slider",
"shadows": "shadows-glass",
"inset_shadows": [
{ "offset": [ 0, 1 ], "blur": 4, "color": "@ink/0F", "blend": "normal" },
{ "offset": [ 0, 0 ], "blur": 0, "spread": 0.6, "color": "@teal-deep", "blend": "normal" }
],
"meta": { "semantic": "effect/glass/slider-fill-flat" }
}
}
},
"dark": {
"launcher": { "background": "@indigo/E6", "border_radius": 24.0 },
"window_controls": {
"bar_bg": "@window-bar-dark",
"icon": "@silver",
"hover_bg": "@white/18",
"pressed_bg": "@white/26",
"close_hover_bg": "@danger",
"close_icon": "@white",
"focus_ring": "@teal"
},
"slots": {
"bg-page": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/bg" } },
"surface": { "type": "color", "value": "@midnight/EE", "meta": { "semantic": "palette/surface" } },
"surface-alt": { "type": "color", "value": "@white/D9", "meta": { "semantic": "palette/surface_alt" } },
"text-primary": { "type": "color", "value": "@navy", "meta": { "semantic": "palette/text_primary" } },
"text-secondary": { "type": "color", "value": "@white/B3", "meta": { "semantic": "palette/text_secondary" } },
"accent": { "type": "color", "value": "@cyan", "meta": { "semantic": "palette/accent" } },
"divider": { "type": "color", "value": "@white/14", "meta": { "semantic": "palette/divider" } },
"icon": { "type": "color", "value": "@white", "meta": { "semantic": "palette/icon" } },
"shadows-glass": {
"type": "shadows",
"shadows": [
{ "offset": [0, 0], "blur": 9, "color": "@glass-elev/40" }
],
"meta": { "semantic": "effect/glass/elevation" }
},
"surface-slider-track": {
"type": "surface",
"fill": "@surface-glass-dark",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/slider-track" }
},
"surface-slider-fill": {
"type": "surface",
"fill": "@fill-cyan-slider",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/slider-fill" }
},
"surface-card": {
"type": "surface",
"fill": "@surface-glass-dark",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/card" }
},
"surface-panel": {
"type": "surface",
"fill": { "type": "solid", "color": "@navy/40" },
"meta": { "semantic": "effect/glass/panel" }
},
"surface-card-flat": {
"type": "surface",
"fill": "@surface-glass-dark",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/card-flat" }
},
"surface-toggle-active": {
"type": "surface",
"fill": "@fill-cyan-toggle",
"shadows": "shadows-glass",
"inset_shadows": [
{ "offset": [-3.6, -3.6], "blur": 13.5, "color": "@glass-hi", "blend": "plus-lighter" },
{ "offset": [ 1.8, 1.8], "blur": 1.8, "color": "@glass-hi", "blend": "plus-lighter" },
{ "offset": [ 0.45, 0.45], "blur": 0.9, "color": "@ink", "blend": "overlay" },
{ "offset": [ 1.8, 1.8], "blur": 7.2, "color": "@ink/26", "blend": "normal" },
{ "offset": [0, 0], "blur": 0, "spread": 0.6, "color": "@cyan", "blend": "normal" }
],
"meta": { "semantic": "effect/toggle/active" }
},
"surface-slider-track-flat": {
"type": "surface",
"fill": "@surface-glass-dark",
"shadows": "shadows-glass",
"inset_shadows": "@glass-insets",
"meta": { "semantic": "effect/glass/slider-track-flat" }
},
"surface-slider-fill-flat": {
"type": "surface",
"fill": "@fill-cyan-slider",
"shadows": "shadows-glass",
"inset_shadows": [
{ "offset": [ 0, 1 ], "blur": 4, "color": "@ink/0F", "blend": "normal" },
{ "offset": [ 0, 0 ], "blur": 0, "spread": 0.6, "color": "@teal-deep", "blend": "normal" }
],
"meta": { "semantic": "effect/glass/slider-fill-flat" }
}
}
}
}
}