Preserve grapheme clusters when shortening desktop labels
This commit is contained in:
Generated
+1
@@ -2176,6 +2176,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ serde_json.workspace = true
|
|||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
tokio = { version = "1", features = ["rt", "time"] }
|
tokio = { version = "1", features = ["rt", "time"] }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
unicode-segmentation = "1.13.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
iced_runtime = "0.14"
|
iced_runtime = "0.14"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use iced::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use oa_core::*;
|
use oa_core::*;
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub fn label<'a>(s: impl Into<String>) -> widget::Text<'a> {
|
pub fn label<'a>(s: impl Into<String>) -> widget::Text<'a> {
|
||||||
text(s.into()).size(11).color(style::MUTED)
|
text(s.into()).size(11).color(style::MUTED)
|
||||||
@@ -24,10 +25,12 @@ pub fn action<'a>(s: &str, m: Message) -> widget::Button<'a, Message> {
|
|||||||
}
|
}
|
||||||
pub fn short(s: &str, limit: usize) -> String {
|
pub fn short(s: &str, limit: usize) -> String {
|
||||||
let s = s.split_whitespace().collect::<Vec<_>>().join(" ");
|
let s = s.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||||
if s.chars().count() > limit {
|
if s.graphemes(true).count() > limit {
|
||||||
format!(
|
format!(
|
||||||
"{}…",
|
"{}…",
|
||||||
s.chars().take(limit.saturating_sub(1)).collect::<String>()
|
s.graphemes(true)
|
||||||
|
.take(limit.saturating_sub(1))
|
||||||
|
.collect::<String>()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
s
|
s
|
||||||
@@ -1290,3 +1293,57 @@ fn checks(app: &App) -> View<'_, Message> {
|
|||||||
}
|
}
|
||||||
scrollable(out.padding(24)).into()
|
scrollable(out.padding(24)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::short;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_preserves_native_lifeline_clusters_at_cutoff() {
|
||||||
|
let oa = "abcdefghijklmnope\u{0301}尾";
|
||||||
|
assert_eq!(short(oa, 17), "abcdefghijklmnop…");
|
||||||
|
assert_eq!(short(oa, 18), oa);
|
||||||
|
assert_eq!(short(oa, 19), oa);
|
||||||
|
|
||||||
|
let sa = "abcdefghijklmnop👩\u{200d}🔬 tail";
|
||||||
|
assert_eq!(short(sa, 17), "abcdefghijklmnop…");
|
||||||
|
assert_eq!(short(sa, 18), "abcdefghijklmnop👩\u{200d}🔬…");
|
||||||
|
assert_eq!(short(sa, 19), "abcdefghijklmnop👩\u{200d}🔬 …");
|
||||||
|
assert_eq!(short(sa, 22), sa);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_never_splits_retained_extended_graphemes() {
|
||||||
|
for cluster in ["e\u{0301}", "👩\u{200d}🔬", "👍🏽", "🇫🇷"] {
|
||||||
|
let value = format!("A{cluster}BC");
|
||||||
|
assert_eq!(short(&value, 2), "A…");
|
||||||
|
assert_eq!(short(&value, 3), format!("A{cluster}…"));
|
||||||
|
assert_eq!(short(&value, 4), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_preserves_ascii_cjk_and_whitespace_policy() {
|
||||||
|
assert_eq!(short("abcd", 3), "ab…");
|
||||||
|
assert_eq!(short("abcd", 4), "abcd");
|
||||||
|
assert_eq!(short("abcd", 5), "abcd");
|
||||||
|
assert_eq!(short("甲乙丙丁", 3), "甲乙…");
|
||||||
|
assert_eq!(short("甲乙丙丁", 4), "甲乙丙丁");
|
||||||
|
assert_eq!(short(" \tA\r\n e\u{0301}\u{2003}B ", 5), "A e\u{0301} B");
|
||||||
|
assert_eq!(short(" \tA\r\n e\u{0301}\u{2003}B ", 4), "A e\u{0301}…");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn short_preserves_empty_and_small_limit_policy() {
|
||||||
|
for limit in [0, 1, 2] {
|
||||||
|
assert_eq!(short("", limit), "");
|
||||||
|
assert_eq!(short(" \t\r\n", limit), "");
|
||||||
|
}
|
||||||
|
assert_eq!(short("A", 0), "…");
|
||||||
|
assert_eq!(short("A", 1), "A");
|
||||||
|
assert_eq!(short("AB", 1), "…");
|
||||||
|
assert_eq!(short("e\u{0301}", 0), "…");
|
||||||
|
assert_eq!(short("e\u{0301}", 1), "e\u{0301}");
|
||||||
|
assert_eq!(short("e\u{0301}B", 1), "…");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user