use std::any::Any;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
use web_time::Instant;
use crate::clipboard::Clipboard;
use crate::file_dialogs::FileDialogs;
use crate::fonts::FontSource;
use crate::geometry::{Color, Offset, Rect};
use crate::menus::{DockMenu, MenuBar, PopupMenus, TrayIcons};
use crate::notifications::Notifications;
use crate::opener::Opener;
use crate::restoration::{RestorationMap, RestorationUpdate};
use crate::system_context_menu::SystemContextMenuItem;
use crate::{View, ViewId};
pub type PlatformRef = Rc<dyn Platform>;
pub type ViewRef = Rc<dyn View>;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ApplicationSwitcherDescription {
pub label: Option<String>,
pub primary_color: Option<u32>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SystemUiOverlayStyle {
pub system_navigation_bar_color: Option<Color>,
pub system_navigation_bar_divider_color: Option<Color>,
pub system_navigation_bar_icon_brightness: Option<Brightness>,
pub system_navigation_bar_contrast_enforced: Option<bool>,
pub status_bar_color: Option<Color>,
pub status_bar_brightness: Option<Brightness>,
pub status_bar_icon_brightness: Option<Brightness>,
pub system_status_bar_contrast_enforced: Option<bool>,
}
impl SystemUiOverlayStyle {
pub const LIGHT: SystemUiOverlayStyle = SystemUiOverlayStyle {
system_navigation_bar_color: Some(Color::new(0xFF000000)),
system_navigation_bar_icon_brightness: Some(Brightness::Light),
status_bar_icon_brightness: Some(Brightness::Light),
status_bar_brightness: Some(Brightness::Dark),
system_navigation_bar_divider_color: None,
system_navigation_bar_contrast_enforced: None,
status_bar_color: None,
system_status_bar_contrast_enforced: None,
};
pub const DARK: SystemUiOverlayStyle = SystemUiOverlayStyle {
system_navigation_bar_color: Some(Color::new(0xFF000000)),
system_navigation_bar_icon_brightness: Some(Brightness::Light),
status_bar_icon_brightness: Some(Brightness::Dark),
status_bar_brightness: Some(Brightness::Light),
system_navigation_bar_divider_color: None,
system_navigation_bar_contrast_enforced: None,
status_bar_color: None,
system_status_bar_contrast_enforced: None,
};
pub const fn new() -> SystemUiOverlayStyle {
SystemUiOverlayStyle {
system_navigation_bar_color: None,
system_navigation_bar_divider_color: None,
system_navigation_bar_icon_brightness: None,
system_navigation_bar_contrast_enforced: None,
status_bar_color: None,
status_bar_brightness: None,
status_bar_icon_brightness: None,
system_status_bar_contrast_enforced: None,
}
}
pub fn system_navigation_bar_color(mut self, color: Color) -> SystemUiOverlayStyle {
self.system_navigation_bar_color = Some(color);
self
}
pub fn system_navigation_bar_divider_color(mut self, color: Color) -> SystemUiOverlayStyle {
self.system_navigation_bar_divider_color = Some(color);
self
}
pub fn system_navigation_bar_icon_brightness(
mut self,
brightness: Brightness,
) -> SystemUiOverlayStyle {
self.system_navigation_bar_icon_brightness = Some(brightness);
self
}
pub fn system_navigation_bar_contrast_enforced(
mut self,
enforced: bool,
) -> SystemUiOverlayStyle {
self.system_navigation_bar_contrast_enforced = Some(enforced);
self
}
pub fn status_bar_color(mut self, color: Color) -> SystemUiOverlayStyle {
self.status_bar_color = Some(color);
self
}
pub fn status_bar_brightness(mut self, brightness: Brightness) -> SystemUiOverlayStyle {
self.status_bar_brightness = Some(brightness);
self
}
pub fn status_bar_icon_brightness(mut self, brightness: Brightness) -> SystemUiOverlayStyle {
self.status_bar_icon_brightness = Some(brightness);
self
}
pub fn system_status_bar_contrast_enforced(mut self, enforced: bool) -> SystemUiOverlayStyle {
self.system_status_bar_contrast_enforced = Some(enforced);
self
}
pub fn copy_with(&self) -> SystemUiOverlayStyle {
*self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum HapticFeedbackType {
Vibrate,
LightImpact,
MediumImpact,
HeavyImpact,
SelectionClick,
SuccessNotification,
WarningNotification,
ErrorNotification,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Brightness {
Dark,
Light,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AppLifecycleState {
Detached,
Resumed,
Inactive,
Hidden,
Paused,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AppExitResponse {
Exit,
Cancel,
}
pub struct AppExitReply(Option<Box<dyn FnOnce(AppExitResponse)>>);
impl AppExitReply {
pub fn new(send: impl FnOnce(AppExitResponse) + 'static) -> AppExitReply {
AppExitReply(Some(Box::new(send)))
}
pub fn answer(mut self, response: AppExitResponse) {
self.send(response);
}
fn send(&mut self, response: AppExitResponse) {
if let Some(send) = self.0.take() {
send(response);
}
}
}
impl Drop for AppExitReply {
fn drop(&mut self) {
self.send(AppExitResponse::Exit);
}
}
impl std::fmt::Debug for AppExitReply {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AppExitReply")
.field("answered", &self.0.is_none())
.finish()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AppExitType {
Cancelable,
Required,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum TargetPlatform {
Android,
Fuchsia,
IOS,
Linux,
MacOS,
Windows,
}
const THIS_OS: Option<TargetPlatform> = if cfg!(target_os = "android") {
Some(TargetPlatform::Android)
} else if cfg!(target_os = "fuchsia") {
Some(TargetPlatform::Fuchsia)
} else if cfg!(target_os = "ios") {
Some(TargetPlatform::IOS)
} else if cfg!(target_os = "linux") {
Some(TargetPlatform::Linux)
} else if cfg!(target_os = "macos") {
Some(TargetPlatform::MacOS)
} else if cfg!(target_os = "windows") {
Some(TargetPlatform::Windows)
} else {
None
};
pub trait Dispatcher: Send + Sync {
fn wake_at(&self, timer_wakeup: Option<Instant>);
fn wake_now(&self);
fn dispatch(&self, work: Box<dyn FnOnce() + Send>);
}
pub struct InertDispatcher;
impl Dispatcher for InertDispatcher {
fn wake_at(&self, _timer_wakeup: Option<Instant>) {}
fn wake_now(&self) {}
fn dispatch(&self, work: Box<dyn FnOnce() + Send>) {
work();
}
}
pub trait Platform: Any {
fn target_platform(&self) -> TargetPlatform;
fn request_frame(&self, view: ViewId);
fn now(&self) -> Instant;
fn dispatcher(&self) -> Arc<dyn Dispatcher>;
fn views(&self) -> Vec<ViewRef>;
fn view(&self, id: ViewId) -> Option<ViewRef>;
fn request_view(
&self,
config: crate::WindowConfig,
initial_data: Option<String>,
) -> Result<crate::RequestId, crate::ViewError> {
let _ = (config, initial_data);
Err(crate::ViewError::Unsupported)
}
fn locales(&self) -> Vec<crate::Locale> {
Vec::new()
}
fn set_application_locale(&self, locale: &crate::Locale) {
let _ = locale;
}
fn platform_brightness(&self) -> Brightness {
Brightness::Light
}
fn default_route_name(&self) -> String {
String::from("/")
}
fn initial_lifecycle_state(&self) -> Option<AppLifecycleState> {
None
}
fn font_source(&self) -> Option<Box<dyn FontSource>> {
None
}
fn default_font_families(&self) -> Vec<String> {
match THIS_OS {
Some(os) => crate::default_font_families(os),
None => vec![String::from("Arial")],
}
}
fn open_image_codec(&self, bytes: std::sync::Arc<[u8]>) -> crate::ImageCodecFuture {
let _ = bytes;
Box::pin(std::future::ready(Err(crate::ImageDecodeError::NoDecoder)))
}
fn import_pixels(&self, pixels: valo::PixelBuffer) -> Option<crate::Image> {
let _ = pixels;
None
}
fn compositor(&self) -> Option<Arc<dyn crate::Compositor>> {
None
}
fn vsync_waiter(&self) -> Option<Arc<dyn crate::VsyncWaiter>> {
None
}
fn screens(&self) -> Option<&dyn Screens> {
None
}
fn clipboard(&self) -> Option<&dyn Clipboard> {
None
}
fn system_chrome(&self) -> Option<&dyn SystemChrome> {
None
}
fn haptics(&self) -> Option<&dyn Haptics> {
None
}
fn system_context_menu(&self) -> Option<&dyn SystemContextMenu> {
None
}
fn menu_bar(&self) -> Option<&dyn MenuBar> {
None
}
fn popup_menus(&self) -> Option<&dyn PopupMenus> {
None
}
fn tray_icons(&self) -> Option<&dyn TrayIcons> {
None
}
fn file_dialogs(&self) -> Option<&dyn FileDialogs> {
None
}
fn players(&self) -> Option<&dyn crate::Players> {
None
}
fn cameras(&self) -> Option<&dyn crate::Cameras> {
None
}
fn video_frame_readers(&self) -> Option<&dyn crate::VideoFrameReaders> {
None
}
fn caption_settings(&self) -> Option<&dyn crate::CaptionSettings> {
None
}
fn view_embedder(&self) -> Option<&dyn crate::ViewEmbedder> {
None
}
fn dock_menu(&self) -> Option<&dyn DockMenu> {
None
}
fn text_services(&self) -> Option<&dyn TextServices> {
None
}
fn restoration(&self) -> Option<&dyn Restoration> {
None
}
fn app_exit(&self) -> Option<&dyn AppExit> {
None
}
fn opener(&self) -> Option<&dyn Opener> {
None
}
fn notifications(&self) -> Option<&dyn Notifications> {
None
}
}
impl dyn Platform {
pub fn downcast_ref<T: Platform>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref::<T>()
}
}
pub trait SystemChrome {
fn set_overlay_style(&self, style: &SystemUiOverlayStyle);
fn set_application_switcher_description(&self, description: &ApplicationSwitcherDescription);
}
pub trait Haptics {
fn feedback(&self, kind: HapticFeedbackType);
}
pub trait SystemContextMenu {
fn show(&self, target_rect: Rect, items: Option<&[SystemContextMenuItem]>);
fn hide(&self);
}
pub trait Screens {
fn screens(&self) -> Vec<Screen>;
fn pointer_location(&self) -> Option<Offset>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ScreenId(pub u64);
#[derive(Clone, Debug, PartialEq)]
pub struct Screen {
pub id: ScreenId,
pub name: String,
pub frame: Rect,
pub visible_frame: Rect,
pub scale: f64,
pub refresh_rate: Option<f64>,
}
pub trait TextServices {
fn look_up(&self, text: &str);
fn search_web(&self, text: &str);
fn share(&self, text: &str);
}
pub trait Restoration {
fn get(&self) -> Option<RestorationUpdate>;
fn put(&self, data: RestorationMap);
}
pub trait AppExit {
fn quit(&self, exit_code: i32);
}
pub struct InertPlatform;
impl Platform for InertPlatform {
fn target_platform(&self) -> TargetPlatform {
TargetPlatform::Android
}
fn request_frame(&self, _view: ViewId) {}
fn now(&self) -> Instant {
Instant::now()
}
fn dispatcher(&self) -> Arc<dyn Dispatcher> {
Arc::new(InertDispatcher)
}
fn views(&self) -> Vec<ViewRef> {
Vec::new()
}
fn view(&self, _id: ViewId) -> Option<ViewRef> {
None
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Frame {
pub elapsed: Duration,
}
#[cfg(test)]
mod tests {
use super::{Brightness, InertPlatform, Platform, TargetPlatform};
#[test]
fn inert_platform_is_android() {
assert_eq!(InertPlatform.target_platform(), TargetPlatform::Android);
}
#[test]
fn inert_platform_brightness_is_light() {
assert_eq!(InertPlatform.platform_brightness(), Brightness::Light);
}
#[test]
fn a_platform_downcasts_to_its_own_type_and_no_other() {
struct Other;
impl Platform for Other {
fn target_platform(&self) -> TargetPlatform {
TargetPlatform::Linux
}
fn request_frame(&self, _view: super::ViewId) {}
fn now(&self) -> super::Instant {
super::Instant::now()
}
fn dispatcher(&self) -> std::sync::Arc<dyn super::Dispatcher> {
std::sync::Arc::new(super::InertDispatcher)
}
fn views(&self) -> Vec<super::ViewRef> {
Vec::new()
}
fn view(&self, _id: super::ViewId) -> Option<super::ViewRef> {
None
}
}
let platform: super::PlatformRef = std::rc::Rc::new(InertPlatform);
assert!(platform.downcast_ref::<InertPlatform>().is_some());
assert!(platform.downcast_ref::<Other>().is_none());
}
}