WIP
This commit is contained in:
@@ -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
@@ -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> {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user