Make platform input handler private
Automatically record the context on non-view input handlers Simplify the async window context update() method
This commit is contained in:
parent
0858db9ebb
commit
33105486aa
17 changed files with 229 additions and 148 deletions
|
@ -311,7 +311,7 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
let action = command.action;
|
let action = command.action;
|
||||||
cx.focus(&self.previous_focus_handle);
|
cx.focus(&self.previous_focus_handle);
|
||||||
cx.window_context()
|
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);
|
.detach_and_log_err(cx);
|
||||||
self.dismissed(cx);
|
self.dismissed(cx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,7 +355,7 @@ fn initiate_sign_in(cx: &mut WindowContext) {
|
||||||
|
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
task.await;
|
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
|
workspace
|
||||||
.update(&mut cx, |workspace, cx| match copilot.read(cx).status() {
|
.update(&mut cx, |workspace, cx| match copilot.read(cx).status() {
|
||||||
Status::Authorized => workspace.show_toast(
|
Status::Authorized => workspace.show_toast(
|
||||||
|
|
|
@ -57,9 +57,10 @@ use gpui::{
|
||||||
div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
|
div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
|
||||||
AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
|
AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
|
||||||
DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
|
DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight,
|
||||||
HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model, MouseButton,
|
HighlightStyle, Hsla, InteractiveText, KeyContext, Model, MouseButton, ParentElement, Pixels,
|
||||||
ParentElement, Pixels, Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
|
Render, SharedString, Styled, StyledText, Subscription, Task, TextStyle,
|
||||||
UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
|
UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakView,
|
||||||
|
WhiteSpace, WindowContext,
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
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 replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
|
||||||
|
|
||||||
let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
|
let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
|
||||||
cx.update(|_, cx| {
|
cx.update(|cx| {
|
||||||
entries.sort_unstable_by_key(|(buffer, _)| {
|
entries.sort_unstable_by_key(|(buffer, _)| {
|
||||||
buffer.read(cx).file().map(|f| f.path().clone())
|
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(
|
fn text_for_range(
|
||||||
&mut self,
|
&mut self,
|
||||||
range_utf16: Range<usize>,
|
range_utf16: Range<usize>,
|
||||||
|
|
|
@ -2951,9 +2951,10 @@ impl Element for EditorElement {
|
||||||
self.register_key_listeners(cx);
|
self.register_key_listeners(cx);
|
||||||
|
|
||||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||||
let input_handler =
|
cx.handle_input(
|
||||||
ElementInputHandler::new(bounds, self.editor.clone(), cx);
|
&focus_handle,
|
||||||
cx.handle_input(&focus_handle, input_handler);
|
ElementInputHandler::new(bounds, self.editor.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
|
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
|
||||||
if layout.gutter_size.width > Pixels::ZERO {
|
if layout.gutter_size.width > Pixels::ZERO {
|
||||||
|
|
|
@ -247,7 +247,7 @@ fn show_hover(
|
||||||
};
|
};
|
||||||
|
|
||||||
// query the LSP for hover info
|
// query the LSP for hover info
|
||||||
let hover_request = cx.update(|_, cx| {
|
let hover_request = cx.update(|cx| {
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
project.hover(&buffer, buffer_position, cx)
|
project.hover(&buffer, buffer_position, cx)
|
||||||
})
|
})
|
||||||
|
|
|
@ -213,7 +213,12 @@ impl AsyncWindowContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A convenience method for [WindowContext::update()]
|
/// 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,
|
&mut self,
|
||||||
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
|
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
|
||||||
) -> Result<R> {
|
) -> Result<R> {
|
||||||
|
|
|
@ -1420,7 +1420,7 @@ impl Interactivity {
|
||||||
|
|
||||||
move |mut cx| async move {
|
move |mut cx| async move {
|
||||||
cx.background_executor().timer(TOOLTIP_DELAY).await;
|
cx.background_executor().timer(TOOLTIP_DELAY).await;
|
||||||
cx.update(|_, cx| {
|
cx.update(|cx| {
|
||||||
active_tooltip.borrow_mut().replace(
|
active_tooltip.borrow_mut().replace(
|
||||||
ActiveTooltip {
|
ActiveTooltip {
|
||||||
tooltip: Some(AnyTooltip {
|
tooltip: Some(AnyTooltip {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use crate::{
|
use crate::{Bounds, InputHandler, Pixels, View, ViewContext, WindowContext};
|
||||||
AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext, WindowContext,
|
|
||||||
};
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
/// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
|
/// 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>`].
|
/// 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`].
|
/// 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>)
|
fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
|
||||||
-> Option<String>;
|
-> Option<String>;
|
||||||
fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
|
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> {
|
pub struct ElementInputHandler<V> {
|
||||||
view: View<V>,
|
view: View<V>,
|
||||||
element_bounds: Bounds<Pixels>,
|
element_bounds: Bounds<Pixels>,
|
||||||
cx: AsyncWindowContext,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> ElementInputHandler<V> {
|
impl<V: 'static> ElementInputHandler<V> {
|
||||||
|
@ -47,45 +44,42 @@ impl<V: 'static> ElementInputHandler<V> {
|
||||||
/// containing view.
|
/// containing view.
|
||||||
///
|
///
|
||||||
/// [element_paint]: crate::Element::paint
|
/// [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 {
|
ElementInputHandler {
|
||||||
view,
|
view,
|
||||||
element_bounds,
|
element_bounds,
|
||||||
cx: cx.to_async(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
|
impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
|
||||||
fn selected_text_range(&mut self) -> Option<Range<usize>> {
|
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||||
self.view
|
self.view
|
||||||
.update(&mut self.cx, |view, cx| view.selected_text_range(cx))
|
.update(cx, |view, cx| view.selected_text_range(cx))
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn marked_text_range(&mut self) -> Option<Range<usize>> {
|
fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||||
self.view
|
self.view.update(cx, |view, cx| view.marked_text_range(cx))
|
||||||
.update(&mut self.cx, |view, cx| view.marked_text_range(cx))
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
self.view
|
||||||
.update(&mut self.cx, |view, cx| {
|
.update(cx, |view, cx| view.text_for_range(range_utf16, cx))
|
||||||
view.text_for_range(range_utf16, cx)
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
|
fn replace_text_in_range(
|
||||||
self.view
|
&mut self,
|
||||||
.update(&mut self.cx, |view, cx| {
|
replacement_range: Option<Range<usize>>,
|
||||||
view.replace_text_in_range(replacement_range, text, cx)
|
text: &str,
|
||||||
})
|
cx: &mut WindowContext,
|
||||||
.ok();
|
) {
|
||||||
|
self.view.update(cx, |view, cx| {
|
||||||
|
view.replace_text_in_range(replacement_range, text, cx)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_and_mark_text_in_range(
|
fn replace_and_mark_text_in_range(
|
||||||
|
@ -93,26 +87,24 @@ impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
|
||||||
range_utf16: Option<Range<usize>>,
|
range_utf16: Option<Range<usize>>,
|
||||||
new_text: &str,
|
new_text: &str,
|
||||||
new_selected_range: Option<Range<usize>>,
|
new_selected_range: Option<Range<usize>>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
self.view
|
self.view.update(cx, |view, cx| {
|
||||||
.update(&mut self.cx, |view, cx| {
|
view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
|
||||||
view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
|
});
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmark_text(&mut self) {
|
fn unmark_text(&mut self, cx: &mut WindowContext) {
|
||||||
self.view
|
self.view.update(cx, |view, cx| view.unmark_text(cx));
|
||||||
.update(&mut self.cx, |view, cx| view.unmark_text(cx))
|
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
|
fn bounds_for_range(
|
||||||
self.view
|
&mut self,
|
||||||
.update(&mut self.cx, |view, cx| {
|
range_utf16: Range<usize>,
|
||||||
view.bounds_for_range(range_utf16, self.element_bounds, cx)
|
cx: &mut WindowContext,
|
||||||
})
|
) -> Option<Bounds<Pixels>> {
|
||||||
.ok()
|
self.view.update(cx, |view, cx| {
|
||||||
.flatten()
|
view.bounds_for_range(range_utf16, self.element_bounds, cx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ mod mac;
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
|
Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font,
|
||||||
FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, Pixels, PlatformInput,
|
FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout,
|
||||||
Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
|
Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
|
||||||
Size, TaskLabel,
|
Scene, SharedString, Size, TaskLabel, WindowContext,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
|
@ -149,8 +149,8 @@ pub(crate) trait PlatformWindow {
|
||||||
fn mouse_position(&self) -> Point<Pixels>;
|
fn mouse_position(&self) -> Point<Pixels>;
|
||||||
fn modifiers(&self) -> Modifiers;
|
fn modifiers(&self) -> Modifiers;
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
|
fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
|
||||||
fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
|
fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
|
||||||
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
|
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
|
||||||
fn activate(&self);
|
fn activate(&self);
|
||||||
fn set_title(&mut self, title: &str);
|
fn set_title(&mut self, title: &str);
|
||||||
|
@ -325,19 +325,103 @@ impl From<TileId> for etagere::AllocId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PlatformInputHandler: 'static {
|
pub(crate) struct PlatformInputHandler {
|
||||||
fn selected_text_range(&mut self) -> Option<Range<usize>>;
|
cx: AsyncWindowContext,
|
||||||
fn marked_text_range(&mut self) -> Option<Range<usize>>;
|
handler: Box<dyn InputHandler>,
|
||||||
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);
|
|
||||||
|
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(
|
fn replace_and_mark_text_in_range(
|
||||||
&mut self,
|
&mut self,
|
||||||
range_utf16: Option<Range<usize>>,
|
range_utf16: Option<Range<usize>>,
|
||||||
new_text: &str,
|
new_text: &str,
|
||||||
new_selected_range: Option<Range<usize>>,
|
new_selected_range: Option<Range<usize>>,
|
||||||
|
) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputHandler: 'static {
|
||||||
|
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||||
|
fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||||
|
fn text_for_range(
|
||||||
|
&mut self,
|
||||||
|
range_utf16: Range<usize>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Option<String>;
|
||||||
|
fn replace_text_in_range(
|
||||||
|
&mut self,
|
||||||
|
replacement_range: Option<Range<usize>>,
|
||||||
|
text: &str,
|
||||||
|
cx: &mut WindowContext,
|
||||||
);
|
);
|
||||||
fn unmark_text(&mut self);
|
fn replace_and_mark_text_in_range(
|
||||||
fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
|
&mut self,
|
||||||
|
range_utf16: Option<Range<usize>>,
|
||||||
|
new_text: &str,
|
||||||
|
new_selected_range: Option<Range<usize>>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
);
|
||||||
|
fn unmark_text(&mut self, cx: &mut WindowContext);
|
||||||
|
fn bounds_for_range(
|
||||||
|
&mut self,
|
||||||
|
range_utf16: Range<usize>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Option<Bounds<Pixels>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange};
|
use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange};
|
||||||
use crate::{
|
use crate::{
|
||||||
global_bounds_to_ns_rect, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
|
global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle,
|
||||||
FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, Keystroke, Modifiers,
|
Bounds, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent,
|
||||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
|
||||||
PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
|
PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
|
||||||
};
|
};
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
|
@ -327,7 +327,7 @@ struct MacWindowState {
|
||||||
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
|
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
|
||||||
close_callback: Option<Box<dyn FnOnce()>>,
|
close_callback: Option<Box<dyn FnOnce()>>,
|
||||||
appearance_changed_callback: Option<Box<dyn FnMut()>>,
|
appearance_changed_callback: Option<Box<dyn FnMut()>>,
|
||||||
input_handler: Option<Box<dyn PlatformInputHandler>>,
|
input_handler: Option<PlatformInputHandler>,
|
||||||
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
|
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
|
||||||
last_key_equivalent: Option<KeyDownEvent>,
|
last_key_equivalent: Option<KeyDownEvent>,
|
||||||
synthetic_drag_counter: usize,
|
synthetic_drag_counter: usize,
|
||||||
|
@ -764,11 +764,11 @@ impl PlatformWindow for MacWindow {
|
||||||
self
|
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);
|
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()
|
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>
|
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut dyn PlatformInputHandler) -> R,
|
F: FnOnce(&mut PlatformInputHandler) -> R,
|
||||||
{
|
{
|
||||||
let window_state = unsafe { get_window_state(window) };
|
let window_state = unsafe { get_window_state(window) };
|
||||||
let mut lock = window_state.as_ref().lock();
|
let mut lock = window_state.as_ref().lock();
|
||||||
if let Some(mut input_handler) = lock.input_handler.take() {
|
if let Some(mut input_handler) = lock.input_handler.take() {
|
||||||
drop(lock);
|
drop(lock);
|
||||||
let result = f(input_handler.as_mut());
|
let result = f(&mut input_handler);
|
||||||
window_state.lock().input_handler = Some(input_handler);
|
window_state.lock().input_handler = Some(input_handler);
|
||||||
Some(result)
|
Some(result)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,13 +5,13 @@ use crate::{
|
||||||
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
||||||
DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
|
DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
|
||||||
Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
|
Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
|
||||||
ImageData, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, KeystrokeEvent, LayoutId,
|
ImageData, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent,
|
||||||
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent,
|
KeystrokeEvent, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
|
||||||
MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
|
||||||
PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
|
PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel,
|
||||||
RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene, Shadow,
|
Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
|
||||||
SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task,
|
Shadow, SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine,
|
||||||
Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
|
Task, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
|
||||||
SUBPIXEL_VARIANTS,
|
SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
|
@ -298,7 +298,7 @@ pub(crate) struct ElementStateBox {
|
||||||
|
|
||||||
struct RequestedInputHandler {
|
struct RequestedInputHandler {
|
||||||
view_id: EntityId,
|
view_id: EntityId,
|
||||||
handler: Option<Box<dyn PlatformInputHandler>>,
|
handler: Option<PlatformInputHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TooltipRequest {
|
struct TooltipRequest {
|
||||||
|
@ -2188,16 +2188,15 @@ impl<'a> WindowContext<'a> {
|
||||||
/// rendered.
|
/// rendered.
|
||||||
///
|
///
|
||||||
/// [element_input_handler]: crate::ElementInputHandler
|
/// [element_input_handler]: crate::ElementInputHandler
|
||||||
pub fn handle_input(
|
pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
|
||||||
&mut self,
|
|
||||||
focus_handle: &FocusHandle,
|
|
||||||
input_handler: impl PlatformInputHandler,
|
|
||||||
) {
|
|
||||||
if focus_handle.is_focused(self) {
|
if focus_handle.is_focused(self) {
|
||||||
let view_id = self.parent_view_id();
|
let view_id = self.parent_view_id();
|
||||||
self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
|
self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
|
||||||
view_id,
|
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
|
self.window
|
||||||
.platform_window
|
.platform_window
|
||||||
.on_should_close(Box::new(move || {
|
.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
|
// Ensure that the window is removed from the app if it's been closed
|
||||||
// by always pre-empting the system close event.
|
// by always pre-empting the system close event.
|
||||||
if f(cx) {
|
if f(cx) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut WindowContext) {
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
let (journal_dir, entry_path) = create_entry.await?;
|
let (journal_dir, entry_path) = create_entry.await?;
|
||||||
let (workspace, _) = cx
|
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?;
|
.await?;
|
||||||
|
|
||||||
let opened = workspace
|
let opened = workspace
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, fill, point, px, relative, AnyElement, AsyncWindowContext, AvailableSpace, BorrowWindow,
|
div, fill, point, px, relative, AnyElement, AvailableSpace, BorrowWindow, Bounds,
|
||||||
Bounds, DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight,
|
DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight, HighlightStyle,
|
||||||
HighlightStyle, Hsla, InteractiveBounds, InteractiveElement, InteractiveElementState,
|
Hsla, InputHandler, InteractiveBounds, InteractiveElement, InteractiveElementState,
|
||||||
Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
|
Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
|
||||||
MouseMoveEvent, Pixels, PlatformInputHandler, Point, ShapedLine, StatefulInteractiveElement,
|
MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, Styled, TextRun,
|
||||||
Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
|
TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::CursorShape;
|
use language::CursorShape;
|
||||||
|
@ -749,7 +749,6 @@ impl Element for TerminalElement {
|
||||||
let origin = bounds.origin + Point::new(layout.gutter, px(0.));
|
let origin = bounds.origin + Point::new(layout.gutter, px(0.));
|
||||||
|
|
||||||
let terminal_input_handler = TerminalInputHandler {
|
let terminal_input_handler = TerminalInputHandler {
|
||||||
cx: cx.to_async(),
|
|
||||||
terminal: self.terminal.clone(),
|
terminal: self.terminal.clone(),
|
||||||
cursor_bounds: layout
|
cursor_bounds: layout
|
||||||
.cursor
|
.cursor
|
||||||
|
@ -838,37 +837,35 @@ impl IntoElement for TerminalElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TerminalInputHandler {
|
struct TerminalInputHandler {
|
||||||
cx: AsyncWindowContext,
|
|
||||||
terminal: Model<Terminal>,
|
terminal: Model<Terminal>,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
cursor_bounds: Option<Bounds<Pixels>>,
|
cursor_bounds: Option<Bounds<Pixels>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformInputHandler for TerminalInputHandler {
|
impl InputHandler for TerminalInputHandler {
|
||||||
fn selected_text_range(&mut self) -> Option<std::ops::Range<usize>> {
|
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<std::ops::Range<usize>> {
|
||||||
self.cx
|
if self
|
||||||
.update(|_, cx| {
|
.terminal
|
||||||
if self
|
.read(cx)
|
||||||
.terminal
|
.last_content
|
||||||
.read(cx)
|
.mode
|
||||||
.last_content
|
.contains(TermMode::ALT_SCREEN)
|
||||||
.mode
|
{
|
||||||
.contains(TermMode::ALT_SCREEN)
|
None
|
||||||
{
|
} else {
|
||||||
None
|
Some(0..0)
|
||||||
} else {
|
}
|
||||||
Some(0..0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,19 +873,16 @@ impl PlatformInputHandler for TerminalInputHandler {
|
||||||
&mut self,
|
&mut self,
|
||||||
_replacement_range: Option<std::ops::Range<usize>>,
|
_replacement_range: Option<std::ops::Range<usize>>,
|
||||||
text: &str,
|
text: &str,
|
||||||
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
self.cx
|
self.terminal.update(cx, |terminal, _| {
|
||||||
.update(|_, cx| {
|
terminal.input(text.into());
|
||||||
self.terminal.update(cx, |terminal, _| {
|
});
|
||||||
terminal.input(text.into());
|
|
||||||
});
|
|
||||||
|
|
||||||
self.workspace
|
self.workspace
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
let telemetry = this.project().read(cx).client().telemetry().clone();
|
let telemetry = this.project().read(cx).client().telemetry().clone();
|
||||||
telemetry.log_edit_event("terminal");
|
telemetry.log_edit_event("terminal");
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
@ -898,12 +892,17 @@ impl PlatformInputHandler for TerminalInputHandler {
|
||||||
_range_utf16: Option<std::ops::Range<usize>>,
|
_range_utf16: Option<std::ops::Range<usize>>,
|
||||||
_new_text: &str,
|
_new_text: &str,
|
||||||
_new_selected_range: Option<std::ops::Range<usize>>,
|
_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
|
self.cursor_bounds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -772,7 +772,7 @@ impl Item for TerminalView {
|
||||||
.log_err()
|
.log_err()
|
||||||
.flatten()
|
.flatten()
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
cx.update(|_, cx| {
|
cx.update(|cx| {
|
||||||
let strategy = TerminalSettings::get_global(cx).working_directory.clone();
|
let strategy = TerminalSettings::get_global(cx).working_directory.clone();
|
||||||
workspace
|
workspace
|
||||||
.upgrade()
|
.upgrade()
|
||||||
|
|
|
@ -281,7 +281,7 @@ where
|
||||||
Ok(value) => Some(value),
|
Ok(value) => Some(value),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("TODO {err:?}");
|
log::error!("TODO {err:?}");
|
||||||
cx.update(|view, cx| {
|
cx.update_root(|view, cx| {
|
||||||
if let Ok(workspace) = view.downcast::<Workspace>() {
|
if let Ok(workspace) = view.downcast::<Workspace>() {
|
||||||
workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
|
workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1092,7 +1092,7 @@ impl Pane {
|
||||||
return Ok(true);
|
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.has_conflict(cx),
|
||||||
item.is_dirty(cx),
|
item.is_dirty(cx),
|
||||||
|
@ -1132,7 +1132,7 @@ impl Pane {
|
||||||
}
|
}
|
||||||
} else if is_dirty && (can_save || can_save_as) {
|
} else if is_dirty && (can_save || can_save_as) {
|
||||||
if save_intent == SaveIntent::Close {
|
if save_intent == SaveIntent::Close {
|
||||||
let will_autosave = cx.update(|_, cx| {
|
let will_autosave = cx.update(|cx| {
|
||||||
matches!(
|
matches!(
|
||||||
WorkspaceSettings::get_global(cx).autosave,
|
WorkspaceSettings::get_global(cx).autosave,
|
||||||
AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
|
AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
|
||||||
|
@ -1166,7 +1166,7 @@ impl Pane {
|
||||||
})?
|
})?
|
||||||
.unwrap_or_else(|| Path::new("").into());
|
.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() {
|
if let Some(abs_path) = abs_path.await.ok().flatten() {
|
||||||
pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
|
pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1233,7 +1233,7 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
for (pane, item) in dirty_items {
|
for (pane, item) in dirty_items {
|
||||||
let (singleton, project_entry_ids) =
|
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 singleton || !project_entry_ids.is_empty() {
|
||||||
if let Some(ix) =
|
if let Some(ix) =
|
||||||
pane.update(&mut cx, |pane, _| pane.index_for_item(item.as_ref()))?
|
pane.update(&mut cx, |pane, _| pane.index_for_item(item.as_ref()))?
|
||||||
|
@ -1307,7 +1307,7 @@ impl Workspace {
|
||||||
} else {
|
} else {
|
||||||
None
|
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?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -1912,7 +1912,7 @@ impl Workspace {
|
||||||
let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
|
let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
let (project_entry_id, project_item) = project_item.await?;
|
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>()
|
cx.default_global::<ProjectItemBuilders>()
|
||||||
.get(&project_item.entity_type())
|
.get(&project_item.entity_type())
|
||||||
.ok_or_else(|| anyhow!("no item builder for project item"))
|
.ok_or_else(|| anyhow!("no item builder for project item"))
|
||||||
|
@ -2709,7 +2709,7 @@ impl Workspace {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let this = this.upgrade().context("workspace dropped")?;
|
let this = this.upgrade().context("workspace dropped")?;
|
||||||
|
|
||||||
let item_builders = cx.update(|_, cx| {
|
let item_builders = cx.update(|cx| {
|
||||||
cx.default_global::<FollowableItemBuilders>()
|
cx.default_global::<FollowableItemBuilders>()
|
||||||
.values()
|
.values()
|
||||||
.map(|b| b.0)
|
.map(|b| b.0)
|
||||||
|
@ -2728,7 +2728,7 @@ impl Workspace {
|
||||||
Err(anyhow!("missing view variant"))?;
|
Err(anyhow!("missing view variant"))?;
|
||||||
}
|
}
|
||||||
for build_item in &item_builders {
|
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)
|
build_item(pane.clone(), this.clone(), id, &mut variant, cx)
|
||||||
})?;
|
})?;
|
||||||
if let Some(task) = task {
|
if let Some(task) = task {
|
||||||
|
@ -3141,7 +3141,7 @@ impl Workspace {
|
||||||
center_group = Some((group, active_pane))
|
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
|
center_items
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -3407,7 +3407,7 @@ fn open_items(
|
||||||
let restored_project_paths = restored_items
|
let restored_project_paths = restored_items
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|item| {
|
.filter_map(|item| {
|
||||||
cx.update(|_, cx| item.as_ref()?.project_path(cx))
|
cx.update(|cx| item.as_ref()?.project_path(cx))
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue