mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-24 10:16:59 -06:00
Integration: #753 Fixed Width columns resize the frame to fit them; columnWidth and firstBaselineMin are range-checked
This commit is contained in:
@@ -232,7 +232,7 @@ pub fn fitted(d: &Document, fid: ItemId, sid: StoryId) -> Option<Rect> {
|
||||
}
|
||||
|
||||
/// Give frame `fid` the inner bounds `to` (its path scaled into them).
|
||||
fn resize(d: &mut Document, fid: ItemId, to: Rect) -> bool {
|
||||
pub(crate) fn resize(d: &mut Document, fid: ItemId, to: Rect) -> bool {
|
||||
let Some(it) = d.item_mut(fid) else { return false };
|
||||
let r = it.inner_bounds();
|
||||
if r.width() < EPS || r.height() < EPS {
|
||||
|
||||
@@ -2265,6 +2265,8 @@ fn text_frame_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let rule_bottom = ranged("columnRuleBottomInset", -1440.0, 1440.0, "a number of points")?;
|
||||
let min_height = ranged("minimumHeight", 0.0, crate::autosize::MAX_SIDE, "a number of points")?;
|
||||
let min_width = ranged("minimumWidth", 0.0, crate::autosize::MAX_SIDE, "a number of points")?;
|
||||
let column_width = ranged("columnWidth", 0.0, 15552.0, "a number of points")?;
|
||||
let first_baseline_min = ranged("firstBaselineMin", 0.0, 15552.0, "a number of points")?;
|
||||
let reference = match p.get("autoSizeReference") {
|
||||
None | Some(Value::Null) => None,
|
||||
Some(v) => Some(
|
||||
@@ -2334,11 +2336,11 @@ fn text_frame_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if let Some(v) = p.get("ignoreWrap").and_then(Value::as_bool) {
|
||||
o.ignore_wrap = v;
|
||||
}
|
||||
if let Some(v) = p.get("columnWidth").and_then(Value::as_f64) {
|
||||
o.column_width = v.max(0.0);
|
||||
if let Some(v) = column_width {
|
||||
o.column_width = v;
|
||||
}
|
||||
if let Some(v) = p.get("firstBaselineMin").and_then(Value::as_f64) {
|
||||
o.first_baseline_min = v.max(0.0);
|
||||
if let Some(v) = first_baseline_min {
|
||||
o.first_baseline_min = v;
|
||||
}
|
||||
if let Some(v) = p.get("balanceColumns").and_then(Value::as_bool) {
|
||||
o.balance_columns = v;
|
||||
@@ -2364,6 +2366,16 @@ fn text_frame_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if let Some(v) = rule_bottom {
|
||||
o.column_rule_bottom_inset = v;
|
||||
}
|
||||
// Fixed Width columns set the frame's width: n columns, the gutters between them and
|
||||
// the left and right insets.
|
||||
let fixed_width = o.columns_kind == designcraft_doc::ColumnsKind::FixedWidth && o.column_width > 0.0;
|
||||
let width = f64::from(o.columns.max(1)) * o.column_width + f64::from(o.columns.max(1) - 1) * o.gutter + o.inset[1] + o.inset[3];
|
||||
if fixed_width
|
||||
&& ["columnsKind", "columnWidth", "columns", "gutter", "inset"].iter().any(|k| p.get(*k).is_some())
|
||||
&& let Some(r) = d.item(*id).map(Item::inner_bounds)
|
||||
{
|
||||
crate::autosize::resize(d, *id, Rect::new(r.x0, r.y0, r.x0 + width, r.y1));
|
||||
}
|
||||
}
|
||||
// Story direction belongs to the story: every frame of the thread turns.
|
||||
if let Some(v) = p.get("vertical").and_then(Value::as_bool) {
|
||||
@@ -4965,3 +4977,26 @@ mod ungroup_tests {
|
||||
d.check().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod fixed_width_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fixed_width_columns_set_the_frame_width() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({})).unwrap();
|
||||
let r = s.execute("frame.create", &json!({"rect": [36, 36, 300, 300], "content": "text", "text": "Hello"})).unwrap();
|
||||
let id = ItemId(r["id"].as_u64().unwrap());
|
||||
let p = json!({"ids": [id.0], "columns": 2, "gutter": 12, "inset": 0, "columnsKind": "fixedWidth", "columnWidth": 120});
|
||||
s.execute("object.textFrameOptions", &p).unwrap();
|
||||
let b = s.doc().unwrap().doc.item(id).unwrap().inner_bounds();
|
||||
assert!((b.width() - (2.0 * 120.0 + 12.0)).abs() < 1e-6, "{b:?}");
|
||||
assert!((b.x0 - 36.0).abs() < 1e-6 && (b.height() - 264.0).abs() < 1e-6, "{b:?}");
|
||||
// Widths beyond the largest page are refused and change nothing.
|
||||
for (k, v) in [("columnWidth", 1e9), ("columnWidth", -1.0), ("firstBaselineMin", 1e9)] {
|
||||
assert!(s.execute("object.textFrameOptions", &json!({"ids": [id.0], k: v})).is_err(), "{k} {v}");
|
||||
}
|
||||
assert!((s.doc().unwrap().doc.item(id).unwrap().inner_bounds().width() - 252.0).abs() < 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +413,6 @@ fn pdf_export(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The options of the selected text frame (the first selected item, or the frame of the caret).
|
||||
/// The selected text frame's width (its own, before rotation).
|
||||
fn selected_frame_width(app: &DesignApp) -> Option<f64> {
|
||||
let st = app.session.active()?;
|
||||
@@ -421,6 +420,7 @@ fn selected_frame_width(app: &DesignApp) -> Option<f64> {
|
||||
Some(st.doc.item(fid)?.inner_bounds().width())
|
||||
}
|
||||
|
||||
/// The options of the selected text frame (the first selected item, or the frame of the caret).
|
||||
fn selected_frame_options(app: &DesignApp) -> Option<designcraft_doc::TextFrameOptions> {
|
||||
let st = app.session.active()?;
|
||||
let fid = st.selection.items.first().copied().or_else(|| st.selection.text.and_then(|t| t.frame))?;
|
||||
|
||||
Reference in New Issue
Block a user