style: rustfmt app.rs/dialogs.rs (no behavior change)

The two files carried hand-wrapped lines that predate the workspace
tab policy; normalize them so the feature commits on top stay
reviewable.
This commit is contained in:
2026-08-25 01:32:56 +08:00
parent dfbb0eaec5
commit 72416e621e
2 changed files with 613 additions and 432 deletions
+342 -229
View File
File diff suppressed because it is too large Load Diff
+271 -203
View File
@@ -25,31 +25,30 @@
//! additionally emit a [`PreferencesEvent`] so the host can re-apply the
//! shell chrome immediately.
use crate::oakui::component::controls::SliderModel;
use crate::oakui::component::controls::ValueKind;
use crate::oakui::component::controls::{CheckBox, CheckBoxEvent, CheckState};
use crate::oakui::component::controls::{ComboBox, ComboBoxEvent, ComboBoxOption};
use crate::oakui::component::controls::{SpinBox, SpinBoxEvent};
use crate::oakui::component::text_input;
use gpui::colors::DefaultColors;
use gpui::prelude::*;
use gpui::{
div, px, App, Context, ElementId, Entity, EventEmitter, Focusable, FocusHandle, Keystroke,
div, px, App, Context, ElementId, Entity, EventEmitter, FocusHandle, Focusable, Keystroke,
PathPromptOptions, Render, SharedString, Window,
};
use gpui_elements::editable_text::{EditableTextState, StringStorage, TextChanged};
use crate::oakui::component::controls::{CheckBox, CheckBoxEvent, CheckState};
use crate::oakui::component::controls::{ComboBox, ComboBoxEvent, ComboBoxOption};
use crate::oakui::component::controls::SliderModel;
use crate::oakui::component::controls::{SpinBox, SpinBoxEvent};
use crate::oakui::component::controls::ValueKind;
use crate::oakui::component::text_input;
use crate::actions::ActionId;
use crate::i18n;
use crate::oakui::real::{
audio_input_device, audio_input_devices, audio_output_device, audio_output_devices,
config_get_bool, config_get_int, config_get_string, config_set_bool, config_set_int,
config_set_string, encoding_formats, proxy_dividers, renderer_backends,
set_audio_input_device, set_audio_output_device, set_theme_dark, theme_is_dark,
CONFIG_KEY_DEFAULT_TRANSITION_SEC, CONFIG_KEY_DISK_CACHE_PATH, CONFIG_KEY_FFMPEG_PATH,
CONFIG_KEY_PROXY_DIVIDER, CONFIG_KEY_RENDERER_BACKEND, CONFIG_KEY_SNAPSHOT_INTERVAL_SEC,
CONFIG_KEY_USE_PROXY, DEFAULT_SNAPSHOT_INTERVAL_SEC, DEFAULT_TRANSITION_SEC,
EXPORT_FORMAT_MP4,
config_set_string, encoding_formats, proxy_dividers, renderer_backends, set_audio_input_device,
set_audio_output_device, set_theme_dark, theme_is_dark, CONFIG_KEY_DEFAULT_TRANSITION_SEC,
CONFIG_KEY_DISK_CACHE_PATH, CONFIG_KEY_FFMPEG_PATH, CONFIG_KEY_PROXY_DIVIDER,
CONFIG_KEY_RENDERER_BACKEND, CONFIG_KEY_SNAPSHOT_INTERVAL_SEC, CONFIG_KEY_USE_PROXY,
DEFAULT_SNAPSHOT_INTERVAL_SEC, DEFAULT_TRANSITION_SEC, EXPORT_FORMAT_MP4,
};
// ---------------------------------------------------------------------------
@@ -157,10 +156,7 @@ impl PreferencesContent {
.iter()
.enumerate()
.map(|(index, code)| {
ComboBoxOption::new(
index,
format!("{} ({code})", i18n::pack_native_name(code)),
)
ComboBoxOption::new(index, format!("{} ({code})", i18n::pack_native_name(code)))
})
.collect();
let language = cx.new(|cx| {
@@ -171,14 +167,17 @@ impl PreferencesContent {
.iter()
.position(|code| *code == i18n::language_code())
.unwrap_or(0);
cx.subscribe(&language, move |_this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
if let Some(code) = languages.get(*value) {
crate::i18n::set_language_code(code);
cx.emit(PreferencesEvent::LanguageChanged);
cx.subscribe(
&language,
move |_this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
if let Some(code) = languages.get(*value) {
crate::i18n::set_language_code(code);
cx.emit(PreferencesEvent::LanguageChanged);
}
}
}
})
},
)
.detach();
language.update(cx, |combo, cx| {
combo.set_selected(Some(language_selected), cx)
@@ -295,9 +294,7 @@ impl PreferencesContent {
// display's ICC profile (system profile, or a custom file below).
// The macOS layer tag is applied at startup, so a mode change takes
// effect after a restart.
use crate::oakui::displaycolor::{
CONFIG_KEY_COLOR_MODE, CONFIG_KEY_CUSTOM_ICC,
};
use crate::oakui::displaycolor::{CONFIG_KEY_COLOR_MODE, CONFIG_KEY_CUSTOM_ICC};
let display_icc = cx.new(|cx| {
let mode = config_get_string(CONFIG_KEY_COLOR_MODE);
CheckBox::new(
@@ -332,8 +329,10 @@ impl PreferencesContent {
// --- 项目 Project: snapshot interval + default transition ----------
let snapshot_interval = cx.new(|cx| {
let current =
config_get_int(CONFIG_KEY_SNAPSHOT_INTERVAL_SEC, DEFAULT_SNAPSHOT_INTERVAL_SEC);
let current = config_get_int(
CONFIG_KEY_SNAPSHOT_INTERVAL_SEC,
DEFAULT_SNAPSHOT_INTERVAL_SEC,
);
SpinBox::new(
8,
SliderModel::new(ValueKind::Integer, 0.0, 86400.0, 10.0, current as f64),
@@ -385,10 +384,8 @@ impl PreferencesContent {
// --- 音频 Audio: output / input devices -----------------------------
// The enumeration reads the oakaudio manager even on the mock
// engine; the config choice applies the moment the dropdown changes.
let (audio_output, output_devices) =
device_combo(5, true, window, cx);
let (audio_input, input_devices) =
device_combo(6, false, window, cx);
let (audio_output, output_devices) = device_combo(5, true, window, cx);
let (audio_input, input_devices) = device_combo(6, false, window, cx);
cx.subscribe(&audio_output, |this, _combo, event: &ComboBoxEvent, cx| {
if let ComboBoxEvent::Selected { value, .. } = event {
// Option 0 is the system default; the devices start at 1.
@@ -440,10 +437,7 @@ impl PreferencesContent {
/// when the dialog closes, like the cache directory).
pub fn commit_display_icc_path(&self, cx: &App) {
let path = self.display_icc_path.read(cx).path(cx).trim().to_string();
config_set_string(
crate::oakui::displaycolor::CONFIG_KEY_CUSTOM_ICC,
&path,
);
config_set_string(crate::oakui::displaycolor::CONFIG_KEY_CUSTOM_ICC, &path);
}
/// Opens the platform file picker for a custom ICC profile.
@@ -530,7 +524,10 @@ fn device_combo(
} else {
audio_input_device()
};
let mut options = vec![ComboBoxOption::new(0, i18n::tr("preferences.audio.default"))];
let mut options = vec![ComboBoxOption::new(
0,
i18n::tr("preferences.audio.default"),
)];
for (i, name) in devices.iter().enumerate() {
options.push(ComboBoxOption::new(i + 1, name.clone()));
}
@@ -544,7 +541,8 @@ fn device_combo(
} else {
i18n::tr("preferences.audio.input.placeholder")
};
let combo = cx.new(|cx| ComboBox::new(control, options, window, cx).with_placeholder(placeholder));
let combo =
cx.new(|cx| ComboBox::new(control, options, window, cx).with_placeholder(placeholder));
combo.update(cx, |combo, cx| combo.set_selected(Some(selected), cx));
(combo, devices)
}
@@ -598,7 +596,10 @@ impl Render for PreferencesContent {
.max_h(px(720.0))
.overflow_y_scroll()
// 常规 General
.child(section_header(&colors, i18n::tr("preferences.section.general").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.general").into(),
))
.child(form_row(
&colors,
i18n::tr("preferences.language").into(),
@@ -610,7 +611,10 @@ impl Render for PreferencesContent {
self.theme.clone(),
))
// 渲染 Rendering
.child(section_header(&colors, i18n::tr("preferences.section.render").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.render").into(),
))
.child(form_row(
&colors,
i18n::tr("preferences.backend").into(),
@@ -618,7 +622,10 @@ impl Render for PreferencesContent {
))
.child(self.hw_decode.clone())
// 缓存 Cache
.child(section_header(&colors, i18n::tr("preferences.section.cache").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.cache").into(),
))
.child(form_row(
&colors,
i18n::tr("preferences.cache.dir").into(),
@@ -644,7 +651,10 @@ impl Render for PreferencesContent {
),
))
// 代理 Proxy
.child(section_header(&colors, i18n::tr("preferences.section.proxy").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.proxy").into(),
))
.child(self.use_proxy.clone())
.child(form_row(
&colors,
@@ -652,7 +662,10 @@ impl Render for PreferencesContent {
self.proxy_divider.clone(),
))
// 色彩 Color
.child(section_header(&colors, i18n::tr("preferences.section.color").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.color").into(),
))
.child(self.display_icc.clone())
.child(form_row(
&colors,
@@ -685,7 +698,10 @@ impl Render for PreferencesContent {
.child(i18n::tr("preferences.color.restart_hint")),
)
// 项目 Project
.child(section_header(&colors, i18n::tr("preferences.section.project").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.project").into(),
))
.child(form_row(
&colors,
i18n::tr("preferences.snapshot.interval").into(),
@@ -697,7 +713,10 @@ impl Render for PreferencesContent {
self.transition_length.clone(),
))
// 音频 Audio
.child(section_header(&colors, i18n::tr("preferences.section.audio").into()))
.child(section_header(
&colors,
i18n::tr("preferences.section.audio").into(),
))
.child(form_row(
&colors,
i18n::tr("preferences.audio.output").into(),
@@ -717,7 +736,6 @@ impl Render for PreferencesContent {
}
}
// ---------------------------------------------------------------------------
// Export
// ---------------------------------------------------------------------------
@@ -949,7 +967,9 @@ impl<E: crate::oakui::engine::AppEngine> ProxyDialogContent<E> {
.iter()
.position(|d| *d == params.divider)
.unwrap_or(0);
divider.update(cx, |combo, cx| combo.set_selected(Some(divider_selected), cx));
divider.update(cx, |combo, cx| {
combo.set_selected(Some(divider_selected), cx)
});
let width = cx.new(|cx| {
SpinBox::new(
@@ -998,7 +1018,9 @@ impl<E: crate::oakui::engine::AppEngine> ProxyDialogContent<E> {
.iter()
.position(|name| *name == params.preset)
.unwrap_or(2);
preset.update(cx, |combo, cx| combo.set_selected(Some(preset_selected), cx));
preset.update(cx, |combo, cx| {
combo.set_selected(Some(preset_selected), cx)
});
let include_audio = cx.new(|cx| {
CheckBox::new(
@@ -1119,7 +1141,9 @@ impl<E: crate::oakui::engine::AppEngine> ProxyDialogContent<E> {
engine.proxy_set_custom_params(id, params.clone(), cx)
});
}
if let Err(err) = self.engine.update(cx, |engine, cx| engine.proxy_generate(id, cx))
if let Err(err) = self
.engine
.update(cx, |engine, cx| engine.proxy_generate(id, cx))
{
println!("[proxy] generate failed for {id}: {err}");
}
@@ -1131,7 +1155,8 @@ impl<E: crate::oakui::engine::AppEngine> ProxyDialogContent<E> {
pub fn delete(&mut self, cx: &mut Context<Self>) {
let ids: Vec<u64> = self.rows.iter().map(|row| row.id).collect();
for id in ids {
self.engine.update(cx, |engine, cx| engine.proxy_delete(id, cx));
self.engine
.update(cx, |engine, cx| engine.proxy_delete(id, cx));
}
self.refresh(cx);
}
@@ -1198,7 +1223,8 @@ impl<E: crate::oakui::engine::AppEngine> Render for ProxyDialogContent<E> {
if row.has_custom {
state.push_str(&i18n::tr("proxydialog.custom_suffix"));
}
let enabled = row.enabled && row.state == crate::oakui::engine::ProxyMediaState::Ready;
let enabled =
row.enabled && row.state == crate::oakui::engine::ProxyMediaState::Ready;
let dot = if enabled {
colors.selected
} else {
@@ -1353,20 +1379,25 @@ impl<E: crate::oakui::engine::AppEngine> ProjectPropertiesContent<E> {
combo.set_selected(Some(cache_setting as usize), cx)
});
// The custom-path field follows the combo selection live.
cx.subscribe(&cache_location, |this, _combo, event: &ComboBoxEvent, cx| {
let ComboBoxEvent::Selected { value } = event;
let setting = *value as i32;
this.cache_setting = setting;
this.custom_cache_path.update(cx, |field, cx| {
field.set_enabled(setting == 2, cx)
});
cx.notify();
})
cx.subscribe(
&cache_location,
|this, _combo, event: &ComboBoxEvent, cx| {
let ComboBoxEvent::Selected { value } = event;
let setting = *value as i32;
this.cache_setting = setting;
this.custom_cache_path
.update(cx, |field, cx| field.set_enabled(setting == 2, cx));
cx.notify();
},
)
.detach();
let custom_cache_path = cx.new(|cx| {
let editor = cx.new(|cx| EditableTextState::new(StringStorage::default(), cx));
PathField { editor, enabled: cache_setting == 2 }
PathField {
editor,
enabled: cache_setting == 2,
}
});
custom_cache_path.update(cx, |field, cx| field.set_path(custom_path, cx));
@@ -1412,11 +1443,11 @@ impl<E: crate::oakui::engine::AppEngine> ProjectPropertiesContent<E> {
pub fn select_cache_setting(&mut self, setting: i32, cx: &mut Context<Self>) {
let setting = setting.clamp(0, 2);
self.cache_setting = setting;
self.cache_location
.update(cx, |combo, cx| combo.set_selected(Some(setting as usize), cx));
self.custom_cache_path.update(cx, |field, cx| {
field.set_enabled(setting == 2, cx)
self.cache_location.update(cx, |combo, cx| {
combo.set_selected(Some(setting as usize), cx)
});
self.custom_cache_path
.update(cx, |field, cx| field.set_enabled(setting == 2, cx));
cx.notify();
}
@@ -1430,8 +1461,9 @@ impl<E: crate::oakui::engine::AppEngine> ProjectPropertiesContent<E> {
let custom = self.custom_cache_path(cx).to_string();
let setting = self.cache_setting;
let path = if setting == 2 { custom } else { String::new() };
self.engine
.update(cx, |engine, cx| engine.set_project_cache_location(setting, path, cx));
self.engine.update(cx, |engine, cx| {
engine.set_project_cache_location(setting, path, cx)
});
self.set_error(None, cx);
Ok(())
}
@@ -1473,30 +1505,29 @@ impl<E: crate::oakui::engine::AppEngine> Render for ProjectPropertiesContent<E>
.border_color(colors.border)
.text_color(colors.text)
.cursor_pointer()
.on_click(cx.listener(move |_this, _event: &gpui::ClickEvent, _window, cx| {
let receiver = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: false,
multiple: false,
prompt: None,
});
cx.spawn(async move |this, cx| {
if let Ok(Ok(Some(paths))) = receiver.await {
if let Some(path) = paths.first() {
let path = path.to_string_lossy().into_owned();
let _ = this.update(cx, |this, cx| this.set_ocio_config_path(path, cx));
.on_click(
cx.listener(move |_this, _event: &gpui::ClickEvent, _window, cx| {
let receiver = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: false,
multiple: false,
prompt: None,
});
cx.spawn(async move |this, cx| {
if let Ok(Ok(Some(paths))) = receiver.await {
if let Some(path) = paths.first() {
let path = path.to_string_lossy().into_owned();
let _ =
this.update(cx, |this, cx| this.set_ocio_config_path(path, cx));
}
}
}
})
.detach();
}))
})
.detach();
}),
)
.child(i18n::tr("projprops.browse"));
let ocio_row = div()
.flex()
.gap_2()
.child(ocio_field)
.child(browse);
let ocio_row = div().flex().gap_2().child(ocio_field).child(browse);
let custom_row = form_row(
&colors,
@@ -1515,20 +1546,17 @@ impl<E: crate::oakui::engine::AppEngine> Render for ProjectPropertiesContent<E>
div().text_color(colors.text).child(project_name),
))
.child(
form_row(
&colors,
i18n::tr("projprops.ocio_config").into(),
ocio_row,
)
.child(if let Some(error) = &self.error {
div()
.debug_selector(|| "projprops-error".into())
.text_color(gpui::rgb(0xe5484d))
.text_xs()
.child(error.clone())
} else {
div()
}),
form_row(&colors, i18n::tr("projprops.ocio_config").into(), ocio_row).child(
if let Some(error) = &self.error {
div()
.debug_selector(|| "projprops-error".into())
.text_color(gpui::rgb(0xe5484d))
.text_xs()
.child(error.clone())
} else {
div()
},
),
)
.child(form_row(
&colors,
@@ -1584,13 +1612,16 @@ impl PreferencesDialogContent {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let general = cx.new(|cx| PreferencesContent::new(window, cx));
let keyboard = cx.new(|cx| KeyboardTabContent::new(window, cx));
cx.subscribe(&general, |_this, _general, event: &PreferencesEvent, cx| match event {
PreferencesEvent::ThemeChanged(dark) => {
cx.emit(PreferencesEvent::ThemeChanged(*dark));
}
PreferencesEvent::LanguageChanged => cx.emit(PreferencesEvent::LanguageChanged),
PreferencesEvent::ShortcutsChanged => {}
})
cx.subscribe(
&general,
|_this, _general, event: &PreferencesEvent, cx| match event {
PreferencesEvent::ThemeChanged(dark) => {
cx.emit(PreferencesEvent::ThemeChanged(*dark));
}
PreferencesEvent::LanguageChanged => cx.emit(PreferencesEvent::LanguageChanged),
PreferencesEvent::ShortcutsChanged => {}
},
)
.detach();
cx.subscribe(&keyboard, |_this, _keyboard, event: &KeyboardEvent, cx| {
if matches!(event, KeyboardEvent::Changed) {
@@ -1635,10 +1666,18 @@ impl Render for PreferencesDialogContent {
div()
.flex()
.gap_1()
.child(
tab_button(PreferencesTab::General, self.active, &colors, cx),
)
.child(tab_button(PreferencesTab::Keyboard, self.active, &colors, cx)),
.child(tab_button(
PreferencesTab::General,
self.active,
&colors,
cx,
))
.child(tab_button(
PreferencesTab::Keyboard,
self.active,
&colors,
cx,
)),
)
.child(content)
}
@@ -1669,8 +1708,16 @@ fn tab_button(
.py_1()
.rounded_md()
.cursor_pointer()
.bg(if selected { colors.selected } else { transparent() })
.text_color(if selected { colors.selected_text } else { colors.text })
.bg(if selected {
colors.selected
} else {
transparent()
})
.text_color(if selected {
colors.selected_text
} else {
colors.text
})
.on_click(cx.listener(move |this, _event, _window, cx| {
this.active = tab;
cx.notify();
@@ -1931,8 +1978,7 @@ impl KeyboardTabContent {
this.update(cx, |this, cx| {
match result {
Ok(_) => {
this.status =
Some(i18n::tr("preferences.keyboard.imported").to_string());
this.status = Some(i18n::tr("preferences.keyboard.imported").to_string());
let _ = crate::actions::save_custom_shortcuts();
}
Err(_) => {
@@ -1951,10 +1997,7 @@ impl KeyboardTabContent {
/// Export: write the current override diff to a picked file.
fn export_shortcuts(&mut self, cx: &mut Context<Self>) {
let receiver = cx.prompt_for_new_path(
&std::path::PathBuf::from("."),
Some("shortcuts"),
);
let receiver = cx.prompt_for_new_path(&std::path::PathBuf::from("."), Some("shortcuts"));
cx.spawn(async move |this, cx| {
let Ok(Ok(Some(path))) = receiver.await else {
return;
@@ -1987,7 +2030,11 @@ impl Render for KeyboardTabContent {
.bg(colors.background)
.px_2()
.py_1()
.child(text_input("keyboard-search-input", cx).state(weak).accepts_input(true));
.child(
text_input("keyboard-search-input", cx)
.state(weak)
.accepts_input(true),
);
// The grouped, filtered action list.
let mut list = div()
@@ -2027,7 +2074,11 @@ impl Render for KeyboardTabContent {
.px_1()
.py_0p5()
.rounded_md()
.bg(if row_selected { colors.selected } else { transparent() })
.bg(if row_selected {
colors.selected
} else {
transparent()
})
.on_click(cx.listener(move |this, _event, _window, cx| {
this.selected = Some(index);
cx.notify();
@@ -2037,12 +2088,7 @@ impl Render for KeyboardTabContent {
.flex_1()
.flex_col()
.child(div().text_color(colors.text).child(label))
.child(
div()
.text_color(colors.disabled)
.text_xs()
.child(row_path),
),
.child(div().text_color(colors.disabled).text_xs().child(row_path)),
)
.child(
div()
@@ -2062,14 +2108,12 @@ impl Render for KeyboardTabContent {
.text_color(colors.text)
.cursor_pointer()
.track_focus(&focus)
.on_click(cx.listener(
move |this, _event, _window, cx| {
if this.capturing != Some(index) {
this.begin_capture(index, cx);
}
cx.stop_propagation();
},
))
.on_click(cx.listener(move |this, _event, _window, cx| {
if this.capturing != Some(index) {
this.begin_capture(index, cx);
}
cx.stop_propagation();
}))
.child(field_label),
),
);
@@ -2121,9 +2165,7 @@ impl Render for KeyboardTabContent {
colors.background,
colors.text,
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.import_shortcuts(cx)
})),
.on_click(cx.listener(|this, _event, _window, cx| this.import_shortcuts(cx))),
)
.child(
pill_button(
@@ -2132,9 +2174,7 @@ impl Render for KeyboardTabContent {
colors.background,
colors.text,
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.export_shortcuts(cx)
})),
.on_click(cx.listener(|this, _event, _window, cx| this.export_shortcuts(cx))),
)
.child(div().flex_1())
.child(
@@ -2144,9 +2184,7 @@ impl Render for KeyboardTabContent {
colors.background,
colors.text,
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.reset_selected(cx)
})),
.on_click(cx.listener(|this, _event, _window, cx| this.reset_selected(cx))),
)
.child(
pill_button(
@@ -2155,9 +2193,7 @@ impl Render for KeyboardTabContent {
colors.background,
colors.text,
)
.on_click(cx.listener(|this, _event, _window, cx| {
this.reset_all(cx)
})),
.on_click(cx.listener(|this, _event, _window, cx| this.reset_all(cx))),
)
};
@@ -2178,16 +2214,14 @@ impl Render for KeyboardTabContent {
)
.child(list)
.child(footer)
.child(
if let Some(status) = &self.status {
div()
.text_color(colors.disabled)
.text_xs()
.child(status.clone())
} else {
div()
},
)
.child(if let Some(status) = &self.status {
div()
.text_color(colors.disabled)
.text_xs()
.child(status.clone())
} else {
div()
})
}
}
@@ -2214,8 +2248,8 @@ fn capture_decision(keystroke: &Keystroke) -> CaptureDecision {
"backspace" | "delete" => CaptureDecision::Clear,
// Modifier-only key presses (the parser represents a bare modifier as
// the modifier's own key name).
"shift" | "control" | "alt" | "cmd" | "super" | "win" | "fn" | "function"
| "secondary" | "platform" => CaptureDecision::Ignore,
"shift" | "control" | "alt" | "cmd" | "super" | "win" | "fn" | "function" | "secondary"
| "platform" => CaptureDecision::Ignore,
"" => CaptureDecision::Ignore,
_ => CaptureDecision::Assign(keystroke.unparse()),
}
@@ -2369,7 +2403,9 @@ impl ActionSearchContent {
/// The currently selected action (tests).
pub fn selected_action(&self) -> Option<ActionId> {
self.selection.and_then(|index| self.items.get(index)).map(|item| item.action)
self.selection
.and_then(|index| self.items.get(index))
.map(|item| item.action)
}
/// The current search filter (tests).
@@ -2406,16 +2442,13 @@ impl Render for ActionSearchContent {
.flex_col()
.max_h(px(360.0))
.overflow_y_scroll()
.child(
div()
.text_color(colors.disabled)
.text_xs()
.child(if self.items.is_empty() {
i18n::tr("actionsearch.no_actions")
} else {
i18n::tr("actionsearch.empty")
}),
)
.child(div().text_color(colors.disabled).text_xs().child(
if self.items.is_empty() {
i18n::tr("actionsearch.no_actions")
} else {
i18n::tr("actionsearch.empty")
},
))
} else {
div()
.id("action-search-list")
@@ -2436,7 +2469,11 @@ impl Render for ActionSearchContent {
.px_2()
.py_0p5()
.rounded_md()
.bg(if row_selected { colors.selected } else { transparent() })
.bg(if row_selected {
colors.selected
} else {
transparent()
})
.text_color(if row_selected {
colors.selected_text
} else {
@@ -2449,27 +2486,21 @@ impl Render for ActionSearchContent {
}))
.on_mouse_down(
gpui::MouseButton::Left,
cx.listener(
move |this, event: &gpui::MouseDownEvent, _window, cx| {
// Double-click executes.
if event.click_count >= 2 {
this.selection = Some(index);
this.execute(cx);
}
},
),
cx.listener(move |this, event: &gpui::MouseDownEvent, _window, cx| {
// Double-click executes.
if event.click_count >= 2 {
this.selection = Some(index);
this.execute(cx);
}
}),
)
.child(
div()
.flex_1()
.flex_col()
.child(div().child(label))
.child(
div()
.text_xs()
.text_color(colors.disabled)
.child(format!("({path})")),
),
div().flex_1().flex_col().child(div().child(label)).child(
div()
.text_xs()
.text_color(colors.disabled)
.child(format!("({path})")),
),
)
}))
};
@@ -2488,7 +2519,11 @@ impl Render for ActionSearchContent {
.bg(colors.background)
.px_2()
.py_1()
.child(text_input("action-search-input", cx).state(weak).accepts_input(true)),
.child(
text_input("action-search-input", cx)
.state(weak)
.accepts_input(true),
),
)
.child(list)
}
@@ -2533,7 +2568,10 @@ mod tests {
#[test]
fn capture_decision_handles_all_shapes() {
assert!(matches!(capture_decision(&keystroke("escape")), CaptureDecision::Cancel));
assert!(matches!(
capture_decision(&keystroke("escape")),
CaptureDecision::Cancel
));
assert!(matches!(
capture_decision(&keystroke("backspace")),
CaptureDecision::Clear
@@ -2569,11 +2607,31 @@ mod tests {
#[test]
fn keyboard_filter_matches_name_path_and_shortcut() {
assert!(keyboard_filter_matches("Save Project", "File", Some("⌘S"), "save"));
assert!(keyboard_filter_matches("Save Project", "File", Some("⌘S"), "file > save"));
assert!(keyboard_filter_matches(
"Save Project",
"File",
Some("⌘S"),
"save"
));
assert!(keyboard_filter_matches(
"Save Project",
"File",
Some("⌘S"),
"file > save"
));
// Shortcut matching is case-insensitive.
assert!(keyboard_filter_matches("Save Project", "File", Some("⌘S"), "⌘s"));
assert!(!keyboard_filter_matches("Save Project", "File", Some("⌘S"), "undo"));
assert!(keyboard_filter_matches(
"Save Project",
"File",
Some("⌘S"),
"⌘s"
));
assert!(!keyboard_filter_matches(
"Save Project",
"File",
Some("⌘S"),
"undo"
));
// Empty query matches everything; rows without a shortcut match only
// by name/path.
assert!(keyboard_filter_matches("About Oak…", "Help", None, ""));
@@ -2583,10 +2641,16 @@ mod tests {
#[test]
fn search_filter_matches_label_or_path() {
let _guard = crate::i18n::lang_test_lock().lock().unwrap_or_else(|e| e.into_inner());
let _guard = crate::i18n::lang_test_lock()
.lock()
.unwrap_or_else(|e| e.into_inner());
crate::i18n::set_language_code("en-US");
assert!(search_filter_matches(ActionId::NewProject, "File", "new"));
assert!(search_filter_matches(ActionId::NewProject, "File", "file > new"));
assert!(search_filter_matches(
ActionId::NewProject,
"File",
"file > new"
));
assert!(!search_filter_matches(ActionId::NewProject, "File", "undo"));
assert!(search_filter_matches(ActionId::NewProject, "File", ""));
}
@@ -2603,8 +2667,12 @@ mod tests {
#[test]
fn keyboard_rows_cover_the_menu_bar() {
let _guard = crate::actions::shortcuts_test_lock().lock().unwrap_or_else(|e| e.into_inner());
let _lang = crate::i18n::lang_test_lock().lock().unwrap_or_else(|e| e.into_inner());
let _guard = crate::actions::shortcuts_test_lock()
.lock()
.unwrap_or_else(|e| e.into_inner());
let _lang = crate::i18n::lang_test_lock()
.lock()
.unwrap_or_else(|e| e.into_inner());
crate::i18n::set_language_code("en-US");
let rows = crate::app::menu_action_paths();
assert!(!rows.is_empty());