@@ -1,8 +1,8 @@
use crate::SharedString;
use anyhow::{anyhow, Context, Result};
use collections::{HashMap, HashSet};
-use ctor::ctor;
-use parking_lot::Mutex;
+use lazy_static::lazy_static;
+use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use serde::Deserialize;
use std::any::{type_name, Any};
@@ -21,23 +21,41 @@ pub trait Action: std::fmt::Debug + 'static {
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
-#[ctor]
-static ACTION_BUILDERS: Mutex<HashMap<SharedString, ActionBuilder>> = Mutex::default();
+lazy_static! {
+ static ref ACTION_REGISTRY: RwLock<ActionRegistry> = RwLock::default();
+}
+
+#[derive(Default)]
+struct ActionRegistry {
+ builders_by_name: HashMap<SharedString, ActionBuilder>,
+ all_names: Vec<SharedString>, // So we can return a static slice.
+}
/// Register an action type to allow it to be referenced in keymaps.
pub fn register_action<A: Action>() {
- ACTION_BUILDERS.lock().insert(A::qualified_name(), A::build);
+ let name = A::qualified_name();
+ let mut lock = ACTION_REGISTRY.write();
+ lock.builders_by_name.insert(name.clone(), A::build);
+ lock.all_names.push(name);
}
/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
- let lock = &ACTION_BUILDERS.lock();
+ let lock = ACTION_REGISTRY.read();
let build_action = lock
+ .builders_by_name
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build_action)(params)
}
+pub fn all_action_names() -> MappedRwLockReadGuard<'static, [SharedString]> {
+ let lock = ACTION_REGISTRY.read();
+ RwLockReadGuard::map(lock, |registry: &ActionRegistry| {
+ registry.all_names.as_slice()
+ })
+}
+
// actions defines structs that can be used as actions.
#[macro_export]
macro_rules! actions {
@@ -17,9 +17,9 @@ use crate::{
current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId,
- PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString,
- SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem,
- View, Window, WindowContext, WindowHandle, WindowId,
+ PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet,
+ Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
+ WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@@ -140,7 +140,6 @@ impl App {
}
}
-type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
@@ -176,7 +175,6 @@ pub struct AppContext {
pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>,
- action_builders: HashMap<SharedString, ActionBuilder>,
pending_effects: VecDeque<Effect>,
pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>,
@@ -234,7 +232,6 @@ impl AppContext {
windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())),
global_action_listeners: HashMap::default(),
- action_builders: HashMap::default(),
pending_effects: VecDeque::new(),
pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(),
@@ -695,10 +692,6 @@ impl AppContext {
)
}
- pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = SharedString> + 'a {
- self.action_builders.keys().cloned()
- }
-
/// Move the global of the given type to the stack.
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
GlobalLease::new(
@@ -761,24 +754,6 @@ impl AppContext {
}));
}
- /// Register an action type to allow it to be referenced in keymaps.
- pub fn register_action_type<A: Action>(&mut self) {
- self.action_builders.insert(A::qualified_name(), A::build);
- }
-
- /// Construct an action based on its name and parameters.
- 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)
- }
-
/// Event handlers propagate events by default. Call this method to stop dispatching to
/// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is
/// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by