From 0f157d50830056cd029677a86c088ae463c1e462 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Apr 2021 14:36:16 -0700 Subject: [PATCH 01/12] Silence warning in text example --- gpui/examples/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpui/examples/text.rs b/gpui/examples/text.rs index bc07057fb1..e481ce5e2f 100644 --- a/gpui/examples/text.rs +++ b/gpui/examples/text.rs @@ -31,7 +31,7 @@ impl gpui::View for TextView { "View" } - fn render<'a>(&self, app: &gpui::AppContext) -> gpui::ElementBox { + fn render<'a>(&self, _: &gpui::AppContext) -> gpui::ElementBox { TextElement.boxed() } } From dabd6abe3765825232a1f53e45077156a7be3fe5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Apr 2021 14:37:21 -0700 Subject: [PATCH 02/12] Start work on displaying modified status in tabs --- gpui/src/elements/canvas.rs | 73 +++++++++++++++++++++++++++++ gpui/src/elements/mod.rs | 2 + zed/src/editor/buffer_view.rs | 4 ++ zed/src/workspace/pane.rs | 53 ++++++++++++++++++++- zed/src/workspace/workspace_view.rs | 8 ++++ 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 gpui/src/elements/canvas.rs diff --git a/gpui/src/elements/canvas.rs b/gpui/src/elements/canvas.rs new file mode 100644 index 0000000000..4788ab9b9d --- /dev/null +++ b/gpui/src/elements/canvas.rs @@ -0,0 +1,73 @@ +use super::Element; +use crate::PaintContext; +use pathfinder_geometry::{ + rect::RectF, + vector::{vec2f, Vector2F}, +}; + +pub struct Canvas(F) +where + F: FnMut(RectF, &mut PaintContext); + +impl Canvas +where + F: FnMut(RectF, &mut PaintContext), +{ + pub fn new(f: F) -> Self { + Self(f) + } +} + +impl Element for Canvas +where + F: FnMut(RectF, &mut PaintContext), +{ + type LayoutState = (); + type PaintState = (); + + fn layout( + &mut self, + constraint: crate::SizeConstraint, + _: &mut crate::LayoutContext, + ) -> (Vector2F, Self::LayoutState) { + let x = if constraint.max.x().is_finite() { + constraint.max.x() + } else { + constraint.min.x() + }; + let y = if constraint.max.y().is_finite() { + constraint.max.y() + } else { + constraint.min.y() + }; + (vec2f(x, y), ()) + } + + fn paint( + &mut self, + bounds: RectF, + _: &mut Self::LayoutState, + ctx: &mut PaintContext, + ) -> Self::PaintState { + self.0(bounds, ctx) + } + + fn after_layout( + &mut self, + _: Vector2F, + _: &mut Self::LayoutState, + _: &mut crate::AfterLayoutContext, + ) { + } + + fn dispatch_event( + &mut self, + _: &crate::Event, + _: RectF, + _: &mut Self::LayoutState, + _: &mut Self::PaintState, + _: &mut crate::EventContext, + ) -> bool { + false + } +} diff --git a/gpui/src/elements/mod.rs b/gpui/src/elements/mod.rs index 121fa9b6a6..1bcfa7f6fa 100644 --- a/gpui/src/elements/mod.rs +++ b/gpui/src/elements/mod.rs @@ -1,4 +1,5 @@ mod align; +mod canvas; mod constrained_box; mod container; mod empty; @@ -13,6 +14,7 @@ mod uniform_list; pub use crate::presenter::ChildView; pub use align::*; +pub use canvas::*; pub use constrained_box::*; pub use container::*; pub use empty::*; diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 53b7415d83..d842096c0c 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -1181,6 +1181,10 @@ impl workspace::ItemView for BufferView { fn save(&self, ctx: &mut MutableAppContext) -> Option>> { self.buffer.update(ctx, |buffer, ctx| buffer.save(ctx)) } + + fn is_modified(&self, ctx: &AppContext) -> bool { + self.buffer.as_ref(ctx).is_modified() + } } impl Selection { diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index c900340a04..e2662eab96 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -1,7 +1,11 @@ use super::{ItemViewHandle, SplitDirection}; use crate::{settings::Settings, watch}; use gpui::{ - color::ColorU, elements::*, keymap::Binding, App, AppContext, Border, Entity, View, ViewContext, + color::{ColorF, ColorU}, + elements::*, + geometry::{rect::RectF, vector::vec2f}, + keymap::Binding, + App, AppContext, Border, Entity, Quad, View, ViewContext, }; use std::cmp; @@ -190,7 +194,32 @@ impl Pane { let padding = 6.; let mut container = Container::new( Align::new( - Label::new(title, settings.ui_font_family, settings.ui_font_size).boxed(), + Flex::row() + .with_child( + Expanded::new( + 1.0, + Label::new(title, settings.ui_font_family, settings.ui_font_size) + .boxed(), + ) + .boxed(), + ) + .with_child( + Expanded::new( + 1.0, + LineBox::new( + settings.ui_font_family, + settings.ui_font_size, + ConstrainedBox::new(Self::render_modified_icon( + item.is_modified(app), + )) + .with_max_width(12.) + .boxed(), + ) + .boxed(), + ) + .boxed(), + ) + .boxed(), ) .boxed(), ) @@ -243,6 +272,26 @@ impl Pane { row.boxed() } + + fn render_modified_icon(is_modified: bool) -> ElementBox { + Canvas::new(move |bounds, ctx| { + if is_modified { + let padding = if bounds.height() < bounds.width() { + vec2f(bounds.width() - bounds.height(), 0.0) + } else { + vec2f(0.0, bounds.height() - bounds.width()) + }; + let square = RectF::new(bounds.origin() + padding / 2., bounds.size() - padding); + ctx.scene.push_quad(Quad { + bounds: square, + background: Some(ColorF::new(0.639, 0.839, 1.0, 1.0).to_u8()), + border: Default::default(), + corner_radius: square.width() / 2., + }); + } + }) + .boxed() + } } impl Entity for Pane { diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 1f442866d2..fa61bdcb0d 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -22,6 +22,9 @@ pub trait ItemView: View { { None } + fn is_modified(&self, _: &AppContext) -> bool { + false + } fn save(&self, _: &mut MutableAppContext) -> Option>> { None } @@ -35,6 +38,7 @@ pub trait ItemViewHandle: Send + Sync { fn set_parent_pane(&self, pane: &ViewHandle, app: &mut MutableAppContext); fn id(&self) -> usize; fn to_any(&self) -> AnyViewHandle; + fn is_modified(&self, ctx: &AppContext) -> bool; fn save(&self, ctx: &mut MutableAppContext) -> Option>>; } @@ -75,6 +79,10 @@ impl ItemViewHandle for ViewHandle { self.update(ctx, |item, ctx| item.save(ctx.app_mut())) } + fn is_modified(&self, ctx: &AppContext) -> bool { + self.as_ref(ctx).is_modified(ctx) + } + fn id(&self) -> usize { self.id() } From bd37b113065fe32c9ec2e7b5e3a1e1d4678c03f4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Apr 2021 15:47:05 -0700 Subject: [PATCH 03/12] Update tab's modified icon via a saved event emitted from buffer --- zed/src/editor/buffer/mod.rs | 5 +++++ zed/src/editor/buffer_view.rs | 20 +++++++++++++------- zed/src/editor/display_map/mod.rs | 1 + zed/src/file_finder.rs | 2 +- zed/src/workspace/workspace_view.rs | 24 +++++++++++++++++------- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 374343c237..abf9121039 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -244,6 +244,10 @@ impl Buffer { pub fn save(&self, ctx: &mut ModelContext) -> Option>> { if let Some(file) = &self.file { let snapshot = self.snapshot(); + + // TODO - don't emit this until the save has finished + ctx.emit(Event::Saved); + Some(file.save(snapshot, ctx.app())) } else { None @@ -1395,6 +1399,7 @@ impl Snapshot { #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { Edited(Vec), + Saved, } impl Entity for Buffer { diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index d842096c0c..a02c9fa54a 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -2,12 +2,15 @@ use super::{ buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point, ToOffset, ToPoint, }; -use crate::{settings::Settings, watch, workspace}; +use crate::{ + settings::Settings, + watch, + workspace::{self, ItemEventEffect}, +}; use anyhow::Result; use gpui::{ fonts::Properties as FontProperties, keymap::Binding, text_layout, App, AppContext, Element, - ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, Task, View, ViewContext, - WeakViewHandle, + ElementBox, Entity, FontCache, ModelHandle, Task, View, ViewContext, WeakViewHandle, }; use gpui::{geometry::vector::Vector2F, TextLayoutCache}; use parking_lot::Mutex; @@ -1091,6 +1094,7 @@ impl BufferView { ) { match event { buffer::Event::Edited(_) => ctx.emit(Event::Edited), + buffer::Event::Saved => ctx.emit(Event::Saved), } } } @@ -1106,6 +1110,7 @@ pub enum Event { Activate, Edited, Blurred, + Saved, } impl Entity for BufferView { @@ -1147,10 +1152,11 @@ impl workspace::Item for Buffer { } impl workspace::ItemView for BufferView { - fn is_activate_event(event: &Self::Event) -> bool { + fn event_effect(event: &Self::Event) -> ItemEventEffect { match event { - Event::Activate => true, - _ => false, + Event::Activate => ItemEventEffect::Activate, + Event::Edited => ItemEventEffect::ChangeTab, + _ => ItemEventEffect::None, } } @@ -1178,7 +1184,7 @@ impl workspace::ItemView for BufferView { Some(clone) } - fn save(&self, ctx: &mut MutableAppContext) -> Option>> { + fn save(&self, ctx: &mut ViewContext) -> Option>> { self.buffer.update(ctx, |buffer, ctx| buffer.save(ctx)) } diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 72e140d149..2679766aa1 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -126,6 +126,7 @@ impl DisplayMap { fn handle_buffer_event(&mut self, event: &buffer::Event, ctx: &mut ModelContext) { match event { buffer::Event::Edited(edits) => self.fold_map.apply_edits(edits, ctx.app()).unwrap(), + _ => {} } } } diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index fcf32ea64c..39b497f5c8 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -309,7 +309,7 @@ impl FileFinder { } } Blurred => ctx.emit(Event::Dismissed), - Activate => {} + _ => {} } } diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index fa61bdcb0d..adc3a3b5f9 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -12,8 +12,14 @@ pub fn init(app: &mut App) { app.add_bindings(vec![Binding::new("cmd-s", "workspace:save", None)]); } +pub enum ItemEventEffect { + None, + ChangeTab, + Activate, +} + pub trait ItemView: View { - fn is_activate_event(event: &Self::Event) -> bool; + fn event_effect(event: &Self::Event) -> ItemEventEffect; fn title(&self, app: &AppContext) -> String; fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>; fn clone_on_split(&self, _: &mut ViewContext) -> Option @@ -25,7 +31,7 @@ pub trait ItemView: View { fn is_modified(&self, _: &AppContext) -> bool { false } - fn save(&self, _: &mut MutableAppContext) -> Option>> { + fn save(&self, _: &mut ViewContext) -> Option>> { None } } @@ -65,18 +71,22 @@ impl ItemViewHandle for ViewHandle { fn set_parent_pane(&self, pane: &ViewHandle, app: &mut MutableAppContext) { pane.update(app, |_, ctx| { ctx.subscribe_to_view(self, |pane, item, event, ctx| { - if T::is_activate_event(event) { - if let Some(ix) = pane.item_index(&item) { - pane.activate_item(ix, ctx); - pane.activate(ctx); + match T::event_effect(event) { + ItemEventEffect::Activate => { + if let Some(ix) = pane.item_index(&item) { + pane.activate_item(ix, ctx); + pane.activate(ctx); + } } + ItemEventEffect::ChangeTab => ctx.notify(), + _ => {} } }) }) } fn save(&self, ctx: &mut MutableAppContext) -> Option>> { - self.update(ctx, |item, ctx| item.save(ctx.app_mut())) + self.update(ctx, |item, ctx| item.save(ctx)) } fn is_modified(&self, ctx: &AppContext) -> bool { From 685665f3c0287c00c7fa541878dc855f07e92483 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Apr 2021 16:19:17 -0700 Subject: [PATCH 04/12] Implement Buffer::is_modified by storing persisted version --- zed/src/editor/buffer/mod.rs | 53 +++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index abf9121039..9fcca50113 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -36,6 +36,7 @@ pub struct Buffer { fragments: SumTree, insertion_splits: HashMap>, pub version: time::Global, + persisted_version: time::Global, last_edit: time::Local, selections: HashMap>, pub selections_last_update: SelectionsVersion, @@ -216,6 +217,7 @@ impl Buffer { fragments, insertion_splits, version: time::Global::new(), + persisted_version: time::Global::new(), last_edit: time::Local::default(), selections: HashMap::default(), selections_last_update: 0, @@ -241,21 +243,28 @@ impl Buffer { } } - pub fn save(&self, ctx: &mut ModelContext) -> Option>> { + pub fn save(&mut self, ctx: &mut ModelContext) -> Option>> { if let Some(file) = &self.file { let snapshot = self.snapshot(); - // TODO - don't emit this until the save has finished - ctx.emit(Event::Saved); + let result = file.save(snapshot, ctx.app()); - Some(file.save(snapshot, ctx.app())) + // TODO - don't do this until the save has finished + self.did_save(ctx); + + Some(result) } else { None } } + fn did_save(&mut self, ctx: &mut ModelContext) { + self.persisted_version = self.fragments.summary().max_version; + ctx.emit(Event::Saved); + } + pub fn is_modified(&self) -> bool { - self.version != time::Global::new() + self.fragments.summary().max_version > self.persisted_version } pub fn text_summary(&self) -> TextSummary { @@ -1374,6 +1383,7 @@ impl Clone for Buffer { fragments: self.fragments.clone(), insertion_splits: self.insertion_splits.clone(), version: self.version.clone(), + persisted_version: self.persisted_version.clone(), last_edit: self.last_edit.clone(), selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), @@ -1953,6 +1963,7 @@ impl ToPoint for usize { #[cfg(test)] mod tests { use super::*; + use gpui::App; use std::collections::BTreeMap; #[test] @@ -1975,7 +1986,6 @@ mod tests { #[test] fn test_edit_events() { - use gpui::App; use std::{cell::RefCell, rc::Rc}; App::test((), |mut app| async move { @@ -2489,11 +2499,34 @@ mod tests { #[test] fn test_is_modified() -> Result<()> { - let mut buffer = Buffer::new(0, "abc"); - assert!(!buffer.is_modified()); - buffer.edit(vec![1..2], "", None)?; - assert!(buffer.is_modified()); + App::test((), |mut app| async move { + let model = app.add_model(|_| Buffer::new(0, "abc")); + model.update(&mut app, |buffer, ctx| { + // initially, buffer isn't modified. + assert!(!buffer.is_modified()); + // after editing, buffer is modified. + buffer.edit(vec![1..2], "", None).unwrap(); + assert!(buffer.text() == "ac"); + assert!(buffer.is_modified()); + + // after saving, buffer is not modified. + buffer.did_save(ctx); + assert!(!buffer.is_modified()); + + // after editing again, buffer is modified. + buffer.edit(vec![1..1], "B", None).unwrap(); + buffer.edit(vec![2..2], "D", None).unwrap(); + assert!(buffer.text() == "aBDc"); + assert!(buffer.is_modified()); + + // TODO - currently, after restoring the buffer to its + // saved state, it is still considered modified. + buffer.edit(vec![1..3], "", None).unwrap(); + assert!(buffer.text() == "ac"); + assert!(buffer.is_modified()); + }); + }); Ok(()) } From d724387158a09453e7c72505e08856614e58bb2a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 6 Apr 2021 18:25:50 -0700 Subject: [PATCH 05/12] wip --- Cargo.lock | 1 + zed/Cargo.toml | 1 + zed/src/editor/buffer/mod.rs | 33 ++++++++++++++--------- zed/src/editor/buffer_view.rs | 15 ++++++----- zed/src/workspace/workspace_view.rs | 41 ++++++++++++++--------------- 5 files changed, 50 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4cd3076580..38d75ed246 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2241,6 +2241,7 @@ dependencies = [ "crossbeam-channel 0.5.0", "dirs", "easy-parallel", + "futures-core", "gpui", "ignore", "lazy_static", diff --git a/zed/Cargo.toml b/zed/Cargo.toml index c7026445cc..b1cecf1ae0 100644 --- a/zed/Cargo.toml +++ b/zed/Cargo.toml @@ -20,6 +20,7 @@ dirs = "3.0" easy-parallel = "3.1.0" gpui = {path = "../gpui"} ignore = {git = "https://github.com/zed-industries/ripgrep", rev = "1d152118f35b3e3590216709b86277062d79b8a0"} +futures-core = "0.3" lazy_static = "1.4.0" libc = "0.2" log = "0.4" diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 9fcca50113..2e25a70bae 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -3,6 +3,7 @@ mod point; mod text; pub use anchor::*; +use futures_core::future::LocalBoxFuture; pub use point::*; pub use text::*; @@ -14,7 +15,7 @@ use crate::{ worktree::FileHandle, }; use anyhow::{anyhow, Result}; -use gpui::{AppContext, Entity, ModelContext, Task}; +use gpui::{AppContext, Entity, ModelContext}; use lazy_static::lazy_static; use rand::prelude::*; use std::{ @@ -243,23 +244,25 @@ impl Buffer { } } - pub fn save(&mut self, ctx: &mut ModelContext) -> Option>> { + pub fn save(&mut self, ctx: &mut ModelContext) -> LocalBoxFuture<'static, Result<()>> { if let Some(file) = &self.file { let snapshot = self.snapshot(); - - let result = file.save(snapshot, ctx.app()); - - // TODO - don't do this until the save has finished - self.did_save(ctx); - - Some(result) + let version = self.version.clone(); + let save_task = file.save(snapshot, ctx.app()); + let task = ctx.spawn(save_task, |me, save_result, ctx| { + if save_result.is_ok() { + me.did_save(version, ctx); + } + save_result + }); + Box::pin(task) } else { - None + Box::pin(async { Ok(()) }) } } - fn did_save(&mut self, ctx: &mut ModelContext) { - self.persisted_version = self.fragments.summary().max_version; + fn did_save(&mut self, version: time::Global, ctx: &mut ModelContext) { + self.persisted_version = version; ctx.emit(Event::Saved); } @@ -429,7 +432,7 @@ impl Buffer { ctx.notify(); let changes = self.edits_since(old_version).collect::>(); if !changes.is_empty() { - ctx.emit(Event::Edited(changes)) + self.did_edit(changes, ctx); } } @@ -447,6 +450,10 @@ impl Buffer { Ok(ops) } + fn did_edit(&self, changes: Vec, ctx: &mut ModelContext) { + ctx.emit(Event::Edited(changes)) + } + pub fn simulate_typing(&mut self, rng: &mut T) { let end = rng.gen_range(0..self.len() + 1); let start = rng.gen_range(0..end + 1); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index a02c9fa54a..3057bd4ffc 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -5,12 +5,13 @@ use super::{ use crate::{ settings::Settings, watch, - workspace::{self, ItemEventEffect}, + workspace::{self, WorkspaceEvent}, }; use anyhow::Result; +use futures_core::future::LocalBoxFuture; use gpui::{ fonts::Properties as FontProperties, keymap::Binding, text_layout, App, AppContext, Element, - ElementBox, Entity, FontCache, ModelHandle, Task, View, ViewContext, WeakViewHandle, + ElementBox, Entity, FontCache, ModelHandle, View, ViewContext, WeakViewHandle, }; use gpui::{geometry::vector::Vector2F, TextLayoutCache}; use parking_lot::Mutex; @@ -1152,11 +1153,11 @@ impl workspace::Item for Buffer { } impl workspace::ItemView for BufferView { - fn event_effect(event: &Self::Event) -> ItemEventEffect { + fn to_workspace_event(event: &Self::Event) -> Option { match event { - Event::Activate => ItemEventEffect::Activate, - Event::Edited => ItemEventEffect::ChangeTab, - _ => ItemEventEffect::None, + Event::Activate => Some(WorkspaceEvent::Activate), + Event::Saved => Some(WorkspaceEvent::TabStateChanged), + _ => None, } } @@ -1184,7 +1185,7 @@ impl workspace::ItemView for BufferView { Some(clone) } - fn save(&self, ctx: &mut ViewContext) -> Option>> { + fn save(&self, ctx: &mut ViewContext) -> LocalBoxFuture<'static, Result<()>> { self.buffer.update(ctx, |buffer, ctx| buffer.save(ctx)) } diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index adc3a3b5f9..5c62139a45 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -1,8 +1,9 @@ use super::{pane, Pane, PaneGroup, SplitDirection, Workspace}; use crate::{settings::Settings, watch}; +use futures_core::future::LocalBoxFuture; use gpui::{ color::rgbu, elements::*, keymap::Binding, AnyViewHandle, App, AppContext, Entity, ModelHandle, - MutableAppContext, Task, View, ViewContext, ViewHandle, + MutableAppContext, View, ViewContext, ViewHandle, }; use log::{error, info}; use std::{collections::HashSet, path::PathBuf}; @@ -12,14 +13,13 @@ pub fn init(app: &mut App) { app.add_bindings(vec![Binding::new("cmd-s", "workspace:save", None)]); } -pub enum ItemEventEffect { - None, - ChangeTab, +pub enum WorkspaceEvent { + TabStateChanged, Activate, } pub trait ItemView: View { - fn event_effect(event: &Self::Event) -> ItemEventEffect; + fn to_workspace_event(event: &Self::Event) -> Option; fn title(&self, app: &AppContext) -> String; fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>; fn clone_on_split(&self, _: &mut ViewContext) -> Option @@ -31,8 +31,8 @@ pub trait ItemView: View { fn is_modified(&self, _: &AppContext) -> bool { false } - fn save(&self, _: &mut ViewContext) -> Option>> { - None + fn save(&self, _: &mut ViewContext) -> LocalBoxFuture<'static, anyhow::Result<()>> { + Box::pin(async { Ok(()) }) } } @@ -45,7 +45,7 @@ pub trait ItemViewHandle: Send + Sync { fn id(&self) -> usize; fn to_any(&self) -> AnyViewHandle; fn is_modified(&self, ctx: &AppContext) -> bool; - fn save(&self, ctx: &mut MutableAppContext) -> Option>>; + fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>>; } impl ItemViewHandle for ViewHandle { @@ -71,21 +71,21 @@ impl ItemViewHandle for ViewHandle { fn set_parent_pane(&self, pane: &ViewHandle, app: &mut MutableAppContext) { pane.update(app, |_, ctx| { ctx.subscribe_to_view(self, |pane, item, event, ctx| { - match T::event_effect(event) { - ItemEventEffect::Activate => { + match T::to_workspace_event(event) { + Some(WorkspaceEvent::Activate) => { if let Some(ix) = pane.item_index(&item) { pane.activate_item(ix, ctx); pane.activate(ctx); } } - ItemEventEffect::ChangeTab => ctx.notify(), + Some(WorkspaceEvent::TabStateChanged) => ctx.notify(), _ => {} } }) }) } - fn save(&self, ctx: &mut MutableAppContext) -> Option>> { + fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>> { self.update(ctx, |item, ctx| item.save(ctx)) } @@ -240,15 +240,14 @@ impl WorkspaceView { pub fn save_active_item(&mut self, _: &(), ctx: &mut ViewContext) { self.active_pane.update(ctx, |pane, ctx| { if let Some(item) = pane.active_item() { - if let Some(task) = item.save(ctx.app_mut()) { - ctx.spawn(task, |_, result, _| { - if let Err(e) = result { - // TODO - present this error to the user - error!("failed to save item: {:?}, ", e); - } - }) - .detach(); - } + let task = item.save(ctx.app_mut()); + ctx.spawn(task, |_, result, _| { + if let Err(e) = result { + // TODO - present this error to the user + error!("failed to save item: {:?}, ", e); + } + }) + .detach() } }); } From 2619bc4602b4122c82d90db5204a5c9e925c64f5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 6 Apr 2021 20:46:35 -0600 Subject: [PATCH 06/12] Update modified status by emitting event whenever buffer is dirtied or saved I used the word "dirty" because it felt more expressive than "modified" to me, but not married to it. Tagging Max because we did a lot of this thinking together. Co-Authored-By: Max Brunsfeld --- zed/src/editor/buffer/mod.rs | 34 +++++++++++++++++++---------- zed/src/editor/buffer_view.rs | 14 +++++++----- zed/src/workspace/pane.rs | 2 +- zed/src/workspace/workspace_view.rs | 18 +++++++-------- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 2e25a70bae..0f4b985955 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -266,8 +266,12 @@ impl Buffer { ctx.emit(Event::Saved); } - pub fn is_modified(&self) -> bool { - self.fragments.summary().max_version > self.persisted_version + pub fn is_dirty(&self) -> bool { + self.version > self.persisted_version + } + + pub fn version(&self) -> time::Global { + self.version.clone() } pub fn text_summary(&self) -> TextSummary { @@ -414,6 +418,7 @@ impl Buffer { None }; + let was_dirty = self.is_dirty(); let old_version = self.version.clone(); let old_ranges = old_ranges .into_iter() @@ -432,7 +437,7 @@ impl Buffer { ctx.notify(); let changes = self.edits_since(old_version).collect::>(); if !changes.is_empty() { - self.did_edit(changes, ctx); + self.did_edit(changes, was_dirty, ctx); } } @@ -450,8 +455,11 @@ impl Buffer { Ok(ops) } - fn did_edit(&self, changes: Vec, ctx: &mut ModelContext) { - ctx.emit(Event::Edited(changes)) + fn did_edit(&self, changes: Vec, was_dirty: bool, ctx: &mut ModelContext) { + ctx.emit(Event::Edited(changes)); + if !was_dirty { + ctx.emit(Event::Dirtied); + } } pub fn simulate_typing(&mut self, rng: &mut T) { @@ -639,6 +647,7 @@ impl Buffer { ops: I, ctx: Option<&mut ModelContext>, ) -> Result<()> { + let was_dirty = self.is_dirty(); let old_version = self.version.clone(); let mut deferred_ops = Vec::new(); @@ -657,7 +666,7 @@ impl Buffer { ctx.notify(); let changes = self.edits_since(old_version).collect::>(); if !changes.is_empty() { - ctx.emit(Event::Edited(changes)); + self.did_edit(changes, was_dirty, ctx); } } @@ -1416,6 +1425,7 @@ impl Snapshot { #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { Edited(Vec), + Dirtied, Saved, } @@ -2510,28 +2520,28 @@ mod tests { let model = app.add_model(|_| Buffer::new(0, "abc")); model.update(&mut app, |buffer, ctx| { // initially, buffer isn't modified. - assert!(!buffer.is_modified()); + assert!(!buffer.is_dirty()); // after editing, buffer is modified. buffer.edit(vec![1..2], "", None).unwrap(); assert!(buffer.text() == "ac"); - assert!(buffer.is_modified()); + assert!(buffer.is_dirty()); // after saving, buffer is not modified. - buffer.did_save(ctx); - assert!(!buffer.is_modified()); + buffer.did_save(buffer.version(), ctx); + assert!(!buffer.is_dirty()); // after editing again, buffer is modified. buffer.edit(vec![1..1], "B", None).unwrap(); buffer.edit(vec![2..2], "D", None).unwrap(); assert!(buffer.text() == "aBDc"); - assert!(buffer.is_modified()); + assert!(buffer.is_dirty()); // TODO - currently, after restoring the buffer to its // saved state, it is still considered modified. buffer.edit(vec![1..3], "", None).unwrap(); assert!(buffer.text() == "ac"); - assert!(buffer.is_modified()); + assert!(buffer.is_dirty()); }); }); Ok(()) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 3057bd4ffc..1c5a07dca7 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -5,7 +5,7 @@ use super::{ use crate::{ settings::Settings, watch, - workspace::{self, WorkspaceEvent}, + workspace::{self, ItemViewEvent}, }; use anyhow::Result; use futures_core::future::LocalBoxFuture; @@ -1095,6 +1095,7 @@ impl BufferView { ) { match event { buffer::Event::Edited(_) => ctx.emit(Event::Edited), + buffer::Event::Dirtied => ctx.emit(Event::Dirtied), buffer::Event::Saved => ctx.emit(Event::Saved), } } @@ -1111,6 +1112,7 @@ pub enum Event { Activate, Edited, Blurred, + Dirtied, Saved, } @@ -1153,10 +1155,10 @@ impl workspace::Item for Buffer { } impl workspace::ItemView for BufferView { - fn to_workspace_event(event: &Self::Event) -> Option { + fn to_workspace_event(event: &Self::Event) -> Option { match event { - Event::Activate => Some(WorkspaceEvent::Activate), - Event::Saved => Some(WorkspaceEvent::TabStateChanged), + Event::Activate => Some(ItemViewEvent::Activated), + Event::Dirtied | Event::Saved => Some(ItemViewEvent::TabStateChanged), _ => None, } } @@ -1189,8 +1191,8 @@ impl workspace::ItemView for BufferView { self.buffer.update(ctx, |buffer, ctx| buffer.save(ctx)) } - fn is_modified(&self, ctx: &AppContext) -> bool { - self.buffer.as_ref(ctx).is_modified() + fn is_dirty(&self, ctx: &AppContext) -> bool { + self.buffer.as_ref(ctx).is_dirty() } } diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index e2662eab96..15c8195d31 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -210,7 +210,7 @@ impl Pane { settings.ui_font_family, settings.ui_font_size, ConstrainedBox::new(Self::render_modified_icon( - item.is_modified(app), + item.is_dirty(app), )) .with_max_width(12.) .boxed(), diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 5c62139a45..5f7222f292 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -13,13 +13,13 @@ pub fn init(app: &mut App) { app.add_bindings(vec![Binding::new("cmd-s", "workspace:save", None)]); } -pub enum WorkspaceEvent { +pub enum ItemViewEvent { TabStateChanged, - Activate, + Activated, } pub trait ItemView: View { - fn to_workspace_event(event: &Self::Event) -> Option; + fn to_workspace_event(event: &Self::Event) -> Option; fn title(&self, app: &AppContext) -> String; fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>; fn clone_on_split(&self, _: &mut ViewContext) -> Option @@ -28,7 +28,7 @@ pub trait ItemView: View { { None } - fn is_modified(&self, _: &AppContext) -> bool { + fn is_dirty(&self, _: &AppContext) -> bool { false } fn save(&self, _: &mut ViewContext) -> LocalBoxFuture<'static, anyhow::Result<()>> { @@ -44,7 +44,7 @@ pub trait ItemViewHandle: Send + Sync { fn set_parent_pane(&self, pane: &ViewHandle, app: &mut MutableAppContext); fn id(&self) -> usize; fn to_any(&self) -> AnyViewHandle; - fn is_modified(&self, ctx: &AppContext) -> bool; + fn is_dirty(&self, ctx: &AppContext) -> bool; fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>>; } @@ -72,13 +72,13 @@ impl ItemViewHandle for ViewHandle { pane.update(app, |_, ctx| { ctx.subscribe_to_view(self, |pane, item, event, ctx| { match T::to_workspace_event(event) { - Some(WorkspaceEvent::Activate) => { + Some(ItemViewEvent::Activated) => { if let Some(ix) = pane.item_index(&item) { pane.activate_item(ix, ctx); pane.activate(ctx); } } - Some(WorkspaceEvent::TabStateChanged) => ctx.notify(), + Some(ItemViewEvent::TabStateChanged) => ctx.notify(), _ => {} } }) @@ -89,8 +89,8 @@ impl ItemViewHandle for ViewHandle { self.update(ctx, |item, ctx| item.save(ctx)) } - fn is_modified(&self, ctx: &AppContext) -> bool { - self.as_ref(ctx).is_modified(ctx) + fn is_dirty(&self, ctx: &AppContext) -> bool { + self.as_ref(ctx).is_dirty(ctx) } fn id(&self) -> usize { From 13ee86e334542faba022864ec711aee8e20d23b4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 6 Apr 2021 20:47:09 -0600 Subject: [PATCH 07/12] Remove unused method --- gpui/src/platform/mac/window.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs index cf4c617512..e9e6318b5d 100644 --- a/gpui/src/platform/mac/window.rs +++ b/gpui/src/platform/mac/window.rs @@ -230,12 +230,6 @@ impl Window { Ok(window) } } - - pub fn zoom(&self) { - unsafe { - self.0.as_ref().borrow().native_window.performZoom_(nil); - } - } } impl Drop for Window { From 3f9d224e0f0a2a507aeeb3f8226565089cdffb9b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 6 Apr 2021 20:59:44 -0600 Subject: [PATCH 08/12] persisted_version -> saved_version --- zed/src/editor/buffer/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 0f4b985955..8a94b2b710 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -37,7 +37,7 @@ pub struct Buffer { fragments: SumTree, insertion_splits: HashMap>, pub version: time::Global, - persisted_version: time::Global, + saved_version: time::Global, last_edit: time::Local, selections: HashMap>, pub selections_last_update: SelectionsVersion, @@ -218,7 +218,7 @@ impl Buffer { fragments, insertion_splits, version: time::Global::new(), - persisted_version: time::Global::new(), + saved_version: time::Global::new(), last_edit: time::Local::default(), selections: HashMap::default(), selections_last_update: 0, @@ -262,12 +262,12 @@ impl Buffer { } fn did_save(&mut self, version: time::Global, ctx: &mut ModelContext) { - self.persisted_version = version; + self.saved_version = version; ctx.emit(Event::Saved); } pub fn is_dirty(&self) -> bool { - self.version > self.persisted_version + self.version > self.saved_version } pub fn version(&self) -> time::Global { @@ -1399,7 +1399,7 @@ impl Clone for Buffer { fragments: self.fragments.clone(), insertion_splits: self.insertion_splits.clone(), version: self.version.clone(), - persisted_version: self.persisted_version.clone(), + saved_version: self.saved_version.clone(), last_edit: self.last_edit.clone(), selections: self.selections.clone(), selections_last_update: self.selections_last_update.clone(), From 05125f76bdcf3b45d2733df579b4b1341903c938 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 6 Apr 2021 20:59:55 -0600 Subject: [PATCH 09/12] Fix tests --- zed/src/editor/buffer/mod.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 8a94b2b710..a263664c00 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2030,19 +2030,25 @@ mod tests { let buffer_1_events = buffer_1_events.borrow(); assert_eq!( *buffer_1_events, - vec![Event::Edited(vec![Edit { - old_range: 2..4, - new_range: 2..5 - }])] + vec![ + Event::Edited(vec![Edit { + old_range: 2..4, + new_range: 2..5 + },]), + Event::Dirtied + ] ); let buffer_2_events = buffer_2_events.borrow(); assert_eq!( *buffer_2_events, - vec![Event::Edited(vec![Edit { - old_range: 2..4, - new_range: 2..5 - }])] + vec![ + Event::Edited(vec![Edit { + old_range: 2..4, + new_range: 2..5 + },]), + Event::Dirtied + ] ); }); } From a95d33f66203b31869515966429ab5a82370e73d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Apr 2021 14:52:22 -0700 Subject: [PATCH 10/12] Add assertions about Dirtied events in test --- zed/src/editor/buffer/mod.rs | 82 ++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index a263664c00..973fbf6a2d 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -1982,6 +1982,7 @@ mod tests { use super::*; use gpui::App; use std::collections::BTreeMap; + use std::{cell::RefCell, rc::Rc}; #[test] fn test_edit() -> Result<()> { @@ -2003,8 +2004,6 @@ mod tests { #[test] fn test_edit_events() { - use std::{cell::RefCell, rc::Rc}; - App::test((), |mut app| async move { let buffer_1_events = Rc::new(RefCell::new(Vec::new())); let buffer_2_events = Rc::new(RefCell::new(Vec::new())); @@ -2524,31 +2523,86 @@ mod tests { fn test_is_modified() -> Result<()> { App::test((), |mut app| async move { let model = app.add_model(|_| Buffer::new(0, "abc")); - model.update(&mut app, |buffer, ctx| { - // initially, buffer isn't modified. - assert!(!buffer.is_dirty()); + let events = Rc::new(RefCell::new(Vec::new())); - // after editing, buffer is modified. - buffer.edit(vec![1..2], "", None).unwrap(); + // initially, the buffer isn't dirty. + model.update(&mut app, |buffer, ctx| { + ctx.subscribe(&model, { + let events = events.clone(); + move |_, event, _| events.borrow_mut().push(event.clone()) + }); + + assert!(!buffer.is_dirty()); + assert!(events.borrow().is_empty()); + + buffer.edit(vec![1..2], "", Some(ctx)).unwrap(); + }); + + // after the first edit, the buffer is dirty, and emits a dirtied event. + model.update(&mut app, |buffer, ctx| { assert!(buffer.text() == "ac"); assert!(buffer.is_dirty()); + assert_eq!( + *events.borrow(), + &[ + Event::Edited(vec![Edit { + old_range: 1..2, + new_range: 1..1 + }]), + Event::Dirtied + ] + ); + events.borrow_mut().clear(); - // after saving, buffer is not modified. buffer.did_save(buffer.version(), ctx); - assert!(!buffer.is_dirty()); + }); - // after editing again, buffer is modified. - buffer.edit(vec![1..1], "B", None).unwrap(); - buffer.edit(vec![2..2], "D", None).unwrap(); + // after saving, the buffer is not dirty, and emits a saved event. + model.update(&mut app, |buffer, ctx| { + assert!(!buffer.is_dirty()); + assert_eq!(*events.borrow(), &[Event::Saved]); + events.borrow_mut().clear(); + + buffer.edit(vec![1..1], "B", Some(ctx)).unwrap(); + buffer.edit(vec![2..2], "D", Some(ctx)).unwrap(); + }); + + // after editing again, the buffer is dirty, and emits another dirty event. + model.update(&mut app, |buffer, ctx| { assert!(buffer.text() == "aBDc"); assert!(buffer.is_dirty()); + assert_eq!( + *events.borrow(), + &[ + Event::Edited(vec![Edit { + old_range: 1..1, + new_range: 1..2 + }]), + Event::Dirtied, + Event::Edited(vec![Edit { + old_range: 2..2, + new_range: 2..3 + }]), + ], + ); + events.borrow_mut().clear(); // TODO - currently, after restoring the buffer to its - // saved state, it is still considered modified. - buffer.edit(vec![1..3], "", None).unwrap(); + // previously-saved state, the is still considered dirty. + buffer.edit(vec![1..3], "", Some(ctx)).unwrap(); assert!(buffer.text() == "ac"); assert!(buffer.is_dirty()); }); + + model.update(&mut app, |_, _| { + assert_eq!( + *events.borrow(), + &[Event::Edited(vec![Edit { + old_range: 1..3, + new_range: 1..1 + },])] + ); + }); }); Ok(()) } From ae57178f3e710b82f61048b05b9fd343d2fd13e5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Apr 2021 15:04:08 -0700 Subject: [PATCH 11/12] Restructure event-handling methods in workspace ItemView --- zed/src/editor/buffer_view.rs | 30 +++++++---------------------- zed/src/workspace/workspace_view.rs | 27 +++++++++++++------------- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 1c5a07dca7..6f3d6f108e 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -2,11 +2,7 @@ use super::{ buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point, ToOffset, ToPoint, }; -use crate::{ - settings::Settings, - watch, - workspace::{self, ItemViewEvent}, -}; +use crate::{settings::Settings, watch, workspace}; use anyhow::Result; use futures_core::future::LocalBoxFuture; use gpui::{ @@ -86,18 +82,6 @@ pub enum SelectAction { End, } -// impl workspace::Item for Buffer { -// type View = BufferView; - -// fn build_view( -// buffer: ModelHandle, -// settings: watch::Receiver, -// ctx: &mut ViewContext, -// ) -> Self::View { -// BufferView::for_buffer(buffer, settings, ctx) -// } -// } - pub struct BufferView { handle: WeakViewHandle, buffer: ModelHandle, @@ -1155,12 +1139,12 @@ impl workspace::Item for Buffer { } impl workspace::ItemView for BufferView { - fn to_workspace_event(event: &Self::Event) -> Option { - match event { - Event::Activate => Some(ItemViewEvent::Activated), - Event::Dirtied | Event::Saved => Some(ItemViewEvent::TabStateChanged), - _ => None, - } + fn should_activate_item_on_event(event: &Self::Event) -> bool { + matches!(event, Event::Activate) + } + + fn should_update_tab_on_event(event: &Self::Event) -> bool { + matches!(event, Event::Saved | Event::Dirtied) } fn title(&self, app: &AppContext) -> std::string::String { diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 5f7222f292..46cbdb0f28 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -13,13 +13,7 @@ pub fn init(app: &mut App) { app.add_bindings(vec![Binding::new("cmd-s", "workspace:save", None)]); } -pub enum ItemViewEvent { - TabStateChanged, - Activated, -} - pub trait ItemView: View { - fn to_workspace_event(event: &Self::Event) -> Option; fn title(&self, app: &AppContext) -> String; fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>; fn clone_on_split(&self, _: &mut ViewContext) -> Option @@ -34,6 +28,12 @@ pub trait ItemView: View { fn save(&self, _: &mut ViewContext) -> LocalBoxFuture<'static, anyhow::Result<()>> { Box::pin(async { Ok(()) }) } + fn should_activate_item_on_event(_: &Self::Event) -> bool { + false + } + fn should_update_tab_on_event(_: &Self::Event) -> bool { + false + } } pub trait ItemViewHandle: Send + Sync { @@ -71,15 +71,14 @@ impl ItemViewHandle for ViewHandle { fn set_parent_pane(&self, pane: &ViewHandle, app: &mut MutableAppContext) { pane.update(app, |_, ctx| { ctx.subscribe_to_view(self, |pane, item, event, ctx| { - match T::to_workspace_event(event) { - Some(ItemViewEvent::Activated) => { - if let Some(ix) = pane.item_index(&item) { - pane.activate_item(ix, ctx); - pane.activate(ctx); - } + if T::should_activate_item_on_event(event) { + if let Some(ix) = pane.item_index(&item) { + pane.activate_item(ix, ctx); + pane.activate(ctx); } - Some(ItemViewEvent::TabStateChanged) => ctx.notify(), - _ => {} + } + if T::should_update_tab_on_event(event) { + ctx.notify() } }) }) From 187eb95b1336a082e1a92852af9cf4c3455d9498 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Apr 2021 15:39:09 -0700 Subject: [PATCH 12/12] Throw a little between filename and modified icon We probably need a primitive to right-align an element within its parent, but I don't have strong opinions about how that should be designed, so I'm just adding this as a temporary measure so that the tabs won't look too ugly in the meantime. --- gpui/src/elements/container.rs | 5 +++++ zed/src/workspace/pane.rs | 12 ++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/gpui/src/elements/container.rs b/gpui/src/elements/container.rs index 13a80848c4..83b3e820e3 100644 --- a/gpui/src/elements/container.rs +++ b/gpui/src/elements/container.rs @@ -36,6 +36,11 @@ impl Container { self } + pub fn with_margin_left(mut self, margin: f32) -> Self { + self.margin.left = margin; + self + } + pub fn with_uniform_padding(mut self, padding: f32) -> Self { self.padding = Padding { top: padding, diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index 15c8195d31..845fa0c4a7 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -196,16 +196,11 @@ impl Pane { Align::new( Flex::row() .with_child( - Expanded::new( - 1.0, - Label::new(title, settings.ui_font_family, settings.ui_font_size) - .boxed(), - ) - .boxed(), + Label::new(title, settings.ui_font_family, settings.ui_font_size) + .boxed(), ) .with_child( - Expanded::new( - 1.0, + Container::new( LineBox::new( settings.ui_font_family, settings.ui_font_size, @@ -217,6 +212,7 @@ impl Pane { ) .boxed(), ) + .with_margin_left(20.) .boxed(), ) .boxed(),