settings ui: Add basic support for drop down menus (#38019)
Enums with six or less fields can still use toggle groups by adding a definition. I also renamed the `OpenSettingsEditor` action to `OpenSettingsUi` Release Notes: - N/A
This commit is contained in:
@@ -16,7 +16,10 @@ use settings::{
|
||||
SettingsValue,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use ui::{NumericStepper, SwitchField, ToggleButtonGroup, ToggleButtonSimple, prelude::*};
|
||||
use ui::{
|
||||
ContextMenu, DropdownMenu, NumericStepper, SwitchField, ToggleButtonGroup, ToggleButtonSimple,
|
||||
prelude::*,
|
||||
};
|
||||
use workspace::{
|
||||
Workspace,
|
||||
item::{Item, ItemEvent},
|
||||
@@ -33,14 +36,14 @@ impl FeatureFlag for SettingsUiFeatureFlag {
|
||||
actions!(
|
||||
zed,
|
||||
[
|
||||
/// Opens the settings editor.
|
||||
OpenSettingsEditor
|
||||
/// Opens settings UI.
|
||||
OpenSettingsUi
|
||||
]
|
||||
);
|
||||
|
||||
pub fn open_settings_editor(
|
||||
workspace: &mut Workspace,
|
||||
_: &OpenSettingsEditor,
|
||||
_: &OpenSettingsUi,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
@@ -62,7 +65,7 @@ pub fn open_settings_editor(
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(|workspace: &mut Workspace, _, _| {
|
||||
workspace.register_action_renderer(|div, _, _, cx| {
|
||||
let settings_ui_actions = [std::any::TypeId::of::<OpenSettingsEditor>()];
|
||||
let settings_ui_actions = [std::any::TypeId::of::<OpenSettingsUi>()];
|
||||
let has_flag = cx.has_flag::<SettingsUiFeatureFlag>();
|
||||
command_palette_hooks::CommandPaletteFilter::update_global(cx, |filter, _| {
|
||||
if has_flag {
|
||||
@@ -635,8 +638,8 @@ fn render_item_single(
|
||||
variants: values,
|
||||
labels: titles,
|
||||
} => render_toggle_button_group(settings_value, values, titles, window, cx),
|
||||
SettingsUiItemSingle::DropDown { .. } => {
|
||||
unimplemented!("This")
|
||||
SettingsUiItemSingle::DropDown { variants, labels } => {
|
||||
render_dropdown(settings_value, variants, labels, window, cx)
|
||||
}
|
||||
SettingsUiItemSingle::TextField => render_text_field(settings_value, window, cx),
|
||||
}
|
||||
@@ -896,6 +899,41 @@ fn render_toggle_button_group(
|
||||
});
|
||||
}
|
||||
|
||||
fn render_dropdown(
|
||||
value: SettingsValue<serde_json::Value>,
|
||||
variants: &'static [&'static str],
|
||||
labels: &'static [&'static str],
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
let value = downcast_any_item::<String>(value);
|
||||
let id = element_id_from_path(&value.path);
|
||||
|
||||
let menu = window.use_state(cx, |window, cx| {
|
||||
let path = value.path.clone();
|
||||
let handler = Rc::new(move |variant: &'static str, cx: &mut App| {
|
||||
SettingsValue::write_value(&path, serde_json::Value::String(variant.to_string()), cx);
|
||||
});
|
||||
|
||||
ContextMenu::build(window, cx, |mut menu, _, _| {
|
||||
for (label, variant) in labels.iter().zip(variants) {
|
||||
menu = menu.entry(*label, None, {
|
||||
let handler = handler.clone();
|
||||
move |_, cx| {
|
||||
handler(variant, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
menu
|
||||
})
|
||||
});
|
||||
|
||||
DropdownMenu::new(id, value.read(), menu.read(cx).clone())
|
||||
.style(ui::DropdownStyle::Outlined)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_toggle_button_group_inner(
|
||||
title: SharedString,
|
||||
labels: &'static [&'static str],
|
||||
|
||||
@@ -50,6 +50,10 @@ pub fn derive_settings_ui(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit: LitStr = meta.input.parse()?;
|
||||
path_name = Some(lit.value());
|
||||
} else if meta.path.is_ident("render") {
|
||||
// Just consume the tokens even if we don't use them here
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let _lit: LitStr = meta.input.parse()?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@@ -170,6 +174,7 @@ fn generate_ui_item_body(group_name: Option<&String>, input: &syn::DeriveInput)
|
||||
}
|
||||
(None, Data::Enum(data_enum)) => {
|
||||
let serde_attrs = parse_serde_attributes(&input.attrs);
|
||||
let render_as = parse_render_as(&input.attrs);
|
||||
let length = data_enum.variants.len();
|
||||
|
||||
let mut variants = Vec::with_capacity(length);
|
||||
@@ -187,13 +192,19 @@ fn generate_ui_item_body(group_name: Option<&String>, input: &syn::DeriveInput)
|
||||
|
||||
let is_not_union = data_enum.variants.iter().all(|v| v.fields.is_empty());
|
||||
if is_not_union {
|
||||
return if length > 6 {
|
||||
quote! {
|
||||
settings::SettingsUiItem::Single(settings::SettingsUiItemSingle::DropDown{ variants: &[#(#variants),*], labels: &[#(#labels),*] })
|
||||
return match render_as {
|
||||
RenderAs::ToggleGroup if length > 6 => {
|
||||
panic!("Can't set toggle group with more than six entries");
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
settings::SettingsUiItem::Single(settings::SettingsUiItemSingle::ToggleGroup{ variants: &[#(#variants),*], labels: &[#(#labels),*] })
|
||||
RenderAs::ToggleGroup => {
|
||||
quote! {
|
||||
settings::SettingsUiItem::Single(settings::SettingsUiItemSingle::ToggleGroup{ variants: &[#(#variants),*], labels: &[#(#labels),*] })
|
||||
}
|
||||
}
|
||||
RenderAs::Default => {
|
||||
quote! {
|
||||
settings::SettingsUiItem::Single(settings::SettingsUiItemSingle::DropDown{ variants: &[#(#variants),*], labels: &[#(#labels),*] })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -369,6 +380,38 @@ impl SerdeOptions {
|
||||
}
|
||||
}
|
||||
|
||||
enum RenderAs {
|
||||
ToggleGroup,
|
||||
Default,
|
||||
}
|
||||
|
||||
fn parse_render_as(attrs: &[syn::Attribute]) -> RenderAs {
|
||||
let mut render_as = RenderAs::Default;
|
||||
|
||||
for attr in attrs {
|
||||
if !attr.path().is_ident("settings_ui") {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("render") {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit = meta.input.parse::<LitStr>()?.value();
|
||||
|
||||
if lit == "toggle_group" {
|
||||
render_as = RenderAs::ToggleGroup;
|
||||
} else {
|
||||
return Err(meta.error(format!("invalid `render` attribute: {}", lit)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
render_as
|
||||
}
|
||||
|
||||
fn parse_serde_attributes(attrs: &[syn::Attribute]) -> SerdeOptions {
|
||||
let mut options = SerdeOptions {
|
||||
rename_all: SerdeRenameAll::None,
|
||||
|
||||
Reference in New Issue
Block a user