use ely_gpui_component::{ buttons::{Button, ButtonVariant}, shell::{TrayIcon, TrayItem, dock_badge, set_dock_badge}, typography::Caption, }; use gpui::{App, IntoElement, ParentElement, SharedString, Window}; use crate::{ probe::probe, ui::{blocked, row, section}, }; const ITEMS: [&str; 3] = ["Open gallery", "Toggle theme", "Quit"]; pub fn tray(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> { let tray = window.use_keyed_state("tray", cx, |_, _| None::); let last = window.use_keyed_state("tray-last", cx, |_, _| SharedString::from("nothing yet")); let shown = tray.read(cx).is_some(); let note = last.read(cx).clone(); let (toggle, pick) = (tray.clone(), tray); section( "TrayIcon / TrayMenu", "An SF Symbol in the menu bar with a menu. macOS only.", cx, ) .child( row() .child(probe( "tray-show", Button::new( "tray-show", if shown { "Remove tray icon" } else { "Show tray icon" }, ) .variant(ButtonVariant::Outline) .on_click(move |_, _, cx| { let last = last.clone(); let next = match toggle.read(cx) { Some(_) => None, None => { let items = vec![ TrayItem::Action(ITEMS[0].into()), TrayItem::Action(ITEMS[1].into()), TrayItem::Separator, TrayItem::Action(ITEMS[2].into()), ]; let made = TrayIcon::new( "circle.hexagongrid", items, move |index, cx| { let label = match index { 0 => ITEMS[0], 1 => ITEMS[1], 3 => ITEMS[2], other => panic!("tray has no action at {other}"), }; last.update(cx, |last, cx| { *last = label.into(); cx.notify(); }) }, cx, ); match made { Ok(icon) => Some(icon), Err(error) => { log::error!("gallery: tray failed: {error:#}"); None } } } }; toggle.update(cx, |tray, cx| { *tray = next; cx.notify(); }) }), )) .child(probe( "tray-pick", Button::new("tray-pick", "Pick “Open gallery”") .variant(ButtonVariant::Ghost) .disabled(!shown) .on_click(move |_, _, cx| { if let Some(icon) = pick.read(cx) { icon.pick(0); } }), )) .child(Caption::new(format!("Last pick: {note}"))), ) } pub fn dock(cx: &mut App) -> impl IntoElement + use<> { let reading = match dock_badge() { Ok(Some(label)) => format!("The Dock shows “{label}”."), Ok(None) => "The Dock shows no badge.".to_string(), Err(error) => format!("{error:#}"), }; let badge = |label: Option<&'static str>| { move |_: &gpui::ClickEvent, window: &mut Window, _: &mut App| { if let Err(error) = set_dock_badge(label) { log::error!("gallery: badge failed: {error:#}"); } window.refresh(); } }; section( "DockBadge", "A count on the app's Dock icon. macOS only.", cx, ) .child( row() .child(probe( "badge-set", Button::new("badge-set", "Badge 3") .variant(ButtonVariant::Outline) .on_click(badge(Some("3"))), )) .child(probe( "badge-clear", Button::new("badge-clear", "Clear") .variant(ButtonVariant::Ghost) .on_click(badge(None)), )) .child(Caption::new(reading)), ) } pub fn notification(cx: &App) -> impl IntoElement + use<> { section( "SystemNotification", "Notices from the system's own center.", cx, ) .child(blocked( "Blocked: an unbundled app has no notification center, and gpui 0.2.2 has no notification API.", cx, )) }