mirror of
https://github.com/storytold/wordcraft.git
synced 2026-11-02 16:30:05 -06:00
This commit is contained in:
@@ -2645,11 +2645,13 @@ fn rulers(app: &mut WordApp, ui: &mut Ui, h: Rect, v: Rect, rects: &[Rect], layo
|
||||
// Margins: the edges of the white text area drag the section's margins. Registered before
|
||||
// the indent markers, so a marker sitting on an edge keeps the pointer where they overlap.
|
||||
let (le, re) = (x0 + page.body.x * scale, x0 + page.body.right() * scale);
|
||||
// An even page of a mirrored layout is the odd page reflected: its left edge is the stored right margin.
|
||||
let mirrored = app.session.doc.settings.mirror_margins && page.number.is_multiple_of(2);
|
||||
for (edge, zone) in [
|
||||
(MarginEdge::Left, Rect::from_min_max(pos2(le - 10.0, bar.min.y), pos2(le + 3.0, bar.max.y))),
|
||||
(MarginEdge::Right, Rect::from_min_max(pos2(re - 3.0, bar.min.y), pos2(re + 10.0, bar.max.y))),
|
||||
] {
|
||||
margin_handle(app, ui, edge, zone, |p| (p.x - x0) / scale, §);
|
||||
margin_handle(app, ui, edge, zone, |p| (p.x - x0) / scale, §, mirrored);
|
||||
}
|
||||
// Indent markers for the caret's paragraph.
|
||||
if let Some(rp) = rp {
|
||||
@@ -2733,7 +2735,7 @@ fn rulers(app: &mut WordApp, ui: &mut Ui, h: Rect, v: Rect, rects: &[Rect], layo
|
||||
vp.rect_filled(Rect::from_min_max(pos2(vbar.min.x, te), pos2(vbar.max.x, be)), 0.0, t.ruler);
|
||||
for (edge, ey) in [(MarginEdge::Top, te), (MarginEdge::Bottom, be)] {
|
||||
let zone = Rect::from_min_max(pos2(vbar.min.x, ey - 4.0), pos2(vbar.max.x, ey + 4.0));
|
||||
margin_handle(app, ui, edge, zone, |p| (p.y - y0) / scale, §);
|
||||
margin_handle(app, ui, edge, zone, |p| (p.y - y0) / scale, §, false);
|
||||
}
|
||||
// The same graduations down the page; the numbers keep clear of a table's row markers.
|
||||
let keep_clear = crate::table_ruler::row_markers(app, ui.ctx(), layout, pi, vbar, y0, scale);
|
||||
@@ -2934,9 +2936,31 @@ pub(crate) fn dragged_margin(edge: MarginEdge, at: f32, s: &wordcraft_doc::secti
|
||||
((raw / 4.5).round() * 4.5).clamp(0.0, room.max(0.0))
|
||||
}
|
||||
|
||||
/// The stored margin a drag of the visible `edge` to `at` points edits, and its new value. On an
|
||||
/// even page of a mirrored layout (`mirrored`) the text area is the odd page's reflected, so the
|
||||
/// visible left edge is the stored right margin and the other way round: the pointer is reflected
|
||||
/// across the page and the edge swapped (#2914). Top and bottom are the same on every page.
|
||||
pub(crate) fn margin_drag(edge: MarginEdge, at: f32, s: &wordcraft_doc::section::SectionProps, mirrored: bool) -> (MarginEdge, f32) {
|
||||
let (edge, at) = match edge {
|
||||
MarginEdge::Left if mirrored => (MarginEdge::Right, s.page_w - at),
|
||||
MarginEdge::Right if mirrored => (MarginEdge::Left, s.page_w - at),
|
||||
e => (e, at),
|
||||
};
|
||||
(edge, dragged_margin(edge, at, s))
|
||||
}
|
||||
|
||||
/// A draggable margin edge on a ruler: `to_pt` turns the pointer into points from the page's
|
||||
/// left (or top) edge. A whole drag is one undo step, through `layout.margins`.
|
||||
fn margin_handle(app: &mut WordApp, ui: &Ui, edge: MarginEdge, zone: Rect, to_pt: impl Fn(Pos2) -> f32, sect: &wordcraft_doc::section::SectionProps) {
|
||||
/// left (or top) edge, and `mirrored` is an even page of a mirrored layout ([`margin_drag`]). A
|
||||
/// whole drag is one undo step, through `layout.margins`.
|
||||
fn margin_handle(
|
||||
app: &mut WordApp,
|
||||
ui: &Ui,
|
||||
edge: MarginEdge,
|
||||
zone: Rect,
|
||||
to_pt: impl Fn(Pos2) -> f32,
|
||||
sect: &wordcraft_doc::section::SectionProps,
|
||||
mirrored: bool,
|
||||
) {
|
||||
let cursor =
|
||||
if matches!(edge, MarginEdge::Left | MarginEdge::Right) { egui::CursorIcon::ResizeHorizontal } else { egui::CursorIcon::ResizeVertical };
|
||||
let r = ui.interact(zone, ui.id().with(("ruler_margin", edge)), Sense::drag()).on_hover_cursor(cursor);
|
||||
@@ -2944,11 +2968,11 @@ fn margin_handle(app: &mut WordApp, ui: &Ui, edge: MarginEdge, zone: Rect, to_pt
|
||||
if r.dragged()
|
||||
&& let Some(pp) = r.interact_pointer_pos()
|
||||
{
|
||||
let v = dragged_margin(edge, to_pt(pp), sect);
|
||||
let (stored, v) = margin_drag(edge, to_pt(pp), sect, mirrored);
|
||||
if !r.drag_started() {
|
||||
app.session.join_next_undo();
|
||||
}
|
||||
let _ = app.run("layout.margins", json!({ edge.param(): v }));
|
||||
let _ = app.run("layout.margins", json!({ stored.param(): v }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3657,6 +3681,50 @@ mod tests {
|
||||
assert_eq!(wordcraft_engine::cmd::page::sect(&app.session).margin_left, sect.margin_left);
|
||||
}
|
||||
|
||||
/// #2914: on an even page of a mirrored layout the page is the odd page reflected, so dragging
|
||||
/// its visible left edge edits the stored right margin and the visible right edge the left one.
|
||||
#[test]
|
||||
fn ruler_margin_drags_on_even_mirrored_pages_move_the_visible_edge() {
|
||||
let mut app = WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::from_text("one")), Default::default());
|
||||
app.session.doc.settings.mirror_margins = true;
|
||||
app.run("layout.margins", json!({"left": 72.0, "right": 36.0})).unwrap();
|
||||
app.run("caret.docEnd", json!({})).unwrap();
|
||||
app.run("insert.pageBreak", json!({})).unwrap();
|
||||
let sect = wordcraft_engine::cmd::page::sect(&app.session);
|
||||
let body = |app: &mut WordApp, page: usize| {
|
||||
let l = app.session.layout();
|
||||
let b = l.pages.get(page).expect("the page exists").body;
|
||||
(b.x, b.right(), l.pages[page].number)
|
||||
};
|
||||
let (x, right, number) = body(&mut app, 1);
|
||||
assert_eq!(number, 2, "the second page is even");
|
||||
assert!((x - 36.0).abs() < 0.01 && (right - (sect.page_w - 72.0)).abs() < 0.01, "mirrored: outside margin on the left ({x}, {right})");
|
||||
// Odd pages and unmirrored layouts keep the plain mapping.
|
||||
assert_eq!(margin_drag(MarginEdge::Left, 90.0, §, false), (MarginEdge::Left, 90.0));
|
||||
assert_eq!(margin_drag(MarginEdge::Right, sect.page_w - 90.0, §, false), (MarginEdge::Right, 90.0));
|
||||
|
||||
// The visible left edge of the even page, dragged to 90 pt, becomes the right margin.
|
||||
let (edge, v) = margin_drag(MarginEdge::Left, 90.0, §, true);
|
||||
assert_eq!(edge, MarginEdge::Right);
|
||||
app.run("layout.margins", json!({edge.param(): v})).unwrap();
|
||||
let after = wordcraft_engine::cmd::page::sect(&app.session);
|
||||
assert_eq!((after.margin_left, after.margin_right), (72.0, 90.0), "the left margin was not touched");
|
||||
let (x, right, _) = body(&mut app, 1);
|
||||
assert!((x - 90.0).abs() < 0.01 && (right - (sect.page_w - 72.0)).abs() < 0.01, "the visible left edge moved to the pointer ({x}, {right})");
|
||||
let (x, right, _) = body(&mut app, 0);
|
||||
assert!((x - 72.0).abs() < 0.01 && (right - (sect.page_w - 90.0)).abs() < 0.01, "the odd page follows ({x}, {right})");
|
||||
|
||||
// The visible right edge, dragged to 500 pt, becomes the left margin (snapped to 1/16 inch).
|
||||
let sect = after;
|
||||
let (edge, v) = margin_drag(MarginEdge::Right, 500.0, §, true);
|
||||
assert_eq!(edge, MarginEdge::Left);
|
||||
app.run("layout.margins", json!({edge.param(): v})).unwrap();
|
||||
let (_, right, _) = body(&mut app, 1);
|
||||
assert!((right - 500.0).abs() <= 2.25, "the visible right edge is at the pointer ({right})");
|
||||
// Top and bottom are not mirrored.
|
||||
assert_eq!(margin_drag(MarginEdge::Top, 50.0, §, true), (MarginEdge::Top, dragged_margin(MarginEdge::Top, 50.0, §)));
|
||||
}
|
||||
|
||||
/// Show revisions in balloons: a document whose only revision is a deletion gets the markup
|
||||
/// area for its balloon; the other modes have nothing to show there, and neither does the view
|
||||
/// while the Comments pane is open.
|
||||
|
||||
Reference in New Issue
Block a user