Merge branch 'main' into terminal-element

This commit is contained in:
Mikayla 2023-12-06 15:20:04 -08:00
commit fd31e429f5
No known key found for this signature in database
103 changed files with 10646 additions and 3579 deletions

View file

@ -15,10 +15,10 @@ use smol::future::FutureExt;
pub use test_context::*;
use crate::{
current_platform, image_cache::ImageCache, Action, ActionRegistry, Any, AnyView,
AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId,
ForegroundExecutor, KeyBinding, Keymap, LayoutId, PathPromptOptions, Pixels, Platform,
ForegroundExecutor, KeyBinding, Keymap, LayoutId, Menu, PathPromptOptions, Pixels, Platform,
PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task,
TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext,
WindowHandle, WindowId,
@ -39,7 +39,10 @@ use std::{
sync::{atomic::Ordering::SeqCst, Arc},
time::Duration,
};
use util::http::{self, HttpClient};
use util::{
http::{self, HttpClient},
ResultExt,
};
/// Temporary(?) wrapper around RefCell<AppContext> to help us debug any double borrows.
/// Strongly consider removing after stabilization.
@ -201,7 +204,7 @@ pub struct AppContext {
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>,
HashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
pending_effects: VecDeque<Effect>,
pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>,
@ -275,6 +278,8 @@ impl AppContext {
}),
});
init_app_menus(platform.as_ref(), &mut *app.borrow_mut());
platform.on_quit(Box::new({
let cx = app.clone();
move || {
@ -425,6 +430,10 @@ impl AppContext {
.collect()
}
pub fn active_window(&self) -> Option<AnyWindowHandle> {
self.platform.active_window()
}
/// 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
/// functionality.
@ -851,7 +860,6 @@ impl AppContext {
}
/// Remove the global of the given type from the app context. Does not notify global observers.
#[cfg(any(test, feature = "test-support"))]
pub fn remove_global<G: Any>(&mut self) -> G {
let global_type = TypeId::of::<G>();
*self
@ -962,9 +970,9 @@ impl AppContext {
self.global_action_listeners
.entry(TypeId::of::<A>())
.or_default()
.push(Box::new(move |action, phase, cx| {
.push(Rc::new(move |action, phase, cx| {
if phase == DispatchPhase::Bubble {
let action = action.as_any().downcast_ref().unwrap();
let action = action.downcast_ref().unwrap();
listener(action, cx)
}
}));
@ -1015,6 +1023,90 @@ impl AppContext {
activate();
subscription
}
pub(crate) fn clear_pending_keystrokes(&mut self) {
for window in self.windows() {
window
.update(self, |_, cx| {
cx.window
.current_frame
.dispatch_tree
.clear_pending_keystrokes()
})
.ok();
}
}
pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
if let Some(window) = self.active_window() {
if let Ok(window_action_available) =
window.update(self, |_, cx| cx.is_action_available(action))
{
return window_action_available;
}
}
self.global_action_listeners
.contains_key(&action.as_any().type_id())
}
pub fn set_menus(&mut self, menus: Vec<Menu>) {
self.platform.set_menus(menus, &self.keymap.lock());
}
pub fn dispatch_action(&mut self, action: &dyn Action) {
if let Some(active_window) = self.active_window() {
active_window
.update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
.log_err();
} else {
self.propagate_event = true;
if let Some(mut global_listeners) = self
.global_action_listeners
.remove(&action.as_any().type_id())
{
for listener in &global_listeners {
listener(action.as_any(), DispatchPhase::Capture, self);
if !self.propagate_event {
break;
}
}
global_listeners.extend(
self.global_action_listeners
.remove(&action.as_any().type_id())
.unwrap_or_default(),
);
self.global_action_listeners
.insert(action.as_any().type_id(), global_listeners);
}
if self.propagate_event {
if let Some(mut global_listeners) = self
.global_action_listeners
.remove(&action.as_any().type_id())
{
for listener in global_listeners.iter().rev() {
listener(action.as_any(), DispatchPhase::Bubble, self);
if !self.propagate_event {
break;
}
}
global_listeners.extend(
self.global_action_listeners
.remove(&action.as_any().type_id())
.unwrap_or_default(),
);
self.global_action_listeners
.insert(action.as_any().type_id(), global_listeners);
}
}
}
}
}
impl Context for AppContext {

View file

@ -1,9 +1,10 @@
use crate::{
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
BackgroundExecutor, Bounds, Context, Div, Entity, EventEmitter, ForegroundExecutor, InputEvent,
KeyDownEvent, Keystroke, Model, ModelContext, Pixels, PlatformWindow, Point, Render, Result,
Size, Task, TestDispatcher, TestPlatform, TestWindow, TestWindowHandlers, View, ViewContext,
VisualContext, WindowBounds, WindowContext, WindowHandle, WindowOptions,
BackgroundExecutor, Bounds, ClipboardItem, Context, Div, Entity, EventEmitter,
ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext,
WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt};
@ -16,6 +17,7 @@ pub struct TestAppContext {
pub foreground_executor: ForegroundExecutor,
pub dispatcher: TestDispatcher,
pub test_platform: Rc<TestPlatform>,
text_system: Arc<TextSystem>,
}
impl Context for TestAppContext {
@ -82,6 +84,7 @@ impl TestAppContext {
let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone());
let asset_source = Arc::new(());
let http_client = util::http::FakeHttpClient::with_404_response();
let text_system = Arc::new(TextSystem::new(platform.text_system()));
Self {
app: AppContext::new(platform.clone(), asset_source, http_client),
@ -89,6 +92,7 @@ impl TestAppContext {
foreground_executor,
dispatcher: dispatcher.clone(),
test_platform: platform,
text_system,
}
}
@ -155,6 +159,18 @@ impl TestAppContext {
(view, Box::leak(cx))
}
pub fn text_system(&self) -> &Arc<TextSystem> {
&self.text_system
}
pub fn write_to_clipboard(&self, item: ClipboardItem) {
self.test_platform.write_to_clipboard(item)
}
pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
self.test_platform.read_from_clipboard()
}
pub fn simulate_new_path_selection(
&self,
select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
@ -529,6 +545,10 @@ pub struct VisualTestContext<'a> {
}
impl<'a> VisualTestContext<'a> {
pub fn update<R>(&mut self, f: impl FnOnce(&mut WindowContext) -> R) -> R {
self.cx.update_window(self.window, |_, cx| f(cx)).unwrap()
}
pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self {
Self { cx, window }
}

View file

@ -1,9 +1,11 @@
use crate::{Bounds, Element, IntoElement, Pixels, StyleRefinement, Styled, WindowContext};
use refineable::Refineable as _;
use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext)) -> Canvas {
Canvas {
paint_callback: Box::new(callback),
style: Default::default(),
style: StyleRefinement::default(),
}
}
@ -32,7 +34,9 @@ impl Element for Canvas {
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::State) {
let layout_id = cx.request_layout(&self.style.clone().into(), []);
let mut style = Style::default();
style.refine(&self.style);
let layout_id = cx.request_layout(&style, []);
(layout_id, ())
}

View file

@ -55,7 +55,7 @@ pub trait InteractiveElement: Sized + Element {
E: Debug,
{
if let Some(key_context) = key_context.try_into().log_err() {
self.interactivity().key_context = key_context;
self.interactivity().key_context = Some(key_context);
}
self
}
@ -737,7 +737,7 @@ impl DivState {
pub struct Interactivity {
pub element_id: Option<ElementId>,
pub key_context: KeyContext,
pub key_context: Option<KeyContext>,
pub focusable: bool,
pub tracked_focus_handle: Option<FocusHandle>,
pub scroll_handle: Option<ScrollHandle>,
@ -1276,7 +1276,7 @@ impl Default for Interactivity {
fn default() -> Self {
Self {
element_id: None,
key_context: KeyContext::default(),
key_context: None,
focusable: false,
tracked_focus_handle: None,
scroll_handle: None,

View file

@ -102,7 +102,7 @@ impl Element for Overlay {
let mut desired = self.anchor_corner.get_bounds(origin, size);
let limits = Bounds {
origin: Point::zero(),
origin: Point::default(),
size: cx.viewport_size(),
};

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@ pub(crate) struct DispatchTree {
pub(crate) struct DispatchNode {
pub key_listeners: SmallVec<[KeyListener; 2]>,
pub action_listeners: SmallVec<[DispatchActionListener; 16]>,
pub context: KeyContext,
pub context: Option<KeyContext>,
parent: Option<DispatchNodeId>,
}
@ -61,7 +61,7 @@ impl DispatchTree {
self.keystroke_matchers.clear();
}
pub fn push_node(&mut self, context: KeyContext) {
pub fn push_node(&mut self, context: Option<KeyContext>) {
let parent = self.node_stack.last().copied();
let node_id = DispatchNodeId(self.nodes.len());
self.nodes.push(DispatchNode {
@ -69,34 +69,34 @@ impl DispatchTree {
..Default::default()
});
self.node_stack.push(node_id);
if !context.is_empty() {
self.active_node().context = context.clone();
if let Some(context) = context {
self.active_node().context = Some(context.clone());
self.context_stack.push(context);
}
}
pub fn pop_node(&mut self) {
let node_id = self.node_stack.pop().unwrap();
if !self.nodes[node_id.0].context.is_empty() {
if self.nodes[node_id.0].context.is_some() {
self.context_stack.pop();
}
}
pub fn clear_keystroke_matchers(&mut self) {
pub fn clear_pending_keystrokes(&mut self) {
self.keystroke_matchers.clear();
}
/// Preserve keystroke matchers from previous frames to support multi-stroke
/// bindings across multiple frames.
pub fn preserve_keystroke_matchers(&mut self, old_tree: &mut Self, focus_id: Option<FocusId>) {
pub fn preserve_pending_keystrokes(&mut self, old_tree: &mut Self, focus_id: Option<FocusId>) {
if let Some(node_id) = focus_id.and_then(|focus_id| self.focusable_node_id(focus_id)) {
let dispatch_path = self.dispatch_path(node_id);
self.context_stack.clear();
for node_id in dispatch_path {
let node = self.node(node_id);
if !node.context.is_empty() {
self.context_stack.push(node.context.clone());
if let Some(context) = node.context.clone() {
self.context_stack.push(context);
}
if let Some((context_stack, matcher)) = old_tree
@ -148,21 +148,33 @@ impl DispatchTree {
false
}
pub fn available_actions(&self, target: FocusId) -> Vec<Box<dyn Action>> {
pub fn available_actions(&self, target: DispatchNodeId) -> Vec<Box<dyn Action>> {
let mut actions = Vec::new();
if let Some(node) = self.focusable_node_ids.get(&target) {
for node_id in self.dispatch_path(*node) {
let node = &self.nodes[node_id.0];
for DispatchActionListener { action_type, .. } in &node.action_listeners {
// Intentionally silence these errors without logging.
// If an action cannot be built by default, it's not available.
actions.extend(self.action_registry.build_action_type(action_type).ok());
}
for node_id in self.dispatch_path(target) {
let node = &self.nodes[node_id.0];
for DispatchActionListener { action_type, .. } in &node.action_listeners {
// Intentionally silence these errors without logging.
// If an action cannot be built by default, it's not available.
actions.extend(self.action_registry.build_action_type(action_type).ok());
}
}
actions
}
pub fn is_action_available(&self, action: &dyn Action, target: DispatchNodeId) -> bool {
for node_id in self.dispatch_path(target) {
let node = &self.nodes[node_id.0];
if node
.action_listeners
.iter()
.any(|listener| listener.action_type == action.as_any().type_id())
{
return true;
}
}
false
}
pub fn bindings_for_action(
&self,
action: &dyn Action,
@ -236,6 +248,11 @@ impl DispatchTree {
self.focusable_node_ids.get(&target).copied()
}
pub fn root_node_id(&self) -> DispatchNodeId {
debug_assert!(!self.nodes.is_empty());
DispatchNodeId(0)
}
fn active_node_id(&self) -> DispatchNodeId {
*self.node_stack.last().unwrap()
}

View file

@ -1,3 +1,4 @@
mod app_menu;
mod keystroke;
#[cfg(target_os = "macos")]
mod mac;
@ -5,10 +6,10 @@ mod mac;
mod test;
use crate::{
point, size, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, LineLayout,
Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene,
SharedString, Size, TaskLabel,
point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap,
LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
Scene, SharedString, Size, TaskLabel,
};
use anyhow::{anyhow, bail};
use async_task::Runnable;
@ -32,6 +33,7 @@ use std::{
};
use uuid::Uuid;
pub use app_menu::*;
pub use keystroke::*;
#[cfg(target_os = "macos")]
pub use mac::*;
@ -44,7 +46,7 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
Rc::new(MacPlatform::new())
}
pub trait Platform: 'static {
pub(crate) trait Platform: 'static {
fn background_executor(&self) -> BackgroundExecutor;
fn foreground_executor(&self) -> ForegroundExecutor;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
@ -59,7 +61,7 @@ pub trait Platform: 'static {
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
fn main_window(&self) -> Option<AnyWindowHandle>;
fn active_window(&self) -> Option<AnyWindowHandle>;
fn open_window(
&self,
handle: AnyWindowHandle,
@ -90,6 +92,11 @@ pub trait Platform: 'static {
fn on_reopen(&self, callback: Box<dyn FnMut()>);
fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
fn os_name(&self) -> &'static str;
fn os_version(&self) -> Result<SemanticVersion>;
fn app_version(&self) -> Result<SemanticVersion>;

View file

@ -0,0 +1,77 @@
use crate::{Action, AppContext, Platform};
use util::ResultExt;
pub struct Menu<'a> {
pub name: &'a str,
pub items: Vec<MenuItem<'a>>,
}
pub enum MenuItem<'a> {
Separator,
Submenu(Menu<'a>),
Action {
name: &'a str,
action: Box<dyn Action>,
os_action: Option<OsAction>,
},
}
impl<'a> MenuItem<'a> {
pub fn separator() -> Self {
Self::Separator
}
pub fn submenu(menu: Menu<'a>) -> Self {
Self::Submenu(menu)
}
pub fn action(name: &'a str, action: impl Action) -> Self {
Self::Action {
name,
action: Box::new(action),
os_action: None,
}
}
pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self {
Self::Action {
name,
action: Box::new(action),
os_action: Some(os_action),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum OsAction {
Cut,
Copy,
Paste,
SelectAll,
Undo,
Redo,
}
pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &mut AppContext) {
platform.on_will_open_app_menu(Box::new({
let cx = cx.to_async();
move || {
cx.update(|cx| cx.clear_pending_keystrokes()).ok();
}
}));
platform.on_validate_app_menu_command(Box::new({
let cx = cx.to_async();
move |action| {
cx.update(|cx| cx.is_action_available(action))
.unwrap_or(false)
}
}));
platform.on_app_menu_action(Box::new({
let cx = cx.to_async();
move |action| {
cx.update(|cx| cx.dispatch_action(action)).log_err();
}
}));
}

View file

@ -7,6 +7,7 @@ use std::{
use crate::DisplayId;
use collections::HashMap;
use parking_lot::Mutex;
pub use sys::CVSMPTETime as SmtpeTime;
pub use sys::CVTimeStamp as VideoTimestamp;
pub(crate) struct MacDisplayLinker {
@ -153,7 +154,7 @@ mod sys {
kCVTimeStampTopField | kCVTimeStampBottomField;
#[repr(C)]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Default)]
pub struct CVSMPTETime {
pub subframes: i16,
pub subframe_divisor: i16,

View file

@ -1,18 +1,19 @@
use super::BoolExt;
use super::{events::key_to_native, BoolExt};
use crate::{
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
InputEvent, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow,
PathPromptOptions, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Result,
SemanticVersion, VideoTimestamp, WindowOptions,
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker,
MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
use cocoa::{
appkit::{
NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
NSModalResponse, NSOpenPanel, NSPasteboard, NSPasteboardTypeString, NSSavePanel, NSWindow,
NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard,
NSPasteboardTypeString, NSSavePanel, NSWindow,
},
base::{id, nil, BOOL, YES},
base::{id, nil, selector, BOOL, YES},
foundation::{
NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSProcessInfo, NSString,
NSUInteger, NSURL,
@ -155,12 +156,12 @@ pub struct MacPlatformState {
reopen: Option<Box<dyn FnMut()>>,
quit: Option<Box<dyn FnMut()>>,
event: Option<Box<dyn FnMut(InputEvent) -> bool>>,
// menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
// validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
will_open_menu: Option<Box<dyn FnMut()>>,
menu_actions: Vec<Box<dyn Action>>,
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
finish_launching: Option<Box<dyn FnOnce()>>,
// menu_actions: Vec<Box<dyn Action>>,
}
impl MacPlatform {
@ -179,12 +180,12 @@ impl MacPlatform {
reopen: None,
quit: None,
event: None,
menu_command: None,
validate_menu_command: None,
will_open_menu: None,
menu_actions: Default::default(),
open_urls: None,
finish_launching: None,
// menu_command: None,
// validate_menu_command: None,
// menu_actions: Default::default(),
}))
}
@ -200,151 +201,153 @@ impl MacPlatform {
}
}
// unsafe fn create_menu_bar(
// &self,
// menus: Vec<Menu>,
// delegate: id,
// actions: &mut Vec<Box<dyn Action>>,
// keystroke_matcher: &KeymapMatcher,
// ) -> id {
// let application_menu = NSMenu::new(nil).autorelease();
// application_menu.setDelegate_(delegate);
unsafe fn create_menu_bar(
&self,
menus: Vec<Menu>,
delegate: id,
actions: &mut Vec<Box<dyn Action>>,
keymap: &Keymap,
) -> id {
let application_menu = NSMenu::new(nil).autorelease();
application_menu.setDelegate_(delegate);
// for menu_config in menus {
// let menu = NSMenu::new(nil).autorelease();
// menu.setTitle_(ns_string(menu_config.name));
// menu.setDelegate_(delegate);
for menu_config in menus {
let menu = NSMenu::new(nil).autorelease();
menu.setTitle_(ns_string(menu_config.name));
menu.setDelegate_(delegate);
// for item_config in menu_config.items {
// menu.addItem_(self.create_menu_item(
// item_config,
// delegate,
// actions,
// keystroke_matcher,
// ));
// }
for item_config in menu_config.items {
menu.addItem_(self.create_menu_item(item_config, delegate, actions, keymap));
}
// let menu_item = NSMenuItem::new(nil).autorelease();
// menu_item.setSubmenu_(menu);
// application_menu.addItem_(menu_item);
let menu_item = NSMenuItem::new(nil).autorelease();
menu_item.setSubmenu_(menu);
application_menu.addItem_(menu_item);
// if menu_config.name == "Window" {
// let app: id = msg_send![APP_CLASS, sharedApplication];
// app.setWindowsMenu_(menu);
// }
// }
if menu_config.name == "Window" {
let app: id = msg_send![APP_CLASS, sharedApplication];
app.setWindowsMenu_(menu);
}
}
// application_menu
// }
application_menu
}
// unsafe fn create_menu_item(
// &self,
// item: MenuItem,
// delegate: id,
// actions: &mut Vec<Box<dyn Action>>,
// keystroke_matcher: &KeymapMatcher,
// ) -> id {
// match item {
// MenuItem::Separator => NSMenuItem::separatorItem(nil),
// MenuItem::Action {
// name,
// action,
// os_action,
// } => {
// // TODO
// let keystrokes = keystroke_matcher
// .bindings_for_action(action.id())
// .find(|binding| binding.action().eq(action.as_ref()))
// .map(|binding| binding.keystrokes());
// let selector = match os_action {
// Some(crate::OsAction::Cut) => selector("cut:"),
// Some(crate::OsAction::Copy) => selector("copy:"),
// Some(crate::OsAction::Paste) => selector("paste:"),
// Some(crate::OsAction::SelectAll) => selector("selectAll:"),
// Some(crate::OsAction::Undo) => selector("undo:"),
// Some(crate::OsAction::Redo) => selector("redo:"),
// None => selector("handleGPUIMenuItem:"),
// };
unsafe fn create_menu_item(
&self,
item: MenuItem,
delegate: id,
actions: &mut Vec<Box<dyn Action>>,
keymap: &Keymap,
) -> id {
match item {
MenuItem::Separator => NSMenuItem::separatorItem(nil),
MenuItem::Action {
name,
action,
os_action,
} => {
let keystrokes = keymap
.bindings_for_action(action.type_id())
.find(|binding| binding.action().partial_eq(action.as_ref()))
.map(|binding| binding.keystrokes());
// let item;
// if let Some(keystrokes) = keystrokes {
// if keystrokes.len() == 1 {
// let keystroke = &keystrokes[0];
// let mut mask = NSEventModifierFlags::empty();
// for (modifier, flag) in &[
// (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
// (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
// (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
// (keystroke.shift, NSEventModifierFlags::NSShiftKeyMask),
// ] {
// if *modifier {
// mask |= *flag;
// }
// }
let selector = match os_action {
Some(crate::OsAction::Cut) => selector("cut:"),
Some(crate::OsAction::Copy) => selector("copy:"),
Some(crate::OsAction::Paste) => selector("paste:"),
Some(crate::OsAction::SelectAll) => selector("selectAll:"),
Some(crate::OsAction::Undo) => selector("undo:"),
Some(crate::OsAction::Redo) => selector("redo:"),
None => selector("handleGPUIMenuItem:"),
};
// item = NSMenuItem::alloc(nil)
// .initWithTitle_action_keyEquivalent_(
// ns_string(name),
// selector,
// ns_string(key_to_native(&keystroke.key).as_ref()),
// )
// .autorelease();
// item.setKeyEquivalentModifierMask_(mask);
// }
// // For multi-keystroke bindings, render the keystroke as part of the title.
// else {
// use std::fmt::Write;
let item;
if let Some(keystrokes) = keystrokes {
if keystrokes.len() == 1 {
let keystroke = &keystrokes[0];
let mut mask = NSEventModifierFlags::empty();
for (modifier, flag) in &[
(
keystroke.modifiers.command,
NSEventModifierFlags::NSCommandKeyMask,
),
(
keystroke.modifiers.control,
NSEventModifierFlags::NSControlKeyMask,
),
(
keystroke.modifiers.alt,
NSEventModifierFlags::NSAlternateKeyMask,
),
(
keystroke.modifiers.shift,
NSEventModifierFlags::NSShiftKeyMask,
),
] {
if *modifier {
mask |= *flag;
}
}
// let mut name = format!("{name} [");
// for (i, keystroke) in keystrokes.iter().enumerate() {
// if i > 0 {
// name.push(' ');
// }
// write!(&mut name, "{}", keystroke).unwrap();
// }
// name.push(']');
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector,
ns_string(key_to_native(&keystroke.key).as_ref()),
)
.autorelease();
item.setKeyEquivalentModifierMask_(mask);
}
// For multi-keystroke bindings, render the keystroke as part of the title.
else {
use std::fmt::Write;
// item = NSMenuItem::alloc(nil)
// .initWithTitle_action_keyEquivalent_(
// ns_string(&name),
// selector,
// ns_string(""),
// )
// .autorelease();
// }
// } else {
// item = NSMenuItem::alloc(nil)
// .initWithTitle_action_keyEquivalent_(
// ns_string(name),
// selector,
// ns_string(""),
// )
// .autorelease();
// }
let mut name = format!("{name} [");
for (i, keystroke) in keystrokes.iter().enumerate() {
if i > 0 {
name.push(' ');
}
write!(&mut name, "{}", keystroke).unwrap();
}
name.push(']');
// let tag = actions.len() as NSInteger;
// let _: () = msg_send![item, setTag: tag];
// actions.push(action);
// item
// }
// MenuItem::Submenu(Menu { name, items }) => {
// let item = NSMenuItem::new(nil).autorelease();
// let submenu = NSMenu::new(nil).autorelease();
// submenu.setDelegate_(delegate);
// for item in items {
// submenu.addItem_(self.create_menu_item(
// item,
// delegate,
// actions,
// keystroke_matcher,
// ));
// }
// item.setSubmenu_(submenu);
// item.setTitle_(ns_string(name));
// item
// }
// }
// }
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(&name),
selector,
ns_string(""),
)
.autorelease();
}
} else {
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector,
ns_string(""),
)
.autorelease();
}
let tag = actions.len() as NSInteger;
let _: () = msg_send![item, setTag: tag];
actions.push(action);
item
}
MenuItem::Submenu(Menu { name, items }) => {
let item = NSMenuItem::new(nil).autorelease();
let submenu = NSMenu::new(nil).autorelease();
submenu.setDelegate_(delegate);
for item in items {
submenu.addItem_(self.create_menu_item(item, delegate, actions, keymap));
}
item.setSubmenu_(submenu);
item.setTitle_(ns_string(name));
item
}
}
}
}
impl Platform for MacPlatform {
@ -479,8 +482,8 @@ impl Platform for MacPlatform {
MacDisplay::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
}
fn main_window(&self) -> Option<AnyWindowHandle> {
MacWindow::main_window()
fn active_window(&self) -> Option<AnyWindowHandle> {
MacWindow::active_window()
}
fn open_window(
@ -631,6 +634,18 @@ impl Platform for MacPlatform {
self.0.lock().event = Some(callback);
}
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
self.0.lock().menu_command = Some(callback);
}
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
self.0.lock().will_open_menu = Some(callback);
}
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
self.0.lock().validate_menu_command = Some(callback);
}
fn os_name(&self) -> &'static str {
"macOS"
}
@ -673,6 +688,15 @@ impl Platform for MacPlatform {
}
}
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {
unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication];
let mut state = self.0.lock();
let actions = &mut state.menu_actions;
app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), actions, keymap));
}
}
fn local_timezone(&self) -> UtcOffset {
unsafe {
let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone];
@ -681,32 +705,6 @@ impl Platform for MacPlatform {
}
}
// fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
// self.0.lock().menu_command = Some(callback);
// }
// fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
// self.0.lock().will_open_menu = Some(callback);
// }
// fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
// self.0.lock().validate_menu_command = Some(callback);
// }
// fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
// unsafe {
// let app: id = msg_send![APP_CLASS, sharedApplication];
// let mut state = self.0.lock();
// let actions = &mut state.menu_actions;
// app.setMainMenu_(self.create_menu_bar(
// menus,
// app.delegate(),
// actions,
// keystroke_matcher,
// ));
// }
// }
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
unsafe {
let bundle: id = NSBundle::mainBundle();
@ -956,7 +954,7 @@ unsafe fn path_from_objc(path: id) -> PathBuf {
PathBuf::from(path)
}
unsafe fn get_foreground_platform(object: &mut Object) -> &MacPlatform {
unsafe fn get_mac_platform(object: &mut Object) -> &MacPlatform {
let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR);
assert!(!platform_ptr.is_null());
&*(platform_ptr as *const MacPlatform)
@ -965,7 +963,7 @@ unsafe fn get_foreground_platform(object: &mut Object) -> &MacPlatform {
extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
unsafe {
if let Some(event) = InputEvent::from_native(native_event, None) {
let platform = get_foreground_platform(this);
let platform = get_mac_platform(this);
if let Some(callback) = platform.0.lock().event.as_mut() {
if !callback(event) {
return;
@ -981,7 +979,7 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
let app: id = msg_send![APP_CLASS, sharedApplication];
app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
let platform = get_foreground_platform(this);
let platform = get_mac_platform(this);
let callback = platform.0.lock().finish_launching.take();
if let Some(callback) = callback {
callback();
@ -991,7 +989,7 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_windows: bool) {
if !has_open_windows {
let platform = unsafe { get_foreground_platform(this) };
let platform = unsafe { get_mac_platform(this) };
if let Some(callback) = platform.0.lock().reopen.as_mut() {
callback();
}
@ -999,21 +997,21 @@ extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_wi
}
extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) {
let platform = unsafe { get_foreground_platform(this) };
let platform = unsafe { get_mac_platform(this) };
if let Some(callback) = platform.0.lock().become_active.as_mut() {
callback();
}
}
extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) {
let platform = unsafe { get_foreground_platform(this) };
let platform = unsafe { get_mac_platform(this) };
if let Some(callback) = platform.0.lock().resign_active.as_mut() {
callback();
}
}
extern "C" fn will_terminate(this: &mut Object, _: Sel, _: id) {
let platform = unsafe { get_foreground_platform(this) };
let platform = unsafe { get_mac_platform(this) };
if let Some(callback) = platform.0.lock().quit.as_mut() {
callback();
}
@ -1035,49 +1033,47 @@ extern "C" fn open_urls(this: &mut Object, _: Sel, _: id, urls: id) {
})
.collect::<Vec<_>>()
};
let platform = unsafe { get_foreground_platform(this) };
let platform = unsafe { get_mac_platform(this) };
if let Some(callback) = platform.0.lock().open_urls.as_mut() {
callback(urls);
}
}
extern "C" fn handle_menu_item(__this: &mut Object, _: Sel, __item: id) {
todo!()
// unsafe {
// let platform = get_foreground_platform(this);
// let mut platform = platform.0.lock();
// if let Some(mut callback) = platform.menu_command.take() {
// let tag: NSInteger = msg_send![item, tag];
// let index = tag as usize;
// if let Some(action) = platform.menu_actions.get(index) {
// callback(action.as_ref());
// }
// platform.menu_command = Some(callback);
// }
// }
extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
unsafe {
let platform = get_mac_platform(this);
let mut platform = platform.0.lock();
if let Some(mut callback) = platform.menu_command.take() {
let tag: NSInteger = msg_send![item, tag];
let index = tag as usize;
if let Some(action) = platform.menu_actions.get(index) {
callback(action.as_ref());
}
platform.menu_command = Some(callback);
}
}
}
extern "C" fn validate_menu_item(__this: &mut Object, _: Sel, __item: id) -> bool {
todo!()
// unsafe {
// let mut result = false;
// let platform = get_foreground_platform(this);
// let mut platform = platform.0.lock();
// if let Some(mut callback) = platform.validate_menu_command.take() {
// let tag: NSInteger = msg_send![item, tag];
// let index = tag as usize;
// if let Some(action) = platform.menu_actions.get(index) {
// result = callback(action.as_ref());
// }
// platform.validate_menu_command = Some(callback);
// }
// result
// }
extern "C" fn validate_menu_item(this: &mut Object, _: Sel, item: id) -> bool {
unsafe {
let mut result = false;
let platform = get_mac_platform(this);
let mut platform = platform.0.lock();
if let Some(mut callback) = platform.validate_menu_command.take() {
let tag: NSInteger = msg_send![item, tag];
let index = tag as usize;
if let Some(action) = platform.menu_actions.get(index) {
result = callback(action.as_ref());
}
platform.validate_menu_command = Some(callback);
}
result
}
}
extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
unsafe {
let platform = get_foreground_platform(this);
let platform = get_mac_platform(this);
let mut platform = platform.0.lock();
if let Some(mut callback) = platform.will_open_menu.take() {
callback();

View file

@ -662,7 +662,7 @@ impl MacWindow {
}
}
pub fn main_window() -> Option<AnyWindowHandle> {
pub fn active_window() -> Option<AnyWindowHandle> {
unsafe {
let app = NSApplication::sharedApplication(nil);
let main_window: id = msg_send![app, mainWindow];

View file

@ -15,7 +15,7 @@ impl TestDisplay {
id: DisplayId(1),
uuid: uuid::Uuid::new_v4(),
bounds: Bounds::from_corners(
Point::zero(),
Point::default(),
Point::new(GlobalPixels(1920.), GlobalPixels(1080.)),
),
}

View file

@ -1,6 +1,6 @@
use crate::{
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
Keymap, Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
};
use anyhow::{anyhow, Result};
use collections::VecDeque;
@ -127,7 +127,7 @@ impl Platform for TestPlatform {
self.displays().iter().find(|d| d.id() == id).cloned()
}
fn main_window(&self) -> Option<crate::AnyWindowHandle> {
fn active_window(&self) -> Option<crate::AnyWindowHandle> {
unimplemented!()
}
@ -147,18 +147,25 @@ impl Platform for TestPlatform {
fn set_display_link_output_callback(
&self,
_display_id: DisplayId,
_callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
mut callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
) {
unimplemented!()
let timestamp = crate::VideoTimestamp {
version: 0,
video_time_scale: 0,
video_time: 0,
host_time: 0,
rate_scalar: 0.0,
video_refresh_period: 0,
smpte_time: crate::SmtpeTime::default(),
flags: 0,
reserved: 0,
};
callback(&timestamp, &timestamp)
}
fn start_display_link(&self, _display_id: DisplayId) {
unimplemented!()
}
fn start_display_link(&self, _display_id: DisplayId) {}
fn stop_display_link(&self, _display_id: DisplayId) {
unimplemented!()
}
fn stop_display_link(&self, _display_id: DisplayId) {}
fn open_url(&self, _url: &str) {
unimplemented!()
@ -205,6 +212,14 @@ impl Platform for TestPlatform {
unimplemented!()
}
fn set_menus(&self, _menus: Vec<crate::Menu>, _keymap: &Keymap) {}
fn on_app_menu_action(&self, _callback: Box<dyn FnMut(&dyn crate::Action)>) {}
fn on_will_open_app_menu(&self, _callback: Box<dyn FnMut()>) {}
fn on_validate_app_menu_command(&self, _callback: Box<dyn FnMut(&dyn crate::Action) -> bool>) {}
fn os_name(&self) -> &'static str {
"test"
}

View file

@ -78,7 +78,7 @@ impl PlatformWindow for TestWindow {
}
fn mouse_position(&self) -> Point<Pixels> {
Point::zero()
Point::default()
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
@ -223,7 +223,7 @@ impl PlatformAtlas for TestAtlas {
},
tile_id: TileId(tile_id),
bounds: crate::Bounds {
origin: Point::zero(),
origin: Point::default(),
size,
},
},

View file

@ -385,7 +385,7 @@ impl Default for Style {
min_size: Size::auto(),
max_size: Size::auto(),
aspect_ratio: None,
gap: Size::zero(),
gap: Size::default(),
// Aligment
align_items: None,
align_self: None,

View file

@ -430,7 +430,7 @@ impl<'a> WindowContext<'a> {
self.window
.current_frame
.dispatch_tree
.clear_keystroke_matchers();
.clear_pending_keystrokes();
self.app.push_effect(Effect::FocusChanged {
window_handle: self.window.handle,
focused: Some(focus_id),
@ -453,19 +453,21 @@ impl<'a> WindowContext<'a> {
}
pub fn dispatch_action(&mut self, action: Box<dyn Action>) {
if let Some(focus_handle) = self.focused() {
self.defer(move |cx| {
if let Some(node_id) = cx
.window
.current_frame
.dispatch_tree
.focusable_node_id(focus_handle.id)
{
cx.propagate_event = true;
cx.dispatch_action_on_node(node_id, action);
}
})
}
let focus_handle = self.focused();
self.defer(move |cx| {
let node_id = focus_handle
.and_then(|handle| {
cx.window
.current_frame
.dispatch_tree
.focusable_node_id(handle.id)
})
.unwrap_or_else(|| cx.window.current_frame.dispatch_tree.root_node_id());
cx.propagate_event = true;
cx.dispatch_action_on_node(node_id, action);
})
}
/// Schedules the given function to be run at the end of the current effect cycle, allowing entities
@ -802,6 +804,22 @@ impl<'a> WindowContext<'a> {
);
}
pub fn is_action_available(&self, action: &dyn Action) -> bool {
let target = self
.focused()
.and_then(|focused_handle| {
self.window
.current_frame
.dispatch_tree
.focusable_node_id(focused_handle.id)
})
.unwrap_or_else(|| self.window.current_frame.dispatch_tree.root_node_id());
self.window
.current_frame
.dispatch_tree
.is_action_available(action, target)
}
/// The position of the mouse relative to the window.
pub fn mouse_position(&self) -> Point<Pixels> {
self.window.mouse_position
@ -1154,8 +1172,19 @@ impl<'a> WindowContext<'a> {
self.start_frame();
self.with_z_index(0, |cx| {
let available_space = cx.window.viewport_size.map(Into::into);
root_view.draw(Point::zero(), available_space, cx);
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
for (action_type, action_listeners) in &cx.app.global_action_listeners {
for action_listener in action_listeners.iter().cloned() {
cx.window.current_frame.dispatch_tree.on_action(
*action_type,
Rc::new(move |action, phase, cx| action_listener(action, phase, cx)),
)
}
}
let available_space = cx.window.viewport_size.map(Into::into);
root_view.draw(Point::default(), available_space, cx);
})
});
if let Some(active_drag) = self.app.active_drag.take() {
@ -1180,7 +1209,7 @@ impl<'a> WindowContext<'a> {
self.window
.current_frame
.dispatch_tree
.preserve_keystroke_matchers(
.preserve_pending_keystrokes(
&mut self.window.previous_frame.dispatch_tree,
self.window.focus,
);
@ -1341,75 +1370,79 @@ impl<'a> WindowContext<'a> {
}
fn dispatch_key_event(&mut self, event: &dyn Any) {
if let Some(node_id) = self.window.focus.and_then(|focus_id| {
self.window
.current_frame
.dispatch_tree
.focusable_node_id(focus_id)
}) {
let dispatch_path = self
.window
.current_frame
.dispatch_tree
.dispatch_path(node_id);
let node_id = self
.window
.focus
.and_then(|focus_id| {
self.window
.current_frame
.dispatch_tree
.focusable_node_id(focus_id)
})
.unwrap_or_else(|| self.window.current_frame.dispatch_tree.root_node_id());
let mut actions: Vec<Box<dyn Action>> = Vec::new();
let dispatch_path = self
.window
.current_frame
.dispatch_tree
.dispatch_path(node_id);
// Capture phase
let mut context_stack: SmallVec<[KeyContext; 16]> = SmallVec::new();
self.propagate_event = true;
let mut actions: Vec<Box<dyn Action>> = Vec::new();
for node_id in &dispatch_path {
let node = self.window.current_frame.dispatch_tree.node(*node_id);
// Capture phase
let mut context_stack: SmallVec<[KeyContext; 16]> = SmallVec::new();
self.propagate_event = true;
if !node.context.is_empty() {
context_stack.push(node.context.clone());
}
for node_id in &dispatch_path {
let node = self.window.current_frame.dispatch_tree.node(*node_id);
for key_listener in node.key_listeners.clone() {
key_listener(event, DispatchPhase::Capture, self);
if !self.propagate_event {
return;
}
}
if let Some(context) = node.context.clone() {
context_stack.push(context);
}
// Bubble phase
for node_id in dispatch_path.iter().rev() {
// Handle low level key events
let node = self.window.current_frame.dispatch_tree.node(*node_id);
for key_listener in node.key_listeners.clone() {
key_listener(event, DispatchPhase::Bubble, self);
if !self.propagate_event {
return;
}
}
// Match keystrokes
let node = self.window.current_frame.dispatch_tree.node(*node_id);
if !node.context.is_empty() {
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
if let Some(found) = self
.window
.current_frame
.dispatch_tree
.dispatch_key(&key_down_event.keystroke, &context_stack)
{
actions.push(found.boxed_clone())
}
}
context_stack.pop();
}
}
for action in actions {
self.dispatch_action_on_node(node_id, action);
for key_listener in node.key_listeners.clone() {
key_listener(event, DispatchPhase::Capture, self);
if !self.propagate_event {
return;
}
}
}
// Bubble phase
for node_id in dispatch_path.iter().rev() {
// Handle low level key events
let node = self.window.current_frame.dispatch_tree.node(*node_id);
for key_listener in node.key_listeners.clone() {
key_listener(event, DispatchPhase::Bubble, self);
if !self.propagate_event {
return;
}
}
// Match keystrokes
let node = self.window.current_frame.dispatch_tree.node(*node_id);
if node.context.is_some() {
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
if let Some(found) = self
.window
.current_frame
.dispatch_tree
.dispatch_key(&key_down_event.keystroke, &context_stack)
{
actions.push(found.boxed_clone())
}
}
context_stack.pop();
}
}
for action in actions {
self.dispatch_action_on_node(node_id, action);
if !self.propagate_event {
return;
}
}
}
fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box<dyn Action>) {
@ -1493,14 +1526,21 @@ impl<'a> WindowContext<'a> {
}
pub fn available_actions(&self) -> Vec<Box<dyn Action>> {
if let Some(focus_id) = self.window.focus {
self.window
.current_frame
.dispatch_tree
.available_actions(focus_id)
} else {
Vec::new()
}
let node_id = self
.window
.focus
.and_then(|focus_id| {
self.window
.current_frame
.dispatch_tree
.focusable_node_id(focus_id)
})
.unwrap_or_else(|| self.window.current_frame.dispatch_tree.root_node_id());
self.window
.current_frame
.dispatch_tree
.available_actions(node_id)
}
pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
@ -1526,7 +1566,7 @@ impl<'a> WindowContext<'a> {
let context_stack = dispatch_tree
.dispatch_path(node_id)
.into_iter()
.map(|node_id| dispatch_tree.node(node_id).context.clone())
.filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
.collect();
dispatch_tree.bindings_for_action(action, &context_stack)
}
@ -1556,7 +1596,7 @@ impl<'a> WindowContext<'a> {
//========== ELEMENT RELATED FUNCTIONS ===========
pub fn with_key_dispatch<R>(
&mut self,
context: KeyContext,
context: Option<KeyContext>,
focus_handle: Option<FocusHandle>,
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
) -> R {
@ -2819,3 +2859,9 @@ impl From<(&'static str, EntityId)> for ElementId {
ElementId::NamedInteger(name.into(), id.as_u64() as usize)
}
}
impl From<(&'static str, usize)> for ElementId {
fn from((name, id): (&'static str, usize)) -> Self {
ElementId::NamedInteger(name.into(), id)
}
}