mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 02:17:35 +03:00
settings_ui: Add theme settings controls (#15115)
This PR adds settings controls for the theme settings. Release Notes: - N/A
This commit is contained in:
parent
325e6b9fef
commit
740c444089
@ -44,7 +44,11 @@ impl EditableSettingControl for BufferFontSizeControl {
|
|||||||
settings.buffer_font_size
|
settings.buffer_font_size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) {
|
||||||
settings.buffer_font_size = Some(value.into());
|
settings.buffer_font_size = Some(value.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +88,11 @@ impl EditableSettingControl for BufferFontWeightControl {
|
|||||||
settings.buffer_font.weight
|
settings.buffer_font.weight
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) {
|
||||||
settings.buffer_font_weight = Some(value.0);
|
settings.buffer_font_weight = Some(value.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +141,11 @@ impl EditableSettingControl for InlineGitBlameControl {
|
|||||||
settings.git.inline_blame_enabled()
|
settings.git.inline_blame_enabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) {
|
||||||
if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
|
if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
|
||||||
inline_blame.enabled = value;
|
inline_blame.enabled = value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,14 +20,18 @@ pub trait EditableSettingControl: RenderOnce {
|
|||||||
/// Applies the given setting file to the settings file contents.
|
/// Applies the given setting file to the settings file contents.
|
||||||
///
|
///
|
||||||
/// This will be called when writing the setting value back to the settings file.
|
/// This will be called when writing the setting value back to the settings file.
|
||||||
fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value);
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
cx: &AppContext,
|
||||||
|
);
|
||||||
|
|
||||||
/// Writes the given setting value to the settings files.
|
/// Writes the given setting value to the settings files.
|
||||||
fn write(value: Self::Value, cx: &AppContext) {
|
fn write(value: Self::Value, cx: &AppContext) {
|
||||||
let fs = <dyn Fs>::global(cx);
|
let fs = <dyn Fs>::global(cx);
|
||||||
|
|
||||||
update_settings_file::<Self::Settings>(fs, cx, move |settings, _cx| {
|
update_settings_file::<Self::Settings>(fs, cx, move |settings, cx| {
|
||||||
Self::apply(settings, value);
|
Self::apply(settings, value, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
257
crates/settings_ui/src/appearance_settings_controls.rs
Normal file
257
crates/settings_ui/src/appearance_settings_controls.rs
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
use gpui::{AppContext, FontWeight};
|
||||||
|
use settings::{EditableSettingControl, Settings};
|
||||||
|
use theme::{SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings};
|
||||||
|
use ui::{
|
||||||
|
prelude::*, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup,
|
||||||
|
ToggleButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct AppearanceSettingsControls {}
|
||||||
|
|
||||||
|
impl AppearanceSettingsControls {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for AppearanceSettingsControls {
|
||||||
|
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
SettingsContainer::new()
|
||||||
|
.child(
|
||||||
|
SettingsGroup::new("Theme").child(
|
||||||
|
h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.justify_between()
|
||||||
|
.child(ThemeControl)
|
||||||
|
.child(ThemeModeControl),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
SettingsGroup::new("Font")
|
||||||
|
.child(UiFontSizeControl)
|
||||||
|
.child(UiFontWeightControl),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
struct ThemeControl;
|
||||||
|
|
||||||
|
impl EditableSettingControl for ThemeControl {
|
||||||
|
type Value = String;
|
||||||
|
type Settings = ThemeSettings;
|
||||||
|
|
||||||
|
fn name(&self) -> SharedString {
|
||||||
|
"Theme".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(cx: &AppContext) -> Self::Value {
|
||||||
|
let settings = ThemeSettings::get_global(cx);
|
||||||
|
let appearance = SystemAppearance::global(cx);
|
||||||
|
settings
|
||||||
|
.theme_selection
|
||||||
|
.as_ref()
|
||||||
|
.map(|selection| selection.theme(appearance.0).to_string())
|
||||||
|
.unwrap_or_else(|| ThemeSettings::default_theme(*appearance).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
cx: &AppContext,
|
||||||
|
) {
|
||||||
|
let appearance = SystemAppearance::global(cx);
|
||||||
|
settings.set_theme(value, appearance.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for ThemeControl {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let value = Self::read(cx);
|
||||||
|
|
||||||
|
DropdownMenu::new(
|
||||||
|
"theme",
|
||||||
|
value.clone(),
|
||||||
|
ContextMenu::build(cx, |mut menu, cx| {
|
||||||
|
let theme_registry = ThemeRegistry::global(cx);
|
||||||
|
|
||||||
|
for theme in theme_registry.list_names(false) {
|
||||||
|
menu = menu.custom_entry(
|
||||||
|
{
|
||||||
|
let theme = theme.clone();
|
||||||
|
move |_cx| Label::new(theme.clone()).into_any_element()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let theme = theme.clone();
|
||||||
|
move |cx| {
|
||||||
|
Self::write(theme.to_string(), cx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
menu
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.full_width(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
struct ThemeModeControl;
|
||||||
|
|
||||||
|
impl EditableSettingControl for ThemeModeControl {
|
||||||
|
type Value = ThemeMode;
|
||||||
|
type Settings = ThemeSettings;
|
||||||
|
|
||||||
|
fn name(&self) -> SharedString {
|
||||||
|
"Theme Mode".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(cx: &AppContext) -> Self::Value {
|
||||||
|
let settings = ThemeSettings::get_global(cx);
|
||||||
|
settings
|
||||||
|
.theme_selection
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|selection| selection.mode())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) {
|
||||||
|
settings.set_mode(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for ThemeModeControl {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let value = Self::read(cx);
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("light", "Light")
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.selected(value == ThemeMode::Light)
|
||||||
|
.on_click(|_, cx| Self::write(ThemeMode::Light, cx))
|
||||||
|
.first(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("system", "System")
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.selected(value == ThemeMode::System)
|
||||||
|
.on_click(|_, cx| Self::write(ThemeMode::System, cx))
|
||||||
|
.middle(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
ToggleButton::new("dark", "Dark")
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.size(ButtonSize::Large)
|
||||||
|
.selected(value == ThemeMode::Dark)
|
||||||
|
.on_click(|_, cx| Self::write(ThemeMode::Dark, cx))
|
||||||
|
.last(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
struct UiFontSizeControl;
|
||||||
|
|
||||||
|
impl EditableSettingControl for UiFontSizeControl {
|
||||||
|
type Value = Pixels;
|
||||||
|
type Settings = ThemeSettings;
|
||||||
|
|
||||||
|
fn name(&self) -> SharedString {
|
||||||
|
"UI Font Size".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(cx: &AppContext) -> Self::Value {
|
||||||
|
let settings = ThemeSettings::get_global(cx);
|
||||||
|
settings.ui_font_size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) {
|
||||||
|
settings.ui_font_size = Some(value.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for UiFontSizeControl {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let value = Self::read(cx);
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(Icon::new(IconName::FontSize))
|
||||||
|
.child(NumericStepper::new(
|
||||||
|
value.to_string(),
|
||||||
|
move |_, cx| {
|
||||||
|
Self::write(value - px(1.), cx);
|
||||||
|
},
|
||||||
|
move |_, cx| {
|
||||||
|
Self::write(value + px(1.), cx);
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
struct UiFontWeightControl;
|
||||||
|
|
||||||
|
impl EditableSettingControl for UiFontWeightControl {
|
||||||
|
type Value = FontWeight;
|
||||||
|
type Settings = ThemeSettings;
|
||||||
|
|
||||||
|
fn name(&self) -> SharedString {
|
||||||
|
"UI Font Weight".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(cx: &AppContext) -> Self::Value {
|
||||||
|
let settings = ThemeSettings::get_global(cx);
|
||||||
|
settings.ui_font.weight
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(
|
||||||
|
settings: &mut <Self::Settings as Settings>::FileContent,
|
||||||
|
value: Self::Value,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) {
|
||||||
|
settings.ui_font_weight = Some(value.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for UiFontWeightControl {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let value = Self::read(cx);
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(Icon::new(IconName::FontWeight))
|
||||||
|
.child(DropdownMenu::new(
|
||||||
|
"ui-font-weight",
|
||||||
|
value.0.to_string(),
|
||||||
|
ContextMenu::build(cx, |mut menu, _cx| {
|
||||||
|
for weight in FontWeight::ALL {
|
||||||
|
menu = menu.custom_entry(
|
||||||
|
move |_cx| Label::new(weight.0.to_string()).into_any_element(),
|
||||||
|
{
|
||||||
|
move |cx| {
|
||||||
|
Self::write(weight, cx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
menu
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
mod theme_settings_controls;
|
mod appearance_settings_controls;
|
||||||
|
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ use ui::prelude::*;
|
|||||||
use workspace::item::{Item, ItemEvent};
|
use workspace::item::{Item, ItemEvent};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::theme_settings_controls::ThemeSettingsControls;
|
use crate::appearance_settings_controls::AppearanceSettingsControls;
|
||||||
|
|
||||||
pub struct SettingsUiFeatureFlag;
|
pub struct SettingsUiFeatureFlag;
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ impl Render for SettingsPage {
|
|||||||
v_flex()
|
v_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(Label::new("Appearance"))
|
.child(Label::new("Appearance"))
|
||||||
.child(ThemeSettingsControls::new()),
|
.child(AppearanceSettingsControls::new()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
use gpui::{AppContext, FontWeight};
|
|
||||||
use settings::{EditableSettingControl, Settings};
|
|
||||||
use theme::ThemeSettings;
|
|
||||||
use ui::{prelude::*, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup};
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct ThemeSettingsControls {}
|
|
||||||
|
|
||||||
impl ThemeSettingsControls {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for ThemeSettingsControls {
|
|
||||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
SettingsContainer::new().child(
|
|
||||||
SettingsGroup::new("Font")
|
|
||||||
.child(UiFontSizeControl)
|
|
||||||
.child(UiFontWeightControl),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
struct UiFontSizeControl;
|
|
||||||
|
|
||||||
impl EditableSettingControl for UiFontSizeControl {
|
|
||||||
type Value = Pixels;
|
|
||||||
type Settings = ThemeSettings;
|
|
||||||
|
|
||||||
fn name(&self) -> SharedString {
|
|
||||||
"UI Font Size".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(cx: &AppContext) -> Self::Value {
|
|
||||||
let settings = ThemeSettings::get_global(cx);
|
|
||||||
settings.ui_font_size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
|
|
||||||
settings.ui_font_size = Some(value.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for UiFontSizeControl {
|
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
let value = Self::read(cx);
|
|
||||||
|
|
||||||
h_flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(Icon::new(IconName::FontSize))
|
|
||||||
.child(NumericStepper::new(
|
|
||||||
value.to_string(),
|
|
||||||
move |_, cx| {
|
|
||||||
Self::write(value - px(1.), cx);
|
|
||||||
},
|
|
||||||
move |_, cx| {
|
|
||||||
Self::write(value + px(1.), cx);
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
struct UiFontWeightControl;
|
|
||||||
|
|
||||||
impl EditableSettingControl for UiFontWeightControl {
|
|
||||||
type Value = FontWeight;
|
|
||||||
type Settings = ThemeSettings;
|
|
||||||
|
|
||||||
fn name(&self) -> SharedString {
|
|
||||||
"UI Font Weight".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(cx: &AppContext) -> Self::Value {
|
|
||||||
let settings = ThemeSettings::get_global(cx);
|
|
||||||
settings.ui_font.weight
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
|
|
||||||
settings.ui_font_weight = Some(value.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for UiFontWeightControl {
|
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
let value = Self::read(cx);
|
|
||||||
|
|
||||||
h_flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(Icon::new(IconName::FontWeight))
|
|
||||||
.child(DropdownMenu::new(
|
|
||||||
"ui-font-weight",
|
|
||||||
value.0.to_string(),
|
|
||||||
ContextMenu::build(cx, |mut menu, _cx| {
|
|
||||||
for weight in FontWeight::ALL {
|
|
||||||
menu = menu.custom_entry(
|
|
||||||
move |_cx| Label::new(weight.0.to_string()).into_any_element(),
|
|
||||||
{
|
|
||||||
move |cx| {
|
|
||||||
Self::write(weight, cx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
menu
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
@ -94,6 +94,17 @@ pub struct ThemeSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ThemeSettings {
|
impl ThemeSettings {
|
||||||
|
const DEFAULT_LIGHT_THEME: &'static str = "One Light";
|
||||||
|
const DEFAULT_DARK_THEME: &'static str = "One Dark";
|
||||||
|
|
||||||
|
/// Returns the name of the default theme for the given [`Appearance`].
|
||||||
|
pub fn default_theme(appearance: Appearance) -> &'static str {
|
||||||
|
match appearance {
|
||||||
|
Appearance::Light => Self::DEFAULT_LIGHT_THEME,
|
||||||
|
Appearance::Dark => Self::DEFAULT_DARK_THEME,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reloads the current theme.
|
/// Reloads the current theme.
|
||||||
///
|
///
|
||||||
/// Reads the [`ThemeSettings`] to know which theme should be loaded,
|
/// Reads the [`ThemeSettings`] to know which theme should be loaded,
|
||||||
@ -109,10 +120,7 @@ impl ThemeSettings {
|
|||||||
// based on the system appearance.
|
// based on the system appearance.
|
||||||
let theme_registry = ThemeRegistry::global(cx);
|
let theme_registry = ThemeRegistry::global(cx);
|
||||||
if theme_registry.get(theme_name).ok().is_none() {
|
if theme_registry.get(theme_name).ok().is_none() {
|
||||||
theme_name = match *system_appearance {
|
theme_name = Self::default_theme(*system_appearance);
|
||||||
Appearance::Light => "One Light",
|
|
||||||
Appearance::Dark => "One Dark",
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(_theme) = theme_settings.switch_theme(theme_name, cx) {
|
if let Some(_theme) = theme_settings.switch_theme(theme_name, cx) {
|
||||||
@ -190,7 +198,7 @@ fn theme_name_ref(_: &mut SchemaGenerator) -> Schema {
|
|||||||
Schema::new_ref("#/definitions/ThemeName".into())
|
Schema::new_ref("#/definitions/ThemeName".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ThemeMode {
|
pub enum ThemeMode {
|
||||||
/// Use the specified `light` theme.
|
/// Use the specified `light` theme.
|
||||||
@ -218,6 +226,13 @@ impl ThemeSelection {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mode(&self) -> Option<ThemeMode> {
|
||||||
|
match self {
|
||||||
|
ThemeSelection::Static(_) => None,
|
||||||
|
ThemeSelection::Dynamic { mode, .. } => Some(*mode),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Settings for rendering text in UI and text buffers.
|
/// Settings for rendering text in UI and text buffers.
|
||||||
@ -267,6 +282,56 @@ pub struct ThemeSettingsContent {
|
|||||||
pub theme_overrides: Option<ThemeStyleContent>,
|
pub theme_overrides: Option<ThemeStyleContent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ThemeSettingsContent {
|
||||||
|
/// Sets the theme for the given appearance to the theme with the specified name.
|
||||||
|
pub fn set_theme(&mut self, theme_name: String, appearance: Appearance) {
|
||||||
|
if let Some(selection) = self.theme.as_mut() {
|
||||||
|
let theme_to_update = match selection {
|
||||||
|
ThemeSelection::Static(theme) => theme,
|
||||||
|
ThemeSelection::Dynamic { mode, light, dark } => match mode {
|
||||||
|
ThemeMode::Light => light,
|
||||||
|
ThemeMode::Dark => dark,
|
||||||
|
ThemeMode::System => match appearance {
|
||||||
|
Appearance::Light => light,
|
||||||
|
Appearance::Dark => dark,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
*theme_to_update = theme_name.to_string();
|
||||||
|
} else {
|
||||||
|
self.theme = Some(ThemeSelection::Static(theme_name.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mode(&mut self, mode: ThemeMode) {
|
||||||
|
if let Some(selection) = self.theme.as_mut() {
|
||||||
|
match selection {
|
||||||
|
ThemeSelection::Static(theme) => {
|
||||||
|
// If the theme was previously set to a single static theme,
|
||||||
|
// we don't know whether it was a light or dark theme, so we
|
||||||
|
// just use it for both.
|
||||||
|
self.theme = Some(ThemeSelection::Dynamic {
|
||||||
|
mode,
|
||||||
|
light: theme.clone(),
|
||||||
|
dark: theme.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ThemeSelection::Dynamic {
|
||||||
|
mode: mode_to_update,
|
||||||
|
..
|
||||||
|
} => *mode_to_update = mode,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.theme = Some(ThemeSelection::Dynamic {
|
||||||
|
mode,
|
||||||
|
light: ThemeSettings::DEFAULT_LIGHT_THEME.into(),
|
||||||
|
dark: ThemeSettings::DEFAULT_DARK_THEME.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum BufferLineHeight {
|
pub enum BufferLineHeight {
|
||||||
|
@ -10,9 +10,7 @@ use picker::{Picker, PickerDelegate};
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use settings::{update_settings_file, SettingsStore};
|
use settings::{update_settings_file, SettingsStore};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::{
|
use theme::{Appearance, Theme, ThemeMeta, ThemeRegistry, ThemeSettings};
|
||||||
Appearance, Theme, ThemeMeta, ThemeMode, ThemeRegistry, ThemeSelection, ThemeSettings,
|
|
||||||
};
|
|
||||||
use ui::{prelude::*, v_flex, ListItem, ListItemSpacing};
|
use ui::{prelude::*, v_flex, ListItem, ListItemSpacing};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{ui::HighlightedLabel, ModalView, Workspace};
|
use workspace::{ui::HighlightedLabel, ModalView, Workspace};
|
||||||
@ -197,23 +195,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
|
|||||||
let appearance = Appearance::from(cx.appearance());
|
let appearance = Appearance::from(cx.appearance());
|
||||||
|
|
||||||
update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
|
update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
|
||||||
if let Some(selection) = settings.theme.as_mut() {
|
settings.set_theme(theme_name.to_string(), appearance);
|
||||||
let theme_to_update = match selection {
|
|
||||||
ThemeSelection::Static(theme) => theme,
|
|
||||||
ThemeSelection::Dynamic { mode, light, dark } => match mode {
|
|
||||||
ThemeMode::Light => light,
|
|
||||||
ThemeMode::Dark => dark,
|
|
||||||
ThemeMode::System => match appearance {
|
|
||||||
Appearance::Light => light,
|
|
||||||
Appearance::Dark => dark,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
*theme_to_update = theme_name.to_string();
|
|
||||||
} else {
|
|
||||||
settings.theme = Some(ThemeSelection::Static(theme_name.to_string()));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.view
|
self.view
|
||||||
|
@ -38,6 +38,7 @@ impl RenderOnce for ApplicationMenu {
|
|||||||
))
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.reserve_space_for_reset(true)
|
||||||
.when(
|
.when(
|
||||||
theme::has_adjusted_buffer_font_size(cx),
|
theme::has_adjusted_buffer_font_size(cx),
|
||||||
|stepper| {
|
|stepper| {
|
||||||
@ -72,6 +73,7 @@ impl RenderOnce for ApplicationMenu {
|
|||||||
))
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.reserve_space_for_reset(true)
|
||||||
.when(
|
.when(
|
||||||
theme::has_adjusted_ui_font_size(cx),
|
theme::has_adjusted_ui_font_size(cx),
|
||||||
|stepper| {
|
|stepper| {
|
||||||
|
@ -42,8 +42,9 @@ impl Disableable for DropdownMenu {
|
|||||||
impl RenderOnce for DropdownMenu {
|
impl RenderOnce for DropdownMenu {
|
||||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||||
PopoverMenu::new(self.id)
|
PopoverMenu::new(self.id)
|
||||||
|
.full_width(self.full_width)
|
||||||
.menu(move |_cx| Some(self.menu.clone()))
|
.menu(move |_cx| Some(self.menu.clone()))
|
||||||
.trigger(DropdownMenuTrigger::new(self.label))
|
.trigger(DropdownMenuTrigger::new(self.label).full_width(self.full_width))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +69,11 @@ impl DropdownMenuTrigger {
|
|||||||
on_click: None,
|
on_click: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn full_width(mut self, full_width: bool) -> Self {
|
||||||
|
self.full_width = full_width;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Disableable for DropdownMenuTrigger {
|
impl Disableable for DropdownMenuTrigger {
|
||||||
|
@ -7,6 +7,8 @@ pub struct NumericStepper {
|
|||||||
value: SharedString,
|
value: SharedString,
|
||||||
on_decrement: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
|
on_decrement: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
|
||||||
on_increment: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
|
on_increment: Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
|
||||||
|
/// Whether to reserve space for the reset button.
|
||||||
|
reserve_space_for_reset: bool,
|
||||||
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,10 +22,16 @@ impl NumericStepper {
|
|||||||
value: value.into(),
|
value: value.into(),
|
||||||
on_decrement: Box::new(on_decrement),
|
on_decrement: Box::new(on_decrement),
|
||||||
on_increment: Box::new(on_increment),
|
on_increment: Box::new(on_increment),
|
||||||
|
reserve_space_for_reset: false,
|
||||||
on_reset: None,
|
on_reset: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reserve_space_for_reset(mut self, reserve_space_for_reset: bool) -> Self {
|
||||||
|
self.reserve_space_for_reset = reserve_space_for_reset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_reset(
|
pub fn on_reset(
|
||||||
mut self,
|
mut self,
|
||||||
on_reset: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
on_reset: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
||||||
@ -48,13 +56,15 @@ impl RenderOnce for NumericStepper {
|
|||||||
.icon_size(icon_size)
|
.icon_size(icon_size)
|
||||||
.on_click(on_reset),
|
.on_click(on_reset),
|
||||||
)
|
)
|
||||||
} else {
|
} else if self.reserve_space_for_reset {
|
||||||
element.child(
|
element.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.size(icon_size.square(cx))
|
.size(icon_size.square(cx))
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
element
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
anchored, deferred, div, point, prelude::FluentBuilder, px, AnchorCorner, AnyElement, Bounds,
|
anchored, deferred, div, point, prelude::FluentBuilder, px, size, AnchorCorner, AnyElement,
|
||||||
DismissEvent, DispatchPhase, Element, ElementId, GlobalElementId, HitboxId, InteractiveElement,
|
Bounds, DismissEvent, DispatchPhase, Element, ElementId, GlobalElementId, HitboxId,
|
||||||
IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
|
InteractiveElement, IntoElement, LayoutId, Length, ManagedView, MouseDownEvent, ParentElement,
|
||||||
VisualContext, WindowContext,
|
Pixels, Point, Style, View, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -74,6 +74,7 @@ pub struct PopoverMenu<M: ManagedView> {
|
|||||||
attach: Option<AnchorCorner>,
|
attach: Option<AnchorCorner>,
|
||||||
offset: Option<Point<Pixels>>,
|
offset: Option<Point<Pixels>>,
|
||||||
trigger_handle: Option<PopoverMenuHandle<M>>,
|
trigger_handle: Option<PopoverMenuHandle<M>>,
|
||||||
|
full_width: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ManagedView> PopoverMenu<M> {
|
impl<M: ManagedView> PopoverMenu<M> {
|
||||||
@ -87,9 +88,15 @@ impl<M: ManagedView> PopoverMenu<M> {
|
|||||||
attach: None,
|
attach: None,
|
||||||
offset: None,
|
offset: None,
|
||||||
trigger_handle: None,
|
trigger_handle: None,
|
||||||
|
full_width: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn full_width(mut self, full_width: bool) -> Self {
|
||||||
|
self.full_width = full_width;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> Option<View<M>> + 'static) -> Self {
|
pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> Option<View<M>> + 'static) -> Self {
|
||||||
self.menu_builder = Some(Rc::new(f));
|
self.menu_builder = Some(Rc::new(f));
|
||||||
self
|
self
|
||||||
@ -258,10 +265,13 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.map(|child_element| child_element.request_layout(cx));
|
.map(|child_element| child_element.request_layout(cx));
|
||||||
|
|
||||||
let layout_id = cx.request_layout(
|
let mut style = Style::default();
|
||||||
gpui::Style::default(),
|
if self.full_width {
|
||||||
menu_layout_id.into_iter().chain(child_layout_id),
|
style.size = size(relative(1.).into(), Length::Auto);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
let layout_id =
|
||||||
|
cx.request_layout(style, menu_layout_id.into_iter().chain(child_layout_id));
|
||||||
|
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
|
Loading…
Reference in New Issue
Block a user