Detailed changes
@@ -3,13 +3,12 @@ use crate::{
WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
-use async_task::Runnable;
-use futures::Future;
use parking_lot::RwLock;
use slotmap::SlotMap;
use std::{
any::Any,
marker::PhantomData,
+ mem,
sync::{Arc, Weak},
};
@@ -19,17 +18,43 @@ pub struct App(Arc<RwLock<AppContext<()>>>);
pub struct MainThread;
impl App {
- pub fn new() -> Self {
- Self(Arc::new(RwLock::new(AppContext::new(current_platform()))))
+ pub fn production() -> Self {
+ Self::new(current_platform())
+ }
+
+ #[cfg(any(test, feature = "test"))]
+ pub fn test() -> Self {
+ Self::new(Arc::new(super::TestPlatform::new()))
+ }
+
+ fn new(platform: Arc<dyn Platform>) -> Self {
+ let text_system = Arc::new(TextSystem::new(platform.text_system()));
+ let mut entities = SlotMap::with_key();
+ let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any>));
+ Self(Arc::new_cyclic(|this| {
+ RwLock::new(AppContext {
+ this: this.clone(),
+ thread: PhantomData,
+ platform,
+ text_system,
+ unit_entity_id,
+ entities,
+ windows: SlotMap::with_key(),
+ layout_id_buffer: Default::default(),
+ })
+ }))
}
pub fn run<F>(self, on_finish_launching: F)
where
F: 'static + FnOnce(&mut AppContext<MainThread>),
{
+ let platform = self.0.read().platform.clone();
platform.run(Box::new(move || {
let mut cx = self.0.write();
- on_finish_launching(&mut *cx);
+ let cx: &mut AppContext<()> = &mut cx;
+ let cx: &mut AppContext<MainThread> = unsafe { mem::transmute(cx) };
+ on_finish_launching(cx);
}));
}
}
@@ -37,7 +62,7 @@ impl App {
pub struct AppContext<Thread = ()> {
this: Weak<RwLock<AppContext>>,
thread: PhantomData<Thread>,
- platform: Box<dyn Platform>,
+ platform: Arc<dyn Platform>,
text_system: Arc<TextSystem>,
pub(crate) unit_entity_id: EntityId,
pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any>>>,
@@ -47,53 +72,33 @@ pub struct AppContext<Thread = ()> {
}
impl AppContext<()> {
- pub fn run_on_main<F: 'static, T: 'static>(
- &self,
- to_call: F,
- ) -> Result<T, impl Future<Output = T>>
- where
- F: Fn(&mut AppContext<MainThread>) -> T + Send + Sync,
- {
- let dispatcher = self.platform().dispatcher();
- if dispatcher.is_main_thread() {
- } else {
- let future = async move {
- // let cx = unsafe { };
- };
- let schedule = move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
-
- let (runnable, task) = async_task::spawn_local();
- runnable.schedule();
- }
-
- let (runnable, task) = async_task::spawn_local(future, schedule);
- runnable.schedule();
- task
- }
+ // pub fn run_on_main<F: 'static, T: 'static>(
+ // &self,
+ // to_call: F,
+ // ) -> Result<T, impl Future<Output = T>>
+ // where
+ // F: Fn(&mut AppContext<MainThread>) -> T + Send + Sync,
+ // {
+ // todo!();
+
+ // // let dispatcher = self.platform().dispatcher();
+ // // if dispatcher.is_main_thread() {
+ // // } else {
+ // // let future = async move {
+ // // // let cx = unsafe { };
+ // // };
+ // // let schedule = move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
+ // // // let (runnable, task) = async_task::spawn_local();
+ // // // runnable.schedule();
+ // // }
+
+ // // let (runnable, task) = async_task::spawn_local(future, schedule);
+ // // runnable.schedule();
+ // // task
+ // }
}
impl<Thread> AppContext<Thread> {
- pub fn new(platform: Arc<dyn Platform>) -> Self {
- let text_system = Arc::new(TextSystem::new(platform.text_system()));
- let mut entities = SlotMap::with_key();
- let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any>));
-
- AppContext {
- thread: PhantomData,
- platform,
- text_system,
- unit_entity_id,
- entities,
- windows: SlotMap::with_key(),
- layout_id_buffer: Default::default(),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn test() -> Self {
- Self::new(Arc::new(super::TestPlatform::new()))
- }
-
pub fn text_system(&self) -> &Arc<TextSystem> {
&self.text_system
}
@@ -101,7 +106,7 @@ impl<Thread> AppContext<Thread> {
pub fn open_window<S: 'static>(
&mut self,
options: crate::WindowOptions,
- build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S>,
+ build_root_view: impl FnOnce(&mut WindowContext<Thread>) -> RootView<S>,
) -> WindowHandle<S> {
let id = self.windows.insert(None);
let handle = WindowHandle::new(id);
@@ -118,7 +123,7 @@ impl<Thread> AppContext<Thread> {
pub(crate) fn update_window<R>(
&mut self,
window_id: WindowId,
- update: impl FnOnce(&mut WindowContext) -> R,
+ update: impl FnOnce(&mut WindowContext<Thread>) -> R,
) -> Result<R> {
let mut window = self
.windows
@@ -1,6 +1,5 @@
use super::{Layout, LayoutId, Pixels, Point, Result, ViewContext};
pub(crate) use smallvec::SmallVec;
-use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
pub trait Element: 'static {
type State;
@@ -1,7 +1,8 @@
use crate::util;
+use crate::PlatformDispatcher;
use anyhow::{anyhow, Result};
use async_task::Runnable;
-use futures::channel::mpsc;
+use futures::channel::{mpsc, oneshot};
use smol::{channel, prelude::*, Executor};
use std::{
any::Any,
@@ -16,7 +17,51 @@ use std::{
time::Duration,
};
-use crate::PlatformDispatcher;
+/// Enqueues the given closure to be run on the application's event loop. Can be
+/// called on any thread.
+pub(crate) fn spawn_on_main<R>(
+ dispatcher: Arc<dyn PlatformDispatcher>,
+ future: impl Future<Output = R> + Send + 'static,
+) -> impl Future<Output = R>
+where
+ R: Send + 'static,
+{
+ let (tx, rx) = oneshot::channel();
+ let (runnable, task) = async_task::spawn(
+ async move {
+ let result = future.await;
+ let _ = tx.send(result);
+ },
+ move |runnable| dispatcher.run_on_main_thread(runnable),
+ );
+ runnable.schedule();
+ task.detach();
+ async move { rx.await.unwrap() }
+}
+
+/// Enqueues the given closure to be run on the application's event loop. Must
+/// be called on the main thread.
+pub(crate) fn spawn_on_main_local<R>(
+ dispatcher: Arc<dyn PlatformDispatcher>,
+ future: impl Future<Output = R> + 'static,
+) -> impl Future<Output = R>
+where
+ R: 'static,
+{
+ assert!(dispatcher.is_main_thread(), "must be called on main thread");
+
+ let (tx, rx) = oneshot::channel();
+ let (runnable, task) = async_task::spawn_local(
+ async move {
+ let result = future.await;
+ let _ = tx.send(result);
+ },
+ move |runnable| dispatcher.run_on_main_thread(runnable),
+ );
+ runnable.schedule();
+ task.detach();
+ async move { rx.await.unwrap() }
+}
pub enum ForegroundExecutor {
Platform {
@@ -6,8 +6,8 @@ mod mac;
mod test;
use crate::{
- AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight,
- ForegroundExecutor, GlyphId, LineLayout, Pixels, Point, Result, RunStyle, SharedString, Size,
+ AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, GlyphId,
+ LineLayout, Pixels, Point, Result, RunStyle, SharedString, Size,
};
use anyhow::anyhow;
use async_task::Runnable;
@@ -37,14 +37,14 @@ pub use test::*;
#[cfg(target_os = "macos")]
pub(crate) fn current_platform() -> Arc<dyn Platform> {
- Rc::new(MacPlatform::new())
+ Arc::new(MacPlatform::new())
}
pub trait Platform {
fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
- fn run(&self, on_finish_launching: Box<dyn FnOnce()>);
+ fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
fn quit(&self);
fn restart(&self);
fn activate(&self, ignoring_other_apps: bool);
@@ -1,8 +1,8 @@
use super::BoolExt;
use crate::{
- AnyWindowHandle, ClipboardItem, CursorStyle, Event, ForegroundExecutor, MacDispatcher,
- MacScreen, MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformScreen,
- PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions,
+ AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem,
+ MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow,
+ Result, SemanticVersion, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
@@ -33,9 +33,10 @@ use objc::{
runtime::{Class, Object, Sel},
sel, sel_impl,
};
+use parking_lot::Mutex;
use ptr::null_mut;
use std::{
- cell::{Cell, RefCell},
+ cell::Cell,
convert::TryInto,
ffi::{c_void, CStr, OsStr},
os::{raw::c_char, unix::ffi::OsStrExt},
@@ -138,10 +139,10 @@ unsafe fn build_classes() {
}
}
-pub struct MacPlatform(RefCell<MacPlatformState>);
+pub struct MacPlatform(Mutex<MacPlatformState>);
pub struct MacPlatformState {
- executor: Rc<ForegroundExecutor>,
+ dispatcher: Arc<MacDispatcher>,
text_system: Arc<MacTextSystem>,
pasteboard: id,
text_hash_pasteboard_type: id,
@@ -161,8 +162,8 @@ pub struct MacPlatformState {
impl MacPlatform {
pub fn new() -> Self {
- Self(RefCell::new(MacPlatformState {
- executor: Rc::new(ForegroundExecutor::new(Arc::new(MacDispatcher)).unwrap()),
+ Self(Mutex::new(MacPlatformState {
+ dispatcher: Arc::new(MacDispatcher),
text_system: Arc::new(MacTextSystem::new()),
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
@@ -182,7 +183,7 @@ impl MacPlatform {
}
unsafe fn read_from_pasteboard(&self, kind: id) -> Option<&[u8]> {
- let pasteboard = self.0.borrow().pasteboard;
+ let pasteboard = self.0.lock().pasteboard;
let data = pasteboard.dataForType(kind);
if data == nil {
None
@@ -342,16 +343,16 @@ impl MacPlatform {
}
impl Platform for MacPlatform {
- fn executor(&self) -> Rc<ForegroundExecutor> {
- self.0.borrow().executor.clone()
+ fn dispatcher(&self) -> Arc<dyn crate::PlatformDispatcher> {
+ Arc::new(MacDispatcher)
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
- self.0.borrow().text_system.clone()
+ self.0.lock().text_system.clone()
}
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
- self.0.borrow_mut().finish_launching = Some(on_finish_launching);
+ self.0.lock().finish_launching = Some(on_finish_launching);
unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication];
@@ -465,25 +466,21 @@ impl Platform for MacPlatform {
MacScreen::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
}
- fn main_window(&self) -> Option<AnyWindowHandle> {
- MacWindow::main_window()
- }
-
// fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
// Box::new(StatusItem::add(self.fonts()))
// }
+ fn main_window(&self) -> Option<AnyWindowHandle> {
+ MacWindow::main_window()
+ }
+
fn open_window(
&self,
handle: AnyWindowHandle,
options: WindowOptions,
) -> Box<dyn PlatformWindow> {
- Box::new(MacWindow::open(
- handle,
- options,
- self.executor(),
- self.text_system(),
- ))
+ let dispatcher = self.0.lock().dispatcher.clone();
+ Box::new(MacWindow::open(handle, options, dispatcher))
}
fn open_url(&self, url: &str) {
@@ -497,7 +494,7 @@ impl Platform for MacPlatform {
}
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
- self.0.borrow_mut().open_urls = Some(callback);
+ self.0.lock().open_urls = Some(callback);
}
fn prompt_for_paths(
@@ -570,41 +567,38 @@ impl Platform for MacPlatform {
fn reveal_path(&self, path: &Path) {
unsafe {
let path = path.to_path_buf();
- self.0
- .borrow()
- .executor
- .spawn(async move {
- let full_path = ns_string(path.to_str().unwrap_or(""));
- let root_full_path = ns_string("");
- let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
- let _: BOOL = msg_send![
- workspace,
- selectFile: full_path
- inFileViewerRootedAtPath: root_full_path
- ];
- })
- .detach();
+ let dispatcher = self.0.lock().dispatcher.clone();
+ let _ = crate::spawn_on_main_local(dispatcher, async move {
+ let full_path = ns_string(path.to_str().unwrap_or(""));
+ let root_full_path = ns_string("");
+ let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
+ let _: BOOL = msg_send![
+ workspace,
+ selectFile: full_path
+ inFileViewerRootedAtPath: root_full_path
+ ];
+ });
}
}
fn on_become_active(&self, callback: Box<dyn FnMut()>) {
- self.0.borrow_mut().become_active = Some(callback);
+ self.0.lock().become_active = Some(callback);
}
fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
- self.0.borrow_mut().resign_active = Some(callback);
+ self.0.lock().resign_active = Some(callback);
}
fn on_quit(&self, callback: Box<dyn FnMut()>) {
- self.0.borrow_mut().quit = Some(callback);
+ self.0.lock().quit = Some(callback);
}
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
- self.0.borrow_mut().reopen = Some(callback);
+ self.0.lock().reopen = Some(callback);
}
fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
- self.0.borrow_mut().event = Some(callback);
+ self.0.lock().event = Some(callback);
}
fn os_name(&self) -> &'static str {
@@ -704,7 +698,7 @@ impl Platform for MacPlatform {
}
fn write_to_clipboard(&self, item: ClipboardItem) {
- let state = self.0.borrow();
+ let state = self.0.lock();
unsafe {
state.pasteboard.clearContents();
@@ -741,7 +735,7 @@ impl Platform for MacPlatform {
}
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
- let state = self.0.borrow();
+ let state = self.0.lock();
unsafe {
if let Some(text_bytes) = self.read_from_pasteboard(NSPasteboardTypeString) {
let text = String::from_utf8_lossy(text_bytes).to_string();
@@ -777,6 +771,32 @@ impl Platform for MacPlatform {
}
}
+ // fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
+ // self.0.lock().menu_command = Some(callback);
+ // }
+
+ // fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
+ // self.0.lock().will_open_menu = Some(callback);
+ // }
+
+ // fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
+ // self.0.lock().validate_menu_command = Some(callback);
+ // }
+
+ // fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
+ // unsafe {
+ // let app: id = msg_send![APP_CLASS, sharedApplication];
+ // let mut state = self.0.lock();
+ // let actions = &mut state.menu_actions;
+ // app.setMainMenu_(self.create_menu_bar(
+ // menus,
+ // app.delegate(),
+ // actions,
+ // keystroke_matcher,
+ // ));
+ // }
+ // }
+
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
let url = CFString::from(url);
let username = CFString::from(username);
@@ -816,32 +836,6 @@ impl Platform for MacPlatform {
Ok(())
}
- // fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
- // self.0.borrow_mut().menu_command = Some(callback);
- // }
-
- // fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
- // self.0.borrow_mut().will_open_menu = Some(callback);
- // }
-
- // fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
- // self.0.borrow_mut().validate_menu_command = Some(callback);
- // }
-
- // fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
- // unsafe {
- // let app: id = msg_send![APP_CLASS, sharedApplication];
- // let mut state = self.0.borrow_mut();
- // let actions = &mut state.menu_actions;
- // app.setMainMenu_(self.create_menu_bar(
- // menus,
- // app.delegate(),
- // actions,
- // keystroke_matcher,
- // ));
- // }
- // }
-
fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
let url = CFString::from(url);
let cf_true = CFBoolean::true_value().as_CFTypeRef();
@@ -921,7 +915,7 @@ extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
unsafe {
if let Some(event) = Event::from_native(native_event, None) {
let platform = get_foreground_platform(this);
- if let Some(callback) = platform.0.borrow_mut().event.as_mut() {
+ if let Some(callback) = platform.0.lock().event.as_mut() {
if callback(event) {
return;
}
@@ -937,7 +931,7 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
let platform = get_foreground_platform(this);
- let callback = platform.0.borrow_mut().finish_launching.take();
+ let callback = platform.0.lock().finish_launching.take();
if let Some(callback) = callback {
callback();
}
@@ -947,7 +941,7 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_windows: bool) {
if !has_open_windows {
let platform = unsafe { get_foreground_platform(this) };
- if let Some(callback) = platform.0.borrow_mut().reopen.as_mut() {
+ if let Some(callback) = platform.0.lock().reopen.as_mut() {
callback();
}
}
@@ -955,21 +949,21 @@ extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_wi
extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) {
let platform = unsafe { get_foreground_platform(this) };
- if let Some(callback) = platform.0.borrow_mut().become_active.as_mut() {
+ if let Some(callback) = platform.0.lock().become_active.as_mut() {
callback();
}
}
extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) {
let platform = unsafe { get_foreground_platform(this) };
- if let Some(callback) = platform.0.borrow_mut().resign_active.as_mut() {
+ if let Some(callback) = platform.0.lock().resign_active.as_mut() {
callback();
}
}
extern "C" fn will_terminate(this: &mut Object, _: Sel, _: id) {
let platform = unsafe { get_foreground_platform(this) };
- if let Some(callback) = platform.0.borrow_mut().quit.as_mut() {
+ if let Some(callback) = platform.0.lock().quit.as_mut() {
callback();
}
}
@@ -991,7 +985,7 @@ extern "C" fn open_urls(this: &mut Object, _: Sel, _: id, urls: id) {
.collect::<Vec<_>>()
};
let platform = unsafe { get_foreground_platform(this) };
- if let Some(callback) = platform.0.borrow_mut().open_urls.as_mut() {
+ if let Some(callback) = platform.0.lock().open_urls.as_mut() {
callback(urls);
}
}
@@ -1000,7 +994,7 @@ extern "C" fn handle_menu_item(__this: &mut Object, _: Sel, __item: id) {
todo!()
// unsafe {
// let platform = get_foreground_platform(this);
- // let mut platform = platform.0.borrow_mut();
+ // let mut platform = platform.0.lock();
// if let Some(mut callback) = platform.menu_command.take() {
// let tag: NSInteger = msg_send![item, tag];
// let index = tag as usize;
@@ -1017,7 +1011,7 @@ extern "C" fn validate_menu_item(__this: &mut Object, _: Sel, __item: id) -> boo
// unsafe {
// let mut result = false;
// let platform = get_foreground_platform(this);
- // let mut platform = platform.0.borrow_mut();
+ // let mut platform = platform.0.lock();
// if let Some(mut callback) = platform.validate_menu_command.take() {
// let tag: NSInteger = msg_send![item, tag];
// let index = tag as usize;
@@ -1033,7 +1027,7 @@ extern "C" fn validate_menu_item(__this: &mut Object, _: Sel, __item: id) -> boo
extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
unsafe {
let platform = get_foreground_platform(this);
- let mut platform = platform.0.borrow_mut();
+ let mut platform = platform.0.lock();
if let Some(mut callback) = platform.will_open_menu.take() {
callback();
platform.will_open_menu = Some(callback);
@@ -1112,7 +1106,7 @@ mod tests {
);
platform
.0
- .borrow_mut()
+ .lock()
.pasteboard
.setData_forType(bytes, NSPasteboardTypeString);
}
@@ -1124,7 +1118,7 @@ mod tests {
fn build_platform() -> MacPlatform {
let platform = MacPlatform::new();
- platform.0.borrow_mut().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
+ platform.0.lock().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
platform
}
}
@@ -1,9 +1,9 @@
use crate::{
- point, px, size, AnyWindowHandle, Bounds, Event, ForegroundExecutor, InputHandler,
- KeyDownEvent, Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton,
- MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, PlatformScreen,
- PlatformTextSystem, PlatformWindow, Point, Size, Timer, WindowAppearance, WindowBounds,
- WindowKind, WindowOptions, WindowPromptLevel,
+ point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke,
+ MacDispatcher, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+ MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, PlatformDispatcher, PlatformScreen,
+ PlatformWindow, Point, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
+ WindowPromptLevel,
};
use block::ConcreteBlock;
use cocoa::{
@@ -25,6 +25,7 @@ use objc::{
runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
sel, sel_impl,
};
+use parking_lot::Mutex;
use raw_window_handle::{
AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle,
RawDisplayHandle, RawWindowHandle,
@@ -282,6 +283,7 @@ struct InsertText {
struct WindowState {
handle: AnyWindowHandle,
+ dispatcher: Arc<dyn PlatformDispatcher>,
native_window: id,
kind: WindowKind,
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
@@ -296,7 +298,6 @@ struct WindowState {
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
last_key_equivalent: Option<KeyDownEvent>,
synthetic_drag_counter: usize,
- executor: Rc<ForegroundExecutor>,
last_fresh_keydown: Option<Keystroke>,
traffic_light_position: Option<Point<Pixels>>,
previous_modifiers_changed_event: Option<Event>,
@@ -411,14 +412,15 @@ impl WindowState {
}
}
-pub struct MacWindow(Rc<RefCell<WindowState>>);
+unsafe impl Send for WindowState {}
+
+pub struct MacWindow(Arc<Mutex<WindowState>>);
impl MacWindow {
pub fn open(
handle: AnyWindowHandle,
options: WindowOptions,
- executor: Rc<ForegroundExecutor>,
- text_system: Arc<dyn PlatformTextSystem>,
+ dispatcher: Arc<MacDispatcher>,
) -> Self {
unsafe {
let pool = NSAutoreleasePool::new(nil);
@@ -483,8 +485,9 @@ impl MacWindow {
assert!(!native_view.is_null());
- let window = Self(Rc::new(RefCell::new(WindowState {
+ let window = Self(Arc::new(Mutex::new(WindowState {
handle,
+ dispatcher,
native_window,
kind: options.kind,
event_callback: None,
@@ -499,7 +502,6 @@ impl MacWindow {
pending_key_down: None,
last_key_equivalent: None,
synthetic_drag_counter: 0,
- executor,
last_fresh_keydown: None,
traffic_light_position: options
.titlebar
@@ -512,12 +514,12 @@ impl MacWindow {
(*native_window).set_ivar(
WINDOW_STATE_IVAR,
- Rc::into_raw(window.0.clone()) as *const c_void,
+ Arc::into_raw(window.0.clone()) as *const c_void,
);
native_window.setDelegate_(native_window);
(*native_view).set_ivar(
WINDOW_STATE_IVAR,
- Rc::into_raw(window.0.clone()) as *const c_void,
+ Arc::into_raw(window.0.clone()) as *const c_void,
);
if let Some(title) = options
@@ -596,7 +598,7 @@ impl MacWindow {
native_window.orderFront_(nil);
}
- window.0.borrow().move_traffic_light();
+ window.0.lock().move_traffic_light();
pool.drain();
window
@@ -619,21 +621,19 @@ impl MacWindow {
impl Drop for MacWindow {
fn drop(&mut self) {
- let this = self.0.borrow();
- let window = this.native_window;
- this.executor
- .spawn(async move {
- unsafe {
- window.close();
- }
- })
- .detach();
+ let this = self.0.clone();
+ let dispatcher = self.0.lock().dispatcher.clone();
+ let _ = crate::spawn_on_main(dispatcher, async move {
+ unsafe {
+ this.lock().native_window.close();
+ }
+ });
}
}
unsafe impl HasRawWindowHandle for MacWindow {
fn raw_window_handle(&self) -> RawWindowHandle {
- let ns_window = self.0.borrow().native_window;
+ let ns_window = self.0.lock().native_window;
let ns_view = unsafe { ns_window.contentView() };
let mut handle = AppKitWindowHandle::empty();
handle.ns_window = ns_window as *mut c_void;
@@ -650,24 +650,24 @@ unsafe impl HasRawDisplayHandle for MacWindow {
impl PlatformWindow for MacWindow {
fn bounds(&self) -> WindowBounds {
- self.0.as_ref().borrow().bounds()
+ self.0.as_ref().lock().bounds()
}
fn content_size(&self) -> Size<Pixels> {
- self.0.as_ref().borrow().content_size().into()
+ self.0.as_ref().lock().content_size().into()
}
fn scale_factor(&self) -> f32 {
- self.0.as_ref().borrow().scale_factor()
+ self.0.as_ref().lock().scale_factor()
}
fn titlebar_height(&self) -> Pixels {
- self.0.as_ref().borrow().titlebar_height()
+ self.0.as_ref().lock().titlebar_height()
}
fn appearance(&self) -> WindowAppearance {
unsafe {
- let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
+ let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
WindowAppearance::from_native(appearance)
}
}
@@ -675,7 +675,7 @@ impl PlatformWindow for MacWindow {
fn screen(&self) -> Rc<dyn PlatformScreen> {
unsafe {
Rc::new(MacScreen {
- native_screen: self.0.as_ref().borrow().native_window.screen(),
+ native_screen: self.0.as_ref().lock().native_window.screen(),
})
}
}
@@ -683,7 +683,7 @@ impl PlatformWindow for MacWindow {
fn mouse_position(&self) -> Point<Pixels> {
let position = unsafe {
self.0
- .borrow()
+ .lock()
.native_window
.mouseLocationOutsideOfEventStream()
};
@@ -695,7 +695,7 @@ impl PlatformWindow for MacWindow {
}
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
- self.0.as_ref().borrow_mut().input_handler = Some(input_handler);
+ self.0.as_ref().lock().input_handler = Some(input_handler);
}
fn prompt(
@@ -761,131 +761,123 @@ impl PlatformWindow for MacWindow {
}
});
let block = block.copy();
- let native_window = self.0.borrow().native_window;
- self.0
- .borrow()
- .executor
- .spawn(async move {
- let _: () = msg_send![
- alert,
- beginSheetModalForWindow: native_window
- completionHandler: block
- ];
- })
- .detach();
+ let native_window = self.0.lock().native_window;
+ let dispatcher = self.0.lock().dispatcher.clone();
+ let _ = crate::spawn_on_main_local(dispatcher, async move {
+ let _: () = msg_send![
+ alert,
+ beginSheetModalForWindow: native_window
+ completionHandler: block
+ ];
+ });
done_rx
}
}
fn activate(&self) {
- let window = self.0.borrow().native_window;
- self.0
- .borrow()
- .executor
- .spawn(async move {
- unsafe {
- let _: () = msg_send![window, makeKeyAndOrderFront: nil];
- }
- })
- .detach();
+ let window = self.0.lock().native_window;
+ let dispatcher = self.0.lock().dispatcher.clone();
+ let _ = crate::spawn_on_main_local(dispatcher.clone(), async move {
+ unsafe {
+ let _: () = msg_send![window, makeKeyAndOrderFront: nil];
+ }
+ });
}
fn set_title(&mut self, title: &str) {
unsafe {
let app = NSApplication::sharedApplication(nil);
- let window = self.0.borrow().native_window;
+ let window = self.0.lock().native_window;
let title = ns_string(title);
let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
let _: () = msg_send![window, setTitle: title];
- self.0.borrow().move_traffic_light();
+ self.0.lock().move_traffic_light();
}
}
fn set_edited(&mut self, edited: bool) {
unsafe {
- let window = self.0.borrow().native_window;
+ let window = self.0.lock().native_window;
msg_send![window, setDocumentEdited: edited as BOOL]
}
// Changing the document edited state resets the traffic light position,
// so we have to move it again.
- self.0.borrow().move_traffic_light();
+ self.0.lock().move_traffic_light();
}
fn show_character_palette(&self) {
unsafe {
let app = NSApplication::sharedApplication(nil);
- let window = self.0.borrow().native_window;
+ let window = self.0.lock().native_window;
let _: () = msg_send![app, orderFrontCharacterPalette: window];
}
}
fn minimize(&self) {
- let window = self.0.borrow().native_window;
+ let window = self.0.lock().native_window;
unsafe {
window.miniaturize_(nil);
}
}
fn zoom(&self) {
- let this = self.0.borrow();
+ let this = self.0.lock();
let window = this.native_window;
- this.executor
- .spawn(async move {
- unsafe {
- window.zoom_(nil);
- }
- })
- .detach();
+ let dispatcher = this.dispatcher.clone();
+ let _ = crate::spawn_on_main_local(dispatcher, async move {
+ unsafe {
+ window.zoom_(nil);
+ }
+ });
}
fn toggle_full_screen(&self) {
- let this = self.0.borrow();
+ let this = self.0.lock();
let window = this.native_window;
- this.executor
- .spawn(async move {
- unsafe {
- window.toggleFullScreen_(nil);
- }
- })
- .detach();
+ let dispatcher = this.dispatcher.clone();
+ let _ = crate::spawn_on_main_local(dispatcher, async move {
+ unsafe {
+ window.toggleFullScreen_(nil);
+ }
+ });
}
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
- self.0.as_ref().borrow_mut().event_callback = Some(callback);
+ self.0.as_ref().lock().event_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);
+ self.0.as_ref().lock().activate_callback = Some(callback);
}
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
- self.0.as_ref().borrow_mut().resize_callback = Some(callback);
+ self.0.as_ref().lock().resize_callback = Some(callback);
}
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
- self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
+ self.0.as_ref().lock().fullscreen_callback = Some(callback);
}
fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
- self.0.as_ref().borrow_mut().moved_callback = Some(callback);
+ self.0.as_ref().lock().moved_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);
+ self.0.as_ref().lock().should_close_callback = Some(callback);
}
fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
- self.0.as_ref().borrow_mut().close_callback = Some(callback);
+ self.0.as_ref().lock().close_callback = Some(callback);
}
fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
- self.0.borrow_mut().appearance_changed_callback = Some(callback);
+ self.0.lock().appearance_changed_callback = Some(callback);
}
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
- let self_borrow = self.0.borrow();
+ let self_borrow = self.0.lock();
let self_handle = self_borrow.handle;
unsafe {
@@ -1133,14 +1125,15 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
},
) => {
window_state_borrow.synthetic_drag_counter += 1;
- window_state_borrow
- .executor
- .spawn(synthetic_drag(
+ let dispatcher = window_state_borrow.dispatcher.clone();
+ let _ = crate::spawn_on_main_local(
+ dispatcher,
+ synthetic_drag(
weak_window_state,
window_state_borrow.synthetic_drag_counter,
event.clone(),
- ))
- .detach();
+ ),
+ );
}
Event::MouseMoved(_)
@@ -1263,18 +1256,16 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
}
}
- let executor = window_state_borrow.executor.clone();
+ let dispatcher = window_state_borrow.dispatcher.clone();
drop(window_state_borrow);
- executor
- .spawn(async move {
- let mut window_state_borrow = window_state.as_ref().borrow_mut();
- if let Some(mut callback) = window_state_borrow.activate_callback.take() {
- drop(window_state_borrow);
- callback(is_active);
- window_state.borrow_mut().activate_callback = Some(callback);
- };
- })
- .detach();
+ let _ = crate::spawn_on_main_local(dispatcher, async move {
+ let mut window_state_borrow = window_state.as_ref().borrow_mut();
+ if let Some(mut callback) = window_state_borrow.activate_callback.take() {
+ drop(window_state_borrow);
+ callback(is_active);
+ window_state.borrow_mut().activate_callback = Some(callback);
+ };
+ });
}
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
@@ -9,7 +9,7 @@ impl TestPlatform {
}
impl Platform for TestPlatform {
- fn executor(&self) -> std::rc::Rc<crate::ForegroundExecutor> {
+ fn dispatcher(&self) -> std::sync::Arc<dyn crate::PlatformDispatcher> {
todo!()
}
@@ -215,133 +215,134 @@ impl Boundary {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{AppContext, FontWeight};
+ use crate::{App, AppContext, FontWeight};
#[test]
fn test_wrap_line() {
- let cx = AppContext::test();
+ App::test().run(|cx| {
+ let text_system = cx.text_system().clone();
+ let family = text_system
+ .load_font_family(&["Courier"], &Default::default())
+ .unwrap();
+ let font_id = text_system
+ .select_font(family, Default::default(), Default::default())
+ .unwrap();
- let text_system = cx.text_system().clone();
- let family = text_system
- .load_font_family(&["Courier"], &Default::default())
- .unwrap();
- let font_id = text_system
- .select_font(family, Default::default(), Default::default())
- .unwrap();
-
- let mut wrapper =
- LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
- assert_eq!(
- wrapper
- .wrap_line("aa bbb cccc ddddd eeee", px(72.))
- .collect::<Vec<_>>(),
- &[
- Boundary::new(7, 0),
- Boundary::new(12, 0),
- Boundary::new(18, 0)
- ],
- );
- assert_eq!(
- wrapper
- .wrap_line("aaa aaaaaaaaaaaaaaaaaa", px(72.0))
- .collect::<Vec<_>>(),
- &[
- Boundary::new(4, 0),
- Boundary::new(11, 0),
- Boundary::new(18, 0)
- ],
- );
- assert_eq!(
- wrapper
- .wrap_line(" aaaaaaa", px(72.))
- .collect::<Vec<_>>(),
- &[
- Boundary::new(7, 5),
- Boundary::new(9, 5),
- Boundary::new(11, 5),
- ]
- );
- assert_eq!(
- wrapper
- .wrap_line(" ", px(72.))
- .collect::<Vec<_>>(),
- &[
- Boundary::new(7, 0),
- Boundary::new(14, 0),
- Boundary::new(21, 0)
- ]
- );
- assert_eq!(
- wrapper
- .wrap_line(" aaaaaaaaaaaaaa", px(72.))
- .collect::<Vec<_>>(),
- &[
- Boundary::new(7, 0),
- Boundary::new(14, 3),
- Boundary::new(18, 3),
- Boundary::new(22, 3),
- ]
- );
+ let mut wrapper =
+ LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
+ assert_eq!(
+ wrapper
+ .wrap_line("aa bbb cccc ddddd eeee", px(72.))
+ .collect::<Vec<_>>(),
+ &[
+ Boundary::new(7, 0),
+ Boundary::new(12, 0),
+ Boundary::new(18, 0)
+ ],
+ );
+ assert_eq!(
+ wrapper
+ .wrap_line("aaa aaaaaaaaaaaaaaaaaa", px(72.0))
+ .collect::<Vec<_>>(),
+ &[
+ Boundary::new(4, 0),
+ Boundary::new(11, 0),
+ Boundary::new(18, 0)
+ ],
+ );
+ assert_eq!(
+ wrapper
+ .wrap_line(" aaaaaaa", px(72.))
+ .collect::<Vec<_>>(),
+ &[
+ Boundary::new(7, 5),
+ Boundary::new(9, 5),
+ Boundary::new(11, 5),
+ ]
+ );
+ assert_eq!(
+ wrapper
+ .wrap_line(" ", px(72.))
+ .collect::<Vec<_>>(),
+ &[
+ Boundary::new(7, 0),
+ Boundary::new(14, 0),
+ Boundary::new(21, 0)
+ ]
+ );
+ assert_eq!(
+ wrapper
+ .wrap_line(" aaaaaaaaaaaaaa", px(72.))
+ .collect::<Vec<_>>(),
+ &[
+ Boundary::new(7, 0),
+ Boundary::new(14, 3),
+ Boundary::new(18, 3),
+ Boundary::new(22, 3),
+ ]
+ );
+ });
}
// todo! repeat this test
#[test]
fn test_wrap_shaped_line() {
- let cx = AppContext::test();
- let text_system = cx.text_system().clone();
+ App::test().run(|cx| {
+ let text_system = cx.text_system().clone();
- let family = text_system
- .load_font_family(&["Helvetica"], &Default::default())
- .unwrap();
- let font_id = text_system
- .select_font(family, Default::default(), Default::default())
- .unwrap();
- let normal = RunStyle {
- font_id,
- color: Default::default(),
- underline: Default::default(),
- };
- let bold = RunStyle {
- font_id: text_system
- .select_font(family, FontWeight::BOLD, Default::default())
- .unwrap(),
- color: Default::default(),
- underline: Default::default(),
- };
+ let family = text_system
+ .load_font_family(&["Helvetica"], &Default::default())
+ .unwrap();
+ let font_id = text_system
+ .select_font(family, Default::default(), Default::default())
+ .unwrap();
+ let normal = RunStyle {
+ font_id,
+ color: Default::default(),
+ underline: Default::default(),
+ };
+ let bold = RunStyle {
+ font_id: text_system
+ .select_font(family, FontWeight::BOLD, Default::default())
+ .unwrap(),
+ color: Default::default(),
+ underline: Default::default(),
+ };
- let text = "aa bbb cccc ddddd eeee";
- let line = text_system.layout_str(
- text,
- px(16.),
- &[
- (4, normal.clone()),
- (5, bold.clone()),
- (6, normal.clone()),
- (1, bold),
- (7, normal),
- ],
- );
+ let text = "aa bbb cccc ddddd eeee";
+ let line = text_system.layout_str(
+ text,
+ px(16.),
+ &[
+ (4, normal.clone()),
+ (5, bold.clone()),
+ (6, normal.clone()),
+ (1, bold),
+ (7, normal),
+ ],
+ );
- let mut wrapper =
- LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
- assert_eq!(
- wrapper
- .wrap_shaped_line(text, &line, px(72.))
- .collect::<Vec<_>>(),
- &[
- ShapedBoundary {
- run_ix: 1,
- glyph_ix: 3
- },
- ShapedBoundary {
- run_ix: 2,
- glyph_ix: 3
- },
- ShapedBoundary {
- run_ix: 4,
- glyph_ix: 2
- }
- ],
- );
+ let mut wrapper =
+ LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
+ assert_eq!(
+ wrapper
+ .wrap_shaped_line(text, &line, px(72.))
+ .collect::<Vec<_>>(),
+ &[
+ ShapedBoundary {
+ run_ix: 1,
+ glyph_ix: 3
+ },
+ ShapedBoundary {
+ run_ix: 2,
+ glyph_ix: 3
+ },
+ ShapedBoundary {
+ run_ix: 4,
+ glyph_ix: 2
+ }
+ ],
+ );
+ });
}
}
@@ -1,6 +1,4 @@
-use crate::{
- AvailableSpace, Bg, PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement,
-};
+use crate::{AvailableSpace, PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement};
use super::{
px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference,
@@ -39,22 +37,22 @@ impl Window {
}
#[derive(Deref, DerefMut)]
-pub struct WindowContext<'a, 'b> {
+pub struct WindowContext<'a, 'b, Thread = ()> {
#[deref]
#[deref_mut]
- app: Reference<'a, AppContext>,
+ app: Reference<'a, AppContext<Thread>>,
window: Reference<'b, Window>,
}
-impl<'a, 'w> WindowContext<'a, 'w> {
- pub(crate) fn mutable(app: &'a mut AppContext<Bg>, window: &'w mut Window) -> Self {
+impl<'a, 'w, Thread> WindowContext<'a, 'w, Thread> {
+ pub(crate) fn mutable(app: &'a mut AppContext<Thread>, window: &'w mut Window) -> Self {
Self {
app: Reference::Mutable(app),
window: Reference::Mutable(window),
}
}
- pub(crate) fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
+ pub(crate) fn immutable(app: &'a AppContext<Thread>, window: &'w Window) -> Self {
Self {
app: Reference::Immutable(app),
window: Reference::Immutable(window),
@@ -123,7 +121,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
fn update_window<R>(
&mut self,
window_id: WindowId,
- update: impl FnOnce(&mut WindowContext) -> R,
+ update: impl FnOnce(&mut WindowContext<Thread>) -> R,
) -> Result<R> {
if window_id == self.window.id {
Ok(update(self))
@@ -21,7 +21,7 @@ mod workspace;
fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
- gpui3::App::new().run(|cx| {
+ gpui3::App::production().run(|cx| {
let window: gpui3::WindowHandle<()> = cx.open_window(Default::default(), |cx| todo!());
});