This commit is contained in:
Antonio Scandurra
2023-10-21 17:52:47 +02:00
parent e4fe9538d7
commit b7d30fca2b
30 changed files with 3644 additions and 29 deletions
+31 -7
View File
@@ -1,18 +1,42 @@
use crate::SharedString;
use anyhow::{anyhow, Result};
use anyhow::{anyhow, Context, Result};
use collections::{HashMap, HashSet};
use std::any::Any;
use serde::Deserialize;
use std::any::{type_name, Any};
pub trait Action: Any + Send + Sync {
fn qualified_name() -> SharedString
where
Self: Sized;
fn build(value: Option<serde_json::Value>) -> Result<Box<dyn Action>>
where
Self: Sized;
fn partial_eq(&self, action: &dyn Action) -> bool;
fn boxed_clone(&self) -> Box<dyn Action>;
fn as_any(&self) -> &dyn Any;
}
impl<T> Action for T
impl<A> Action for A
where
T: Any + PartialEq + Clone + Send + Sync,
A: for<'a> Deserialize<'a> + Any + PartialEq + Clone + Default + Send + Sync,
{
fn qualified_name() -> SharedString {
type_name::<A>().into()
}
fn build(params: Option<serde_json::Value>) -> Result<Box<dyn Action>>
where
Self: Sized,
{
let action = if let Some(params) = params {
serde_json::from_value(params).context("failed to deserialize action")?
} else {
Self::default()
};
Ok(Box::new(action))
}
fn partial_eq(&self, action: &dyn Action) -> bool {
action
.as_any()
@@ -130,15 +154,15 @@ impl DispatchContextPredicate {
return false;
};
match self {
Self::Identifier(name) => context.set.contains(&name),
Self::Identifier(name) => context.set.contains(name),
Self::Equal(left, right) => context
.map
.get(&left)
.get(left)
.map(|value| value == right)
.unwrap_or(false),
Self::NotEqual(left, right) => context
.map
.get(&left)
.get(left)
.map(|value| value != right)
.unwrap_or(true),
Self::Not(pred) => !pred.eval(contexts),
+79 -6
View File
@@ -9,10 +9,10 @@ use refineable::Refineable;
use smallvec::SmallVec;
use crate::{
current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor,
current_platform, image_cache::ImageCache, Action, AssetSource, Context, DisplayId, Executor,
FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly,
Platform, SubscriberSet, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View,
Window, WindowContext, WindowHandle, WindowId,
Platform, SharedString, SubscriberSet, SvgRenderer, Task, TextStyle, TextStyleRefinement,
TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@@ -55,10 +55,10 @@ impl App {
Mutex::new(AppContext {
this: this.clone(),
text_system: Arc::new(TextSystem::new(platform.text_system())),
pending_updates: 0,
flushing_effects: false,
next_frame_callbacks: Default::default(),
platform: MainThreadOnly::new(platform, executor.clone()),
flushing_effects: false,
pending_updates: 0,
next_frame_callbacks: Default::default(),
executor,
svg_renderer: SvgRenderer::new(asset_source),
image_cache: ImageCache::new(http_client),
@@ -68,6 +68,7 @@ impl App {
entities,
windows: SlotMap::with_key(),
keymap: Arc::new(RwLock::new(Keymap::default())),
action_builders: HashMap::default(),
pending_notifications: Default::default(),
pending_effects: Default::default(),
observers: SubscriberSet::new(),
@@ -90,12 +91,17 @@ impl App {
on_finish_launching(cx);
}));
}
pub fn executor(&self) -> Executor {
self.0.lock().executor.clone()
}
}
type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
type EventHandler = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
type ReleaseHandler = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
pub struct AppContext {
this: Weak<Mutex<AppContext>>,
@@ -113,6 +119,7 @@ pub struct AppContext {
pub(crate) entities: EntityMap,
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) keymap: Arc<RwLock<Keymap>>,
action_builders: HashMap<SharedString, ActionBuilder>,
pub(crate) pending_notifications: HashSet<EntityId>,
pending_effects: VecDeque<Effect>,
pub(crate) observers: SubscriberSet<EntityId, Handler>,
@@ -134,6 +141,20 @@ impl AppContext {
result
}
pub(crate) fn read_window<R>(
&mut self,
id: WindowId,
read: impl FnOnce(&WindowContext) -> R,
) -> Result<R> {
let window = self
.windows
.get(id)
.ok_or_else(|| anyhow!("window not found"))?
.as_ref()
.unwrap();
Ok(read(&WindowContext::immutable(self, &window)))
}
pub(crate) fn update_window<R>(
&mut self,
id: WindowId,
@@ -385,6 +406,24 @@ impl AppContext {
.unwrap()
}
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where
G: 'static + Send + Sync,
{
let mut global = self
.global_stacks_by_type
.get_mut(&TypeId::of::<G>())
.and_then(|stack| stack.pop())
.ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
.unwrap();
let result = f(global.downcast_mut().unwrap(), self);
self.global_stacks_by_type
.get_mut(&TypeId::of::<G>())
.unwrap()
.push(global);
result
}
pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G {
let stack = self
.global_stacks_by_type
@@ -396,6 +435,19 @@ impl AppContext {
stack.last_mut().unwrap().downcast_mut::<G>().unwrap()
}
pub fn set_global<T: Send + Sync + 'static>(&mut self, global: T) {
let global = Box::new(global);
let stack = self
.global_stacks_by_type
.entry(TypeId::of::<T>())
.or_default();
if let Some(last) = stack.last_mut() {
*last = global;
} else {
stack.push(global)
}
}
pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, state: T) {
self.global_stacks_by_type
.entry(TypeId::of::<T>())
@@ -422,9 +474,26 @@ impl AppContext {
self.keymap.write().add_bindings(bindings);
self.push_effect(Effect::Refresh);
}
pub fn register_action_type<A: Action>(&mut self) {
self.action_builders.insert(A::qualified_name(), A::build);
}
pub fn build_action(
&mut self,
name: &str,
params: Option<serde_json::Value>,
) -> Result<Box<dyn Action>> {
let build = self
.action_builders
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build)(params)
}
}
impl Context for AppContext {
type BorrowedContext<'a, 'w> = Self;
type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
type Result<T> = T;
@@ -451,6 +520,10 @@ impl Context for AppContext {
result
})
}
fn read_global<G: 'static + Send + Sync, R>(&self, read: impl FnOnce(&G, &Self) -> R) -> R {
read(self.global(), self)
}
}
impl MainThread<AppContext> {
+40 -6
View File
@@ -9,18 +9,19 @@ use std::sync::Weak;
pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>);
impl Context for AsyncAppContext {
type BorrowedContext<'a, 'w> = AppContext;
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
type Result<T> = Result<T>;
fn entity<T: Send + Sync + 'static>(
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Result<Handle<T>> {
) -> Self::Result<Handle<T>> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock(); // Does not compile without this variable.
let mut lock = app.lock();
Ok(lock.entity(build_entity))
}
@@ -28,17 +29,42 @@ impl Context for AsyncAppContext {
&mut self,
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> Self::Result<R> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock();
Ok(lock.update_entity(handle, update))
}
fn read_global<G: 'static + Send + Sync, R>(
&self,
read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
) -> Self::Result<R> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock();
Ok(lock.read_global(read))
}
}
impl AsyncAppContext {
pub fn read_window<R>(
&self,
handle: AnyWindowHandle,
update: impl FnOnce(&WindowContext) -> R,
) -> Result<R> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock(); // Does not compile without this variable.
Ok(lock.update_entity(handle, update))
let mut app_context = app.lock();
app_context.read_window(handle.id, update)
}
}
impl AsyncAppContext {
pub fn update_window<R>(
&self,
handle: AnyWindowHandle,
@@ -76,6 +102,7 @@ impl AsyncWindowContext {
}
impl Context for AsyncWindowContext {
type BorrowedContext<'a, 'w> = WindowContext<'a, 'w>;
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
type Result<T> = Result<T>;
@@ -95,4 +122,11 @@ impl Context for AsyncWindowContext {
self.app
.update_window(self.window, |cx| cx.update_entity(handle, update))
}
fn read_global<G: 'static + Send + Sync, R>(
&self,
read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
) -> Result<R> {
self.app.read_window(self.window, |cx| cx.read_global(read))
}
}
+8
View File
@@ -132,6 +132,7 @@ impl<'a, T: EventEmitter + Send + Sync + 'static> ModelContext<'a, T> {
}
impl<'a, T: 'static> Context for ModelContext<'a, T> {
type BorrowedContext<'b, 'c> = ModelContext<'b, T>;
type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
type Result<U> = U;
@@ -149,4 +150,11 @@ impl<'a, T: 'static> Context for ModelContext<'a, T> {
) -> R {
self.app.update_entity(handle, update)
}
fn read_global<G: 'static + Send + Sync, R>(
&self,
read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
) -> R {
read(self.app.global(), self)
}
}
+13 -1
View File
@@ -1,10 +1,12 @@
use crate::PlatformDispatcher;
use crate::{AppContext, PlatformDispatcher};
use smol::prelude::*;
use std::{
fmt::Debug,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use util::TryFutureExt;
#[derive(Clone)]
pub struct Executor {
@@ -30,6 +32,16 @@ impl<T> Task<T> {
}
}
impl<E, T> Task<Result<T, E>>
where
T: 'static + Send,
E: 'static + Send + Debug,
{
pub fn detach_and_log_err(self, cx: &mut AppContext) {
cx.executor().spawn(self.log_err()).detach();
}
}
impl<T> Future for Task<T> {
type Output = T;
+35 -2
View File
@@ -56,6 +56,7 @@ pub use window::*;
use derive_more::{Deref, DerefMut};
use std::{
any::{Any, TypeId},
borrow::Borrow,
mem,
ops::{Deref, DerefMut},
sync::Arc,
@@ -65,6 +66,7 @@ use taffy::TaffyLayoutEngine;
type AnyBox = Box<dyn Any + Send + Sync>;
pub trait Context {
type BorrowedContext<'a, 'w>: Context;
type EntityContext<'a, 'w, T: 'static + Send + Sync>;
type Result<T>;
@@ -78,6 +80,11 @@ pub trait Context {
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> Self::Result<R>;
fn read_global<G: 'static + Send + Sync, R>(
&self,
read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
) -> Self::Result<R>;
}
pub enum GlobalKey {
@@ -104,6 +111,7 @@ impl<T> DerefMut for MainThread<T> {
}
impl<C: Context> Context for MainThread<C> {
type BorrowedContext<'a, 'w> = MainThread<C::BorrowedContext<'a, 'w>>;
type EntityContext<'a, 'w, T: 'static + Send + Sync> = MainThread<C::EntityContext<'a, 'w, T>>;
type Result<T> = C::Result<T>;
@@ -137,6 +145,21 @@ impl<C: Context> Context for MainThread<C> {
update(entity, cx)
})
}
fn read_global<G: 'static + Send + Sync, R>(
&self,
read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
) -> Self::Result<R> {
self.0.read_global(|global, cx| {
let cx = unsafe {
mem::transmute::<
&C::BorrowedContext<'_, '_>,
&MainThread<C::BorrowedContext<'_, '_>>,
>(cx)
};
read(global, cx)
})
}
}
pub trait BorrowAppContext {
@@ -152,15 +175,19 @@ pub trait BorrowAppContext {
result
}
fn with_global<T: Send + Sync + 'static, F, R>(&mut self, state: T, f: F) -> R
fn with_global<T: Send + Sync + 'static, F, R>(&mut self, global: T, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
self.app_mut().push_global(state);
self.app_mut().push_global(global);
let result = f(self);
self.app_mut().pop_global::<T>();
result
}
fn set_global<T: Send + Sync + 'static>(&mut self, global: T) {
self.app_mut().set_global(global)
}
}
pub trait EventEmitter {
@@ -198,6 +225,12 @@ impl AsRef<str> for SharedString {
}
}
impl Borrow<str> for SharedString {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl std::fmt::Debug for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
+25 -2
View File
@@ -288,6 +288,13 @@ pub struct WindowContext<'a, 'w> {
}
impl<'a, 'w> WindowContext<'a, 'w> {
pub(crate) fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
Self {
app: Reference::Immutable(app),
window: Reference::Immutable(window),
}
}
pub(crate) fn mutable(app: &'a mut AppContext, window: &'w mut Window) -> Self {
Self {
app: Reference::Mutable(app),
@@ -1049,6 +1056,7 @@ impl<'a, 'w> MainThread<WindowContext<'a, 'w>> {
}
impl Context for WindowContext<'_, '_> {
type BorrowedContext<'a, 'w> = WindowContext<'a, 'w>;
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
type Result<T> = T;
@@ -1078,6 +1086,10 @@ impl Context for WindowContext<'_, '_> {
self.entities.end_lease(entity);
result
}
fn read_global<G: 'static + Send + Sync, R>(&self, read: impl FnOnce(&G, &Self) -> R) -> R {
read(self.app.global(), self)
}
}
impl<'a, 'w> std::ops::Deref for WindowContext<'a, 'w> {
@@ -1520,7 +1532,11 @@ impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {
}
}
impl<'a, 'w, S> Context for ViewContext<'a, 'w, S> {
impl<'a, 'w, V> Context for ViewContext<'a, 'w, V>
where
V: 'static + Send + Sync,
{
type BorrowedContext<'b, 'c> = ViewContext<'b, 'c, V>;
type EntityContext<'b, 'c, U: 'static + Send + Sync> = ViewContext<'b, 'c, U>;
type Result<U> = U;
@@ -1531,13 +1547,20 @@ impl<'a, 'w, S> Context for ViewContext<'a, 'w, S> {
self.window_cx.entity(build_entity)
}
fn update_entity<U: Send + Sync + 'static, R>(
fn update_entity<U: 'static + Send + Sync, R>(
&mut self,
handle: &Handle<U>,
update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
) -> R {
self.window_cx.update_entity(handle, update)
}
fn read_global<G: 'static + Send + Sync, R>(
&self,
read: impl FnOnce(&G, &Self::BorrowedContext<'_, '_>) -> R,
) -> R {
read(self.global(), self)
}
}
impl<'a, 'w, S: 'static> std::ops::Deref for ViewContext<'a, 'w, S> {