Merge pull request #2090 from zed-industries/workspace-window-position-persistence

Workspace window position persistence
This commit is contained in:
Kay Simmons 2023-01-27 15:24:01 -08:00 committed by GitHub
commit ea0dd8972f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1297 additions and 662 deletions

View file

@ -32,6 +32,7 @@ use collections::{hash_map::Entry, HashMap, HashSet, VecDeque};
use platform::Event;
#[cfg(any(test, feature = "test-support"))]
pub use test_app_context::{ContextHandle, TestAppContext};
use uuid::Uuid;
use crate::{
elements::ElementBox,
@ -43,6 +44,7 @@ use crate::{
util::post_inc,
Appearance, AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, KeyUpEvent,
ModifiersChangedEvent, MouseButton, MouseRegionId, PathPromptOptions, TextLayoutCache,
WindowBounds,
};
pub trait Entity: 'static {
@ -594,6 +596,7 @@ type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext
type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut MutableAppContext)>;
type WindowActivationCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
type WindowFullscreenCallback = Box<dyn FnMut(bool, &mut MutableAppContext) -> bool>;
type WindowBoundsCallback = Box<dyn FnMut(WindowBounds, Uuid, &mut MutableAppContext) -> bool>;
type KeystrokeCallback = Box<
dyn FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut MutableAppContext) -> bool,
>;
@ -624,6 +627,7 @@ pub struct MutableAppContext {
action_dispatch_observations: CallbackCollection<(), ActionObservationCallback>,
window_activation_observations: CallbackCollection<usize, WindowActivationCallback>,
window_fullscreen_observations: CallbackCollection<usize, WindowFullscreenCallback>,
window_bounds_observations: CallbackCollection<usize, WindowBoundsCallback>,
keystroke_observations: CallbackCollection<usize, KeystrokeCallback>,
#[allow(clippy::type_complexity)]
@ -681,6 +685,7 @@ impl MutableAppContext {
global_observations: Default::default(),
window_activation_observations: Default::default(),
window_fullscreen_observations: Default::default(),
window_bounds_observations: Default::default(),
keystroke_observations: Default::default(),
action_dispatch_observations: Default::default(),
presenters_and_platform_windows: Default::default(),
@ -905,10 +910,17 @@ impl MutableAppContext {
.map_or(false, |window| window.is_fullscreen)
}
pub fn window_bounds(&self, window_id: usize) -> RectF {
pub fn window_bounds(&self, window_id: usize) -> WindowBounds {
self.presenters_and_platform_windows[&window_id].1.bounds()
}
pub fn window_display_uuid(&self, window_id: usize) -> Uuid {
self.presenters_and_platform_windows[&window_id]
.1
.screen()
.display_uuid()
}
pub fn render_view(&mut self, params: RenderParams) -> Result<ElementBox> {
let window_id = params.window_id;
let view_id = params.view_id;
@ -1240,6 +1252,23 @@ impl MutableAppContext {
)
}
fn observe_window_bounds<F>(&mut self, window_id: usize, callback: F) -> Subscription
where
F: 'static + FnMut(WindowBounds, Uuid, &mut MutableAppContext) -> bool,
{
let subscription_id = post_inc(&mut self.next_subscription_id);
self.pending_effects
.push_back(Effect::WindowBoundsObservation {
window_id,
subscription_id,
callback: Box::new(callback),
});
Subscription::WindowBoundsObservation(
self.window_bounds_observations
.subscribe(window_id, subscription_id),
)
}
pub fn observe_keystrokes<F>(&mut self, window_id: usize, callback: F) -> Subscription
where
F: 'static
@ -1759,6 +1788,13 @@ impl MutableAppContext {
}));
}
{
let mut app = self.upgrade();
window.on_moved(Box::new(move || {
app.update(|cx| cx.window_was_moved(window_id))
}));
}
{
let mut app = self.upgrade();
window.on_fullscreen(Box::new(move |is_fullscreen| {
@ -2056,6 +2092,11 @@ impl MutableAppContext {
.invalidation
.get_or_insert(WindowInvalidation::default());
}
self.handle_window_moved(window_id);
}
Effect::MoveWindow { window_id } => {
self.handle_window_moved(window_id);
}
Effect::WindowActivationObservation {
@ -2088,6 +2129,16 @@ impl MutableAppContext {
is_fullscreen,
} => self.handle_fullscreen_effect(window_id, is_fullscreen),
Effect::WindowBoundsObservation {
window_id,
subscription_id,
callback,
} => self.window_bounds_observations.add_callback(
window_id,
subscription_id,
callback,
),
Effect::RefreshWindows => {
refreshing = true;
}
@ -2182,6 +2233,11 @@ impl MutableAppContext {
.push_back(Effect::ResizeWindow { window_id });
}
fn window_was_moved(&mut self, window_id: usize) {
self.pending_effects
.push_back(Effect::MoveWindow { window_id });
}
fn window_was_fullscreen_changed(&mut self, window_id: usize, is_fullscreen: bool) {
self.pending_effects.push_back(Effect::FullscreenWindow {
window_id,
@ -2314,11 +2370,18 @@ impl MutableAppContext {
let window = this.cx.windows.get_mut(&window_id)?;
window.is_fullscreen = is_fullscreen;
let mut observations = this.window_fullscreen_observations.clone();
observations.emit(window_id, this, |callback, this| {
let mut fullscreen_observations = this.window_fullscreen_observations.clone();
fullscreen_observations.emit(window_id, this, |callback, this| {
callback(is_fullscreen, this)
});
let bounds = this.window_bounds(window_id);
let uuid = this.window_display_uuid(window_id);
let mut bounds_observations = this.window_bounds_observations.clone();
bounds_observations.emit(window_id, this, |callback, this| {
callback(bounds, uuid, this)
});
Some(())
});
}
@ -2495,6 +2558,17 @@ impl MutableAppContext {
}
}
fn handle_window_moved(&mut self, window_id: usize) {
let bounds = self.window_bounds(window_id);
let display = self.window_display_uuid(window_id);
self.window_bounds_observations
.clone()
.emit(window_id, self, move |callback, this| {
callback(bounds, display, this);
true
});
}
pub fn focus(&mut self, window_id: usize, view_id: Option<usize>) {
self.pending_effects
.push_back(Effect::Focus { window_id, view_id });
@ -2898,9 +2972,8 @@ pub enum Effect {
ResizeWindow {
window_id: usize,
},
FullscreenWindow {
MoveWindow {
window_id: usize,
is_fullscreen: bool,
},
ActivateWindow {
window_id: usize,
@ -2911,11 +2984,20 @@ pub enum Effect {
subscription_id: usize,
callback: WindowActivationCallback,
},
FullscreenWindow {
window_id: usize,
is_fullscreen: bool,
},
WindowFullscreenObservation {
window_id: usize,
subscription_id: usize,
callback: WindowFullscreenCallback,
},
WindowBoundsObservation {
window_id: usize,
subscription_id: usize,
callback: WindowBoundsCallback,
},
Keystroke {
window_id: usize,
keystroke: Keystroke,
@ -3026,6 +3108,10 @@ impl Debug for Effect {
.debug_struct("Effect::RefreshWindow")
.field("window_id", window_id)
.finish(),
Effect::MoveWindow { window_id } => f
.debug_struct("Effect::MoveWindow")
.field("window_id", window_id)
.finish(),
Effect::WindowActivationObservation {
window_id,
subscription_id,
@ -3060,6 +3146,16 @@ impl Debug for Effect {
.field("window_id", window_id)
.field("subscription_id", subscription_id)
.finish(),
Effect::WindowBoundsObservation {
window_id,
subscription_id,
callback: _,
} => f
.debug_struct("Effect::WindowBoundsObservation")
.field("window_id", window_id)
.field("subscription_id", subscription_id)
.finish(),
Effect::RefreshWindows => f.debug_struct("Effect::FullViewRefresh").finish(),
Effect::WindowShouldCloseSubscription { window_id, .. } => f
.debug_struct("Effect::WindowShouldCloseSubscription")
@ -3635,7 +3731,7 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.toggle_window_full_screen(self.window_id)
}
pub fn window_bounds(&self) -> RectF {
pub fn window_bounds(&self) -> WindowBounds {
self.app.window_bounds(self.window_id)
}
@ -3939,6 +4035,24 @@ impl<'a, T: View> ViewContext<'a, T> {
)
}
pub fn observe_window_bounds<F>(&mut self, mut callback: F) -> Subscription
where
F: 'static + FnMut(&mut T, WindowBounds, Uuid, &mut ViewContext<T>),
{
let observer = self.weak_handle();
self.app
.observe_window_bounds(self.window_id(), move |bounds, display, cx| {
if let Some(observer) = observer.upgrade(cx) {
observer.update(cx, |observer, cx| {
callback(observer, bounds, display, cx);
});
true
} else {
false
}
})
}
pub fn emit(&mut self, payload: T::Event) {
self.app.pending_effects.push_back(Effect::Event {
entity_id: self.view_id,
@ -5103,6 +5217,7 @@ pub enum Subscription {
FocusObservation(callback_collection::Subscription<usize, FocusObservationCallback>),
WindowActivationObservation(callback_collection::Subscription<usize, WindowActivationCallback>),
WindowFullscreenObservation(callback_collection::Subscription<usize, WindowFullscreenCallback>),
WindowBoundsObservation(callback_collection::Subscription<usize, WindowBoundsCallback>),
KeystrokeObservation(callback_collection::Subscription<usize, KeystrokeCallback>),
ReleaseObservation(callback_collection::Subscription<usize, ReleaseObservationCallback>),
ActionObservation(callback_collection::Subscription<(), ActionObservationCallback>),
@ -5118,6 +5233,7 @@ impl Subscription {
Subscription::FocusObservation(subscription) => subscription.id(),
Subscription::WindowActivationObservation(subscription) => subscription.id(),
Subscription::WindowFullscreenObservation(subscription) => subscription.id(),
Subscription::WindowBoundsObservation(subscription) => subscription.id(),
Subscription::KeystrokeObservation(subscription) => subscription.id(),
Subscription::ReleaseObservation(subscription) => subscription.id(),
Subscription::ActionObservation(subscription) => subscription.id(),
@ -5134,6 +5250,7 @@ impl Subscription {
Subscription::KeystrokeObservation(subscription) => subscription.detach(),
Subscription::WindowActivationObservation(subscription) => subscription.detach(),
Subscription::WindowFullscreenObservation(subscription) => subscription.detach(),
Subscription::WindowBoundsObservation(subscription) => subscription.detach(),
Subscription::ReleaseObservation(subscription) => subscription.detach(),
Subscription::ActionObservation(subscription) => subscription.detach(),
}

View file

@ -18,11 +18,15 @@ use crate::{
text_layout::{LineLayout, RunStyle},
Action, ClipboardItem, Menu, Scene,
};
use anyhow::{anyhow, Result};
use anyhow::{anyhow, bail, Result};
use async_task::Runnable;
pub use event::*;
use postage::oneshot;
use serde::Deserialize;
use sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
use std::{
any::Any,
fmt::{self, Debug, Display},
@ -33,6 +37,7 @@ use std::{
sync::Arc,
};
use time::UtcOffset;
use uuid::Uuid;
pub trait Platform: Send + Sync {
fn dispatcher(&self) -> Arc<dyn Dispatcher>;
@ -44,6 +49,7 @@ pub trait Platform: Send + Sync {
fn unhide_other_apps(&self);
fn quit(&self);
fn screen_by_id(&self, id: Uuid) -> Option<Rc<dyn Screen>>;
fn screens(&self) -> Vec<Rc<dyn Screen>>;
fn open_window(
@ -117,17 +123,19 @@ pub trait InputHandler {
pub trait Screen: Debug {
fn as_any(&self) -> &dyn Any;
fn size(&self) -> Vector2F;
fn bounds(&self) -> RectF;
fn display_uuid(&self) -> Uuid;
}
pub trait Window {
fn bounds(&self) -> WindowBounds;
fn content_size(&self) -> Vector2F;
fn scale_factor(&self) -> f32;
fn titlebar_height(&self) -> f32;
fn appearance(&self) -> Appearance;
fn screen(&self) -> Rc<dyn Screen>;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>);
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
fn activate(&self);
@ -136,14 +144,16 @@ pub trait Window {
fn show_character_palette(&self);
fn minimize(&self);
fn zoom(&self);
fn present_scene(&mut self, scene: Scene);
fn toggle_full_screen(&self);
fn bounds(&self) -> RectF;
fn content_size(&self) -> Vector2F;
fn scale_factor(&self) -> f32;
fn titlebar_height(&self) -> f32;
fn present_scene(&mut self, scene: Scene);
fn appearance(&self) -> Appearance;
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>);
fn on_moved(&mut self, callback: Box<dyn FnMut()>);
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>);
fn is_topmost_for_position(&self, position: Vector2F) -> bool;
}
@ -186,12 +196,70 @@ pub enum WindowKind {
PopUp,
}
#[derive(Debug)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum WindowBounds {
Fullscreen,
Maximized,
Fixed(RectF),
}
impl StaticColumnCount for WindowBounds {
fn column_count() -> usize {
5
}
}
impl Bind for WindowBounds {
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
let (region, next_index) = match self {
WindowBounds::Fullscreen => {
let next_index = statement.bind("Fullscreen", start_index)?;
(None, next_index)
}
WindowBounds::Maximized => {
let next_index = statement.bind("Maximized", start_index)?;
(None, next_index)
}
WindowBounds::Fixed(region) => {
let next_index = statement.bind("Fixed", start_index)?;
(Some(*region), next_index)
}
};
statement.bind(
region.map(|region| {
(
region.min_x(),
region.min_y(),
region.width(),
region.height(),
)
}),
next_index,
)
}
}
impl Column for WindowBounds {
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
let (window_state, next_index) = String::column(statement, start_index)?;
let bounds = match window_state.as_str() {
"Fullscreen" => WindowBounds::Fullscreen,
"Maximized" => WindowBounds::Maximized,
"Fixed" => {
let ((x, y, width, height), _) = Column::column(statement, next_index)?;
WindowBounds::Fixed(RectF::new(
Vector2F::new(x, y),
Vector2F::new(width, height),
))
}
_ => bail!("Window State did not have a valid string"),
};
Ok((bounds, next_index + 4))
}
}
pub struct PathPromptOptions {
pub files: bool,
pub directories: bool,

View file

@ -12,12 +12,15 @@ mod sprite_cache;
mod status_item;
mod window;
use cocoa::base::{BOOL, NO, YES};
use cocoa::{
base::{id, nil, BOOL, NO, YES},
foundation::{NSAutoreleasePool, NSNotFound, NSString, NSUInteger},
};
pub use dispatcher::Dispatcher;
pub use fonts::FontSystem;
use platform::{MacForegroundPlatform, MacPlatform};
pub use renderer::Surface;
use std::{rc::Rc, sync::Arc};
use std::{ops::Range, rc::Rc, sync::Arc};
use window::Window;
pub(crate) fn platform() -> Arc<dyn super::Platform> {
@ -41,3 +44,57 @@ impl BoolExt for bool {
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct NSRange {
pub location: NSUInteger,
pub length: NSUInteger,
}
impl NSRange {
fn invalid() -> Self {
Self {
location: NSNotFound as NSUInteger,
length: 0,
}
}
fn is_valid(&self) -> bool {
self.location != NSNotFound as NSUInteger
}
fn to_range(self) -> Option<Range<usize>> {
if self.is_valid() {
let start = self.location as usize;
let end = start + self.length as usize;
Some(start..end)
} else {
None
}
}
}
impl From<Range<usize>> for NSRange {
fn from(range: Range<usize>) -> Self {
NSRange {
location: range.start as NSUInteger,
length: range.len() as NSUInteger,
}
}
}
unsafe impl objc::Encode for NSRange {
fn encode() -> objc::Encoding {
let encoding = format!(
"{{NSRange={}{}}}",
NSUInteger::encode().as_str(),
NSUInteger::encode().as_str()
);
unsafe { objc::Encoding::from_str(&encoding) }
}
}
unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease()
}

View file

@ -1,27 +1,97 @@
use cocoa::foundation::{NSPoint, NSRect, NSSize};
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use cocoa::{
appkit::NSWindow,
base::id,
foundation::{NSPoint, NSRect, NSSize},
};
use objc::{msg_send, sel, sel_impl};
use pathfinder_geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
};
///! Macos screen have a y axis that goings up from the bottom of the screen and
///! an origin at the bottom left of the main display.
pub trait Vector2FExt {
fn to_ns_point(&self) -> NSPoint;
fn to_ns_size(&self) -> NSSize;
/// Converts self to an NSPoint with y axis pointing up.
fn to_screen_ns_point(&self, native_window: id) -> NSPoint;
}
impl Vector2FExt for Vector2F {
fn to_screen_ns_point(&self, native_window: id) -> NSPoint {
unsafe {
let point = NSPoint::new(self.x() as f64, -self.y() as f64);
msg_send![native_window, convertPointToScreen: point]
}
}
}
pub trait RectFExt {
/// Converts self to an NSRect with y axis pointing up.
/// The resulting NSRect will have an origin at the bottom left of the rectangle.
/// Also takes care of converting from window scaled coordinates to screen coordinates
fn to_screen_ns_rect(&self, native_window: id) -> NSRect;
/// Converts self to an NSRect with y axis point up.
/// The resulting NSRect will have an origin at the bottom left of the rectangle.
/// Unlike to_screen_ns_rect, coordinates are not converted and are assumed to already be in screen scale
fn to_ns_rect(&self) -> NSRect;
}
impl Vector2FExt for Vector2F {
fn to_ns_point(&self) -> NSPoint {
NSPoint::new(self.x() as f64, self.y() as f64)
}
fn to_ns_size(&self) -> NSSize {
NSSize::new(self.x() as f64, self.y() as f64)
}
}
impl RectFExt for RectF {
fn to_screen_ns_rect(&self, native_window: id) -> NSRect {
unsafe { native_window.convertRectToScreen_(self.to_ns_rect()) }
}
fn to_ns_rect(&self) -> NSRect {
NSRect::new(self.origin().to_ns_point(), self.size().to_ns_size())
NSRect::new(
NSPoint::new(
self.origin_x() as f64,
-(self.origin_y() + self.height()) as f64,
),
NSSize::new(self.width() as f64, self.height() as f64),
)
}
}
pub trait NSRectExt {
/// Converts self to a RectF with y axis pointing down.
/// The resulting RectF will have an origin at the top left of the rectangle.
/// Also takes care of converting from screen scale coordinates to window coordinates
fn to_window_rectf(&self, native_window: id) -> RectF;
/// Converts self to a RectF with y axis pointing down.
/// The resulting RectF will have an origin at the top left of the rectangle.
/// Unlike to_screen_ns_rect, coordinates are not converted and are assumed to already be in screen scale
fn to_rectf(&self) -> RectF;
fn intersects(&self, other: Self) -> bool;
}
impl NSRectExt for NSRect {
fn to_window_rectf(&self, native_window: id) -> RectF {
unsafe {
self.origin.x;
let rect: NSRect = native_window.convertRectFromScreen_(*self);
rect.to_rectf()
}
}
fn to_rectf(&self) -> RectF {
RectF::new(
vec2f(
self.origin.x as f32,
-(self.origin.y + self.size.height) as f32,
),
vec2f(self.size.width as f32, self.size.height as f32),
)
}
fn intersects(&self, other: Self) -> bool {
self.size.width > 0.
&& self.size.height > 0.
&& other.size.width > 0.
&& other.size.height > 0.
&& self.origin.x <= other.origin.x + other.size.width
&& self.origin.x + self.size.width >= other.origin.x
&& self.origin.y <= other.origin.y + other.size.height
&& self.origin.y + self.size.height >= other.origin.y
}
}

View file

@ -440,6 +440,10 @@ impl platform::Platform for MacPlatform {
self.dispatcher.clone()
}
fn fonts(&self) -> Arc<dyn platform::FontSystem> {
self.fonts.clone()
}
fn activate(&self, ignoring_other_apps: bool) {
unsafe {
let app = NSApplication::sharedApplication(nil);
@ -488,6 +492,10 @@ impl platform::Platform for MacPlatform {
}
}
fn screen_by_id(&self, id: uuid::Uuid) -> Option<Rc<dyn crate::Screen>> {
Screen::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
}
fn screens(&self) -> Vec<Rc<dyn platform::Screen>> {
Screen::all()
.into_iter()
@ -512,10 +520,6 @@ impl platform::Platform for MacPlatform {
Box::new(StatusItem::add(self.fonts()))
}
fn fonts(&self) -> Arc<dyn platform::FontSystem> {
self.fonts.clone()
}
fn write_to_clipboard(&self, item: ClipboardItem) {
unsafe {
self.pasteboard.clearContents();

View file

@ -1,14 +1,25 @@
use std::any::Any;
use std::{any::Any, ffi::c_void};
use crate::{
geometry::vector::{vec2f, Vector2F},
platform,
};
use crate::platform;
use cocoa::{
appkit::NSScreen,
base::{id, nil},
foundation::NSArray,
foundation::{NSArray, NSDictionary},
};
use core_foundation::{
number::{kCFNumberIntType, CFNumberGetValue, CFNumberRef},
uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
};
use core_graphics::display::CGDirectDisplayID;
use pathfinder_geometry::rect::RectF;
use uuid::Uuid;
use super::{geometry::NSRectExt, ns_string};
#[link(name = "ApplicationServices", kind = "framework")]
extern "C" {
pub fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
}
#[derive(Debug)]
pub struct Screen {
@ -16,11 +27,23 @@ pub struct Screen {
}
impl Screen {
pub fn find_by_id(uuid: Uuid) -> Option<Self> {
unsafe {
let native_screens = NSScreen::screens(nil);
(0..NSArray::count(native_screens))
.into_iter()
.map(|ix| Screen {
native_screen: native_screens.objectAtIndex(ix),
})
.find(|screen| platform::Screen::display_uuid(screen) == uuid)
}
}
pub fn all() -> Vec<Self> {
let mut screens = Vec::new();
unsafe {
let native_screens = NSScreen::screens(nil);
for ix in 0..native_screens.count() {
for ix in 0..NSArray::count(native_screens) {
screens.push(Screen {
native_screen: native_screens.objectAtIndex(ix),
});
@ -35,10 +58,48 @@ impl platform::Screen for Screen {
self
}
fn size(&self) -> Vector2F {
fn display_uuid(&self) -> uuid::Uuid {
unsafe {
// Screen ids are not stable. Further, the default device id is also unstable across restarts.
// CGDisplayCreateUUIDFromDisplayID is stable but not exposed in the bindings we use.
// This approach is similar to that which winit takes
// https://github.com/rust-windowing/winit/blob/402cbd55f932e95dbfb4e8b5e8551c49e56ff9ac/src/platform_impl/macos/monitor.rs#L99
let device_description = self.native_screen.deviceDescription();
let key = ns_string("NSScreenNumber");
let device_id_obj = device_description.objectForKey_(key);
let mut device_id: u32 = 0;
CFNumberGetValue(
device_id_obj as CFNumberRef,
kCFNumberIntType,
(&mut device_id) as *mut _ as *mut c_void,
);
let cfuuid = CGDisplayCreateUUIDFromDisplayID(device_id as CGDirectDisplayID);
let bytes = CFUUIDGetUUIDBytes(cfuuid);
Uuid::from_bytes([
bytes.byte0,
bytes.byte1,
bytes.byte2,
bytes.byte3,
bytes.byte4,
bytes.byte5,
bytes.byte6,
bytes.byte7,
bytes.byte8,
bytes.byte9,
bytes.byte10,
bytes.byte11,
bytes.byte12,
bytes.byte13,
bytes.byte14,
bytes.byte15,
])
}
}
fn bounds(&self) -> RectF {
unsafe {
let frame = self.native_screen.frame();
vec2f(frame.size.width as f32, frame.size.height as f32)
frame.to_rectf()
}
}
}

View file

@ -7,7 +7,7 @@ use crate::{
self,
mac::{platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer},
},
Event, FontSystem, Scene,
Event, FontSystem, Scene, WindowBounds,
};
use cocoa::{
appkit::{NSScreen, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},
@ -32,6 +32,8 @@ use std::{
sync::Arc,
};
use super::screen::Screen;
static mut VIEW_CLASS: *const Class = ptr::null();
const STATE_IVAR: &str = "state";
@ -167,28 +169,42 @@ impl StatusItem {
}
impl platform::Window for StatusItem {
fn bounds(&self) -> WindowBounds {
self.0.borrow().bounds()
}
fn content_size(&self) -> Vector2F {
self.0.borrow().content_size()
}
fn scale_factor(&self) -> f32 {
self.0.borrow().scale_factor()
}
fn titlebar_height(&self) -> f32 {
0.
}
fn appearance(&self) -> crate::Appearance {
unsafe {
let appearance: id =
msg_send![self.0.borrow().native_item.button(), effectiveAppearance];
crate::Appearance::from_native(appearance)
}
}
fn screen(&self) -> Rc<dyn crate::Screen> {
unsafe {
Rc::new(Screen {
native_screen: self.0.borrow().native_window().screen(),
})
}
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event) -> bool>) {
self.0.borrow_mut().event_callback = Some(callback);
}
fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
self.0.borrow_mut().appearance_changed_callback = Some(callback);
}
fn on_active_status_change(&mut self, _: Box<dyn FnMut(bool)>) {}
fn on_resize(&mut self, _: Box<dyn FnMut()>) {}
fn on_fullscreen(&mut self, _: Box<dyn FnMut(bool)>) {}
fn on_should_close(&mut self, _: Box<dyn FnMut() -> bool>) {}
fn on_close(&mut self, _: Box<dyn FnOnce()>) {}
fn set_input_handler(&mut self, _: Box<dyn crate::InputHandler>) {}
fn prompt(
@ -224,26 +240,6 @@ impl platform::Window for StatusItem {
unimplemented!()
}
fn toggle_full_screen(&self) {
unimplemented!()
}
fn bounds(&self) -> RectF {
self.0.borrow().bounds()
}
fn content_size(&self) -> Vector2F {
self.0.borrow().content_size()
}
fn scale_factor(&self) -> f32 {
self.0.borrow().scale_factor()
}
fn titlebar_height(&self) -> f32 {
0.
}
fn present_scene(&mut self, scene: Scene) {
self.0.borrow_mut().scene = Some(scene);
unsafe {
@ -251,12 +247,28 @@ impl platform::Window for StatusItem {
}
}
fn appearance(&self) -> crate::Appearance {
unsafe {
let appearance: id =
msg_send![self.0.borrow().native_item.button(), effectiveAppearance];
crate::Appearance::from_native(appearance)
}
fn toggle_full_screen(&self) {
unimplemented!()
}
fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event) -> bool>) {
self.0.borrow_mut().event_callback = Some(callback);
}
fn on_active_status_change(&mut self, _: Box<dyn FnMut(bool)>) {}
fn on_resize(&mut self, _: Box<dyn FnMut()>) {}
fn on_fullscreen(&mut self, _: Box<dyn FnMut(bool)>) {}
fn on_moved(&mut self, _: Box<dyn FnMut()>) {}
fn on_should_close(&mut self, _: Box<dyn FnMut() -> bool>) {}
fn on_close(&mut self, _: Box<dyn FnOnce()>) {}
fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
self.0.borrow_mut().appearance_changed_callback = Some(callback);
}
fn is_topmost_for_position(&self, _: Vector2F) -> bool {
@ -265,9 +277,9 @@ impl platform::Window for StatusItem {
}
impl StatusItemState {
fn bounds(&self) -> RectF {
fn bounds(&self) -> WindowBounds {
unsafe {
let window: id = msg_send![self.native_item.button(), window];
let window: id = self.native_window();
let screen_frame = window.screen().visibleFrame();
let window_frame = NSWindow::frame(window);
let origin = vec2f(
@ -279,7 +291,7 @@ impl StatusItemState {
window_frame.size.width as f32,
window_frame.size.height as f32,
);
RectF::new(origin, size)
WindowBounds::Fixed(RectF::new(origin, size))
}
}
@ -297,6 +309,10 @@ impl StatusItemState {
NSScreen::backingScaleFactor(window.screen()) as f32
}
}
pub fn native_window(&self) -> id {
unsafe { msg_send![self.native_item.button(), window] }
}
}
extern "C" fn dealloc_view(this: &Object, _: Sel) {

View file

@ -17,14 +17,12 @@ use crate::{
use block::ConcreteBlock;
use cocoa::{
appkit::{
CGFloat, CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView,
NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton,
NSWindowCollectionBehavior, NSWindowStyleMask,
CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
NSWindowStyleMask,
},
base::{id, nil},
foundation::{
NSAutoreleasePool, NSInteger, NSNotFound, NSPoint, NSRect, NSSize, NSString, NSUInteger,
},
foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
};
use core_graphics::display::CGRect;
use ctor::ctor;
@ -52,6 +50,11 @@ use std::{
time::Duration,
};
use super::{
geometry::{NSRectExt, Vector2FExt},
ns_string, NSRange,
};
const WINDOW_STATE_IVAR: &str = "windowState";
static mut WINDOW_CLASS: *const Class = ptr::null();
@ -76,56 +79,6 @@ const NSTrackingInVisibleRect: NSUInteger = 0x200;
#[allow(non_upper_case_globals)]
const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct NSRange {
pub location: NSUInteger,
pub length: NSUInteger,
}
impl NSRange {
fn invalid() -> Self {
Self {
location: NSNotFound as NSUInteger,
length: 0,
}
}
fn is_valid(&self) -> bool {
self.location != NSNotFound as NSUInteger
}
fn to_range(self) -> Option<Range<usize>> {
if self.is_valid() {
let start = self.location as usize;
let end = start + self.length as usize;
Some(start..end)
} else {
None
}
}
}
impl From<Range<usize>> for NSRange {
fn from(range: Range<usize>) -> Self {
NSRange {
location: range.start as NSUInteger,
length: range.len() as NSUInteger,
}
}
}
unsafe impl objc::Encode for NSRange {
fn encode() -> objc::Encoding {
let encoding = format!(
"{{NSRange={}{}}}",
NSUInteger::encode().as_str(),
NSUInteger::encode().as_str()
);
unsafe { objc::Encoding::from_str(&encoding) }
}
}
#[ctor]
unsafe fn build_classes() {
WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
@ -295,6 +248,10 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
sel!(windowWillExitFullScreen:),
window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidMove:),
window_did_move as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidBecomeKey:),
window_did_change_key_status as extern "C" fn(&Object, Sel, id),
@ -311,8 +268,6 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
decl.register()
}
pub struct Window(Rc<RefCell<WindowState>>);
///Used to track what the IME does when we send it a keystroke.
///This is only used to handle the case where the IME mysteriously
///swallows certain keys.
@ -325,6 +280,11 @@ enum ImeState {
None,
}
struct InsertText {
replacement_range: Option<Range<usize>>,
text: String,
}
struct WindowState {
id: usize,
native_window: id,
@ -333,6 +293,7 @@ struct WindowState {
activate_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut()>>,
fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
moved_callback: Option<Box<dyn FnMut()>>,
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
close_callback: Option<Box<dyn FnOnce()>>,
appearance_changed_callback: Option<Box<dyn FnMut()>>,
@ -352,11 +313,109 @@ struct WindowState {
ime_text: Option<String>,
}
struct InsertText {
replacement_range: Option<Range<usize>>,
text: String,
impl WindowState {
fn move_traffic_light(&self) {
if let Some(traffic_light_position) = self.traffic_light_position {
let titlebar_height = self.titlebar_height();
unsafe {
let close_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowCloseButton
];
let min_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
];
let zoom_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowZoomButton
];
let mut close_button_frame: CGRect = msg_send![close_button, frame];
let mut min_button_frame: CGRect = msg_send![min_button, frame];
let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
let mut origin = vec2f(
traffic_light_position.x(),
titlebar_height
- traffic_light_position.y()
- close_button_frame.size.height as f32,
);
let button_spacing =
(min_button_frame.origin.x - close_button_frame.origin.x) as f32;
close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![close_button, setFrame: close_button_frame];
origin.set_x(origin.x() + button_spacing);
min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![min_button, setFrame: min_button_frame];
origin.set_x(origin.x() + button_spacing);
zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
}
}
}
fn is_fullscreen(&self) -> bool {
unsafe {
let style_mask = self.native_window.styleMask();
style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
}
}
fn bounds(&self) -> WindowBounds {
unsafe {
if self.is_fullscreen() {
return WindowBounds::Fullscreen;
}
let window_frame = self.frame();
if window_frame == self.native_window.screen().visibleFrame().to_rectf() {
WindowBounds::Maximized
} else {
WindowBounds::Fixed(window_frame)
}
}
}
// Returns the window bounds in window coordinates
fn frame(&self) -> RectF {
unsafe {
let ns_frame = NSWindow::frame(self.native_window);
ns_frame.to_rectf()
}
}
fn content_size(&self) -> Vector2F {
let NSSize { width, height, .. } =
unsafe { NSView::frame(self.native_window.contentView()) }.size;
vec2f(width as f32, height as f32)
}
fn scale_factor(&self) -> f32 {
get_scale_factor(self.native_window)
}
fn titlebar_height(&self) -> f32 {
unsafe {
let frame = NSWindow::frame(self.native_window);
let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
(frame.size.height - content_layout_rect.size.height) as f32
}
}
fn present_scene(&mut self, scene: Scene) {
self.scene_to_render = Some(scene);
unsafe {
let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
}
}
}
pub struct Window(Rc<RefCell<WindowState>>);
impl Window {
pub fn open(
id: usize,
@ -390,7 +449,7 @@ impl Window {
}
};
let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)),
style_mask,
NSBackingStoreBuffered,
NO,
@ -405,25 +464,20 @@ impl Window {
let screen = native_window.screen();
match options.bounds {
WindowBounds::Fullscreen => {
native_window.toggleFullScreen_(nil);
}
WindowBounds::Maximized => {
native_window.setFrame_display_(screen.visibleFrame(), YES);
}
WindowBounds::Fixed(top_left_bounds) => {
let frame = screen.visibleFrame();
let bottom_left_bounds = RectF::new(
vec2f(
top_left_bounds.origin_x(),
frame.size.height as f32
- top_left_bounds.origin_y()
- top_left_bounds.height(),
),
top_left_bounds.size(),
)
.to_ns_rect();
native_window.setFrame_display_(
native_window.convertRectToScreen_(bottom_left_bounds),
YES,
);
WindowBounds::Fixed(rect) => {
let screen_frame = screen.visibleFrame();
let ns_rect = rect.to_ns_rect();
if ns_rect.intersects(screen_frame) {
native_window.setFrame_display_(ns_rect, YES);
} else {
native_window.setFrame_display_(screen_frame, YES);
}
}
}
@ -441,6 +495,7 @@ impl Window {
close_callback: None,
activate_callback: None,
fullscreen_callback: None,
moved_callback: None,
appearance_changed_callback: None,
input_handler: None,
pending_key_down: None,
@ -576,34 +631,41 @@ impl Drop for Window {
}
impl platform::Window for Window {
fn bounds(&self) -> WindowBounds {
self.0.as_ref().borrow().bounds()
}
fn content_size(&self) -> Vector2F {
self.0.as_ref().borrow().content_size()
}
fn scale_factor(&self) -> f32 {
self.0.as_ref().borrow().scale_factor()
}
fn titlebar_height(&self) -> f32 {
self.0.as_ref().borrow().titlebar_height()
}
fn appearance(&self) -> crate::Appearance {
unsafe {
let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
crate::Appearance::from_native(appearance)
}
}
fn screen(&self) -> Rc<dyn crate::Screen> {
unsafe {
Rc::new(Screen {
native_screen: self.0.as_ref().borrow().native_window.screen(),
})
}
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
self.0.as_ref().borrow_mut().event_callback = Some(callback);
}
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
}
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
}
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
}
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
self.0.as_ref().borrow_mut().close_callback = Some(callback);
}
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
self.0.as_ref().borrow_mut().activate_callback = Some(callback);
}
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
self.0.as_ref().borrow_mut().input_handler = Some(input_handler);
}
@ -713,6 +775,10 @@ impl platform::Window for Window {
.detach();
}
fn present_scene(&mut self, scene: Scene) {
self.0.as_ref().borrow_mut().present_scene(scene);
}
fn toggle_full_screen(&self) {
let this = self.0.borrow();
let window = this.native_window;
@ -725,31 +791,32 @@ impl platform::Window for Window {
.detach();
}
fn bounds(&self) -> RectF {
self.0.as_ref().borrow().bounds()
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
self.0.as_ref().borrow_mut().event_callback = Some(callback);
}
fn content_size(&self) -> Vector2F {
self.0.as_ref().borrow().content_size()
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
self.0.as_ref().borrow_mut().activate_callback = Some(callback);
}
fn scale_factor(&self) -> f32 {
self.0.as_ref().borrow().scale_factor()
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
}
fn present_scene(&mut self, scene: Scene) {
self.0.as_ref().borrow_mut().present_scene(scene);
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
}
fn titlebar_height(&self) -> f32 {
self.0.as_ref().borrow().titlebar_height()
fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
self.0.as_ref().borrow_mut().moved_callback = Some(callback);
}
fn appearance(&self) -> crate::Appearance {
unsafe {
let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
crate::Appearance::from_native(appearance)
}
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
}
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
self.0.as_ref().borrow_mut().close_callback = Some(callback);
}
fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
@ -757,21 +824,16 @@ impl platform::Window for Window {
}
fn is_topmost_for_position(&self, position: Vector2F) -> bool {
let window_bounds = self.bounds();
let self_borrow = self.0.borrow();
let self_id = self_borrow.id;
unsafe {
let window_frame = self_borrow.frame();
let app = NSApplication::sharedApplication(nil);
// Convert back to bottom-left coordinates
let point = NSPoint::new(
position.x() as CGFloat,
(window_bounds.height() - position.y()) as CGFloat,
);
let screen_point: NSPoint =
msg_send![self_borrow.native_window, convertPointToScreen: point];
// Convert back to screen coordinates
let screen_point =
(position + window_frame.origin()).to_screen_ns_point(self_borrow.native_window);
let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
@ -788,94 +850,6 @@ impl platform::Window for Window {
}
}
impl WindowState {
fn move_traffic_light(&self) {
if let Some(traffic_light_position) = self.traffic_light_position {
let titlebar_height = self.titlebar_height();
unsafe {
let close_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowCloseButton
];
let min_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
];
let zoom_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowZoomButton
];
let mut close_button_frame: CGRect = msg_send![close_button, frame];
let mut min_button_frame: CGRect = msg_send![min_button, frame];
let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
let mut origin = vec2f(
traffic_light_position.x(),
titlebar_height
- traffic_light_position.y()
- close_button_frame.size.height as f32,
);
let button_spacing =
(min_button_frame.origin.x - close_button_frame.origin.x) as f32;
close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![close_button, setFrame: close_button_frame];
origin.set_x(origin.x() + button_spacing);
min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![min_button, setFrame: min_button_frame];
origin.set_x(origin.x() + button_spacing);
zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
}
}
}
fn bounds(&self) -> RectF {
unsafe {
let screen_frame = self.native_window.screen().visibleFrame();
let window_frame = NSWindow::frame(self.native_window);
let origin = vec2f(
window_frame.origin.x as f32,
(window_frame.origin.y - screen_frame.size.height - window_frame.size.height)
as f32,
);
let size = vec2f(
window_frame.size.width as f32,
window_frame.size.height as f32,
);
RectF::new(origin, size)
}
}
fn content_size(&self) -> Vector2F {
let NSSize { width, height, .. } =
unsafe { NSView::frame(self.native_window.contentView()) }.size;
vec2f(width as f32, height as f32)
}
fn scale_factor(&self) -> f32 {
get_scale_factor(self.native_window)
}
fn titlebar_height(&self) -> f32 {
unsafe {
let frame = NSWindow::frame(self.native_window);
let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
(frame.size.height - content_layout_rect.size.height) as f32
}
}
fn present_scene(&mut self, scene: Scene) {
self.scene_to_render = Some(scene);
unsafe {
let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
}
}
}
fn get_scale_factor(native_window: id) -> f32 {
unsafe {
let screen: id = msg_send![native_window, screen];
@ -1137,6 +1111,16 @@ fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
}
}
extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
let mut window_state_borrow = window_state.as_ref().borrow_mut();
if let Some(mut callback) = window_state_borrow.moved_callback.take() {
drop(window_state_borrow);
callback();
window_state.borrow_mut().moved_callback = Some(callback);
}
}
extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
let window_state_borrow = window_state.borrow();
@ -1499,10 +1483,6 @@ async fn synthetic_drag(
}
}
unsafe fn ns_string(string: &str) -> id {
NSString::alloc(nil).init_str(string).autorelease()
}
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
where
F: FnOnce(&mut dyn InputHandler) -> R,

View file

@ -20,11 +20,20 @@ use std::{
};
use time::UtcOffset;
pub struct Platform {
dispatcher: Arc<dyn super::Dispatcher>,
fonts: Arc<dyn super::FontSystem>,
current_clipboard_item: Mutex<Option<ClipboardItem>>,
cursor: Mutex<CursorStyle>,
struct Dispatcher;
impl super::Dispatcher for Dispatcher {
fn is_main_thread(&self) -> bool {
true
}
fn run_on_main_thread(&self, task: async_task::Runnable) {
task.run();
}
}
pub fn foreground_platform() -> ForegroundPlatform {
ForegroundPlatform::default()
}
#[derive(Default)]
@ -32,23 +41,6 @@ pub struct ForegroundPlatform {
last_prompt_for_new_path_args: RefCell<Option<(PathBuf, oneshot::Sender<Option<PathBuf>>)>>,
}
struct Dispatcher;
pub struct Window {
pub(crate) size: Vector2F,
scale_factor: f32,
current_scene: Option<crate::Scene>,
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
pub(crate) resize_handlers: Vec<Box<dyn FnMut()>>,
close_handlers: Vec<Box<dyn FnOnce()>>,
fullscreen_handlers: Vec<Box<dyn FnMut(bool)>>,
pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
pub(crate) title: Option<String>,
pub(crate) edited: bool,
pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
}
#[cfg(any(test, feature = "test-support"))]
impl ForegroundPlatform {
pub(crate) fn simulate_new_path_selection(
@ -102,6 +94,17 @@ impl super::ForegroundPlatform for ForegroundPlatform {
}
}
pub fn platform() -> Platform {
Platform::new()
}
pub struct Platform {
dispatcher: Arc<dyn super::Dispatcher>,
fonts: Arc<dyn super::FontSystem>,
current_clipboard_item: Mutex<Option<ClipboardItem>>,
cursor: Mutex<CursorStyle>,
}
impl Platform {
fn new() -> Self {
Self {
@ -132,6 +135,10 @@ impl super::Platform for Platform {
fn quit(&self) {}
fn screen_by_id(&self, _id: uuid::Uuid) -> Option<Rc<dyn crate::Screen>> {
None
}
fn screens(&self) -> Vec<Rc<dyn crate::Screen>> {
Default::default()
}
@ -143,7 +150,7 @@ impl super::Platform for Platform {
_executor: Rc<super::executor::Foreground>,
) -> Box<dyn super::Window> {
Box::new(Window::new(match options.bounds {
WindowBounds::Maximized => vec2f(1024., 768.),
WindowBounds::Maximized | WindowBounds::Fullscreen => vec2f(1024., 768.),
WindowBounds::Fixed(rect) => rect.size(),
}))
}
@ -219,12 +226,46 @@ impl super::Platform for Platform {
}
}
#[derive(Debug)]
pub struct Screen;
impl super::Screen for Screen {
fn as_any(&self) -> &dyn Any {
self
}
fn bounds(&self) -> RectF {
RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
}
fn display_uuid(&self) -> uuid::Uuid {
uuid::Uuid::new_v4()
}
}
pub struct Window {
pub(crate) size: Vector2F,
scale_factor: f32,
current_scene: Option<crate::Scene>,
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
pub(crate) resize_handlers: Vec<Box<dyn FnMut()>>,
pub(crate) moved_handlers: Vec<Box<dyn FnMut()>>,
close_handlers: Vec<Box<dyn FnOnce()>>,
fullscreen_handlers: Vec<Box<dyn FnMut(bool)>>,
pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
pub(crate) title: Option<String>,
pub(crate) edited: bool,
pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
}
impl Window {
fn new(size: Vector2F) -> Self {
Self {
size,
event_handlers: Default::default(),
resize_handlers: Default::default(),
moved_handlers: Default::default(),
close_handlers: Default::default(),
should_close_handler: Default::default(),
active_status_change_handlers: Default::default(),
@ -242,41 +283,35 @@ impl Window {
}
}
impl super::Dispatcher for Dispatcher {
fn is_main_thread(&self) -> bool {
true
}
fn run_on_main_thread(&self, task: async_task::Runnable) {
task.run();
}
}
impl super::Window for Window {
fn bounds(&self) -> WindowBounds {
WindowBounds::Fixed(RectF::new(Vector2F::zero(), self.size))
}
fn content_size(&self) -> Vector2F {
self.size
}
fn scale_factor(&self) -> f32 {
self.scale_factor
}
fn titlebar_height(&self) -> f32 {
24.
}
fn appearance(&self) -> crate::Appearance {
crate::Appearance::Light
}
fn screen(&self) -> Rc<dyn crate::Screen> {
Rc::new(Screen)
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event) -> bool>) {
self.event_handlers.push(callback);
}
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
self.active_status_change_handlers.push(callback);
}
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
self.fullscreen_handlers.push(callback)
}
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
self.resize_handlers.push(callback);
}
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
self.close_handlers.push(callback);
}
fn set_input_handler(&mut self, _: Box<dyn crate::InputHandler>) {}
fn prompt(&self, _: crate::PromptLevel, _: &str, _: &[&str]) -> oneshot::Receiver<usize> {
@ -295,40 +330,44 @@ impl super::Window for Window {
self.edited = edited;
}
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
self.should_close_handler = Some(callback);
}
fn show_character_palette(&self) {}
fn minimize(&self) {}
fn zoom(&self) {}
fn toggle_full_screen(&self) {}
fn bounds(&self) -> RectF {
RectF::new(Default::default(), self.size)
}
fn content_size(&self) -> Vector2F {
self.size
}
fn scale_factor(&self) -> f32 {
self.scale_factor
}
fn titlebar_height(&self) -> f32 {
24.
}
fn present_scene(&mut self, scene: crate::Scene) {
self.current_scene = Some(scene);
}
fn appearance(&self) -> crate::Appearance {
crate::Appearance::Light
fn toggle_full_screen(&self) {}
fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event) -> bool>) {
self.event_handlers.push(callback);
}
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
self.active_status_change_handlers.push(callback);
}
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
self.resize_handlers.push(callback);
}
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
self.fullscreen_handlers.push(callback)
}
fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
self.moved_handlers.push(callback);
}
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
self.should_close_handler = Some(callback);
}
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
self.close_handlers.push(callback);
}
fn on_appearance_changed(&mut self, _: Box<dyn FnMut()>) {}
@ -337,11 +376,3 @@ impl super::Window for Window {
true
}
}
pub fn platform() -> Platform {
Platform::new()
}
pub fn foreground_platform() -> ForegroundPlatform {
ForegroundPlatform::default()
}

View file

@ -23,7 +23,7 @@ use pathfinder_geometry::vector::{vec2f, Vector2F};
use serde_json::json;
use smallvec::SmallVec;
use sqlez::{
bindable::{Bind, Column},
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
use std::{
@ -932,6 +932,7 @@ impl ToJson for Axis {
}
}
impl StaticColumnCount for Axis {}
impl Bind for Axis {
fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
match self {