Document the gpui platform code (#4180)

In the process I also:
- Made the AsyncWindowContext slightly more ergonomic.
- Refactored the input handler traits to enable easy, non-view input
handlers
- Locked down the visibility on all mac-specific GPUI code
- Documented all remaining, public types

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki 2024-01-20 15:11:03 -08:00 committed by GitHub
commit 76d38525ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 511 additions and 249 deletions

View File

@ -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);
}

View File

@ -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(

View File

@ -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::<Vec<_>>();
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<usize>,

View File

@ -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 {

View File

@ -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)
})

View File

@ -213,7 +213,12 @@ impl AsyncWindowContext {
}
/// A convenience method for [WindowContext::update()]
pub fn update<R>(
pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
self.app.update_window(self.window, |_, cx| update(cx))
}
/// A convenience method for [WindowContext::update()]
pub fn update_root<R>(
&mut self,
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
) -> Result<R> {

View File

@ -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

View File

@ -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 {

View File

@ -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<V>`].
/// 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<usize>, cx: &mut ViewContext<Self>)
-> Option<String>;
fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
@ -39,7 +37,6 @@ pub trait InputHandler: 'static + Sized {
pub struct ElementInputHandler<V> {
view: View<V>,
element_bounds: Bounds<Pixels>,
cx: AsyncWindowContext,
}
impl<V: 'static> ElementInputHandler<V> {
@ -47,45 +44,42 @@ impl<V: 'static> ElementInputHandler<V> {
/// containing view.
///
/// [element_paint]: crate::Element::paint
pub fn new(element_bounds: Bounds<Pixels>, view: View<V>, cx: &mut WindowContext) -> Self {
pub fn new(element_bounds: Bounds<Pixels>, view: View<V>) -> Self {
ElementInputHandler {
view,
element_bounds,
cx: cx.to_async(),
}
}
}
impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
fn selected_text_range(&mut self) -> Option<Range<usize>> {
impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
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<Range<usize>> {
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<Range<usize>> {
self.view.update(cx, |view, cx| view.marked_text_range(cx))
}
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
fn text_for_range(
&mut self,
range_utf16: Range<usize>,
cx: &mut WindowContext,
) -> Option<String> {
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<Range<usize>>, 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<Range<usize>>,
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<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
range_utf16: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
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<usize>) -> Option<Bounds<Pixels>> {
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<usize>,
cx: &mut WindowContext,
) -> Option<Bounds<Pixels>> {
self.view.update(cx, |view, cx| {
view.bounds_for_range(range_utf16, self.element_bounds, cx)
})
}
}

View File

@ -1,3 +1,5 @@
#![deny(missing_docs)]
mod app_menu;
mod keystroke;
#[cfg(target_os = "macos")]
@ -6,10 +8,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;
@ -34,9 +36,9 @@ 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::*;
pub(crate) use test::*;
use time::UtcOffset;
#[cfg(target_os = "macos")]
@ -69,11 +71,10 @@ pub(crate) trait Platform: 'static {
fn set_display_link_output_callback(
&self,
display_id: DisplayId,
callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
callback: Box<dyn FnMut() + Send>,
);
fn start_display_link(&self, display_id: DisplayId);
fn stop_display_link(&self, display_id: DisplayId);
// fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn PlatformWindow>;
fn open_url(&self, url: &str);
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
@ -149,8 +150,8 @@ pub(crate) trait PlatformWindow {
fn mouse_position(&self) -> Point<Pixels>;
fn modifiers(&self) -> Modifiers;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
fn activate(&self);
fn set_title(&mut self, title: &str);
@ -325,30 +326,168 @@ impl From<TileId> for etagere::AllocId {
}
}
pub trait PlatformInputHandler: 'static {
fn selected_text_range(&mut self) -> Option<Range<usize>>;
fn marked_text_range(&mut self) -> Option<Range<usize>>;
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String>;
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
pub(crate) struct PlatformInputHandler {
cx: AsyncWindowContext,
handler: Box<dyn InputHandler>,
}
impl PlatformInputHandler {
pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
Self { cx, handler }
}
fn selected_text_range(&mut self) -> Option<Range<usize>> {
self.cx
.update(|cx| self.handler.selected_text_range(cx))
.ok()
.flatten()
}
fn marked_text_range(&mut self) -> Option<Range<usize>> {
self.cx
.update(|cx| self.handler.marked_text_range(cx))
.ok()
.flatten()
}
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
self.cx
.update(|cx| self.handler.text_for_range(range_utf16, cx))
.ok()
.flatten()
}
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, 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<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
);
fn unmark_text(&mut self);
fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
) {
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<usize>) -> Option<Bounds<Pixels>> {
self.cx
.update(|cx| self.handler.bounds_for_range(range_utf16, cx))
.ok()
.flatten()
}
}
/// Zed's interface for handling text input from the platform's IME system
/// This is currently a 1:1 exposure of the NSTextInputClient API:
///
/// <https://developer.apple.com/documentation/appkit/nstextinputclient>
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<Range<usize>>;
/// 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<Range<usize>>;
/// 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<usize>,
cx: &mut WindowContext,
) -> Option<String>;
/// 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<Range<usize>>,
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<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
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<usize>,
cx: &mut WindowContext,
) -> Option<Bounds<Pixels>>;
}
/// 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<TitlebarOptions>,
/// 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<DisplayId>,
}
@ -371,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<SharedString>,
/// Whether the titlebar should appear transparent
pub appears_transparent: bool,
/// The position of the macOS traffic light buttons
pub traffic_light_position: Option<Point<Pixels>>,
}
#[derive(Copy, Clone, Debug)]
pub enum Appearance {
Light,
VibrantLight,
Dark,
VibrantDark,
}
impl Default for Appearance {
fn default() -> Self {
Self::Light
}
}
/// 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<GlobalPixels>),
}
/// 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,
}
@ -420,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 disappear 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,
}
@ -463,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,
@ -501,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,
@ -508,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,
@ -515,15 +740,18 @@ impl ClipboardItem {
}
}
/// Create a new clipboard item with the given text and metadata
pub fn with_metadata<T: Serialize>(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<T>(&self) -> Option<T>
where
T: for<'a> Deserialize<'a>,

View File

@ -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<MenuItem<'a>>,
}
/// 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<dyn Action>,
/// The OS Action that corresponds to this action, if any
/// See [`OsAction`] for more information
os_action: Option<OsAction>,
},
}
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,
}

View File

@ -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<String>,
}
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<Self> {
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
}

View File

@ -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;

View File

@ -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<Mutex<Parker>>,
}

View File

@ -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> {
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 {

View File

@ -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<DisplayId, MacDisplayLink>,
@ -27,13 +25,13 @@ impl MacDisplayLinker {
}
}
type OutputCallback = Mutex<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>>;
type OutputCallback = Mutex<Box<dyn FnMut() + Send>>;
impl MacDisplayLinker {
pub fn set_output_callback(
&mut self,
display_id: DisplayId,
output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
output_callback: Box<dyn FnMut() + Send>,
) {
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<OutputCallback> =
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,

View File

@ -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<Pixels>) -> Option<Self> {
pub(crate) unsafe fn from_native(
native_event: id,
window_height: Option<Pixels>,
) -> Option<Self> {
let event_type = native_event.eventType();
// Filter out event types that aren't in the NSEventType enum.

View File

@ -10,10 +10,10 @@ use metal::Device;
use parking_lot::Mutex;
use std::borrow::Cow;
pub struct MetalAtlas(Mutex<MetalAtlasState>);
pub(crate) struct MetalAtlas(Mutex<MetalAtlasState>);
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(),

View File

@ -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<MacPlatformState>);
pub(crate) struct MacPlatform(Mutex<MacPlatformState>);
pub struct MacPlatformState {
pub(crate) struct MacPlatformState {
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
text_system: Arc<MacTextSystem>,
@ -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<dyn platform::Window> {
// Box::new(StatusItem::add(self.fonts()))
// }
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
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<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
callback: Box<dyn FnMut() + Send>,
) {
self.0
.lock()

View File

@ -41,7 +41,7 @@ use super::open_type;
#[allow(non_upper_case_globals)]
const kCGImageAlphaOnly: u32 = 7;
pub struct MacTextSystem(RwLock<MacTextSystemState>);
pub(crate) struct MacTextSystem(RwLock<MacTextSystemState>);
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(),

View File

@ -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;
@ -220,7 +220,7 @@ unsafe fn build_classes() {
};
}
pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
pub(crate) fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
point(
px(position.x as f32),
// MacOS screen coordinates are relative to bottom left
@ -327,7 +327,7 @@ struct MacWindowState {
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
close_callback: Option<Box<dyn FnOnce()>>,
appearance_changed_callback: Option<Box<dyn FnMut()>>,
input_handler: Option<Box<dyn PlatformInputHandler>>,
input_handler: Option<PlatformInputHandler>,
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
last_key_equivalent: Option<KeyDownEvent>,
synthetic_drag_counter: usize,
@ -446,7 +446,7 @@ impl MacWindowState {
unsafe impl Send for MacWindowState {}
pub struct MacWindow(Arc<Mutex<MacWindowState>>);
pub(crate) struct MacWindow(Arc<Mutex<MacWindowState>>);
impl MacWindow {
pub fn open(
@ -764,11 +764,11 @@ impl PlatformWindow for MacWindow {
self
}
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
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<Box<dyn PlatformInputHandler>> {
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
self.0.as_ref().lock().input_handler.take()
}
@ -1761,13 +1761,13 @@ fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id)
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
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 {

View File

@ -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

View File

@ -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::*;

View File

@ -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<Mutex<TestDispatcherState>>,

View File

@ -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<GlobalPixels>,

View File

@ -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<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
mut callback: Box<dyn FnMut() + Send>,
) {
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)
callback()
}
fn start_display_link(&self, _display_id: DisplayId) {}

View File

@ -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<dyn PlatformDisplay>,
@ -23,11 +23,11 @@ pub struct TestWindowState {
active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
moved_callback: Option<Box<dyn FnMut()>>,
input_handler: Option<Box<dyn PlatformInputHandler>>,
input_handler: Option<PlatformInputHandler>,
}
#[derive(Clone)]
pub struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
pub(crate) struct TestWindow(pub(crate) Arc<Mutex<TestWindowState>>);
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<dyn crate::PlatformInputHandler>) {
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.0.lock().input_handler = Some(input_handler);
}
fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
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<AtlasKey, AtlasTile>,
}
pub struct TestAtlas(Mutex<TestAtlasState>);
pub(crate) struct TestAtlas(Mutex<TestAtlasState>);
impl TestAtlas {
pub fn new() -> Self {

View File

@ -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;

View File

@ -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<Box<dyn PlatformInputHandler>>,
handler: Option<PlatformInputHandler>,
}
struct TooltipRequest {
@ -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 {
@ -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) {

View File

@ -102,7 +102,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, 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

View File

@ -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<Terminal>,
workspace: WeakView<Workspace>,
cursor_bounds: Option<Bounds<Pixels>>,
}
impl PlatformInputHandler for TerminalInputHandler {
fn selected_text_range(&mut self) -> Option<std::ops::Range<usize>> {
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<std::ops::Range<usize>> {
if self
.terminal
.read(cx)
.last_content
.mode
.contains(TermMode::ALT_SCREEN)
{
None
} else {
Some(0..0)
}
}
fn marked_text_range(&mut self) -> Option<std::ops::Range<usize>> {
fn marked_text_range(&mut self, _: &mut WindowContext) -> Option<std::ops::Range<usize>> {
None
}
fn text_for_range(&mut self, _: std::ops::Range<usize>) -> Option<String> {
fn text_for_range(
&mut self,
_: std::ops::Range<usize>,
_: &mut WindowContext,
) -> Option<String> {
None
}
@ -876,19 +873,16 @@ impl PlatformInputHandler for TerminalInputHandler {
&mut self,
_replacement_range: Option<std::ops::Range<usize>>,
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<std::ops::Range<usize>>,
_new_text: &str,
_new_selected_range: Option<std::ops::Range<usize>>,
_: &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<usize>) -> Option<Bounds<Pixels>> {
fn bounds_for_range(
&mut self,
_range_utf16: std::ops::Range<usize>,
_: &mut WindowContext,
) -> Option<Bounds<Pixels>> {
self.cursor_bounds
}
}

View File

@ -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()

View File

@ -202,7 +202,7 @@ mod test {
use futures::StreamExt;
use indoc::indoc;
use gpui::InputHandler;
use gpui::ViewInputHandler;
use crate::{
state::Mode,

View File

@ -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>() {
workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
}

View File

@ -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?;

View File

@ -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::<ProjectItemBuilders>()
.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::<FollowableItemBuilders>()
.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()
})

View File

@ -875,7 +875,7 @@ mod tests {
let window = cx.update(|cx| cx.windows()[0].downcast::<Workspace>().unwrap());
let window_is_edited = |window: WindowHandle<Workspace>, 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())