From acab2f900392a33d86916bf979f6d4052a309384 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 7 Nov 2023 13:23:08 -0700 Subject: [PATCH 1/8] MODAL --- crates/go_to_line2/src/go_to_line.rs | 11 +- crates/gpui2/src/styled.rs | 19 +-- crates/ui2/src/components.rs | 2 + crates/ui2/src/components/elevated_surface.rs | 28 +++++ crates/ui2/src/elevation.rs | 27 ++++- crates/workspace2/src/modal_layer.rs | 51 ++++++-- styles/src/style_tree/assistant.ts | 114 +++++++++++++++--- styles/src/style_tree/status_bar.ts | 18 +-- styles/src/themes/rose-pine/rose-pine-dawn.ts | 2 +- 9 files changed, 207 insertions(+), 65 deletions(-) create mode 100644 crates/ui2/src/components/elevated_surface.rs diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index 13d283ecff..6ae58124d8 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -1,4 +1,8 @@ -use gpui::{actions, div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext}; +use gpui::{ + actions, div, px, red, AppContext, Div, ParentElement, Render, Styled, ViewContext, + VisualContext, +}; +use ui::modal; use workspace::ModalRegistry; actions!(Toggle); @@ -27,9 +31,8 @@ pub struct GoToLine; impl Render for GoToLine { type Element = Div; - fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { - dbg!("rendering GoToLine"); - div().bg(red()).w(px(100.0)).h(px(100.0)) + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + modal(cx).child(div().bg(red()).w(px(100.0)).h(px(100.0))) } } diff --git a/crates/gpui2/src/styled.rs b/crates/gpui2/src/styled.rs index 06be0368c0..2bf3006d41 100644 --- a/crates/gpui2/src/styled.rs +++ b/crates/gpui2/src/styled.rs @@ -4,7 +4,7 @@ use crate::{ SharedString, StyleRefinement, Visibility, }; use crate::{BoxShadow, TextStyleRefinement}; -use smallvec::smallvec; +use smallvec::{smallvec, SmallVec}; pub trait Styled { fn style(&mut self) -> &mut StyleRefinement; @@ -290,24 +290,11 @@ pub trait Styled { /// Sets the box shadow of the element. /// [Docs](https://tailwindcss.com/docs/box-shadow) - fn shadow(mut self) -> Self + fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self where Self: Sized, { - self.style().box_shadow = Some(smallvec![ - BoxShadow { - color: hsla(0., 0., 0., 0.1), - offset: point(px(0.), px(1.)), - blur_radius: px(3.), - spread_radius: px(0.), - }, - BoxShadow { - color: hsla(0., 0., 0., 0.1), - offset: point(px(0.), px(1.)), - blur_radius: px(2.), - spread_radius: px(-1.), - } - ]); + self.style().box_shadow = Some(shadows); self } diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 857d0f1042..706918c080 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -3,6 +3,7 @@ mod button; mod checkbox; mod context_menu; mod details; +mod elevated_surface; mod facepile; mod icon; mod icon_button; @@ -30,6 +31,7 @@ pub use button::*; pub use checkbox::*; pub use context_menu::*; pub use details::*; +pub use elevated_surface::*; pub use facepile::*; pub use icon::*; pub use icon_button::*; diff --git a/crates/ui2/src/components/elevated_surface.rs b/crates/ui2/src/components/elevated_surface.rs new file mode 100644 index 0000000000..9070ac4428 --- /dev/null +++ b/crates/ui2/src/components/elevated_surface.rs @@ -0,0 +1,28 @@ +use gpui::{div, hsla, point, px, BoxShadow, Div}; + +use crate::{prelude::*, v_stack}; + +/// Create an elevated surface. +/// +/// Must be used inside of a relative parent element +pub fn elevated_surface(level: ElevationIndex, cx: &mut ViewContext) -> Div { + let colors = cx.theme().colors(); + + // let shadow = BoxShadow { + // color: hsla(0., 0., 0., 0.1), + // offset: point(px(0.), px(1.)), + // blur_radius: px(3.), + // spread_radius: px(0.), + // }; + + v_stack() + .rounded_lg() + .bg(colors.elevated_surface_background) + .border() + .border_color(colors.border) + .shadow(level.shadow()) +} + +pub fn modal(cx: &mut ViewContext) -> Div { + elevated_surface(ElevationIndex::ModalSurfaces, cx) +} diff --git a/crates/ui2/src/elevation.rs b/crates/ui2/src/elevation.rs index 3218f3f5f1..0dd51e3314 100644 --- a/crates/ui2/src/elevation.rs +++ b/crates/ui2/src/elevation.rs @@ -1,3 +1,6 @@ +use gpui::{hsla, point, px, BoxShadow}; +use smallvec::{smallvec, SmallVec}; + #[doc = include_str!("elevation.md")] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Elevation { @@ -17,8 +20,8 @@ pub enum ElevationIndex { } impl ElevationIndex { - pub fn usize(&self) -> usize { - match *self { + pub fn z_index(self) -> u32 { + match self { ElevationIndex::AppBackground => 0, ElevationIndex::UISurface => 100, ElevationIndex::ElevatedSurface => 200, @@ -27,6 +30,26 @@ impl ElevationIndex { ElevationIndex::DraggedElement => 900, } } + + pub fn shadow(self) -> SmallVec<[BoxShadow; 2]> { + match self { + ElevationIndex::AppBackground => smallvec![], + + ElevationIndex::UISurface => smallvec![BoxShadow { + color: hsla(0., 0., 0., 0.12), + offset: point(px(0.), px(1.)), + blur_radius: px(3.), + spread_radius: px(0.), + }], + + _ => smallvec![BoxShadow { + color: hsla(0., 0., 0., 0.32), + offset: point(px(1.), px(3.)), + blur_radius: px(12.), + spread_radius: px(0.), + }], + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index 01f940273a..5f8b9b0345 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,8 +1,8 @@ use std::{any::TypeId, sync::Arc}; use gpui::{ - div, AnyView, AppContext, DispatchPhase, Div, ParentElement, Render, StatelessInteractive, - View, ViewContext, + div, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render, + StatelessInteractive, Styled, View, ViewContext, }; use crate::Workspace; @@ -69,22 +69,47 @@ impl ModalLayer { Self { open_modal: None } } + // Workspace + // - ModalLayer parent + // - - container + // - - - modal + // - - - content of the modal + // - - content of the workspace + + // app + // workspace + // container some layer that contains all modals and is 100% wide and high + // modal (this has a shadow, some witdht) + // whatever + pub fn render(&self, workspace: &Workspace, cx: &ViewContext) -> Div { - let mut div = div(); - - // div, c workspace.toggle_modal()div.on_action()) { - // - // } - - // for (type_id, action) in cx.global::().registered_modals.iter() { - // div = div.useful_on_action(*type_id, action) - // } + let mut parent = div().relative(); for (_, action) in cx.global::().registered_modals.iter() { - div = (action)(div); + parent = (action)(parent); } - div.children(self.open_modal.clone()) + parent.when_some(self.open_modal.as_ref(), |parent, open_modal| { + let container1 = div() + .absolute() + .size_full() + .top_0() + .left_0() + .right_0() + .bottom_0(); + + // transparent layer + let container2 = div() + .flex() + .h_96() + .justify_center() + .size_full() + .relative() + .top_20() + .z_index(400); + + parent.child(container1.child(container2.child(open_modal.clone()))) + }) } } diff --git a/styles/src/style_tree/assistant.ts b/styles/src/style_tree/assistant.ts index 08297731bb..d261a23bcd 100644 --- a/styles/src/style_tree/assistant.ts +++ b/styles/src/style_tree/assistant.ts @@ -23,7 +23,7 @@ export default function assistant(): any { const theme = useTheme() const interactive_role = ( - color: StyleSets + color: StyleSets, ): Interactive => { return interactive({ base: { @@ -94,7 +94,7 @@ export default function assistant(): any { margin: { left: 8, right: 18 }, color: foreground(theme.highest, "positive"), width: 12, - } + }, }, retrieve_context: toggleable({ base: interactive({ @@ -106,7 +106,8 @@ export default function assistant(): any { background: background(theme.highest, "on"), corner_radius: 2, border: { - width: 1., color: background(theme.highest, "on") + width: 1, + color: background(theme.highest, "on"), }, margin: { left: 2 }, padding: { @@ -118,17 +119,45 @@ export default function assistant(): any { }, state: { hovered: { - ...text(theme.highest, "mono", "variant", "hovered"), - background: background(theme.highest, "on", "hovered"), + ...text( + theme.highest, + "mono", + "variant", + "hovered", + ), + background: background( + theme.highest, + "on", + "hovered", + ), border: { - width: 1., color: background(theme.highest, "on", "hovered") + width: 1, + color: background( + theme.highest, + "on", + "hovered", + ), }, }, clicked: { - ...text(theme.highest, "mono", "variant", "pressed"), - background: background(theme.highest, "on", "pressed"), + ...text( + theme.highest, + "mono", + "variant", + "pressed", + ), + background: background( + theme.highest, + "on", + "pressed", + ), border: { - width: 1., color: background(theme.highest, "on", "pressed") + width: 1, + color: background( + theme.highest, + "on", + "pressed", + ), }, }, }, @@ -143,11 +172,19 @@ export default function assistant(): any { border: border(theme.highest, "accent"), }, hovered: { - background: background(theme.highest, "accent", "hovered"), + background: background( + theme.highest, + "accent", + "hovered", + ), border: border(theme.highest, "accent", "hovered"), }, clicked: { - background: background(theme.highest, "accent", "pressed"), + background: background( + theme.highest, + "accent", + "pressed", + ), border: border(theme.highest, "accent", "pressed"), }, }, @@ -163,7 +200,8 @@ export default function assistant(): any { background: background(theme.highest, "on"), corner_radius: 2, border: { - width: 1., color: background(theme.highest, "on") + width: 1, + color: background(theme.highest, "on"), }, padding: { left: 4, @@ -174,17 +212,45 @@ export default function assistant(): any { }, state: { hovered: { - ...text(theme.highest, "mono", "variant", "hovered"), - background: background(theme.highest, "on", "hovered"), + ...text( + theme.highest, + "mono", + "variant", + "hovered", + ), + background: background( + theme.highest, + "on", + "hovered", + ), border: { - width: 1., color: background(theme.highest, "on", "hovered") + width: 1, + color: background( + theme.highest, + "on", + "hovered", + ), }, }, clicked: { - ...text(theme.highest, "mono", "variant", "pressed"), - background: background(theme.highest, "on", "pressed"), + ...text( + theme.highest, + "mono", + "variant", + "pressed", + ), + background: background( + theme.highest, + "on", + "pressed", + ), border: { - width: 1., color: background(theme.highest, "on", "pressed") + width: 1, + color: background( + theme.highest, + "on", + "pressed", + ), }, }, }, @@ -199,11 +265,19 @@ export default function assistant(): any { border: border(theme.highest, "accent"), }, hovered: { - background: background(theme.highest, "accent", "hovered"), + background: background( + theme.highest, + "accent", + "hovered", + ), border: border(theme.highest, "accent", "hovered"), }, clicked: { - background: background(theme.highest, "accent", "pressed"), + background: background( + theme.highest, + "accent", + "pressed", + ), border: border(theme.highest, "accent", "pressed"), }, }, diff --git a/styles/src/style_tree/status_bar.ts b/styles/src/style_tree/status_bar.ts index b279bbac14..5ced6b0d7b 100644 --- a/styles/src/style_tree/status_bar.ts +++ b/styles/src/style_tree/status_bar.ts @@ -78,33 +78,33 @@ export default function status_bar(): any { padding: { top: 2, bottom: 2, left: 6, right: 6 }, }, container_warning: diagnostic_status_container, - container_error: diagnostic_status_container + container_error: diagnostic_status_container, }, state: { hovered: { icon_color_ok: foreground(layer, "on"), container_ok: { - background: background(layer, "hovered") + background: background(layer, "hovered"), }, container_warning: { - background: background(layer, "hovered") + background: background(layer, "hovered"), }, container_error: { - background: background(layer, "hovered") + background: background(layer, "hovered"), }, }, clicked: { icon_color_ok: foreground(layer, "on"), container_ok: { - background: background(layer, "pressed") + background: background(layer, "pressed"), }, container_warning: { - background: background(layer, "pressed") + background: background(layer, "pressed"), }, container_error: { - background: background(layer, "pressed") - } - } + background: background(layer, "pressed"), + }, + }, }, }), panel_buttons: { diff --git a/styles/src/themes/rose-pine/rose-pine-dawn.ts b/styles/src/themes/rose-pine/rose-pine-dawn.ts index c78f1132dd..9642107bdb 100644 --- a/styles/src/themes/rose-pine/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine/rose-pine-dawn.ts @@ -31,7 +31,7 @@ export const theme: ThemeConfig = { color.muted, color.subtle, color.text, - ].reverse() + ].reverse(), ) .domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]), red: color_ramp(chroma(color.love)), From 3a85beeaa550b207b2ed2f6021e20cc3c117f0e7 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 7 Nov 2023 16:23:41 -0500 Subject: [PATCH 2/8] center a div --- crates/go_to_line2/src/go_to_line.rs | 2 +- crates/ui2/src/components/elevated_surface.rs | 2 +- crates/workspace2/src/modal_layer.rs | 22 +++++++++---------- crates/workspace2/src/workspace2.rs | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index 6ae58124d8..c2e2f28ab2 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -32,7 +32,7 @@ impl Render for GoToLine { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - modal(cx).child(div().bg(red()).w(px(100.0)).h(px(100.0))) + modal(cx).child(div().m_4().bg(red()).w(px(100.0)).h(px(100.0))) } } diff --git a/crates/ui2/src/components/elevated_surface.rs b/crates/ui2/src/components/elevated_surface.rs index 9070ac4428..5d0bbe698c 100644 --- a/crates/ui2/src/components/elevated_surface.rs +++ b/crates/ui2/src/components/elevated_surface.rs @@ -1,4 +1,4 @@ -use gpui::{div, hsla, point, px, BoxShadow, Div}; +use gpui::Div; use crate::{prelude::*, v_stack}; diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index 5f8b9b0345..a0c3515245 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,9 +1,10 @@ use std::{any::TypeId, sync::Arc}; use gpui::{ - div, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render, + div, hsla, px, red, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render, StatelessInteractive, Styled, View, ViewContext, }; +use ui::v_stack; use crate::Workspace; @@ -83,7 +84,7 @@ impl ModalLayer { // whatever pub fn render(&self, workspace: &Workspace, cx: &ViewContext) -> Div { - let mut parent = div().relative(); + let mut parent = div().relative().bg(red()).size_full(); for (_, action) in cx.global::().registered_modals.iter() { parent = (action)(parent); @@ -92,21 +93,20 @@ impl ModalLayer { parent.when_some(self.open_modal.as_ref(), |parent, open_modal| { let container1 = div() .absolute() + .flex() + .flex_col() + .items_center() .size_full() .top_0() .left_0() - .right_0() - .bottom_0(); + .z_index(400); // transparent layer - let container2 = div() - .flex() - .h_96() - .justify_center() - .size_full() + let container2 = v_stack() + .bg(hsla(0.5, 0.5, 0.5, 0.5)) + .h(px(0.0)) .relative() - .top_20() - .z_index(400); + .top_20(); parent.child(container1.child(container2.child(open_modal.clone()))) }) diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 90204f6038..ee6134e9e4 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -3710,10 +3710,10 @@ impl Render for Workspace { self.modal_layer .read(cx) .render(self, cx) + .relative() .flex_1() .w_full() .flex() - .flex_row() .overflow_hidden() .border_t() .border_b() From 5751303ea4423ca4758561616474ff018331d293 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 7 Nov 2023 13:42:33 -0700 Subject: [PATCH 3/8] MOAR CODE --- crates/go_to_line2/src/go_to_line.rs | 163 ++++++++++++++++----------- crates/workspace2/src/modal_layer.rs | 8 +- 2 files changed, 105 insertions(+), 66 deletions(-) diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index c2e2f28ab2..d280f31de2 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -3,22 +3,27 @@ use gpui::{ VisualContext, }; use ui::modal; +use editor::{scroll::autoscroll::Autoscroll, Editor}; +use gpui::{ + actions, div, px, red, AppContext, Div, EventEmitter, ParentElement, Render, Styled, View, + ViewContext, VisualContext, +}; +use text::{Bias, Point}; +use ui::modal; +use util::paths::FILE_ROW_COLUMN_DELIMITER; use workspace::ModalRegistry; -actions!(Toggle); +actions!(Toggle, Cancel, Confirm); pub fn init(cx: &mut AppContext) { cx.register_action_type::(); cx.global_mut::() - .register_modal(Toggle, |_, cx| { - // if let Some(editor) = workspace - // .active_item(cx) - // .and_then(|active_item| active_item.downcast::()) - // { - // cx.build_view(|cx| GoToLine::new(editor, cx)) - // } - let view = cx.build_view(|_| GoToLine); - view + .register_modal(Toggle, |workspace, cx| { + let editor = workspace + .active_item(cx) + .and_then(|active_item| active_item.downcast::())?; + + Some(cx.build_view(|cx| GoToLine::new(editor, cx))) }); // cx.add_action(GoToLine::toggle); @@ -26,13 +31,96 @@ pub fn init(cx: &mut AppContext) { // cx.add_action(GoToLine::cancel); } -pub struct GoToLine; +pub struct GoToLine { + line_editor: View, + active_editor: View, +} + +pub enum Event { + Dismissed, +} + +impl EventEmitter for GoToLine { + type Event = Event; +} + +impl GoToLine { + pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { + let line_editor = cx.build_view(|cx| Editor::single_line(cx)); + cx.subscribe(&line_editor, Self::on_line_editor_event) + .detach(); + + Self { + line_editor, + active_editor, + } + } + + fn on_line_editor_event( + &mut self, + _: View, + event: &editor::Event, + cx: &mut ViewContext, + ) { + match event { + editor::Event::Blurred => cx.emit(Event::Dismissed), + editor::Event::BufferEdited { .. } => { + if let Some(point) = self.point_from_query(cx) { + // todo!() + // self.active_editor.update(cx, |active_editor, cx| { + // let snapshot = active_editor.snapshot(cx).display_snapshot; + // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + // let display_point = point.to_display_point(&snapshot); + // let row = display_point.row(); + // active_editor.highlight_rows(Some(row..row + 1)); + // active_editor.request_autoscroll(Autoscroll::center(), cx); + // }); + cx.notify(); + } + } + _ => {} + } + } + + fn point_from_query(&self, cx: &ViewContext) -> Option { + // todo!() + let line_editor = "2:2"; //self.line_editor.read(cx).text(cx); + let mut components = line_editor + .splitn(2, FILE_ROW_COLUMN_DELIMITER) + .map(str::trim) + .fuse(); + let row = components.next().and_then(|row| row.parse::().ok())?; + let column = components.next().and_then(|col| col.parse::().ok()); + Some(Point::new( + row.saturating_sub(1), + column.unwrap_or(0).saturating_sub(1), + )) + } + + fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { + cx.emit(Event::Dismissed); + } + + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { + if let Some(point) = self.point_from_query(cx) { + self.active_editor.update(cx, |active_editor, cx| { + let snapshot = active_editor.snapshot(cx).display_snapshot; + let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { + s.select_ranges([point..point]) + }); + }); + } + + cx.emit(Event::Dismissed); + } +} impl Render for GoToLine { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - modal(cx).child(div().m_4().bg(red()).w(px(100.0)).h(px(100.0))) + modal(cx).child(self.line_editor.clone()).child("blah blah") } } @@ -51,14 +139,6 @@ impl Render for GoToLine { // impl GoToLine { // pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { -// // let line_editor = cx.build_view(|cx| { -// // Editor::single_line( -// // Some(Arc::new(|theme| theme.picker.input_editor.clone())), -// // cx, -// // ) -// // }); -// // cx.subscribe(&line_editor, Self::on_line_editor_event) -// // .detach(); // let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| { // let scroll_position = editor.scroll_position(cx); @@ -101,49 +181,6 @@ impl Render for GoToLine { // cx.emit(Event::Dismissed); // } -// fn on_line_editor_event( -// &mut self, -// _: View, -// event: &editor::Event, -// cx: &mut ViewContext, -// ) { -// match event { -// editor::Event::Blurred => cx.emit(Event::Dismissed), -// editor::Event::BufferEdited { .. } => { -// if let Some(point) = self.point_from_query(cx) { -// // todo!() -// // self.active_editor.update(cx, |active_editor, cx| { -// // let snapshot = active_editor.snapshot(cx).display_snapshot; -// // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); -// // let display_point = point.to_display_point(&snapshot); -// // let row = display_point.row(); -// // active_editor.highlight_rows(Some(row..row + 1)); -// // active_editor.request_autoscroll(Autoscroll::center(), cx); -// // }); -// cx.notify(); -// } -// } -// _ => {} -// } -// } - -// fn point_from_query(&self, cx: &ViewContext) -> Option { -// return None; -// // todo!() -// // let line_editor = self.line_editor.read(cx).text(cx); -// // let mut components = line_editor -// // .splitn(2, FILE_ROW_COLUMN_DELIMITER) -// // .map(str::trim) -// // .fuse(); -// // let row = components.next().and_then(|row| row.parse::().ok())?; -// // let column = components.next().and_then(|col| col.parse::().ok()); -// // Some(Point::new( -// // row.saturating_sub(1), -// // column.unwrap_or(0).saturating_sub(1), -// // )) -// } -// } - // impl EventEmitter for GoToLine { // type Event = Event; // } diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index a0c3515245..298e5646b7 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -33,7 +33,7 @@ impl ModalRegistry { pub fn register_modal(&mut self, action: A, build_view: B) where V: Render, - B: Fn(&Workspace, &mut ViewContext) -> View + 'static, + B: Fn(&Workspace, &mut ViewContext) -> Option> + 'static, { let build_view = Arc::new(build_view); @@ -47,12 +47,14 @@ impl ModalRegistry { event: &A, phase: DispatchPhase, cx: &mut ViewContext| { - dbg!("GOT HERE"); if phase == DispatchPhase::Capture { return; } - let new_modal = (build_view)(workspace, cx); + let Some(new_modal) = (build_view)(workspace, cx) else { + return; + }; + workspace.modal_layer.update(cx, |modal_layer, _| { modal_layer.open_modal = Some(new_modal.into()); }); From 7cdece4857caee93f3dd6af3319a976165895c72 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 7 Nov 2023 15:27:08 -0700 Subject: [PATCH 4/8] Beautiful go to line modal Co-authored-by: Julia Co-authored-by: Marshall --- crates/editor2/src/editor.rs | 68 +++++++++++------ crates/go_to_line2/src/go_to_line.rs | 106 +++++++++++++++++---------- crates/theme2/src/theme2.rs | 8 +- crates/workspace2/src/modal_layer.rs | 23 +----- 4 files changed, 124 insertions(+), 81 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index c63c531ceb..10831995ff 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -36,10 +36,10 @@ pub use element::{ use futures::FutureExt; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, div, px, relative, AnyElement, AppContext, BackgroundExecutor, Context, - DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, - Model, Pixels, Render, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, - WeakView, WindowContext, + actions, div, hsla, px, relative, rems, AnyElement, AppContext, BackgroundExecutor, Context, + DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle, FontFeatures, FontStyle, + FontWeight, Hsla, Model, Pixels, Render, Styled, Subscription, Task, TextStyle, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -2162,14 +2162,14 @@ impl Editor { // self.collaboration_hub = Some(hub); // } - // pub fn set_placeholder_text( - // &mut self, - // placeholder_text: impl Into>, - // cx: &mut ViewContext, - // ) { - // self.placeholder_text = Some(placeholder_text.into()); - // cx.notify(); - // } + pub fn set_placeholder_text( + &mut self, + placeholder_text: impl Into>, + cx: &mut ViewContext, + ) { + self.placeholder_text = Some(placeholder_text.into()); + cx.notify(); + } // pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext) { // self.cursor_shape = cursor_shape; @@ -9365,18 +9365,42 @@ impl Render for Editor { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let settings = ThemeSettings::get_global(cx); - let text_style = TextStyle { - color: cx.theme().colors().text, - font_family: settings.buffer_font.family.clone(), - font_features: settings.buffer_font.features, - font_size: settings.buffer_font_size.into(), - font_weight: FontWeight::NORMAL, - font_style: FontStyle::Normal, - line_height: relative(settings.buffer_line_height.value()), - underline: None, + let text_style = match self.mode { + EditorMode::SingleLine => { + TextStyle { + color: cx.theme().colors().text, + font_family: "Zed Sans".into(), // todo!() + font_features: FontFeatures::default(), + font_size: rems(1.0).into(), + font_weight: FontWeight::NORMAL, + font_style: FontStyle::Normal, + line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()), + underline: None, + } + } + + EditorMode::AutoHeight { max_lines } => todo!(), + + EditorMode::Full => TextStyle { + color: cx.theme().colors().text, + font_family: settings.buffer_font.family.clone(), + font_features: settings.buffer_font.features, + font_size: settings.buffer_font_size.into(), + font_weight: FontWeight::NORMAL, + font_style: FontStyle::Normal, + line_height: relative(settings.buffer_line_height.value()), + underline: None, + }, }; + + let background = match self.mode { + EditorMode::SingleLine => cx.theme().system().transparent, + EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent, + EditorMode::Full => cx.theme().colors().editor_background, + }; + EditorElement::new(EditorStyle { - background: cx.theme().colors().editor_background, + background, local_player: cx.theme().players().local(), text: text_style, scrollbar_width: px(12.), diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index d280f31de2..fc0b7432c7 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -1,15 +1,11 @@ +use editor::Editor; use gpui::{ - actions, div, px, red, AppContext, Div, ParentElement, Render, Styled, ViewContext, - VisualContext, -}; -use ui::modal; -use editor::{scroll::autoscroll::Autoscroll, Editor}; -use gpui::{ - actions, div, px, red, AppContext, Div, EventEmitter, ParentElement, Render, Styled, View, + actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext, }; -use text::{Bias, Point}; -use ui::modal; +use text::Point; +use theme::ActiveTheme; +use ui::{h_stack, modal, v_stack, Label, LabelColor}; use util::paths::FILE_ROW_COLUMN_DELIMITER; use workspace::ModalRegistry; @@ -33,6 +29,7 @@ pub fn init(cx: &mut AppContext) { pub struct GoToLine { line_editor: View, + #[allow(unused)] // todo!() active_editor: View, } @@ -46,7 +43,11 @@ impl EventEmitter for GoToLine { impl GoToLine { pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { - let line_editor = cx.build_view(|cx| Editor::single_line(cx)); + let line_editor = cx.build_view(|cx| { + let mut editor = Editor::single_line(cx); + editor.set_placeholder_text("Find something", cx); + editor + }); cx.subscribe(&line_editor, Self::on_line_editor_event) .detach(); @@ -65,26 +66,27 @@ impl GoToLine { match event { editor::Event::Blurred => cx.emit(Event::Dismissed), editor::Event::BufferEdited { .. } => { - if let Some(point) = self.point_from_query(cx) { - // todo!() - // self.active_editor.update(cx, |active_editor, cx| { - // let snapshot = active_editor.snapshot(cx).display_snapshot; - // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); - // let display_point = point.to_display_point(&snapshot); - // let row = display_point.row(); - // active_editor.highlight_rows(Some(row..row + 1)); - // active_editor.request_autoscroll(Autoscroll::center(), cx); - // }); - cx.notify(); - } + // if let Some(point) = self.point_from_query(cx) { + // todo!() + // self.active_editor.update(cx, |active_editor, cx| { + // let snapshot = active_editor.snapshot(cx).display_snapshot; + // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + // let display_point = point.to_display_point(&snapshot); + // let row = display_point.row(); + // active_editor.highlight_rows(Some(row..row + 1)); + // active_editor.request_autoscroll(Autoscroll::center(), cx); + // }); + // cx.notify(); + // } } _ => {} } } + #[allow(unused)] fn point_from_query(&self, cx: &ViewContext) -> Option { // todo!() - let line_editor = "2:2"; //self.line_editor.read(cx).text(cx); + let line_editor = self.line_editor.read(cx).text(cx); let mut components = line_editor .splitn(2, FILE_ROW_COLUMN_DELIMITER) .map(str::trim) @@ -97,22 +99,26 @@ impl GoToLine { )) } - fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { - cx.emit(Event::Dismissed); - } + // fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { + // cx.emit(Event::Dismissed); + // } - fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { - if let Some(point) = self.point_from_query(cx) { - self.active_editor.update(cx, |active_editor, cx| { - let snapshot = active_editor.snapshot(cx).display_snapshot; - let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); - active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { - s.select_ranges([point..point]) - }); - }); - } + // fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { + // if let Some(point) = self.point_from_query(cx) { + // self.active_editor.update(cx, |active_editor, cx| { + // let snapshot = active_editor.snapshot(cx).display_snapshot; + // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + // active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { + // s.select_ranges([point..point]) + // }); + // }); + // } - cx.emit(Event::Dismissed); + // cx.emit(Event::Dismissed); + // } + + fn status_text(&self) -> SharedString { + "Default text".into() } } @@ -120,7 +126,31 @@ impl Render for GoToLine { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - modal(cx).child(self.line_editor.clone()).child("blah blah") + modal(cx).w_96().child( + v_stack() + .px_1() + .pt_0p5() + .gap_px() + .child( + v_stack() + .py_0p5() + .px_1() + .child(div().px_1().py_0p5().child(self.line_editor.clone())), + ) + .child( + div() + .h_px() + .w_full() + .bg(cx.theme().colors().element_background), + ) + .child( + h_stack() + .justify_between() + .px_2() + .py_1() + .child(Label::new(self.status_text()).color(LabelColor::Muted)), + ), + ) } } diff --git a/crates/theme2/src/theme2.rs b/crates/theme2/src/theme2.rs index 88db3c55f4..33a977f695 100644 --- a/crates/theme2/src/theme2.rs +++ b/crates/theme2/src/theme2.rs @@ -63,7 +63,13 @@ pub struct Theme { } impl Theme { - /// Returns the [`ThemeColors`] for the theme. + /// Returns the [`SystemColors`] for the theme. + #[inline(always)] + pub fn system(&self) -> &SystemColors { + &self.styles.system + } + + /// Returns the [`PlayerColors`] for the theme. #[inline(always)] pub fn players(&self) -> &PlayerColors { &self.styles.player diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index 298e5646b7..c69354f87b 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,7 +1,7 @@ use std::{any::TypeId, sync::Arc}; use gpui::{ - div, hsla, px, red, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render, + div, px, AnyView, AppContext, Component, DispatchPhase, Div, ParentElement, Render, StatelessInteractive, Styled, View, ViewContext, }; use ui::v_stack; @@ -72,21 +72,8 @@ impl ModalLayer { Self { open_modal: None } } - // Workspace - // - ModalLayer parent - // - - container - // - - - modal - // - - - content of the modal - // - - content of the workspace - - // app - // workspace - // container some layer that contains all modals and is 100% wide and high - // modal (this has a shadow, some witdht) - // whatever - pub fn render(&self, workspace: &Workspace, cx: &ViewContext) -> Div { - let mut parent = div().relative().bg(red()).size_full(); + let mut parent = div().relative().size_full(); for (_, action) in cx.global::().registered_modals.iter() { parent = (action)(parent); @@ -104,11 +91,7 @@ impl ModalLayer { .z_index(400); // transparent layer - let container2 = v_stack() - .bg(hsla(0.5, 0.5, 0.5, 0.5)) - .h(px(0.0)) - .relative() - .top_20(); + let container2 = v_stack().h(px(0.0)).relative().top_20(); parent.child(container1.child(container2.child(open_modal.clone()))) }) From 097efdebc5f564ce8fa692e4958aa5fd4caa770f Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 8 Nov 2023 12:49:09 -0800 Subject: [PATCH 5/8] WIP --- Cargo.lock | 1 + crates/go_to_line2/src/go_to_line.rs | 98 ++++++++++++++-------------- crates/gpui2/src/action.rs | 1 + crates/menu2/src/menu2.rs | 7 +- crates/workspace2/src/modal_layer.rs | 19 +++++- crates/zed2/Cargo.toml | 1 + crates/zed2/src/main.rs | 4 ++ 7 files changed, 79 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcbf5a0f95..1e6d24df8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11279,6 +11279,7 @@ dependencies = [ "libc", "log", "lsp2", + "menu2", "node_runtime", "num_cpus", "parking_lot 0.11.2", diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index bb705427b1..f6481fdbd9 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -1,7 +1,7 @@ use editor::Editor; use gpui::{ - actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString, Styled, View, - ViewContext, VisualContext, + actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString, + StatelessInteractive, Styled, View, ViewContext, VisualContext, }; use text::Point; use theme::ActiveTheme; @@ -9,7 +9,7 @@ use ui::{h_stack, modal, v_stack, Label, LabelColor}; use util::paths::FILE_ROW_COLUMN_DELIMITER; use workspace::ModalRegistry; -actions!(Toggle, Cancel, Confirm); +actions!(Toggle); pub fn init(cx: &mut AppContext) { cx.global_mut::() @@ -20,10 +20,6 @@ pub fn init(cx: &mut AppContext) { Some(cx.build_view(|cx| GoToLine::new(editor, cx))) }); - - // cx.add_action(GoToLine::toggle); - // cx.add_action(GoToLine::confirm); - // cx.add_action(GoToLine::cancel); } pub struct GoToLine { @@ -37,7 +33,7 @@ pub enum Event { } impl EventEmitter for GoToLine { - type Event = Event; + type Event = ModalEvent; } impl GoToLine { @@ -45,6 +41,7 @@ impl GoToLine { let line_editor = cx.build_view(|cx| { let mut editor = Editor::single_line(cx); editor.set_placeholder_text("Find something", cx); + editor.focus(cx); editor }); cx.subscribe(&line_editor, Self::on_line_editor_event) @@ -98,23 +95,24 @@ impl GoToLine { )) } - // fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { - // cx.emit(Event::Dismissed); - // } + fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext) { + println!("CANCLE"); + cx.emit(Event::Dismissed); + } - // fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { - // if let Some(point) = self.point_from_query(cx) { - // self.active_editor.update(cx, |active_editor, cx| { - // let snapshot = active_editor.snapshot(cx).display_snapshot; - // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); - // active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { - // s.select_ranges([point..point]) - // }); - // }); - // } + fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { + // // if let Some(point) = self.point_from_query(cx) { + // // self.active_editor.update(cx, |active_editor, cx| { + // // let snapshot = active_editor.snapshot(cx).display_snapshot; + // // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + // // active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { + // // s.select_ranges([point..point]) + // // }); + // // }); + // // } - // cx.emit(Event::Dismissed); - // } + // cx.emit(Event::Dismissed); + } fn status_text(&self) -> SharedString { "Default text".into() @@ -125,31 +123,35 @@ impl Render for GoToLine { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { - modal(cx).w_96().child( - v_stack() - .px_1() - .pt_0p5() - .gap_px() - .child( - v_stack() - .py_0p5() - .px_1() - .child(div().px_1().py_0p5().child(self.line_editor.clone())), - ) - .child( - div() - .h_px() - .w_full() - .bg(cx.theme().colors().element_background), - ) - .child( - h_stack() - .justify_between() - .px_2() - .py_1() - .child(Label::new(self.status_text()).color(LabelColor::Muted)), - ), - ) + modal(cx) + .w_96() + .on_action(Self::cancel) + .on_action(Self::confirm) + .child( + v_stack() + .px_1() + .pt_0p5() + .gap_px() + .child( + v_stack() + .py_0p5() + .px_1() + .child(div().px_1().py_0p5().child(self.line_editor.clone())), + ) + .child( + div() + .h_px() + .w_full() + .bg(cx.theme().colors().element_background), + ) + .child( + h_stack() + .justify_between() + .px_2() + .py_1() + .child(Label::new(self.status_text()).color(LabelColor::Muted)), + ), + ) } } diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 4d89ba1826..85149f5d55 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -123,6 +123,7 @@ pub fn register_action() { /// Construct an action based on its name and optional JSON parameters sourced from the keymap. pub fn build_action(name: &str, params: Option) -> Result> { let lock = ACTION_REGISTRY.read(); + let build_action = lock .builders_by_name .get(name) diff --git a/crates/menu2/src/menu2.rs b/crates/menu2/src/menu2.rs index e5e8242f37..eafb1e295f 100644 --- a/crates/menu2/src/menu2.rs +++ b/crates/menu2/src/menu2.rs @@ -1,4 +1,9 @@ -use gpui::actions; +use gpui::{actions, ctor}; + +// todo!(remove this) +// https://github.com/rust-lang/rust/issues/47384 +// https://github.com/mmastrac/rust-ctor/issues/280 +pub fn unused() {} actions!( Cancel, diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index 694b4e7ffe..55edab353b 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,7 +1,7 @@ use crate::Workspace; use gpui::{ div, px, AnyView, AppContext, Component, Div, ParentElement, Render, StatelessInteractive, - Styled, View, ViewContext, + Styled, View, ViewContext, EventEmitter, }; use std::{any::TypeId, sync::Arc}; use ui::v_stack; @@ -27,10 +27,18 @@ struct ToggleModal { name: String, } +pub enum ModalEvents { + Dismissed +} + +trait Modal: EventEmitter + Render { + fn to_modal_events(&Self::Event) -> Option; +} + impl ModalRegistry { pub fn register_modal(&mut self, action: A, build_view: B) where - V: Render, + V: Modal, B: Fn(&Workspace, &mut ViewContext) -> Option> + 'static, { let build_view = Arc::new(build_view); @@ -45,10 +53,15 @@ impl ModalRegistry { let Some(new_modal) = (build_view)(workspace, cx) else { return; }; - workspace.modal_layer.update(cx, |modal_layer, _| { modal_layer.open_modal = Some(new_modal.into()); }); + cx.subscribe(new_modal, |e, modal, cx| { + match modal.to_modal_events(e) { + Some(Dismissed) => + dismissed -> whatever + } + }) cx.notify(); }, diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 2c93de734e..f7cb69c73a 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -48,6 +48,7 @@ journal = { package = "journal2", path = "../journal2" } language = { package = "language2", path = "../language2" } # language_selector = { path = "../language_selector" } lsp = { package = "lsp2", path = "../lsp2" } +menu = { package = "menu2", path = "../menu2" } language_tools = { path = "../language_tools" } node_runtime = { path = "../node_runtime" } # assistant = { path = "../assistant" } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 7f25a6a5d4..cd0f8e5fbf 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -56,6 +56,10 @@ use zed2::{ mod open_listener; fn main() { + //TODO!(figure out what the linker issues are here) + // https://github.com/rust-lang/rust/issues/47384 + // https://github.com/mmastrac/rust-ctor/issues/280 + menu::unused(); let http = http::client(); init_paths(); init_logger(); From 6a802e2fdab675512fc63fe6484f7f31583206fa Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 8 Nov 2023 14:45:36 -0700 Subject: [PATCH 6/8] Make Modals dismissable in theory --- crates/go_to_line2/src/go_to_line.rs | 14 ++++-- crates/menu2/src/menu2.rs | 2 +- crates/workspace2/src/modal_layer.rs | 75 +++++++++++++++------------- crates/workspace2/src/workspace2.rs | 8 +-- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index f6481fdbd9..9d14e2d2d6 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -7,7 +7,7 @@ use text::Point; use theme::ActiveTheme; use ui::{h_stack, modal, v_stack, Label, LabelColor}; use util::paths::FILE_ROW_COLUMN_DELIMITER; -use workspace::ModalRegistry; +use workspace::{ModalRegistry, Modal, ModalEvent}; actions!(Toggle); @@ -33,7 +33,7 @@ pub enum Event { } impl EventEmitter for GoToLine { - type Event = ModalEvent; + type Event = Event; } impl GoToLine { @@ -100,7 +100,7 @@ impl GoToLine { cx.emit(Event::Dismissed); } - fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { + fn confirm(&mut self, _: &menu::Confirm, _cx: &mut ViewContext) { // // if let Some(point) = self.point_from_query(cx) { // // self.active_editor.update(cx, |active_editor, cx| { // // let snapshot = active_editor.snapshot(cx).display_snapshot; @@ -119,6 +119,14 @@ impl GoToLine { } } +impl Modal for GoToLine { + fn to_modal_event(&self, e: &Self::Event) -> Option { + match e { + Event::Dismissed => Some(ModalEvent::Dismissed), + } + } +} + impl Render for GoToLine { type Element = Div; diff --git a/crates/menu2/src/menu2.rs b/crates/menu2/src/menu2.rs index eafb1e295f..44ebffcca2 100644 --- a/crates/menu2/src/menu2.rs +++ b/crates/menu2/src/menu2.rs @@ -1,4 +1,4 @@ -use gpui::{actions, ctor}; +use gpui::actions; // todo!(remove this) // https://github.com/rust-lang/rust/issues/47384 diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index 55edab353b..c46e8f7acc 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,7 +1,7 @@ use crate::Workspace; use gpui::{ - div, px, AnyView, AppContext, Component, Div, ParentElement, Render, StatelessInteractive, - Styled, View, ViewContext, EventEmitter, + div, px, AnyView, AppContext, Component, Div, EventEmitter, ParentElement, Render, + StatelessInteractive, Styled, Subscription, View, ViewContext, WeakView, }; use std::{any::TypeId, sync::Arc}; use ui::v_stack; @@ -10,11 +10,10 @@ pub struct ModalRegistry { registered_modals: Vec<(TypeId, Box) -> Div>)>, } -pub trait Modal {} - -#[derive(Clone)] pub struct ModalLayer { + workspace: WeakView, open_modal: Option, + subscription: Option, } pub fn init_modal_registry(cx: &mut AppContext) { @@ -23,23 +22,19 @@ pub fn init_modal_registry(cx: &mut AppContext) { }); } -struct ToggleModal { - name: String, +pub enum ModalEvent { + Dismissed, } -pub enum ModalEvents { - Dismissed -} - -trait Modal: EventEmitter + Render { - fn to_modal_events(&Self::Event) -> Option; +pub trait Modal: EventEmitter + Render { + fn to_modal_event(&self, _: &Self::Event) -> Option; } impl ModalRegistry { pub fn register_modal(&mut self, action: A, build_view: B) where V: Modal, - B: Fn(&Workspace, &mut ViewContext) -> Option> + 'static, + B: Fn(&mut Workspace, &mut ViewContext) -> Option> + 'static, { let build_view = Arc::new(build_view); @@ -48,35 +43,47 @@ impl ModalRegistry { Box::new(move |mut div| { let build_view = build_view.clone(); - div.on_action( - move |workspace: &mut Workspace, event: &A, cx: &mut ViewContext| { - let Some(new_modal) = (build_view)(workspace, cx) else { - return; + div.on_action(move |workspace, event: &A, cx| { + let Some(new_modal) = + (build_view)(workspace, cx) else { + return }; - workspace.modal_layer.update(cx, |modal_layer, _| { - modal_layer.open_modal = Some(new_modal.into()); - }); - cx.subscribe(new_modal, |e, modal, cx| { - match modal.to_modal_events(e) { - Some(Dismissed) => - dismissed -> whatever - } - }) - - cx.notify(); - }, - ) + workspace.modal_layer.update(cx, |modal_layer, cx| { + modal_layer.show_modal(new_modal, cx); + }) + }) }), )); } } impl ModalLayer { - pub fn new() -> Self { - Self { open_modal: None } + pub fn new(workspace: WeakView) -> Self { + Self { + workspace, + open_modal: None, + subscription: None, + } } - pub fn render(&self, workspace: &Workspace, cx: &ViewContext) -> Div { + pub fn show_modal(&mut self, new_modal: View, cx: &mut ViewContext) { + self.subscription = Some(cx.subscribe(&new_modal, |this, modal, e, cx| { + match modal.read(cx).to_modal_event(e) { + Some(ModalEvent::Dismissed) => this.hide_modal(cx), + None => {} + } + })); + self.open_modal = Some(new_modal.into()); + cx.notify(); + } + + pub fn hide_modal(&mut self, cx: &mut ViewContext) { + self.open_modal.take(); + self.subscription.take(); + cx.notify(); + } + + pub fn render(&self, cx: &ViewContext) -> Div { let mut parent = div().relative().size_full(); for (_, action) in cx.global::().registered_modals.iter() { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 42751ed632..4eda0e2af6 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -46,8 +46,7 @@ use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, use itertools::Itertools; use language2::LanguageRegistry; use lazy_static::lazy_static; -pub use modal_layer::ModalRegistry; -use modal_layer::{init_modal_registry, ModalLayer}; +pub use modal_layer::*; use node_runtime::NodeRuntime; use notifications::{simple_message_notification::MessageNotification, NotificationHandle}; pub use pane::*; @@ -697,7 +696,8 @@ impl Workspace { status_bar }); - let modal_layer = cx.build_view(|cx| ModalLayer::new()); + let workspace_handle = cx.view().downgrade(); + let modal_layer = cx.build_view(|cx| ModalLayer::new(workspace_handle)); // todo!() // cx.update_default_global::, _, _>(|drag_and_drop, _| { @@ -3709,7 +3709,7 @@ impl Render for Workspace { .child( self.modal_layer .read(cx) - .render(self, cx) + .render(cx) .relative() .flex_1() .w_full() From 1b9f76c01dfd2d8462d7a1acb51f4697fcf4eef2 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 8 Nov 2023 16:23:05 -0700 Subject: [PATCH 7/8] Refactor GoToLine to use cx.observe_new_views() --- crates/editor2/src/editor.rs | 3 +- crates/go_to_line2/src/go_to_line.rs | 53 +++++++++++++++++++------ crates/gpui2/src/app.rs | 25 +++++++++++- crates/gpui2/src/app/async_context.rs | 2 +- crates/gpui2/src/gpui2.rs | 2 +- crates/gpui2/src/interactive.rs | 4 ++ crates/gpui2/src/window.rs | 15 +++++-- crates/workspace2/src/dock.rs | 8 ++++ crates/workspace2/src/modal_layer.rs | 57 ++++++++++----------------- crates/workspace2/src/toolbar.rs | 11 +++++- crates/workspace2/src/workspace2.rs | 21 +++++----- 11 files changed, 136 insertions(+), 65 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 4361ed53d0..a34f7d734d 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -57,6 +57,7 @@ use language::{ Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId, }; +use lazy_static::lazy_static; use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState}; use lsp::{DiagnosticSeverity, Documentation, LanguageServerId}; use movement::TextLayoutDetails; @@ -66,7 +67,7 @@ pub use multi_buffer::{ ToPoint, }; use ordered_float::OrderedFloat; -use parking_lot::RwLock; +use parking_lot::{Mutex, RwLock}; use project::{FormatTrigger, Location, Project}; use rand::prelude::*; use rpc::proto::*; diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index 9d14e2d2d6..c9a04b408f 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -1,25 +1,55 @@ use editor::Editor; use gpui::{ actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString, - StatelessInteractive, Styled, View, ViewContext, VisualContext, + StatefulInteractivity, StatelessInteractive, Styled, View, ViewContext, VisualContext, }; use text::Point; use theme::ActiveTheme; use ui::{h_stack, modal, v_stack, Label, LabelColor}; use util::paths::FILE_ROW_COLUMN_DELIMITER; -use workspace::{ModalRegistry, Modal, ModalEvent}; +use workspace::{Modal, ModalEvent, Workspace}; actions!(Toggle); pub fn init(cx: &mut AppContext) { - cx.global_mut::() - .register_modal(Toggle, |workspace, cx| { - let editor = workspace - .active_item(cx) - .and_then(|active_item| active_item.downcast::())?; + cx.observe_new_views( + |workspace: &mut Workspace, _: &mut ViewContext| { + workspace + .modal_layer() + .register_modal(Toggle, |workspace, cx| { + let editor = workspace + .active_item(cx) + .and_then(|active_item| active_item.downcast::())?; - Some(cx.build_view(|cx| GoToLine::new(editor, cx))) - }); + Some(cx.build_view(|cx| GoToLine::new(editor, cx))) + }); + dbg!("hey!"); + }, + ) + .detach(); + + // // cx.window_global() + // // cx.window_global:: + // // cx.window_global::() + // Workspace::on_init(|workspace, cx| { + // workspace.on_open_item() + // }); + + // Editor::on_init(|editor, cx|{ + + // }) + + // Editor::register_action(|_editor, _: &Toggle, cx| { + // dbg!("HEY!"); + // // let editor = cx.view(); + // // cx.update_window(cx.window().handle(), |cx, view| { + // // let workspace = view.downcast::(); + // // }) + // // workspace.show_modal(cx.build_view(|cx| GoToLine::new(editor, cx))) + // }) + // cx.global_mut::() + // .register_modal(Toggle, |workspace, cx| { + // }); } pub struct GoToLine { @@ -128,13 +158,14 @@ impl Modal for GoToLine { } impl Render for GoToLine { - type Element = Div; + type Element = Div>; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { modal(cx) - .w_96() + .id("go to line") .on_action(Self::cancel) .on_action(Self::confirm) + .w_96() .child( v_stack() .px_1() diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index a3ab426321..1cb46e87c1 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -18,8 +18,8 @@ use crate::{ AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet, - Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, - WindowContext, WindowHandle, WindowId, + Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, + Window, WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -167,6 +167,7 @@ type Handler = Box bool + 'static>; type Listener = Box bool + 'static>; type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>; type ReleaseListener = Box; +type NewViewListener = Box; // struct FrameConsumer { // next_frame_callbacks: Vec, @@ -193,6 +194,7 @@ pub struct AppContext { pub(crate) text_style_stack: Vec, pub(crate) globals_by_type: HashMap, pub(crate) entities: EntityMap, + pub(crate) new_view_observers: SubscriberSet, pub(crate) windows: SlotMap>, pub(crate) keymap: Arc>, pub(crate) global_action_listeners: @@ -251,6 +253,7 @@ impl AppContext { text_style_stack: Vec::new(), globals_by_type: HashMap::default(), entities, + new_view_observers: SubscriberSet::new(), windows: SlotMap::with_key(), keymap: Arc::new(Mutex::new(Keymap::default())), global_action_listeners: HashMap::default(), @@ -599,6 +602,7 @@ impl AppContext { fn apply_notify_effect(&mut self, emitter: EntityId) { self.pending_notifications.remove(&emitter); + self.observers .clone() .retain(&emitter, |handler| handler(self)); @@ -828,6 +832,23 @@ impl AppContext { self.globals_by_type.insert(global_type, lease.global); } + pub fn observe_new_views( + &mut self, + on_new: impl 'static + Fn(&mut V, &mut ViewContext), + ) -> Subscription { + self.new_view_observers.insert( + TypeId::of::(), + Box::new(move |any_view: AnyView, cx: &mut WindowContext| { + any_view + .downcast::() + .unwrap() + .update(cx, |view_state, cx| { + on_new(view_state, cx); + }) + }), + ) + } + pub fn observe_release( &mut self, handle: &E, diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index c05182444e..e191e7315f 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -258,7 +258,7 @@ impl VisualContext for AsyncWindowContext { build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where - V: 'static, + V: 'static + Render, { self.window .update(self, |_, cx| cx.build_view(build_view_state)) diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index e253872ed4..91e4141735 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -112,7 +112,7 @@ pub trait VisualContext: Context { build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where - V: 'static; + V: 'static + Render; fn update_view( &mut self, diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index a546c1b40b..40d247f32b 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -414,10 +414,14 @@ pub trait ElementInteractivity: 'static { Box::new(move |_, key_down, context, phase, cx| { if phase == DispatchPhase::Bubble { let key_down = key_down.downcast_ref::().unwrap(); + dbg!(key_down); if let KeyMatch::Some(action) = cx.match_keystroke(&global_id, &key_down.keystroke, context) { + dbg!(&action); return Some(action); + } else { + dbg!("none"); } } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index cf138eb1ef..374e893037 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1411,7 +1411,7 @@ impl VisualContext for WindowContext<'_> { build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where - V: 'static, + V: 'static + Render, { let slot = self.app.entities.reserve(); let view = View { @@ -1419,7 +1419,16 @@ impl VisualContext for WindowContext<'_> { }; let mut cx = ViewContext::new(&mut *self.app, &mut *self.window, &view); let entity = build_view_state(&mut cx); - self.entities.insert(slot, entity); + cx.entities.insert(slot, entity); + + cx.new_view_observers + .clone() + .retain(&TypeId::of::(), |observer| { + let any_view = AnyView::from(view.clone()); + (observer)(any_view, self); + true + }); + view } @@ -2102,7 +2111,7 @@ impl Context for ViewContext<'_, V> { } impl VisualContext for ViewContext<'_, V> { - fn build_view( + fn build_view( &mut self, build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W, ) -> Self::Result> { diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index e6b6c7561d..635b48051a 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -407,6 +407,14 @@ impl Dock { // } } +impl Render for Dock { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + todo!() + } +} + // todo!() // impl View for Dock { // fn ui_name() -> &'static str { diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index c46e8f7acc..564db0f982 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,25 +1,15 @@ use crate::Workspace; use gpui::{ - div, px, AnyView, AppContext, Component, Div, EventEmitter, ParentElement, Render, - StatelessInteractive, Styled, Subscription, View, ViewContext, WeakView, + div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatelessInteractive, + Styled, Subscription, View, ViewContext, }; use std::{any::TypeId, sync::Arc}; use ui::v_stack; -pub struct ModalRegistry { - registered_modals: Vec<(TypeId, Box) -> Div>)>, -} - pub struct ModalLayer { - workspace: WeakView, open_modal: Option, subscription: Option, -} - -pub fn init_modal_registry(cx: &mut AppContext) { - cx.set_global(ModalRegistry { - registered_modals: Vec::new(), - }); + registered_modals: Vec<(TypeId, Box) -> Div>)>, } pub enum ModalEvent { @@ -30,7 +20,15 @@ pub trait Modal: EventEmitter + Render { fn to_modal_event(&self, _: &Self::Event) -> Option; } -impl ModalRegistry { +impl ModalLayer { + pub fn new() -> Self { + Self { + open_modal: None, + subscription: None, + registered_modals: Vec::new(), + } + } + pub fn register_modal(&mut self, action: A, build_view: B) where V: Modal, @@ -44,32 +42,19 @@ impl ModalRegistry { let build_view = build_view.clone(); div.on_action(move |workspace, event: &A, cx| { - let Some(new_modal) = - (build_view)(workspace, cx) else { - return - }; - workspace.modal_layer.update(cx, |modal_layer, cx| { - modal_layer.show_modal(new_modal, cx); - }) + let Some(new_modal) = (build_view)(workspace, cx) else { + return; + }; + workspace.modal_layer().show_modal(new_modal, cx); }) }), )); } -} -impl ModalLayer { - pub fn new(workspace: WeakView) -> Self { - Self { - workspace, - open_modal: None, - subscription: None, - } - } - - pub fn show_modal(&mut self, new_modal: View, cx: &mut ViewContext) { + pub fn show_modal(&mut self, new_modal: View, cx: &mut ViewContext) { self.subscription = Some(cx.subscribe(&new_modal, |this, modal, e, cx| { match modal.read(cx).to_modal_event(e) { - Some(ModalEvent::Dismissed) => this.hide_modal(cx), + Some(ModalEvent::Dismissed) => this.modal_layer().hide_modal(cx), None => {} } })); @@ -77,16 +62,16 @@ impl ModalLayer { cx.notify(); } - pub fn hide_modal(&mut self, cx: &mut ViewContext) { + pub fn hide_modal(&mut self, cx: &mut ViewContext) { self.open_modal.take(); self.subscription.take(); cx.notify(); } - pub fn render(&self, cx: &ViewContext) -> Div { + pub fn wrapper_element(&self, cx: &ViewContext) -> Div { let mut parent = div().relative().size_full(); - for (_, action) in cx.global::().registered_modals.iter() { + for (_, action) in self.registered_modals.iter() { parent = (action)(parent); } diff --git a/crates/workspace2/src/toolbar.rs b/crates/workspace2/src/toolbar.rs index 80503ad7bb..25054571da 100644 --- a/crates/workspace2/src/toolbar.rs +++ b/crates/workspace2/src/toolbar.rs @@ -1,6 +1,7 @@ use crate::ItemHandle; use gpui::{ - AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext, + AnyView, AppContext, Div, Entity, EntityId, EventEmitter, Render, View, ViewContext, + WindowContext, }; pub trait ToolbarItemView: Render + EventEmitter { @@ -56,6 +57,14 @@ pub struct Toolbar { items: Vec<(Box, ToolbarItemLocation)>, } +impl Render for Toolbar { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + todo!() + } +} + // todo!() // impl View for Toolbar { // fn ui_name() -> &'static str { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 4eda0e2af6..e11bcdc163 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -37,10 +37,10 @@ use futures::{ }; use gpui::{ div, point, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, - AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, FocusHandle, - GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, StatefulInteractive, - Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, - WindowContext, WindowHandle, WindowOptions, + AsyncWindowContext, Bounds, Component, Context, Div, Entity, EntityId, EventEmitter, + FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, + StatefulInteractive, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, + WindowBounds, WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -225,7 +225,6 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(app_state: Arc, cx: &mut AppContext) { init_settings(cx); - init_modal_registry(cx); pane::init(cx); notifications::init(cx); @@ -545,7 +544,7 @@ pub struct Workspace { last_active_center_pane: Option>, last_active_view_id: Option, status_bar: View, - modal_layer: View, + modal_layer: ModalLayer, // titlebar_item: Option, notifications: Vec<(TypeId, usize, Box)>, project: Model, @@ -697,7 +696,7 @@ impl Workspace { }); let workspace_handle = cx.view().downgrade(); - let modal_layer = cx.build_view(|cx| ModalLayer::new(workspace_handle)); + let modal_layer = ModalLayer::new(); // todo!() // cx.update_default_global::, _, _>(|drag_and_drop, _| { @@ -781,6 +780,10 @@ impl Workspace { } } + pub fn modal_layer(&mut self) -> &mut ModalLayer { + &mut self.modal_layer + } + fn new_local( abs_paths: Vec, app_state: Arc, @@ -3707,9 +3710,9 @@ impl Render for Workspace { .bg(cx.theme().colors().background) .child(self.render_titlebar(cx)) .child( + // todo! should this be a component a view? self.modal_layer - .read(cx) - .render(cx) + .wrapper_element(cx) .relative() .flex_1() .w_full() From cef8fa5570d3f32fe45a6d3dd7507b31fe420e63 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 8 Nov 2023 17:09:38 -0700 Subject: [PATCH 8/8] tidy tidy --- crates/go_to_line2/src/go_to_line.rs | 267 +++++++-------------------- crates/workspace2/src/workspace2.rs | 8 +- 2 files changed, 69 insertions(+), 206 deletions(-) diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index c9a04b408f..af69d42d41 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -1,9 +1,10 @@ -use editor::Editor; +use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor}; use gpui::{ actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString, - StatefulInteractivity, StatelessInteractive, Styled, View, ViewContext, VisualContext, + StatefulInteractivity, StatelessInteractive, Styled, Subscription, View, ViewContext, + VisualContext, WindowContext, }; -use text::Point; +use text::{Bias, Point}; use theme::ActiveTheme; use ui::{h_stack, modal, v_stack, Label, LabelColor}; use util::paths::FILE_ROW_COLUMN_DELIMITER; @@ -23,39 +24,17 @@ pub fn init(cx: &mut AppContext) { Some(cx.build_view(|cx| GoToLine::new(editor, cx))) }); - dbg!("hey!"); }, ) .detach(); - - // // cx.window_global() - // // cx.window_global:: - // // cx.window_global::() - // Workspace::on_init(|workspace, cx| { - // workspace.on_open_item() - // }); - - // Editor::on_init(|editor, cx|{ - - // }) - - // Editor::register_action(|_editor, _: &Toggle, cx| { - // dbg!("HEY!"); - // // let editor = cx.view(); - // // cx.update_window(cx.window().handle(), |cx, view| { - // // let workspace = view.downcast::(); - // // }) - // // workspace.show_modal(cx.build_view(|cx| GoToLine::new(editor, cx))) - // }) - // cx.global_mut::() - // .register_modal(Toggle, |workspace, cx| { - // }); } pub struct GoToLine { line_editor: View, - #[allow(unused)] // todo!() active_editor: View, + current_text: SharedString, + prev_scroll_position: Option>, + _subscriptions: Vec, } pub enum Event { @@ -69,20 +48,45 @@ impl EventEmitter for GoToLine { impl GoToLine { pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { let line_editor = cx.build_view(|cx| { - let mut editor = Editor::single_line(cx); - editor.set_placeholder_text("Find something", cx); + let editor = Editor::single_line(cx); editor.focus(cx); editor }); - cx.subscribe(&line_editor, Self::on_line_editor_event) - .detach(); + let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event); + + let editor = active_editor.read(cx); + let cursor = editor.selections.last::(cx).head(); + let last_line = editor.buffer().read(cx).snapshot(cx).max_point().row; + let scroll_position = active_editor.update(cx, |editor, cx| editor.scroll_position(cx)); + + let current_text = format!( + "line {} of {} (column {})", + cursor.row + 1, + last_line + 1, + cursor.column + 1, + ); Self { line_editor, active_editor, + current_text: current_text.into(), + prev_scroll_position: Some(scroll_position), + _subscriptions: vec![line_editor_change, cx.on_release(Self::release)], } } + fn release(&mut self, cx: &mut WindowContext) { + let scroll_position = self.prev_scroll_position.take(); + self.active_editor.update(cx, |editor, cx| { + editor.focus(cx); + editor.highlight_rows(None); + if let Some(scroll_position) = scroll_position { + editor.set_scroll_position(scroll_position, cx); + } + cx.notify(); + }) + } + fn on_line_editor_event( &mut self, _: View, @@ -90,28 +94,28 @@ impl GoToLine { cx: &mut ViewContext, ) { match event { + // todo!() this isn't working... editor::Event::Blurred => cx.emit(Event::Dismissed), - editor::Event::BufferEdited { .. } => { - // if let Some(point) = self.point_from_query(cx) { - // todo!() - // self.active_editor.update(cx, |active_editor, cx| { - // let snapshot = active_editor.snapshot(cx).display_snapshot; - // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); - // let display_point = point.to_display_point(&snapshot); - // let row = display_point.row(); - // active_editor.highlight_rows(Some(row..row + 1)); - // active_editor.request_autoscroll(Autoscroll::center(), cx); - // }); - // cx.notify(); - // } - } + editor::Event::BufferEdited { .. } => self.highlight_current_line(cx), _ => {} } } - #[allow(unused)] + fn highlight_current_line(&mut self, cx: &mut ViewContext) { + if let Some(point) = self.point_from_query(cx) { + self.active_editor.update(cx, |active_editor, cx| { + let snapshot = active_editor.snapshot(cx).display_snapshot; + let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + let display_point = point.to_display_point(&snapshot); + let row = display_point.row(); + active_editor.highlight_rows(Some(row..row + 1)); + active_editor.request_autoscroll(Autoscroll::center(), cx); + }); + cx.notify(); + } + } + fn point_from_query(&self, cx: &ViewContext) -> Option { - // todo!() let line_editor = self.line_editor.read(cx).text(cx); let mut components = line_editor .splitn(2, FILE_ROW_COLUMN_DELIMITER) @@ -126,26 +130,22 @@ impl GoToLine { } fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext) { - println!("CANCLE"); cx.emit(Event::Dismissed); } - fn confirm(&mut self, _: &menu::Confirm, _cx: &mut ViewContext) { - // // if let Some(point) = self.point_from_query(cx) { - // // self.active_editor.update(cx, |active_editor, cx| { - // // let snapshot = active_editor.snapshot(cx).display_snapshot; - // // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); - // // active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { - // // s.select_ranges([point..point]) - // // }); - // // }); - // // } + fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { + if let Some(point) = self.point_from_query(cx) { + self.active_editor.update(cx, |active_editor, cx| { + let snapshot = active_editor.snapshot(cx).display_snapshot; + let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { + s.select_ranges([point..point]) + }); + }); + self.prev_scroll_position.take(); + } - // cx.emit(Event::Dismissed); - } - - fn status_text(&self) -> SharedString { - "Default text".into() + cx.emit(Event::Dismissed); } } @@ -188,145 +188,8 @@ impl Render for GoToLine { .justify_between() .px_2() .py_1() - .child(Label::new(self.status_text()).color(LabelColor::Muted)), + .child(Label::new(self.current_text.clone()).color(LabelColor::Muted)), ), ) } } - -// pub struct GoToLine { -// //line_editor: View, -// active_editor: View, -// prev_scroll_position: Option>, -// cursor_point: Point, -// max_point: Point, -// has_focus: bool, -// } - -// pub enum Event { -// Dismissed, -// } - -// impl GoToLine { -// pub fn new(active_editor: View, cx: &mut ViewContext) -> Self { - -// let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| { -// let scroll_position = editor.scroll_position(cx); -// let buffer = editor.buffer().read(cx).snapshot(cx); -// ( -// Some(scroll_position), -// editor.selections.newest(cx).head(), -// buffer.max_point(), -// ) -// }); - -// cx.on_release(|_, on_release| {}).detach(); - -// Self { -// //line_editor, -// active_editor, -// prev_scroll_position: scroll_position, -// cursor_point, -// max_point, -// has_focus: false, -// } -// } - -// fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { -// cx.emit(Event::Dismissed); -// } - -// fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { -// self.prev_scroll_position.take(); -// if let Some(point) = self.point_from_query(cx) { -// self.active_editor.update(cx, |active_editor, cx| { -// let snapshot = active_editor.snapshot(cx).display_snapshot; -// let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); -// active_editor.change_selections(Some(Autoscroll::center()), cx, |s| { -// s.select_ranges([point..point]) -// }); -// }); -// } - -// cx.emit(Event::Dismissed); -// } - -// impl EventEmitter for GoToLine { -// type Event = Event; -// } - -// impl Entity for GoToLine { -// fn release(&mut self, cx: &mut AppContext) { -// let scroll_position = self.prev_scroll_position.take(); -// self.active_editor.window().update(cx, |cx| { -// self.active_editor.update(cx, |editor, cx| { -// editor.highlight_rows(None); -// if let Some(scroll_position) = scroll_position { -// editor.set_scroll_position(scroll_position, cx); -// } -// }) -// }); -// } -// } - -// impl Render for GoToLine { -// type Element = Div; - -// fn render(&mut self, cx: &mut ViewContext) -> Self::Element { -// // todo!() -// div() -// } -// } - -// impl View for GoToLine { -// fn ui_name() -> &'static str { -// "GoToLine" -// } - -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// let theme = &theme::current(cx).picker; - -// let label = format!( -// "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines", -// self.cursor_point.row + 1, -// self.cursor_point.column + 1, -// self.max_point.row + 1 -// ); - -// Flex::new(Axis::Vertical) -// .with_child( -// ChildView::new(&self.line_editor, cx) -// .contained() -// .with_style(theme.input_editor.container), -// ) -// .with_child( -// Label::new(label, theme.no_matches.label.clone()) -// .contained() -// .with_style(theme.no_matches.container), -// ) -// .contained() -// .with_style(theme.container) -// .constrained() -// .with_max_width(500.0) -// .into_any_named("go to line") -// } - -// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext) { -// self.has_focus = true; -// cx.focus(&self.line_editor); -// } - -// fn focus_out(&mut self, _: AnyView, _: &mut ViewContext) { -// self.has_focus = false; -// } -// } - -// impl Modal for GoToLine { -// fn has_focus(&self) -> bool { -// self.has_focus -// } - -// fn dismiss_on_event(event: &Self::Event) -> bool { -// matches!(event, Event::Dismissed) -// } -// } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index e11bcdc163..cfa0d6fa07 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -37,10 +37,10 @@ use futures::{ }; use gpui::{ div, point, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, - AsyncWindowContext, Bounds, Component, Context, Div, Entity, EntityId, EventEmitter, - FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, - StatefulInteractive, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, - WindowBounds, WindowContext, WindowHandle, WindowOptions, + AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, FocusHandle, + GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, StatefulInteractive, + Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, + WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools;