Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
550 changes: 550 additions & 0 deletions .github/workflows/ui-review-screenshots.yml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions nebula_app/src/gpui_shell/settings_pane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use std::time::Duration;

use crate::gpui_shell::config::{DEFAULT_CURSOR_BLINK, effective_cursor_blink};
use crate::gpui_shell::prelude::*;
use crate::gpui_shell::widgets::NebulaButton;
use crate::gpui_shell::widgets::{NebulaButton, settings_control_height};

mod about;
mod agents;
Expand Down Expand Up @@ -683,7 +683,7 @@ impl SettingsPane {
.debug_selector(move || format!("settings-select-{key}"))
.w(px(SETTINGS_SELECT_WIDTH))
.text_color(cx.theme().link)
.children(select.map(|state| Select::new(&state)))
.children(select.map(|state| Select::new(&state).h(settings_control_height(cx))))
.into_any_element()
});
self.maybe_marked(key, label, desc, control, cx)
Expand All @@ -698,7 +698,7 @@ impl SettingsPane {
.w(px(SETTINGS_SELECT_WIDTH))
.font_family(cx.theme().mono_font_family.clone())
.text_color(cx.theme().link)
.child(Select::new(&self.shell_select)),
.child(Select::new(&self.shell_select).h(settings_control_height(cx))),
cx,
)
}
Expand Down
6 changes: 3 additions & 3 deletions nebula_app/src/gpui_shell/settings_pane/design.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl SettingsPane {
let reset = dirty.then(|| {
div()
.id(SharedString::from(format!("setting-reset-{label}")))
.size(px(20.0))
.size(px(32.0))
.rounded_md()
.flex()
.items_center()
Expand All @@ -144,7 +144,7 @@ impl SettingsPane {
.build(window, cx)
})
.on_click(cx.listener(move |this, _, window, cx| on_reset(this, window, cx)))
.child(Icon::new(IconName::Undo2).xsmall())
.child(Icon::new(IconName::Undo2).size(px(16.0)))
.into_any_element()
});
self.row_shell(label, desc.into(), reset, dirty, RowLayout::Standard, control, cx)
Expand Down Expand Up @@ -239,7 +239,7 @@ impl SettingsPane {
Button::new(SharedString::from(format!("settings-help-{label}")))
.icon(IconName::Info)
.ghost()
.size(px(22.0))
.size(px(32.0))
.text_color(theme.muted_foreground)
.accessibility_id(SharedString::from(format!(
"settings-help-{label}"
Expand Down
6 changes: 2 additions & 4 deletions nebula_app/src/gpui_shell/settings_pane/segmented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@ impl SettingsPane {
)))
.w(px(SETTINGS_SELECT_WIDTH))
.max_w_full()
.small()
.outline()
.children(values.iter().copied().zip(labels).enumerate().map(
|(index, (value, label))| {
Button::new(SharedString::from(format!("settings-choice-{key}-{value}")))
.debug_selector(move || format!("settings-choice-{key}-{value}"))
.flex_1()
.min_w_0()
.small()
.h(px(28.0))
.rounded(px(14.0))
.h(settings_control_height(cx))
.rounded(px(6.0))
.selected(index == selected)
.label(label)
.on_click(cx.listener(move |this, _, window, cx| {
Expand Down
24 changes: 21 additions & 3 deletions nebula_app/src/gpui_shell/widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use std::sync::Arc;

use gpui::prelude::FluentBuilder as _;
use gpui::{
App, ClickEvent, ElementId, IntoElement, ParentElement as _, RenderImage, RenderOnce,
SharedString, Styled as _, Window, div, px,
App, ClickEvent, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
RenderImage, RenderOnce, SharedString, Styled as _, Window, div, px,
};
use gpui_component::button::{Button, ButtonVariants as _};
use gpui_component::switch::Switch;
Expand Down Expand Up @@ -51,6 +51,20 @@ pub fn shell_brand_image(
Some(Arc::new(RenderImage::new([Frame::new(rgba)])))
}

/// Desktop form controls keep their padding even with a small UI font.
pub(crate) fn settings_control_height(cx: &App) -> gpui::Pixels {
px(f32::from(cx.theme().font_size).max(16.0) * 2.0)
}

/// Toolbar glyphs and their hover/hit surfaces have independent logical sizes.
pub(crate) fn toolbar_button(id: impl Into<ElementId>, icon: impl Into<Icon>) -> Button {
Button::new(id).icon(Icon::new(icon).size(px(18.0))).ghost().size(px(32.0))
}

#[cfg(all(test, feature = "gpui-test-support"))]
#[path = "widgets_tests.rs"]
mod tests;

/// 设置行开关。组件库 `Switch` 的转发壳——`on_click` 与它同签名
/// (`Fn(&bool, &mut Window, &mut App)`,参数是**点击后**的目标值)。
#[derive(IntoElement)]
Expand Down Expand Up @@ -187,8 +201,12 @@ impl NebulaButton {
}

impl RenderOnce for NebulaButton {
fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let key = self.key.clone();
let button = Button::new(ElementId::Name(format!("nebula-btn-{}", self.key).into()))
.debug_selector(move || format!("nebula-btn-{key}"))
.h(settings_control_height(cx))
.px(px(12.0))
.label(self.label)
.disabled(self.disabled);
// Default 走 outline:设置行里的动作按钮需要一条边把自己从行底分出来,
Expand Down
66 changes: 66 additions & 0 deletions nebula_app/src/gpui_shell/widgets_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::{cell::Cell, rc::Rc};

use gpui::{AppContext as _, Context, Modifiers, Render, TestAppContext, point};
use gpui_component::{IconName, Root, Theme, h_flex};

use super::*;

struct ControlProbe(Rc<Cell<usize>>);

impl Render for ControlProbe {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let action = self.0.clone();
let tool = self.0.clone();
let disabled = self.0.clone();
h_flex()
.gap(px(8.0))
.child(
NebulaButton::new("comfort-action")
.label("查看详情 / Details")
.on_click(move |_, _, _| action.set(action.get() + 1)),
)
.child(
toolbar_button("comfort-tool", IconName::Settings)
.debug_selector(|| "comfort-tool".to_owned())
.on_click(move |_, _, _| tool.set(tool.get() + 1)),
)
.child(
NebulaButton::new("comfort-disabled")
.label("Disabled")
.disabled(true)
.on_click(move |_, _, _| disabled.set(disabled.get() + 1)),
)
}
}

#[gpui::test]
fn desktop_controls_keep_padding_clickable_with_small_ui_fonts(cx: &mut TestAppContext) {
cx.update(gpui_component::init);
let clicks = Rc::new(Cell::new(0));
let (_, cx) = cx.add_window_view(|window, cx| {
let view = cx.new(|_| ControlProbe(clicks.clone()));
Root::new(view, window, cx)
});
cx.simulate_resize(gpui::size(px(1000.0), px(200.0)));
for (font, height) in [(10.0, 32.0), (14.0, 32.0), (24.0, 48.0)] {
cx.update(|window, cx| {
Theme::global_mut(cx).font_size = px(font);
window.refresh();
let _ = window.draw(cx);
});
let action = cx.debug_bounds("nebula-btn-comfort-action").expect("text button");
let tool = cx.debug_bounds("comfort-tool").expect("toolbar button");
let disabled = cx.debug_bounds("nebula-btn-comfort-disabled").expect("disabled button");
assert!(action.size.height >= px(height));
assert_eq!(tool.size, gpui::size(px(32.0), px(32.0)));
let before = clicks.get();
// These corners are padding, not glyphs: the whole surface must activate.
for bounds in [action, tool, disabled] {
cx.simulate_click(
point(bounds.origin.x + px(2.0), bounds.bottom() - px(2.0)),
Modifiers::default(),
);
}
assert_eq!(clicks.get(), before + 2, "disabled padding must not activate");
}
}
5 changes: 2 additions & 3 deletions nebula_app/src/gpui_shell/workspace/details_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use super::*;
use crate::display::side_panel::PanelView;
use crate::gpui_shell::file_editor::{DocumentDetails, DocumentSection, TextFileView};
use crate::gpui_shell::widgets::toolbar_button;
use crate::i18n::Message;

#[cfg(all(test, feature = "gpui-test-support"))]
Expand Down Expand Up @@ -103,9 +104,7 @@ impl NebulaWorkspace {
cx: &mut Context<Self>,
) -> Button {
let visible = self.side_panel.open && !self.reader_focus_active(cx);
Button::new("toggle-right-sidebar")
.icon(IconName::PanelRight)
.ghost()
toolbar_button("toggle-right-sidebar", IconName::PanelRight)
.disabled(disabled)
.selected(visible)
.when(visible, |button| button.bg(cx.theme().secondary))
Expand Down
37 changes: 16 additions & 21 deletions nebula_app/src/gpui_shell/workspace/sidebar.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use crate::gpui_shell::widgets::toolbar_button;

/// 折叠箭头的固定布局槽。图标是 SVG,不应借任一字体的 advance 决定留白。
const TABS_DISCLOSURE_SLOT_W: f32 = 24.0;
Expand Down Expand Up @@ -871,15 +872,12 @@ impl NebulaWorkspace {
.justify_between()
.child(
h_flex()
// 旧壳两枚 32px 命中块之间固定留 8px;默认 Button 正好是
// 32px,`.small()` 会把热区缩成 24px。
.gap_2()
// Keep toolbar gaps independent of the UI font/rem size.
.gap(px(8.0))
.items_center()
.occlude()
.child(
Button::new("toggle-sidebar")
.icon(IconName::PanelLeft)
.ghost()
toolbar_button("toggle-sidebar", IconName::PanelLeft)
.disabled(settings_active)
// 侧栏是开关而非一次性动作:展开期间必须持续显示
// 选中底,和旧壳 `left_sidebar_visible()` 同义。
Expand All @@ -899,9 +897,7 @@ impl NebulaWorkspace {
})),
)
.child(
Button::new("open-settings")
.icon(IconName::Settings)
.ghost()
toolbar_button("open-settings", IconName::Settings)
.selected(settings_active)
.when(settings_active, |button| {
button.bg(settings_active_bg).text_color(settings_active_fg)
Expand All @@ -915,19 +911,18 @@ impl NebulaWorkspace {
.child(self.render_collapsed_tab_title(cx))
.child(
title_bar_panel_controls()
.gap_2()
.gap(px(8.0))
.child(
Button::new("toggle-command-manager")
.icon(
Icon::new(Icon::empty())
.path(crate::gpui_shell::assets::nav::COMMAND_MANAGER),
)
.ghost()
.selected(self.command_manager_open)
.tooltip("命令列表")
.on_click(cx.listener(|this, _, window, cx| {
this.toggle_command_manager(window, cx);
})),
toolbar_button(
"toggle-command-manager",
Icon::new(Icon::empty())
.path(crate::gpui_shell::assets::nav::COMMAND_MANAGER),
)
.selected(self.command_manager_open)
.tooltip("命令列表")
.on_click(cx.listener(|this, _, window, cx| {
this.toggle_command_manager(window, cx);
})),
)
.child(self.render_right_sidebar_button(settings_active, cx)),
)
Expand Down
13 changes: 6 additions & 7 deletions nebula_app/src/gpui_shell/workspace/top_tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use gpui_component::menu::PopupMenuItem;

use crate::gpui_shell::prelude::*;
use crate::gpui_shell::terminal::view::SidebarActivity;
use crate::gpui_shell::widgets::toolbar_button;

use super::{
NebulaWorkspace, NewWindow, OpenSettings, TAB_LABEL_ICON_SIZE, TAB_LABEL_ICON_W, TabDrag,
Expand Down Expand Up @@ -683,13 +684,11 @@ impl NebulaWorkspace {
.child(
title_bar_panel_controls()
.child(
Button::new("top-toggle-command-manager")
.icon(
Icon::new(Icon::empty()).path(
crate::gpui_shell::assets::nav::COMMAND_MANAGER,
),
)
.ghost()
toolbar_button(
"top-toggle-command-manager",
Icon::new(Icon::empty())
.path(crate::gpui_shell::assets::nav::COMMAND_MANAGER),
)
.selected(self.command_manager_open)
.tooltip("命令列表")
.on_click(cx.listener(|this, _, window, cx| {
Expand Down
Loading