From 33105486aa7c49cb68dd4c916bc64f07e1405fea Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 20 Jan 2024 06:56:04 -0800 Subject: [PATCH 1/4] Make platform input handler private Automatically record the context on non-view input handlers Simplify the async window context update() method --- crates/command_palette/src/command_palette.rs | 2 +- crates/copilot_ui/src/copilot_button.rs | 2 +- crates/editor/src/editor.rs | 11 +- crates/editor/src/element.rs | 7 +- crates/editor/src/hover_popover.rs | 2 +- crates/gpui/src/app/async_context.rs | 7 +- crates/gpui/src/elements/div.rs | 2 +- crates/gpui/src/input.rs | 82 ++++++------- crates/gpui/src/platform.rs | 110 +++++++++++++++--- crates/gpui/src/platform/mac/window.rs | 18 +-- crates/gpui/src/window.rs | 29 +++-- crates/journal/src/journal.rs | 2 +- crates/terminal_view/src/terminal_element.rs | 79 +++++++------ crates/terminal_view/src/terminal_view.rs | 2 +- crates/workspace/src/notifications.rs | 2 +- crates/workspace/src/pane.rs | 6 +- crates/workspace/src/workspace.rs | 14 +-- 17 files changed, 229 insertions(+), 148 deletions(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index c90e44886568b6def4e7c66cced834553cf5bb96..a130947793717934c33c233a989e20abe4093196 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -311,7 +311,7 @@ impl PickerDelegate for CommandPaletteDelegate { let action = command.action; cx.focus(&self.previous_focus_handle); cx.window_context() - .spawn(move |mut cx| async move { cx.update(|_, cx| cx.dispatch_action(action)) }) + .spawn(move |mut cx| async move { cx.update(|cx| cx.dispatch_action(action)) }) .detach_and_log_err(cx); self.dismissed(cx); } diff --git a/crates/copilot_ui/src/copilot_button.rs b/crates/copilot_ui/src/copilot_button.rs index 9dc4e75cb1cced8c5097bb3b7ee474fba9ed2249..28b28ffe9afec835eb5d6877aad0dfef2f05c017 100644 --- a/crates/copilot_ui/src/copilot_button.rs +++ b/crates/copilot_ui/src/copilot_button.rs @@ -355,7 +355,7 @@ fn initiate_sign_in(cx: &mut WindowContext) { cx.spawn(|mut cx| async move { task.await; - if let Some(copilot) = cx.update(|_, cx| Copilot::global(cx)).ok().flatten() { + if let Some(copilot) = cx.update(|cx| Copilot::global(cx)).ok().flatten() { workspace .update(&mut cx, |workspace, cx| match copilot.read(cx).status() { Status::Authorized => workspace.show_toast( diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4dc505a84120a4aaf825facbae2c8fb85ffee0da..409148f59d702b873647821cf255268e2c2bf193 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -57,9 +57,10 @@ use gpui::{ div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, - HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model, MouseButton, - ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle, - UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext, + HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton, ParentElement, Pixels, + Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle, + UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakView, + WhiteSpace, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -3378,7 +3379,7 @@ impl Editor { let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?; let mut entries = transaction.0.into_iter().collect::>(); - cx.update(|_, cx| { + cx.update(|cx| { entries.sort_unstable_by_key(|(buffer, _)| { buffer.read(cx).file().map(|f| f.path().clone()) }); @@ -9166,7 +9167,7 @@ impl Render for Editor { } } -impl InputHandler for Editor { +impl ViewInputHandler for Editor { fn text_for_range( &mut self, range_utf16: Range, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4c6efcb02a470f53c61d4907889c4d1721f61036..ad166bb42babf91fbb052ccad138ce3f3d8e1b81 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2951,9 +2951,10 @@ impl Element for EditorElement { self.register_key_listeners(cx); cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - let input_handler = - ElementInputHandler::new(bounds, self.editor.clone(), cx); - cx.handle_input(&focus_handle, input_handler); + cx.handle_input( + &focus_handle, + ElementInputHandler::new(bounds, self.editor.clone()), + ); self.paint_background(gutter_bounds, text_bounds, &layout, cx); if layout.gutter_size.width > Pixels::ZERO { diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 609c20ac680819ff738f4a4f3ab08ffa58762146..9268c6de4e4e845899259959ad49734ffd259490 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -247,7 +247,7 @@ fn show_hover( }; // query the LSP for hover info - let hover_request = cx.update(|_, cx| { + let hover_request = cx.update(|cx| { project.update(cx, |project, cx| { project.hover(&buffer, buffer_position, cx) }) diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index 1ee01d90dfac22632f718088bbd7bbe54364136c..7c36aebf57b0236d54f286789d03b60ec547cab5 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -213,7 +213,12 @@ impl AsyncWindowContext { } /// A convenience method for [WindowContext::update()] - pub fn update( + pub fn update(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result { + self.app.update_window(self.window, |_, cx| update(cx)) + } + + /// A convenience method for [WindowContext::update()] + pub fn update_root( &mut self, update: impl FnOnce(AnyView, &mut WindowContext) -> R, ) -> Result { diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index aa912eadbe9c986969dd197927af8963a3c58d0c..fe069a9b6c9bc1124bfa005011ca648f8d0b8c28 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -1420,7 +1420,7 @@ impl Interactivity { move |mut cx| async move { cx.background_executor().timer(TOOLTIP_DELAY).await; - cx.update(|_, cx| { + cx.update(|cx| { active_tooltip.borrow_mut().replace( ActiveTooltip { tooltip: Some(AnyTooltip { diff --git a/crates/gpui/src/input.rs b/crates/gpui/src/input.rs index 7290b48abd7157cdbf11ffa74e6eed294bcc271e..3440d08ef3825d004c72a25f87f66654091e0e2a 100644 --- a/crates/gpui/src/input.rs +++ b/crates/gpui/src/input.rs @@ -1,13 +1,11 @@ -use crate::{ - AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext, WindowContext, -}; +use crate::{Bounds, InputHandler, Pixels, View, ViewContext, WindowContext}; use std::ops::Range; /// Implement this trait to allow views to handle textual input when implementing an editor, field, etc. /// /// Once your view `V` implements this trait, you can use it to construct an [`ElementInputHandler`]. /// This input handler can then be assigned during paint by calling [`WindowContext::handle_input`]. -pub trait InputHandler: 'static + Sized { +pub trait ViewInputHandler: 'static + Sized { fn text_for_range(&mut self, range: Range, cx: &mut ViewContext) -> Option; fn selected_text_range(&mut self, cx: &mut ViewContext) -> Option>; @@ -39,7 +37,6 @@ pub trait InputHandler: 'static + Sized { pub struct ElementInputHandler { view: View, element_bounds: Bounds, - cx: AsyncWindowContext, } impl ElementInputHandler { @@ -47,45 +44,42 @@ impl ElementInputHandler { /// containing view. /// /// [element_paint]: crate::Element::paint - pub fn new(element_bounds: Bounds, view: View, cx: &mut WindowContext) -> Self { + pub fn new(element_bounds: Bounds, view: View) -> Self { ElementInputHandler { view, element_bounds, - cx: cx.to_async(), } } } -impl PlatformInputHandler for ElementInputHandler { - fn selected_text_range(&mut self) -> Option> { +impl InputHandler for ElementInputHandler { + fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option> { self.view - .update(&mut self.cx, |view, cx| view.selected_text_range(cx)) - .ok() - .flatten() + .update(cx, |view, cx| view.selected_text_range(cx)) } - fn marked_text_range(&mut self) -> Option> { - self.view - .update(&mut self.cx, |view, cx| view.marked_text_range(cx)) - .ok() - .flatten() + fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option> { + self.view.update(cx, |view, cx| view.marked_text_range(cx)) } - fn text_for_range(&mut self, range_utf16: Range) -> Option { + fn text_for_range( + &mut self, + range_utf16: Range, + cx: &mut WindowContext, + ) -> Option { self.view - .update(&mut self.cx, |view, cx| { - view.text_for_range(range_utf16, cx) - }) - .ok() - .flatten() + .update(cx, |view, cx| view.text_for_range(range_utf16, cx)) } - fn replace_text_in_range(&mut self, replacement_range: Option>, text: &str) { - self.view - .update(&mut self.cx, |view, cx| { - view.replace_text_in_range(replacement_range, text, cx) - }) - .ok(); + fn replace_text_in_range( + &mut self, + replacement_range: Option>, + text: &str, + cx: &mut WindowContext, + ) { + self.view.update(cx, |view, cx| { + view.replace_text_in_range(replacement_range, text, cx) + }); } fn replace_and_mark_text_in_range( @@ -93,26 +87,24 @@ impl PlatformInputHandler for ElementInputHandler { range_utf16: Option>, new_text: &str, new_selected_range: Option>, + cx: &mut WindowContext, ) { - self.view - .update(&mut self.cx, |view, cx| { - view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx) - }) - .ok(); + self.view.update(cx, |view, cx| { + view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx) + }); } - fn unmark_text(&mut self) { - self.view - .update(&mut self.cx, |view, cx| view.unmark_text(cx)) - .ok(); + fn unmark_text(&mut self, cx: &mut WindowContext) { + self.view.update(cx, |view, cx| view.unmark_text(cx)); } - fn bounds_for_range(&mut self, range_utf16: Range) -> Option> { - self.view - .update(&mut self.cx, |view, cx| { - view.bounds_for_range(range_utf16, self.element_bounds, cx) - }) - .ok() - .flatten() + fn bounds_for_range( + &mut self, + range_utf16: Range, + cx: &mut WindowContext, + ) -> Option> { + self.view.update(cx, |view, cx| { + view.bounds_for_range(range_utf16, self.element_bounds, cx) + }) } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index e08d7a85521c9f24d4c25ab6decb1f3fb99d9882..cbf5908b067de51d6e487aa644a7afbdabb3393d 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -6,10 +6,10 @@ mod mac; mod test; use crate::{ - Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics, - FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, Pixels, PlatformInput, - Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString, - Size, TaskLabel, + Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font, + FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, + Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, + Scene, SharedString, Size, TaskLabel, WindowContext, }; use anyhow::anyhow; use async_task::Runnable; @@ -149,8 +149,8 @@ pub(crate) trait PlatformWindow { fn mouse_position(&self) -> Point; fn modifiers(&self) -> Modifiers; fn as_any_mut(&mut self) -> &mut dyn Any; - fn set_input_handler(&mut self, input_handler: Box); - fn take_input_handler(&mut self) -> Option>; + fn set_input_handler(&mut self, input_handler: PlatformInputHandler); + fn take_input_handler(&mut self) -> Option; fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver; fn activate(&self); fn set_title(&mut self, title: &str); @@ -325,19 +325,103 @@ impl From for etagere::AllocId { } } -pub trait PlatformInputHandler: 'static { - fn selected_text_range(&mut self) -> Option>; - fn marked_text_range(&mut self) -> Option>; - fn text_for_range(&mut self, range_utf16: Range) -> Option; - fn replace_text_in_range(&mut self, replacement_range: Option>, text: &str); +pub(crate) struct PlatformInputHandler { + cx: AsyncWindowContext, + handler: Box, +} + +impl PlatformInputHandler { + pub fn new(cx: AsyncWindowContext, handler: Box) -> Self { + Self { cx, handler } + } + + fn selected_text_range(&mut self) -> Option> { + self.cx + .update(|cx| self.handler.selected_text_range(cx)) + .ok() + .flatten() + } + + fn marked_text_range(&mut self) -> Option> { + self.cx + .update(|cx| self.handler.marked_text_range(cx)) + .ok() + .flatten() + } + + fn text_for_range(&mut self, range_utf16: Range) -> Option { + self.cx + .update(|cx| self.handler.text_for_range(range_utf16, cx)) + .ok() + .flatten() + } + + fn replace_text_in_range(&mut self, replacement_range: Option>, text: &str) { + self.cx + .update(|cx| { + self.handler + .replace_text_in_range(replacement_range, text, cx) + }) + .ok(); + } + fn replace_and_mark_text_in_range( &mut self, range_utf16: Option>, new_text: &str, new_selected_range: Option>, + ) { + self.cx + .update(|cx| { + self.handler.replace_and_mark_text_in_range( + range_utf16, + new_text, + new_selected_range, + cx, + ) + }) + .ok(); + } + + fn unmark_text(&mut self) { + self.cx.update(|cx| self.handler.unmark_text(cx)).ok(); + } + + fn bounds_for_range(&mut self, range_utf16: Range) -> Option> { + self.cx + .update(|cx| self.handler.bounds_for_range(range_utf16, cx)) + .ok() + .flatten() + } +} + +pub trait InputHandler: 'static { + fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option>; + fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option>; + fn text_for_range( + &mut self, + range_utf16: Range, + cx: &mut WindowContext, + ) -> Option; + fn replace_text_in_range( + &mut self, + replacement_range: Option>, + text: &str, + cx: &mut WindowContext, ); - fn unmark_text(&mut self); - fn bounds_for_range(&mut self, range_utf16: Range) -> Option>; + fn replace_and_mark_text_in_range( + &mut self, + range_utf16: Option>, + new_text: &str, + new_selected_range: Option>, + cx: &mut WindowContext, + ); + fn unmark_text(&mut self, cx: &mut WindowContext); + fn bounds_for_range( + &mut self, + range_utf16: Range, + cx: &mut WindowContext, + ) -> Option>; } #[derive(Debug)] diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 814b8cc788cc2c607fc3cf3dd7f943d1868ecb25..fcfd02aa411f3cc8d6fe2fc422e8c1ba56e41baf 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1,9 +1,9 @@ use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange}; use crate::{ - global_bounds_to_ns_rect, point, px, size, AnyWindowHandle, Bounds, ExternalPaths, - FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, Keystroke, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, - PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, + global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle, + Bounds, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, + Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, + MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, }; use block::ConcreteBlock; @@ -327,7 +327,7 @@ struct MacWindowState { should_close_callback: Option bool>>, close_callback: Option>, appearance_changed_callback: Option>, - input_handler: Option>, + input_handler: Option, pending_key_down: Option<(KeyDownEvent, Option)>, last_key_equivalent: Option, synthetic_drag_counter: usize, @@ -764,11 +764,11 @@ impl PlatformWindow for MacWindow { self } - fn set_input_handler(&mut self, input_handler: Box) { + fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { self.0.as_ref().lock().input_handler = Some(input_handler); } - fn take_input_handler(&mut self) -> Option> { + fn take_input_handler(&mut self) -> Option { self.0.as_ref().lock().input_handler.take() } @@ -1761,13 +1761,13 @@ fn drag_event_position(window_state: &Mutex, dragging_info: id) fn with_input_handler(window: &Object, f: F) -> Option where - F: FnOnce(&mut dyn PlatformInputHandler) -> R, + F: FnOnce(&mut PlatformInputHandler) -> R, { let window_state = unsafe { get_window_state(window) }; let mut lock = window_state.as_ref().lock(); if let Some(mut input_handler) = lock.input_handler.take() { drop(lock); - let result = f(input_handler.as_mut()); + let result = f(&mut input_handler); window_state.lock().input_handler = Some(input_handler); Some(result) } else { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 4e7ba2001a373e1a66406373ade0182fedd9fe34..81bd8b55b30a14b368581fcd9506bd44fe4c6696 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -5,13 +5,13 @@ use crate::{ AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla, - ImageData, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, KeystrokeEvent, LayoutId, - Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, - MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, - RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene, Shadow, - SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, - Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, + ImageData, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, + KeystrokeEvent, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, + MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, + PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, + Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene, + Shadow, SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, + Task, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Context as _, Result}; @@ -298,7 +298,7 @@ pub(crate) struct ElementStateBox { struct RequestedInputHandler { view_id: EntityId, - handler: Option>, + handler: Option, } struct TooltipRequest { @@ -2188,16 +2188,15 @@ impl<'a> WindowContext<'a> { /// rendered. /// /// [element_input_handler]: crate::ElementInputHandler - pub fn handle_input( - &mut self, - focus_handle: &FocusHandle, - input_handler: impl PlatformInputHandler, - ) { + pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) { if focus_handle.is_focused(self) { let view_id = self.parent_view_id(); self.window.next_frame.requested_input_handler = Some(RequestedInputHandler { view_id, - handler: Some(Box::new(input_handler)), + handler: Some(PlatformInputHandler::new( + self.to_async(), + Box::new(input_handler), + )), }) } } @@ -2209,7 +2208,7 @@ impl<'a> WindowContext<'a> { self.window .platform_window .on_should_close(Box::new(move || { - this.update(|_, cx| { + this.update(|cx| { // Ensure that the window is removed from the app if it's been closed // by always pre-empting the system close event. if f(cx) { diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index b15da05e1737d3e21a7c3e84fb2a8e5184330be9..7383a0ee55a61a8cfb8bab62e1c8b1c816813f10 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -102,7 +102,7 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut WindowContext) { cx.spawn(|mut cx| async move { let (journal_dir, entry_path) = create_entry.await?; let (workspace, _) = cx - .update(|_, cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))? + .update(|cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))? .await?; let opened = workspace diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 29944b54d7729d7cf257c515339234db9f666751..9ab62947fd46f96b43eafddaab5a86952ed714e6 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -1,11 +1,11 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine}; use gpui::{ - div, fill, point, px, relative, AnyElement, AsyncWindowContext, AvailableSpace, BorrowWindow, - Bounds, DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight, - HighlightStyle, Hsla, InteractiveBounds, InteractiveElement, InteractiveElementState, + div, fill, point, px, relative, AnyElement, AvailableSpace, BorrowWindow, Bounds, + DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight, HighlightStyle, + Hsla, InputHandler, InteractiveBounds, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, - MouseMoveEvent, Pixels, PlatformInputHandler, Point, ShapedLine, StatefulInteractiveElement, - Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext, + MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, Styled, TextRun, + TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext, }; use itertools::Itertools; use language::CursorShape; @@ -749,7 +749,6 @@ impl Element for TerminalElement { let origin = bounds.origin + Point::new(layout.gutter, px(0.)); let terminal_input_handler = TerminalInputHandler { - cx: cx.to_async(), terminal: self.terminal.clone(), cursor_bounds: layout .cursor @@ -838,37 +837,35 @@ impl IntoElement for TerminalElement { } struct TerminalInputHandler { - cx: AsyncWindowContext, terminal: Model, workspace: WeakView, cursor_bounds: Option>, } -impl PlatformInputHandler for TerminalInputHandler { - fn selected_text_range(&mut self) -> Option> { - self.cx - .update(|_, cx| { - if self - .terminal - .read(cx) - .last_content - .mode - .contains(TermMode::ALT_SCREEN) - { - None - } else { - Some(0..0) - } - }) - .ok() - .flatten() +impl InputHandler for TerminalInputHandler { + fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option> { + if self + .terminal + .read(cx) + .last_content + .mode + .contains(TermMode::ALT_SCREEN) + { + None + } else { + Some(0..0) + } } - fn marked_text_range(&mut self) -> Option> { + fn marked_text_range(&mut self, _: &mut WindowContext) -> Option> { None } - fn text_for_range(&mut self, _: std::ops::Range) -> Option { + fn text_for_range( + &mut self, + _: std::ops::Range, + _: &mut WindowContext, + ) -> Option { None } @@ -876,19 +873,16 @@ impl PlatformInputHandler for TerminalInputHandler { &mut self, _replacement_range: Option>, text: &str, + cx: &mut WindowContext, ) { - self.cx - .update(|_, cx| { - self.terminal.update(cx, |terminal, _| { - terminal.input(text.into()); - }); + self.terminal.update(cx, |terminal, _| { + terminal.input(text.into()); + }); - self.workspace - .update(cx, |this, cx| { - let telemetry = this.project().read(cx).client().telemetry().clone(); - telemetry.log_edit_event("terminal"); - }) - .ok(); + self.workspace + .update(cx, |this, cx| { + let telemetry = this.project().read(cx).client().telemetry().clone(); + telemetry.log_edit_event("terminal"); }) .ok(); } @@ -898,12 +892,17 @@ impl PlatformInputHandler for TerminalInputHandler { _range_utf16: Option>, _new_text: &str, _new_selected_range: Option>, + _: &mut WindowContext, ) { } - fn unmark_text(&mut self) {} + fn unmark_text(&mut self, _: &mut WindowContext) {} - fn bounds_for_range(&mut self, _range_utf16: std::ops::Range) -> Option> { + fn bounds_for_range( + &mut self, + _range_utf16: std::ops::Range, + _: &mut WindowContext, + ) -> Option> { self.cursor_bounds } } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 25e67aa8f472c14c2943f55b75a0909ec831426d..180667b113816f0928a23882fb40222824136bbc 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -772,7 +772,7 @@ impl Item for TerminalView { .log_err() .flatten() .or_else(|| { - cx.update(|_, cx| { + cx.update(|cx| { let strategy = TerminalSettings::get_global(cx).working_directory.clone(); workspace .upgrade() diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 6e7590c7d3886493f00d2b43728326549e5665f8..1b41b7040ceb00eacbcc38890018e852f446d95a 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -281,7 +281,7 @@ where Ok(value) => Some(value), Err(err) => { log::error!("TODO {err:?}"); - cx.update(|view, cx| { + cx.update_root(|view, cx| { if let Ok(workspace) = view.downcast::() { workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx)) } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 17001ed76f084275ac2b93304e8e5bcad152b650..306ffab90f6b02a23026e2d95c3eeb800b088bd3 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1092,7 +1092,7 @@ impl Pane { return Ok(true); } - let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|_, cx| { + let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.update(|cx| { ( item.has_conflict(cx), item.is_dirty(cx), @@ -1132,7 +1132,7 @@ impl Pane { } } else if is_dirty && (can_save || can_save_as) { if save_intent == SaveIntent::Close { - let will_autosave = cx.update(|_, cx| { + let will_autosave = cx.update(|cx| { matches!( WorkspaceSettings::get_global(cx).autosave, AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange @@ -1166,7 +1166,7 @@ impl Pane { })? .unwrap_or_else(|| Path::new("").into()); - let abs_path = cx.update(|_, cx| cx.prompt_for_new_path(&start_abs_path))?; + let abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path))?; if let Some(abs_path) = abs_path.await.ok().flatten() { pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))? .await?; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a0f42cec29fd904bd1dd5df4dbbad3e8b60efad0..bb7ba143d6063c4e2b46bb193fb8c249c657934a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1233,7 +1233,7 @@ impl Workspace { } for (pane, item) in dirty_items { let (singleton, project_entry_ids) = - cx.update(|_, cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?; + cx.update(|cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?; if singleton || !project_entry_ids.is_empty() { if let Some(ix) = pane.update(&mut cx, |pane, _| pane.index_for_item(item.as_ref()))? @@ -1307,7 +1307,7 @@ impl Workspace { } else { None }; - cx.update(|_, cx| open_paths(&paths, &app_state, window_to_replace, cx))? + cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))? .await?; Ok(()) }) @@ -1912,7 +1912,7 @@ impl Workspace { let project_item = project.update(cx, |project, cx| project.open_path(path, cx)); cx.spawn(|_, mut cx| async move { let (project_entry_id, project_item) = project_item.await?; - let build_item = cx.update(|_, cx| { + let build_item = cx.update(|cx| { cx.default_global::() .get(&project_item.entity_type()) .ok_or_else(|| anyhow!("no item builder for project item")) @@ -2709,7 +2709,7 @@ impl Workspace { ) -> Result<()> { let this = this.upgrade().context("workspace dropped")?; - let item_builders = cx.update(|_, cx| { + let item_builders = cx.update(|cx| { cx.default_global::() .values() .map(|b| b.0) @@ -2728,7 +2728,7 @@ impl Workspace { Err(anyhow!("missing view variant"))?; } for build_item in &item_builders { - let task = cx.update(|_, cx| { + let task = cx.update(|cx| { build_item(pane.clone(), this.clone(), id, &mut variant, cx) })?; if let Some(task) = task { @@ -3141,7 +3141,7 @@ impl Workspace { center_group = Some((group, active_pane)) } - let mut items_by_project_path = cx.update(|_, cx| { + let mut items_by_project_path = cx.update(|cx| { center_items .unwrap_or_default() .into_iter() @@ -3407,7 +3407,7 @@ fn open_items( let restored_project_paths = restored_items .iter() .filter_map(|item| { - cx.update(|_, cx| item.as_ref()?.project_path(cx)) + cx.update(|cx| item.as_ref()?.project_path(cx)) .ok() .flatten() }) From 9da6b8c7f632664a8f7a5a6f3c22f732da4c25b7 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 20 Jan 2024 14:38:03 -0800 Subject: [PATCH 2/4] Lock down mac os platform type visibility in the rest of GPUI Add documentation to all platform types --- crates/gpui/src/platform.rs | 178 ++++++++++++++++-- crates/gpui/src/platform/app_menu.rs | 38 ++++ crates/gpui/src/platform/keystroke.rs | 34 +++- crates/gpui/src/platform/mac.rs | 14 +- crates/gpui/src/platform/mac/dispatcher.rs | 2 +- crates/gpui/src/platform/mac/display.rs | 7 +- .../gpui/src/platform/mac/display_linker.rs | 14 +- crates/gpui/src/platform/mac/events.rs | 5 +- crates/gpui/src/platform/mac/metal_atlas.rs | 4 +- crates/gpui/src/platform/mac/platform.rs | 14 +- crates/gpui/src/platform/mac/text_system.rs | 4 +- crates/gpui/src/platform/mac/window.rs | 4 +- .../src/platform/mac/window_appearance.rs | 2 +- crates/gpui/src/styled.rs | 6 +- crates/gpui/src/window.rs | 2 +- 15 files changed, 261 insertions(+), 67 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index cbf5908b067de51d6e487aa644a7afbdabb3393d..50cfce5aae4dbf32c30e5486197b62c6cfe5a645 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -1,3 +1,5 @@ +#![deny(missing_docs)] + mod app_menu; mod keystroke; #[cfg(target_os = "macos")] @@ -34,7 +36,7 @@ use uuid::Uuid; pub use app_menu::*; pub use keystroke::*; #[cfg(target_os = "macos")] -pub use mac::*; +pub(crate) use mac::*; #[cfg(any(test, feature = "test-support"))] pub use test::*; use time::UtcOffset; @@ -69,11 +71,10 @@ pub(crate) trait Platform: 'static { fn set_display_link_output_callback( &self, display_id: DisplayId, - callback: Box, + callback: Box, ); fn start_display_link(&self, display_id: DisplayId); fn stop_display_link(&self, display_id: DisplayId); - // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box; fn open_url(&self, url: &str); fn on_open_urls(&self, callback: Box)>); @@ -395,20 +396,50 @@ impl PlatformInputHandler { } } +/// Zed's interface for handling text input from the platform's IME system +/// This is currently a 1:1 exposure of the NSTextInputClient API: +/// +/// pub trait InputHandler: 'static { + /// Get the range of the user's currently selected text, if any + /// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange) + /// + /// Return value is in terms of UTF-16 characters, from 0 to the length of the document fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option>; + + /// Get the range of the currently marked text, if any + /// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange) + /// + /// Return value is in terms of UTF-16 characters, from 0 to the length of the document fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option>; + + /// Get the text for the given document range in UTF-16 characters + /// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring) + /// + /// range_utf16 is in terms of UTF-16 characters fn text_for_range( &mut self, range_utf16: Range, cx: &mut WindowContext, ) -> Option; + + /// Replace the text in the given document range with the given text + /// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext) + /// + /// replacement_range is in terms of UTF-16 characters fn replace_text_in_range( &mut self, replacement_range: Option>, text: &str, cx: &mut WindowContext, ); + + /// Replace the text in the given document range with the given text, + /// and mark the given text as part of of an IME 'composing' state + /// Corresponds to [setMarkedText(_:selectedRange:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438246-setmarkedtext) + /// + /// range_utf16 is in terms of UTF-16 characters + /// new_selected_range is in terms of UTF-16 characters fn replace_and_mark_text_in_range( &mut self, range_utf16: Option>, @@ -416,7 +447,15 @@ pub trait InputHandler: 'static { new_selected_range: Option>, cx: &mut WindowContext, ); + + /// Remove the IME 'composing' state from the document + /// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext) fn unmark_text(&mut self, cx: &mut WindowContext); + + /// Get the bounds of the given document range in screen coordinates + /// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect) + /// + /// This is used for positioning the IME candidate window fn bounds_for_range( &mut self, range_utf16: Range, @@ -424,15 +463,31 @@ pub trait InputHandler: 'static { ) -> Option>; } +/// The variables that can be configured when creating a new window #[derive(Debug)] pub struct WindowOptions { + /// The initial bounds of the window pub bounds: WindowBounds, + + /// The titlebar configuration of the window pub titlebar: Option, + + /// Whether the window should be centered on the screen pub center: bool, + + /// Whether the window should be focused when created pub focus: bool, + + /// Whether the window should be shown when created pub show: bool, + + /// The kind of window to create pub kind: WindowKind, + + /// Whether the window should be movable by the user pub is_movable: bool, + + /// The display to create the window on pub display_id: Option, } @@ -455,46 +510,67 @@ impl Default for WindowOptions { } } +/// The options that can be configured for a window's titlebar #[derive(Debug, Default)] pub struct TitlebarOptions { + /// The initial title of the window pub title: Option, - pub appears_transparent: bool, - pub traffic_light_position: Option>, -} -#[derive(Copy, Clone, Debug)] -pub enum Appearance { - Light, - VibrantLight, - Dark, - VibrantDark, -} + /// Whether the titlebar should appear transparent + pub appears_transparent: bool, -impl Default for Appearance { - fn default() -> Self { - Self::Light - } + /// The position of the macOS traffic light buttons + pub traffic_light_position: Option>, } +/// The kind of window to create #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum WindowKind { + /// A normal application window Normal, + + /// A window that appears above all other windows, usually used for alerts or popups + /// use sparingly! PopUp, } +/// Which bounds algorithm to use for the initial size a window #[derive(Copy, Clone, Debug, PartialEq, Default)] pub enum WindowBounds { + /// The window should be full screen, on macOS this corresponds to the full screen feature Fullscreen, + + /// Make the window as large as the current display's size. #[default] Maximized, + + /// Set the window to the given size in pixels Fixed(Bounds), } +/// The appearance of the window, as defined by the operating system +/// On macOS, this corresponds to named [NSAppearance](https://developer.apple.com/documentation/appkit/nsappearance) +/// values #[derive(Copy, Clone, Debug)] pub enum WindowAppearance { + /// A light appearance + /// + /// on macOS, this corresponds to the `aqua` appearance Light, + + /// A light appearance with vibrant colors + /// + /// on macOS, this corresponds to the `NSAppearanceNameVibrantLight` appearance VibrantLight, + + /// A dark appearance + /// + /// on macOS, this corresponds to the `darkAqua` appearance Dark, + + /// A dark appearance with vibrant colors + /// + /// on macOS, this corresponds to the `NSAppearanceNameVibrantDark` appearance VibrantDark, } @@ -504,40 +580,102 @@ impl Default for WindowAppearance { } } +/// The options that can be configured for a file dialog prompt #[derive(Copy, Clone, Debug)] pub struct PathPromptOptions { + /// Should the prompt allow files to be selected? pub files: bool, + /// Should the prompt allow directories to be selected? pub directories: bool, + /// Should the prompt allow multiple files to be selected? pub multiple: bool, } +/// What kind of prompt styling to show #[derive(Copy, Clone, Debug)] pub enum PromptLevel { + /// A prompt that is shown when the user should be notified of something Info, + + /// A prompt that is shown when the user needs to be warned of a potential problem Warning, + + /// A prompt that is shown when a critical problem has occurred Critical, } /// The style of the cursor (pointer) #[derive(Copy, Clone, Debug)] pub enum CursorStyle { + /// The default cursor Arrow, + + /// A text input cursor + /// corresponds to the CSS cursor value `text` IBeam, + + /// A crosshair cursor + /// corresponds to the CSS cursor value `crosshair` Crosshair, + + /// A closed hand cursor + /// corresponds to the CSS cursor value `grabbing` ClosedHand, + + /// An open hand cursor + /// corresponds to the CSS cursor value `grab` OpenHand, + + /// A pointing hand cursor + /// corresponds to the CSS cursor value `pointer` PointingHand, + + /// A resize left cursor + /// corresponds to the CSS cursor value `w-resize` ResizeLeft, + + /// A resize right cursor + /// corresponds to the CSS cursor value `e-resize` ResizeRight, + + /// A resize cursor to the left and right + /// corresponds to the CSS cursor value `col-resize` ResizeLeftRight, + + /// A resize up cursor + /// corresponds to the CSS cursor value `n-resize` ResizeUp, + + /// A resize down cursor + /// corresponds to the CSS cursor value `s-resize` ResizeDown, + + /// A resize cursor directing up and down + /// corresponds to the CSS cursor value `row-resize` ResizeUpDown, + + /// A cursor indicating that something will dissappear if moved here + /// Does not correspond to a CSS cursor value DisappearingItem, + + /// A text input cursor for vertical layout + /// corresponds to the CSS cursor value `vertical-text` IBeamCursorForVerticalLayout, + + /// A cursor indicating that the operation is not allowed + /// corresponds to the CSS cursor value `not-allowed` OperationNotAllowed, + + /// A cursor indicating that the operation will result in a link + /// corresponds to the CSS cursor value `alias` DragLink, + + /// A cursor indicating that the operation will result in a copy + /// corresponds to the CSS cursor value `copy` DragCopy, + + /// A cursor indicating that the operation will result in a context menu + /// corresponds to the CSS cursor value `context-menu` ContextualMenu, } @@ -547,6 +685,7 @@ impl Default for CursorStyle { } } +/// A datastructure representing a semantic version number #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct SemanticVersion { major: usize, @@ -585,6 +724,7 @@ impl Display for SemanticVersion { } } +/// A clipboard item that should be copied to the clipboard #[derive(Clone, Debug, Eq, PartialEq)] pub struct ClipboardItem { pub(crate) text: String, @@ -592,6 +732,7 @@ pub struct ClipboardItem { } impl ClipboardItem { + /// Create a new clipboard item with the given text pub fn new(text: String) -> Self { Self { text, @@ -599,15 +740,18 @@ impl ClipboardItem { } } + /// Create a new clipboard item with the given text and metadata pub fn with_metadata(mut self, metadata: T) -> Self { self.metadata = Some(serde_json::to_string(&metadata).unwrap()); self } + /// Get the text of the clipboard item pub fn text(&self) -> &String { &self.text } + /// Get the metadata of the clipboard item pub fn metadata(&self) -> Option where T: for<'a> Deserialize<'a>, diff --git a/crates/gpui/src/platform/app_menu.rs b/crates/gpui/src/platform/app_menu.rs index 10fe2cf33ae6d8b830efc92f72bba7f5157de4b9..91fe358931b643ceb4365cd9a5545ca8ee4c8c3f 100644 --- a/crates/gpui/src/platform/app_menu.rs +++ b/crates/gpui/src/platform/app_menu.rs @@ -1,30 +1,49 @@ use crate::{Action, AppContext, Platform}; use util::ResultExt; +/// A menu of the application, either a main menu or a submenu pub struct Menu<'a> { + /// The name of the menu pub name: &'a str, + + /// The items in the menu pub items: Vec>, } +/// The different kinds of items that can be in a menu pub enum MenuItem<'a> { + /// A separator between items Separator, + + /// A submenu Submenu(Menu<'a>), + + /// An action that can be performed Action { + /// The name of this menu item name: &'a str, + + /// the action to perform when this menu item is selected action: Box, + + /// The OS Action that corresponds to this action, if any + /// See [`OsAction`] for more information os_action: Option, }, } impl<'a> MenuItem<'a> { + /// Creates a new menu item that is a separator pub fn separator() -> Self { Self::Separator } + /// Creates a new menu item that is a submenu pub fn submenu(menu: Menu<'a>) -> Self { Self::Submenu(menu) } + /// Creates a new menu item that invokes an action pub fn action(name: &'a str, action: impl Action) -> Self { Self::Action { name, @@ -33,6 +52,7 @@ impl<'a> MenuItem<'a> { } } + /// Creates a new menu item that invokes an action and has an OS action pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self { Self::Action { name, @@ -42,13 +62,31 @@ impl<'a> MenuItem<'a> { } } +// TODO: As part of the global selections refactor, these should +// be moved to GPUI-provided actions that make this association +// without leaking the platform details to GPUI users + +/// OS actions are actions that are recognized by the operating system +/// This allows the operating system to provide specialized behavior for +/// these actions #[derive(Copy, Clone, Eq, PartialEq)] pub enum OsAction { + /// The 'cut' action Cut, + + /// The 'copy' action Copy, + + /// The 'paste' action Paste, + + /// The 'select all' action SelectAll, + + /// The 'undo' action Undo, + + /// The 'redo' action Redo, } diff --git a/crates/gpui/src/platform/keystroke.rs b/crates/gpui/src/platform/keystroke.rs index 64a901789abb688c36c8dd2f2eef9c5fb16a34e8..0db8d0744dbff82ab7844c8a0f9c5677e4c1c919 100644 --- a/crates/gpui/src/platform/keystroke.rs +++ b/crates/gpui/src/platform/keystroke.rs @@ -3,24 +3,31 @@ use serde::Deserialize; use smallvec::SmallVec; use std::fmt::Write; +/// A keystroke and associated metadata generated by the platform #[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)] pub struct Keystroke { + /// the state of the modifier keys at the time the keystroke was generated pub modifiers: Modifiers, + /// key is the character printed on the key that was pressed /// e.g. for option-s, key is "s" pub key: String, + /// ime_key is the character inserted by the IME engine when that key was pressed. /// e.g. for option-s, ime_key is "ß" pub ime_key: Option, } impl Keystroke { - // When matching a key we cannot know whether the user intended to type - // the ime_key or the key. On some non-US keyboards keys we use in our - // bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard), - // and on some keyboards the IME handler converts a sequence of keys into a - // specific character (for example `"` is typed as `" space` on a brazilian keyboard). - pub fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> { + /// When matching a key we cannot know whether the user intended to type + /// the ime_key or the key itself. On some non-US keyboards keys we use in our + /// bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard), + /// and on some keyboards the IME handler converts a sequence of keys into a + /// specific character (for example `"` is typed as `" space` on a brazilian keyboard). + /// + /// This method generates a list of potential keystroke candidates that could be matched + /// against when resolving a keybinding. + pub(crate) fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> { let mut possibilities = SmallVec::new(); match self.ime_key.as_ref() { None => possibilities.push(self.clone()), @@ -47,7 +54,7 @@ impl Keystroke { /// key syntax is: /// [ctrl-][alt-][shift-][cmd-][fn-]key[->ime_key] - /// ime_key is only used for generating test events, + /// ime_key syntax is only used for generating test events, /// when matching a key with an ime_key set will be matched without it. pub fn parse(source: &str) -> anyhow::Result { let mut control = false; @@ -135,16 +142,29 @@ impl std::fmt::Display for Keystroke { } } +/// The state of the modifier keys at some point in time #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)] pub struct Modifiers { + /// The control key pub control: bool, + + /// The alt key + /// Sometimes also known as the 'meta' key pub alt: bool, + + /// The shift key pub shift: bool, + + /// The command key, on macos + /// the windows key, on windows pub command: bool, + + /// The function key pub function: bool, } impl Modifiers { + /// Returns true if any modifier key is pressed pub fn modified(&self) -> bool { self.control || self.alt || self.shift || self.command || self.function } diff --git a/crates/gpui/src/platform/mac.rs b/crates/gpui/src/platform/mac.rs index 3cc74a968399dcc0fffcf8c795137262e66df8de..2194ae41e791f135de34e312653f2c2fc13489d9 100644 --- a/crates/gpui/src/platform/mac.rs +++ b/crates/gpui/src/platform/mac.rs @@ -21,13 +21,13 @@ use metal_renderer::*; use objc::runtime::{BOOL, NO, YES}; use std::ops::Range; -pub use dispatcher::*; -pub use display::*; -pub use display_linker::*; -pub use metal_atlas::*; -pub use platform::*; -pub use text_system::*; -pub use window::*; +pub(crate) use dispatcher::*; +pub(crate) use display::*; +pub(crate) use display_linker::*; +pub(crate) use metal_atlas::*; +pub(crate) use platform::*; +pub(crate) use text_system::*; +pub(crate) use window::*; trait BoolExt { fn to_objc(self) -> BOOL; diff --git a/crates/gpui/src/platform/mac/dispatcher.rs b/crates/gpui/src/platform/mac/dispatcher.rs index 18e361885e71d58a8328bb6dc38c1b6b2d286041..72daa8c4404f52836d45ad39517b9b4bb3db57dd 100644 --- a/crates/gpui/src/platform/mac/dispatcher.rs +++ b/crates/gpui/src/platform/mac/dispatcher.rs @@ -24,7 +24,7 @@ pub(crate) fn dispatch_get_main_queue() -> dispatch_queue_t { unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t } } -pub struct MacDispatcher { +pub(crate) struct MacDispatcher { parker: Arc>, } diff --git a/crates/gpui/src/platform/mac/display.rs b/crates/gpui/src/platform/mac/display.rs index 1f6023ed147364ca891a8b90f6db870e21420d51..d5eb089300cdae400d03075f0288b0f1b57b054c 100644 --- a/crates/gpui/src/platform/mac/display.rs +++ b/crates/gpui/src/platform/mac/display.rs @@ -11,7 +11,7 @@ use objc::{msg_send, sel, sel_impl}; use uuid::Uuid; #[derive(Debug)] -pub struct MacDisplay(pub(crate) CGDirectDisplayID); +pub(crate) struct MacDisplay(pub(crate) CGDirectDisplayID); unsafe impl Send for MacDisplay {} @@ -21,11 +21,6 @@ impl MacDisplay { Self::all().find(|screen| screen.id() == id) } - /// Get the screen with the given persistent [`Uuid`]. - pub fn find_by_uuid(uuid: Uuid) -> Option { - Self::all().find(|screen| screen.uuid().ok() == Some(uuid)) - } - /// Get the primary screen - the one with the menu bar, and whose bottom left /// corner is at the origin of the AppKit coordinate system. pub fn primary() -> Self { diff --git a/crates/gpui/src/platform/mac/display_linker.rs b/crates/gpui/src/platform/mac/display_linker.rs index 8f1b233046fd85ffc4d3b875e73e5f891841c75a..e25487ec0bb623cbc6714cf1e06d83343f25f7fd 100644 --- a/crates/gpui/src/platform/mac/display_linker.rs +++ b/crates/gpui/src/platform/mac/display_linker.rs @@ -7,8 +7,6 @@ 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 { links: HashMap, @@ -27,13 +25,13 @@ impl MacDisplayLinker { } } -type OutputCallback = Mutex>; +type OutputCallback = Mutex>; impl MacDisplayLinker { pub fn set_output_callback( &mut self, display_id: DisplayId, - output_callback: Box, + output_callback: Box, ) { if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } { let callback = Arc::new(Mutex::new(output_callback)); @@ -81,11 +79,11 @@ unsafe extern "C" fn trampoline( _flags_out: *mut i64, user_data: *mut c_void, ) -> i32 { - if let Some((current_time, output_time)) = current_time.as_ref().zip(output_time.as_ref()) { + if let Some((_current_time, _output_time)) = current_time.as_ref().zip(output_time.as_ref()) { let output_callback: Weak = Weak::from_raw(user_data as *mut OutputCallback); if let Some(output_callback) = output_callback.upgrade() { - (output_callback.lock())(current_time, output_time) + (output_callback.lock())() } mem::forget(output_callback); } @@ -126,7 +124,7 @@ mod sys { #[repr(C)] #[derive(Clone, Copy)] - pub struct CVTimeStamp { + pub(crate) struct CVTimeStamp { pub version: u32, pub video_time_scale: i32, pub video_time: i64, @@ -154,7 +152,7 @@ mod sys { #[repr(C)] #[derive(Clone, Copy, Default)] - pub struct CVSMPTETime { + pub(crate) struct CVSMPTETime { pub subframes: i16, pub subframe_divisor: i16, pub counter: u32, diff --git a/crates/gpui/src/platform/mac/events.rs b/crates/gpui/src/platform/mac/events.rs index f84833d3cbb1678c1ee1ee9b0c1793bb9df8bae0..4653ce89b45f6b79f538d98ac32c498f684a5c86 100644 --- a/crates/gpui/src/platform/mac/events.rs +++ b/crates/gpui/src/platform/mac/events.rs @@ -83,7 +83,10 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers { } impl PlatformInput { - pub unsafe fn from_native(native_event: id, window_height: Option) -> Option { + pub(crate) unsafe fn from_native( + native_event: id, + window_height: Option, + ) -> Option { let event_type = native_event.eventType(); // Filter out event types that aren't in the NSEventType enum. diff --git a/crates/gpui/src/platform/mac/metal_atlas.rs b/crates/gpui/src/platform/mac/metal_atlas.rs index d3caeba5222e6a4739fc63ef54358eb3589debbf..95f78a446539de117938113f010201afd62cbb71 100644 --- a/crates/gpui/src/platform/mac/metal_atlas.rs +++ b/crates/gpui/src/platform/mac/metal_atlas.rs @@ -10,10 +10,10 @@ use metal::Device; use parking_lot::Mutex; use std::borrow::Cow; -pub struct MetalAtlas(Mutex); +pub(crate) struct MetalAtlas(Mutex); impl MetalAtlas { - pub fn new(device: Device) -> Self { + pub(crate) fn new(device: Device) -> Self { MetalAtlas(Mutex::new(MetalAtlasState { device: AssertSend(device), monochrome_textures: Default::default(), diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 499ac0b59104d9ab0b8204a95c01371a228a6b47..e4a688c2fdb35c1dd3adec829fd66a4baf1e4441 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -3,7 +3,7 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, - PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions, + PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions, }; use anyhow::anyhow; use block::ConcreteBlock; @@ -139,9 +139,9 @@ unsafe fn build_classes() { } } -pub struct MacPlatform(Mutex); +pub(crate) struct MacPlatform(Mutex); -pub struct MacPlatformState { +pub(crate) struct MacPlatformState { background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, text_system: Arc, @@ -169,7 +169,7 @@ impl Default for MacPlatform { } impl MacPlatform { - pub fn new() -> Self { + pub(crate) fn new() -> Self { let dispatcher = Arc::new(MacDispatcher::new()); Self(Mutex::new(MacPlatformState { background_executor: BackgroundExecutor::new(dispatcher.clone()), @@ -475,10 +475,6 @@ impl Platform for MacPlatform { } } - // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box { - // Box::new(StatusItem::add(self.fonts())) - // } - fn displays(&self) -> Vec> { MacDisplay::all() .map(|screen| Rc::new(screen) as Rc<_>) @@ -504,7 +500,7 @@ impl Platform for MacPlatform { fn set_display_link_output_callback( &self, display_id: DisplayId, - callback: Box, + callback: Box, ) { self.0 .lock() diff --git a/crates/gpui/src/platform/mac/text_system.rs b/crates/gpui/src/platform/mac/text_system.rs index d11efa902ae7c270ff0331b74b1860032ff5f212..ba434026f65f9573acaf73c14c6abb21d6448b35 100644 --- a/crates/gpui/src/platform/mac/text_system.rs +++ b/crates/gpui/src/platform/mac/text_system.rs @@ -41,7 +41,7 @@ use super::open_type; #[allow(non_upper_case_globals)] const kCGImageAlphaOnly: u32 = 7; -pub struct MacTextSystem(RwLock); +pub(crate) struct MacTextSystem(RwLock); struct MacTextSystemState { memory_source: MemSource, @@ -54,7 +54,7 @@ struct MacTextSystemState { } impl MacTextSystem { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self(RwLock::new(MacTextSystemState { memory_source: MemSource::empty(), system_source: SystemSource::new(), diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index fcfd02aa411f3cc8d6fe2fc422e8c1ba56e41baf..1ef5d346cc379c0b025f8a148189082731b564c8 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -220,7 +220,7 @@ unsafe fn build_classes() { }; } -pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point { +pub(crate) fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point { point( px(position.x as f32), // MacOS screen coordinates are relative to bottom left @@ -446,7 +446,7 @@ impl MacWindowState { unsafe impl Send for MacWindowState {} -pub struct MacWindow(Arc>); +pub(crate) struct MacWindow(Arc>); impl MacWindow { pub fn open( diff --git a/crates/gpui/src/platform/mac/window_appearance.rs b/crates/gpui/src/platform/mac/window_appearance.rs index 2edc896289ef8056424a0399d38ff937155adad2..0a5df85a446859880a2a04e27b77bedcf44cb925 100644 --- a/crates/gpui/src/platform/mac/window_appearance.rs +++ b/crates/gpui/src/platform/mac/window_appearance.rs @@ -8,7 +8,7 @@ use objc::{msg_send, sel, sel_impl}; use std::ffi::CStr; impl WindowAppearance { - pub unsafe fn from_native(appearance: id) -> Self { + pub(crate) unsafe fn from_native(appearance: id) -> Self { let name: id = msg_send![appearance, name]; if name == NSAppearanceNameVibrantLight { Self::VibrantLight diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index 535cc3e0536bdb1db084016765e1aeffb4b62efa..ee27741642b44b6b3c3e8dc819313dc91b079a3c 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -1,11 +1,11 @@ use crate::{ self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle, - DefiniteLength, Display, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length, - Position, SharedString, StyleRefinement, Visibility, WhiteSpace, + DefiniteLength, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length, Position, + SharedString, StyleRefinement, Visibility, WhiteSpace, }; use crate::{BoxShadow, TextStyleRefinement}; use smallvec::{smallvec, SmallVec}; -use taffy::style::Overflow; +use taffy::style::{Display, Overflow}; pub trait Styled: Sized { fn style(&mut self) -> &mut StyleRefinement; diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 81bd8b55b30a14b368581fcd9506bd44fe4c6696..1a7a35bf57f23322c103bff9a0ed245687087d72 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -737,7 +737,7 @@ impl<'a> WindowContext<'a> { let (tx, mut rx) = mpsc::unbounded::<()>(); self.platform.set_display_link_output_callback( display_id, - Box::new(move |_current_time, _output_time| _ = tx.unbounded_send(())), + Box::new(move || _ = tx.unbounded_send(())), ); let consumer_task = self.app.spawn(|cx| async move { From 017661818dd2eb759b5269f619547d7adf90b85a Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 20 Jan 2024 14:42:38 -0800 Subject: [PATCH 3/4] Fix typo --- crates/gpui/src/platform.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 50cfce5aae4dbf32c30e5486197b62c6cfe5a645..f368b3c35091db7b65a3450637c328b8e37ed406 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -654,7 +654,7 @@ pub enum CursorStyle { /// corresponds to the CSS cursor value `row-resize` ResizeUpDown, - /// A cursor indicating that something will dissappear if moved here + /// A cursor indicating that something will disappear if moved here /// Does not correspond to a CSS cursor value DisappearingItem, From 4184686e8dadfacc295530bf15dcb8227785892f Mon Sep 17 00:00:00 2001 From: Mikayla Date: Sat, 20 Jan 2024 14:56:50 -0800 Subject: [PATCH 4/4] Lock down test platform implementation --- crates/gpui/src/app/test_context.rs | 2 +- crates/gpui/src/platform.rs | 2 +- crates/gpui/src/platform/test.rs | 8 ++++---- crates/gpui/src/platform/test/dispatcher.rs | 1 + crates/gpui/src/platform/test/display.rs | 2 +- crates/gpui/src/platform/test/platform.rs | 17 +++-------------- crates/gpui/src/platform/test/window.rs | 17 +++++++---------- crates/vim/src/normal/repeat.rs | 2 +- crates/zed/src/zed.rs | 2 +- 9 files changed, 20 insertions(+), 33 deletions(-) diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 66f74a91b38871da18829ce9539f48d3e397fcbb..139d52b6404d3ae022c0611c607cd757934467b8 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -352,7 +352,7 @@ impl TestAppContext { } /// Returns the `TestWindow` backing the given handle. - pub fn test_window(&self, window: AnyWindowHandle) -> TestWindow { + pub(crate) fn test_window(&self, window: AnyWindowHandle) -> TestWindow { self.app .borrow_mut() .windows diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index f368b3c35091db7b65a3450637c328b8e37ed406..feb0e2e55141b6c152122034b040f70c7ef717ae 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -38,7 +38,7 @@ pub use keystroke::*; #[cfg(target_os = "macos")] pub(crate) use mac::*; #[cfg(any(test, feature = "test-support"))] -pub use test::*; +pub(crate) use test::*; use time::UtcOffset; #[cfg(target_os = "macos")] diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index acc8ffe41ce423e7b16fd9e17e86ef3b5f71dfd4..d17739239eede2e3b0aa2d5f91028dadd94d3bdd 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -3,7 +3,7 @@ mod display; mod platform; mod window; -pub use dispatcher::*; -pub use display::*; -pub use platform::*; -pub use window::*; +pub(crate) use dispatcher::*; +pub(crate) use display::*; +pub(crate) use platform::*; +pub(crate) use window::*; diff --git a/crates/gpui/src/platform/test/dispatcher.rs b/crates/gpui/src/platform/test/dispatcher.rs index 9023627d1e2d777188d1b5bc96f89cabc4fbe903..9c59fce48c43ba06d5887ea87b4a0d5cd15ae4f8 100644 --- a/crates/gpui/src/platform/test/dispatcher.rs +++ b/crates/gpui/src/platform/test/dispatcher.rs @@ -18,6 +18,7 @@ use util::post_inc; #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct TestDispatcherId(usize); +#[doc(hidden)] pub struct TestDispatcher { id: TestDispatcherId, state: Arc>, diff --git a/crates/gpui/src/platform/test/display.rs b/crates/gpui/src/platform/test/display.rs index 838d600147b86e62d2f8197cf695590dbdefd750..488731607084ad1647eec53e151299e97c04c424 100644 --- a/crates/gpui/src/platform/test/display.rs +++ b/crates/gpui/src/platform/test/display.rs @@ -3,7 +3,7 @@ use anyhow::{Ok, Result}; use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point}; #[derive(Debug)] -pub struct TestDisplay { +pub(crate) struct TestDisplay { id: DisplayId, uuid: uuid::Uuid, bounds: Bounds, diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index f5e2170b28acdeae304495172c7b6bbac568bcf4..9a33b4b3b58bf67591746d1c1f000089db315098 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -15,7 +15,7 @@ use std::{ }; /// TestPlatform implements the Platform trait for use in tests. -pub struct TestPlatform { +pub(crate) struct TestPlatform { background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, @@ -178,20 +178,9 @@ impl Platform for TestPlatform { fn set_display_link_output_callback( &self, _display_id: DisplayId, - mut callback: Box, + mut callback: Box, ) { - 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(×tamp, ×tamp) + callback() } fn start_display_link(&self, _display_id: DisplayId) {} diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index 2f080bd7098bd42cf309c52e86df754e0d153a35..095af9b094a9f93f8d3e7660e8088ee6cf220f49 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -10,7 +10,7 @@ use std::{ sync::{self, Arc}, }; -pub struct TestWindowState { +pub(crate) struct TestWindowState { pub(crate) bounds: WindowBounds, pub(crate) handle: AnyWindowHandle, display: Rc, @@ -23,11 +23,11 @@ pub struct TestWindowState { active_status_change_callback: Option>, resize_callback: Option, f32)>>, moved_callback: Option>, - input_handler: Option>, + input_handler: Option, } #[derive(Clone)] -pub struct TestWindow(pub(crate) Arc>); +pub(crate) struct TestWindow(pub(crate) Arc>); impl TestWindow { pub fn new( @@ -117,9 +117,6 @@ impl TestWindow { self.0.lock().input_handler = Some(input_handler); } - pub fn edited(&self) -> bool { - self.0.lock().edited - } } impl PlatformWindow for TestWindow { @@ -163,11 +160,11 @@ impl PlatformWindow for TestWindow { self } - fn set_input_handler(&mut self, input_handler: Box) { + fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { self.0.lock().input_handler = Some(input_handler); } - fn take_input_handler(&mut self) -> Option> { + fn take_input_handler(&mut self) -> Option { self.0.lock().input_handler.take() } @@ -269,12 +266,12 @@ impl PlatformWindow for TestWindow { } } -pub struct TestAtlasState { +pub(crate) struct TestAtlasState { next_id: u32, tiles: HashMap, } -pub struct TestAtlas(Mutex); +pub(crate) struct TestAtlas(Mutex); impl TestAtlas { pub fn new() -> Self { diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 796cfce7a3c5bb3091996d0a8c72d76c4379abfa..4850151d94d09996474ae000ed4d5255141ac33f 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -202,7 +202,7 @@ mod test { use futures::StreamExt; use indoc::indoc; - use gpui::InputHandler; + use gpui::ViewInputHandler; use crate::{ state::Mode, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 112c219d2d728b9a21b36d05554358d47a2b0aa2..8010f3c1b9a512bbfa1d370372f750bb21d5ea61 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -875,7 +875,7 @@ mod tests { let window = cx.update(|cx| cx.windows()[0].downcast::().unwrap()); let window_is_edited = |window: WindowHandle, cx: &mut TestAppContext| { - cx.test_window(window.into()).edited() + cx.update(|cx| window.read(cx).unwrap().is_edited()) }; let pane = window .read_with(cx, |workspace, _| workspace.active_pane().clone())