UI: drop cap fields, selection over drop caps

This commit is contained in:
aiongg
2026-10-07 09:04:59 -04:00
parent 47d3f5b4bf
commit 88b3be0136
5 changed files with 74 additions and 3 deletions
+9
View File
@@ -1004,6 +1004,15 @@ fn draw_text_selection(
let x1 = if e == l.range.end && range.end > l.range.end { l.end_x.max(x0 + 3.0) } else { compose::caret_x(l, e) };
quads.push(quad(x0, x1));
}
// A selected drop cap is highlighted down to its baseline.
if let Some(dc) = l.drop_cap {
let sel = l.glyphs.iter().filter(|g| g.len > 0 && g.byte < dc.end && g.byte >= s && g.byte < e);
let (a, b) = sel.fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), g| (a.min(g.x), b.max(g.x + g.adv)));
if a < b {
let r = dc.rect;
quads.push([Point::new(a, r.y0), Point::new(b, r.y0), Point::new(b, r.y1), Point::new(a, r.y1)]);
}
}
for (gi, g) in l.glyphs.iter().enumerate() {
if g.visible && g.len > 0 && g.byte >= s && g.byte < e {
glyphs.push((li, gi));
+42 -3
View File
@@ -1771,6 +1771,35 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
});
}
"nested" => {
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Drop Caps")).font(semibold(12.0)));
egui::Grid::new("psdc").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
for (label, key, max) in [
("Lines:", "dropCapLines", designcraft_compose::MAX_DROP_CAP_LINES),
("Characters:", "dropCapChars", designcraft_compose::MAX_DROP_CAP_CHARS),
] {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let v = cur(d, &format!("p.{key}"), &pv[key]).as_f64();
if let Some(n) = crate::widgets::number(ui, &format!("ps{key}"), v, "", 60.0, 0) {
d.fields.insert(format!("p.{key}"), json!((n.max(0.0) as u64).min(max as u64)));
}
ui.end_row();
}
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Character Style:"));
let style = cur(d, "p.dropCapStyle", &pv["dropCapStyle"]).as_str().unwrap_or("").to_string();
let style = if style.is_empty() { designcraft_doc::NO_CHAR_STYLE.to_string() } else { style };
egui::ComboBox::from_id_salt("ps_dropcap_style")
.selected_text(crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, &style)))
.width(160.0)
.show_ui(ui, |ui| {
for c in &cnames {
if ui.selectable_label(*c == style, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, c))).clicked() {
d.fields.insert("p.dropCapStyle".into(), json!(c));
}
}
});
ui.end_row();
});
ui.add_space(8.0);
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Nested Styles")).font(semibold(12.0)));
let mut list: Vec<Value> = cur(d, "p.nestedStyles", &pv["nestedStyles"]).as_array().cloned().unwrap_or_default();
let mut changed = false;
@@ -1814,9 +1843,19 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, &kind)))
.width(100.0)
.show_ui(ui, |ui| {
for k in
["sentences", "words", "characters", "letters", "digits", "tab", "forcedLineBreak", "emSpace", "enSpace", "chars"]
{
for k in [
"sentences",
"words",
"characters",
"letters",
"digits",
"tab",
"forcedLineBreak",
"emSpace",
"enSpace",
"dropcap",
"chars",
] {
if ui.selectable_label(k == kind, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, k))).clicked() {
ns["until"] = if k == "chars" { json!({"kind": "chars", "chars": ":"}) } else { json!({"kind": k}) };
changed = true;
+6
View File
@@ -1891,6 +1891,12 @@ const TABLE: &[(&str, [&str; 5])] = &[
("forcedLineBreak", ["forcedLineBreak", "forcedLineBreak", "forcedLineBreak", "forcedLineBreak", "强制换行符"]),
("emSpace", ["emSpace", "emSpace", "emSpace", "emSpace", "全角空格"]),
("enSpace", ["enSpace", "enSpace", "enSpace", "enSpace", "半角空格"]),
("dropcap", ["Initiale", "lettrine", "capitular", "ドロップキャップ", "首字下沉"]),
("Drop Caps", ["Initialen", "Lettrines", "Capitulares", "ドロップキャップ", "首字下沉"]),
("Lines:", ["Zeilen:", "Lignes :", "Líneas:", "行数:", "行数:"]),
("Characters:", ["Zeichen:", "Caractères :", "Caracteres:", "文字数:", "字符数:"]),
("Drop Lines", ["Initialzeilen", "Lignes lettrine", "Líneas capitular", "ドロップ行数", "下沉行数"]),
("Drop Chars", ["Initialzeichen", "Car. lettrine", "Car. capitular", "ドロップ文字数", "下沉字符数"]),
("chars", ["chars", "chars", "chars", "chars", "指定字符"]),
("[Automatic]", ["[Automatic]", "[Automatic]", "[Automatic]", "[Automatic]", "[自动]"]),
("Tracking:", ["Tracking:", "Tracking:", "Tracking:", "Tracking:", "字距:"]),
+6
View File
@@ -317,6 +317,12 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("forcedLineBreak", "فاصل سطر إجباري"),
("emSpace", "مسافة em"),
("enSpace", "مسافة en"),
("dropcap", "الحرف الاستهلالي"),
("Drop Caps", "الأحرف الاستهلالية"),
("Lines:", "الأسطر:"),
("Characters:", "الأحرف:"),
("Drop Lines", "أسطر الاستهلال"),
("Drop Chars", "أحرف الاستهلال"),
("chars", "محارف محددة"),
("[Automatic]", "[تلقائي]"),
("Size:", "الحجم:"),
+11
View File
@@ -1452,6 +1452,17 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
}
ui.end_row();
}
// Drop cap: number of lines and characters.
for (label, key, max, id) in [
("Drop Lines", "dropCapLines", designcraft_compose::MAX_DROP_CAP_LINES, "ppdl"),
("Drop Chars", "dropCapChars", designcraft_compose::MAX_DROP_CAP_CHARS, "ppdc"),
] {
caption(ui, crate::i18n::tr(&app.ui.language, label));
if let Some(v) = number(ui, id, p[key].as_f64(), "", 64.0, 0) {
let _ = app.run("type.para", json!({"attrs": {key: (v.max(0.0) as u64).min(max as u64)}}));
}
}
ui.end_row();
});
ui.horizontal(|ui| {
let mut h = p["hyphenate"].as_bool().unwrap_or(true);