diff --git a/crates/onestore/src/op/mod.rs b/crates/onestore/src/op/mod.rs index 87c649658b58639d1249351570b16eba83648a7e..df2655772cdfaa38c90fec42edc504b9521376b1 100644 --- a/crates/onestore/src/op/mod.rs +++ b/crates/onestore/src/op/mod.rs @@ -27,7 +27,7 @@ pub(crate) mod tests; pub use lower::{lower, lower_page}; /// `op` applied to `page`, the model of what the section holds: the page `Section::apply` /// leaves, as reading it back shows it. -pub use model::apply as predict; +pub use model::{apply as predict, title_line}; /// Property identifiers with their encoded values. pub(crate) type Values = Vec<(u32, Vec)>; diff --git a/crates/onestore/src/op/model.rs b/crates/onestore/src/op/model.rs index adefb6755d888744748d15a20ea518df5308e55f..b57f54c2e306471a4240941fb25f87220e1f5fdf 100644 --- a/crates/onestore/src/op/model.rs +++ b/crates/onestore/src/op/model.rs @@ -551,22 +551,25 @@ fn relevel(list: &mut [PageParagraph], index: usize, level: u32) { /// The title a page's writers store after changing it: the title text's first line when /// it has one, which only an edit of that text changes, else the first line of body text. fn retitle(page: &mut Page, edited: Option) { - let line = |text: &str| { - crate::edit::without_fields(text.trim_start().split('\r').next().unwrap_or_default()) - }; match find_title_text(page) { Some(title) => { let text = text_of(page, title); - if line(&text).is_empty() { + if title_line(&text).is_empty() { automatic(page); } else if edited == Some(title) { - page.title = line(&text); + page.title = title_line(&text); } } None => automatic(page), } } +/// The title stored for title text `text`: its first line, without leading space or fields. +/// Empty leaves the page titled by its body. +pub fn title_line(text: &str) -> String { + crate::edit::without_fields(text.trim_start().split('\r').next().unwrap_or_default()) +} + fn automatic(page: &mut Page) { let mut roots: Vec<&PageObject> = page .objects diff --git a/crates/snowbound/src/macos.rs b/crates/snowbound/src/macos.rs index ca9a567dd3418f0b68e3ab83b871b541f388515d..105fd0955eb41ff976ea1e0729e96a739606c431 100644 --- a/crates/snowbound/src/macos.rs +++ b/crates/snowbound/src/macos.rs @@ -1456,16 +1456,20 @@ pub fn text_colors(window: &Window) -> [[f32; 4]; 3] { } } -/// Whether the window takes commands: not while a native dialog is up over it. +/// Whether the window takes commands: not while a native dialog is up over it or another +/// of the application's windows has the keys. pub(crate) fn commands_allowed() -> bool { let app = NSApplication::sharedApplication( MainThreadMarker::new().expect("Commands run on the main thread"), ); WINDOW.with_borrow(|window| unsafe { app.modalWindow().is_none() - && window - .as_ref() - .is_none_or(|window| window.attachedSheet().is_none()) + && window.as_ref().is_none_or(|window| { + window.attachedSheet().is_none() + && app + .keyWindow() + .is_none_or(|key| std::ptr::eq(&*key, &**window)) + }) }) } diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index f79dd3074b10b5a70eaf5435f5c51ab69ecdd4a4..4fdd0fa24b109e247642dd8f862cbb83b3b209fe 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -4304,10 +4304,14 @@ impl State { }, )?; // The page list shows the title as it is typed, even when the caret left it since. - let title = (self.view.editor.outlines().iter()) + let editor = &self.view.editor; + let title = (editor.outlines().iter()) .find(|outline| outline.title) - .map(canvas::editor::TextOutline::shown_text); - if title.is_some_and(|title| title != session.title()) { + .and_then(|outline| outline.document().paragraphs().last()) + .map(|paragraph| onestore::op::title_line(paragraph.text())); + if editor.active_outline().title + || title.is_some_and(|title| !title.is_empty() && title != session.title()) + { session.pages = session.section.pages()?; } self.edited(vec![space]);