gpui: Add Global marker trait (#7095)
This should prevent a class of bugs where one queries the wrong type of global, which results in oddities at runtime. Release Notes: - N/A --------- Co-authored-by: Marshall <marshall@zed.dev> Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
co-authored by
Marshall
Marshall Bowers
parent
7bfa584eb6
commit
e6ebe7974d
+16
-16
@@ -17,7 +17,7 @@ use time::UtcOffset;
|
||||
use crate::{
|
||||
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
|
||||
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
||||
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, KeyBinding, Keymap,
|
||||
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
|
||||
Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render,
|
||||
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
|
||||
TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
|
||||
@@ -823,13 +823,13 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Check whether a global of the given type has been assigned.
|
||||
pub fn has_global<G: 'static>(&self) -> bool {
|
||||
pub fn has_global<G: Global>(&self) -> bool {
|
||||
self.globals_by_type.contains_key(&TypeId::of::<G>())
|
||||
}
|
||||
|
||||
/// Access the global of the given type. Panics if a global for that type has not been assigned.
|
||||
#[track_caller]
|
||||
pub fn global<G: 'static>(&self) -> &G {
|
||||
pub fn global<G: Global>(&self) -> &G {
|
||||
self.globals_by_type
|
||||
.get(&TypeId::of::<G>())
|
||||
.map(|any_state| any_state.downcast_ref::<G>().unwrap())
|
||||
@@ -838,7 +838,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Access the global of the given type if a value has been assigned.
|
||||
pub fn try_global<G: 'static>(&self) -> Option<&G> {
|
||||
pub fn try_global<G: Global>(&self) -> Option<&G> {
|
||||
self.globals_by_type
|
||||
.get(&TypeId::of::<G>())
|
||||
.map(|any_state| any_state.downcast_ref::<G>().unwrap())
|
||||
@@ -846,7 +846,7 @@ impl AppContext {
|
||||
|
||||
/// Access the global of the given type mutably. Panics if a global for that type has not been assigned.
|
||||
#[track_caller]
|
||||
pub fn global_mut<G: 'static>(&mut self) -> &mut G {
|
||||
pub fn global_mut<G: Global>(&mut self) -> &mut G {
|
||||
let global_type = TypeId::of::<G>();
|
||||
self.push_effect(Effect::NotifyGlobalObservers { global_type });
|
||||
self.globals_by_type
|
||||
@@ -858,7 +858,7 @@ impl AppContext {
|
||||
|
||||
/// Access the global of the given type mutably. A default value is assigned if a global of this type has not
|
||||
/// yet been assigned.
|
||||
pub fn default_global<G: 'static + Default>(&mut self) -> &mut G {
|
||||
pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
|
||||
let global_type = TypeId::of::<G>();
|
||||
self.push_effect(Effect::NotifyGlobalObservers { global_type });
|
||||
self.globals_by_type
|
||||
@@ -869,7 +869,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Sets the value of the global of the given type.
|
||||
pub fn set_global<G: Any>(&mut self, global: G) {
|
||||
pub fn set_global<G: Global>(&mut self, global: G) {
|
||||
let global_type = TypeId::of::<G>();
|
||||
self.push_effect(Effect::NotifyGlobalObservers { global_type });
|
||||
self.globals_by_type.insert(global_type, Box::new(global));
|
||||
@@ -882,7 +882,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Remove the global of the given type from the app context. Does not notify global observers.
|
||||
pub fn remove_global<G: Any>(&mut self) -> G {
|
||||
pub fn remove_global<G: Global>(&mut self) -> G {
|
||||
let global_type = TypeId::of::<G>();
|
||||
self.push_effect(Effect::NotifyGlobalObservers { global_type });
|
||||
*self
|
||||
@@ -895,7 +895,7 @@ impl AppContext {
|
||||
|
||||
/// Updates the global of the given type with a closure. Unlike `global_mut`, this method provides
|
||||
/// your closure with mutable access to the `AppContext` and the global simultaneously.
|
||||
pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
|
||||
pub fn update_global<G: Global, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
|
||||
self.update(|cx| {
|
||||
let mut global = cx.lease_global::<G>();
|
||||
let result = f(&mut global, cx);
|
||||
@@ -905,7 +905,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when a global of the given type is updated.
|
||||
pub fn observe_global<G: 'static>(
|
||||
pub fn observe_global<G: Global>(
|
||||
&mut self,
|
||||
mut f: impl FnMut(&mut Self) + 'static,
|
||||
) -> Subscription {
|
||||
@@ -921,7 +921,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Move the global of the given type to the stack.
|
||||
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
|
||||
pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
|
||||
GlobalLease::new(
|
||||
self.globals_by_type
|
||||
.remove(&TypeId::of::<G>())
|
||||
@@ -931,7 +931,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Restore the global of the given type after it is moved to the stack.
|
||||
pub(crate) fn end_global_lease<G: 'static>(&mut self, lease: GlobalLease<G>) {
|
||||
pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
|
||||
let global_type = TypeId::of::<G>();
|
||||
self.push_effect(Effect::NotifyGlobalObservers { global_type });
|
||||
self.globals_by_type.insert(global_type, lease.global);
|
||||
@@ -1293,12 +1293,12 @@ pub(crate) enum Effect {
|
||||
}
|
||||
|
||||
/// Wraps a global variable value during `update_global` while the value has been moved to the stack.
|
||||
pub(crate) struct GlobalLease<G: 'static> {
|
||||
pub(crate) struct GlobalLease<G: Global> {
|
||||
global: Box<dyn Any>,
|
||||
global_type: PhantomData<G>,
|
||||
}
|
||||
|
||||
impl<G: 'static> GlobalLease<G> {
|
||||
impl<G: Global> GlobalLease<G> {
|
||||
fn new(global: Box<dyn Any>) -> Self {
|
||||
GlobalLease {
|
||||
global,
|
||||
@@ -1307,7 +1307,7 @@ impl<G: 'static> GlobalLease<G> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: 'static> Deref for GlobalLease<G> {
|
||||
impl<G: Global> Deref for GlobalLease<G> {
|
||||
type Target = G;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -1315,7 +1315,7 @@ impl<G: 'static> Deref for GlobalLease<G> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: 'static> DerefMut for GlobalLease<G> {
|
||||
impl<G: Global> DerefMut for GlobalLease<G> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.global.downcast_mut().unwrap()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent,
|
||||
FocusableView, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View,
|
||||
FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, Result, Task, View,
|
||||
ViewContext, VisualContext, WindowContext, WindowHandle,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
@@ -144,7 +144,7 @@ impl AsyncAppContext {
|
||||
|
||||
/// Determine whether global state of the specified type has been assigned.
|
||||
/// Returns an error if the `AppContext` has been dropped.
|
||||
pub fn has_global<G: 'static>(&self) -> Result<bool> {
|
||||
pub fn has_global<G: Global>(&self) -> Result<bool> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
@@ -157,7 +157,7 @@ impl AsyncAppContext {
|
||||
///
|
||||
/// Panics if no global state of the specified type has been assigned.
|
||||
/// Returns an error if the `AppContext` has been dropped.
|
||||
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
|
||||
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
@@ -172,7 +172,7 @@ impl AsyncAppContext {
|
||||
/// if no state of the specified type has been assigned.
|
||||
///
|
||||
/// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
|
||||
pub fn try_read_global<G: 'static, R>(
|
||||
pub fn try_read_global<G: Global, R>(
|
||||
&self,
|
||||
read: impl FnOnce(&G, &AppContext) -> R,
|
||||
) -> Option<R> {
|
||||
@@ -183,7 +183,7 @@ impl AsyncAppContext {
|
||||
|
||||
/// A convenience method for [AppContext::update_global]
|
||||
/// for updating the global state of the specified type.
|
||||
pub fn update_global<G: 'static, R>(
|
||||
pub fn update_global<G: Global, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut AppContext) -> R,
|
||||
) -> Result<R> {
|
||||
@@ -235,7 +235,7 @@ impl AsyncWindowContext {
|
||||
}
|
||||
|
||||
/// A convenience method for [`AppContext::global`].
|
||||
pub fn read_global<G: 'static, R>(
|
||||
pub fn read_global<G: Global, R>(
|
||||
&mut self,
|
||||
read: impl FnOnce(&G, &WindowContext) -> R,
|
||||
) -> Result<R> {
|
||||
@@ -249,7 +249,7 @@ impl AsyncWindowContext {
|
||||
update: impl FnOnce(&mut G, &mut WindowContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
G: 'static,
|
||||
G: Global,
|
||||
{
|
||||
self.window.update(self, |_, cx| cx.update_global(update))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
|
||||
EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
|
||||
EventEmitter, Global, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
@@ -193,7 +193,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
/// Updates the given global
|
||||
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
|
||||
where
|
||||
G: 'static,
|
||||
G: Global,
|
||||
{
|
||||
let mut global = self.app.lease_global::<G>();
|
||||
let result = f(&mut global, self);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
||||
AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter,
|
||||
ForegroundExecutor, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform, Point,
|
||||
Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View,
|
||||
ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform,
|
||||
Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View,
|
||||
ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
@@ -256,20 +256,20 @@ impl TestAppContext {
|
||||
}
|
||||
|
||||
/// true if the given global is defined
|
||||
pub fn has_global<G: 'static>(&self) -> bool {
|
||||
pub fn has_global<G: Global>(&self) -> bool {
|
||||
let app = self.app.borrow();
|
||||
app.has_global::<G>()
|
||||
}
|
||||
|
||||
/// runs the given closure with a reference to the global
|
||||
/// panics if `has_global` would return false.
|
||||
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
|
||||
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
|
||||
let app = self.app.borrow();
|
||||
read(app.global(), &app)
|
||||
}
|
||||
|
||||
/// runs the given closure with a reference to the global (if set)
|
||||
pub fn try_read_global<G: 'static, R>(
|
||||
pub fn try_read_global<G: Global, R>(
|
||||
&self,
|
||||
read: impl FnOnce(&G, &AppContext) -> R,
|
||||
) -> Option<R> {
|
||||
@@ -278,13 +278,13 @@ impl TestAppContext {
|
||||
}
|
||||
|
||||
/// sets the global in this context.
|
||||
pub fn set_global<G: 'static>(&mut self, global: G) {
|
||||
pub fn set_global<G: Global>(&mut self, global: G) {
|
||||
let mut lock = self.app.borrow_mut();
|
||||
lock.set_global(global);
|
||||
}
|
||||
|
||||
/// updates the global in this context. (panics if `has_global` would return false)
|
||||
pub fn update_global<G: 'static, R>(
|
||||
pub fn update_global<G: Global, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut AppContext) -> R,
|
||||
) -> R {
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
|
||||
use crate::{
|
||||
point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
|
||||
ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement,
|
||||
IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
|
||||
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
|
||||
SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
|
||||
WindowContext,
|
||||
ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, Global,
|
||||
IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
|
||||
ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
|
||||
View, Visibility, WindowContext,
|
||||
};
|
||||
|
||||
use collections::HashMap;
|
||||
@@ -2070,6 +2070,8 @@ impl ElementClickedState {
|
||||
#[derive(Default)]
|
||||
pub(crate) struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
|
||||
|
||||
impl Global for GroupBounds {}
|
||||
|
||||
impl GroupBounds {
|
||||
pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
|
||||
cx.default_global::<Self>()
|
||||
|
||||
@@ -258,14 +258,14 @@ pub trait EventEmitter<E: Any>: 'static {}
|
||||
/// can be used interchangeably.
|
||||
pub trait BorrowAppContext {
|
||||
/// Set a global value on the context.
|
||||
fn set_global<T: 'static>(&mut self, global: T);
|
||||
fn set_global<T: Global>(&mut self, global: T);
|
||||
}
|
||||
|
||||
impl<C> BorrowAppContext for C
|
||||
where
|
||||
C: BorrowMut<AppContext>,
|
||||
{
|
||||
fn set_global<G: 'static>(&mut self, global: G) {
|
||||
fn set_global<G: Global>(&mut self, global: G) {
|
||||
self.borrow_mut().set_global(global)
|
||||
}
|
||||
}
|
||||
@@ -287,3 +287,8 @@ impl<T> Flatten<T> for Result<T> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A marker trait for types that can be stored in GPUI's global state.
|
||||
///
|
||||
/// Implement this on types you want to store in the context as a global.
|
||||
pub trait Global: 'static {}
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::{iter, mem, ops::Range};
|
||||
use crate::{
|
||||
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
|
||||
CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
|
||||
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
|
||||
SizeRefinement, Styled, TextRun,
|
||||
FontStyle, FontWeight, Global, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
|
||||
SharedString, Size, SizeRefinement, Styled, TextRun,
|
||||
};
|
||||
use collections::HashSet;
|
||||
use refineable::Refineable;
|
||||
@@ -20,6 +20,8 @@ pub use taffy::style::{
|
||||
/// GPUI.
|
||||
pub struct DebugBelow;
|
||||
|
||||
impl Global for DebugBelow {}
|
||||
|
||||
/// The CSS styling that can be applied to an element via the `Styled` trait
|
||||
#[derive(Clone, Refineable, Debug)]
|
||||
#[refineable(Debug)]
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
|
||||
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
|
||||
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
|
||||
GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchMode,
|
||||
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchMode,
|
||||
KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton,
|
||||
MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
||||
PlatformWindow, Point, PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet,
|
||||
@@ -708,7 +708,7 @@ impl<'a> WindowContext<'a> {
|
||||
/// access both to the global and the context.
|
||||
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
|
||||
where
|
||||
G: 'static,
|
||||
G: Global,
|
||||
{
|
||||
let mut global = self.app.lease_global::<G>();
|
||||
let result = f(&mut global, self);
|
||||
@@ -1441,7 +1441,7 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
/// Register the given handler to be invoked whenever the global of the given type
|
||||
/// is updated.
|
||||
pub fn observe_global<G: 'static>(
|
||||
pub fn observe_global<G: Global>(
|
||||
&mut self,
|
||||
f: impl Fn(&mut WindowContext<'_>) + 'static,
|
||||
) -> Subscription {
|
||||
@@ -2198,7 +2198,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||
/// Updates the global state of the given type.
|
||||
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
|
||||
where
|
||||
G: 'static,
|
||||
G: Global,
|
||||
{
|
||||
let mut global = self.app.lease_global::<G>();
|
||||
let result = f(&mut global, self);
|
||||
@@ -2207,7 +2207,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the given global state changes.
|
||||
pub fn observe_global<G: 'static>(
|
||||
pub fn observe_global<G: Global>(
|
||||
&mut self,
|
||||
mut f: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static,
|
||||
) -> Subscription {
|
||||
|
||||
Reference in New Issue
Block a user