use crate::schema::{EventPayload, Theme};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
pub use crate::schema::{LastWindowClosed, WindowMode};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WindowDragPhase {
Move,
End,
Cancel,
}
impl WindowDragPhase {
fn from_wire(value: u32) -> Option<Self> {
match value {
crate::protocol::WINDOW_DRAG_MOVE => Some(Self::Move),
crate::protocol::WINDOW_DRAG_END => Some(Self::End),
crate::protocol::WINDOW_DRAG_CANCEL => Some(Self::Cancel),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WindowDragOverKind {
Enter,
Move,
Leave,
Drop,
}
impl WindowDragOverKind {
fn from_wire(value: u32) -> Option<Self> {
match value {
crate::protocol::WINDOW_DRAG_OVER_ENTER => Some(Self::Enter),
crate::protocol::WINDOW_DRAG_OVER_MOVE => Some(Self::Move),
crate::protocol::WINDOW_DRAG_OVER_LEAVE => Some(Self::Leave),
crate::protocol::WINDOW_DRAG_OVER_DROP => Some(Self::Drop),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum WindowEvent {
CloseRequested,
FocusChanged { focused: bool },
Moved {
x: f32,
y: f32,
width: f32,
height: f32,
},
Drag {
phase: WindowDragPhase,
screen_x: f32,
screen_y: f32,
over_window: u32,
},
DragOver {
kind: WindowDragOverKind,
window: u32,
x: f32,
y: f32,
},
Unavailable,
Closed,
}
impl WindowEvent {
pub(crate) fn from_payload(payload: &EventPayload<'_>) -> Option<Self> {
Some(match *payload {
EventPayload::WindowCloseRequested => Self::CloseRequested,
EventPayload::WindowFocusChanged { focused } => Self::FocusChanged { focused },
EventPayload::WindowMoved {
x,
y,
width,
height,
} => Self::Moved {
x,
y,
width,
height,
},
EventPayload::WindowDrag {
phase,
screen_x,
screen_y,
over_window,
} => Self::Drag {
phase: WindowDragPhase::from_wire(phase)?,
screen_x,
screen_y,
over_window,
},
EventPayload::WindowDragOver { kind, window, x, y } => Self::DragOver {
kind: WindowDragOverKind::from_wire(kind)?,
window,
x,
y,
},
EventPayload::WindowUnavailable => Self::Unavailable,
EventPayload::WindowClosed => Self::Closed,
_ => return None,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WindowFrame {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WindowDragEvent {
pub phase: WindowDragPhase,
pub screen_x: f32,
pub screen_y: f32,
pub over_window: u32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WindowDragOver {
pub kind: WindowDragOverKind,
pub window: u32,
pub x: f32,
pub y: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DragFromPointer {
pub pointer_id: u32,
pub anchor_x: f32,
pub anchor_y: f32,
}
impl DragFromPointer {
pub(crate) fn to_bytes(self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(12);
bytes.extend_from_slice(&self.pointer_id.to_le_bytes());
bytes.extend_from_slice(&self.anchor_x.to_bits().to_le_bytes());
bytes.extend_from_slice(&self.anchor_y.to_bits().to_le_bytes());
bytes
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WindowRequest {
pub key: String,
pub request: u32,
}
pub(crate) enum Command {
Focus(u32),
Theme(u32, Theme),
ZoomLevel(u32, i8),
}
type RequestHandler = Rc<dyn Fn(WindowRequest)>;
type ReopenHandler = Rc<dyn Fn()>;
thread_local! {
static DISPATCH_WINDOW: Cell<u32> = const { Cell::new(0) };
static COMMANDS: RefCell<Vec<Command>> = const { RefCell::new(Vec::new()) };
static APPLIED_THEMES: RefCell<Vec<(u32, Theme)>> = const { RefCell::new(Vec::new()) };
static REQUEST_HANDLERS: RefCell<Vec<(u64, RequestHandler)>> =
const { RefCell::new(Vec::new()) };
static REOPEN_HANDLERS: RefCell<Vec<(u64, ReopenHandler)>> =
const { RefCell::new(Vec::new()) };
static NEXT_ID: Cell<u64> = const { Cell::new(1) };
}
pub(crate) struct DispatchWindow {
previous: u32,
}
impl DispatchWindow {
pub(crate) fn enter(window: u32) -> Self {
let previous = DISPATCH_WINDOW.with(|current| current.replace(window));
Self { previous }
}
}
impl Drop for DispatchWindow {
fn drop(&mut self) {
let _ = DISPATCH_WINDOW.try_with(|current| current.set(self.previous));
}
}
pub(crate) fn current_window_id() -> u32 {
let composing = crate::runtime::with_composer_if_idle(|composer| {
composer.composing().then(|| composer.enclosing_window())
})
.flatten();
composing.unwrap_or_else(|| DISPATCH_WINDOW.with(Cell::get))
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WindowHandle {
node: u32,
}
pub fn current_window() -> Option<WindowHandle> {
match current_window_id() {
0 => None,
node => Some(WindowHandle { node }),
}
}
impl WindowHandle {
pub const fn id(&self) -> u32 {
self.node
}
pub fn focus(&self) {
COMMANDS.with_borrow_mut(|commands| commands.push(Command::Focus(self.node)));
crate::boundary::request_frame_from_worker();
}
pub fn set_theme(&self, theme: Theme) {
apply_theme(self.node, theme);
}
pub fn size(&self) -> crate::window::WindowSize {
crate::window::window_size_of(self.node)
}
pub fn zoom(&self) -> crate::zoom::Zoom {
crate::zoom::zoom_of(self.node)
}
pub fn set_zoom_level(&self, level: i8) {
queue_zoom_level(self.node, crate::zoom::clamp_level(level));
}
}
pub(crate) fn apply_theme(window: u32, theme: Theme) {
let changed = APPLIED_THEMES.with_borrow_mut(|applied| {
match applied.iter_mut().find(|(id, _)| *id == window) {
Some(entry) => {
if entry.1 == theme {
return false;
}
entry.1 = theme;
true
}
None => {
applied.push((window, theme));
true
}
}
});
if !changed {
return;
}
COMMANDS.with_borrow_mut(|commands| {
commands.retain(|command| !matches!(command, Command::Theme(id, _) if *id == window));
commands.push(Command::Theme(window, theme));
});
crate::boundary::request_frame_from_worker();
}
pub(crate) fn queue_zoom_level(window: u32, level: i8) {
COMMANDS.with_borrow_mut(|commands| {
commands.retain(|command| !matches!(command, Command::ZoomLevel(id, _) if *id == window));
commands.push(Command::ZoomLevel(window, level));
});
crate::boundary::request_frame_from_worker();
}
pub(crate) fn pending_zoom_level(window: u32) -> Option<i8> {
COMMANDS.with_borrow(|commands| {
commands.iter().find_map(|command| match command {
Command::ZoomLevel(id, level) if *id == window => Some(*level),
_ => None,
})
})
}
pub(crate) fn drain_commands(mut each: impl FnMut(Command)) {
let commands = COMMANDS.with_borrow_mut(std::mem::take);
for command in commands {
each(command);
}
}
pub(crate) fn forget_window(window: u32) {
crate::window::forget_window(window);
crate::zoom::forget_window(window);
APPLIED_THEMES.with_borrow_mut(|applied| applied.retain(|(id, _)| *id != window));
COMMANDS.with_borrow_mut(|commands| {
commands.retain(|command| match command {
Command::Focus(id) | Command::Theme(id, _) | Command::ZoomLevel(id, _) => *id != window,
});
});
}
#[doc(hidden)]
pub fn window_state_len() -> usize {
crate::window::window_state_len()
+ crate::zoom::window_state_len()
+ APPLIED_THEMES.with_borrow(Vec::len)
+ COMMANDS.with_borrow(Vec::len)
}
#[doc(hidden)]
pub fn reset_windows() {
DISPATCH_WINDOW.with(|current| current.set(0));
COMMANDS.with_borrow_mut(Vec::clear);
APPLIED_THEMES.with_borrow_mut(Vec::clear);
REQUEST_HANDLERS.with_borrow_mut(Vec::clear);
REOPEN_HANDLERS.with_borrow_mut(Vec::clear);
}
fn next_id() -> u64 {
NEXT_ID.with(|next| {
let id = next.get();
next.set(id + 1);
id
})
}
pub struct WindowRequestSubscription {
id: u64,
}
impl Drop for WindowRequestSubscription {
fn drop(&mut self) {
REQUEST_HANDLERS.with_borrow_mut(|handlers| handlers.retain(|(id, _)| *id != self.id));
}
}
pub fn on_window_request(handler: Rc<dyn Fn(WindowRequest)>) -> WindowRequestSubscription {
let id = next_id();
REQUEST_HANDLERS.with_borrow_mut(|handlers| handlers.push((id, handler)));
WindowRequestSubscription { id }
}
pub(crate) fn deliver_request(request: &WindowRequest) -> bool {
let handlers: Vec<Rc<dyn Fn(WindowRequest)>> = REQUEST_HANDLERS.with_borrow(|handlers| {
handlers
.iter()
.map(|(_, handler)| handler.clone())
.collect()
});
for handler in &handlers {
handler(request.clone());
}
!handlers.is_empty()
}
pub struct ApplicationReopenSubscription {
id: u64,
}
impl Drop for ApplicationReopenSubscription {
fn drop(&mut self) {
REOPEN_HANDLERS.with_borrow_mut(|handlers| handlers.retain(|(id, _)| *id != self.id));
}
}
pub fn on_application_reopen_handler(handler: Rc<dyn Fn()>) -> ApplicationReopenSubscription {
let id = next_id();
REOPEN_HANDLERS.with_borrow_mut(|handlers| handlers.push((id, handler)));
ApplicationReopenSubscription { id }
}
pub(crate) fn deliver_reopen() -> bool {
let handlers: Vec<Rc<dyn Fn()>> = REOPEN_HANDLERS.with_borrow(|handlers| {
handlers
.iter()
.map(|(_, handler)| handler.clone())
.collect()
});
for handler in &handlers {
handler();
}
!handlers.is_empty()
}