Merge remote-tracking branch 'origin/main' into zmd
This commit is contained in:
commit
747322a02d
107 changed files with 5048 additions and 3991 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,125 +0,0 @@
|
|||
use super::{icon_for_dock_anchor, Dock, FocusDock, HideDock};
|
||||
use crate::{handle_dropped_item, StatusItemView, Workspace};
|
||||
use gpui::{
|
||||
elements::{Empty, MouseEventHandler, Svg},
|
||||
platform::CursorStyle,
|
||||
platform::MouseButton,
|
||||
AnyElement, Element, Entity, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
|
||||
pub struct ToggleDockButton {
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
}
|
||||
|
||||
impl ToggleDockButton {
|
||||
pub fn new(workspace: ViewHandle<Workspace>, cx: &mut ViewContext<Self>) -> Self {
|
||||
// When dock moves, redraw so that the icon and toggle status matches.
|
||||
cx.subscribe(&workspace, |_, _, _, cx| cx.notify()).detach();
|
||||
|
||||
Self {
|
||||
workspace: workspace.downgrade(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for ToggleDockButton {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for ToggleDockButton {
|
||||
fn ui_name() -> &'static str {
|
||||
"Dock Toggle"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> AnyElement<Self> {
|
||||
let workspace = self.workspace.upgrade(cx);
|
||||
|
||||
if workspace.is_none() {
|
||||
return Empty::new().into_any();
|
||||
}
|
||||
|
||||
let workspace = workspace.unwrap();
|
||||
let dock_position = workspace.read(cx).dock.position;
|
||||
let dock_pane = workspace.read(cx).dock_pane().clone();
|
||||
|
||||
let theme = theme::current(cx).clone();
|
||||
|
||||
let button = MouseEventHandler::<Self, _>::new(0, cx, {
|
||||
let theme = theme.clone();
|
||||
move |state, _| {
|
||||
let style = theme
|
||||
.workspace
|
||||
.status_bar
|
||||
.sidebar_buttons
|
||||
.item
|
||||
.style_for(state, dock_position.is_visible());
|
||||
|
||||
Svg::new(icon_for_dock_anchor(dock_position.anchor()))
|
||||
.with_color(style.icon_color)
|
||||
.constrained()
|
||||
.with_width(style.icon_size)
|
||||
.with_height(style.icon_size)
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
}
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_up(MouseButton::Left, move |event, this, cx| {
|
||||
let drop_index = dock_pane.read(cx).items_len() + 1;
|
||||
handle_dropped_item(
|
||||
event,
|
||||
this.workspace.clone(),
|
||||
&dock_pane.downgrade(),
|
||||
drop_index,
|
||||
false,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
if dock_position.is_visible() {
|
||||
button
|
||||
.on_click(MouseButton::Left, |_, this, cx| {
|
||||
if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Dock::hide_dock(workspace, &Default::default(), cx)
|
||||
})
|
||||
}
|
||||
})
|
||||
.with_tooltip::<Self>(
|
||||
0,
|
||||
"Hide Dock".into(),
|
||||
Some(Box::new(HideDock)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
button
|
||||
.on_click(MouseButton::Left, |_, this, cx| {
|
||||
if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Dock::focus_dock(workspace, &Default::default(), cx)
|
||||
})
|
||||
}
|
||||
})
|
||||
.with_tooltip::<Self>(
|
||||
0,
|
||||
"Focus Dock".into(),
|
||||
Some(Box::new(FocusDock)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusItemView for ToggleDockButton {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
_active_pane_item: Option<&dyn crate::ItemHandle>,
|
||||
_cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
//Not applicable
|
||||
}
|
||||
}
|
|
@ -437,7 +437,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
|||
for item_event in T::to_item_events(event).into_iter() {
|
||||
match item_event {
|
||||
ItemEvent::CloseItem => {
|
||||
Pane::close_item_by_id(workspace, pane, item.id(), cx)
|
||||
pane.update(cx, |pane, cx| pane.close_item_by_id(item.id(), cx))
|
||||
.detach_and_log_err(cx);
|
||||
return;
|
||||
}
|
||||
|
@ -769,7 +769,7 @@ impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
|
|||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::{Item, ItemEvent};
|
||||
use crate::{sidebar::SidebarItem, ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
|
||||
use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
|
||||
use gpui::{
|
||||
elements::Empty, AnyElement, AppContext, Element, Entity, ModelHandle, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
|
@ -1062,6 +1062,4 @@ pub(crate) mod test {
|
|||
Task::Ready(Some(anyhow::Ok(view)))
|
||||
}
|
||||
}
|
||||
|
||||
impl SidebarItem for TestItem {}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@ use gpui::{
|
|||
use project::ProjectEntryId;
|
||||
|
||||
pub fn dragged_item_receiver<Tag, D, F>(
|
||||
pane: &Pane,
|
||||
region_id: usize,
|
||||
drop_index: usize,
|
||||
allow_same_pane: bool,
|
||||
|
@ -24,22 +25,24 @@ where
|
|||
D: Element<Pane>,
|
||||
F: FnOnce(&mut MouseState, &mut ViewContext<Pane>) -> D,
|
||||
{
|
||||
MouseEventHandler::<Tag, _>::above(region_id, cx, |state, cx| {
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
let drag_position = if (pane.can_drop)(drag_and_drop, cx) {
|
||||
drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
.or_else(|| {
|
||||
drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut handler = MouseEventHandler::<Tag, _>::above(region_id, cx, |state, cx| {
|
||||
// Observing hovered will cause a render when the mouse enters regardless
|
||||
// of if mouse position was accessed before
|
||||
let drag_position = if state.hovered() {
|
||||
cx.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
.or_else(|| {
|
||||
cx.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let drag_position = if state.hovered() { drag_position } else { None };
|
||||
Stack::new()
|
||||
.with_child(render_child(state, cx))
|
||||
.with_children(drag_position.map(|drag_position| {
|
||||
|
@ -64,38 +67,44 @@ where
|
|||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
.on_up(MouseButton::Left, {
|
||||
move |event, pane, cx| {
|
||||
let workspace = pane.workspace.clone();
|
||||
let pane = cx.weak_handle();
|
||||
handle_dropped_item(
|
||||
event,
|
||||
workspace,
|
||||
&pane,
|
||||
drop_index,
|
||||
allow_same_pane,
|
||||
split_margin,
|
||||
cx,
|
||||
);
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
.on_move(|_, _, cx| {
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
});
|
||||
|
||||
if drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.is_some()
|
||||
|| drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.is_some()
|
||||
{
|
||||
cx.notify();
|
||||
} else {
|
||||
cx.propagate_event();
|
||||
}
|
||||
})
|
||||
if drag_position.is_some() {
|
||||
handler = handler
|
||||
.on_up(MouseButton::Left, {
|
||||
move |event, pane, cx| {
|
||||
let workspace = pane.workspace.clone();
|
||||
let pane = cx.weak_handle();
|
||||
handle_dropped_item(
|
||||
event,
|
||||
workspace,
|
||||
&pane,
|
||||
drop_index,
|
||||
allow_same_pane,
|
||||
split_margin,
|
||||
cx,
|
||||
);
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
.on_move(|_, _, cx| {
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
|
||||
if drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.is_some()
|
||||
|| drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.is_some()
|
||||
{
|
||||
cx.notify();
|
||||
} else {
|
||||
cx.propagate_event();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handler
|
||||
}
|
||||
|
||||
pub fn handle_dropped_item<V: View>(
|
||||
|
@ -115,7 +124,7 @@ pub fn handle_dropped_item<V: View>(
|
|||
let action = if let Some((_, dragged_item)) =
|
||||
drag_and_drop.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
{
|
||||
Action::Move(dragged_item.pane.clone(), dragged_item.item.id())
|
||||
Action::Move(dragged_item.pane.clone(), dragged_item.handle.id())
|
||||
} else if let Some((_, project_entry)) =
|
||||
drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ use gpui::{
|
|||
elements::*,
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
Axis, Border, ModelHandle, ViewContext, ViewHandle,
|
||||
AnyViewHandle, Axis, Border, ModelHandle, ViewContext, ViewHandle,
|
||||
};
|
||||
use project::Project;
|
||||
use serde::Deserialize;
|
||||
|
@ -71,6 +71,7 @@ impl PaneGroup {
|
|||
follower_states: &FollowerStatesByLeader,
|
||||
active_call: Option<&ModelHandle<ActiveCall>>,
|
||||
active_pane: &ViewHandle<Pane>,
|
||||
zoomed: Option<&AnyViewHandle>,
|
||||
app_state: &Arc<AppState>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> AnyElement<Workspace> {
|
||||
|
@ -80,6 +81,7 @@ impl PaneGroup {
|
|||
follower_states,
|
||||
active_call,
|
||||
active_pane,
|
||||
zoomed,
|
||||
app_state,
|
||||
cx,
|
||||
)
|
||||
|
@ -134,6 +136,7 @@ impl Member {
|
|||
follower_states: &FollowerStatesByLeader,
|
||||
active_call: Option<&ModelHandle<ActiveCall>>,
|
||||
active_pane: &ViewHandle<Pane>,
|
||||
zoomed: Option<&AnyViewHandle>,
|
||||
app_state: &Arc<AppState>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> AnyElement<Workspace> {
|
||||
|
@ -141,6 +144,12 @@ impl Member {
|
|||
|
||||
match self {
|
||||
Member::Pane(pane) => {
|
||||
let pane_element = if Some(&**pane) == zoomed {
|
||||
Empty::new().into_any()
|
||||
} else {
|
||||
ChildView::new(pane, cx).into_any()
|
||||
};
|
||||
|
||||
let leader = follower_states
|
||||
.iter()
|
||||
.find_map(|(leader_id, follower_states)| {
|
||||
|
@ -257,7 +266,7 @@ impl Member {
|
|||
};
|
||||
|
||||
Stack::new()
|
||||
.with_child(ChildView::new(pane, cx).contained().with_border(border))
|
||||
.with_child(pane_element.contained().with_border(border))
|
||||
.with_children(leader_status_box)
|
||||
.into_any()
|
||||
}
|
||||
|
@ -267,6 +276,7 @@ impl Member {
|
|||
follower_states,
|
||||
active_call,
|
||||
active_pane,
|
||||
zoomed,
|
||||
app_state,
|
||||
cx,
|
||||
),
|
||||
|
@ -371,6 +381,7 @@ impl PaneAxis {
|
|||
follower_state: &FollowerStatesByLeader,
|
||||
active_call: Option<&ModelHandle<ActiveCall>>,
|
||||
active_pane: &ViewHandle<Pane>,
|
||||
zoomed: Option<&AnyViewHandle>,
|
||||
app_state: &Arc<AppState>,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> AnyElement<Workspace> {
|
||||
|
@ -388,6 +399,7 @@ impl PaneAxis {
|
|||
follower_state,
|
||||
active_call,
|
||||
active_pane,
|
||||
zoomed,
|
||||
app_state,
|
||||
cx,
|
||||
);
|
||||
|
|
|
@ -11,7 +11,6 @@ use gpui::{platform::WindowBounds, Axis};
|
|||
use util::{unzip_option, ResultExt};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dock::DockPosition;
|
||||
use crate::WorkspaceId;
|
||||
|
||||
use model::{
|
||||
|
@ -19,15 +18,17 @@ use model::{
|
|||
WorkspaceLocation,
|
||||
};
|
||||
|
||||
use self::model::DockStructure;
|
||||
|
||||
define_connection! {
|
||||
// Current schema shape using pseudo-rust syntax:
|
||||
//
|
||||
// workspaces(
|
||||
// workspace_id: usize, // Primary key for workspaces
|
||||
// workspace_location: Bincode<Vec<PathBuf>>,
|
||||
// dock_visible: bool,
|
||||
// dock_anchor: DockAnchor, // 'Bottom' / 'Right' / 'Expanded'
|
||||
// dock_pane: Option<usize>, // PaneId
|
||||
// dock_visible: bool, // Deprecated
|
||||
// dock_anchor: DockAnchor, // Deprecated
|
||||
// dock_pane: Option<usize>, // Deprecated
|
||||
// left_sidebar_open: boolean,
|
||||
// timestamp: String, // UTC YYYY-MM-DD HH:MM:SS
|
||||
// window_state: String, // WindowBounds Discriminant
|
||||
|
@ -71,10 +72,10 @@ define_connection! {
|
|||
CREATE TABLE workspaces(
|
||||
workspace_id INTEGER PRIMARY KEY,
|
||||
workspace_location BLOB UNIQUE,
|
||||
dock_visible INTEGER, // Boolean
|
||||
dock_anchor TEXT, // Enum: 'Bottom' / 'Right' / 'Expanded'
|
||||
dock_pane INTEGER, // NULL indicates that we don't have a dock pane yet
|
||||
left_sidebar_open INTEGER, //Boolean
|
||||
dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
|
||||
dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
|
||||
dock_pane INTEGER, // Deprecated. Preserving so users can downgrade Zed.
|
||||
left_sidebar_open INTEGER, // Boolean
|
||||
timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY(dock_pane) REFERENCES panes(pane_id)
|
||||
) STRICT;
|
||||
|
@ -131,6 +132,36 @@ define_connection! {
|
|||
ALTER TABLE workspaces ADD COLUMN window_width REAL;
|
||||
ALTER TABLE workspaces ADD COLUMN window_height REAL;
|
||||
ALTER TABLE workspaces ADD COLUMN display BLOB;
|
||||
),
|
||||
// Drop foreign key constraint from workspaces.dock_pane to panes table.
|
||||
sql!(
|
||||
CREATE TABLE workspaces_2(
|
||||
workspace_id INTEGER PRIMARY KEY,
|
||||
workspace_location BLOB UNIQUE,
|
||||
dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
|
||||
dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
|
||||
dock_pane INTEGER, // Deprecated. Preserving so users can downgrade Zed.
|
||||
left_sidebar_open INTEGER, // Boolean
|
||||
timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
window_state TEXT,
|
||||
window_x REAL,
|
||||
window_y REAL,
|
||||
window_width REAL,
|
||||
window_height REAL,
|
||||
display BLOB
|
||||
) STRICT;
|
||||
INSERT INTO workspaces_2 SELECT * FROM workspaces;
|
||||
DROP TABLE workspaces;
|
||||
ALTER TABLE workspaces_2 RENAME TO workspaces;
|
||||
),
|
||||
// Add panels related information
|
||||
sql!(
|
||||
ALTER TABLE workspaces ADD COLUMN left_dock_visible INTEGER; //bool
|
||||
ALTER TABLE workspaces ADD COLUMN left_dock_active_panel TEXT;
|
||||
ALTER TABLE workspaces ADD COLUMN right_dock_visible INTEGER; //bool
|
||||
ALTER TABLE workspaces ADD COLUMN right_dock_active_panel TEXT;
|
||||
ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool
|
||||
ALTER TABLE workspaces ADD COLUMN bottom_dock_active_panel TEXT;
|
||||
)];
|
||||
}
|
||||
|
||||
|
@ -146,27 +177,29 @@ impl WorkspaceDb {
|
|||
|
||||
// Note that we re-assign the workspace_id here in case it's empty
|
||||
// and we've grabbed the most recent workspace
|
||||
let (workspace_id, workspace_location, left_sidebar_open, dock_position, bounds, display): (
|
||||
let (workspace_id, workspace_location, bounds, display, docks): (
|
||||
WorkspaceId,
|
||||
WorkspaceLocation,
|
||||
bool,
|
||||
DockPosition,
|
||||
Option<WindowBounds>,
|
||||
Option<Uuid>,
|
||||
DockStructure,
|
||||
) = self
|
||||
.select_row_bound(sql! {
|
||||
SELECT
|
||||
workspace_id,
|
||||
workspace_location,
|
||||
left_sidebar_open,
|
||||
dock_visible,
|
||||
dock_anchor,
|
||||
window_state,
|
||||
window_x,
|
||||
window_y,
|
||||
window_width,
|
||||
window_height,
|
||||
display
|
||||
display,
|
||||
left_dock_visible,
|
||||
left_dock_active_panel,
|
||||
right_dock_visible,
|
||||
right_dock_active_panel,
|
||||
bottom_dock_visible,
|
||||
bottom_dock_active_panel
|
||||
FROM workspaces
|
||||
WHERE workspace_location = ?
|
||||
})
|
||||
|
@ -178,18 +211,13 @@ impl WorkspaceDb {
|
|||
Some(SerializedWorkspace {
|
||||
id: workspace_id,
|
||||
location: workspace_location.clone(),
|
||||
dock_pane: self
|
||||
.get_dock_pane(workspace_id)
|
||||
.context("Getting dock pane")
|
||||
.log_err()?,
|
||||
center_group: self
|
||||
.get_center_pane_group(workspace_id)
|
||||
.context("Getting center group")
|
||||
.log_err()?,
|
||||
dock_position,
|
||||
left_sidebar_open,
|
||||
bounds,
|
||||
display,
|
||||
docks,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -200,7 +228,6 @@ impl WorkspaceDb {
|
|||
conn.with_savepoint("update_worktrees", || {
|
||||
// Clear out panes and pane_groups
|
||||
conn.exec_bound(sql!(
|
||||
UPDATE workspaces SET dock_pane = NULL WHERE workspace_id = ?1;
|
||||
DELETE FROM pane_groups WHERE workspace_id = ?1;
|
||||
DELETE FROM panes WHERE workspace_id = ?1;))?(workspace.id)
|
||||
.expect("Clearing old panes");
|
||||
|
@ -215,42 +242,32 @@ impl WorkspaceDb {
|
|||
INSERT INTO workspaces(
|
||||
workspace_id,
|
||||
workspace_location,
|
||||
left_sidebar_open,
|
||||
dock_visible,
|
||||
dock_anchor,
|
||||
left_dock_visible,
|
||||
left_dock_active_panel,
|
||||
right_dock_visible,
|
||||
right_dock_active_panel,
|
||||
bottom_dock_visible,
|
||||
bottom_dock_active_panel,
|
||||
timestamp
|
||||
)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, CURRENT_TIMESTAMP)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT DO
|
||||
UPDATE SET
|
||||
workspace_location = ?2,
|
||||
left_sidebar_open = ?3,
|
||||
dock_visible = ?4,
|
||||
dock_anchor = ?5,
|
||||
left_dock_visible = ?3,
|
||||
left_dock_active_panel = ?4,
|
||||
right_dock_visible = ?5,
|
||||
right_dock_active_panel = ?6,
|
||||
bottom_dock_visible = ?7,
|
||||
bottom_dock_active_panel = ?8,
|
||||
timestamp = CURRENT_TIMESTAMP
|
||||
))?((
|
||||
workspace.id,
|
||||
&workspace.location,
|
||||
workspace.left_sidebar_open,
|
||||
workspace.dock_position,
|
||||
))
|
||||
))?((workspace.id, &workspace.location, workspace.docks))
|
||||
.context("Updating workspace")?;
|
||||
|
||||
// Save center pane group and dock pane
|
||||
// Save center pane group
|
||||
Self::save_pane_group(conn, workspace.id, &workspace.center_group, None)
|
||||
.context("save pane group in save workspace")?;
|
||||
|
||||
let dock_id = Self::save_pane(conn, workspace.id, &workspace.dock_pane, None, true)
|
||||
.context("save pane in save workspace")?;
|
||||
|
||||
// Complete workspace initialization
|
||||
conn.exec_bound(sql!(
|
||||
UPDATE workspaces
|
||||
SET dock_pane = ?
|
||||
WHERE workspace_id = ?
|
||||
))?((dock_id, workspace.id))
|
||||
.context("Finishing initialization with dock pane")?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.log_err();
|
||||
|
@ -402,32 +419,17 @@ impl WorkspaceDb {
|
|||
Ok(())
|
||||
}
|
||||
SerializedPaneGroup::Pane(pane) => {
|
||||
Self::save_pane(conn, workspace_id, &pane, parent, false)?;
|
||||
Self::save_pane(conn, workspace_id, &pane, parent)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_dock_pane(&self, workspace_id: WorkspaceId) -> Result<SerializedPane> {
|
||||
let (pane_id, active) = self.select_row_bound(sql!(
|
||||
SELECT pane_id, active
|
||||
FROM panes
|
||||
WHERE pane_id = (SELECT dock_pane FROM workspaces WHERE workspace_id = ?)
|
||||
))?(workspace_id)?
|
||||
.context("No dock pane for workspace")?;
|
||||
|
||||
Ok(SerializedPane::new(
|
||||
self.get_items(pane_id).context("Reading items")?,
|
||||
active,
|
||||
))
|
||||
}
|
||||
|
||||
fn save_pane(
|
||||
conn: &Connection,
|
||||
workspace_id: WorkspaceId,
|
||||
pane: &SerializedPane,
|
||||
parent: Option<(GroupId, usize)>, // None indicates BOTH dock pane AND center_pane
|
||||
dock: bool,
|
||||
parent: Option<(GroupId, usize)>,
|
||||
) -> Result<PaneId> {
|
||||
let pane_id = conn.select_row_bound::<_, i64>(sql!(
|
||||
INSERT INTO panes(workspace_id, active)
|
||||
|
@ -436,13 +438,11 @@ impl WorkspaceDb {
|
|||
))?((workspace_id, pane.active))?
|
||||
.ok_or_else(|| anyhow!("Could not retrieve inserted pane_id"))?;
|
||||
|
||||
if !dock {
|
||||
let (parent_id, order) = unzip_option(parent);
|
||||
conn.exec_bound(sql!(
|
||||
INSERT INTO center_panes(pane_id, parent_group_id, position)
|
||||
VALUES (?, ?, ?)
|
||||
))?((pane_id, parent_id, order))?;
|
||||
}
|
||||
let (parent_id, order) = unzip_option(parent);
|
||||
conn.exec_bound(sql!(
|
||||
INSERT INTO center_panes(pane_id, parent_group_id, position)
|
||||
VALUES (?, ?, ?)
|
||||
))?((pane_id, parent_id, order))?;
|
||||
|
||||
Self::save_items(conn, workspace_id, pane_id, &pane.children).context("Saving items")?;
|
||||
|
||||
|
@ -498,9 +498,7 @@ impl WorkspaceDb {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::DockAnchor;
|
||||
use db::open_test_db;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_next_id_stability() {
|
||||
|
@ -575,23 +573,19 @@ mod tests {
|
|||
let mut workspace_1 = SerializedWorkspace {
|
||||
id: 1,
|
||||
location: (["/tmp", "/tmp2"]).into(),
|
||||
dock_position: crate::dock::DockPosition::Shown(DockAnchor::Bottom),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
left_sidebar_open: true,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
};
|
||||
|
||||
let mut workspace_2 = SerializedWorkspace {
|
||||
let workspace_2 = SerializedWorkspace {
|
||||
id: 2,
|
||||
location: (["/tmp"]).into(),
|
||||
dock_position: crate::dock::DockPosition::Hidden(DockAnchor::Expanded),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
left_sidebar_open: false,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
};
|
||||
|
||||
db.save_workspace(workspace_1.clone()).await;
|
||||
|
@ -615,12 +609,6 @@ mod tests {
|
|||
workspace_1.location = (["/tmp", "/tmp3"]).into();
|
||||
db.save_workspace(workspace_1.clone()).await;
|
||||
db.save_workspace(workspace_1).await;
|
||||
|
||||
workspace_2.dock_pane.children.push(SerializedItem {
|
||||
kind: Arc::from("Test"),
|
||||
item_id: 10,
|
||||
active: true,
|
||||
});
|
||||
db.save_workspace(workspace_2).await;
|
||||
|
||||
let test_text_2 = db
|
||||
|
@ -644,16 +632,6 @@ mod tests {
|
|||
|
||||
let db = WorkspaceDb(open_test_db("test_full_workspace_serialization").await);
|
||||
|
||||
let dock_pane = crate::persistence::model::SerializedPane {
|
||||
children: vec![
|
||||
SerializedItem::new("Terminal", 1, false),
|
||||
SerializedItem::new("Terminal", 2, false),
|
||||
SerializedItem::new("Terminal", 3, true),
|
||||
SerializedItem::new("Terminal", 4, false),
|
||||
],
|
||||
active: false,
|
||||
};
|
||||
|
||||
// -----------------
|
||||
// | 1,2 | 5,6 |
|
||||
// | - - - | |
|
||||
|
@ -694,12 +672,10 @@ mod tests {
|
|||
let workspace = SerializedWorkspace {
|
||||
id: 5,
|
||||
location: (["/tmp", "/tmp2"]).into(),
|
||||
dock_position: DockPosition::Shown(DockAnchor::Bottom),
|
||||
center_group,
|
||||
dock_pane,
|
||||
left_sidebar_open: true,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
};
|
||||
|
||||
db.save_workspace(workspace.clone()).await;
|
||||
|
@ -724,23 +700,19 @@ mod tests {
|
|||
let workspace_1 = SerializedWorkspace {
|
||||
id: 1,
|
||||
location: (["/tmp", "/tmp2"]).into(),
|
||||
dock_position: crate::dock::DockPosition::Shown(DockAnchor::Bottom),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
left_sidebar_open: true,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
};
|
||||
|
||||
let mut workspace_2 = SerializedWorkspace {
|
||||
id: 2,
|
||||
location: (["/tmp"]).into(),
|
||||
dock_position: crate::dock::DockPosition::Hidden(DockAnchor::Expanded),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
left_sidebar_open: false,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
};
|
||||
|
||||
db.save_workspace(workspace_1.clone()).await;
|
||||
|
@ -773,12 +745,10 @@ mod tests {
|
|||
let mut workspace_3 = SerializedWorkspace {
|
||||
id: 3,
|
||||
location: (&["/tmp", "/tmp2"]).into(),
|
||||
dock_position: DockPosition::Shown(DockAnchor::Right),
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
left_sidebar_open: false,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
};
|
||||
|
||||
db.save_workspace(workspace_3.clone()).await;
|
||||
|
@ -798,52 +768,23 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
use crate::dock::DockPosition;
|
||||
use crate::persistence::model::SerializedWorkspace;
|
||||
use crate::persistence::model::{SerializedItem, SerializedPane, SerializedPaneGroup};
|
||||
|
||||
fn default_workspace<P: AsRef<Path>>(
|
||||
workspace_id: &[P],
|
||||
dock_pane: SerializedPane,
|
||||
center_group: &SerializedPaneGroup,
|
||||
) -> SerializedWorkspace {
|
||||
SerializedWorkspace {
|
||||
id: 4,
|
||||
location: workspace_id.into(),
|
||||
dock_position: crate::dock::DockPosition::Hidden(DockAnchor::Right),
|
||||
center_group: center_group.clone(),
|
||||
dock_pane,
|
||||
left_sidebar_open: true,
|
||||
bounds: Default::default(),
|
||||
display: Default::default(),
|
||||
docks: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_basic_dock_pane() {
|
||||
env_logger::try_init().ok();
|
||||
|
||||
let db = WorkspaceDb(open_test_db("basic_dock_pane").await);
|
||||
|
||||
let dock_pane = crate::persistence::model::SerializedPane::new(
|
||||
vec![
|
||||
SerializedItem::new("Terminal", 1, false),
|
||||
SerializedItem::new("Terminal", 4, false),
|
||||
SerializedItem::new("Terminal", 2, false),
|
||||
SerializedItem::new("Terminal", 3, true),
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
let workspace = default_workspace(&["/tmp"], dock_pane, &Default::default());
|
||||
|
||||
db.save_workspace(workspace.clone()).await;
|
||||
|
||||
let new_workspace = db.workspace_for_roots(&["/tmp"]).unwrap();
|
||||
|
||||
assert_eq!(workspace.dock_pane, new_workspace.dock_pane);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_simple_split() {
|
||||
env_logger::try_init().ok();
|
||||
|
@ -887,7 +828,7 @@ mod tests {
|
|||
],
|
||||
};
|
||||
|
||||
let workspace = default_workspace(&["/tmp"], Default::default(), ¢er_pane);
|
||||
let workspace = default_workspace(&["/tmp"], ¢er_pane);
|
||||
|
||||
db.save_workspace(workspace.clone()).await;
|
||||
|
||||
|
@ -936,7 +877,7 @@ mod tests {
|
|||
|
||||
let id = &["/tmp"];
|
||||
|
||||
let mut workspace = default_workspace(id, Default::default(), ¢er_pane);
|
||||
let mut workspace = default_workspace(id, ¢er_pane);
|
||||
|
||||
db.save_workspace(workspace.clone()).await;
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use crate::{
|
||||
dock::DockPosition, item::ItemHandle, DockAnchor, ItemDeserializers, Member, Pane, PaneAxis,
|
||||
Workspace, WorkspaceId,
|
||||
};
|
||||
use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_recursion::async_recursion;
|
||||
use db::sqlez::{
|
||||
|
@ -62,12 +59,68 @@ impl Column for WorkspaceLocation {
|
|||
pub struct SerializedWorkspace {
|
||||
pub id: WorkspaceId,
|
||||
pub location: WorkspaceLocation,
|
||||
pub dock_position: DockPosition,
|
||||
pub center_group: SerializedPaneGroup,
|
||||
pub dock_pane: SerializedPane,
|
||||
pub left_sidebar_open: bool,
|
||||
pub bounds: Option<WindowBounds>,
|
||||
pub display: Option<Uuid>,
|
||||
pub docks: DockStructure,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub struct DockStructure {
|
||||
pub(crate) left: DockData,
|
||||
pub(crate) right: DockData,
|
||||
pub(crate) bottom: DockData,
|
||||
}
|
||||
|
||||
impl Column for DockStructure {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let (left, next_index) = DockData::column(statement, start_index)?;
|
||||
let (right, next_index) = DockData::column(statement, next_index)?;
|
||||
let (bottom, next_index) = DockData::column(statement, next_index)?;
|
||||
Ok((
|
||||
DockStructure {
|
||||
left,
|
||||
right,
|
||||
bottom,
|
||||
},
|
||||
next_index,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Bind for DockStructure {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let next_index = statement.bind(&self.left, start_index)?;
|
||||
let next_index = statement.bind(&self.right, next_index)?;
|
||||
statement.bind(&self.bottom, next_index)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub struct DockData {
|
||||
pub(crate) visible: bool,
|
||||
pub(crate) active_panel: Option<String>,
|
||||
}
|
||||
|
||||
impl Column for DockData {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
|
||||
let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
|
||||
Ok((
|
||||
DockData {
|
||||
visible: visible.unwrap_or(false),
|
||||
active_panel,
|
||||
},
|
||||
next_index,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Bind for DockData {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let next_index = statement.bind(&self.visible, start_index)?;
|
||||
statement.bind(&self.active_panel, next_index)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
|
@ -266,9 +319,9 @@ impl StaticColumnCount for SerializedItem {
|
|||
}
|
||||
impl Bind for &SerializedItem {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let next_index = statement.bind(self.kind.clone(), start_index)?;
|
||||
let next_index = statement.bind(self.item_id, next_index)?;
|
||||
statement.bind(self.active, next_index)
|
||||
let next_index = statement.bind(&self.kind, start_index)?;
|
||||
let next_index = statement.bind(&self.item_id, next_index)?;
|
||||
statement.bind(&self.active, next_index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,64 +340,3 @@ impl Column for SerializedItem {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticColumnCount for DockPosition {
|
||||
fn column_count() -> usize {
|
||||
2
|
||||
}
|
||||
}
|
||||
impl Bind for DockPosition {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let next_index = statement.bind(self.is_visible(), start_index)?;
|
||||
statement.bind(self.anchor(), next_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Column for DockPosition {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let (visible, next_index) = bool::column(statement, start_index)?;
|
||||
let (dock_anchor, next_index) = DockAnchor::column(statement, next_index)?;
|
||||
let position = if visible {
|
||||
DockPosition::Shown(dock_anchor)
|
||||
} else {
|
||||
DockPosition::Hidden(dock_anchor)
|
||||
};
|
||||
Ok((position, next_index))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::WorkspaceLocation;
|
||||
use crate::DockAnchor;
|
||||
use db::sqlez::connection::Connection;
|
||||
|
||||
#[test]
|
||||
fn test_workspace_round_trips() {
|
||||
let db = Connection::open_memory(Some("workspace_id_round_trips"));
|
||||
|
||||
db.exec(indoc::indoc! {"
|
||||
CREATE TABLE workspace_id_test(
|
||||
workspace_id INTEGER,
|
||||
dock_anchor TEXT
|
||||
);"})
|
||||
.unwrap()()
|
||||
.unwrap();
|
||||
|
||||
let workspace_id: WorkspaceLocation = WorkspaceLocation::from(&["\test2", "\test1"]);
|
||||
|
||||
db.exec_bound("INSERT INTO workspace_id_test(workspace_id, dock_anchor) VALUES (?,?)")
|
||||
.unwrap()((&workspace_id, DockAnchor::Bottom))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
db.select_row("SELECT workspace_id, dock_anchor FROM workspace_id_test LIMIT 1")
|
||||
.unwrap()()
|
||||
.unwrap(),
|
||||
Some((
|
||||
WorkspaceLocation::from(&["\test1", "\test2"]),
|
||||
DockAnchor::Bottom
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,321 +0,0 @@
|
|||
use crate::{StatusItemView, Workspace};
|
||||
use gpui::{
|
||||
elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle,
|
||||
AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait SidebarItem: View {
|
||||
fn should_activate_item_on_event(&self, _: &Self::Event, _: &AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
fn should_show_badge(&self, _: &AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
fn contains_focused_view(&self, _: &AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SidebarItemHandle {
|
||||
fn id(&self) -> usize;
|
||||
fn should_show_badge(&self, cx: &WindowContext) -> bool;
|
||||
fn is_focused(&self, cx: &WindowContext) -> bool;
|
||||
fn as_any(&self) -> &AnyViewHandle;
|
||||
}
|
||||
|
||||
impl<T> SidebarItemHandle for ViewHandle<T>
|
||||
where
|
||||
T: SidebarItem,
|
||||
{
|
||||
fn id(&self) -> usize {
|
||||
self.id()
|
||||
}
|
||||
|
||||
fn should_show_badge(&self, cx: &WindowContext) -> bool {
|
||||
self.read(cx).should_show_badge(cx)
|
||||
}
|
||||
|
||||
fn is_focused(&self, cx: &WindowContext) -> bool {
|
||||
ViewHandle::is_focused(self, cx) || self.read(cx).contains_focused_view(cx)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &AnyViewHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dyn SidebarItemHandle> for AnyViewHandle {
|
||||
fn from(val: &dyn SidebarItemHandle) -> Self {
|
||||
val.as_any().clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sidebar {
|
||||
sidebar_side: SidebarSide,
|
||||
items: Vec<Item>,
|
||||
is_open: bool,
|
||||
active_item_ix: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||
pub enum SidebarSide {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl SidebarSide {
|
||||
fn to_resizable_side(self) -> Side {
|
||||
match self {
|
||||
Self::Left => Side::Right,
|
||||
Self::Right => Side::Left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Item {
|
||||
icon_path: &'static str,
|
||||
tooltip: String,
|
||||
view: Rc<dyn SidebarItemHandle>,
|
||||
_subscriptions: [Subscription; 2],
|
||||
}
|
||||
|
||||
pub struct SidebarButtons {
|
||||
sidebar: ViewHandle<Sidebar>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
pub struct ToggleSidebarItem {
|
||||
pub sidebar_side: SidebarSide,
|
||||
pub item_index: usize,
|
||||
}
|
||||
|
||||
impl_actions!(workspace, [ToggleSidebarItem]);
|
||||
|
||||
impl Sidebar {
|
||||
pub fn new(sidebar_side: SidebarSide) -> Self {
|
||||
Self {
|
||||
sidebar_side,
|
||||
items: Default::default(),
|
||||
active_item_ix: 0,
|
||||
is_open: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.is_open
|
||||
}
|
||||
|
||||
pub fn active_item_ix(&self) -> usize {
|
||||
self.active_item_ix
|
||||
}
|
||||
|
||||
pub fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
|
||||
if open != self.is_open {
|
||||
self.is_open = open;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_open(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if self.is_open {}
|
||||
self.is_open = !self.is_open;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn add_item<T: SidebarItem>(
|
||||
&mut self,
|
||||
icon_path: &'static str,
|
||||
tooltip: String,
|
||||
view: ViewHandle<T>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let subscriptions = [
|
||||
cx.observe(&view, |_, _, cx| cx.notify()),
|
||||
cx.subscribe(&view, |this, view, event, cx| {
|
||||
if view.read(cx).should_activate_item_on_event(event, cx) {
|
||||
if let Some(ix) = this
|
||||
.items
|
||||
.iter()
|
||||
.position(|item| item.view.id() == view.id())
|
||||
{
|
||||
this.activate_item(ix, cx);
|
||||
}
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
self.items.push(Item {
|
||||
icon_path,
|
||||
tooltip,
|
||||
view: Rc::new(view),
|
||||
_subscriptions: subscriptions,
|
||||
});
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
pub fn activate_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
|
||||
self.active_item_ix = item_ix;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn toggle_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
|
||||
if self.active_item_ix == item_ix {
|
||||
self.is_open = false;
|
||||
} else {
|
||||
self.active_item_ix = item_ix;
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn active_item(&self) -> Option<&Rc<dyn SidebarItemHandle>> {
|
||||
if self.is_open {
|
||||
self.items.get(self.active_item_ix).map(|item| &item.view)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Sidebar {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for Sidebar {
|
||||
fn ui_name() -> &'static str {
|
||||
"Sidebar"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
if let Some(active_item) = self.active_item() {
|
||||
enum ResizeHandleTag {}
|
||||
let style = &theme::current(cx).workspace.sidebar;
|
||||
ChildView::new(active_item.as_any(), cx)
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.with_resize_handle::<ResizeHandleTag>(
|
||||
self.sidebar_side as usize,
|
||||
self.sidebar_side.to_resizable_side(),
|
||||
4.,
|
||||
style.initial_size,
|
||||
cx,
|
||||
)
|
||||
.into_any()
|
||||
} else {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SidebarButtons {
|
||||
pub fn new(
|
||||
sidebar: ViewHandle<Sidebar>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
cx.observe(&sidebar, |_, _, cx| cx.notify()).detach();
|
||||
Self { sidebar, workspace }
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for SidebarButtons {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for SidebarButtons {
|
||||
fn ui_name() -> &'static str {
|
||||
"SidebarToggleButton"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
let theme = &theme::current(cx);
|
||||
let tooltip_style = theme.tooltip.clone();
|
||||
let theme = &theme.workspace.status_bar.sidebar_buttons;
|
||||
let sidebar = self.sidebar.read(cx);
|
||||
let item_style = theme.item.clone();
|
||||
let badge_style = theme.badge;
|
||||
let active_ix = sidebar.active_item_ix;
|
||||
let is_open = sidebar.is_open;
|
||||
let sidebar_side = sidebar.sidebar_side;
|
||||
let group_style = match sidebar_side {
|
||||
SidebarSide::Left => theme.group_left,
|
||||
SidebarSide::Right => theme.group_right,
|
||||
};
|
||||
|
||||
#[allow(clippy::needless_collect)]
|
||||
let items = sidebar
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| (item.icon_path, item.tooltip.clone(), item.view.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Flex::row()
|
||||
.with_children(items.into_iter().enumerate().map(
|
||||
|(ix, (icon_path, tooltip, item_view))| {
|
||||
let action = ToggleSidebarItem {
|
||||
sidebar_side,
|
||||
item_index: ix,
|
||||
};
|
||||
MouseEventHandler::<Self, _>::new(ix, cx, |state, cx| {
|
||||
let is_active = is_open && ix == active_ix;
|
||||
let style = item_style.style_for(state, is_active);
|
||||
Stack::new()
|
||||
.with_child(Svg::new(icon_path).with_color(style.icon_color))
|
||||
.with_children(if !is_active && item_view.should_show_badge(cx) {
|
||||
Some(
|
||||
Empty::new()
|
||||
.collapsed()
|
||||
.contained()
|
||||
.with_style(badge_style)
|
||||
.aligned()
|
||||
.bottom()
|
||||
.right(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.constrained()
|
||||
.with_width(style.icon_size)
|
||||
.with_height(style.icon_size)
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, {
|
||||
let action = action.clone();
|
||||
move |_, this, cx| {
|
||||
if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
let action = action.clone();
|
||||
cx.window_context().defer(move |cx| {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.toggle_sidebar_item(&action, cx)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.with_tooltip::<Self>(
|
||||
ix,
|
||||
tooltip,
|
||||
Some(Box::new(action)),
|
||||
tooltip_style.clone(),
|
||||
cx,
|
||||
)
|
||||
},
|
||||
))
|
||||
.contained()
|
||||
.with_style(group_style)
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusItemView for SidebarButtons {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
_: Option<&dyn crate::ItemHandle>,
|
||||
_: &mut ViewContext<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,3 @@
|
|||
use anyhow::bail;
|
||||
use db::sqlez::{
|
||||
bindable::{Bind, Column, StaticColumnCount},
|
||||
statement::Statement,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
|
@ -13,17 +8,15 @@ pub struct WorkspaceSettings {
|
|||
pub confirm_quit: bool,
|
||||
pub show_call_status_icon: bool,
|
||||
pub autosave: AutosaveSetting,
|
||||
pub default_dock_anchor: DockAnchor,
|
||||
pub git: GitSettings,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct WorkspaceSettingsContent {
|
||||
pub active_pane_magnification: Option<f32>,
|
||||
pub confirm_quit: Option<bool>,
|
||||
pub show_call_status_icon: Option<bool>,
|
||||
pub autosave: Option<AutosaveSetting>,
|
||||
pub default_dock_anchor: Option<DockAnchor>,
|
||||
pub git: Option<GitSettings>,
|
||||
}
|
||||
|
||||
|
@ -36,15 +29,6 @@ pub enum AutosaveSetting {
|
|||
OnWindowChange,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DockAnchor {
|
||||
#[default]
|
||||
Bottom,
|
||||
Right,
|
||||
Expanded,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GitSettings {
|
||||
pub git_gutter: Option<GitGutterSetting>,
|
||||
|
@ -59,35 +43,6 @@ pub enum GitGutterSetting {
|
|||
Hide,
|
||||
}
|
||||
|
||||
impl StaticColumnCount for DockAnchor {}
|
||||
|
||||
impl Bind for DockAnchor {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
|
||||
match self {
|
||||
DockAnchor::Bottom => "Bottom",
|
||||
DockAnchor::Right => "Right",
|
||||
DockAnchor::Expanded => "Expanded",
|
||||
}
|
||||
.bind(statement, start_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Column for DockAnchor {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
|
||||
String::column(statement, start_index).and_then(|(anchor_text, next_index)| {
|
||||
Ok((
|
||||
match anchor_text.as_ref() {
|
||||
"Bottom" => DockAnchor::Bottom,
|
||||
"Right" => DockAnchor::Right,
|
||||
"Expanded" => DockAnchor::Expanded,
|
||||
_ => bail!("Stored dock anchor is incorrect"),
|
||||
},
|
||||
next_index,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Setting for WorkspaceSettings {
|
||||
const KEY: Option<&'static str> = None;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue