+ManagedView

And some games with rust traits
This commit is contained in:
Conrad Irwin 2023-11-16 22:46:44 -07:00
parent 2182cb2656
commit 2d1d75f482
8 changed files with 75 additions and 73 deletions

View file

@ -1,9 +1,8 @@
use collections::{CommandPaletteFilter, HashMap}; use collections::{CommandPaletteFilter, HashMap};
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
actions, div, prelude::*, Action, AppContext, Component, Div, EventEmitter, FocusHandle, actions, div, prelude::*, Action, AppContext, Component, Dismiss, Div, FocusHandle, Keystroke,
Keystroke, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView, ManagedView, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
WindowContext,
}; };
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};
use std::{ use std::{
@ -16,7 +15,7 @@ use util::{
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL}, channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
ResultExt, ResultExt,
}; };
use workspace::{Modal, ModalEvent, Workspace}; use workspace::Workspace;
use zed_actions::OpenZedURL; use zed_actions::OpenZedURL;
actions!(Toggle); actions!(Toggle);
@ -69,10 +68,9 @@ impl CommandPalette {
} }
} }
impl EventEmitter<ModalEvent> for CommandPalette {} impl ManagedView for CommandPalette {
impl Modal for CommandPalette { fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
fn focus(&self, cx: &mut WindowContext) { self.picker.focus_handle(cx)
self.picker.update(cx, |picker, cx| picker.focus(cx));
} }
} }
@ -267,7 +265,7 @@ impl PickerDelegate for CommandPaletteDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) { fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
self.command_palette self.command_palette
.update(cx, |_, cx| cx.emit(ModalEvent::Dismissed)) .update(cx, |_, cx| cx.emit(Dismiss))
.log_err(); .log_err();
} }

View file

@ -2,9 +2,9 @@ use collections::HashMap;
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor}; use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
use fuzzy::{CharBag, PathMatch, PathMatchCandidate}; use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{ use gpui::{
actions, div, AppContext, Component, Div, EventEmitter, InteractiveComponent, Model, actions, div, AppContext, Component, Dismiss, Div, FocusHandle, InteractiveComponent,
ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext, WeakView, ManagedView, Model, ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext,
WindowContext, WeakView,
}; };
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};
use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId}; use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@ -19,7 +19,7 @@ use text::Point;
use theme::ActiveTheme; use theme::ActiveTheme;
use ui::{v_stack, HighlightedLabel, StyledExt}; use ui::{v_stack, HighlightedLabel, StyledExt};
use util::{paths::PathLikeWithPosition, post_inc, ResultExt}; use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
use workspace::{Modal, ModalEvent, Workspace}; use workspace::Workspace;
actions!(Toggle); actions!(Toggle);
@ -111,10 +111,9 @@ impl FileFinder {
} }
} }
impl EventEmitter<ModalEvent> for FileFinder {} impl ManagedView for FileFinder {
impl Modal for FileFinder { fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
fn focus(&self, cx: &mut WindowContext) { self.picker.focus_handle(cx)
self.picker.update(cx, |picker, cx| picker.focus(cx))
} }
} }
impl Render for FileFinder { impl Render for FileFinder {
@ -689,9 +688,7 @@ impl PickerDelegate for FileFinderDelegate {
.log_err(); .log_err();
} }
} }
finder finder.update(&mut cx, |_, cx| cx.emit(Dismiss)).ok()?;
.update(&mut cx, |_, cx| cx.emit(ModalEvent::Dismissed))
.ok()?;
Some(()) Some(())
}) })
@ -702,7 +699,7 @@ impl PickerDelegate for FileFinderDelegate {
fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) { fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
self.file_finder self.file_finder
.update(cx, |_, cx| cx.emit(ModalEvent::Dismissed)) .update(cx, |_, cx| cx.emit(Dismiss))
.log_err(); .log_err();
} }

View file

@ -1,13 +1,13 @@
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor}; use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
use gpui::{ use gpui::{
actions, div, prelude::*, AppContext, Div, EventEmitter, ParentComponent, Render, SharedString, actions, div, prelude::*, AppContext, Dismiss, Div, FocusHandle, ManagedView, ParentComponent,
Styled, Subscription, View, ViewContext, VisualContext, WindowContext, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
}; };
use text::{Bias, Point}; use text::{Bias, Point};
use theme::ActiveTheme; use theme::ActiveTheme;
use ui::{h_stack, v_stack, Label, StyledExt, TextColor}; use ui::{h_stack, v_stack, Label, StyledExt, TextColor};
use util::paths::FILE_ROW_COLUMN_DELIMITER; use util::paths::FILE_ROW_COLUMN_DELIMITER;
use workspace::{Modal, ModalEvent, Workspace}; use workspace::Workspace;
actions!(Toggle); actions!(Toggle);
@ -23,10 +23,9 @@ pub struct GoToLine {
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
impl EventEmitter<ModalEvent> for GoToLine {} impl ManagedView for GoToLine {
impl Modal for GoToLine { fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
fn focus(&self, cx: &mut WindowContext) { self.line_editor.focus_handle(cx)
self.line_editor.update(cx, |editor, cx| editor.focus(cx))
} }
} }
@ -88,7 +87,7 @@ impl GoToLine {
) { ) {
match event { match event {
// todo!() this isn't working... // todo!() this isn't working...
editor::Event::Blurred => cx.emit(ModalEvent::Dismissed), editor::Event::Blurred => cx.emit(Dismiss),
editor::Event::BufferEdited { .. } => self.highlight_current_line(cx), editor::Event::BufferEdited { .. } => self.highlight_current_line(cx),
_ => {} _ => {}
} }
@ -123,7 +122,7 @@ impl GoToLine {
} }
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) { fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
cx.emit(ModalEvent::Dismissed); cx.emit(Dismiss);
} }
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) { fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
@ -140,7 +139,7 @@ impl GoToLine {
self.prev_scroll_position.take(); self.prev_scroll_position.take();
} }
cx.emit(ModalEvent::Dismissed); cx.emit(Dismiss);
} }
} }

View file

@ -185,10 +185,27 @@ impl Drop for FocusHandle {
} }
} }
/// FocusableView allows users of your view to easily
/// focus it (using cx.focus_view(view))
pub trait FocusableView: Render { pub trait FocusableView: Render {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle; fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
} }
/// ManagedView is a view (like a Modal, Popover, Menu, etc.)
/// where the lifecycle of the view is handled by another view.
pub trait ManagedView: Render {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
}
pub struct Dismiss;
impl<T: ManagedView> EventEmitter<Dismiss> for T {}
impl<T: ManagedView> FocusableView for T {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.focus_handle(cx)
}
}
// Holds the state for a specific window. // Holds the state for a specific window.
pub struct Window { pub struct Window {
pub(crate) handle: AnyWindowHandle, pub(crate) handle: AnyWindowHandle,

View file

@ -1,7 +1,7 @@
use editor::Editor; use editor::Editor;
use gpui::{ use gpui::{
div, prelude::*, uniform_list, Component, Div, MouseButton, Render, Task, div, prelude::*, uniform_list, AppContext, Component, Div, FocusHandle, FocusableView,
UniformListScrollHandle, View, ViewContext, WindowContext, MouseButton, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
}; };
use std::{cmp, sync::Arc}; use std::{cmp, sync::Arc};
use ui::{prelude::*, v_stack, Divider, Label, TextColor}; use ui::{prelude::*, v_stack, Divider, Label, TextColor};
@ -35,6 +35,12 @@ pub trait PickerDelegate: Sized + 'static {
) -> Self::ListItem; ) -> Self::ListItem;
} }
impl<D: PickerDelegate> FocusableView for Picker<D> {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.editor.focus_handle(cx)
}
}
impl<D: PickerDelegate> Picker<D> { impl<D: PickerDelegate> Picker<D> {
pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self { pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
let editor = cx.build_view(|cx| { let editor = cx.build_view(|cx| {

View file

@ -4,8 +4,8 @@ use std::rc::Rc;
use crate::prelude::*; use crate::prelude::*;
use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader}; use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
use gpui::{ use gpui::{
overlay, px, Action, AnchorCorner, AnyElement, Bounds, DispatchPhase, Div, EventEmitter, overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div,
FocusHandle, FocusableView, LayoutId, MouseButton, MouseDownEvent, Pixels, Point, Render, View, FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
}; };
pub struct ContextMenu { pub struct ContextMenu {
@ -13,17 +13,11 @@ pub struct ContextMenu {
focus_handle: FocusHandle, focus_handle: FocusHandle,
} }
pub enum MenuEvent { impl ManagedView for ContextMenu {
Dismissed,
}
impl EventEmitter<MenuEvent> for ContextMenu {}
impl FocusableView for ContextMenu {
fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle { fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
self.focus_handle.clone() self.focus_handle.clone()
} }
} }
impl Menu for ContextMenu {}
impl ContextMenu { impl ContextMenu {
pub fn new(cx: &mut WindowContext) -> Self { pub fn new(cx: &mut WindowContext) -> Self {
@ -50,11 +44,11 @@ impl ContextMenu {
pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) { pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
// todo!() // todo!()
cx.emit(MenuEvent::Dismissed); cx.emit(Dismiss);
} }
pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) { pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
cx.emit(MenuEvent::Dismissed); cx.emit(Dismiss);
} }
} }
@ -82,9 +76,7 @@ impl Render for ContextMenu {
} }
} }
pub trait Menu: Render + EventEmitter<MenuEvent> + FocusableView {} pub struct MenuHandle<V: 'static, M: ManagedView> {
pub struct MenuHandle<V: 'static, M: Menu> {
id: Option<ElementId>, id: Option<ElementId>,
child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement<V> + 'static>>, child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement<V> + 'static>>,
menu_builder: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static>>, menu_builder: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static>>,
@ -93,7 +85,7 @@ pub struct MenuHandle<V: 'static, M: Menu> {
attach: Option<AnchorCorner>, attach: Option<AnchorCorner>,
} }
impl<V: 'static, M: Menu> MenuHandle<V, M> { impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
pub fn id(mut self, id: impl Into<ElementId>) -> Self { pub fn id(mut self, id: impl Into<ElementId>) -> Self {
self.id = Some(id.into()); self.id = Some(id.into());
self self
@ -123,7 +115,7 @@ impl<V: 'static, M: Menu> MenuHandle<V, M> {
} }
} }
pub fn menu_handle<V: 'static, M: Menu>() -> MenuHandle<V, M> { pub fn menu_handle<V: 'static, M: ManagedView>() -> MenuHandle<V, M> {
MenuHandle { MenuHandle {
id: None, id: None,
child_builder: None, child_builder: None,
@ -140,7 +132,7 @@ pub struct MenuHandleState<V, M> {
child_element: Option<AnyElement<V>>, child_element: Option<AnyElement<V>>,
menu_element: Option<AnyElement<V>>, menu_element: Option<AnyElement<V>>,
} }
impl<V: 'static, M: Menu> Element<V> for MenuHandle<V, M> { impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
type ElementState = MenuHandleState<V, M>; type ElementState = MenuHandleState<V, M>;
fn element_id(&self) -> Option<gpui::ElementId> { fn element_id(&self) -> Option<gpui::ElementId> {
@ -234,7 +226,7 @@ impl<V: 'static, M: Menu> Element<V> for MenuHandle<V, M> {
let new_menu = (builder)(view_state, cx); let new_menu = (builder)(view_state, cx);
let menu2 = menu.clone(); let menu2 = menu.clone();
cx.subscribe(&new_menu, move |this, modal, e, cx| match e { cx.subscribe(&new_menu, move |this, modal, e, cx| match e {
MenuEvent::Dismissed => { &Dismiss => {
*menu2.borrow_mut() = None; *menu2.borrow_mut() = None;
cx.notify(); cx.notify();
} }
@ -255,7 +247,7 @@ impl<V: 'static, M: Menu> Element<V> for MenuHandle<V, M> {
} }
} }
impl<V: 'static, M: Menu> Component<V> for MenuHandle<V, M> { impl<V: 'static, M: ManagedView> Component<V> for MenuHandle<V, M> {
fn render(self) -> AnyElement<V> { fn render(self) -> AnyElement<V> {
AnyElement::new(self) AnyElement::new(self)
} }

View file

@ -1,6 +1,6 @@
use gpui::{ use gpui::{
div, prelude::*, px, AnyView, Div, EventEmitter, FocusHandle, Render, Subscription, View, div, prelude::*, px, AnyView, Div, FocusHandle, ManagedView, Render, Subscription, View,
ViewContext, WindowContext, ViewContext,
}; };
use ui::{h_stack, v_stack}; use ui::{h_stack, v_stack};
@ -15,14 +15,6 @@ pub struct ModalLayer {
active_modal: Option<ActiveModal>, active_modal: Option<ActiveModal>,
} }
pub trait Modal: Render + EventEmitter<ModalEvent> {
fn focus(&self, cx: &mut WindowContext);
}
pub enum ModalEvent {
Dismissed,
}
impl ModalLayer { impl ModalLayer {
pub fn new() -> Self { pub fn new() -> Self {
Self { active_modal: None } Self { active_modal: None }
@ -30,7 +22,7 @@ impl ModalLayer {
pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B) pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B)
where where
V: Modal, V: ManagedView,
B: FnOnce(&mut ViewContext<V>) -> V, B: FnOnce(&mut ViewContext<V>) -> V,
{ {
if let Some(active_modal) = &self.active_modal { if let Some(active_modal) = &self.active_modal {
@ -46,17 +38,15 @@ impl ModalLayer {
pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>) pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
where where
V: Modal, V: ManagedView,
{ {
self.active_modal = Some(ActiveModal { self.active_modal = Some(ActiveModal {
modal: new_modal.clone().into(), modal: new_modal.clone().into(),
subscription: cx.subscribe(&new_modal, |this, modal, e, cx| match e { subscription: cx.subscribe(&new_modal, |this, modal, e, cx| this.hide_modal(cx)),
ModalEvent::Dismissed => this.hide_modal(cx),
}),
previous_focus_handle: cx.focused(), previous_focus_handle: cx.focused(),
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
}); });
new_modal.update(cx, |modal, cx| modal.focus(cx)); cx.focus_view(&new_modal);
cx.notify(); cx.notify();
} }

View file

@ -31,9 +31,9 @@ use futures::{
use gpui::{ use gpui::{
actions, div, point, size, Action, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, actions, div, point, size, Action, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter, FocusHandle,
FocusableView, GlobalPixels, InteractiveComponent, KeyContext, Model, ModelContext, FocusableView, GlobalPixels, InteractiveComponent, KeyContext, ManagedView, Model,
ParentComponent, Point, Render, Size, Styled, Subscription, Task, View, ViewContext, ModelContext, ParentComponent, Point, Render, Size, Styled, Subscription, Task, View,
VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
}; };
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools; use itertools::Itertools;
@ -3380,11 +3380,14 @@ impl Workspace {
div div
} }
pub fn active_modal<V: Modal + 'static>(&mut self, cx: &ViewContext<Self>) -> Option<View<V>> { pub fn active_modal<V: ManagedView + 'static>(
&mut self,
cx: &ViewContext<Self>,
) -> Option<View<V>> {
self.modal_layer.read(cx).active_modal() self.modal_layer.read(cx).active_modal()
} }
pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B) pub fn toggle_modal<V: ManagedView, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
where where
B: FnOnce(&mut ViewContext<V>) -> V, B: FnOnce(&mut ViewContext<V>) -> V,
{ {