mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Conflicts: crates/ui-egui/src/canvas.rs (cursor match: kept #806's resize_cursor arm inside #846's painter-armed wrapper)
This commit is contained in:
@@ -171,6 +171,7 @@ app's JSON control channel (`deckcraft --control PORT`).
|
||||
| 2026-10-15 | A `.pptx` slide that can't be read is reported, not dropped in silence: `lostSlides` from `file.open`, a message and a repaired copy in the app, a warning from `deckcraft-cli` (#203) |
|
||||
| 2026-10-16 | AutoFormat as you type: ordinals (1st, 22nd, 103rd) get superscript suffixes after a space or punctuation; Tools ▸ AutoCorrect and `text.autocorrect {ordinals}` turn it off |
|
||||
| 2026-10-16 | A PowerPoint Show (`.ppsx`) opened from the command line or the file association starts its slide show, as in PowerPoint; File › Open still opens it for editing (#862) |
|
||||
| 2026-10-15 | Canvas: the Format and Animation Painter override the cursor to show the tool is armed (#830) |
|
||||
| 2026-10-15 | PDF export and rendering: `anchorCtr` centres the drawn text block — right-aligned text no longer ran past the box edge (#736) |
|
||||
| 2026-10-14 | connected MCP rejects unrelated, malformed, incomplete and oversized replies without replay; buffered unsolicited bytes force a fresh connection before sending (#718) |
|
||||
| 2026-10-14 | Empty bulleted and numbered paragraphs show gray markers while editing, including on first click and after Enter. Markers use the list style and stay out of slide shows and exports. |
|
||||
|
||||
@@ -889,29 +889,35 @@ fn pointer(app: &mut SlideApp, ui: &Ui, resp: &egui::Response, xf: Xf) {
|
||||
if let Err(e) = r {
|
||||
app.set_status(e.to_string());
|
||||
}
|
||||
// Cursor.
|
||||
let cursor = match (&app.session.tool.kind, &app.session.tool.hover) {
|
||||
(ToolKind::Shape { .. }, _) => egui::CursorIcon::Crosshair,
|
||||
(ToolKind::TextBox, _) => egui::CursorIcon::Text,
|
||||
(ToolKind::Ink { .. }, _) => egui::CursorIcon::Crosshair,
|
||||
(_, Some(Hit::Resize { id, handle })) => resize_cursor(&app.session, *id, *handle),
|
||||
(_, Some(Hit::Rotate { .. })) => egui::CursorIcon::Alias,
|
||||
(_, Some(Hit::TableColumn { .. })) => egui::CursorIcon::ResizeColumn,
|
||||
(_, Some(Hit::TableRow { .. })) => egui::CursorIcon::ResizeRow,
|
||||
(_, Some(Hit::Adjust { .. } | Hit::Warp { .. } | Hit::LineEnd { .. })) => egui::CursorIcon::PointingHand,
|
||||
(_, Some(Hit::Shape { text: true, .. })) => {
|
||||
if app.session.active().is_some_and(|d| d.selection.text.is_some()) {
|
||||
egui::CursorIcon::Text
|
||||
} else {
|
||||
egui::CursorIcon::Move
|
||||
// Cursor. The Format and Animation Painters override everything: their whole point is to
|
||||
// tell the user the tool is armed and waiting for a click (#830).
|
||||
let painter_armed = app.session.painter.is_some() || app.session.anim_painter.is_some();
|
||||
let cursor = if painter_armed {
|
||||
egui::CursorIcon::Copy
|
||||
} else {
|
||||
match (&app.session.tool.kind, &app.session.tool.hover) {
|
||||
(ToolKind::Shape { .. }, _) => egui::CursorIcon::Crosshair,
|
||||
(ToolKind::TextBox, _) => egui::CursorIcon::Text,
|
||||
(ToolKind::Ink { .. }, _) => egui::CursorIcon::Crosshair,
|
||||
(_, Some(Hit::Resize { id, handle })) => resize_cursor(&app.session, *id, *handle),
|
||||
(_, Some(Hit::Rotate { .. })) => egui::CursorIcon::Alias,
|
||||
(_, Some(Hit::TableColumn { .. })) => egui::CursorIcon::ResizeColumn,
|
||||
(_, Some(Hit::TableRow { .. })) => egui::CursorIcon::ResizeRow,
|
||||
(_, Some(Hit::Adjust { .. } | Hit::Warp { .. } | Hit::LineEnd { .. })) => egui::CursorIcon::PointingHand,
|
||||
(_, Some(Hit::Shape { text: true, .. })) => {
|
||||
if app.session.active().is_some_and(|d| d.selection.text.is_some()) {
|
||||
egui::CursorIcon::Text
|
||||
} else {
|
||||
egui::CursorIcon::Move
|
||||
}
|
||||
}
|
||||
(_, Some(Hit::Shape { .. } | Hit::Frame { .. })) => egui::CursorIcon::Move,
|
||||
_ => match app.session.tool.guide_drag.map(|g| g.0).or(app.session.tool.guide_hover) {
|
||||
Some(i) if app.session.active().and_then(|d| d.doc.guides.get(i)).is_some_and(|g| g.vertical) => egui::CursorIcon::ResizeHorizontal,
|
||||
Some(_) => egui::CursorIcon::ResizeVertical,
|
||||
None => egui::CursorIcon::Default,
|
||||
},
|
||||
}
|
||||
(_, Some(Hit::Shape { .. } | Hit::Frame { .. })) => egui::CursorIcon::Move,
|
||||
_ => match app.session.tool.guide_drag.map(|g| g.0).or(app.session.tool.guide_hover) {
|
||||
Some(i) if app.session.active().and_then(|d| d.doc.guides.get(i)).is_some_and(|g| g.vertical) => egui::CursorIcon::ResizeHorizontal,
|
||||
Some(_) => egui::CursorIcon::ResizeVertical,
|
||||
None => egui::CursorIcon::Default,
|
||||
},
|
||||
};
|
||||
if resp.hovered() {
|
||||
ui.ctx().set_cursor_icon(cursor);
|
||||
@@ -1988,6 +1994,37 @@ mod tests {
|
||||
assert!(super::grid_line_step(6.0, 0.0).is_none());
|
||||
assert!(super::grid_line_step(6.0, 0.1).is_none(), "a 6 pt grid at 10% zoom is finer than one screen point");
|
||||
}
|
||||
|
||||
/// #830: when the Format or Animation Painter is armed, the cursor shows the copy icon so the
|
||||
/// user can see the tool is waiting for a click.
|
||||
#[test]
|
||||
fn format_painter_sets_the_cursor() {
|
||||
let mut h = harness();
|
||||
// Hover over the canvas so the pointer function runs and sets the cursor.
|
||||
h.hover_at(egui::pos2(600.0, 400.0));
|
||||
h.run_steps(2);
|
||||
let before = h.output().platform_output.cursor_icon;
|
||||
assert_ne!(before, egui::CursorIcon::Copy, "no painter is armed yet");
|
||||
// Arming the format painter overrides the cursor to Copy (#830).
|
||||
h.state_mut().session.painter = Some((Default::default(), false));
|
||||
h.run_steps(3);
|
||||
let after = h.output().platform_output.cursor_icon;
|
||||
assert_eq!(after, egui::CursorIcon::Copy, "the format painter arms the Copy cursor");
|
||||
}
|
||||
|
||||
/// #830: the Animation Painter arms the same cursor.
|
||||
#[test]
|
||||
fn animation_painter_sets_the_cursor() {
|
||||
let mut h = harness();
|
||||
h.hover_at(egui::pos2(600.0, 400.0));
|
||||
h.run_steps(2);
|
||||
let before = h.output().platform_output.cursor_icon;
|
||||
assert_ne!(before, egui::CursorIcon::Copy, "no painter is armed yet");
|
||||
h.state_mut().session.anim_painter = Some((vec![], false));
|
||||
h.run_steps(3);
|
||||
let after = h.output().platform_output.cursor_icon;
|
||||
assert_eq!(after, egui::CursorIcon::Copy, "the animation painter arms the Copy cursor");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user