| author | |
| committer | |
| log | e3d4066dc407255c595ad64f3ec4aaed85e64628 |
| tree | 124b617d8f5fae8d69fef9aa985596a0734401d6 |
| parent | c355a943bcb3d015015c97abff850d15d654252f |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover) |
On macOS, commands wait while another of the app's windows, such as the About panel, has the
keys. The page list compares titles the way the store derives them, so pages whose stored title
differs from their title text no longer reread the list on every edit.
Refs #129
Assisted-by: claude-opus-5.54 files changed, 24 insertions(+), 13 deletions(-)
crates/onestore/src/op/mod.rs+1-1| ... | ... | @@ -27,7 +27,7 @@ pub(crate) mod tests; |
| 27 | 27 | pub use lower::{lower, lower_page}; |
| 28 | 28 | /// `op` applied to `page`, the model of what the section holds: the page `Section::apply` |
| 29 | 29 | /// leaves, as reading it back shows it. |
| 30 | pub use model::apply as predict; | |
| 30 | pub use model::{apply as predict, title_line}; | |
| 31 | 31 | |
| 32 | 32 | /// Property identifiers with their encoded values. |
| 33 | 33 | pub(crate) type Values = Vec<(u32, Vec<u8>)>; |
crates/onestore/src/op/model.rs+8-5| ... | ... | @@ -551,22 +551,25 @@ fn relevel(list: &mut [PageParagraph], index: usize, level: u32) { |
| 551 | 551 | /// The title a page's writers store after changing it: the title text's first line when |
| 552 | 552 | /// it has one, which only an edit of that text changes, else the first line of body text. |
| 553 | 553 | fn retitle(page: &mut Page, edited: Option<ExGuid>) { |
| 554 | let line = |text: &str| { | |
| 555 | crate::edit::without_fields(text.trim_start().split('\r').next().unwrap_or_default()) | |
| 556 | }; | |
| 557 | 554 | match find_title_text(page) { |
| 558 | 555 | Some(title) => { |
| 559 | 556 | let text = text_of(page, title); |
| 560 | if line(&text).is_empty() { | |
| 557 | if title_line(&text).is_empty() { | |
| 561 | 558 | automatic(page); |
| 562 | 559 | } else if edited == Some(title) { |
| 563 | page.title = line(&text); | |
| 560 | page.title = title_line(&text); | |
| 564 | 561 | } |
| 565 | 562 | } |
| 566 | 563 | None => automatic(page), |
| 567 | 564 | } |
| 568 | 565 | } |
| 569 | 566 | |
| 567 | /// The title stored for title text `text`: its first line, without leading space or fields. | |
| 568 | /// Empty leaves the page titled by its body. | |
| 569 | pub fn title_line(text: &str) -> String { | |
| 570 | crate::edit::without_fields(text.trim_start().split('\r').next().unwrap_or_default()) | |
| 571 | } | |
| 572 | ||
| 570 | 573 | fn automatic(page: &mut Page) { |
| 571 | 574 | let mut roots: Vec<&PageObject> = page |
| 572 | 575 | .objects |
crates/snowbound/src/macos.rs+8-4| ... | ... | @@ -1456,16 +1456,20 @@ pub fn text_colors(window: &Window) -> [[f32; 4]; 3] { |
| 1456 | 1456 | } |
| 1457 | 1457 | } |
| 1458 | 1458 | |
| 1459 | /// Whether the window takes commands: not while a native dialog is up over it. | |
| 1459 | /// Whether the window takes commands: not while a native dialog is up over it or another | |
| 1460 | /// of the application's windows has the keys. | |
| 1460 | 1461 | pub(crate) fn commands_allowed() -> bool { |
| 1461 | 1462 | let app = NSApplication::sharedApplication( |
| 1462 | 1463 | MainThreadMarker::new().expect("Commands run on the main thread"), |
| 1463 | 1464 | ); |
| 1464 | 1465 | WINDOW.with_borrow(|window| unsafe { |
| 1465 | 1466 | app.modalWindow().is_none() |
| 1466 | && window | |
| 1467 | .as_ref() | |
| 1468 | .is_none_or(|window| window.attachedSheet().is_none()) | |
| 1467 | && window.as_ref().is_none_or(|window| { | |
| 1468 | window.attachedSheet().is_none() | |
| 1469 | && app | |
| 1470 | .keyWindow() | |
| 1471 | .is_none_or(|key| std::ptr::eq(&*key, &**window)) | |
| 1472 | }) | |
| 1469 | 1473 | }) |
| 1470 | 1474 | } |
| 1471 | 1475 |
crates/snowbound/src/main.rs+7-3| ... | ... | @@ -4304,10 +4304,14 @@ impl State { |
| 4304 | 4304 | }, |
| 4305 | 4305 | )?; |
| 4306 | 4306 | // The page list shows the title as it is typed, even when the caret left it since. |
| 4307 | let title = (self.view.editor.outlines().iter()) | |
| 4307 | let editor = &self.view.editor; | |
| 4308 | let title = (editor.outlines().iter()) | |
| 4308 | 4309 | .find(|outline| outline.title) |
| 4309 | .map(canvas::editor::TextOutline::shown_text); | |
| 4310 | if title.is_some_and(|title| title != session.title()) { | |
| 4310 | .and_then(|outline| outline.document().paragraphs().last()) | |
| 4311 | .map(|paragraph| onestore::op::title_line(paragraph.text())); | |
| 4312 | if editor.active_outline().title | |
| 4313 | || title.is_some_and(|title| !title.is_empty() && title != session.title()) | |
| 4314 | { | |
| 4311 | 4315 | session.pages = session.section.pages()?; |
| 4312 | 4316 | } |
| 4313 | 4317 | self.edited(vec![space]); |