History

Enable zoom (#3668)

* [x] zoom * [x] pane tests

Max Brunsfeld committed 2 years ago

Commit 4eb609a2 parents 7af0edf 4c3ec0a3 files · +585 −804Browse files

Unified · Split

3 files
  1. crates/workspace2/src/dock.rs +1 −1
  2. crates/workspace2/src/pane.rs +535 −652
  3. crates/workspace2/src/workspace2.rs +49 −151
crates/workspace2/src/dock.rs+1−1
@@ -317,7 +317,7 @@ impl Dock { · 316 unmodified lines
317317 new_dock.add_panel(panel.clone(), workspace.clone(), cx);
318318 if was_visible {
319319 new_dock.set_open(true, cx);
320− new_dock.activate_panel(this.panels_len() - 1, cx);
320+ new_dock.activate_panel(new_dock.panels_len() - 1, cx);
321321 }
322322 });
323323 }
crates/workspace2/src/pane.rs+535−652
@@ -177,7 +177,7 @@ pub struct Pane { · 176 unmodified lines
177177 toolbar: View<Toolbar>,
178178 new_item_menu: Option<View<ContextMenu>>,
179179 split_item_menu: Option<View<ContextMenu>>,
180− // tab_context_menu: ViewHandle<ContextMenu>,
180+ // tab_context_menu: View<ContextMenu>,
181181 workspace: WeakView<Workspace>,
182182 project: Model<Project>,
183183 drag_split_direction: Option<SplitDirection>,
@@ -256,11 +256,11 @@ struct DraggedTab { · 72 unmodified lines
256256
257257 // struct TabBarContextMenu {
258258 // kind: TabBarContextMenuKind,
259−// handle: ViewHandle<ContextMenu>,
259+// handle: View<ContextMenu>,
260260 // }
261261
262262 // impl TabBarContextMenu {
263−// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option<ViewHandle<ContextMenu>> {
263+// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option<View<ContextMenu>> {
264264 // if self.kind == kind {
265265 // return Some(self.handle.clone());
266266 // }
@@ -329,7 +329,7 @@ impl Pane { · 62 unmodified lines
329329 ) -> Self {
330330 // todo!("context menu")
331331 // let pane_view_id = cx.view_id();
332− // let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx));
332+ // let context_menu = cx.build_view(|cx| ContextMenu::new(pane_view_id, cx));
333333 // context_menu.update(cx, |menu, _| {
334334 // menu.set_position_mode(OverlayPositionMode::Local)
335335 // });
@@ -368,7 +368,7 @@ impl Pane { · 32 unmodified lines
368368 // kind: TabBarContextMenuKind::New,
369369 // handle: context_menu,
370370 // },
371− // tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
371+ // tab_context_menu: cx.build_view(|_| ContextMenu::new(pane_view_id, cx)),
372372 workspace,
373373 project,
374374 // can_drop: Rc::new(|_, _| true),
@@ -1963,52 +1963,6 @@ impl Render for Pane { · 1588 unmodified lines
19631963 }),
19641964 )
19651965 }
1966−
1967− // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
1968− // if !self.has_focus {
1969− // self.has_focus = true;
1970− // cx.emit(Event::Focus);
1971− // cx.notify();
1972− // }
1973−
1974− // self.toolbar.update(cx, |toolbar, cx| {
1975− // toolbar.focus_changed(true, cx);
1976− // });
1977−
1978− // if let Some(active_item) = self.active_item() {
1979− // if cx.is_self_focused() {
1980− // // Pane was focused directly. We need to either focus a view inside the active item,
1981− // // or focus the active item itself
1982− // if let Some(weak_last_focused_view) =
1983− // self.last_focused_view_by_item.get(&active_item.id())
1984− // {
1985− // if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) {
1986− // cx.focus(&last_focused_view);
1987− // return;
1988− // } else {
1989− // self.last_focused_view_by_item.remove(&active_item.id());
1990− // }
1991− // }
1992−
1993− // cx.focus(active_item.as_any());
1994− // } else if focused != self.tab_bar_context_menu.handle {
1995− // self.last_focused_view_by_item
1996− // .insert(active_item.id(), focused.downgrade());
1997− // }
1998− // }
1999− // }
2000−
2001− // fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
2002− // self.has_focus = false;
2003− // self.toolbar.update(cx, |toolbar, cx| {
2004− // toolbar.focus_changed(false, cx);
2005− // });
2006− // cx.notify();
2007− // }
2008−
2009− // fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
2010− // Self::reset_to_default_keymap_context(keymap);
2011− // }
20121966 }
20131967
20141968 impl ItemNavHistory {
@@ -2166,98 +2120,6 @@ impl NavHistoryState { · 151 unmodified lines
21662120 }
21672121 }
21682122
2169−// pub struct PaneBackdrop<V> {
2170−// child_view: usize,
2171−// child: AnyElement<V>,
2172−// }
2173−
2174−// impl<V> PaneBackdrop<V> {
2175−// pub fn new(pane_item_view: usize, child: AnyElement<V>) -> Self {
2176−// PaneBackdrop {
2177−// child,
2178−// child_view: pane_item_view,
2179−// }
2180−// }
2181−// }
2182−
2183−// impl<V: 'static> Element<V> for PaneBackdrop<V> {
2184−// type LayoutState = ();
2185−
2186−// type PaintState = ();
2187−
2188−// fn layout(
2189−// &mut self,
2190−// constraint: gpui::SizeConstraint,
2191−// view: &mut V,
2192−// cx: &mut ViewContext<V>,
2193−// ) -> (Vector2F, Self::LayoutState) {
2194−// let size = self.child.layout(constraint, view, cx);
2195−// (size, ())
2196−// }
2197−
2198−// fn paint(
2199−// &mut self,
2200−// bounds: RectF,
2201−// visible_bounds: RectF,
2202−// _: &mut Self::LayoutState,
2203−// view: &mut V,
2204−// cx: &mut ViewContext<V>,
2205−// ) -> Self::PaintState {
2206−// let background = theme::current(cx).editor.background;
2207−
2208−// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
2209−
2210−// cx.scene().push_quad(gpui::Quad {
2211−// bounds: RectF::new(bounds.origin(), bounds.size()),
2212−// background: Some(background),
2213−// ..Default::default()
2214−// });
2215−
2216−// let child_view_id = self.child_view;
2217−// cx.scene().push_mouse_region(
2218−// MouseRegion::new::<Self>(child_view_id, 0, visible_bounds).on_down(
2219−// gpui::platform::MouseButton::Left,
2220−// move |_, _: &mut V, cx| {
2221−// let window = cx.window();
2222−// cx.app_context().focus(window, Some(child_view_id))
2223−// },
2224−// ),
2225−// );
2226−
2227−// cx.scene().push_layer(Some(bounds));
2228−// self.child.paint(bounds.origin(), visible_bounds, view, cx);
2229−// cx.scene().pop_layer();
2230−// }
2231−
2232−// fn rect_for_text_range(
2233−// &self,
2234−// range_utf16: std::ops::Range<usize>,
2235−// _bounds: RectF,
2236−// _visible_bounds: RectF,
2237−// _layout: &Self::LayoutState,
2238−// _paint: &Self::PaintState,
2239−// view: &V,
2240−// cx: &gpui::ViewContext<V>,
2241−// ) -> Option<RectF> {
2242−// self.child.rect_for_text_range(range_utf16, view, cx)
2243−// }
2244−
2245−// fn debug(
2246−// &self,
2247−// _bounds: RectF,
2248−// _layout: &Self::LayoutState,
2249−// _paint: &Self::PaintState,
2250−// view: &V,
2251−// cx: &gpui::ViewContext<V>,
2252−// ) -> serde_json::Value {
2253−// gpui::json::json!({
2254−// "type": "Pane Back Drop",
2255−// "view": self.child_view,
2256−// "child": self.child.debug(view, cx),
2257−// })
2258−// }
2259−// }
2260−
22612123 fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {
22622124 let path = buffer_path
22632125 .as_ref()
@@ -2267,528 +2129,549 @@ fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String { · 3 unmodified lines
22672129 format!("{path} contains unsaved edits. Do you want to save it?")
22682130 }
22692131
2270−// todo!("uncomment tests")
2271−// #[cfg(test)]
2272−// mod tests {
2273−// use super::*;
2274−// use crate::item::test::{TestItem, TestProjectItem};
2275−// use gpui::TestAppContext;
2276−// use project::FakeFs;
2277−// use settings::SettingsStore;
2278−
2279−// #[gpui::test]
2280−// async fn test_remove_active_empty(cx: &mut TestAppContext) {
2281−// init_test(cx);
2282−// let fs = FakeFs::new(cx.background());
2283−
2284−// let project = Project::test(fs, None, cx).await;
2285−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2286−// let workspace = window.root(cx);
2287−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2288−
2289−// pane.update(cx, |pane, cx| {
2290−// assert!(pane
2291−// .close_active_item(&CloseActiveItem { save_intent: None }, cx)
2292−// .is_none())
2293−// });
2294−// }
2132+#[cfg(test)]
2133+mod tests {
2134+ use super::*;
2135+ use crate::item::test::{TestItem, TestProjectItem};
2136+ use gpui::{TestAppContext, VisualTestContext};
2137+ use project::FakeFs;
2138+ use settings::SettingsStore;
2139+ use theme::LoadThemes;
2140+
2141+ #[gpui::test]
2142+ async fn test_remove_active_empty(cx: &mut TestAppContext) {
2143+ init_test(cx);
2144+ let fs = FakeFs::new(cx.executor());
2145+
2146+ let project = Project::test(fs, None, cx).await;
2147+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2148+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2149+
2150+ pane.update(cx, |pane, cx| {
2151+ assert!(pane
2152+ .close_active_item(&CloseActiveItem { save_intent: None }, cx)
2153+ .is_none())
2154+ });
2155+ }
22952156
2296−// #[gpui::test]
2297−// async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
2298−// cx.foreground().forbid_parking();
2299−// init_test(cx);
2300−// let fs = FakeFs::new(cx.background());
2301−
2302−// let project = Project::test(fs, None, cx).await;
2303−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2304−// let workspace = window.root(cx);
2305−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2306−
2307−// // 1. Add with a destination index
2308−// // a. Add before the active item
2309−// set_labeled_items(&pane, ["A", "B*", "C"], cx);
2310−// pane.update(cx, |pane, cx| {
2311−// pane.add_item(
2312−// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
2313−// false,
2314−// false,
2315−// Some(0),
2316−// cx,
2317−// );
2318−// });
2319−// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
2320−
2321−// // b. Add after the active item
2322−// set_labeled_items(&pane, ["A", "B*", "C"], cx);
2323−// pane.update(cx, |pane, cx| {
2324−// pane.add_item(
2325−// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
2326−// false,
2327−// false,
2328−// Some(2),
2329−// cx,
2330−// );
2331−// });
2332−// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
2333−
2334−// // c. Add at the end of the item list (including off the length)
2335−// set_labeled_items(&pane, ["A", "B*", "C"], cx);
2336−// pane.update(cx, |pane, cx| {
2337−// pane.add_item(
2338−// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
2339−// false,
2340−// false,
2341−// Some(5),
2342−// cx,
2343−// );
2344−// });
2345−// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2346−
2347−// // 2. Add without a destination index
2348−// // a. Add with active item at the start of the item list
2349−// set_labeled_items(&pane, ["A*", "B", "C"], cx);
2350−// pane.update(cx, |pane, cx| {
2351−// pane.add_item(
2352−// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
2353−// false,
2354−// false,
2355−// None,
2356−// cx,
2357−// );
2358−// });
2359−// set_labeled_items(&pane, ["A", "D*", "B", "C"], cx);
2360−
2361−// // b. Add with active item at the end of the item list
2362−// set_labeled_items(&pane, ["A", "B", "C*"], cx);
2363−// pane.update(cx, |pane, cx| {
2364−// pane.add_item(
2365−// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),
2366−// false,
2367−// false,
2368−// None,
2369−// cx,
2370−// );
2371−// });
2372−// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2373−// }
2157+ #[gpui::test]
2158+ async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
2159+ init_test(cx);
2160+ let fs = FakeFs::new(cx.executor());
2161+
2162+ let project = Project::test(fs, None, cx).await;
2163+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2164+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2165+
2166+ // 1. Add with a destination index
2167+ // a. Add before the active item
2168+ set_labeled_items(&pane, ["A", "B*", "C"], cx);
2169+ pane.update(cx, |pane, cx| {
2170+ pane.add_item(
2171+ Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2172+ false,
2173+ false,
2174+ Some(0),
2175+ cx,
2176+ );
2177+ });
2178+ assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
2179+
2180+ // b. Add after the active item
2181+ set_labeled_items(&pane, ["A", "B*", "C"], cx);
2182+ pane.update(cx, |pane, cx| {
2183+ pane.add_item(
2184+ Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2185+ false,
2186+ false,
2187+ Some(2),
2188+ cx,
2189+ );
2190+ });
2191+ assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
2192+
2193+ // c. Add at the end of the item list (including off the length)
2194+ set_labeled_items(&pane, ["A", "B*", "C"], cx);
2195+ pane.update(cx, |pane, cx| {
2196+ pane.add_item(
2197+ Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2198+ false,
2199+ false,
2200+ Some(5),
2201+ cx,
2202+ );
2203+ });
2204+ assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2205+
2206+ // 2. Add without a destination index
2207+ // a. Add with active item at the start of the item list
2208+ set_labeled_items(&pane, ["A*", "B", "C"], cx);
2209+ pane.update(cx, |pane, cx| {
2210+ pane.add_item(
2211+ Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2212+ false,
2213+ false,
2214+ None,
2215+ cx,
2216+ );
2217+ });
2218+ set_labeled_items(&pane, ["A", "D*", "B", "C"], cx);
2219+
2220+ // b. Add with active item at the end of the item list
2221+ set_labeled_items(&pane, ["A", "B", "C*"], cx);
2222+ pane.update(cx, |pane, cx| {
2223+ pane.add_item(
2224+ Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2225+ false,
2226+ false,
2227+ None,
2228+ cx,
2229+ );
2230+ });
2231+ assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2232+ }
23742233
2375−// #[gpui::test]
2376−// async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
2377−// cx.foreground().forbid_parking();
2378−// init_test(cx);
2379−// let fs = FakeFs::new(cx.background());
2380−
2381−// let project = Project::test(fs, None, cx).await;
2382−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2383−// let workspace = window.root(cx);
2384−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2385−
2386−// // 1. Add with a destination index
2387−// // 1a. Add before the active item
2388−// let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2389−// pane.update(cx, |pane, cx| {
2390−// pane.add_item(d, false, false, Some(0), cx);
2391−// });
2392−// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
2393−
2394−// // 1b. Add after the active item
2395−// let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2396−// pane.update(cx, |pane, cx| {
2397−// pane.add_item(d, false, false, Some(2), cx);
2398−// });
2399−// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
2400−
2401−// // 1c. Add at the end of the item list (including off the length)
2402−// let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2403−// pane.update(cx, |pane, cx| {
2404−// pane.add_item(a, false, false, Some(5), cx);
2405−// });
2406−// assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
2407−
2408−// // 1d. Add same item to active index
2409−// let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
2410−// pane.update(cx, |pane, cx| {
2411−// pane.add_item(b, false, false, Some(1), cx);
2412−// });
2413−// assert_item_labels(&pane, ["A", "B*", "C"], cx);
2414−
2415−// // 1e. Add item to index after same item in last position
2416−// let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
2417−// pane.update(cx, |pane, cx| {
2418−// pane.add_item(c, false, false, Some(2), cx);
2419−// });
2420−// assert_item_labels(&pane, ["A", "B", "C*"], cx);
2421−
2422−// // 2. Add without a destination index
2423−// // 2a. Add with active item at the start of the item list
2424−// let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx);
2425−// pane.update(cx, |pane, cx| {
2426−// pane.add_item(d, false, false, None, cx);
2427−// });
2428−// assert_item_labels(&pane, ["A", "D*", "B", "C"], cx);
2429−
2430−// // 2b. Add with active item at the end of the item list
2431−// let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx);
2432−// pane.update(cx, |pane, cx| {
2433−// pane.add_item(a, false, false, None, cx);
2434−// });
2435−// assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
2436−
2437−// // 2c. Add active item to active item at end of list
2438−// let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx);
2439−// pane.update(cx, |pane, cx| {
2440−// pane.add_item(c, false, false, None, cx);
2441−// });
2442−// assert_item_labels(&pane, ["A", "B", "C*"], cx);
2443−
2444−// // 2d. Add active item to active item at start of list
2445−// let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx);
2446−// pane.update(cx, |pane, cx| {
2447−// pane.add_item(a, false, false, None, cx);
2448−// });
2449−// assert_item_labels(&pane, ["A*", "B", "C"], cx);
2450−// }
2234+ #[gpui::test]
2235+ async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
2236+ init_test(cx);
2237+ let fs = FakeFs::new(cx.executor());
24512238
2452−// #[gpui::test]
2453−// async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {
2454−// cx.foreground().forbid_parking();
2455−// init_test(cx);
2456−// let fs = FakeFs::new(cx.background());
2457−
2458−// let project = Project::test(fs, None, cx).await;
2459−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2460−// let workspace = window.root(cx);
2461−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2462−
2463−// // singleton view
2464−// pane.update(cx, |pane, cx| {
2465−// let item = TestItem::new()
2466−// .with_singleton(true)
2467−// .with_label("buffer 1")
2468−// .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]);
2469−
2470−// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
2471−// });
2472−// assert_item_labels(&pane, ["buffer 1*"], cx);
2473−
2474−// // new singleton view with the same project entry
2475−// pane.update(cx, |pane, cx| {
2476−// let item = TestItem::new()
2477−// .with_singleton(true)
2478−// .with_label("buffer 1")
2479−// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]);
2480−
2481−// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
2482−// });
2483−// assert_item_labels(&pane, ["buffer 1*"], cx);
2484−
2485−// // new singleton view with different project entry
2486−// pane.update(cx, |pane, cx| {
2487−// let item = TestItem::new()
2488−// .with_singleton(true)
2489−// .with_label("buffer 2")
2490−// .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]);
2491−// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
2492−// });
2493−// assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx);
2494−
2495−// // new multibuffer view with the same project entry
2496−// pane.update(cx, |pane, cx| {
2497−// let item = TestItem::new()
2498−// .with_singleton(false)
2499−// .with_label("multibuffer 1")
2500−// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]);
2501−
2502−// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
2503−// });
2504−// assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx);
2505−
2506−// // another multibuffer view with the same project entry
2507−// pane.update(cx, |pane, cx| {
2508−// let item = TestItem::new()
2509−// .with_singleton(false)
2510−// .with_label("multibuffer 1b")
2511−// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]);
2512−
2513−// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx);
2514−// });
2515−// assert_item_labels(
2516−// &pane,
2517−// ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"],
2518−// cx,
2519−// );
2520−// }
2239+ let project = Project::test(fs, None, cx).await;
2240+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2241+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
25212242
2522−// #[gpui::test]
2523−// async fn test_remove_item_ordering(cx: &mut TestAppContext) {
2524−// init_test(cx);
2525−// let fs = FakeFs::new(cx.background());
2526−
2527−// let project = Project::test(fs, None, cx).await;
2528−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2529−// let workspace = window.root(cx);
2530−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2531−
2532−// add_labeled_item(&pane, "A", false, cx);
2533−// add_labeled_item(&pane, "B", false, cx);
2534−// add_labeled_item(&pane, "C", false, cx);
2535−// add_labeled_item(&pane, "D", false, cx);
2536−// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2537−
2538−// pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx));
2539−// add_labeled_item(&pane, "1", false, cx);
2540−// assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx);
2541−
2542−// pane.update(cx, |pane, cx| {
2543−// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2544−// })
2545−// .unwrap()
2546−// .await
2547−// .unwrap();
2548−// assert_item_labels(&pane, ["A", "B*", "C", "D"], cx);
2549−
2550−// pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx));
2551−// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2552−
2553−// pane.update(cx, |pane, cx| {
2554−// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2555−// })
2556−// .unwrap()
2557−// .await
2558−// .unwrap();
2559−// assert_item_labels(&pane, ["A", "B*", "C"], cx);
2560−
2561−// pane.update(cx, |pane, cx| {
2562−// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2563−// })
2564−// .unwrap()
2565−// .await
2566−// .unwrap();
2567−// assert_item_labels(&pane, ["A", "C*"], cx);
2568−
2569−// pane.update(cx, |pane, cx| {
2570−// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2571−// })
2572−// .unwrap()
2573−// .await
2574−// .unwrap();
2575−// assert_item_labels(&pane, ["A*"], cx);
2576−// }
2243+ // 1. Add with a destination index
2244+ // 1a. Add before the active item
2245+ let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2246+ pane.update(cx, |pane, cx| {
2247+ pane.add_item(d, false, false, Some(0), cx);
2248+ });
2249+ assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
25772250
2578−// #[gpui::test]
2579−// async fn test_close_inactive_items(cx: &mut TestAppContext) {
2580−// init_test(cx);
2581−// let fs = FakeFs::new(cx.background());
2582−
2583−// let project = Project::test(fs, None, cx).await;
2584−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2585−// let workspace = window.root(cx);
2586−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2587−
2588−// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
2589−
2590−// pane.update(cx, |pane, cx| {
2591−// pane.close_inactive_items(&CloseInactiveItems, cx)
2592−// })
2593−// .unwrap()
2594−// .await
2595−// .unwrap();
2596−// assert_item_labels(&pane, ["C*"], cx);
2597−// }
2251+ // 1b. Add after the active item
2252+ let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2253+ pane.update(cx, |pane, cx| {
2254+ pane.add_item(d, false, false, Some(2), cx);
2255+ });
2256+ assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
25982257
2599−// #[gpui::test]
2600−// async fn test_close_clean_items(cx: &mut TestAppContext) {
2601−// init_test(cx);
2602−// let fs = FakeFs::new(cx.background());
2603−
2604−// let project = Project::test(fs, None, cx).await;
2605−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2606−// let workspace = window.root(cx);
2607−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2608−
2609−// add_labeled_item(&pane, "A", true, cx);
2610−// add_labeled_item(&pane, "B", false, cx);
2611−// add_labeled_item(&pane, "C", true, cx);
2612−// add_labeled_item(&pane, "D", false, cx);
2613−// add_labeled_item(&pane, "E", false, cx);
2614−// assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx);
2615−
2616−// pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx))
2617−// .unwrap()
2618−// .await
2619−// .unwrap();
2620−// assert_item_labels(&pane, ["A^", "C*^"], cx);
2621−// }
2258+ // 1c. Add at the end of the item list (including off the length)
2259+ let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2260+ pane.update(cx, |pane, cx| {
2261+ pane.add_item(a, false, false, Some(5), cx);
2262+ });
2263+ assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
26222264
2623−// #[gpui::test]
2624−// async fn test_close_items_to_the_left(cx: &mut TestAppContext) {
2625−// init_test(cx);
2626−// let fs = FakeFs::new(cx.background());
2627−
2628−// let project = Project::test(fs, None, cx).await;
2629−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2630−// let workspace = window.root(cx);
2631−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2632−
2633−// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
2634−
2635−// pane.update(cx, |pane, cx| {
2636−// pane.close_items_to_the_left(&CloseItemsToTheLeft, cx)
2637−// })
2638−// .unwrap()
2639−// .await
2640−// .unwrap();
2641−// assert_item_labels(&pane, ["C*", "D", "E"], cx);
2642−// }
2265+ // 1d. Add same item to active index
2266+ let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
2267+ pane.update(cx, |pane, cx| {
2268+ pane.add_item(b, false, false, Some(1), cx);
2269+ });
2270+ assert_item_labels(&pane, ["A", "B*", "C"], cx);
26432271
2644−// #[gpui::test]
2645−// async fn test_close_items_to_the_right(cx: &mut TestAppContext) {
2646−// init_test(cx);
2647−// let fs = FakeFs::new(cx.background());
2648−
2649−// let project = Project::test(fs, None, cx).await;
2650−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2651−// let workspace = window.root(cx);
2652−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2653−
2654−// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
2655−
2656−// pane.update(cx, |pane, cx| {
2657−// pane.close_items_to_the_right(&CloseItemsToTheRight, cx)
2658−// })
2659−// .unwrap()
2660−// .await
2661−// .unwrap();
2662−// assert_item_labels(&pane, ["A", "B", "C*"], cx);
2663−// }
2272+ // 1e. Add item to index after same item in last position
2273+ let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
2274+ pane.update(cx, |pane, cx| {
2275+ pane.add_item(c, false, false, Some(2), cx);
2276+ });
2277+ assert_item_labels(&pane, ["A", "B", "C*"], cx);
26642278
2665−// #[gpui::test]
2666−// async fn test_close_all_items(cx: &mut TestAppContext) {
2667−// init_test(cx);
2668−// let fs = FakeFs::new(cx.background());
2669−
2670−// let project = Project::test(fs, None, cx).await;
2671−// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
2672−// let workspace = window.root(cx);
2673−// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
2674−
2675−// add_labeled_item(&pane, "A", false, cx);
2676−// add_labeled_item(&pane, "B", false, cx);
2677−// add_labeled_item(&pane, "C", false, cx);
2678−// assert_item_labels(&pane, ["A", "B", "C*"], cx);
2679−
2680−// pane.update(cx, |pane, cx| {
2681−// pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
2682−// })
2683−// .unwrap()
2684−// .await
2685−// .unwrap();
2686−// assert_item_labels(&pane, [], cx);
2687−
2688−// add_labeled_item(&pane, "A", true, cx);
2689−// add_labeled_item(&pane, "B", true, cx);
2690−// add_labeled_item(&pane, "C", true, cx);
2691−// assert_item_labels(&pane, ["A^", "B^", "C*^"], cx);
2692−
2693−// let save = pane
2694−// .update(cx, |pane, cx| {
2695−// pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
2696−// })
2697−// .unwrap();
2698−
2699−// cx.foreground().run_until_parked();
2700−// window.simulate_prompt_answer(2, cx);
2701−// save.await.unwrap();
2702−// assert_item_labels(&pane, [], cx);
2703−// }
2279+ // 2. Add without a destination index
2280+ // 2a. Add with active item at the start of the item list
2281+ let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx);
2282+ pane.update(cx, |pane, cx| {
2283+ pane.add_item(d, false, false, None, cx);
2284+ });
2285+ assert_item_labels(&pane, ["A", "D*", "B", "C"], cx);
27042286
2705−// fn init_test(cx: &mut TestAppContext) {
2706−// cx.update(|cx| {
2707−// cx.set_global(SettingsStore::test(cx));
2708−// theme::init((), cx);
2709−// crate::init_settings(cx);
2710−// Project::init_settings(cx);
2711−// });
2712−// }
2287+ // 2b. Add with active item at the end of the item list
2288+ let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx);
2289+ pane.update(cx, |pane, cx| {
2290+ pane.add_item(a, false, false, None, cx);
2291+ });
2292+ assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
27132293
2714−// fn add_labeled_item(
2715−// pane: &ViewHandle<Pane>,
2716−// label: &str,
2717−// is_dirty: bool,
2718−// cx: &mut TestAppContext,
2719−// ) -> Box<ViewHandle<TestItem>> {
2720−// pane.update(cx, |pane, cx| {
2721−// let labeled_item =
2722−// Box::new(cx.add_view(|_| TestItem::new().with_label(label).with_dirty(is_dirty)));
2723−// pane.add_item(labeled_item.clone(), false, false, None, cx);
2724−// labeled_item
2725−// })
2726−// }
2294+ // 2c. Add active item to active item at end of list
2295+ let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx);
2296+ pane.update(cx, |pane, cx| {
2297+ pane.add_item(c, false, false, None, cx);
2298+ });
2299+ assert_item_labels(&pane, ["A", "B", "C*"], cx);
27272300
2728−// fn set_labeled_items<const COUNT: usize>(
2729−// pane: &ViewHandle<Pane>,
2730−// labels: [&str; COUNT],
2731−// cx: &mut TestAppContext,
2732−// ) -> [Box<ViewHandle<TestItem>>; COUNT] {
2733−// pane.update(cx, |pane, cx| {
2734−// pane.items.clear();
2735−// let mut active_item_index = 0;
2736−
2737−// let mut index = 0;
2738−// let items = labels.map(|mut label| {
2739−// if label.ends_with("*") {
2740−// label = label.trim_end_matches("*");
2741−// active_item_index = index;
2742−// }
2743−
2744−// let labeled_item = Box::new(cx.add_view(|_| TestItem::new().with_label(label)));
2745−// pane.add_item(labeled_item.clone(), false, false, None, cx);
2746−// index += 1;
2747−// labeled_item
2748−// });
2749−
2750−// pane.activate_item(active_item_index, false, false, cx);
2751−
2752−// items
2753−// })
2754−// }
2301+ // 2d. Add active item to active item at start of list
2302+ let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx);
2303+ pane.update(cx, |pane, cx| {
2304+ pane.add_item(a, false, false, None, cx);
2305+ });
2306+ assert_item_labels(&pane, ["A*", "B", "C"], cx);
2307+ }
2308+
2309+ #[gpui::test]
2310+ async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {
2311+ init_test(cx);
2312+ let fs = FakeFs::new(cx.executor());
2313+
2314+ let project = Project::test(fs, None, cx).await;
2315+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2316+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2317+
2318+ // singleton view
2319+ pane.update(cx, |pane, cx| {
2320+ pane.add_item(
2321+ Box::new(cx.build_view(|cx| {
2322+ TestItem::new(cx)
2323+ .with_singleton(true)
2324+ .with_label("buffer 1")
2325+ .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
2326+ })),
2327+ false,
2328+ false,
2329+ None,
2330+ cx,
2331+ );
2332+ });
2333+ assert_item_labels(&pane, ["buffer 1*"], cx);
2334+
2335+ // new singleton view with the same project entry
2336+ pane.update(cx, |pane, cx| {
2337+ pane.add_item(
2338+ Box::new(cx.build_view(|cx| {
2339+ TestItem::new(cx)
2340+ .with_singleton(true)
2341+ .with_label("buffer 1")
2342+ .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
2343+ })),
2344+ false,
2345+ false,
2346+ None,
2347+ cx,
2348+ );
2349+ });
2350+ assert_item_labels(&pane, ["buffer 1*"], cx);
2351+
2352+ // new singleton view with different project entry
2353+ pane.update(cx, |pane, cx| {
2354+ pane.add_item(
2355+ Box::new(cx.build_view(|cx| {
2356+ TestItem::new(cx)
2357+ .with_singleton(true)
2358+ .with_label("buffer 2")
2359+ .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
2360+ })),
2361+ false,
2362+ false,
2363+ None,
2364+ cx,
2365+ );
2366+ });
2367+ assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx);
2368+
2369+ // new multibuffer view with the same project entry
2370+ pane.update(cx, |pane, cx| {
2371+ pane.add_item(
2372+ Box::new(cx.build_view(|cx| {
2373+ TestItem::new(cx)
2374+ .with_singleton(false)
2375+ .with_label("multibuffer 1")
2376+ .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
2377+ })),
2378+ false,
2379+ false,
2380+ None,
2381+ cx,
2382+ );
2383+ });
2384+ assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx);
2385+
2386+ // another multibuffer view with the same project entry
2387+ pane.update(cx, |pane, cx| {
2388+ pane.add_item(
2389+ Box::new(cx.build_view(|cx| {
2390+ TestItem::new(cx)
2391+ .with_singleton(false)
2392+ .with_label("multibuffer 1b")
2393+ .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
2394+ })),
2395+ false,
2396+ false,
2397+ None,
2398+ cx,
2399+ );
2400+ });
2401+ assert_item_labels(
2402+ &pane,
2403+ ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"],
2404+ cx,
2405+ );
2406+ }
27552407
2756−// // Assert the item label, with the active item label suffixed with a '*'
2757−// fn assert_item_labels<const COUNT: usize>(
2758−// pane: &ViewHandle<Pane>,
2759−// expected_states: [&str; COUNT],
2760−// cx: &mut TestAppContext,
2761−// ) {
2762−// pane.read_with(cx, |pane, cx| {
2763−// let actual_states = pane
2764−// .items
2765−// .iter()
2766−// .enumerate()
2767−// .map(|(ix, item)| {
2768−// let mut state = item
2769−// .as_any()
2770−// .downcast_ref::<TestItem>()
2771−// .unwrap()
2772−// .read(cx)
2773−// .label
2774−// .clone();
2775−// if ix == pane.active_item_index {
2776−// state.push('*');
2777−// }
2778−// if item.is_dirty(cx) {
2779−// state.push('^');
2780−// }
2781−// state
2782−// })
2783−// .collect::<Vec<_>>();
2784−
2785−// assert_eq!(
2786−// actual_states, expected_states,
2787−// "pane items do not match expectation"
2788−// );
2789−// })
2790−// }
2791−// }
2408+ #[gpui::test]
2409+ async fn test_remove_item_ordering(cx: &mut TestAppContext) {
2410+ init_test(cx);
2411+ let fs = FakeFs::new(cx.executor());
2412+
2413+ let project = Project::test(fs, None, cx).await;
2414+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2415+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2416+
2417+ add_labeled_item(&pane, "A", false, cx);
2418+ add_labeled_item(&pane, "B", false, cx);
2419+ add_labeled_item(&pane, "C", false, cx);
2420+ add_labeled_item(&pane, "D", false, cx);
2421+ assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2422+
2423+ pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx));
2424+ add_labeled_item(&pane, "1", false, cx);
2425+ assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx);
2426+
2427+ pane.update(cx, |pane, cx| {
2428+ pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2429+ })
2430+ .unwrap()
2431+ .await
2432+ .unwrap();
2433+ assert_item_labels(&pane, ["A", "B*", "C", "D"], cx);
2434+
2435+ pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx));
2436+ assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2437+
2438+ pane.update(cx, |pane, cx| {
2439+ pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2440+ })
2441+ .unwrap()
2442+ .await
2443+ .unwrap();
2444+ assert_item_labels(&pane, ["A", "B*", "C"], cx);
2445+
2446+ pane.update(cx, |pane, cx| {
2447+ pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2448+ })
2449+ .unwrap()
2450+ .await
2451+ .unwrap();
2452+ assert_item_labels(&pane, ["A", "C*"], cx);
2453+
2454+ pane.update(cx, |pane, cx| {
2455+ pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2456+ })
2457+ .unwrap()
2458+ .await
2459+ .unwrap();
2460+ assert_item_labels(&pane, ["A*"], cx);
2461+ }
2462+
2463+ #[gpui::test]
2464+ async fn test_close_inactive_items(cx: &mut TestAppContext) {
2465+ init_test(cx);
2466+ let fs = FakeFs::new(cx.executor());
2467+
2468+ let project = Project::test(fs, None, cx).await;
2469+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2470+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2471+
2472+ set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
2473+
2474+ pane.update(cx, |pane, cx| {
2475+ pane.close_inactive_items(&CloseInactiveItems, cx)
2476+ })
2477+ .unwrap()
2478+ .await
2479+ .unwrap();
2480+ assert_item_labels(&pane, ["C*"], cx);
2481+ }
2482+
2483+ #[gpui::test]
2484+ async fn test_close_clean_items(cx: &mut TestAppContext) {
2485+ init_test(cx);
2486+ let fs = FakeFs::new(cx.executor());
2487+
2488+ let project = Project::test(fs, None, cx).await;
2489+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2490+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2491+
2492+ add_labeled_item(&pane, "A", true, cx);
2493+ add_labeled_item(&pane, "B", false, cx);
2494+ add_labeled_item(&pane, "C", true, cx);
2495+ add_labeled_item(&pane, "D", false, cx);
2496+ add_labeled_item(&pane, "E", false, cx);
2497+ assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx);
2498+
2499+ pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx))
2500+ .unwrap()
2501+ .await
2502+ .unwrap();
2503+ assert_item_labels(&pane, ["A^", "C*^"], cx);
2504+ }
2505+
2506+ #[gpui::test]
2507+ async fn test_close_items_to_the_left(cx: &mut TestAppContext) {
2508+ init_test(cx);
2509+ let fs = FakeFs::new(cx.executor());
2510+
2511+ let project = Project::test(fs, None, cx).await;
2512+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2513+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2514+
2515+ set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
2516+
2517+ pane.update(cx, |pane, cx| {
2518+ pane.close_items_to_the_left(&CloseItemsToTheLeft, cx)
2519+ })
2520+ .unwrap()
2521+ .await
2522+ .unwrap();
2523+ assert_item_labels(&pane, ["C*", "D", "E"], cx);
2524+ }
2525+
2526+ #[gpui::test]
2527+ async fn test_close_items_to_the_right(cx: &mut TestAppContext) {
2528+ init_test(cx);
2529+ let fs = FakeFs::new(cx.executor());
2530+
2531+ let project = Project::test(fs, None, cx).await;
2532+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2533+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2534+
2535+ set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx);
2536+
2537+ pane.update(cx, |pane, cx| {
2538+ pane.close_items_to_the_right(&CloseItemsToTheRight, cx)
2539+ })
2540+ .unwrap()
2541+ .await
2542+ .unwrap();
2543+ assert_item_labels(&pane, ["A", "B", "C*"], cx);
2544+ }
2545+
2546+ #[gpui::test]
2547+ async fn test_close_all_items(cx: &mut TestAppContext) {
2548+ init_test(cx);
2549+ let fs = FakeFs::new(cx.executor());
2550+
2551+ let project = Project::test(fs, None, cx).await;
2552+ let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2553+ let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2554+
2555+ add_labeled_item(&pane, "A", false, cx);
2556+ add_labeled_item(&pane, "B", false, cx);
2557+ add_labeled_item(&pane, "C", false, cx);
2558+ assert_item_labels(&pane, ["A", "B", "C*"], cx);
2559+
2560+ pane.update(cx, |pane, cx| {
2561+ pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
2562+ })
2563+ .unwrap()
2564+ .await
2565+ .unwrap();
2566+ assert_item_labels(&pane, [], cx);
2567+
2568+ add_labeled_item(&pane, "A", true, cx);
2569+ add_labeled_item(&pane, "B", true, cx);
2570+ add_labeled_item(&pane, "C", true, cx);
2571+ assert_item_labels(&pane, ["A^", "B^", "C*^"], cx);
2572+
2573+ let save = pane
2574+ .update(cx, |pane, cx| {
2575+ pane.close_all_items(&CloseAllItems { save_intent: None }, cx)
2576+ })
2577+ .unwrap();
2578+
2579+ cx.executor().run_until_parked();
2580+ cx.simulate_prompt_answer(2);
2581+ save.await.unwrap();
2582+ assert_item_labels(&pane, [], cx);
2583+ }
2584+
2585+ fn init_test(cx: &mut TestAppContext) {
2586+ cx.update(|cx| {
2587+ let settings_store = SettingsStore::test(cx);
2588+ cx.set_global(settings_store);
2589+ theme::init(LoadThemes::JustBase, cx);
2590+ crate::init_settings(cx);
2591+ Project::init_settings(cx);
2592+ });
2593+ }
2594+
2595+ fn add_labeled_item(
2596+ pane: &View<Pane>,
2597+ label: &str,
2598+ is_dirty: bool,
2599+ cx: &mut VisualTestContext,
2600+ ) -> Box<View<TestItem>> {
2601+ pane.update(cx, |pane, cx| {
2602+ let labeled_item = Box::new(
2603+ cx.build_view(|cx| TestItem::new(cx).with_label(label).with_dirty(is_dirty)),
2604+ );
2605+ pane.add_item(labeled_item.clone(), false, false, None, cx);
2606+ labeled_item
2607+ })
2608+ }
2609+
2610+ fn set_labeled_items<const COUNT: usize>(
2611+ pane: &View<Pane>,
2612+ labels: [&str; COUNT],
2613+ cx: &mut VisualTestContext,
2614+ ) -> [Box<View<TestItem>>; COUNT] {
2615+ pane.update(cx, |pane, cx| {
2616+ pane.items.clear();
2617+ let mut active_item_index = 0;
2618+
2619+ let mut index = 0;
2620+ let items = labels.map(|mut label| {
2621+ if label.ends_with("*") {
2622+ label = label.trim_end_matches("*");
2623+ active_item_index = index;
2624+ }
2625+
2626+ let labeled_item =
2627+ Box::new(cx.build_view(|cx| TestItem::new(cx).with_label(label)));
2628+ pane.add_item(labeled_item.clone(), false, false, None, cx);
2629+ index += 1;
2630+ labeled_item
2631+ });
2632+
2633+ pane.activate_item(active_item_index, false, false, cx);
2634+
2635+ items
2636+ })
2637+ }
2638+
2639+ // Assert the item label, with the active item label suffixed with a '*'
2640+ fn assert_item_labels<const COUNT: usize>(
2641+ pane: &View<Pane>,
2642+ expected_states: [&str; COUNT],
2643+ cx: &mut VisualTestContext,
2644+ ) {
2645+ pane.update(cx, |pane, cx| {
2646+ let actual_states = pane
2647+ .items
2648+ .iter()
2649+ .enumerate()
2650+ .map(|(ix, item)| {
2651+ let mut state = item
2652+ .to_any()
2653+ .downcast::<TestItem>()
2654+ .unwrap()
2655+ .read(cx)
2656+ .label
2657+ .clone();
2658+ if ix == pane.active_item_index {
2659+ state.push('*');
2660+ }
2661+ if item.is_dirty(cx) {
2662+ state.push('^');
2663+ }
2664+ state
2665+ })
2666+ .collect::<Vec<_>>();
2667+
2668+ assert_eq!(
2669+ actual_states, expected_states,
2670+ "pane items do not match expectation"
2671+ );
2672+ })
2673+ }
2674+}
27922675
27932676 impl Render for DraggedTab {
27942677 type Element = <Tab as RenderOnce>::Rendered;
crates/workspace2/src/workspace2.rs+49−151
@@ -3556,6 +3556,8 @@ impl Render for Workspace { · 3555 unmodified lines
35563556 )
35573557 };
35583558
3559+ let theme = cx.theme().clone();
3560+ let colors = theme.colors();
35593561 cx.set_rem_size(ui_font_size);
35603562
35613563 self.actions(div(), cx)
@@ -3568,10 +3570,10 @@ impl Render for Workspace { · 6 unmodified lines
35683570 .gap_0()
35693571 .justify_start()
35703572 .items_start()
3571− .text_color(cx.theme().colors().text)
3572− .bg(cx.theme().colors().background)
3573+ .text_color(colors.text)
3574+ .bg(colors.background)
35733575 .border()
3574− .border_color(cx.theme().colors().border)
3576+ .border_color(colors.border)
35753577 .children(self.titlebar_item.clone())
35763578 .child(
35773579 div()
@@ -3584,7 +3586,7 @@ impl Render for Workspace { · 6 unmodified lines
35843586 .overflow_hidden()
35853587 .border_t()
35863588 .border_b()
3587− .border_color(cx.theme().colors().border)
3589+ .border_color(colors.border)
35883590 .child(
35893591 canvas(cx.listener(|workspace, bounds, _| {
35903592 workspace.bounds = *bounds;
@@ -3623,13 +3625,15 @@ impl Render for Workspace { · 32 unmodified lines
36233625 .flex_row()
36243626 .h_full()
36253627 // Left Dock
3626− .child(
3627− div()
3628− .flex()
3629− .flex_none()
3630− .overflow_hidden()
3631− .child(self.left_dock.clone()),
3632− )
3628+ .children(self.zoomed_position.ne(&Some(DockPosition::Left)).then(
3629+ || {
3630+ div()
3631+ .flex()
3632+ .flex_none()
3633+ .overflow_hidden()
3634+ .child(self.left_dock.clone())
3635+ },
3636+ ))
36333637 // Panes
36343638 .child(
36353639 div()
@@ -3646,154 +3650,48 @@ impl Render for Workspace { · 10 unmodified lines
36463650 &self.app_state,
36473651 cx,
36483652 ))
3649− .child(self.bottom_dock.clone()),
3653+ .children(
3654+ self.zoomed_position
3655+ .ne(&Some(DockPosition::Bottom))
3656+ .then(|| self.bottom_dock.clone()),
3657+ ),
36503658 )
36513659 // Right Dock
3652− .child(
3653− div()
3654− .flex()
3655− .flex_none()
3656− .overflow_hidden()
3657− .child(self.right_dock.clone()),
3658− ),
3660+ .children(self.zoomed_position.ne(&Some(DockPosition::Right)).then(
3661+ || {
3662+ div()
3663+ .flex()
3664+ .flex_none()
3665+ .overflow_hidden()
3666+ .child(self.right_dock.clone())
3667+ },
3668+ )),
36593669 )
3660− .children(self.render_notifications(cx)),
3670+ .children(self.render_notifications(cx))
3671+ .children(self.zoomed.as_ref().and_then(|view| {
3672+ let zoomed_view = view.upgrade()?;
3673+ let div = div()
3674+ .z_index(1)
3675+ .absolute()
3676+ .overflow_hidden()
3677+ .border_color(colors.border)
3678+ .bg(colors.background)
3679+ .child(zoomed_view)
3680+ .inset_0()
3681+ .shadow_lg();
3682+
3683+ Some(match self.zoomed_position {
3684+ Some(DockPosition::Left) => div.right_2().border_r(),
3685+ Some(DockPosition::Right) => div.left_2().border_l(),
3686+ Some(DockPosition::Bottom) => div.top_2().border_t(),
3687+ None => div.top_2().bottom_2().left_2().right_2().border(),
3688+ })
3689+ })),
36613690 )
36623691 .child(self.status_bar.clone())
36633692 }
36643693 }
36653694
3666−// impl View for Workspace {
3667−
3668−// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
3669−// let theme = theme::current(cx).clone();
3670−// Stack::new()
3671−// .with_child(
3672−// Flex::column()
3673−// .with_child(self.render_titlebar(&theme, cx))
3674−// .with_child(
3675−// Stack::new()
3676−// .with_child({
3677−// let project = self.project.clone();
3678−// Flex::row()
3679−// .with_children(self.render_dock(DockPosition::Left, cx))
3680−// .with_child(
3681−// Flex::column()
3682−// .with_child(
3683−// FlexItem::new(
3684−// self.center.render(
3685−// &project,
3686−// &theme,
3687−// &self.follower_states,
3688−// self.active_call(),
3689−// self.active_pane(),
3690−// self.zoomed
3691−// .as_ref()
3692−// .and_then(|zoomed| zoomed.upgrade(cx))
3693−// .as_ref(),
3694−// &self.app_state,
3695−// cx,
3696−// ),
3697−// )
3698−// .flex(1., true),
3699−// )
3700−// .with_children(
3701−// self.render_dock(DockPosition::Bottom, cx),
3702−// )
3703−// .flex(1., true),
3704−// )
3705−// .with_children(self.render_dock(DockPosition::Right, cx))
3706−// })
3707−// .with_child(Overlay::new(
3708−// Stack::new()
3709−// .with_children(self.zoomed.as_ref().and_then(|zoomed| {
3710−// enum ZoomBackground {}
3711−// let zoomed = zoomed.upgrade(cx)?;
3712−
3713−// let mut foreground_style =
3714−// theme.workspace.zoomed_pane_foreground;
3715−// if let Some(zoomed_dock_position) = self.zoomed_position {
3716−// foreground_style =
3717−// theme.workspace.zoomed_panel_foreground;
3718−// let margin = foreground_style.margin.top;
3719−// let border = foreground_style.border.top;
3720−
3721−// // Only include a margin and border on the opposite side.
3722−// foreground_style.margin.top = 0.;
3723−// foreground_style.margin.left = 0.;
3724−// foreground_style.margin.bottom = 0.;
3725−// foreground_style.margin.right = 0.;
3726−// foreground_style.border.top = false;
3727−// foreground_style.border.left = false;
3728−// foreground_style.border.bottom = false;
3729−// foreground_style.border.right = false;
3730−// match zoomed_dock_position {
3731−// DockPosition::Left => {
3732−// foreground_style.margin.right = margin;
3733−// foreground_style.border.right = border;
3734−// }
3735−// DockPosition::Right => {
3736−// foreground_style.margin.left = margin;
3737−// foreground_style.border.left = border;
3738−// }
3739−// DockPosition::Bottom => {
3740−// foreground_style.margin.top = margin;
3741−// foreground_style.border.top = border;
3742−// }
3743−// }
3744−// }
3745−
3746−// Some(
3747−// ChildView::new(&zoomed, cx)
3748−// .contained()
3749−// .with_style(foreground_style)
3750−// .aligned()
3751−// .contained()
3752−// .with_style(theme.workspace.zoomed_background)
3753−// .mouse::<ZoomBackground>(0)
3754−// .capture_all()
3755−// .on_down(
3756−// MouseButton::Left,
3757−// |_, this: &mut Self, cx| {
3758−// this.zoom_out(cx);
3759−// },
3760−// ),
3761−// )
3762−// }))
3763−// .with_children(self.modal.as_ref().map(|modal| {
3764−// // Prevent clicks within the modal from falling
3765−// // through to the rest of the workspace.
3766−// enum ModalBackground {}
3767−// MouseEventHandler::new::<ModalBackground, _>(
3768−// 0,
3769−// cx,
3770−// |_, cx| ChildView::new(modal.view.as_any(), cx),
3771−// )
3772−// .on_click(MouseButton::Left, |_, _, _| {})
3773−// .contained()
3774−// .with_style(theme.workspace.modal)
3775−// .aligned()
3776−// .top()
3777−// }))
3778−// .with_children(self.render_notifications(&theme.workspace, cx)),
3779−// ))
3780−// .provide_resize_bounds::<WorkspaceBounds>()
3781−// .flex(1.0, true),
3782−// )
3783−// .with_child(ChildView::new(&self.status_bar, cx))
3784−// .contained()
3785−// .with_background_color(theme.workspace.background),
3786−// )
3787−// .with_children(DragAndDrop::render(cx))
3788−// .with_children(self.render_disconnected_overlay(cx))
3789−// .into_any_named("workspace")
3790−// }
3791−
3792−// fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext<Self>) -> bool {
3793−// DragAndDrop::<Workspace>::update_modifiers(e.modifiers, cx)
3794−// }
3795−// }
3796−
37973695 impl WorkspaceStore {
37983696 pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
37993697 Self {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t