From 2870ebbd13b9337398fdc4f3d93249820b68589f Mon Sep 17 00:00:00 2001 From: server2 Date: Thu, 8 Oct 2026 11:45:26 +0200 Subject: [PATCH] island: an idle pill while no player is open the island is always there now: with no player it shows a pill with a drawn music note and 'Nothing playing', in the cover gradient with the pink glow. click-through, static (draws once). checked on all 4 monitors --- README.md | 2 +- src/island.rs | 44 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9fe4ebe..8d549db 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A Dynamic-Island-style media widget for Amane (Rust Wayland shell library), running on Hyprland. It sits as a small pill at the top centre of the screen and opens into a now-playing card. -- **Pill:** cover thumbnail, title, 7 live bars from the shown player's own sound (lows left, highs right, growing up and down from the middle), or a pause sign. +- **Pill:** cover thumbnail, title, 7 live bars from the shown player's own sound (lows left, highs right, growing up and down from the middle), or a pause sign. With no player open, an idle pill: a music note and "Nothing playing". - **Card:** a still CD of the cover ringed by 40 cava "candles", title and artist, a seekable time bar, previous / play-pause / next. - **Player switcher** ("VIA …" chip): every MPRIS player; picking one pins it, otherwise the island follows whichever plays. - **Output switcher:** moves only the shown player's audio to another device. diff --git a/src/island.rs b/src/island.rs index 0e7972f..19dd533 100644 --- a/src/island.rs +++ b/src/island.rs @@ -4,7 +4,7 @@ use std::thread; use std::time::{Duration, Instant}; use amane::{ - Animation, Arc, Argument, Blend, Bus, Canvas, Cap, Center, Color, Column, Easing, End, Full, Gradient, + Animation, Arc, Argument, Blend, Bus, Canvas, Cap, Center, Circle, Color, Column, Easing, End, Full, Gradient, Image, InputArea, Layer, LayerWindow, Line, Media, MediaPlayer, Monitor, Padding, Palette, Parent, Path, Pointer, Rectangle, Row, Scroll, ScrollArea, Service, Shadow, Shape, SpaceBetween, Stack, Start, Text, Vertical, Weight, Widget, Workspaces, Zone, children, shapes, @@ -267,15 +267,21 @@ pub fn view(monitor: &Monitor) -> LayerWindow { .space(Zone::Ignore) .namespace("magic-island"); - /* - * no player: an empty window that lets every click through, never visible(false). - * hyprland 0.56 configures a layer surface before its first commit, so a window - * that starts hidden gets a size anyway: amane then logs "no child to draw" and, - * with nothing ever drawn, waits for frame callbacks that never come, and the - * island never showed up once a player started - */ let Some(player) = shown(&media, island.selected.as_deref()) else { - return window.click_through().child(Rectangle::new().width(0.0).height(0.0)); + /* + * nothing playing: a quiet pill, so the island is always there. never + * visible(false): hyprland 0.56 configures a layer surface before its first + * commit, so a window that starts hidden gets a size anyway, amane logs "no + * child to draw" and waits for frame callbacks an undrawn surface never gets + */ + return window.click_through().child( + Rectangle::new() + .width(Parent) + .height(Parent) + .padding(Padding { top: TOP_GAP, right: 0.0, bottom: 0.0, left: 0.0 }) + .align_child(Center, Start) + .child(idle(&backdrop_colors(&palette))), + ); }; let (grow, pill, reveal) = phases(if opened_here { island.open.value() } else { 0.0 }); @@ -589,6 +595,19 @@ pub fn faded(color: Color, alpha: u8) -> Color { Color::rgba(color.red(), color.green(), color.blue(), alpha) } +// the island while no player is open: a music note and "Nothing playing", glowing like the pill +fn idle(colors: &[Color; 3]) -> Rectangle { + Rectangle::new() + .width(COMPACT_W) + .height(COMPACT_H) + .radius(Full) + .fill(backdrop(colors, 255)) + .border(1.0, EDGE) + .shadow(Shadow::drop(PINK).opacity(0.3).blur(22.0)) + .align_child(Center, Center) + .child(Row::new(children![icon(Glyph::Note, 15.0, PINK), Text::new("Nothing playing").size(12.0).color(DIM)]).gap(8.0).align(Center)) +} + // the pill: cover, title, the player's live levels fn compact(player: &MediaPlayer, playing: bool, art: &str, levels: &[f32]) -> Rectangle { Rectangle::new() @@ -1405,6 +1424,7 @@ enum Glyph { Muted, Bluetooth, Chevron, + Note, } // drawn, not typed, so the island looks the same whatever fonts are installed @@ -1473,6 +1493,12 @@ fn bold_icon(glyph: Glyph, size: f32, color: Color, weight: f32) -> Canvas { Glyph::Chevron => shapes![ Path::new().move_to(0.12 * s, 0.32 * s).line_to(0.5 * s, 0.7 * s).line_to(0.88 * s, 0.32 * s).stroke(1.5, color).cap(Cap::Round) ], + // an eighth note: round head, stem, a curved flag + Glyph::Note => shapes![ + Circle::new().center(0.32 * s, 0.74 * s).radius(0.17 * s).fill(color), + Line::new().from(0.47 * s, 0.74 * s).to(0.47 * s, 0.12 * s).stroke(thin, color).cap(Cap::Round), + Path::new().move_to(0.47 * s, 0.12 * s).quad_to(0.62 * s, 0.3 * s, 0.84 * s, 0.38 * s).stroke(thin, color).cap(Cap::Round), + ], }; Canvas::new().width(size).height(size).shapes(shapes)