use inset_embedder::{ApplicationSwitcherDescription, SystemUiOverlayStyle};
use inset_foundation::{App, Handle, Listener};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SystemUiOverlay {
Top,
Bottom,
}
#[derive(Default)]
struct SystemChromeStyles {
pending_style: Option<SystemUiOverlayStyle>,
latest_style: Option<SystemUiOverlayStyle>,
}
pub struct SystemChrome;
impl SystemChrome {
pub fn set_application_switcher_description(
app: &App,
description: &ApplicationSwitcherDescription,
) {
app.platform()
.set_application_switcher_description(description);
}
pub fn set_system_ui_overlay_style(app: &mut App, style: &SystemUiOverlayStyle) {
let styles = app.singleton::<SystemChromeStyles>();
if app.get(styles).pending_style.is_some() {
app.get_mut(styles).pending_style = Some(*style);
return;
}
if Some(*style) == app.get(styles).latest_style {
return;
}
app.get_mut(styles).pending_style = Some(*style);
app.schedule_microtask(Listener::new(move |app| {
Self::send_pending_style(app, styles)
}));
}
fn send_pending_style(app: &mut App, styles: Handle<SystemChromeStyles>) {
let pending = app.get(styles).pending_style;
debug_assert!(pending.is_some());
if pending != app.get(styles).latest_style {
if let Some(style) = pending {
app.platform().set_system_ui_overlay_style(&style);
}
app.get_mut(styles).latest_style = pending;
}
app.get_mut(styles).pending_style = None;
}
pub fn latest_style(app: &mut App) -> Option<SystemUiOverlayStyle> {
let styles = app.singleton::<SystemChromeStyles>();
app.get(styles).latest_style
}
}
#[cfg(test)]
mod tests {
use inset_foundation::AppCell;
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Instant;
use inset_embedder::{
Brightness, Color, Platform, PlatformRef, TargetPlatform, ViewId, ViewRef,
};
use super::*;
#[derive(Default)]
struct RecordingPlatform {
styles: RefCell<Vec<SystemUiOverlayStyle>>,
}
impl Platform for RecordingPlatform {
fn target_platform(&self) -> TargetPlatform {
TargetPlatform::IOS
}
fn request_frame(&self) {}
fn now(&self) -> Instant {
Instant::now()
}
fn wake_at(&self, _deadline: Instant) {}
fn views(&self) -> Vec<ViewRef> {
Vec::new()
}
fn view(&self, _id: ViewId) -> Option<ViewRef> {
None
}
fn implicit_view(&self) -> Option<ViewRef> {
None
}
fn set_system_ui_overlay_style(&self, style: &SystemUiOverlayStyle) {
self.styles.borrow_mut().push(*style);
}
}
fn recording_app() -> (Rc<RecordingPlatform>, Rc<AppCell>) {
let platform = Rc::new(RecordingPlatform::default());
let cell = AppCell::with_platform(Rc::clone(&platform) as PlatformRef);
(platform, cell)
}
#[test]
fn light_and_dark_differ_only_in_the_two_status_bar_brightnesses() {
assert_eq!(
SystemUiOverlayStyle::LIGHT.status_bar_icon_brightness,
Some(Brightness::Light)
);
assert_eq!(
SystemUiOverlayStyle::LIGHT.status_bar_brightness,
Some(Brightness::Dark)
);
assert_eq!(
SystemUiOverlayStyle::DARK.status_bar_icon_brightness,
Some(Brightness::Dark)
);
assert_eq!(
SystemUiOverlayStyle::DARK.status_bar_brightness,
Some(Brightness::Light)
);
assert_eq!(
SystemUiOverlayStyle::LIGHT.system_navigation_bar_color,
SystemUiOverlayStyle::DARK.system_navigation_bar_color
);
assert_ne!(SystemUiOverlayStyle::LIGHT, SystemUiOverlayStyle::DARK);
}
#[test]
fn copy_with_replaces_only_what_it_is_given() {
let style = SystemUiOverlayStyle::LIGHT
.copy_with()
.status_bar_color(Color::new(0xFF112233));
assert_eq!(style.status_bar_color, Some(Color::new(0xFF112233)));
assert_eq!(
style.status_bar_icon_brightness,
SystemUiOverlayStyle::LIGHT.status_bar_icon_brightness
);
assert_eq!(SystemUiOverlayStyle::LIGHT.status_bar_color, None);
}
#[test]
fn only_the_last_style_of_a_turn_reaches_the_host() {
let (platform, cell) = recording_app();
let mut app = cell.borrow_mut();
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::LIGHT);
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::DARK);
assert!(
platform.styles.borrow().is_empty(),
"nothing is sent before the microtask runs"
);
assert_eq!(SystemChrome::latest_style(&mut app), None);
app.drain_microtasks();
assert_eq!(
platform.styles.borrow().as_slice(),
[SystemUiOverlayStyle::DARK]
);
assert_eq!(
SystemChrome::latest_style(&mut app),
Some(SystemUiOverlayStyle::DARK)
);
}
#[test]
fn a_style_already_in_effect_queues_nothing() {
let (platform, cell) = recording_app();
let mut app = cell.borrow_mut();
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::LIGHT);
app.drain_microtasks();
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::LIGHT);
app.drain_microtasks();
assert_eq!(
platform.styles.borrow().as_slice(),
[SystemUiOverlayStyle::LIGHT],
"an unchanged style does not reach the host again"
);
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::DARK);
app.drain_microtasks();
assert_eq!(
platform.styles.borrow().as_slice(),
[SystemUiOverlayStyle::LIGHT, SystemUiOverlayStyle::DARK]
);
}
#[test]
fn a_style_that_returns_to_the_latest_within_the_turn_sends_nothing() {
let (platform, cell) = recording_app();
let mut app = cell.borrow_mut();
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::LIGHT);
app.drain_microtasks();
platform.styles.borrow_mut().clear();
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::DARK);
SystemChrome::set_system_ui_overlay_style(&mut app, &SystemUiOverlayStyle::LIGHT);
app.drain_microtasks();
assert!(platform.styles.borrow().is_empty());
assert_eq!(
SystemChrome::latest_style(&mut app),
Some(SystemUiOverlayStyle::LIGHT)
);
}
}