@@ -138,6 +138,7 @@ define_connection! {
// window_height: Option<f32>, // WindowBounds::Fixed RectF height
// display: Option<Uuid>, // Display id
// fullscreen: Option<bool>, // Is the window fullscreen?
+ // centered_layout: Option<bool>, // Is the Centered Layout mode activated?
// )
//
// pane_groups(
@@ -280,6 +281,10 @@ define_connection! {
sql!(
ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; //bool
),
+ // Add centered_layout field to workspace
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN centered_layout INTEGER; //bool
+ ),
// Add preview field to items
sql!(
ALTER TABLE items ADD COLUMN preview INTEGER; //bool
@@ -299,12 +304,13 @@ 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, bounds, display, fullscreen, docks): (
+ let (workspace_id, workspace_location, bounds, display, fullscreen, centered_layout, docks): (
WorkspaceId,
WorkspaceLocation,
Option<SerializedWindowsBounds>,
Option<Uuid>,
Option<bool>,
+ Option<bool>,
DockStructure,
) = self
.select_row_bound(sql! {
@@ -318,6 +324,7 @@ impl WorkspaceDb {
window_height,
display,
fullscreen,
+ centered_layout,
left_dock_visible,
left_dock_active_panel,
left_dock_zoom,
@@ -344,6 +351,7 @@ impl WorkspaceDb {
.log_err()?,
bounds: bounds.map(|bounds| bounds.0),
fullscreen: fullscreen.unwrap_or(false),
+ centered_layout: centered_layout.unwrap_or(false),
display,
docks,
})
@@ -678,6 +686,14 @@ impl WorkspaceDb {
WHERE workspace_id = ?1
}
}
+
+ query! {
+ pub(crate) async fn set_centered_layout(workspace_id: WorkspaceId, centered_layout: bool) -> Result<()> {
+ UPDATE workspaces
+ SET centered_layout = ?2
+ WHERE workspace_id = ?1
+ }
+ }
}
#[cfg(test)]
@@ -764,6 +780,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
};
let workspace_2 = SerializedWorkspace {
@@ -774,6 +791,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
};
db.save_workspace(workspace_1.clone()).await;
@@ -873,6 +891,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
};
db.save_workspace(workspace.clone()).await;
@@ -902,6 +921,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
};
let mut workspace_2 = SerializedWorkspace {
@@ -912,6 +932,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
};
db.save_workspace(workspace_1.clone()).await;
@@ -949,6 +970,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
};
db.save_workspace(workspace_3.clone()).await;
@@ -983,6 +1005,7 @@ mod tests {
display: Default::default(),
docks: Default::default(),
fullscreen: false,
+ centered_layout: false,
}
}
@@ -26,7 +26,7 @@ use futures::{
Future, FutureExt, StreamExt,
};
use gpui::{
- actions, canvas, impl_actions, point, size, Action, AnyElement, AnyView, AnyWeakView,
+ actions, canvas, impl_actions, point, relative, size, Action, AnyElement, AnyView, AnyWeakView,
AppContext, AsyncAppContext, AsyncWindowContext, Bounds, DevicePixels, DragMoveEvent,
Entity as _, EntityId, EventEmitter, FocusHandle, FocusableView, Global, KeyContext, Keystroke,
LayoutId, ManagedView, Model, ModelContext, PathPromptOptions, Point, PromptLevel, Render,
@@ -78,9 +78,9 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
pub use ui;
use ui::{
- div, Context as _, Div, Element, ElementContext, FluentBuilder as _, InteractiveElement as _,
- IntoElement, Label, ParentElement as _, Pixels, SharedString, Styled as _, ViewContext,
- VisualContext as _, WindowContext,
+ div, h_flex, Context as _, Div, Element, ElementContext, FluentBuilder,
+ InteractiveElement as _, IntoElement, Label, ParentElement as _, Pixels, SharedString,
+ Styled as _, ViewContext, VisualContext as _, WindowContext,
};
use util::ResultExt;
use uuid::Uuid;
@@ -133,6 +133,7 @@ actions!(
ToggleLeftDock,
ToggleRightDock,
ToggleBottomDock,
+ ToggleCenteredLayout,
CloseAllDocks,
]
);
@@ -581,6 +582,7 @@ pub struct Workspace {
_schedule_serialize: Option<Task<()>>,
pane_history_timestamp: Arc<AtomicUsize>,
bounds: Bounds<Pixels>,
+ centered_layout: bool,
bounds_save_task_queued: Option<Task<()>>,
}
@@ -600,6 +602,9 @@ struct FollowerState {
}
impl Workspace {
+ const DEFAULT_PADDING: f32 = 0.2;
+ const MAX_PADDING: f32 = 0.4;
+
pub fn new(
workspace_id: WorkspaceId,
project: Model<Project>,
@@ -867,6 +872,7 @@ impl Workspace {
workspace_actions: Default::default(),
// This data will be incorrect, but it will be overwritten by the time it needs to be used.
bounds: Default::default(),
+ centered_layout: false,
bounds_save_task_queued: None,
}
}
@@ -956,12 +962,19 @@ impl Workspace {
let mut options = cx.update(|cx| (app_state.build_window_options)(display, cx))?;
options.bounds = bounds;
options.fullscreen = fullscreen;
+ let centered_layout = serialized_workspace
+ .as_ref()
+ .map(|w| w.centered_layout)
+ .unwrap_or(false);
cx.open_window(options, {
let app_state = app_state.clone();
let project_handle = project_handle.clone();
move |cx| {
cx.new_view(|cx| {
- Workspace::new(workspace_id, project_handle, app_state, cx)
+ let mut workspace =
+ Workspace::new(workspace_id, project_handle, app_state, cx);
+ workspace.centered_layout = centered_layout;
+ workspace
})
}
})?
@@ -3541,6 +3554,7 @@ impl Workspace {
display: Default::default(),
docks,
fullscreen: cx.is_fullscreen(),
+ centered_layout: self.centered_layout,
};
return cx.spawn(|_| persistence::DB.save_workspace(serialized_workspace));
}
@@ -3704,6 +3718,7 @@ impl Workspace {
workspace.reopen_closed_item(cx).detach();
}),
)
+ .on_action(cx.listener(Workspace::toggle_centered_layout))
}
#[cfg(any(test, feature = "test-support"))]
@@ -3772,6 +3787,21 @@ impl Workspace {
self.modal_layer
.update(cx, |modal_layer, cx| modal_layer.toggle_modal(cx, build))
}
+
+ pub fn toggle_centered_layout(&mut self, _: &ToggleCenteredLayout, cx: &mut ViewContext<Self>) {
+ self.centered_layout = !self.centered_layout;
+ cx.background_executor()
+ .spawn(DB.set_centered_layout(self.database_id, self.centered_layout))
+ .detach_and_log_err(cx);
+ cx.notify();
+ }
+
+ fn adjust_padding(padding: Option<f32>) -> f32 {
+ padding
+ .unwrap_or(Self::DEFAULT_PADDING)
+ .min(Self::MAX_PADDING)
+ .max(0.0)
+ }
}
fn window_bounds_env_override() -> Option<Bounds<DevicePixels>> {
@@ -3916,7 +3946,27 @@ impl Render for Workspace {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let mut context = KeyContext::default();
context.add("Workspace");
-
+ let centered_layout = self.centered_layout
+ && self.center.panes().len() == 1
+ && self.active_item(cx).is_some();
+ let render_padding = |size| {
+ (size > 0.0).then(|| {
+ div()
+ .h_full()
+ .w(relative(size))
+ .bg(cx.theme().colors().editor_background)
+ .border_color(cx.theme().colors().pane_group_border)
+ })
+ };
+ let paddings = if centered_layout {
+ let settings = WorkspaceSettings::get_global(cx).centered_layout;
+ (
+ render_padding(Self::adjust_padding(settings.left_padding)),
+ render_padding(Self::adjust_padding(settings.right_padding)),
+ )
+ } else {
+ (None, None)
+ };
let (ui_font, ui_font_size) = {
let theme_settings = ThemeSettings::get_global(cx);
(
@@ -4009,15 +4059,25 @@ impl Render for Workspace {
.flex_col()
.flex_1()
.overflow_hidden()
- .child(self.center.render(
- &self.project,
- &self.follower_states,
- self.active_call(),
- &self.active_pane,
- self.zoomed.as_ref(),
- &self.app_state,
- cx,
- ))
+ .child(
+ h_flex()
+ .h_full()
+ .when_some(paddings.0, |this, p| {
+ this.child(p.border_r_1())
+ })
+ .child(self.center.render(
+ &self.project,
+ &self.follower_states,
+ self.active_call(),
+ &self.active_pane,
+ self.zoomed.as_ref(),
+ &self.app_state,
+ cx,
+ ))
+ .when_some(paddings.1, |this, p| {
+ this.child(p.border_l_1())
+ }),
+ )
.children(
self.zoomed_position
.ne(&Some(DockPosition::Bottom))