live_preview: Fuzzy-match palette entries

This commit is contained in:
Tobias Hunger 2025-05-21 17:18:13 +00:00 committed by Tobias Hunger
parent 7afd5dfc4a
commit a90a986263
5 changed files with 88 additions and 4 deletions

View file

@ -103,6 +103,7 @@ i-slint-backend-selector = { workspace = true, optional = true }
i-slint-core = { workspace = true, features = ["std"], optional = true }
slint = { workspace = true, features = ["compat-1-2"], optional = true }
slint-interpreter = { workspace = true, features = ["compat-1-2", "internal", "internal-highlight", "internal-json", "image-default-formats"], optional = true }
nucleo-matcher = "0.3.1"
[target.'cfg(not(any(target_os = "windows", target_arch = "wasm32", all(target_arch = "aarch64", target_os = "linux"))))'.dependencies]
tikv-jemallocator = { workspace = true }

View file

@ -144,6 +144,8 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
api.on_suggest_gradient_stop_at_position(gradient::suggest_gradient_stop_at_position);
api.on_clone_gradient_stops(gradient::clone_gradient_stops);
api.on_filter_palettes(palette::filter_palettes);
#[cfg(target_vendor = "apple")]
api.set_control_key_name("command".into());

View file

@ -9,7 +9,7 @@ use crate::{
use lsp_types::Url;
use i_slint_compiler::{expression_tree, langtype, object_tree};
use slint::SharedString;
use slint::{Model, SharedString};
fn collect_colors_palette() -> Vec<ui::PaletteEntry> {
let colors = i_slint_compiler::lookup::named_colors();
@ -154,6 +154,47 @@ pub fn collect_palettes(
collect_palette_from_globals(document_cache, document_uri, collect_colors_palette())
}
pub fn filter_palettes(
input: slint::ModelRc<ui::PaletteEntry>,
pattern: slint::SharedString,
) -> slint::ModelRc<ui::PaletteEntry> {
let pattern = pattern.to_string();
std::rc::Rc::new(slint::VecModel::from(filter_palettes_iter(&mut input.iter(), &pattern)))
.into()
}
fn filter_palettes_iter(
input: &mut impl Iterator<Item = ui::PaletteEntry>,
pattern: &str,
) -> Vec<ui::PaletteEntry> {
use nucleo_matcher::{pattern, Config, Matcher};
let mut matcher = Matcher::new(Config::DEFAULT.match_paths());
let pattern = pattern::Pattern::parse(
pattern,
pattern::CaseMatching::Ignore,
pattern::Normalization::Smart,
);
input
.filter(|p| {
let terms = [format!(
"{} %kind:{:?} %is_brush:{}",
p.name,
p.value.kind,
if [ui::PropertyValueKind::Color, ui::PropertyValueKind::Brush]
.contains(&p.value.kind)
{
"yes"
} else {
"no"
}
)];
!pattern.match_list(terms.iter(), &mut matcher).is_empty()
})
.collect::<Vec<_>>()
}
#[cfg(test)]
mod tests {
use crate::preview::ui::PaletteEntry;
@ -348,4 +389,39 @@ export component Main { }
compare(&result[4], "Test.palette.light.color2", 0x00, 0x11, 0x00);
compare(&result[5], "Test.palette.light.color3", 0x00, 0x00, 0x11);
}
#[test]
fn test_filter_palette() {
let palette = super::collect_colors_palette();
assert_eq!(filter_palettes_iter(&mut palette.iter().cloned(), "'FOO").len(), 0);
assert_eq!(
filter_palettes_iter(&mut palette.iter().cloned(), "'%kind:Color").len(),
palette.len()
);
assert_eq!(
filter_palettes_iter(&mut palette.iter().cloned(), "'%is_brush:yes").len(),
palette.len()
);
assert_eq!(filter_palettes_iter(&mut palette.iter().cloned(), "'%kind:UNKNOWN").len(), 0);
assert_eq!(
filter_palettes_iter(&mut palette.iter().cloned(), "'Colors.aquamarine").len(),
1
);
assert_eq!(
filter_palettes_iter(&mut palette.iter().cloned(), "Colors.aquamarine").len(),
2
);
assert_eq!(
filter_palettes_iter(&mut palette.iter().cloned(), "Colors.aquamarine '%kind:Color")
.len(),
2
);
assert_eq!(filter_palettes_iter(&mut palette.iter().cloned(), "aquamarine").len(), 2);
assert_eq!(
filter_palettes_iter(&mut palette.iter().cloned(), "^Colors.").len(),
palette.len()
);
assert_eq!(filter_palettes_iter(&mut palette.iter().cloned(), "!^Colors.").len(), 0);
}
}

View file

@ -547,4 +547,7 @@ export global Api {
// Get the property declaration/definition ranges
callback property-declaration-ranges(property-name: string) -> PropertyDeclaration;
// Palettes:
pure callback filter-palettes(palettes: [PaletteEntry], pattern: string) -> [PaletteEntry];
}

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { SimpleColumn } from "../../layout-helpers.slint";
import { Api } from "../../../api.slint";
import { Api, PaletteEntry } from "../../../api.slint";
import { EditorSizeSettings } from "../../styling.slint";
import { ScrollView } from "std-widgets.slint";
@ -27,16 +27,18 @@ export component PalettePicker {
property <int> rows: 9;
property <[PaletteEntry]> palette-entries: Api.filter_palettes(Api.palettes, "%is_brush:yes !^Colors.");
ScrollView {
width: 100%;
height: 100%;
viewport-height: (Api.palettes.length / rows).floor() * 24px;
for i[index] in Api.palettes : PaletteIcon {
for i[index] in root.palette-entries : PaletteIcon {
x: EditorSizeSettings.standard-margin + (index.mod(rows) * 24px);
y: (index / rows).floor() * 24px;
name: i.name;
brush: i.value.value-brush;
}
}
}
}