Merge branch 'modified-status-in-tabs'
This commit is contained in:
commit
57a3207897
13 changed files with 320 additions and 70 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2241,6 +2241,7 @@ dependencies = [
|
|||
"crossbeam-channel 0.5.0",
|
||||
"dirs",
|
||||
"easy-parallel",
|
||||
"futures-core",
|
||||
"gpui",
|
||||
"ignore",
|
||||
"lazy_static",
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
73
gpui/src/elements/canvas.rs
Normal file
73
gpui/src/elements/canvas.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use super::Element;
|
||||
use crate::PaintContext;
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
};
|
||||
|
||||
pub struct Canvas<F>(F)
|
||||
where
|
||||
F: FnMut(RectF, &mut PaintContext);
|
||||
|
||||
impl<F> Canvas<F>
|
||||
where
|
||||
F: FnMut(RectF, &mut PaintContext),
|
||||
{
|
||||
pub fn new(f: F) -> Self {
|
||||
Self(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Element for Canvas<F>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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::{
|
||||
|
@ -36,6 +37,7 @@ pub struct Buffer {
|
|||
fragments: SumTree<Fragment>,
|
||||
insertion_splits: HashMap<time::Local, SumTree<InsertionSplit>>,
|
||||
pub version: time::Global,
|
||||
saved_version: time::Global,
|
||||
last_edit: time::Local,
|
||||
selections: HashMap<SelectionSetId, Vec<Selection>>,
|
||||
pub selections_last_update: SelectionsVersion,
|
||||
|
@ -216,6 +218,7 @@ impl Buffer {
|
|||
fragments,
|
||||
insertion_splits,
|
||||
version: time::Global::new(),
|
||||
saved_version: time::Global::new(),
|
||||
last_edit: time::Local::default(),
|
||||
selections: HashMap::default(),
|
||||
selections_last_update: 0,
|
||||
|
@ -241,17 +244,34 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn save(&self, ctx: &mut ModelContext<Self>) -> Option<Task<Result<()>>> {
|
||||
pub fn save(&mut self, ctx: &mut ModelContext<Self>) -> LocalBoxFuture<'static, Result<()>> {
|
||||
if let Some(file) = &self.file {
|
||||
let snapshot = self.snapshot();
|
||||
Some(file.save(snapshot, ctx.app()))
|
||||
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(()) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_modified(&self) -> bool {
|
||||
self.version != time::Global::new()
|
||||
fn did_save(&mut self, version: time::Global, ctx: &mut ModelContext<Buffer>) {
|
||||
self.saved_version = version;
|
||||
ctx.emit(Event::Saved);
|
||||
}
|
||||
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.version > self.saved_version
|
||||
}
|
||||
|
||||
pub fn version(&self) -> time::Global {
|
||||
self.version.clone()
|
||||
}
|
||||
|
||||
pub fn text_summary(&self) -> TextSummary {
|
||||
|
@ -398,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()
|
||||
|
@ -416,7 +437,7 @@ impl Buffer {
|
|||
ctx.notify();
|
||||
let changes = self.edits_since(old_version).collect::<Vec<_>>();
|
||||
if !changes.is_empty() {
|
||||
ctx.emit(Event::Edited(changes))
|
||||
self.did_edit(changes, was_dirty, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,6 +455,13 @@ impl Buffer {
|
|||
Ok(ops)
|
||||
}
|
||||
|
||||
fn did_edit(&self, changes: Vec<Edit>, was_dirty: bool, ctx: &mut ModelContext<Self>) {
|
||||
ctx.emit(Event::Edited(changes));
|
||||
if !was_dirty {
|
||||
ctx.emit(Event::Dirtied);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulate_typing<T: Rng>(&mut self, rng: &mut T) {
|
||||
let end = rng.gen_range(0..self.len() + 1);
|
||||
let start = rng.gen_range(0..end + 1);
|
||||
|
@ -619,6 +647,7 @@ impl Buffer {
|
|||
ops: I,
|
||||
ctx: Option<&mut ModelContext<Self>>,
|
||||
) -> Result<()> {
|
||||
let was_dirty = self.is_dirty();
|
||||
let old_version = self.version.clone();
|
||||
|
||||
let mut deferred_ops = Vec::new();
|
||||
|
@ -637,7 +666,7 @@ impl Buffer {
|
|||
ctx.notify();
|
||||
let changes = self.edits_since(old_version).collect::<Vec<_>>();
|
||||
if !changes.is_empty() {
|
||||
ctx.emit(Event::Edited(changes));
|
||||
self.did_edit(changes, was_dirty, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1370,6 +1399,7 @@ impl Clone for Buffer {
|
|||
fragments: self.fragments.clone(),
|
||||
insertion_splits: self.insertion_splits.clone(),
|
||||
version: self.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(),
|
||||
|
@ -1395,6 +1425,8 @@ impl Snapshot {
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Event {
|
||||
Edited(Vec<Edit>),
|
||||
Dirtied,
|
||||
Saved,
|
||||
}
|
||||
|
||||
impl Entity for Buffer {
|
||||
|
@ -1948,7 +1980,9 @@ impl ToPoint for usize {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::App;
|
||||
use std::collections::BTreeMap;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[test]
|
||||
fn test_edit() -> Result<()> {
|
||||
|
@ -1970,9 +2004,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_edit_events() {
|
||||
use gpui::App;
|
||||
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()));
|
||||
|
@ -1998,19 +2029,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
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -2484,11 +2521,89 @@ 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"));
|
||||
let events = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
// 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();
|
||||
|
||||
buffer.did_save(buffer.version(), ctx);
|
||||
});
|
||||
|
||||
// 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
|
||||
// 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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ use super::{
|
|||
};
|
||||
use crate::{settings::Settings, watch, workspace};
|
||||
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, MutableAppContext, Task, View, ViewContext,
|
||||
WeakViewHandle,
|
||||
ElementBox, Entity, FontCache, ModelHandle, View, ViewContext, WeakViewHandle,
|
||||
};
|
||||
use gpui::{geometry::vector::Vector2F, TextLayoutCache};
|
||||
use parking_lot::Mutex;
|
||||
|
@ -82,18 +82,6 @@ pub enum SelectAction {
|
|||
End,
|
||||
}
|
||||
|
||||
// impl workspace::Item for Buffer {
|
||||
// type View = BufferView;
|
||||
|
||||
// fn build_view(
|
||||
// buffer: ModelHandle<Self>,
|
||||
// settings: watch::Receiver<Settings>,
|
||||
// ctx: &mut ViewContext<Self::View>,
|
||||
// ) -> Self::View {
|
||||
// BufferView::for_buffer(buffer, settings, ctx)
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct BufferView {
|
||||
handle: WeakViewHandle<Self>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
|
@ -1091,6 +1079,8 @@ 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1106,6 +1096,8 @@ pub enum Event {
|
|||
Activate,
|
||||
Edited,
|
||||
Blurred,
|
||||
Dirtied,
|
||||
Saved,
|
||||
}
|
||||
|
||||
impl Entity for BufferView {
|
||||
|
@ -1147,11 +1139,12 @@ impl workspace::Item for Buffer {
|
|||
}
|
||||
|
||||
impl workspace::ItemView for BufferView {
|
||||
fn is_activate_event(event: &Self::Event) -> bool {
|
||||
match event {
|
||||
Event::Activate => true,
|
||||
_ => false,
|
||||
}
|
||||
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 {
|
||||
|
@ -1178,9 +1171,13 @@ impl workspace::ItemView for BufferView {
|
|||
Some(clone)
|
||||
}
|
||||
|
||||
fn save(&self, ctx: &mut MutableAppContext) -> Option<Task<Result<()>>> {
|
||||
fn save(&self, ctx: &mut ViewContext<Self>) -> LocalBoxFuture<'static, Result<()>> {
|
||||
self.buffer.update(ctx, |buffer, ctx| buffer.save(ctx))
|
||||
}
|
||||
|
||||
fn is_dirty(&self, ctx: &AppContext) -> bool {
|
||||
self.buffer.as_ref(ctx).is_dirty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Selection {
|
||||
|
|
|
@ -126,6 +126,7 @@ impl DisplayMap {
|
|||
fn handle_buffer_event(&mut self, event: &buffer::Event, ctx: &mut ModelContext<Self>) {
|
||||
match event {
|
||||
buffer::Event::Edited(edits) => self.fold_map.apply_edits(edits, ctx.app()).unwrap(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ impl FileFinder {
|
|||
}
|
||||
}
|
||||
Blurred => ctx.emit(Event::Dismissed),
|
||||
Activate => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,28 @@ 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(
|
||||
Label::new(title, settings.ui_font_family, settings.ui_font_size)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Container::new(
|
||||
LineBox::new(
|
||||
settings.ui_font_family,
|
||||
settings.ui_font_size,
|
||||
ConstrainedBox::new(Self::render_modified_icon(
|
||||
item.is_dirty(app),
|
||||
))
|
||||
.with_max_width(12.)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_margin_left(20.)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
|
@ -243,6 +268,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 {
|
||||
|
|
|
@ -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};
|
||||
|
@ -13,7 +14,6 @@ pub fn init(app: &mut App) {
|
|||
}
|
||||
|
||||
pub trait ItemView: View {
|
||||
fn is_activate_event(event: &Self::Event) -> bool;
|
||||
fn title(&self, app: &AppContext) -> String;
|
||||
fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)>;
|
||||
fn clone_on_split(&self, _: &mut ViewContext<Self>) -> Option<Self>
|
||||
|
@ -22,8 +22,17 @@ pub trait ItemView: View {
|
|||
{
|
||||
None
|
||||
}
|
||||
fn save(&self, _: &mut MutableAppContext) -> Option<Task<anyhow::Result<()>>> {
|
||||
None
|
||||
fn is_dirty(&self, _: &AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
fn save(&self, _: &mut ViewContext<Self>) -> 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +44,8 @@ pub trait ItemViewHandle: Send + Sync {
|
|||
fn set_parent_pane(&self, pane: &ViewHandle<Pane>, app: &mut MutableAppContext);
|
||||
fn id(&self) -> usize;
|
||||
fn to_any(&self) -> AnyViewHandle;
|
||||
fn save(&self, ctx: &mut MutableAppContext) -> Option<Task<anyhow::Result<()>>>;
|
||||
fn is_dirty(&self, ctx: &AppContext) -> bool;
|
||||
fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>>;
|
||||
}
|
||||
|
||||
impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
||||
|
@ -61,18 +71,25 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
|
|||
fn set_parent_pane(&self, pane: &ViewHandle<Pane>, app: &mut MutableAppContext) {
|
||||
pane.update(app, |_, ctx| {
|
||||
ctx.subscribe_to_view(self, |pane, item, event, ctx| {
|
||||
if T::is_activate_event(event) {
|
||||
if T::should_activate_item_on_event(event) {
|
||||
if let Some(ix) = pane.item_index(&item) {
|
||||
pane.activate_item(ix, ctx);
|
||||
pane.activate(ctx);
|
||||
}
|
||||
}
|
||||
if T::should_update_tab_on_event(event) {
|
||||
ctx.notify()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn save(&self, ctx: &mut MutableAppContext) -> Option<Task<anyhow::Result<()>>> {
|
||||
self.update(ctx, |item, ctx| item.save(ctx.app_mut()))
|
||||
fn save(&self, ctx: &mut MutableAppContext) -> LocalBoxFuture<'static, anyhow::Result<()>> {
|
||||
self.update(ctx, |item, ctx| item.save(ctx))
|
||||
}
|
||||
|
||||
fn is_dirty(&self, ctx: &AppContext) -> bool {
|
||||
self.as_ref(ctx).is_dirty(ctx)
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
|
@ -222,15 +239,14 @@ impl WorkspaceView {
|
|||
pub fn save_active_item(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||
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()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue