T18: Terminal: scrollback lines, Option as Alt on macOS, copy on select, each applied at once to every session; the settings pages share one switch and one segmented choice
This commit is contained in:
4 files changed
+122
-46
No files matched your search
+54
-2
@@ -1,7 +1,9 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::SegmentedControl,
|
||||
feedback::InlineMessage,
|
||||
forms::{InputEvent, PathInput, TextInput},
|
||||
forms::{InputEvent, PathInput, Switch, TextInput},
|
||||
primitives::{FocusScope, IconName, Severity},
|
||||
settings::{SettingsLayout, SettingsRow, SettingsSection},
|
||||
shell::{ColumnShell, ResizeBorder, ShellColumn},
|
||||
@@ -18,6 +20,7 @@ use super::{Settings, SettingsStore, StartIn};
|
||||
use crate::term::BLINK;
|
||||
|
||||
mod appearance;
|
||||
mod terminal;
|
||||
|
||||
actions!(elyterminal, [OpenSettings, CloseSettings]);
|
||||
|
||||
@@ -25,9 +28,10 @@ actions!(elyterminal, [OpenSettings, CloseSettings]);
|
||||
const CONTEXT: &str = "Settings";
|
||||
|
||||
/// Each category's key, name and icon.
|
||||
const SECTIONS: [(&str, &str, IconName); 2] = [
|
||||
const SECTIONS: [(&str, &str, IconName); 3] = [
|
||||
("general", "General", IconName::Settings),
|
||||
("appearance", "Appearance", IconName::Palette),
|
||||
("terminal", "Terminal", IconName::Terminal),
|
||||
];
|
||||
|
||||
/// Cmd-, opens the settings, Cmd-W closes them; off macOS Ctrl-, and Ctrl-Shift-W, as Ghostty's.
|
||||
@@ -204,6 +208,53 @@ fn start_in(this: &Entity<SettingsWindow>, value: &SharedString, cx: &mut App) {
|
||||
);
|
||||
}
|
||||
|
||||
/// A switch that saves through `put`.
|
||||
fn switch(
|
||||
this: &Entity<SettingsWindow>,
|
||||
field: &'static str,
|
||||
on: bool,
|
||||
put: fn(&mut Settings, bool),
|
||||
) -> Switch {
|
||||
let this = this.clone();
|
||||
Switch::new(field, on).on_change(move |on, _, cx| {
|
||||
set(&this, format!("{field} is {on}"), cx, |settings| {
|
||||
put(settings, on)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Segments for `choices`, each a value, its key and its name, that save the chosen value through `put`.
|
||||
fn choice<T: Copy + PartialEq + Debug + 'static>(
|
||||
this: &Entity<SettingsWindow>,
|
||||
field: &'static str,
|
||||
choices: &'static [(T, &'static str, &'static str)],
|
||||
now: T,
|
||||
put: fn(&mut Settings, T),
|
||||
) -> SegmentedControl {
|
||||
let (_, key, _) = choices
|
||||
.iter()
|
||||
.find(|(each, ..)| *each == now)
|
||||
.expect("every choice is offered");
|
||||
let this = this.clone();
|
||||
choices
|
||||
.iter()
|
||||
.fold(
|
||||
SegmentedControl::new(field, *key),
|
||||
|control, (_, key, name)| control.segment(*key, *name, None),
|
||||
)
|
||||
.size(ControlSize::Sm)
|
||||
.on_change(move |key, _, cx| {
|
||||
let (value, ..) = choices
|
||||
.iter()
|
||||
.find(|(_, each, _)| *each == key.as_ref())
|
||||
.expect("a choice the control offers");
|
||||
let value = *value;
|
||||
set(&this, format!("{field} is {value:?}"), cx, |settings| {
|
||||
put(settings, value)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Saves one change and logs it, telling whether the settings hold it; a refused one changes nothing, and the window says why.
|
||||
fn set(
|
||||
this: &Entity<SettingsWindow>,
|
||||
@@ -241,6 +292,7 @@ impl Render for SettingsWindow {
|
||||
let page = match self.section.as_ref() {
|
||||
"general" => self.general(cx).into_any_element(),
|
||||
"appearance" => self.appearance(window, cx).into_any_element(),
|
||||
"terminal" => self.terminal(cx).into_any_element(),
|
||||
other => unreachable!("settings: no section {other}"),
|
||||
};
|
||||
let page = div()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::SegmentedControl,
|
||||
forms::{FontPicker, NumberInput, RadioCard, Slider, Switch},
|
||||
layout::SimpleGrid,
|
||||
settings::{SettingsRow, SettingsSection},
|
||||
@@ -12,7 +11,7 @@ use gpui::{
|
||||
px, relative, rems,
|
||||
};
|
||||
|
||||
use super::{SettingsWindow, set};
|
||||
use super::{SettingsWindow, choice, set, switch};
|
||||
use crate::{
|
||||
settings::{CursorStyle, FONT_SIZE, LINE_HEIGHT, OPACITY, PADDING, Settings, SettingsStore},
|
||||
themes::{self, TerminalTheme},
|
||||
@@ -97,33 +96,13 @@ impl SettingsWindow {
|
||||
settings.ligatures,
|
||||
|settings, on| settings.ligatures = on,
|
||||
)));
|
||||
let (_, now, _) = CURSORS
|
||||
.iter()
|
||||
.find(|(style, ..)| *style == settings.cursor_style)
|
||||
.expect("every cursor style is offered");
|
||||
let style = CURSORS
|
||||
.iter()
|
||||
.fold(
|
||||
SegmentedControl::new("cursor_style", *now),
|
||||
|control, (_, key, name)| control.segment(*key, *name, None),
|
||||
)
|
||||
.size(ControlSize::Sm)
|
||||
.on_change({
|
||||
let this = this.clone();
|
||||
move |key, _, cx| {
|
||||
let (style, ..) = CURSORS
|
||||
.iter()
|
||||
.find(|(_, each, _)| *each == key.as_ref())
|
||||
.expect("a cursor style the control offers");
|
||||
let style = *style;
|
||||
set(
|
||||
&this,
|
||||
format!("cursor_style is {style:?}"),
|
||||
cx,
|
||||
|settings| settings.cursor_style = style,
|
||||
);
|
||||
}
|
||||
});
|
||||
let style = choice(
|
||||
&this,
|
||||
"cursor_style",
|
||||
&CURSORS,
|
||||
settings.cursor_style,
|
||||
|settings, style| settings.cursor_style = style,
|
||||
);
|
||||
let active = themes::active(settings, window.appearance());
|
||||
let lit = self.lit || !settings.cursor_blink;
|
||||
let cursor = SettingsSection::new("Cursor")
|
||||
@@ -228,20 +207,6 @@ fn fraction(percent: f64) -> f32 {
|
||||
(percent / 100.) as f32
|
||||
}
|
||||
|
||||
fn switch(
|
||||
this: &Entity<SettingsWindow>,
|
||||
field: &'static str,
|
||||
on: bool,
|
||||
put: fn(&mut Settings, bool),
|
||||
) -> Switch {
|
||||
let this = this.clone();
|
||||
Switch::new(field, on).on_change(move |on, _, cx| {
|
||||
set(&this, format!("{field} is {on}"), cx, |settings| {
|
||||
put(settings, on)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Every built-in theme as a card over its preview; `dark` picks the one a dark system wears.
|
||||
fn grid(
|
||||
this: &Entity<SettingsWindow>,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
use ely_gpui_component::{
|
||||
forms::NumberInput,
|
||||
settings::{SettingsRow, SettingsSection},
|
||||
theme::ControlSize,
|
||||
};
|
||||
use gpui::Context;
|
||||
|
||||
use super::{SettingsWindow, choice, set, switch};
|
||||
use crate::settings::{OptionAsAlt, SCROLLBACK, SettingsStore};
|
||||
|
||||
/// Each way Option types Alt, its key and its name.
|
||||
const OPTIONS: [(OptionAsAlt, &str, &str); 4] = [
|
||||
(OptionAsAlt::Off, "off", "Off"),
|
||||
(OptionAsAlt::Left, "left", "Left"),
|
||||
(OptionAsAlt::Right, "right", "Right"),
|
||||
(OptionAsAlt::Both, "both", "Both"),
|
||||
];
|
||||
|
||||
impl SettingsWindow {
|
||||
/// Scrollback, Option as Alt on macOS, and copy on select.
|
||||
pub(super) fn terminal(&self, cx: &Context<Self>) -> SettingsSection {
|
||||
let settings = SettingsStore::get(cx);
|
||||
let this = cx.entity();
|
||||
let lines = NumberInput::new(("scrollback", self.drafts), f64::from(settings.scrollback))
|
||||
.range(f64::from(*SCROLLBACK.start()), f64::from(*SCROLLBACK.end()))
|
||||
.step(1000.)
|
||||
.size(ControlSize::Sm)
|
||||
.on_commit({
|
||||
let this = this.clone();
|
||||
move |lines, _, cx| {
|
||||
let lines = lines as u32;
|
||||
set(&this, format!("scrollback is {lines}"), cx, |settings| {
|
||||
settings.scrollback = lines
|
||||
});
|
||||
}
|
||||
});
|
||||
let mut section = SettingsSection::new("Terminal")
|
||||
.row(SettingsRow::new("Scrollback lines").control(lines));
|
||||
// Off macOS, Alt is always Alt.
|
||||
if cfg!(target_os = "macos") {
|
||||
let option = choice(
|
||||
&this,
|
||||
"option_as_alt",
|
||||
&OPTIONS,
|
||||
settings.option_as_alt,
|
||||
|settings, option| settings.option_as_alt = option,
|
||||
);
|
||||
section = section.row(SettingsRow::new("Option as Alt").control(option));
|
||||
}
|
||||
let copy = switch(
|
||||
&this,
|
||||
"copy_on_select",
|
||||
settings.copy_on_select,
|
||||
|settings, on| settings.copy_on_select = on,
|
||||
);
|
||||
section.row(SettingsRow::new("Copy on select").control(copy))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user