mirror of
https://github.com/storytold/craft-libs.git
synced 2026-10-31 07:39:23 +00:00
keymap ui: Options::visible hides catalog entries from the table
Hidden entries keep their bindings and still count in conflicts; they are left out of the rows, search and the Show entries.
This commit is contained in:
1 parent
0bff6f4c96
commit
02ba09ee9a
3 files changed
+40
-11
No files matched your search
@@ -378,8 +378,9 @@ Words come from `ui::Text`. Apps on craft-i18n pass `I18nText`; others implement
|
||||
right-to-left languages, which mirror the layout. Chord text is never reordered. `ui::STRINGS`
|
||||
and `ui::PLURALS` list every string the dialog looks up, for the translation catalog.
|
||||
`Options` sets the conflict mode, double-press recording, area tabs, the on-screen keyboard,
|
||||
read-only rows for keys outside the catalog, the app's import and export formats, and an
|
||||
`accept` rule for which keys a command may get.
|
||||
read-only rows for keys outside the catalog, the app's import and export formats, an `accept`
|
||||
rule for which keys a command may get, and a `visible` rule for which catalog entries the table
|
||||
lists (hidden entries keep their keys and still count in conflicts).
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -125,6 +125,10 @@ pub struct Options {
|
||||
pub area_tabs: bool,
|
||||
/// The app's own Show entries.
|
||||
pub areas: Vec<Area>,
|
||||
/// `None`: every catalog entry is listed. Otherwise the table, its Show entries and search
|
||||
/// list only the entries it returns true for; the others keep their bindings and still count
|
||||
/// in conflicts.
|
||||
pub visible: Option<fn(&Bindable) -> bool>,
|
||||
/// false: the on-screen keyboard pane.
|
||||
pub keyboard: bool,
|
||||
pub fixed: Vec<FixedRow>,
|
||||
@@ -152,6 +156,7 @@ impl Default for Options {
|
||||
sort_by_label: false,
|
||||
area_tabs: false,
|
||||
areas: Vec::new(),
|
||||
visible: None,
|
||||
keyboard: false,
|
||||
fixed: Vec::new(),
|
||||
import_formats: Vec::new(),
|
||||
|
||||
@@ -186,12 +186,18 @@ fn is_tool(b: &Bindable) -> bool {
|
||||
matches!(b.kind, Kind::Tool { .. })
|
||||
}
|
||||
|
||||
/// The catalog entries the table lists ([`Options::visible`]).
|
||||
fn listed<'k>(keymap: &'k Keymap, options: &Options) -> impl Iterator<Item = &'k Bindable> + Clone {
|
||||
let visible = options.visible;
|
||||
keymap.catalog().iter().filter(move |b| visible.is_none_or(|f| f(b)))
|
||||
}
|
||||
|
||||
/// The Show entries, in catalog order, leaving out areas with no rows.
|
||||
pub(crate) fn areas(keymap: &Keymap, options: &Options, text: &dyn Text) -> Vec<AreaEntry> {
|
||||
let catalog = keymap.catalog();
|
||||
let catalog = listed(keymap, options);
|
||||
let mut out = vec![AreaEntry { id: AreaId::All, label: text.ui("All Commands").into_owned() }];
|
||||
let mut menus: Vec<&str> = Vec::new();
|
||||
for m in catalog.iter().filter_map(top_menu) {
|
||||
for m in catalog.clone().filter_map(top_menu) {
|
||||
if !menus.contains(&m) {
|
||||
menus.push(m);
|
||||
}
|
||||
@@ -200,22 +206,22 @@ pub(crate) fn areas(keymap: &Keymap, options: &Options, text: &dyn Text) -> Vec<
|
||||
let label = craft_i18n::fmt(&text.ui("{menu} Menu"), &[("menu", &text.segment(m))]);
|
||||
out.push(AreaEntry { id: AreaId::Menu(m.to_string()), label });
|
||||
}
|
||||
if catalog.iter().any(is_tool) {
|
||||
if catalog.clone().any(is_tool) {
|
||||
out.push(AreaEntry { id: AreaId::Tools, label: text.ui("Tools").into_owned() });
|
||||
}
|
||||
let mut panels: Vec<&str> = keymap.scopes().panels().iter().map(String::as_str).collect();
|
||||
for b in catalog.iter().filter(|b| !b.scope.is_app()) {
|
||||
for b in catalog.clone().filter(|b| !b.scope.is_app()) {
|
||||
if !panels.contains(&b.scope.as_str()) {
|
||||
panels.push(b.scope.as_str());
|
||||
}
|
||||
}
|
||||
for p in panels.into_iter().filter(|p| catalog.iter().any(|b| b.scope.as_str() == *p)) {
|
||||
for p in panels.into_iter().filter(|p| catalog.clone().any(|b| b.scope.as_str() == *p)) {
|
||||
out.push(AreaEntry { id: AreaId::Panel(p.to_string()), label: panel_label(text, p) });
|
||||
}
|
||||
if catalog.iter().any(|b| top_menu(b).is_none() && !is_tool(b)) {
|
||||
if catalog.clone().any(|b| top_menu(b).is_none() && !is_tool(b)) {
|
||||
out.push(AreaEntry { id: AreaId::Other, label: text.ui("Other").into_owned() });
|
||||
}
|
||||
for a in &options.areas {
|
||||
for a in options.areas.iter().filter(|a| catalog.clone().any(a.rows)) {
|
||||
out.push(AreaEntry { id: AreaId::App(a.id), label: text.segment(a.label).into_owned() });
|
||||
}
|
||||
if !options.fixed.is_empty() {
|
||||
@@ -344,14 +350,15 @@ fn build(keymap: &Keymap, options: &Options, text: &dyn Text, view: &View) -> Bu
|
||||
}
|
||||
}
|
||||
let submenus = match view.area {
|
||||
AreaId::Menu(m) => catalog.iter().any(|b| top_menu(b) == Some(m.as_str()) && b.paths.first().is_some_and(|p| p.len() >= 3)),
|
||||
AreaId::Menu(m) => listed(keymap, options).any(|b| top_menu(b) == Some(m.as_str()) && b.paths.first().is_some_and(|p| p.len() >= 3)),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Groups in first-seen order within their rank.
|
||||
let mut groups: Vec<(u8, Option<String>, Vec<Row>)> = Vec::new();
|
||||
for (index, (b, list)) in catalog.iter().zip(&bindings).enumerate() {
|
||||
if !in_area(view.area, b, options) || !query.matches(b, list, text, platform) {
|
||||
let hidden = options.visible.is_some_and(|f| !f(b));
|
||||
if hidden || !in_area(view.area, b, options) || !query.matches(b, list, text, platform) {
|
||||
continue;
|
||||
}
|
||||
let key = b.command.key();
|
||||
@@ -600,6 +607,22 @@ pub(crate) mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hidden_entries_are_not_listed_but_still_conflict() -> Result<(), crate::AssignError> {
|
||||
let mut km = fixture(Platform::Mac);
|
||||
let o = Options { visible: Some(|b| b.command.id != "file.export" && !b.command.id.starts_with("timeline.")), ..Options::default() };
|
||||
let names: Vec<String> = areas(&km, &o, &English).into_iter().map(|a| a.label).collect();
|
||||
assert_eq!(names, ["All Commands", "File Menu", "Object Menu", "Tools", "Other"]);
|
||||
assert_eq!(lines(&km, &o, &English, "", AreaId::Menu("File".into()), Filter::All), ["Save: ⌘S", "Save As…: ⇧⌘S"]);
|
||||
assert!(lines(&km, &o, &English, "export", AreaId::All, Filter::All).is_empty());
|
||||
|
||||
km.assign(&"file.save".into(), key("Cmd+E"), crate::Policy::Keep)?;
|
||||
let none = BTreeSet::new();
|
||||
assert_eq!(build(&km, &o, &English, &view("", &AreaId::All, Filter::All, &none)).clashes, 1);
|
||||
assert_eq!(lines(&km, &o, &English, "", AreaId::All, Filter::Conflicts), ["# File", "Save: ⌘S, ⌘E"]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commands_sharing_a_default_key_are_no_conflict() -> Result<(), crate::AssignError> {
|
||||
let menu = Bindable::new("app.characterPanel", "Character", Scope::app()).with_path(vec!["Window".into(), "Character".into()]);
|
||||
|
||||
Reference in new issue
Block a user