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
317 new_dock.add_panel(panel.clone(), workspace.clone(), cx);317 new_dock.add_panel(panel.clone(), workspace.clone(), cx);
318 if was_visible {318 if was_visible {
319 new_dock.set_open(true, cx);319 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);
321 }321 }
322 });322 });
323 }323 }
crates/workspace2/src/pane.rs+535−652
@@ -177,7 +177,7 @@ pub struct Pane { · 176 unmodified lines
177 toolbar: View<Toolbar>,177 toolbar: View<Toolbar>,
178 new_item_menu: Option<View<ContextMenu>>,178 new_item_menu: Option<View<ContextMenu>>,
179 split_item_menu: Option<View<ContextMenu>>,179 split_item_menu: Option<View<ContextMenu>>,
180 // tab_context_menu: ViewHandle<ContextMenu>,180 // tab_context_menu: View<ContextMenu>,
181 workspace: WeakView<Workspace>,181 workspace: WeakView<Workspace>,
182 project: Model<Project>,182 project: Model<Project>,
183 drag_split_direction: Option<SplitDirection>,183 drag_split_direction: Option<SplitDirection>,
@@ -256,11 +256,11 @@ struct DraggedTab { · 72 unmodified lines
256 256
257// struct TabBarContextMenu {257// struct TabBarContextMenu {
258// kind: TabBarContextMenuKind,258// kind: TabBarContextMenuKind,
259// handle: ViewHandle<ContextMenu>,259// handle: View<ContextMenu>,
260// }260// }
261 261
262// impl TabBarContextMenu {262// impl TabBarContextMenu {
263// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option<ViewHandle<ContextMenu>> {263// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option<View<ContextMenu>> {
264// if self.kind == kind {264// if self.kind == kind {
265// return Some(self.handle.clone());265// return Some(self.handle.clone());
266// }266// }
@@ -329,7 +329,7 @@ impl Pane { · 62 unmodified lines
329 ) -> Self {329 ) -> Self {
330 // todo!("context menu")330 // todo!("context menu")
331 // let pane_view_id = cx.view_id();331 // 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));
333 // context_menu.update(cx, |menu, _| {333 // context_menu.update(cx, |menu, _| {
334 // menu.set_position_mode(OverlayPositionMode::Local)334 // menu.set_position_mode(OverlayPositionMode::Local)
335 // });335 // });
@@ -368,7 +368,7 @@ impl Pane { · 32 unmodified lines
368 // kind: TabBarContextMenuKind::New,368 // kind: TabBarContextMenuKind::New,
369 // handle: context_menu,369 // handle: context_menu,
370 // },370 // },
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)),
372 workspace,372 workspace,
373 project,373 project,
374 // can_drop: Rc::new(|_, _| true),374 // can_drop: Rc::new(|_, _| true),
@@ -1963,52 +1963,6 @@ impl Render for Pane { · 1588 unmodified lines
1963 }),1963 }),
1964 )1964 )
1965 }1965 }
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 // }
2012}1966}
2013 1967
2014impl ItemNavHistory {1968impl ItemNavHistory {
@@ -2166,98 +2120,6 @@ impl NavHistoryState { · 151 unmodified lines
2166 }2120 }
2167}2121}
2168 2122
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
2261fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {2123fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {
2262 let path = buffer_path2124 let path = buffer_path
2263 .as_ref()2125 .as_ref()
@@ -2267,528 +2129,549 @@ fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String { · 3 unmodified lines
2267 format!("{path} contains unsaved edits. Do you want to save it?")2129 format!("{path} contains unsaved edits. Do you want to save it?")
2268}2130}
2269 2131
2270// todo!("uncomment tests")2132#[cfg(test)]
2271// #[cfg(test)]2133mod tests {
2272// mod tests {2134 use super::*;
2273// use super::*;2135 use crate::item::test::{TestItem, TestProjectItem};
2274// use crate::item::test::{TestItem, TestProjectItem};2136 use gpui::{TestAppContext, VisualTestContext};
2275// use gpui::TestAppContext;2137 use project::FakeFs;
2276// use project::FakeFs;2138 use settings::SettingsStore;
2277// use settings::SettingsStore;2139 use theme::LoadThemes;
2278 2140
2279// #[gpui::test]2141 #[gpui::test]
2280// async fn test_remove_active_empty(cx: &mut TestAppContext) {2142 async fn test_remove_active_empty(cx: &mut TestAppContext) {
2281// init_test(cx);2143 init_test(cx);
2282// let fs = FakeFs::new(cx.background());2144 let fs = FakeFs::new(cx.executor());
2283 2145
2284// let project = Project::test(fs, None, cx).await;2146 let project = Project::test(fs, None, cx).await;
2285// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));2147 let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2286// let workspace = window.root(cx);2148 let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2287// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());2149
2288 2150 pane.update(cx, |pane, cx| {
2289// pane.update(cx, |pane, cx| {2151 assert!(pane
2290// assert!(pane2152 .close_active_item(&CloseActiveItem { save_intent: None }, cx)
2291// .close_active_item(&CloseActiveItem { save_intent: None }, cx)2153 .is_none())
2292// .is_none())2154 });
2293// });2155 }
2294// }
2295 2156
2296// #[gpui::test]2157 #[gpui::test]
2297// async fn test_add_item_with_new_item(cx: &mut TestAppContext) {2158 async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
2298// cx.foreground().forbid_parking();2159 init_test(cx);
2299// init_test(cx);2160 let fs = FakeFs::new(cx.executor());
2300// let fs = FakeFs::new(cx.background());2161
2301 2162 let project = Project::test(fs, None, cx).await;
2302// let project = Project::test(fs, None, cx).await;2163 let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2303// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));2164 let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2304// let workspace = window.root(cx);2165
2305// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());2166 // 1. Add with a destination index
2306 2167 // a. Add before the active item
2307// // 1. Add with a destination index2168 set_labeled_items(&pane, ["A", "B*", "C"], cx);
2308// // a. Add before the active item2169 pane.update(cx, |pane, cx| {
2309// set_labeled_items(&pane, ["A", "B*", "C"], cx);2170 pane.add_item(
2310// pane.update(cx, |pane, cx| {2171 Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2311// pane.add_item(2172 false,
2312// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),2173 false,
2313// false,2174 Some(0),
2314// false,2175 cx,
2315// Some(0),2176 );
2316// cx,2177 });
2317// );2178 assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);
2318// });2179
2319// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx);2180 // b. Add after the active item
2320 2181 set_labeled_items(&pane, ["A", "B*", "C"], cx);
2321// // b. Add after the active item2182 pane.update(cx, |pane, cx| {
2322// set_labeled_items(&pane, ["A", "B*", "C"], cx);2183 pane.add_item(
2323// pane.update(cx, |pane, cx| {2184 Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2324// pane.add_item(2185 false,
2325// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),2186 false,
2326// false,2187 Some(2),
2327// false,2188 cx,
2328// Some(2),2189 );
2329// cx,2190 });
2330// );2191 assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
2331// });2192
2332// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);2193 // c. Add at the end of the item list (including off the length)
2333 2194 set_labeled_items(&pane, ["A", "B*", "C"], cx);
2334// // c. Add at the end of the item list (including off the length)2195 pane.update(cx, |pane, cx| {
2335// set_labeled_items(&pane, ["A", "B*", "C"], cx);2196 pane.add_item(
2336// pane.update(cx, |pane, cx| {2197 Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2337// pane.add_item(2198 false,
2338// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),2199 false,
2339// false,2200 Some(5),
2340// false,2201 cx,
2341// Some(5),2202 );
2342// cx,2203 });
2343// );2204 assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2344// });2205
2345// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);2206 // 2. Add without a destination index
2346 2207 // a. Add with active item at the start of the item list
2347// // 2. Add without a destination index2208 set_labeled_items(&pane, ["A*", "B", "C"], cx);
2348// // a. Add with active item at the start of the item list2209 pane.update(cx, |pane, cx| {
2349// set_labeled_items(&pane, ["A*", "B", "C"], cx);2210 pane.add_item(
2350// pane.update(cx, |pane, cx| {2211 Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2351// pane.add_item(2212 false,
2352// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),2213 false,
2353// false,2214 None,
2354// false,2215 cx,
2355// None,2216 );
2356// cx,2217 });
2357// );2218 set_labeled_items(&pane, ["A", "D*", "B", "C"], cx);
2358// });2219
2359// set_labeled_items(&pane, ["A", "D*", "B", "C"], cx);2220 // b. Add with active item at the end of the item list
2360 2221 set_labeled_items(&pane, ["A", "B", "C*"], cx);
2361// // b. Add with active item at the end of the item list2222 pane.update(cx, |pane, cx| {
2362// set_labeled_items(&pane, ["A", "B", "C*"], cx);2223 pane.add_item(
2363// pane.update(cx, |pane, cx| {2224 Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))),
2364// pane.add_item(2225 false,
2365// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))),2226 false,
2366// false,2227 None,
2367// false,2228 cx,
2368// None,2229 );
2369// cx,2230 });
2370// );2231 assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2371// });2232 }
2372// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2373// }
2374 2233
2375// #[gpui::test]2234 #[gpui::test]
2376// async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {2235 async fn test_add_item_with_existing_item(cx: &mut TestAppContext) {
2377// cx.foreground().forbid_parking();2236 init_test(cx);
2378// init_test(cx);2237 let fs = FakeFs::new(cx.executor());
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// }
2451 2238
2452// #[gpui::test]2239 let project = Project::test(fs, None, cx).await;
2453// async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {2240 let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2454// cx.foreground().forbid_parking();2241 let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
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// }
2521 2242
2522// #[gpui::test]2243 // 1. Add with a destination index
2523// async fn test_remove_item_ordering(cx: &mut TestAppContext) {2244 // 1a. Add before the active item
2524// init_test(cx);2245 let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2525// let fs = FakeFs::new(cx.background());2246 pane.update(cx, |pane, cx| {
2526 2247 pane.add_item(d, false, false, Some(0), cx);
2527// let project = Project::test(fs, None, cx).await;2248 });
2528// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));2249 assert_item_labels(&pane, ["D*", "A", "B", "C"], 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// }
2577 2250
2578// #[gpui::test]2251 // 1b. Add after the active item
2579// async fn test_close_inactive_items(cx: &mut TestAppContext) {2252 let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2580// init_test(cx);2253 pane.update(cx, |pane, cx| {
2581// let fs = FakeFs::new(cx.background());2254 pane.add_item(d, false, false, Some(2), cx);
2582 2255 });
2583// let project = Project::test(fs, None, cx).await;2256 assert_item_labels(&pane, ["A", "B", "D*", "C"], cx);
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// }
2598 2257
2599// #[gpui::test]2258 // 1c. Add at the end of the item list (including off the length)
2600// async fn test_close_clean_items(cx: &mut TestAppContext) {2259 let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx);
2601// init_test(cx);2260 pane.update(cx, |pane, cx| {
2602// let fs = FakeFs::new(cx.background());2261 pane.add_item(a, false, false, Some(5), cx);
2603 2262 });
2604// let project = Project::test(fs, None, cx).await;2263 assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
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// }
2622 2264
2623// #[gpui::test]2265 // 1d. Add same item to active index
2624// async fn test_close_items_to_the_left(cx: &mut TestAppContext) {2266 let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
2625// init_test(cx);2267 pane.update(cx, |pane, cx| {
2626// let fs = FakeFs::new(cx.background());2268 pane.add_item(b, false, false, Some(1), cx);
2627 2269 });
2628// let project = Project::test(fs, None, cx).await;2270 assert_item_labels(&pane, ["A", "B*", "C"], cx);
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// }
2643 2271
2644// #[gpui::test]2272 // 1e. Add item to index after same item in last position
2645// async fn test_close_items_to_the_right(cx: &mut TestAppContext) {2273 let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx);
2646// init_test(cx);2274 pane.update(cx, |pane, cx| {
2647// let fs = FakeFs::new(cx.background());2275 pane.add_item(c, false, false, Some(2), cx);
2648 2276 });
2649// let project = Project::test(fs, None, cx).await;2277 assert_item_labels(&pane, ["A", "B", "C*"], cx);
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// }
2664 2278
2665// #[gpui::test]2279 // 2. Add without a destination index
2666// async fn test_close_all_items(cx: &mut TestAppContext) {2280 // 2a. Add with active item at the start of the item list
2667// init_test(cx);2281 let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx);
2668// let fs = FakeFs::new(cx.background());2282 pane.update(cx, |pane, cx| {
2669 2283 pane.add_item(d, false, false, None, cx);
2670// let project = Project::test(fs, None, cx).await;2284 });
2671// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));2285 assert_item_labels(&pane, ["A", "D*", "B", "C"], 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// }
2704 2286
2705// fn init_test(cx: &mut TestAppContext) {2287 // 2b. Add with active item at the end of the item list
2706// cx.update(|cx| {2288 let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx);
2707// cx.set_global(SettingsStore::test(cx));2289 pane.update(cx, |pane, cx| {
2708// theme::init((), cx);2290 pane.add_item(a, false, false, None, cx);
2709// crate::init_settings(cx);2291 });
2710// Project::init_settings(cx);2292 assert_item_labels(&pane, ["B", "C", "D", "A*"], cx);
2711// });
2712// }
2713 2293
2714// fn add_labeled_item(2294 // 2c. Add active item to active item at end of list
2715// pane: &ViewHandle<Pane>,2295 let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx);
2716// label: &str,2296 pane.update(cx, |pane, cx| {
2717// is_dirty: bool,2297 pane.add_item(c, false, false, None, cx);
2718// cx: &mut TestAppContext,2298 });
2719// ) -> Box<ViewHandle<TestItem>> {2299 assert_item_labels(&pane, ["A", "B", "C*"], cx);
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// }
2727 2300
2728// fn set_labeled_items<const COUNT: usize>(2301 // 2d. Add active item to active item at start of list
2729// pane: &ViewHandle<Pane>,2302 let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx);
2730// labels: [&str; COUNT],2303 pane.update(cx, |pane, cx| {
2731// cx: &mut TestAppContext,2304 pane.add_item(a, false, false, None, cx);
2732// ) -> [Box<ViewHandle<TestItem>>; COUNT] {2305 });
2733// pane.update(cx, |pane, cx| {2306 assert_item_labels(&pane, ["A*", "B", "C"], cx);
2734// pane.items.clear();2307 }
2735// let mut active_item_index = 0;2308
2736 2309 #[gpui::test]
2737// let mut index = 0;2310 async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) {
2738// let items = labels.map(|mut label| {2311 init_test(cx);
2739// if label.ends_with("*") {2312 let fs = FakeFs::new(cx.executor());
2740// label = label.trim_end_matches("*");2313
2741// active_item_index = index;2314 let project = Project::test(fs, None, cx).await;
2742// }2315 let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2743 2316 let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2744// let labeled_item = Box::new(cx.add_view(|_| TestItem::new().with_label(label)));2317
2745// pane.add_item(labeled_item.clone(), false, false, None, cx);2318 // singleton view
2746// index += 1;2319 pane.update(cx, |pane, cx| {
2747// labeled_item2320 pane.add_item(
2748// });2321 Box::new(cx.build_view(|cx| {
2749 2322 TestItem::new(cx)
2750// pane.activate_item(active_item_index, false, false, cx);2323 .with_singleton(true)
2751 2324 .with_label("buffer 1")
2752// items2325 .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
2753// })2326 })),
2754// }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 }
2755 2407
2756// // Assert the item label, with the active item label suffixed with a '*'2408 #[gpui::test]
2757// fn assert_item_labels<const COUNT: usize>(2409 async fn test_remove_item_ordering(cx: &mut TestAppContext) {
2758// pane: &ViewHandle<Pane>,2410 init_test(cx);
2759// expected_states: [&str; COUNT],2411 let fs = FakeFs::new(cx.executor());
2760// cx: &mut TestAppContext,2412
2761// ) {2413 let project = Project::test(fs, None, cx).await;
2762// pane.read_with(cx, |pane, cx| {2414 let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
2763// let actual_states = pane2415 let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
2764// .items2416
2765// .iter()2417 add_labeled_item(&pane, "A", false, cx);
2766// .enumerate()2418 add_labeled_item(&pane, "B", false, cx);
2767// .map(|(ix, item)| {2419 add_labeled_item(&pane, "C", false, cx);
2768// let mut state = item2420 add_labeled_item(&pane, "D", false, cx);
2769// .as_any()2421 assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2770// .downcast_ref::<TestItem>()2422
2771// .unwrap()2423 pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx));
2772// .read(cx)2424 add_labeled_item(&pane, "1", false, cx);
2773// .label2425 assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx);
2774// .clone();2426
2775// if ix == pane.active_item_index {2427 pane.update(cx, |pane, cx| {
2776// state.push('*');2428 pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2777// }2429 })
2778// if item.is_dirty(cx) {2430 .unwrap()
2779// state.push('^');2431 .await
2780// }2432 .unwrap();
2781// state2433 assert_item_labels(&pane, ["A", "B*", "C", "D"], cx);
2782// })2434
2783// .collect::<Vec<_>>();2435 pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx));
2784 2436 assert_item_labels(&pane, ["A", "B", "C", "D*"], cx);
2785// assert_eq!(2437
2786// actual_states, expected_states,2438 pane.update(cx, |pane, cx| {
2787// "pane items do not match expectation"2439 pane.close_active_item(&CloseActiveItem { save_intent: None }, cx)
2788// );2440 })
2789// })2441 .unwrap()
2790// }2442 .await
2791// }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}
2792 2675
2793impl Render for DraggedTab {2676impl Render for DraggedTab {
2794 type Element = <Tab as RenderOnce>::Rendered;2677 type Element = <Tab as RenderOnce>::Rendered;
crates/workspace2/src/workspace2.rs+49−151
@@ -3556,6 +3556,8 @@ impl Render for Workspace { · 3555 unmodified lines
3556 )3556 )
3557 };3557 };
3558 3558
3559 let theme = cx.theme().clone();
3560 let colors = theme.colors();
3559 cx.set_rem_size(ui_font_size);3561 cx.set_rem_size(ui_font_size);
3560 3562
3561 self.actions(div(), cx)3563 self.actions(div(), cx)
@@ -3568,10 +3570,10 @@ impl Render for Workspace { · 6 unmodified lines
3568 .gap_0()3570 .gap_0()
3569 .justify_start()3571 .justify_start()
3570 .items_start()3572 .items_start()
3571 .text_color(cx.theme().colors().text)3573 .text_color(colors.text)
3572 .bg(cx.theme().colors().background)3574 .bg(colors.background)
3573 .border()3575 .border()
3574 .border_color(cx.theme().colors().border)3576 .border_color(colors.border)
3575 .children(self.titlebar_item.clone())3577 .children(self.titlebar_item.clone())
3576 .child(3578 .child(
3577 div()3579 div()
@@ -3584,7 +3586,7 @@ impl Render for Workspace { · 6 unmodified lines
3584 .overflow_hidden()3586 .overflow_hidden()
3585 .border_t()3587 .border_t()
3586 .border_b()3588 .border_b()
3587 .border_color(cx.theme().colors().border)3589 .border_color(colors.border)
3588 .child(3590 .child(
3589 canvas(cx.listener(|workspace, bounds, _| {3591 canvas(cx.listener(|workspace, bounds, _| {
3590 workspace.bounds = *bounds;3592 workspace.bounds = *bounds;
@@ -3623,13 +3625,15 @@ impl Render for Workspace { · 32 unmodified lines
3623 .flex_row()3625 .flex_row()
3624 .h_full()3626 .h_full()
3625 // Left Dock3627 // Left Dock
3626 .child(3628 .children(self.zoomed_position.ne(&Some(DockPosition::Left)).then(
3627 div()3629 || {
3628 .flex()3630 div()
3629 .flex_none()3631 .flex()
3630 .overflow_hidden()3632 .flex_none()
3631 .child(self.left_dock.clone()),3633 .overflow_hidden()
3632 )3634 .child(self.left_dock.clone())
3635 },
3636 ))
3633 // Panes3637 // Panes
3634 .child(3638 .child(
3635 div()3639 div()
@@ -3646,154 +3650,48 @@ impl Render for Workspace { · 10 unmodified lines
3646 &self.app_state,3650 &self.app_state,
3647 cx,3651 cx,
3648 ))3652 ))
3649 .child(self.bottom_dock.clone()),3653 .children(
3654 self.zoomed_position
3655 .ne(&Some(DockPosition::Bottom))
3656 .then(|| self.bottom_dock.clone()),
3657 ),
3650 )3658 )
3651 // Right Dock3659 // Right Dock
3652 .child(3660 .children(self.zoomed_position.ne(&Some(DockPosition::Right)).then(
3653 div()3661 || {
3654 .flex()3662 div()
3655 .flex_none()3663 .flex()
3656 .overflow_hidden()3664 .flex_none()
3657 .child(self.right_dock.clone()),3665 .overflow_hidden()
3658 ),3666 .child(self.right_dock.clone())
3667 },
3668 )),
3659 )3669 )
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 })),
3661 )3690 )
3662 .child(self.status_bar.clone())3691 .child(self.status_bar.clone())
3663 }3692 }
3664}3693}
3665 3694
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
3797impl WorkspaceStore {3695impl WorkspaceStore {
3798 pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {3696 pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
3799 Self {3697 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