mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
ui prompt: Adjust UI and focus visibility (#39106)
Closes https://github.com/zed-industries/zed/issues/38643 This PR adds some UI improvements to the Zed replacement of the system dialog/prompt, including better visibility of which button is currently focused. One little design note, though: because of a current (and somewhat annoying) constraint of button component, where we're only drawing a border when its style is outlined, if I kept them horizontally stacked, there'd be a little layout shift now that I'm toggling styles for better focus visibility. So, for this reason, I changed them to be vertically stacked, which matches the macOS design and avoids this problem. Maybe in the future, we'll revert it back to being `flex_row` because that ultimately consumes less space. https://github.com/user-attachments/assets/500c840b-6b56-4c0c-b56a-535939398a7b Release Notes: - Improve focus visibility of the actions within Zed's UI system prompt.
This commit is contained in:
parent
f9fb389f86
commit
ed7217ff46
1 changed files with 68 additions and 87 deletions
|
|
@ -1,16 +1,12 @@
|
|||
use gpui::{
|
||||
App, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, FontWeight,
|
||||
InteractiveElement, IntoElement, ParentElement, PromptButton, PromptHandle, PromptLevel,
|
||||
PromptResponse, Refineable, Render, RenderablePromptHandle, SharedString, Styled,
|
||||
TextStyleRefinement, Window, div,
|
||||
App, Entity, EventEmitter, FocusHandle, Focusable, PromptButton, PromptHandle, PromptLevel,
|
||||
PromptResponse, RenderablePromptHandle, SharedString, TextStyleRefinement, Window, div,
|
||||
prelude::*,
|
||||
};
|
||||
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
ActiveTheme, ButtonCommon, ButtonStyle, Clickable, ElevationIndex, FluentBuilder, LabelSize,
|
||||
StyledExt, TintColor, h_flex, v_flex,
|
||||
};
|
||||
use ui::{FluentBuilder, TintColor, prelude::*};
|
||||
use workspace::WorkspaceSettings;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
|
|
@ -92,11 +88,7 @@ impl ZedPromptRenderer {
|
|||
}
|
||||
|
||||
fn select_next(&mut self, _: &menu::SelectNext, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.active_action_id > 0 {
|
||||
self.active_action_id -= 1;
|
||||
} else {
|
||||
self.active_action_id = self.actions.len().saturating_sub(1);
|
||||
}
|
||||
self.active_action_id = (self.active_action_id + 1) % self.actions.len();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +98,11 @@ impl ZedPromptRenderer {
|
|||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.active_action_id = (self.active_action_id + 1) % self.actions.len();
|
||||
if self.active_action_id > 0 {
|
||||
self.active_action_id -= 1;
|
||||
} else {
|
||||
self.active_action_id = self.actions.len().saturating_sub(1);
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
|
@ -114,8 +110,8 @@ impl ZedPromptRenderer {
|
|||
impl Render for ZedPromptRenderer {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let font_size = settings.ui_font_size(cx).into();
|
||||
let prompt = v_flex()
|
||||
|
||||
let dialog = v_flex()
|
||||
.key_context("Prompt")
|
||||
.cursor_default()
|
||||
.track_focus(&self.focus)
|
||||
|
|
@ -125,96 +121,81 @@ impl Render for ZedPromptRenderer {
|
|||
.on_action(cx.listener(Self::select_previous))
|
||||
.on_action(cx.listener(Self::select_first))
|
||||
.on_action(cx.listener(Self::select_last))
|
||||
.elevation_3(cx)
|
||||
.w_72()
|
||||
.overflow_hidden()
|
||||
.w_80()
|
||||
.p_4()
|
||||
.gap_4()
|
||||
.elevation_3(cx)
|
||||
.overflow_hidden()
|
||||
.font_family(settings.ui_font.family.clone())
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.child(MarkdownElement::new(self.message.clone(), {
|
||||
let mut base_text_style = window.text_style();
|
||||
base_text_style.refine(&TextStyleRefinement {
|
||||
font_family: Some(settings.ui_font.family.clone()),
|
||||
font_size: Some(font_size),
|
||||
font_weight: Some(FontWeight::BOLD),
|
||||
color: Some(ui::Color::Default.color(cx)),
|
||||
..Default::default()
|
||||
});
|
||||
MarkdownStyle {
|
||||
base_text_style,
|
||||
selection_background_color: cx
|
||||
.theme()
|
||||
.colors()
|
||||
.element_selection_background,
|
||||
..Default::default()
|
||||
}
|
||||
})),
|
||||
)
|
||||
.child(div().w_full().child(MarkdownElement::new(
|
||||
self.message.clone(),
|
||||
markdown_style(true, window, cx),
|
||||
)))
|
||||
.children(self.detail.clone().map(|detail| {
|
||||
div()
|
||||
.w_full()
|
||||
.text_xs()
|
||||
.child(MarkdownElement::new(detail, {
|
||||
let mut base_text_style = window.text_style();
|
||||
base_text_style.refine(&TextStyleRefinement {
|
||||
font_family: Some(settings.ui_font.family.clone()),
|
||||
font_size: Some(font_size),
|
||||
color: Some(ui::Color::Muted.color(cx)),
|
||||
..Default::default()
|
||||
});
|
||||
MarkdownStyle {
|
||||
base_text_style,
|
||||
selection_background_color: cx
|
||||
.theme()
|
||||
.colors()
|
||||
.element_selection_background,
|
||||
..Default::default()
|
||||
}
|
||||
}))
|
||||
div().w_full().text_xs().child(MarkdownElement::new(
|
||||
detail,
|
||||
markdown_style(false, window, cx),
|
||||
))
|
||||
}))
|
||||
.child(h_flex().justify_end().gap_2().children(
|
||||
self.actions.iter().enumerate().rev().map(|(ix, action)| {
|
||||
ui::Button::new(ix, action.clone())
|
||||
.label_size(LabelSize::Large)
|
||||
.style(ButtonStyle::Filled)
|
||||
.when(ix == self.active_action_id, |el| {
|
||||
el.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
})
|
||||
.layer(ElevationIndex::ModalSurface)
|
||||
.on_click(cx.listener(move |_, _, _window, cx| {
|
||||
cx.emit(PromptResponse(ix));
|
||||
}))
|
||||
}),
|
||||
));
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.children(self.actions.iter().enumerate().map(|(ix, action)| {
|
||||
Button::new(ix, action.clone())
|
||||
.full_width()
|
||||
.style(ButtonStyle::Outlined)
|
||||
.when(ix == self.active_action_id, |s| {
|
||||
s.style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
})
|
||||
.tab_index(ix as isize)
|
||||
.on_click(cx.listener(move |_, _, _window, cx| {
|
||||
cx.emit(PromptResponse(ix));
|
||||
}))
|
||||
})),
|
||||
);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.occlude()
|
||||
.bg(gpui::black().opacity(0.2))
|
||||
.child(
|
||||
div()
|
||||
v_flex()
|
||||
.size_full()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_around()
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.justify_around()
|
||||
.child(prompt),
|
||||
),
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.child(dialog),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn markdown_style(main_message: bool, window: &Window, cx: &App) -> MarkdownStyle {
|
||||
let mut base_text_style = window.text_style();
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let font_size = settings.ui_font_size(cx).into();
|
||||
|
||||
let color = if main_message {
|
||||
Color::Default.color(cx)
|
||||
} else {
|
||||
Color::Muted.color(cx)
|
||||
};
|
||||
|
||||
base_text_style.refine(&TextStyleRefinement {
|
||||
font_family: Some(settings.ui_font.family.clone()),
|
||||
font_size: Some(font_size),
|
||||
color: Some(color),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
MarkdownStyle {
|
||||
base_text_style,
|
||||
selection_background_color: cx.theme().colors().element_selection_background,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PromptResponse> for ZedPromptRenderer {}
|
||||
|
||||
impl Focusable for ZedPromptRenderer {
|
||||
|
|
|
|||
Loading…
Reference in a new issue