mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 06:01:18 +00:00
Part of #394: each run hyphenates in its own language; Default Language sets new type's
Squashed from #2818 onto main.
This commit is contained in:
@@ -833,7 +833,10 @@ pub(crate) fn new_type_style(s: &Session, p: &Value) -> CharStyle {
|
||||
None if !s.paint.fill.is_none() && s.paint.fill != vectorcraft_color::Paint::solid(vectorcraft_color::Color::WHITE) => s.paint.fill.clone(),
|
||||
None => vectorcraft_color::Paint::solid(vectorcraft_color::Color::BLACK),
|
||||
};
|
||||
// In the document's language (Document Setup), none for the default.
|
||||
style.language = s.doc().ok().and_then(|d| vectorcraft_doc::setup::char_language(&d.doc.setup.language).ok().flatten()).map(str::to_string);
|
||||
// In the language Document Setup chose, else Preferences › Hyphenation › Default Language; none
|
||||
// for the default. Type already in a document keeps its own.
|
||||
let chosen = |name: &str| vectorcraft_doc::setup::char_language(name).ok().flatten();
|
||||
let document = s.doc().ok().and_then(|d| chosen(&d.doc.setup.language));
|
||||
style.language = document.or_else(|| chosen(&s.prefs.hyphenation_language)).map(str::to_string);
|
||||
style
|
||||
}
|
||||
|
||||
@@ -260,14 +260,40 @@ pub const PREF_SPECS: &[PrefSpec] = &[
|
||||
"",
|
||||
"Default Language",
|
||||
choice(&[
|
||||
("English: USA", "English: USA"),
|
||||
("English: UK", "English: UK"),
|
||||
("French", "French"),
|
||||
("German", "German"),
|
||||
("Spanish", "Spanish"),
|
||||
("Italian", "Italian"),
|
||||
("Dutch", "Dutch"),
|
||||
("Portuguese", "Portuguese")
|
||||
("en-US", "English: USA"),
|
||||
("en-GB", "English: UK"),
|
||||
("de", "German"),
|
||||
("de-CH", "German: Swiss"),
|
||||
("fr", "French"),
|
||||
("it", "Italian"),
|
||||
("es", "Spanish"),
|
||||
("ca", "Catalan"),
|
||||
("pt", "Portuguese"),
|
||||
("nl", "Dutch"),
|
||||
("da", "Danish"),
|
||||
("sv", "Swedish"),
|
||||
("fi", "Finnish"),
|
||||
("nb", "Norwegian"),
|
||||
("pl", "Polish"),
|
||||
("cs", "Czech"),
|
||||
("ru", "Russian"),
|
||||
("sr-Cyrl", "Serbian: Cyrillic"),
|
||||
("sr-Latn", "Serbian: Latin"),
|
||||
("hr", "Croatian"),
|
||||
("tr", "Turkish"),
|
||||
("ja", "Japanese"),
|
||||
("zh", "Chinese"),
|
||||
("hi", "Hindi"),
|
||||
("mr", "Marathi"),
|
||||
("gu", "Gujarati"),
|
||||
("bn", "Bengali"),
|
||||
("pa", "Punjabi"),
|
||||
("ta", "Tamil"),
|
||||
("te", "Telugu"),
|
||||
("kn", "Kannada"),
|
||||
("ml", "Malayalam"),
|
||||
("or", "Odia"),
|
||||
("ur", "Urdu")
|
||||
])
|
||||
),
|
||||
p!("hyphenationExceptions", "Hyphenation", "", "Exceptions (comma separated)", text),
|
||||
|
||||
@@ -10,6 +10,14 @@ pub enum DrawMode {
|
||||
Inside,
|
||||
}
|
||||
|
||||
/// A saved Default Language as its tag: older preferences hold the language's name, and one the list
|
||||
/// no longer has reads as the default.
|
||||
fn language_tag<'de, D: serde::Deserializer<'de>>(d: D) -> std::result::Result<String, D::Error> {
|
||||
let saved = <String as serde::Deserialize>::deserialize(d)?;
|
||||
let tag = vectorcraft_doc::setup::char_language(&saved).ok().flatten();
|
||||
Ok(tag.or_else(|| vectorcraft_doc::setup::LANGUAGES.first().map(|(_, t, _)| *t)).unwrap_or_default().to_string())
|
||||
}
|
||||
|
||||
/// Application preferences (Edit → Preferences). Every field is reachable through `prefs.get` /
|
||||
/// `prefs.set {key, value}` (camelCase keys, validated against [`cmd::prefscmds::PREF_SPECS`]).
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
@@ -114,6 +122,9 @@ pub struct Prefs {
|
||||
pub show_slice_numbers: bool,
|
||||
pub slice_line_color: String,
|
||||
// Hyphenation
|
||||
/// Default Language: the BCP 47 tag of the language new type takes when its document's language
|
||||
/// is the default ([`vectorcraft_doc::setup::LANGUAGES`]).
|
||||
#[serde(deserialize_with = "language_tag")]
|
||||
pub hyphenation_language: String,
|
||||
pub hyphenation_exceptions: String,
|
||||
/// Type › Options › Additional Fonts Folder: a folder (read with its subfolders) whose fonts
|
||||
@@ -404,7 +415,7 @@ impl Default for Prefs {
|
||||
snap_perpendicular: true,
|
||||
show_slice_numbers: true,
|
||||
slice_line_color: s("#ff3f3f"),
|
||||
hyphenation_language: s("English: USA"),
|
||||
hyphenation_language: s("en-US"),
|
||||
hyphenation_exceptions: String::new(),
|
||||
fonts_folder: String::new(),
|
||||
plugins_folder: String::new(),
|
||||
|
||||
@@ -582,6 +582,60 @@ fn set_pref(s: &mut Session, key: &str, value: Value) {
|
||||
s.execute("prefs.set", &json!({"key": key, "value": value})).unwrap();
|
||||
}
|
||||
|
||||
/// New type with the session's defaults: its id and language.
|
||||
fn new_type(s: &mut Session) -> (NodeId, Option<String>) {
|
||||
let id = NodeId(s.execute("text.create", &json!({"x": 10, "y": 50, "text": "Hello"})).unwrap()["id"].as_u64().unwrap());
|
||||
(id, type_language(s, id))
|
||||
}
|
||||
|
||||
fn type_language(s: &Session, id: NodeId) -> Option<String> {
|
||||
let Some(NodeKind::Text(t)) = s.doc().unwrap().doc.node(id).map(|n| &n.kind) else { panic!("not text") };
|
||||
t.runs[0].style.language.clone()
|
||||
}
|
||||
|
||||
/// Preferences › Hyphenation › Default Language (#394) is the language of new type, stored as its
|
||||
/// tag. Type already there keeps its language, and a language chosen in Document Setup comes first.
|
||||
#[test]
|
||||
fn default_language_is_the_language_of_new_type() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
let (before, language) = new_type(&mut s);
|
||||
assert_eq!(language, None, "the default language isn't stored");
|
||||
|
||||
set_pref(&mut s, "hyphenationLanguage", json!("German"));
|
||||
assert_eq!(s.prefs.hyphenation_language, "de", "a name is stored as its tag");
|
||||
assert_eq!(new_type(&mut s).1.as_deref(), Some("de"));
|
||||
assert_eq!(type_language(&s, before), None, "type already there stays as it is");
|
||||
|
||||
s.execute("document.setup", &json!({"language": "French"})).unwrap();
|
||||
assert_eq!(new_type(&mut s).1.as_deref(), Some("fr"), "the document's own language comes first");
|
||||
|
||||
set_pref(&mut s, "hyphenationLanguage", json!("en-US"));
|
||||
s.execute("document.setup", &json!({"language": "English: USA"})).unwrap();
|
||||
assert_eq!(new_type(&mut s).1, None);
|
||||
}
|
||||
|
||||
/// Preferences saved before the language was a tag hold its name; a language no longer in the list
|
||||
/// reads as the default.
|
||||
#[test]
|
||||
fn a_saved_default_language_reads_as_its_tag() {
|
||||
let read = |v: &str| serde_json::from_value::<Prefs>(json!({"hyphenationLanguage": v})).unwrap().hyphenation_language;
|
||||
assert_eq!(read("German"), "de");
|
||||
assert_eq!(read("English: USA"), "en-US");
|
||||
assert_eq!(read("de"), "de");
|
||||
assert_eq!(read("Klingon"), "en-US");
|
||||
assert_eq!(Prefs::default().hyphenation_language, "en-US");
|
||||
}
|
||||
|
||||
/// The preference offers every language the Character panel and Document Setup do.
|
||||
#[test]
|
||||
fn default_language_offers_every_language() {
|
||||
let spec = PREF_SPECS.iter().find(|p| p.key == "hyphenationLanguage").unwrap();
|
||||
let crate::cmd::prefscmds::PrefKind::Choice(options) = spec.kind else { panic!("a choice") };
|
||||
let languages: Vec<(&str, &str)> = vectorcraft_doc::setup::LANGUAGES.iter().map(|(name, tag, _)| (*tag, *name)).collect();
|
||||
assert_eq!(options, languages.as_slice());
|
||||
}
|
||||
|
||||
fn gesture(s: &mut Session, v: ViewInfo, events: &[(vectorcraft_tools::PointerKind, f64, f64)]) {
|
||||
for (k, x, y) in events {
|
||||
s.pointer(&vectorcraft_tools::PointerEvent::new(*k, *x, *y), v).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user