use std::path::PathBuf;
use crate::error::Result;
use crate::geometry::{Edge, LogicalRect};
use crate::keynav::NavKey;
use crate::menu::{Icon, Menu, MenuId};
use crate::theme::MenuOptions;
use crate::Tray;
#[derive(Debug, Clone)]
pub enum SystemFontSource {
Data(Vec<u8>),
Path(PathBuf),
Family(String),
}
#[derive(Debug, Clone)]
pub struct SystemFont {
pub source: SystemFontSource,
pub point_size: f32,
}
impl SystemFont {
pub fn apply_size_to(&self, theme: &mut crate::theme::Theme) {
if self.point_size > 0.0 {
theme.row_font.size = self.point_size;
theme.header_font.size = self.point_size;
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SystemPalette {
pub label: Option<(u8, u8, u8, u8)>,
pub secondary_label: Option<(u8, u8, u8, u8)>,
pub separator: Option<(u8, u8, u8, u8)>,
pub background: Option<(u8, u8, u8, u8)>,
}
impl SystemPalette {
pub fn apply_to(&self, theme: &mut crate::theme::Theme) {
use crate::style::Color;
if let Some((r, g, b, a)) = self.label {
theme.label = Color::Rgba(r, g, b, a);
}
if let Some((r, g, b, a)) = self.secondary_label {
theme.secondary_label = Color::Rgba(r, g, b, a);
}
if let Some((r, g, b, a)) = self.separator {
theme.separator = Color::Rgba(r, g, b, a);
}
if let Some((r, g, b, a)) = self.background {
theme.background = Color::Rgba(r, g, b, a);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Appearance {
Light,
Dark,
}
impl Appearance {
pub fn is_dark(self) -> bool {
matches!(self, Appearance::Dark)
}
pub fn from_is_dark(is_dark: bool) -> Self {
if is_dark {
Appearance::Dark
} else {
Appearance::Light
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PlatformEvent {
TrayActivated,
Key(NavKey),
Dismissed,
}
pub trait Platform {
fn install_tray(&mut self, icon: &Icon, tooltip: Option<&str>) -> Result<()>;
fn tray_anchor_rect(&self) -> Result<LogicalRect>;
fn supports_tray_anchor(&self) -> bool;
fn cursor_position(&self) -> Option<crate::geometry::LogicalPoint> {
None
}
fn appearance(&self) -> Appearance;
fn system_menu_font(&self) -> Option<SystemFont> {
None
}
fn system_palette(&self) -> SystemPalette {
SystemPalette::default()
}
fn work_area(&self) -> LogicalRect;
fn run_tray(self, tray: Tray) -> Result<()>
where
Self: Sized;
fn spawn_tray(self, tray: Tray) -> Result<()>
where
Self: Sized,
{
let _ = tray;
Err(crate::error::Error::Platform(
"spawning a non-blocking tray is not implemented on this platform".into(),
))
}
fn open_popup_session(
&mut self,
menu: Menu,
options: MenuOptions,
on_click: &(dyn Fn(&MenuId) + '_),
anchor: LogicalRect,
edge: Edge,
) -> Result<()> {
let _ = (menu, options, on_click, anchor, edge);
Err(crate::error::Error::Platform(
"styled popup sessions are not implemented on this platform yet".into(),
))
}
}
#[cfg(target_os = "macos")]
pub mod mac;
#[cfg(target_os = "macos")]
pub use mac::MacPlatform as PlatformImpl;
#[cfg(target_os = "windows")]
pub mod windows;
#[cfg(target_os = "windows")]
pub use windows::WindowsPlatform as PlatformImpl;
#[cfg(all(unix, not(target_os = "macos")))]
pub mod linux;
#[cfg(all(unix, not(target_os = "macos")))]
pub use linux::LinuxPlatform as PlatformImpl;
pub fn current() -> PlatformImpl {
PlatformImpl::new()
}
#[cfg(any(target_os = "windows", all(unix, not(target_os = "macos"))))]
pub(crate) fn spawn_tray_thread(tray: Tray, run: fn(Tray) -> Result<()>) -> Result<()> {
std::thread::Builder::new()
.name("muri-tray".to_owned())
.spawn(move || {
if let Err(e) = run(tray) {
eprintln!("muri: tray thread exited with error: {e}");
}
})
.map(|_| ())
.map_err(|e| {
crate::error::Error::Platform(format!("failed to spawn muri tray thread: {e}"))
})
}