use crate::geometry::PhysicalSize;
use crate::render::{Color, Pixmap};
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(unsafe_code)]
pub(crate) mod objc;
#[cfg(target_os = "windows")]
#[allow(unsafe_code)]
#[path = "windows.rs"]
mod os;
#[cfg(target_os = "macos")]
#[allow(unsafe_code)]
#[path = "macos.rs"]
mod os;
#[cfg(target_os = "ios")]
#[allow(unsafe_code)]
#[path = "ios.rs"]
mod os;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod os;
#[cfg(target_os = "android")]
#[path = "android.rs"]
mod os;
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "linux",
target_os = "android"
)))]
#[path = "fallback.rs"]
mod os;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NativeControl {
Button { primary: bool },
Checkbox { checked: bool },
Radio { selected: bool },
Switch { on: bool },
Slider { value: u16 },
Progress { value: u16 },
TextField,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct NativeState {
pub hovered: bool,
pub pressed: bool,
pub focused: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NativeDeco {
pub control: NativeControl,
pub dark: bool,
pub state: NativeState,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SystemLook {
pub dark: bool,
pub accent: Option<Color>,
}
pub fn system_look() -> SystemLook {
os::system_look()
}
pub fn watch() {
#[cfg(target_os = "linux")]
os::watch();
}
#[cfg(target_arch = "wasm32")]
pub(crate) fn set_web_look(look: SystemLook) -> bool {
os::set_look(look)
}
static CHANGED: AtomicBool = AtomicBool::new(false);
#[cfg_attr(
not(any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "android"
)),
allow(dead_code)
)]
pub(crate) fn note_changed() {
CHANGED.store(true, Ordering::Release);
crate::platform::wake::wake();
}
pub(crate) fn take_changed() -> bool {
CHANGED.swap(false, Ordering::AcqRel)
}
pub fn clear_cache() {
if let Some(cache) = CACHE.get()
&& let Ok(mut cache) = cache.lock()
{
cache.clear();
}
}
pub fn supports(control: NativeControl) -> bool {
os::supports(control)
}
pub fn available() -> bool {
os::AVAILABLE
}
pub fn size_hint(control: NativeControl) -> (Option<f64>, Option<f64>) {
os::size_hint(control)
}
pub fn draws_focus(control: NativeControl) -> bool {
os::draws_focus(control)
}
pub fn primary_label_is_on_accent() -> bool {
os::PRIMARY_LABEL_ON_ACCENT
}
pub fn render(deco: NativeDeco, size: PhysicalSize, scale: f64) -> Option<Arc<Pixmap>> {
if size.width == 0 || size.height == 0 {
return None;
}
let key = (deco, size.width, size.height, (scale * 1000.0) as u32);
let cache = CACHE.get_or_init(Default::default);
if let Some(hit) = cache.lock().ok()?.get(&key) {
return hit.clone();
}
let image = os::render(deco, size, scale).map(Arc::new);
let mut cache = cache.lock().ok()?;
if cache.len() >= CACHE_LIMIT {
cache.clear(); }
cache.insert(key, image.clone());
image
}
type CacheKey = (NativeDeco, u32, u32, u32);
const CACHE_LIMIT: usize = 1024;
static CACHE: OnceLock<Mutex<HashMap<CacheKey, Option<Arc<Pixmap>>>>> = OnceLock::new();
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn unpremultiply(data: &mut [u8]) {
for px in data.as_chunks_mut::<4>().0 {
let a = px[3] as u32;
if a != 0 && a != 255 {
for c in &mut px[..3] {
*c = ((*c as u32 * 255 + a / 2) / a).min(255) as u8;
}
}
}
}