Vertically align root folder in project panel with tabs and sidebar icons
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
parent
177306d494
commit
5fb112ac4c
@@ -43,6 +43,8 @@ where
|
||||
state: UniformListState,
|
||||
item_count: usize,
|
||||
append_items: F,
|
||||
padding_top: f32,
|
||||
padding_bottom: f32,
|
||||
}
|
||||
|
||||
impl<F> UniformList<F>
|
||||
@@ -54,9 +56,21 @@ where
|
||||
state,
|
||||
item_count,
|
||||
append_items,
|
||||
padding_top: 0.,
|
||||
padding_bottom: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_padding_top(mut self, padding: f32) -> Self {
|
||||
self.padding_top = padding;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_padding_bottom(mut self, padding: f32) -> Self {
|
||||
self.padding_bottom = padding;
|
||||
self
|
||||
}
|
||||
|
||||
fn scroll(
|
||||
&self,
|
||||
_: Vector2F,
|
||||
@@ -84,7 +98,7 @@ where
|
||||
}
|
||||
|
||||
if let Some(item_ix) = state.scroll_to.take() {
|
||||
let item_top = item_ix as f32 * item_height;
|
||||
let item_top = self.padding_top + item_ix as f32 * item_height;
|
||||
let item_bottom = item_top + item_height;
|
||||
|
||||
if item_top < state.scroll_top {
|
||||
@@ -137,11 +151,16 @@ where
|
||||
size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
|
||||
}
|
||||
|
||||
scroll_max = (item_height * self.item_count as f32 - size.y()).max(0.);
|
||||
let scroll_height =
|
||||
item_height * self.item_count as f32 + self.padding_top + self.padding_bottom;
|
||||
scroll_max = (scroll_height - size.y()).max(0.);
|
||||
self.autoscroll(scroll_max, size.y(), item_height);
|
||||
|
||||
items.clear();
|
||||
let start = cmp::min((self.scroll_top() / item_height) as usize, self.item_count);
|
||||
let start = cmp::min(
|
||||
((self.scroll_top() - self.padding_top) / item_height) as usize,
|
||||
self.item_count,
|
||||
);
|
||||
let end = cmp::min(
|
||||
self.item_count,
|
||||
start + (size.y() / item_height).ceil() as usize + 1,
|
||||
@@ -173,8 +192,11 @@ where
|
||||
) -> Self::PaintState {
|
||||
cx.scene.push_layer(Some(bounds));
|
||||
|
||||
let mut item_origin =
|
||||
bounds.origin() - vec2f(0.0, self.state.scroll_top() % layout.item_height);
|
||||
let mut item_origin = bounds.origin()
|
||||
- vec2f(
|
||||
0.,
|
||||
(self.state.scroll_top() - self.padding_top) % layout.item_height,
|
||||
);
|
||||
|
||||
for item in &mut layout.items {
|
||||
item.paint(item_origin, visible_bounds, cx);
|
||||
|
||||
@@ -18,6 +18,7 @@ padding = { right = 4 }
|
||||
width = 16
|
||||
|
||||
[workspace.tab]
|
||||
height = 34
|
||||
text = "$text.2"
|
||||
padding = { left = 12, right = 12 }
|
||||
icon_width = 8
|
||||
@@ -26,10 +27,11 @@ icon_close = "$text.2.color"
|
||||
icon_close_active = "$text.0.color"
|
||||
icon_dirty = "$status.info"
|
||||
icon_conflict = "$status.warn"
|
||||
border = { left = true, bottom = true, width = 1, color = "$border.0" }
|
||||
border = { left = true, bottom = true, width = 1, color = "$border.0", overlay = true }
|
||||
|
||||
[workspace.active_tab]
|
||||
extends = "$workspace.tab"
|
||||
border.bottom = false
|
||||
background = "$surface.1"
|
||||
text = "$text.0"
|
||||
|
||||
@@ -41,13 +43,14 @@ border = { right = true, width = 1, color = "$border.0" }
|
||||
padding = { left = 1 }
|
||||
background = "$border.0"
|
||||
|
||||
[workspace.sidebar.icon]
|
||||
color = "$text.2.color"
|
||||
height = 18
|
||||
[workspace.sidebar.item]
|
||||
icon_color = "$text.2.color"
|
||||
icon_size = 18
|
||||
height = "$workspace.tab.height"
|
||||
|
||||
[workspace.sidebar.active_icon]
|
||||
extends = "$workspace.sidebar.icon"
|
||||
color = "$text.0.color"
|
||||
[workspace.sidebar.active_item]
|
||||
extends = "$workspace.sidebar.item"
|
||||
icon_color = "$text.0.color"
|
||||
|
||||
[workspace.left_sidebar]
|
||||
extends = "$workspace.sidebar"
|
||||
@@ -58,7 +61,7 @@ extends = "$workspace.sidebar"
|
||||
border = { width = 1, color = "$border.0", left = true }
|
||||
|
||||
[panel]
|
||||
padding = 12
|
||||
padding = { top = 12, left = 12, bottom = 12, right = 12 }
|
||||
|
||||
[chat_panel]
|
||||
extends = "$panel"
|
||||
@@ -161,12 +164,11 @@ corner_radius = 6
|
||||
|
||||
[project_panel]
|
||||
extends = "$panel"
|
||||
padding = 0
|
||||
entry_base_padding = "$panel.padding"
|
||||
padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
|
||||
|
||||
[project_panel.entry]
|
||||
text = "$text.1"
|
||||
padding = { top = 3, bottom = 3 }
|
||||
height = 22
|
||||
icon_color = "$text.3.color"
|
||||
icon_size = 8
|
||||
icon_spacing = 8
|
||||
|
||||
@@ -507,11 +507,15 @@ impl ProjectPanel {
|
||||
Label::new(details.filename, style.text.clone())
|
||||
.contained()
|
||||
.with_margin_left(style.icon_spacing)
|
||||
.aligned()
|
||||
.left()
|
||||
.boxed(),
|
||||
)
|
||||
.constrained()
|
||||
.with_height(theme.entry.height)
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.with_padding_left(theme.entry_base_padding + details.depth as f32 * 20.)
|
||||
.with_padding_left(theme.container.padding.left + details.depth as f32 * 20.)
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
@@ -534,6 +538,8 @@ impl View for ProjectPanel {
|
||||
|
||||
fn render(&mut self, _: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
|
||||
let settings = self.settings.clone();
|
||||
let mut container_style = settings.borrow().theme.project_panel.container;
|
||||
let padding = std::mem::take(&mut container_style.padding);
|
||||
let handle = self.handle.clone();
|
||||
UniformList::new(
|
||||
self.list.clone(),
|
||||
@@ -551,8 +557,10 @@ impl View for ProjectPanel {
|
||||
})
|
||||
},
|
||||
)
|
||||
.with_padding_top(padding.top)
|
||||
.with_padding_bottom(padding.bottom)
|
||||
.contained()
|
||||
.with_style(self.settings.borrow().theme.project_panel.container)
|
||||
.with_style(container_style)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -67,6 +67,7 @@ pub struct OfflineIcon {
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Tab {
|
||||
pub height: f32,
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
#[serde(flatten)]
|
||||
@@ -84,14 +85,15 @@ pub struct Sidebar {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub width: f32,
|
||||
pub icon: SidebarIcon,
|
||||
pub active_icon: SidebarIcon,
|
||||
pub item: SidebarItem,
|
||||
pub active_item: SidebarItem,
|
||||
pub resize_handle: ContainerStyle,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SidebarIcon {
|
||||
pub color: Color,
|
||||
pub struct SidebarItem {
|
||||
pub icon_color: Color,
|
||||
pub icon_size: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
@@ -107,18 +109,18 @@ pub struct ChatPanel {
|
||||
pub hovered_sign_in_prompt: TextStyle,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ProjectPanel {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub entry_base_padding: f32,
|
||||
pub entry: ProjectPanelEntry,
|
||||
pub hovered_entry: ProjectPanelEntry,
|
||||
pub selected_entry: ProjectPanelEntry,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ProjectPanelEntry {
|
||||
pub height: f32,
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub text: TextStyle,
|
||||
|
||||
@@ -2,12 +2,11 @@ use super::{ItemViewHandle, SplitDirection};
|
||||
use crate::settings::Settings;
|
||||
use gpui::{
|
||||
action,
|
||||
color::Color,
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
keymap::Binding,
|
||||
platform::CursorStyle,
|
||||
Border, Entity, MutableAppContext, Quad, RenderContext, View, ViewContext, ViewHandle,
|
||||
Entity, MutableAppContext, Quad, RenderContext, View, ViewContext, ViewHandle,
|
||||
};
|
||||
use postage::watch;
|
||||
use std::{cmp, path::Path, sync::Arc};
|
||||
@@ -180,10 +179,6 @@ impl Pane {
|
||||
fn render_tabs(&self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let settings = self.settings.borrow();
|
||||
let theme = &settings.theme;
|
||||
let line_height = cx.font_cache().line_height(
|
||||
theme.workspace.tab.label.text.font_id,
|
||||
theme.workspace.tab.label.text.font_size,
|
||||
);
|
||||
|
||||
enum Tabs {}
|
||||
let tabs = MouseEventHandler::new::<Tabs, _, _, _>(0, cx, |mouse_state, cx| {
|
||||
@@ -202,12 +197,11 @@ impl Pane {
|
||||
title.push('…');
|
||||
}
|
||||
|
||||
let mut style = theme.workspace.tab.clone();
|
||||
if is_active {
|
||||
style = theme.workspace.active_tab.clone();
|
||||
style.container.border.bottom = false;
|
||||
style.container.padding.bottom += style.container.border.width;
|
||||
}
|
||||
let mut style = if is_active {
|
||||
theme.workspace.active_tab.clone()
|
||||
} else {
|
||||
theme.workspace.tab.clone()
|
||||
};
|
||||
if ix == 0 {
|
||||
style.container.border.left = false;
|
||||
}
|
||||
@@ -319,26 +313,11 @@ impl Pane {
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure there's always a minimum amount of space after the last tab,
|
||||
// so that the tab's border doesn't abut the window's border.
|
||||
let mut border = Border::bottom(1.0, Color::default());
|
||||
border.color = theme.workspace.tab.container.border.color;
|
||||
|
||||
row.add_child(
|
||||
ConstrainedBox::new(
|
||||
Container::new(Empty::new().boxed())
|
||||
.with_border(border)
|
||||
.boxed(),
|
||||
)
|
||||
.with_min_width(20.)
|
||||
.named("fixed-filler"),
|
||||
);
|
||||
|
||||
row.add_child(
|
||||
Expanded::new(
|
||||
0.0,
|
||||
Container::new(Empty::new().boxed())
|
||||
.with_border(border)
|
||||
.with_border(theme.workspace.tab.container.border)
|
||||
.boxed(),
|
||||
)
|
||||
.named("filler"),
|
||||
@@ -348,7 +327,7 @@ impl Pane {
|
||||
});
|
||||
|
||||
ConstrainedBox::new(tabs.boxed())
|
||||
.with_height(line_height + 16.)
|
||||
.with_height(theme.workspace.tab.height)
|
||||
.named("tabs")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,6 @@ impl Sidebar {
|
||||
|
||||
pub fn render(&self, settings: &Settings, cx: &mut RenderContext<Workspace>) -> ElementBox {
|
||||
let side = self.side;
|
||||
let theme = &settings.theme;
|
||||
let line_height = cx.font_cache().line_height(
|
||||
theme.workspace.tab.label.text.font_id,
|
||||
theme.workspace.tab.label.text.font_size,
|
||||
);
|
||||
let theme = self.theme(settings);
|
||||
|
||||
ConstrainedBox::new(
|
||||
@@ -80,9 +75,9 @@ impl Sidebar {
|
||||
Flex::column()
|
||||
.with_children(self.items.iter().enumerate().map(|(item_index, item)| {
|
||||
let theme = if Some(item_index) == self.active_item_ix {
|
||||
&theme.active_icon
|
||||
&theme.active_item
|
||||
} else {
|
||||
&theme.icon
|
||||
&theme.item
|
||||
};
|
||||
enum SidebarButton {}
|
||||
MouseEventHandler::new::<SidebarButton, _, _, _>(
|
||||
@@ -93,15 +88,15 @@ impl Sidebar {
|
||||
Align::new(
|
||||
ConstrainedBox::new(
|
||||
Svg::new(item.icon_path)
|
||||
.with_color(theme.color)
|
||||
.with_color(theme.icon_color)
|
||||
.boxed(),
|
||||
)
|
||||
.with_height(theme.height)
|
||||
.with_height(theme.icon_size)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_height(line_height + 16.0)
|
||||
.with_height(theme.height)
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user