settings ui: Use the tree view item component and other design tweaks (#39329)
An initial pass at some foundational styles. Release Notes: - N/A
This commit is contained in:
@@ -57,11 +57,14 @@ impl RenderOnce for SettingsEditor {
|
||||
let theme_colors = cx.theme().colors();
|
||||
|
||||
div()
|
||||
.child(editor)
|
||||
.bg(theme_colors.editor_background)
|
||||
.py_1()
|
||||
.px_2()
|
||||
.min_w_64()
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.rounded_lg()
|
||||
.border_color(theme_colors.border)
|
||||
.bg(theme_colors.editor_background)
|
||||
.child(editor)
|
||||
.when_some(self.confirm, |this, confirm| {
|
||||
this.on_action::<menu::Confirm>({
|
||||
move |_, _, cx| {
|
||||
|
||||
@@ -4,9 +4,8 @@ use editor::{Editor, EditorEvent};
|
||||
use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use gpui::{
|
||||
App, AppContext as _, Context, Div, Entity, Global, IntoElement, ReadGlobal as _, Render, Task,
|
||||
TitlebarOptions, UniformListScrollHandle, Window, WindowHandle, WindowOptions, actions, div,
|
||||
point, px, size, uniform_list,
|
||||
App, Div, Entity, Global, ReadGlobal as _, Task, TitlebarOptions, UniformListScrollHandle,
|
||||
Window, WindowHandle, WindowOptions, actions, div, point, px, size, uniform_list,
|
||||
};
|
||||
use project::WorktreeId;
|
||||
use settings::{CursorShape, SaturatingBool, SettingsContent, SettingsStore};
|
||||
@@ -18,7 +17,10 @@ use std::{
|
||||
rc::Rc,
|
||||
sync::{Arc, atomic::AtomicBool},
|
||||
};
|
||||
use ui::{Divider, DropdownMenu, ListItem, Switch, prelude::*};
|
||||
use ui::{
|
||||
ContextMenu, Divider, DropdownMenu, DropdownStyle, Switch, SwitchColor, TreeViewItem,
|
||||
prelude::*,
|
||||
};
|
||||
use util::{paths::PathStyle, rel_path::RelPath};
|
||||
|
||||
use crate::components::SettingsEditor;
|
||||
@@ -360,12 +362,23 @@ impl std::fmt::Debug for SettingsPageItem {
|
||||
}
|
||||
|
||||
impl SettingsPageItem {
|
||||
fn render(&self, file: SettingsUiFile, window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
fn render(
|
||||
&self,
|
||||
file: SettingsUiFile,
|
||||
is_last: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
match self {
|
||||
SettingsPageItem::SectionHeader(header) => v_flex()
|
||||
.w_full()
|
||||
.gap_0p5()
|
||||
.child(Label::new(SharedString::new_static(header)).size(LabelSize::Large))
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(SharedString::new_static(header))
|
||||
.size(LabelSize::XSmall)
|
||||
.color(Color::Muted)
|
||||
.buffer_font(cx),
|
||||
)
|
||||
.child(Divider::horizontal().color(ui::DividerColor::BorderVariant))
|
||||
.into_any_element(),
|
||||
SettingsPageItem::SettingItem(setting_item) => {
|
||||
@@ -379,6 +392,11 @@ impl SettingsPageItem {
|
||||
.gap_2()
|
||||
.flex_wrap()
|
||||
.justify_between()
|
||||
.when(!is_last, |this| {
|
||||
this.pb_4()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
})
|
||||
.child(
|
||||
v_flex()
|
||||
.max_w_1_2()
|
||||
@@ -766,52 +784,26 @@ impl SettingsWindow {
|
||||
.map(|ix| {
|
||||
let entry = &this.navbar_entries[ix];
|
||||
|
||||
h_flex()
|
||||
.id(("settings-ui-section", ix))
|
||||
.w_full()
|
||||
.pl_2p5()
|
||||
.py_0p5()
|
||||
.rounded_sm()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border_transparent)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(this.is_navbar_entry_selected(ix), |this| {
|
||||
this.text_color(cx.theme().colors().text)
|
||||
.bg(cx.theme().colors().element_selected.opacity(0.2))
|
||||
.border_color(cx.theme().colors().border)
|
||||
TreeViewItem::new(("settings-ui-navbar-entry", ix), entry.title)
|
||||
.root_item(entry.is_root)
|
||||
.toggle_state(this.is_navbar_entry_selected(ix))
|
||||
.when(entry.is_root, |item| {
|
||||
item.toggle(
|
||||
this.pages[this.page_index_from_navbar_index(ix)]
|
||||
.expanded,
|
||||
)
|
||||
.on_toggle(
|
||||
cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_navbar_entry(ix);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
ListItem::new(("settings-ui-navbar-entry", ix))
|
||||
.selectable(true)
|
||||
.inset(true)
|
||||
.indent_step_size(px(1.))
|
||||
.indent_level(if entry.is_root { 1 } else { 3 })
|
||||
.when(entry.is_root, |item| {
|
||||
item.toggle(
|
||||
this.pages
|
||||
[this.page_index_from_navbar_index(ix)]
|
||||
.expanded,
|
||||
)
|
||||
.always_show_disclosure_icon(true)
|
||||
.on_toggle(cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_navbar_entry(ix);
|
||||
cx.notify();
|
||||
}))
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.text_ui(cx)
|
||||
.truncate()
|
||||
.hover(|s| {
|
||||
s.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
.child(entry.title),
|
||||
),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.navbar_entry = ix;
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
@@ -835,10 +827,15 @@ impl SettingsWindow {
|
||||
}
|
||||
|
||||
fn render_page(&self, window: &mut Window, cx: &mut Context<SettingsWindow>) -> Div {
|
||||
v_flex().gap_4().children(
|
||||
self.page_items()
|
||||
.map(|item| item.render(self.current_file.clone(), window, cx)),
|
||||
)
|
||||
let items: Vec<_> = self.page_items().collect();
|
||||
let items_len = items.len();
|
||||
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.children(items.into_iter().enumerate().map(|(index, item)| {
|
||||
let is_last = index == items_len - 1;
|
||||
item.render(self.current_file.clone(), is_last, window, cx)
|
||||
}))
|
||||
}
|
||||
|
||||
fn current_page_index(&self) -> usize {
|
||||
@@ -869,10 +866,13 @@ impl SettingsWindow {
|
||||
|
||||
impl Render for SettingsWindow {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let ui_font = theme::setup_ui_font(window, cx);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.size_full()
|
||||
.font(ui_font)
|
||||
.bg(cx.theme().colors().background)
|
||||
.text_color(cx.theme().colors().text)
|
||||
.child(self.render_nav(window, cx))
|
||||
@@ -927,9 +927,9 @@ fn render_toggle_button<B: Into<bool> + From<bool> + Copy>(
|
||||
let (_, &value) = SettingsStore::global(cx).get_value_from_file(file.to_settings(), field.pick);
|
||||
|
||||
let toggle_state = if value.into() {
|
||||
ui::ToggleState::Selected
|
||||
ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
ToggleState::Unselected
|
||||
};
|
||||
|
||||
Switch::new("toggle_button", toggle_state)
|
||||
@@ -944,6 +944,7 @@ fn render_toggle_button<B: Into<bool> + From<bool> + Copy>(
|
||||
});
|
||||
}
|
||||
})
|
||||
.color(SwitchColor::Accent)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
@@ -968,7 +969,7 @@ where
|
||||
DropdownMenu::new(
|
||||
"dropdown",
|
||||
current_value_label,
|
||||
ui::ContextMenu::build(window, cx, move |mut menu, _, _| {
|
||||
ContextMenu::build(window, cx, move |mut menu, _, _| {
|
||||
for (value, label) in variants()
|
||||
.into_iter()
|
||||
.copied()
|
||||
@@ -977,7 +978,7 @@ where
|
||||
menu = menu.toggleable_entry(
|
||||
label,
|
||||
value == current_value,
|
||||
ui::IconPosition::Start,
|
||||
IconPosition::Start,
|
||||
None,
|
||||
move |_, cx| {
|
||||
if value == current_value {
|
||||
@@ -997,6 +998,7 @@ where
|
||||
menu
|
||||
}),
|
||||
)
|
||||
.style(DropdownStyle::Outlined)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user