Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions crates/ui-egui/src/containers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ pub fn show(
let mut delete_object: Option<usize> = None;
// An empty box that was clicked: it has no words to answer a click, so the click starts some.
let mut start_in: Option<usize> = None;
// A new page's box clicked below its date: the words start on a line under the date.
let mut start_under_date: Option<usize> = None;
let right_click = ui.input(|i| i.pointer.secondary_clicked());
let pointer = ui.ctx().pointer_latest_pos();
let mut opened_now = false;
Expand Down Expand Up @@ -476,6 +478,22 @@ pub fn show(
filled_bottom = filled_bottom.max(frame.max.y);
}

// A new page's box holds only its date and is drawn tall, as OneNote's first note area is.
// Below the date there was no block to take a click, so the space looked ready for
// writing and swallowed what was typed (issue #148). A click there starts a line under
// the date.
if range.len() == 1 && pageedit::is_page_stamp(blocks, range.start) {
let under_date = Rect::from_min_max(pos2(frame.min.x, child.min_rect().max.y), frame.max);
if under_date.height() > 0.0 {
let start = ui.interact(under_date, ui.id().with(("container-under-date", i)), Sense::click());
if start.hovered() {
ui.ctx().set_cursor_icon(egui::CursorIcon::Text);
}
if start.clicked() {
start_under_date = Some(i);
}
}
}
let hovered = ui.rect_contains_pointer(frame);
// A box with nothing in it has no block under the pointer, so a click inside it went
// nowhere and no caret appeared. The whole of it, below the handle, is a place to start
Expand Down Expand Up @@ -1142,6 +1160,34 @@ pub fn show(
ui.ctx().request_repaint();
}

if let Some(i) = start_under_date
&& start_in.is_none()
&& delete_at.is_none()
&& container::blocks_of(boxes, i).len() == 1
{
let at = container::blocks_of(boxes, i).end.min(blocks.len());
blocks.insert(at, Block::Paragraph(Vec::new()));
out.changed.columns |= cols.insert_block(at);
out.justified |= justified.insert_block(at);
if let Some(c) = boxes.get_mut(i) {
c.blocks += 1;
}
*caret = Some(pageedit::Caret {
block: at,
text: String::new(),
focus: true,
selection: (0, 0),
place: None,
cell: None,
span: None,
inline: None,
});
out.changed.blocks = true;
out.changed.caret = true;
out.boxes = true;
ui.ctx().request_repaint();
}

// Remove an embedded block after it has been drawn, so the current frame's
// hit testing and selection affordance remain stable.
if let Some(block) = delete_object
Expand Down
32 changes: 32 additions & 0 deletions crates/ui-egui/tests/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,38 @@ fn clicking_a_page_opens_it_and_a_container_toggles() {
review(&mut h, "markdown_page");
}

/// Issue #148: on a new page, a click in the blank space under the date line puts a caret
/// there, and what is typed is kept, in both formats.
#[test]
fn a_click_under_a_new_pages_date_starts_writing() {
for format in ["html", "cmgfm"] {
let (_t, app, s) = sample_app("ui-new-page-click");
let mut h = harness(app, LIGHT);
h.run();
let section = s.md_page.parent().expect("section").to_string_lossy().into_owned();
let made = ask(&mut h, "engine.execute", json!({"command": "node.create", "params": {"parent": section, "kind": "page", "format": format}}))
.expect("page");
let page = made.as_str().expect("a path").to_string();
ask(&mut h, "ui.open", json!({"path": page})).expect("open");
settle(&mut h);
let state = ask(&mut h, "ui.inspect", json!({})).expect("inspect");
let origin = state.pointer("/page/origin").and_then(Value::as_array).expect("page origin");
let (ox, oy) = (origin[0].as_f64().unwrap_or(0.0), origin[1].as_f64().unwrap_or(0.0));

for (dx, dy) in [(60.0, 90.0), (200.0, 180.0)] {
ask(&mut h, "ui.click", json!({"x": ox + dx, "y": oy + dy})).expect("click under the date");
settle(&mut h);
let caret = ask(&mut h, "ui.inspect", json!({})).expect("inspect")["page"]["caret"].clone();
assert!(!caret.is_null(), "{format}: a click at +{dx},+{dy} under the date puts a caret: {state}");
}
ask(&mut h, "ui.text", json!({"text": "TEST EMPTY AREA"})).expect("type");
settle(&mut h);
h.state_mut().save_page_now();
let saved = ask(&mut h, "engine.execute", json!({"command": "page.load", "params": {"path": page}})).expect("load");
assert!(saved["body"].as_str().is_some_and(|b| b.contains("TEST EMPTY AREA")), "{format}: what was typed is kept: {saved}");
}
}

/// The caret lives in the page: clicking a block and typing changes that block, and the page
/// is written back in its own format. This is the editor the owner asked for, so it is tested
/// for both formats.
Expand Down
Loading