refactor ui (#17)

* wip: redesign sidebar

* wip: adjust dpi

* update

* update

* refactor modal

* fix modal
This commit is contained in:
reya
2025-04-18 13:43:07 +07:00
committed by GitHub
parent 5c5748a80c
commit a30f2dcc8a
58 changed files with 899 additions and 1167 deletions

View File

@@ -357,7 +357,7 @@ impl RenderOnce for Button {
Size::XSmall => this.h_6().px_0p5(),
Size::Small => this.h_7().px_2(),
Size::Large => this.h_10().px_3(),
_ => this.h_8().px_3(),
_ => this.h_9().px_2(),
}
}
})
@@ -437,7 +437,7 @@ impl RenderOnce for Button {
.id("label")
.items_center()
.justify_center()
.text_xs()
.text_sm()
.map(|this| match self.size {
Size::XSmall => this.gap_0p5(),
Size::Small => this.gap_1(),
@@ -451,7 +451,6 @@ impl RenderOnce for Button {
.when(self.loading, |this| {
this.child(
Indicator::new()
.with_size(self.size)
.when_some(self.loading_icon, |this, icon| this.icon(icon)),
)
})

View File

@@ -407,19 +407,19 @@ impl TabPanel {
let build_popup_menu = move |this, cx: &App| view.read(cx).popup_menu(this, cx);
h_flex()
.gap_2()
.gap_1()
.occlude()
.items_center()
.children(
self.toolbar_buttons(window, cx)
.into_iter()
.map(|btn| btn.xsmall().ghost()),
.map(|btn| btn.small().ghost()),
)
.when(self.is_zoomed, |this| {
this.child(
Button::new("zoom")
.icon(IconName::Minimize)
.xsmall()
.icon(IconName::ArrowIn)
.small()
.ghost()
.tooltip("Zoom Out")
.on_click(cx.listener(|view, _, window, cx| {
@@ -430,7 +430,7 @@ impl TabPanel {
.child(
Button::new("menu")
.icon(IconName::Ellipsis)
.xsmall()
.small()
.ghost()
.popup_menu({
let zoomable = state.zoomable;
@@ -451,7 +451,7 @@ impl TabPanel {
)
}
fn render_dock_toggle_button(
fn _render_dock_toggle_button(
&self,
placement: DockPlacement,
_window: &mut Window,
@@ -517,7 +517,7 @@ impl TabPanel {
Some(
Button::new(SharedString::from(format!("toggle-dock:{:?}", placement)))
.icon(icon)
.xsmall()
.small()
.ghost()
.tooltip(match is_open {
true => "Collapse",
@@ -547,9 +547,6 @@ impl TabPanel {
};
let panel_style = dock_area.read(cx).panel_style;
let left_dock_button = self.render_dock_toggle_button(DockPlacement::Left, window, cx);
let bottom_dock_button = self.render_dock_toggle_button(DockPlacement::Bottom, window, cx);
let right_dock_button = self.render_dock_toggle_button(DockPlacement::Right, window, cx);
if self.panels.len() == 1 && panel_style == PanelStyle::Default {
let panel = self.panels.first().unwrap();
@@ -565,21 +562,6 @@ impl TabPanel {
.h(px(30.))
.py_2()
.px_3()
.when(left_dock_button.is_some(), |this| this.pl_2())
.when(right_dock_button.is_some(), |this| this.pr_2())
.when(
left_dock_button.is_some() || bottom_dock_button.is_some(),
|this| {
this.child(
h_flex()
.flex_shrink_0()
.mr_1()
.gap_1()
.children(left_dock_button)
.children(bottom_dock_button),
)
},
)
.child(
div()
.id("tab")
@@ -612,8 +594,7 @@ impl TabPanel {
.flex_shrink_0()
.ml_1()
.gap_1()
.child(self.render_toolbar(state, window, cx))
.children(right_dock_button),
.child(self.render_toolbar(state, window, cx)),
)
.into_any_element();
}
@@ -622,22 +603,6 @@ impl TabPanel {
TabBar::new("tab-bar")
.track_scroll(self.tab_bar_scroll_handle.clone())
.when(
left_dock_button.is_some() || bottom_dock_button.is_some(),
|this| {
this.prefix(
h_flex()
.items_center()
.top_0()
// Right -1 for avoid border overlap with the first tab
.right(-px(1.))
.h_full()
.px_2()
.children(left_dock_button)
.children(bottom_dock_button),
)
},
)
.children(self.panels.iter().enumerate().filter_map(|(ix, panel)| {
let mut active = state.active_panel.as_ref() == Some(panel);
let disabled = self.is_collapsed;
@@ -723,8 +688,7 @@ impl TabPanel {
.h_full()
.px_2()
.gap_1()
.child(self.render_toolbar(state, window, cx))
.when_some(right_dock_button, |this, btn| this.child(btn)),
.child(self.render_toolbar(state, window, cx)),
)
.into_any_element()
}

View File

@@ -661,9 +661,9 @@ where
Some(icon) => icon,
None => {
if self.open {
IconName::ChevronUp
IconName::CaretUp
} else {
IconName::ChevronDown
IconName::CaretDown
}
}
};

View File

@@ -10,61 +10,37 @@ use gpui::{
#[derive(IntoElement, Clone)]
pub enum IconName {
ALargeSmall,
AddressBook,
ArrowIn,
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
ArrowUpCircle,
Asterisk,
Bell,
BookOpen,
Bot,
BubbleFill,
Calendar,
ChartPie,
CaretUp,
CaretDown,
CaretDownFill,
CaretRight,
Check,
ChevronDown,
ChevronDownSmall,
ChevronLeft,
ChevronRight,
ChevronUp,
ChevronsUpDown,
CircleCheck,
CircleUser,
CircleX,
CheckCircle,
CheckCircleFill,
Close,
CloseCircle,
CloseCircleFill,
Copy,
ComposeFill,
Dash,
Delete,
Ellipsis,
EllipsisVertical,
Eye,
EyeOff,
Frame,
Folder,
FolderFill,
FolderOpenFill,
GalleryVerticalEnd,
GitHub,
Globe,
Group,
GroupFill,
Heart,
HeartOff,
Inbox,
Info,
LayoutDashboard,
Loader,
LoaderCircle,
Map,
Maximize,
MailboxFill,
Menu,
Minimize,
Minus,
Moon,
Relays,
Palette,
PanelBottom,
PanelBottomOpen,
@@ -75,20 +51,19 @@ pub enum IconName {
PanelRightClose,
PanelRightOpen,
Plus,
PlusCircleFill,
Relays,
ResizeCorner,
Search,
Settings,
Settings2,
SortAscending,
SortDescending,
SquareTerminal,
Star,
StarOff,
Sun,
ThumbsDown,
ThumbsUp,
TriangleAlert,
Upload,
ResizeCorner,
UsersThreeFill,
WindowClose,
WindowMaximize,
WindowMinimize,
@@ -98,61 +73,37 @@ pub enum IconName {
impl IconName {
pub fn path(self) -> SharedString {
match self {
Self::ALargeSmall => "icons/a-large-small.svg",
Self::AddressBook => "icons/address-book.svg",
Self::ArrowIn => "icons/arrows-in.svg",
Self::ArrowDown => "icons/arrow-down.svg",
Self::ArrowLeft => "icons/arrow-left.svg",
Self::ArrowRight => "icons/arrow-right.svg",
Self::ArrowUp => "icons/arrow-up.svg",
Self::ArrowUpCircle => "icons/arrow-up-circle.svg",
Self::Asterisk => "icons/asterisk.svg",
Self::Bell => "icons/bell.svg",
Self::BookOpen => "icons/book-open.svg",
Self::Bot => "icons/bot.svg",
Self::BubbleFill => "icons/bubble-fill.svg",
Self::Calendar => "icons/calendar.svg",
Self::ChartPie => "icons/chart-pie.svg",
Self::CaretRight => "icons/caret-right.svg",
Self::CaretUp => "icons/caret-up.svg",
Self::CaretDown => "icons/caret-down.svg",
Self::CaretDownFill => "icons/caret-down-fill.svg",
Self::Check => "icons/check.svg",
Self::ChevronDown => "icons/chevron-down.svg",
Self::ChevronDownSmall => "icons/chevron-down-small.svg",
Self::ChevronLeft => "icons/chevron-left.svg",
Self::ChevronRight => "icons/chevron-right.svg",
Self::ChevronUp => "icons/chevron-up.svg",
Self::ChevronsUpDown => "icons/chevrons-up-down.svg",
Self::CircleCheck => "icons/circle-check.svg",
Self::CircleUser => "icons/circle-user.svg",
Self::CircleX => "icons/circle-x.svg",
Self::CheckCircle => "icons/check-circle.svg",
Self::CheckCircleFill => "icons/check-circle-fill.svg",
Self::Close => "icons/close.svg",
Self::CloseCircle => "icons/close-circle.svg",
Self::CloseCircleFill => "icons/close-circle-fill.svg",
Self::Copy => "icons/copy.svg",
Self::ComposeFill => "icons/compose-fill.svg",
Self::Dash => "icons/dash.svg",
Self::Delete => "icons/delete.svg",
Self::Ellipsis => "icons/ellipsis.svg",
Self::EllipsisVertical => "icons/ellipsis-vertical.svg",
Self::Eye => "icons/eye.svg",
Self::EyeOff => "icons/eye-off.svg",
Self::Frame => "icons/frame.svg",
Self::Folder => "icons/folder.svg",
Self::FolderFill => "icons/folder-fill.svg",
Self::FolderOpenFill => "icons/folder-open-fill.svg",
Self::GalleryVerticalEnd => "icons/gallery-vertical-end.svg",
Self::GitHub => "icons/github.svg",
Self::Globe => "icons/globe.svg",
Self::Group => "icons/group.svg",
Self::GroupFill => "icons/group-fill.svg",
Self::Heart => "icons/heart.svg",
Self::HeartOff => "icons/heart-off.svg",
Self::Inbox => "icons/inbox.svg",
Self::Info => "icons/info.svg",
Self::LayoutDashboard => "icons/layout-dashboard.svg",
Self::Loader => "icons/loader.svg",
Self::LoaderCircle => "icons/loader-circle.svg",
Self::Map => "icons/map.svg",
Self::Maximize => "icons/maximize.svg",
Self::MailboxFill => "icons/mailbox-fill.svg",
Self::Menu => "icons/menu.svg",
Self::Minimize => "icons/minimize.svg",
Self::Minus => "icons/minus.svg",
Self::Moon => "icons/moon.svg",
Self::Relays => "icons/relays.svg",
Self::Palette => "icons/palette.svg",
Self::PanelBottom => "icons/panel-bottom.svg",
Self::PanelBottomOpen => "icons/panel-bottom-open.svg",
@@ -163,20 +114,19 @@ impl IconName {
Self::PanelRightClose => "icons/panel-right-close.svg",
Self::PanelRightOpen => "icons/panel-right-open.svg",
Self::Plus => "icons/plus.svg",
Self::PlusCircleFill => "icons/plus-circle-fill.svg",
Self::Relays => "icons/relays.svg",
Self::ResizeCorner => "icons/resize-corner.svg",
Self::Search => "icons/search.svg",
Self::Settings => "icons/settings.svg",
Self::Settings2 => "icons/settings-2.svg",
Self::SortAscending => "icons/sort-ascending.svg",
Self::SortDescending => "icons/sort-descending.svg",
Self::SquareTerminal => "icons/square-terminal.svg",
Self::Star => "icons/star.svg",
Self::StarOff => "icons/star-off.svg",
Self::Sun => "icons/sun.svg",
Self::ThumbsDown => "icons/thumbs-down.svg",
Self::ThumbsUp => "icons/thumbs-up.svg",
Self::TriangleAlert => "icons/triangle-alert.svg",
Self::Upload => "icons/upload.svg",
Self::ResizeCorner => "icons/resize-corner.svg",
Self::UsersThreeFill => "icons/users-three-fill.svg",
Self::WindowClose => "icons/window-close.svg",
Self::WindowMaximize => "icons/window-maximize.svg",
Self::WindowMinimize => "icons/window-minimize.svg",
@@ -311,8 +261,8 @@ impl RenderOnce for Icon {
.when_some(self.size, |this, size| match size {
Size::Size(px) => this.size(px),
Size::XSmall => this.size_3(),
Size::Small => this.size_3p5(),
Size::Medium => this.size_4(),
Size::Small => this.size_4(),
Size::Medium => this.size_5(),
Size::Large => this.size_6(),
})
.path(self.path)
@@ -341,8 +291,8 @@ impl Render for Icon {
.when_some(self.size, |this, size| match size {
Size::Size(px) => this.size(px),
Size::XSmall => this.size_3(),
Size::Small => this.size_3p5(),
Size::Medium => this.size_4(),
Size::Small => this.size_4(),
Size::Medium => this.size_5(),
Size::Large => this.size_6(),
})
.path(self.path.clone())

View File

@@ -16,7 +16,7 @@ pub struct Indicator {
impl Indicator {
pub fn new() -> Self {
Self {
size: Size::Medium,
size: Size::Small,
speed: Duration::from_secs_f64(0.8),
icon: Icon::new(IconName::Loader),
color: None,

View File

@@ -1568,6 +1568,7 @@ impl Render for TextInput {
let suffix = self.suffix.as_ref().map(|build| build(window, cx));
div()
.flex_1()
.flex()
.id("input")
.key_context(CONTEXT)

View File

@@ -1,14 +1,14 @@
use crate::{
animation::cubic_bezier,
button::{Button, ButtonVariants as _},
button::{Button, ButtonCustomVariant, ButtonVariants as _},
theme::{scale::ColorScaleStep, ActiveTheme as _},
v_flex, ContextModal, IconName, Sizable as _, StyledExt,
};
use gpui::{
actions, anchored, div, point, prelude::FluentBuilder, px, relative, Animation,
AnimationExt as _, AnyElement, App, Bounds, ClickEvent, Div, FocusHandle, InteractiveElement,
IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString,
Styled, Window,
actions, anchored, div, point, prelude::FluentBuilder, px, Animation, AnimationExt as _,
AnyElement, App, Bounds, ClickEvent, Div, FocusHandle, InteractiveElement, IntoElement,
KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString, Styled,
Window,
};
use std::{rc::Rc, time::Duration};
@@ -45,9 +45,9 @@ impl Modal {
let base = v_flex()
.bg(cx.theme().background)
.border_1()
.border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE))
.rounded_lg()
.shadow_xl();
.border_color(cx.theme().base.step(cx, ColorScaleStep::SEVEN))
.rounded_xl()
.shadow_md();
Self {
base,
@@ -190,19 +190,48 @@ impl RenderOnce for Modal {
.top(y)
.w(self.width)
.when_some(self.max_width, |this, w| this.max_w(w))
.when_some(self.title, |this, title| {
this.child(
div()
.flex()
.items_center()
.h_10()
.px_2()
.text_sm()
.font_semibold()
.line_height(relative(1.))
.child(title),
)
})
.px_4()
.pb_4()
.child(
div()
.h_12()
.mb_2()
.border_b_1()
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
.flex()
.items_center()
.justify_between()
.when_some(self.title, |this, title| {
this.child(div().font_semibold().child(title))
})
.when(self.closable, |this| {
this.child(
Button::new(SharedString::from(format!(
"modal-close-{layer_ix}"
)))
.small()
.icon(IconName::CloseCircleFill)
.custom(
ButtonCustomVariant::new(window, cx)
.foreground(
cx.theme()
.base
.step(cx, ColorScaleStep::NINE),
)
.color(cx.theme().transparent)
.hover(cx.theme().transparent)
.active(cx.theme().transparent)
.border(cx.theme().transparent),
)
.on_click(move |_, window, cx| {
on_close(&ClickEvent::default(), window, cx);
window.close_modal(cx);
}),
)
}),
)
.child(self.content)
.children(self.footer)
.when(self.keyboard, |this| {
this.on_action({
let on_close = self.on_close.clone();
@@ -216,27 +245,6 @@ impl RenderOnce for Modal {
}
})
})
.when(self.closable, |this| {
this.child(
Button::new(SharedString::from(format!(
"modal-close-{layer_ix}"
)))
.absolute()
.top_2()
.right_2()
.small()
.ghost()
.icon(IconName::Close)
.on_click(
move |_, window, cx| {
on_close(&ClickEvent::default(), window, cx);
window.close_modal(cx);
},
),
)
})
.child(self.content)
.children(self.footer)
.with_animation(
"slide-down",
Animation::new(Duration::from_secs_f64(0.25))

View File

@@ -221,10 +221,9 @@ impl Render for Notification {
NotificationType::Info => {
Icon::new(IconName::Info).text_color(blue().step(cx, ColorScaleStep::NINE))
}
NotificationType::Error => {
Icon::new(IconName::CircleX).text_color(red().step(cx, ColorScaleStep::NINE))
}
NotificationType::Success => Icon::new(IconName::CircleCheck)
NotificationType::Error => Icon::new(IconName::CloseCircle)
.text_color(red().step(cx, ColorScaleStep::NINE)),
NotificationType::Success => Icon::new(IconName::CheckCircle)
.text_color(green().step(cx, ColorScaleStep::NINE)),
NotificationType::Warning => Icon::new(IconName::TriangleAlert)
.text_color(yellow().step(cx, ColorScaleStep::NINE)),

View File

@@ -574,7 +574,7 @@ impl Render for PopupMenu {
.py_0()
.px_2()
.rounded_md()
.text_xs()
.text_sm()
.on_mouse_enter(cx.listener(
move |this, _, _window, cx| {
this.hovered_menu_ix = Some(ix);
@@ -677,7 +677,7 @@ impl Render for PopupMenu {
.justify_between()
.child(label.clone())
.child(
IconName::ChevronRight,
IconName::CaretRight,
),
),
)

View File

@@ -198,9 +198,10 @@ impl<T: Styled> StyleSized<T> for T {
fn input_h(self, size: Size) -> Self {
match size {
Size::Large => self.h_11(),
Size::Medium => self.h_8(),
_ => self.h(px(28.)),
Size::Small => self.h_7(),
Size::Medium => self.h_9(),
Size::Large => self.h_12(),
_ => self.h(px(24.)),
}
.input_text_size(size)
}

View File

@@ -81,36 +81,38 @@ impl RenderOnce for Tab {
let (text_color, bg_color, hover_bg_color) = match (self.selected, self.disabled) {
(true, false) => (
cx.theme().base.step(cx, ColorScaleStep::TWELVE),
cx.theme().base.step(cx, ColorScaleStep::THREE),
cx.theme().base.step(cx, ColorScaleStep::FIVE),
cx.theme().base.step(cx, ColorScaleStep::FOUR),
),
(false, false) => (
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
cx.theme().base.step(cx, ColorScaleStep::ONE),
cx.theme().transparent,
cx.theme().base.step(cx, ColorScaleStep::FOUR),
),
// disabled
(true, true) => (
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
cx.theme().base.step(cx, ColorScaleStep::ONE),
cx.theme().transparent,
cx.theme().base.step(cx, ColorScaleStep::FOUR),
),
(false, true) => (
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
cx.theme().base.step(cx, ColorScaleStep::ONE),
cx.theme().transparent,
cx.theme().base.step(cx, ColorScaleStep::FOUR),
),
};
self.base
.h(px(30.))
.px_2()
.relative()
.flex()
.items_center()
.flex_shrink_0()
.cursor_pointer()
.overflow_hidden()
.text_sm()
.text_xs()
.text_ellipsis()
.text_color(text_color)
.bg(bg_color)
.rounded(px(cx.theme().radius))
@@ -118,16 +120,7 @@ impl RenderOnce for Tab {
.when_some(self.prefix, |this, prefix| {
this.child(prefix).text_color(text_color)
})
.child(
div()
.px_3()
.flex()
.items_center()
.gap_1()
.text_ellipsis()
.text_xs()
.child(self.label),
)
.child(self.label)
.when_some(self.suffix, |this, suffix| this.child(suffix))
}
}

View File

@@ -148,7 +148,7 @@ impl Theme {
Theme {
base: color_scales.gray,
accent: color_scales.yellow,
font_size: 16.0,
font_size: 15.0,
font_family: if cfg!(target_os = "macos") {
".SystemUIFont".into()
} else if cfg!(target_os = "windows") {

View File

@@ -1,8 +1,4 @@
use crate::{
h_flex,
theme::{scale::ColorScaleStep, ActiveTheme},
Icon, IconName, InteractiveElementExt as _, Sizable as _,
};
use crate::{h_flex, theme::ActiveTheme, Icon, IconName, InteractiveElementExt as _, Sizable as _};
use gpui::{
black, div, prelude::FluentBuilder as _, px, relative, white, AnyElement, App, ClickEvent, Div,
Element, Hsla, InteractiveElement as _, IntoElement, MouseButton, ParentElement, Pixels,
@@ -251,7 +247,7 @@ impl RenderOnce for TitleBar {
.items_center()
.justify_between()
.h(HEIGHT)
.bg(cx.theme().base.step(cx, ColorScaleStep::ONE))
.bg(cx.theme().transparent)
.when(window.is_fullscreen(), |this| this.pl(px(12.)))
.on_double_click(|_, window, _cx| window.zoom_window())
.child(