Merge branch 'main' into fix/windows-ctrl-slash

This commit is contained in:
GeekMr authored and GitHub committed 2026-09-27 20:35:45 +08:00
commit 53ba7301f0
4 files changed
+148 -2

No files matched your search

@@ -136,6 +136,7 @@ impl ThemeAdvancedEditor {
let ansi_pickers = (0..ANSI_COUNT)
.map(|index| {
new_color_picker(
ThemeAdvancedField::Ansi(index),
Some(definition.terminal.palette.get(index).unwrap_or([0, 0, 0])),
ansi_inputs[index].clone(),
window,
@@ -145,6 +146,7 @@ impl ThemeAdvancedEditor {
})
.collect();
let selection_background_picker = new_color_picker(
ThemeAdvancedField::SelectionBackground,
definition.terminal.selection_background,
selection_background_input.clone(),
window,
@@ -152,6 +154,7 @@ impl ThemeAdvancedEditor {
&mut picker_subscriptions,
);
let selection_foreground_picker = new_color_picker(
ThemeAdvancedField::SelectionForeground,
definition.terminal.selection_foreground,
selection_foreground_input.clone(),
window,
@@ -159,6 +162,7 @@ impl ThemeAdvancedEditor {
&mut picker_subscriptions,
);
let cursor_text_picker = new_color_picker(
ThemeAdvancedField::CursorText,
definition.terminal.cursor_text,
cursor_text_input.clone(),
window,
@@ -211,6 +215,17 @@ impl ThemeAdvancedEditor {
}
}
#[cfg(test)]
pub(super) fn picker(&self, field: ThemeAdvancedField) -> Option<&Entity<ColorPickerState>> {
match field {
ThemeAdvancedField::Ansi(index) => self.ansi_pickers.get(index),
ThemeAdvancedField::SelectionBackground => Some(&self.selection_background_picker),
ThemeAdvancedField::SelectionForeground => Some(&self.selection_foreground_picker),
ThemeAdvancedField::CursorText => Some(&self.cursor_text_picker),
_ => None,
}
}
/// Replace all control values after a template/reset/import operation.
/// Invalid input errors belong to the old draft and therefore disappear.
pub(super) fn sync_from_definition(
@@ -557,6 +572,7 @@ impl ThemeAdvancedEditor {
}
fn new_color_picker(
field: ThemeAdvancedField,
color: Option<Rgb8>,
input: Entity<InputState>,
window: &mut Window,
@@ -590,6 +606,7 @@ fn new_color_picker(
input.update(cx, |state, cx| {
state.set_value(format_hex_rgb(rgb), window, cx);
});
this.on_theme_advanced_input(field, window, cx);
},
);
subscriptions.push(subscription);
@@ -938,7 +938,7 @@ impl SettingsPane {
cx.notify();
}
fn on_theme_advanced_input(
pub(super) fn on_theme_advanced_input(
&mut self,
field: ThemeAdvancedField,
window: &mut Window,
@@ -964,6 +964,14 @@ impl SettingsPane {
cx.notify();
}
#[cfg(test)]
pub(super) fn theme_advanced_picker(
&self,
field: ThemeAdvancedField,
) -> Option<Entity<gpui_component::color_picker::ColorPickerState>> {
self.theme_editor.as_ref()?.advanced_editor.as_ref()?.picker(field).cloned()
}
fn request_close_theme_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.theme_editor.as_ref().is_some_and(|editor| editor.save_busy) {
return;
@@ -21,6 +21,8 @@ impl SettingsPane {
let foreground = theme_color(draft.terminal.foreground, 1.0);
let accent = theme_color(resolved_ui.accent, 1.0);
let cursor = theme_color(draft.terminal.cursor.unwrap_or(draft.terminal.foreground), 1.0);
let cursor_text =
theme_color(draft.terminal.cursor_text.unwrap_or(draft.terminal.background), 1.0);
let selection_background = theme_color(
draft
.terminal
@@ -233,7 +235,18 @@ impl SettingsPane {
.gap(px(7.0))
.items_center()
.child(div().text_color(accent).child("❯"))
.child(div().w(px(8.0)).h(px(font_size * line_height)).bg(cursor)),
.child(
div()
.debug_selector(|| "theme-editor-preview-cursor".to_owned())
.w(px(font_size * 0.78))
.h(px(font_size * line_height))
.flex()
.items_center()
.justify_center()
.bg(cursor)
.text_color(cursor_text)
.child("A"),
),
)
.into_any_element(),
};
@@ -483,6 +483,28 @@ fn editor_template(pane: &Entity<SettingsPane>, cx: &mut VisualTestContext) -> T
pane.read_with(cx, |pane, _| pane.theme_editor.as_ref().expect("theme editor state").template)
}
fn emit_advanced_picker_color(
pane: &Entity<SettingsPane>,
field: super::theme_advanced::ThemeAdvancedField,
color: [u8; 3],
cx: &mut VisualTestContext,
) {
let picker = pane
.read_with(cx, |pane, _| pane.theme_advanced_picker(field).expect("advanced color picker"));
picker.update(cx, |_, cx| {
let [r, g, b] = color;
let hsla: gpui::Hsla = gpui::Rgba {
r: f32::from(r) / 255.0,
g: f32::from(g) / 255.0,
b: f32::from(b) / 255.0,
a: 1.0,
}
.into();
cx.emit(gpui_component::color_picker::ColorPickerEvent::Change(Some(hsla)));
});
draw(cx);
}
#[gpui::test]
fn theme_editor_opens_from_the_picker_with_common_fields_and_collapsed_advanced(
cx: &mut TestAppContext,
@@ -728,6 +750,92 @@ fn theme_editor_save_and_apply_persists_copy_and_reloads_its_foreground(cx: &mut
assert_eq!(palette_foreground(&mut window), applied.color("terminal", "foreground").unwrap());
}
#[gpui::test]
fn advanced_selection_and_cursor_text_picker_colors_preview_persist_apply_and_reopen(
cx: &mut TestAppContext,
) {
use super::theme_advanced::ThemeAdvancedField;
let _fixture_guard = lock_theme_studio();
let _settings_guard = SettingsBytesGuard::capture();
std::fs::write(nebula_settings::settings_path(), TEST_SETTINGS)
.expect("write isolated advanced color settings");
let mut theme_cleanup = CustomThemeCleanup::default();
let (pane, mut window) = open_settings(cx);
let before_library_ids = custom_theme_ids();
let saved_name = format!("Advanced colors native test {}", std::process::id());
let selection_background = [0x1b, 0x2c, 0x3d];
let selection_foreground = [0xe1, 0xd2, 0xc3];
let cursor_text = [0x12, 0xeb, 0x77];
open_theme_editor(&mut window);
edit_input("theme-editor-name", &saved_name, &mut window);
click("theme-editor-advanced-toggle", &mut window);
assert!(window.debug_bounds("theme-editor-preview-cursor").is_some());
emit_advanced_picker_color(
&pane,
ThemeAdvancedField::SelectionBackground,
selection_background,
&mut window,
);
emit_advanced_picker_color(
&pane,
ThemeAdvancedField::SelectionForeground,
selection_foreground,
&mut window,
);
emit_advanced_picker_color(&pane, ThemeAdvancedField::CursorText, cursor_text, &mut window);
let draft = editor_draft(&pane, &mut window);
assert_eq!(draft.terminal.selection_background, Some(selection_background));
assert_eq!(draft.terminal.selection_foreground, Some(selection_foreground));
assert_eq!(draft.terminal.cursor_text, Some(cursor_text));
click("theme-editor-save-apply", &mut window);
assert!(window.debug_bounds("theme-editor-dialog").is_none());
let after_documents = custom_theme_documents();
for document in &after_documents {
if document.id().is_some_and(|id| !before_library_ids.iter().any(|before| before == id))
&& document.name().starts_with(&saved_name)
{
theme_cleanup.track(document);
}
}
let applied_id = RuntimeSettings::load().custom_theme.expect("advanced color theme is active");
let applied = after_documents
.iter()
.find(|document| document.id() == Some(applied_id.as_str()))
.expect("advanced color theme remains in the library");
let applied_definition = applied.definition().expect("saved advanced color definition");
assert_eq!(applied_definition.terminal.selection_background, Some(selection_background));
assert_eq!(applied_definition.terminal.selection_foreground, Some(selection_foreground));
assert_eq!(applied_definition.terminal.cursor_text, Some(cursor_text));
window.read(|cx| {
let palette = &cx.global::<crate::gpui_shell::config::Settings>().palette;
let bytes = |color: gpui::Rgba| {
[
(color.r * 255.0).round() as u8,
(color.g * 255.0).round() as u8,
(color.b * 255.0).round() as u8,
]
};
assert_eq!(bytes(palette.selection), selection_background);
assert_eq!(palette.selection_foreground.map(bytes), Some(selection_foreground));
assert_eq!(palette.cursor_text.map(bytes), Some(cursor_text));
});
click("open-theme-picker", &mut window);
assert!(window.debug_bounds("appearance-picker-dialog").is_some());
click("customize-theme", &mut window);
let reopened = editor_draft(&pane, &mut window);
assert_eq!(reopened.terminal.selection_background, Some(selection_background));
assert_eq!(reopened.terminal.selection_foreground, Some(selection_foreground));
assert_eq!(reopened.terminal.cursor_text, Some(cursor_text));
}
#[gpui::test]
fn invalid_color_stays_dirty_and_save_rejects_after_a_later_valid_edit(cx: &mut TestAppContext) {
let _fixture_guard = lock_theme_studio();