Add blank pane experience
This commit is contained in:
@@ -13,7 +13,7 @@ use gpui::{
|
||||
use settings::{DockAnchor, Settings};
|
||||
use theme::Theme;
|
||||
|
||||
use crate::{sidebar::SidebarSide, ItemHandle, Pane, Workspace};
|
||||
use crate::{sidebar::SidebarSide, BackgroundActions, ItemHandle, Pane, Workspace};
|
||||
pub use toggle_dock_button::ToggleDockButton;
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize)]
|
||||
@@ -182,11 +182,12 @@ pub struct Dock {
|
||||
impl Dock {
|
||||
pub fn new(
|
||||
default_item_factory: DockDefaultItemFactory,
|
||||
background_actions: BackgroundActions,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Self {
|
||||
let position = DockPosition::Hidden(cx.global::<Settings>().default_dock_anchor);
|
||||
|
||||
let pane = cx.add_view(|cx| Pane::new(Some(position.anchor()), cx));
|
||||
let pane = cx.add_view(|cx| Pane::new(Some(position.anchor()), background_actions, cx));
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.set_active(false, cx);
|
||||
});
|
||||
@@ -492,6 +493,7 @@ mod tests {
|
||||
0,
|
||||
project.clone(),
|
||||
default_item_factory,
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -620,7 +622,14 @@ mod tests {
|
||||
cx.update(|cx| init(cx));
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, default_item_factory, cx)
|
||||
Workspace::new(
|
||||
Default::default(),
|
||||
0,
|
||||
project,
|
||||
default_item_factory,
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
|
||||
@@ -110,6 +110,8 @@ impl_internal_actions!(
|
||||
|
||||
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
|
||||
|
||||
pub type BackgroundActions = fn() -> &'static [(&'static str, &'static dyn Action)];
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
|
||||
pane.activate_item(action.0, true, true, cx);
|
||||
@@ -215,6 +217,7 @@ pub struct Pane {
|
||||
toolbar: ViewHandle<Toolbar>,
|
||||
tab_bar_context_menu: ViewHandle<ContextMenu>,
|
||||
docked: Option<DockAnchor>,
|
||||
background_actions: BackgroundActions,
|
||||
}
|
||||
|
||||
pub struct ItemNavHistory {
|
||||
@@ -271,7 +274,11 @@ enum ItemType {
|
||||
}
|
||||
|
||||
impl Pane {
|
||||
pub fn new(docked: Option<DockAnchor>, cx: &mut ViewContext<Self>) -> Self {
|
||||
pub fn new(
|
||||
docked: Option<DockAnchor>,
|
||||
background_actions: BackgroundActions,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let handle = cx.weak_handle();
|
||||
let context_menu = cx.add_view(ContextMenu::new);
|
||||
Self {
|
||||
@@ -292,6 +299,7 @@ impl Pane {
|
||||
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
||||
tab_bar_context_menu: context_menu,
|
||||
docked,
|
||||
background_actions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1415,6 +1423,64 @@ impl Pane {
|
||||
.flex(1., false)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_blank_pane(&mut self, theme: &Theme, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let background = theme.workspace.background;
|
||||
let keystroke_style = &theme.context_menu.item;
|
||||
let theme = &theme.workspace.blank_pane;
|
||||
Stack::new()
|
||||
.with_children([
|
||||
Empty::new()
|
||||
.contained()
|
||||
.with_background_color(background)
|
||||
.boxed(),
|
||||
Flex::column()
|
||||
.align_children_center()
|
||||
.with_children([
|
||||
theme::ui::icon(&theme.logo).aligned().boxed(),
|
||||
Flex::column()
|
||||
.with_children({
|
||||
enum KeyboardHint {}
|
||||
let keyboard_hint = &theme.keyboard_hint;
|
||||
(self.background_actions)().into_iter().enumerate().map(
|
||||
move |(idx, (text, action))| {
|
||||
let hint_action = action.boxed_clone();
|
||||
MouseEventHandler::<KeyboardHint>::new(
|
||||
idx,
|
||||
cx,
|
||||
move |state, cx| {
|
||||
theme::ui::keystroke_label(
|
||||
text,
|
||||
&keyboard_hint.style_for(state, false),
|
||||
&keystroke_style
|
||||
.style_for(state, false)
|
||||
.keystroke,
|
||||
hint_action,
|
||||
cx,
|
||||
)
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
.on_click(MouseButton::Left, move |_, cx| {
|
||||
cx.dispatch_any_action(action.boxed_clone())
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
})
|
||||
.contained()
|
||||
.with_style(theme.keyboard_hints)
|
||||
.constrained()
|
||||
.with_max_width(theme.keyboard_hint_width)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
])
|
||||
.aligned()
|
||||
.boxed(),
|
||||
])
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Pane {
|
||||
@@ -1508,11 +1574,8 @@ impl View for Pane {
|
||||
enum EmptyPane {}
|
||||
let theme = cx.global::<Settings>().theme.clone();
|
||||
|
||||
dragged_item_receiver::<EmptyPane, _>(0, 0, false, None, cx, |_, _| {
|
||||
Empty::new()
|
||||
.contained()
|
||||
.with_background_color(theme.workspace.background)
|
||||
.boxed()
|
||||
dragged_item_receiver::<EmptyPane, _>(0, 0, false, None, cx, |_, cx| {
|
||||
self.render_blank_pane(&theme, cx)
|
||||
})
|
||||
.on_down(MouseButton::Left, |_, cx| {
|
||||
cx.focus_parent_view();
|
||||
@@ -1809,9 +1872,7 @@ mod tests {
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx)
|
||||
});
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// 1. Add with a destination index
|
||||
@@ -1899,9 +1960,7 @@ mod tests {
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx)
|
||||
});
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// 1. Add with a destination index
|
||||
@@ -1977,9 +2036,7 @@ mod tests {
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx)
|
||||
});
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
// singleton view
|
||||
@@ -2088,8 +2145,7 @@ mod tests {
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (_, workspace) =
|
||||
cx.add_window(|cx| Workspace::new(None, 0, project, |_, _| unimplemented!(), cx));
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
add_labled_item(&workspace, &pane, "A", cx);
|
||||
|
||||
@@ -432,6 +432,7 @@ pub struct AppState {
|
||||
fn(Option<WindowBounds>, Option<uuid::Uuid>, &dyn Platform) -> WindowOptions<'static>,
|
||||
pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>),
|
||||
pub dock_default_item_factory: DockDefaultItemFactory,
|
||||
pub background_actions: BackgroundActions,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -455,6 +456,7 @@ impl AppState {
|
||||
initialize_workspace: |_, _, _| {},
|
||||
build_window_options: |_, _, _| Default::default(),
|
||||
dock_default_item_factory: |_, _| unimplemented!(),
|
||||
background_actions: || unimplemented!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -542,6 +544,7 @@ pub struct Workspace {
|
||||
active_call: Option<(ModelHandle<ActiveCall>, Vec<gpui::Subscription>)>,
|
||||
leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
|
||||
database_id: WorkspaceId,
|
||||
background_actions: BackgroundActions,
|
||||
_window_subscriptions: [Subscription; 3],
|
||||
_apply_leader_updates: Task<Result<()>>,
|
||||
_observe_current_user: Task<()>,
|
||||
@@ -572,6 +575,7 @@ impl Workspace {
|
||||
workspace_id: WorkspaceId,
|
||||
project: ModelHandle<Project>,
|
||||
dock_default_factory: DockDefaultItemFactory,
|
||||
background_actions: BackgroundActions,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
||||
@@ -602,7 +606,7 @@ impl Workspace {
|
||||
})
|
||||
.detach();
|
||||
|
||||
let center_pane = cx.add_view(|cx| Pane::new(None, cx));
|
||||
let center_pane = cx.add_view(|cx| Pane::new(None, background_actions, cx));
|
||||
let pane_id = center_pane.id();
|
||||
cx.subscribe(¢er_pane, move |this, _, event, cx| {
|
||||
this.handle_pane_event(pane_id, event, cx)
|
||||
@@ -610,7 +614,7 @@ impl Workspace {
|
||||
.detach();
|
||||
cx.focus(¢er_pane);
|
||||
cx.emit(Event::PaneAdded(center_pane.clone()));
|
||||
let dock = Dock::new(dock_default_factory, cx);
|
||||
let dock = Dock::new(dock_default_factory, background_actions, cx);
|
||||
let dock_pane = dock.pane().clone();
|
||||
|
||||
let fs = project.read(cx).fs().clone();
|
||||
@@ -730,6 +734,7 @@ impl Workspace {
|
||||
window_edited: false,
|
||||
active_call,
|
||||
database_id: workspace_id,
|
||||
background_actions,
|
||||
_observe_current_user,
|
||||
_apply_leader_updates,
|
||||
leader_updates_tx,
|
||||
@@ -818,6 +823,7 @@ impl Workspace {
|
||||
workspace_id,
|
||||
project_handle,
|
||||
app_state.dock_default_item_factory,
|
||||
app_state.background_actions,
|
||||
cx,
|
||||
);
|
||||
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
|
||||
@@ -1432,7 +1438,7 @@ impl Workspace {
|
||||
}
|
||||
|
||||
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
|
||||
let pane = cx.add_view(|cx| Pane::new(None, cx));
|
||||
let pane = cx.add_view(|cx| Pane::new(None, self.background_actions, cx));
|
||||
let pane_id = pane.id();
|
||||
cx.subscribe(&pane, move |this, _, event, cx| {
|
||||
this.handle_pane_event(pane_id, event, cx)
|
||||
@@ -2648,6 +2654,11 @@ impl Workspace {
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
||||
Self::new(None, 0, project, |_, _| None, || &[], cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_if_database_failed(workspace: &ViewHandle<Workspace>, cx: &mut AsyncAppContext) {
|
||||
@@ -2988,17 +2999,10 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use fs::FakeFs;
|
||||
use gpui::{executor::Deterministic, TestAppContext, ViewContext};
|
||||
use gpui::{executor::Deterministic, TestAppContext};
|
||||
use project::{Project, ProjectEntryId};
|
||||
use serde_json::json;
|
||||
|
||||
pub fn default_item_factory(
|
||||
_workspace: &mut Workspace,
|
||||
_cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<Box<dyn ItemHandle>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_tab_disambiguation(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
@@ -3011,7 +3015,8 @@ mod tests {
|
||||
Default::default(),
|
||||
0,
|
||||
project.clone(),
|
||||
default_item_factory,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -3083,7 +3088,8 @@ mod tests {
|
||||
Default::default(),
|
||||
0,
|
||||
project.clone(),
|
||||
default_item_factory,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -3183,7 +3189,8 @@ mod tests {
|
||||
Default::default(),
|
||||
0,
|
||||
project.clone(),
|
||||
default_item_factory,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -3222,7 +3229,14 @@ mod tests {
|
||||
|
||||
let project = Project::test(fs, None, cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, default_item_factory, cx)
|
||||
Workspace::new(
|
||||
Default::default(),
|
||||
0,
|
||||
project,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let item1 = cx.add_view(&workspace, |cx| {
|
||||
@@ -3331,7 +3345,14 @@ mod tests {
|
||||
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, default_item_factory, cx)
|
||||
Workspace::new(
|
||||
Default::default(),
|
||||
0,
|
||||
project,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
// Create several workspace items with single project entries, and two
|
||||
@@ -3440,7 +3461,14 @@ mod tests {
|
||||
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, default_item_factory, cx)
|
||||
Workspace::new(
|
||||
Default::default(),
|
||||
0,
|
||||
project,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let item = cx.add_view(&workspace, |cx| {
|
||||
@@ -3559,7 +3587,14 @@ mod tests {
|
||||
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| {
|
||||
Workspace::new(Default::default(), 0, project, default_item_factory, cx)
|
||||
Workspace::new(
|
||||
Default::default(),
|
||||
0,
|
||||
project,
|
||||
|_, _| unimplemented!(),
|
||||
|| unimplemented!(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let item = cx.add_view(&workspace, |cx| {
|
||||
|
||||
Reference in New Issue
Block a user