Introduce more GPUI2 APIs needed for transitioning the workspace

This commit is contained in:
Antonio Scandurra 2023-11-02 10:53:28 +01:00
parent 8793300444
commit 32db64a049
7 changed files with 80 additions and 100 deletions

View file

@ -16,13 +16,13 @@ use crate::{
current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle, current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId,
Pixels, Platform, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString,
TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem,
WindowId, View, Window, WindowContext, WindowHandle, WindowId,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
use futures::{future::LocalBoxFuture, Future}; use futures::{channel::oneshot, future::LocalBoxFuture, Future};
use parking_lot::Mutex; use parking_lot::Mutex;
use slotmap::SlotMap; use slotmap::SlotMap;
use std::{ use std::{
@ -31,7 +31,7 @@ use std::{
marker::PhantomData, marker::PhantomData,
mem, mem,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
path::PathBuf, path::{Path, PathBuf},
rc::{Rc, Weak}, rc::{Rc, Weak},
sync::{atomic::Ordering::SeqCst, Arc}, sync::{atomic::Ordering::SeqCst, Arc},
time::Duration, time::Duration,
@ -262,38 +262,13 @@ impl AppContext {
.collect() .collect()
} }
pub(crate) fn update_window<R>(
&mut self,
handle: AnyWindowHandle,
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
) -> Result<R> {
self.update(|cx| {
let mut window = cx
.windows
.get_mut(handle.id)
.ok_or_else(|| anyhow!("window not found"))?
.take()
.unwrap();
let root_view = window.root_view.clone().unwrap();
let result = update(root_view, &mut WindowContext::new(cx, &mut window));
cx.windows
.get_mut(handle.id)
.ok_or_else(|| anyhow!("window not found"))?
.replace(window);
Ok(result)
})
}
/// Opens a new window with the given option and the root view returned by the given function. /// Opens a new window with the given option and the root view returned by the given function.
/// The function is invoked with a `WindowContext`, which can be used to interact with window-specific /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
/// functionality. /// functionality.
pub fn open_window<V: Render>( pub fn open_window<V: Render>(
&mut self, &mut self,
options: crate::WindowOptions, options: crate::WindowOptions,
build_root_view: impl FnOnce(&mut WindowContext) -> View<V> + 'static, build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
) -> WindowHandle<V> { ) -> WindowHandle<V> {
self.update(|cx| { self.update(|cx| {
let id = cx.windows.insert(None); let id = cx.windows.insert(None);
@ -306,47 +281,63 @@ impl AppContext {
}) })
} }
pub(crate) fn platform(&self) -> &Rc<dyn Platform> {
&self.platform
}
/// Instructs the platform to activate the application by bringing it to the foreground. /// Instructs the platform to activate the application by bringing it to the foreground.
pub fn activate(&self, ignoring_other_apps: bool) { pub fn activate(&self, ignoring_other_apps: bool) {
self.platform().activate(ignoring_other_apps); self.platform.activate(ignoring_other_apps);
}
/// Returns the list of currently active displays.
pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
self.platform.displays()
} }
/// Writes data to the platform clipboard. /// Writes data to the platform clipboard.
pub fn write_to_clipboard(&self, item: ClipboardItem) { pub fn write_to_clipboard(&self, item: ClipboardItem) {
self.platform().write_to_clipboard(item) self.platform.write_to_clipboard(item)
} }
/// Reads data from the platform clipboard. /// Reads data from the platform clipboard.
pub fn read_from_clipboard(&self) -> Option<ClipboardItem> { pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
self.platform().read_from_clipboard() self.platform.read_from_clipboard()
} }
/// Writes credentials to the platform keychain. /// Writes credentials to the platform keychain.
pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> { pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
self.platform().write_credentials(url, username, password) self.platform.write_credentials(url, username, password)
} }
/// Reads credentials from the platform keychain. /// Reads credentials from the platform keychain.
pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> { pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
self.platform().read_credentials(url) self.platform.read_credentials(url)
} }
/// Deletes credentials from the platform keychain. /// Deletes credentials from the platform keychain.
pub fn delete_credentials(&self, url: &str) -> Result<()> { pub fn delete_credentials(&self, url: &str) -> Result<()> {
self.platform().delete_credentials(url) self.platform.delete_credentials(url)
} }
/// Directs the platform's default browser to open the given URL. /// Directs the platform's default browser to open the given URL.
pub fn open_url(&self, url: &str) { pub fn open_url(&self, url: &str) {
self.platform().open_url(url); self.platform.open_url(url);
} }
pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> { pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
self.platform().path_for_auxiliary_executable(name) self.platform.path_for_auxiliary_executable(name)
}
pub fn prompt_for_paths(
&self,
options: PathPromptOptions,
) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
self.platform.prompt_for_paths(options)
}
pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
self.platform.prompt_for_new_path(directory)
}
pub fn reveal_path(&self, path: &Path) {
self.platform.reveal_path(path)
} }
pub(crate) fn push_effect(&mut self, effect: Effect) { pub(crate) fn push_effect(&mut self, effect: Effect) {

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model, AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model,
ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext, ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext,
WindowHandle,
}; };
use anyhow::{anyhow, Context as _}; use anyhow::{anyhow, Context as _};
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -82,17 +83,20 @@ impl AsyncAppContext {
Ok(f(&mut *lock)) Ok(f(&mut *lock))
} }
pub fn update_window<R>( pub fn open_window<V>(
&self, &self,
handle: AnyWindowHandle, options: crate::WindowOptions,
update: impl FnOnce(AnyView, &mut WindowContext) -> R, build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
) -> Result<R> { ) -> Result<WindowHandle<V>>
where
V: Render,
{
let app = self let app = self
.app .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.borrow_mut(); let mut lock = app.borrow_mut();
app_context.update_window(handle, update) Ok(lock.open_window(options, build_root_view))
} }
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R> pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>

View file

@ -87,15 +87,6 @@ impl TestAppContext {
cx.update(f) cx.update(f)
} }
pub fn update_window<R>(
&self,
handle: AnyWindowHandle,
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
) -> R {
let mut app = self.app.borrow_mut();
app.update_window(handle, update).unwrap()
}
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R> pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
where where
Fut: Future<Output = R> + 'static, Fut: Future<Output = R> + 'static,

View file

@ -138,12 +138,7 @@ pub(crate) trait PlatformWindow {
fn mouse_position(&self) -> Point<Pixels>; fn mouse_position(&self) -> Point<Pixels>;
fn as_any_mut(&mut self) -> &mut dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>); fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
fn prompt( fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
&self,
level: WindowPromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize>;
fn activate(&self); fn activate(&self);
fn set_title(&mut self, title: &str); fn set_title(&mut self, title: &str);
fn set_edited(&mut self, edited: bool); fn set_edited(&mut self, edited: bool);
@ -454,14 +449,6 @@ impl Default for WindowAppearance {
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub enum WindowPromptLevel {
#[default]
Info,
Warning,
Critical,
}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct PathPromptOptions { pub struct PathPromptOptions {
pub files: bool, pub files: bool,

View file

@ -4,7 +4,7 @@ use crate::{
FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke, FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke,
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene,
Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, PromptLevel,
}; };
use block::ConcreteBlock; use block::ConcreteBlock;
use cocoa::{ use cocoa::{
@ -742,12 +742,7 @@ impl PlatformWindow for MacWindow {
self.0.as_ref().lock().input_handler = Some(input_handler); self.0.as_ref().lock().input_handler = Some(input_handler);
} }
fn prompt( fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> {
&self,
level: WindowPromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
// macOs applies overrides to modal window buttons after they are added. // macOs applies overrides to modal window buttons after they are added.
// Two most important for this logic are: // Two most important for this logic are:
// * Buttons with "Cancel" title will be displayed as the last buttons in the modal // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
@ -777,9 +772,9 @@ impl PlatformWindow for MacWindow {
let alert: id = msg_send![class!(NSAlert), alloc]; let alert: id = msg_send![class!(NSAlert), alloc];
let alert: id = msg_send![alert, init]; let alert: id = msg_send![alert, init];
let alert_style = match level { let alert_style = match level {
WindowPromptLevel::Info => 1, PromptLevel::Info => 1,
WindowPromptLevel::Warning => 0, PromptLevel::Warning => 0,
WindowPromptLevel::Critical => 2, PromptLevel::Critical => 2,
}; };
let _: () = msg_send![alert, setAlertStyle: alert_style]; let _: () = msg_send![alert, setAlertStyle: alert_style];
let _: () = msg_send![alert, setMessageText: ns_string(msg)]; let _: () = msg_send![alert, setMessageText: ns_string(msg)];

View file

@ -98,6 +98,10 @@ pub struct WeakView<V> {
} }
impl<V: 'static> WeakView<V> { impl<V: 'static> WeakView<V> {
pub fn entity_id(&self) -> EntityId {
self.model.entity_id
}
pub fn upgrade(&self) -> Option<View<V>> { pub fn upgrade(&self) -> Option<View<V>> {
Entity::upgrade_from(self) Entity::upgrade_from(self)
} }

View file

@ -4,14 +4,15 @@ use crate::{
Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId,
Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite,
Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task,
UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::HashMap; use collections::HashMap;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use futures::channel::oneshot;
use parking_lot::RwLock; use parking_lot::RwLock;
use slotmap::SlotMap; use slotmap::SlotMap;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -195,7 +196,7 @@ impl Window {
options: WindowOptions, options: WindowOptions,
cx: &mut AppContext, cx: &mut AppContext,
) -> Self { ) -> Self {
let platform_window = cx.platform().open_window(handle, options); let platform_window = cx.platform.open_window(handle, options);
let display_id = platform_window.display().id(); let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas(); let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position(); let mouse_position = platform_window.mouse_position();
@ -419,7 +420,7 @@ impl<'a> WindowContext<'a> {
} else { } else {
let mut async_cx = self.to_async(); let mut async_cx = self.to_async();
self.next_frame_callbacks.insert(display_id, vec![f]); self.next_frame_callbacks.insert(display_id, vec![f]);
self.platform().set_display_link_output_callback( self.platform.set_display_link_output_callback(
display_id, display_id,
Box::new(move |_current_time, _output_time| { Box::new(move |_current_time, _output_time| {
let _ = async_cx.update(|_, cx| { let _ = async_cx.update(|_, cx| {
@ -434,32 +435,26 @@ impl<'a> WindowContext<'a> {
} }
if cx.next_frame_callbacks.get(&display_id).unwrap().is_empty() { if cx.next_frame_callbacks.get(&display_id).unwrap().is_empty() {
cx.platform().stop_display_link(display_id); cx.platform.stop_display_link(display_id);
} }
}); });
}), }),
); );
} }
self.platform().start_display_link(display_id); self.platform.start_display_link(display_id);
} }
/// Spawn the future returned by the given closure on the application thread pool. /// Spawn the future returned by the given closure on the application thread pool.
/// The closure is provided a handle to the current window and an `AsyncWindowContext` for /// The closure is provided a handle to the current window and an `AsyncWindowContext` for
/// use within your future. /// use within your future.
pub fn spawn<Fut, R>( pub fn spawn<Fut, R>(&mut self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
&mut self,
f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut,
) -> Task<R>
where where
R: 'static, R: 'static,
Fut: Future<Output = R> + 'static, Fut: Future<Output = R> + 'static,
{ {
let window = self.window.handle; self.app
self.app.spawn(move |app| { .spawn(|app| f(AsyncWindowContext::new(app, self.window.handle)))
let cx = AsyncWindowContext::new(app, window);
f(window, cx)
})
} }
/// Update the global of the given type. The given closure is given simultaneous mutable /// Update the global of the given type. The given closure is given simultaneous mutable
@ -1153,6 +1148,19 @@ impl<'a> WindowContext<'a> {
) )
} }
pub fn activate_window(&self) {
self.window.platform_window.activate();
}
pub fn prompt(
&self,
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
self.window.platform_window.prompt(level, msg, answers)
}
fn dispatch_action( fn dispatch_action(
&mut self, &mut self,
action: Box<dyn Action>, action: Box<dyn Action>,
@ -1809,7 +1817,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
Fut: Future<Output = R> + 'static, Fut: Future<Output = R> + 'static,
{ {
let view = self.view().downgrade(); let view = self.view().downgrade();
self.window_cx.spawn(move |_, cx| f(view, cx)) self.window_cx.spawn(|cx| f(view, cx))
} }
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R