| author | |
| committer | |
| log | 4534c84f0003ef1bc0827ad8d1ab37a6ce30655f |
| tree | 791cd40813ab1bbc8239e1b17b2d248b2045a5ae |
| parent | f6222caecf1c286dee2a1746307bf90d715044cf |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover) |
Splitting the window needs everything one page view owns in one place. State.pane now holds the view, which page it shows (Showing: the space, the open version, the conflict pages and versions listed beneath it, the selected conflicting change), its loads in flight, Back and Forward, the stale and focus flags, and the locked section or sectionless notebook it shows instead of a page. Session keeps only what belongs to the section, so one section can later back two panes; its methods that depended on the open page take a Showing, and loads hand over the page they read beside the session.
The recent pages leave Trail for State, since they are one list kept in the settings while Back and Forward become each pane's own.
Nothing visible or behavioural changes: there is still one pane.
Assisted-by: claude-opus-5.529 files changed, 667 insertions(+), 561 deletions(-)
crates/snowbound/src/attachment.rs+13-6| ... | @@ -10,10 +10,14 @@ use std::{ | ... | @@ -10,10 +10,14 @@ use std::{ |
| 10 | impl State { | 10 | impl State { |
| 11 | /// Inserts a file at the caret or window point `at`, as a picture when requested. | 11 | /// Inserts a file at the caret or window point `at`, as a picture when requested. |
| 12 | pub(crate) fn import_file(&mut self, path: &Path, at: Option<[f32; 2]>, picture: bool) { | 12 | pub(crate) fn import_file(&mut self, path: &Path, at: Option<[f32; 2]>, picture: bool) { |
| 13 | if self.session.as_ref().is_some_and(crate::Session::read_only) { | 13 | if self |
| 14 | .session | ||
| 15 | .as_ref() | ||
| 16 | .is_some_and(|session| session.read_only(&self.pane.showing)) | ||
| 17 | { | ||
| 14 | return; | 18 | return; |
| 15 | } | 19 | } |
| 16 | let loading = self.loading; | 20 | let loading = self.pane.loading; |
| 17 | let reply = self.reply( | 21 | let reply = self.reply( |
| 18 | move |state, (path, read): (PathBuf, std::io::Result<Vec<u8>>)| { | 22 | move |state, (path, read): (PathBuf, std::io::Result<Vec<u8>>)| { |
| 19 | let bytes = match read { | 23 | let bytes = match read { |
| ... | @@ -27,7 +31,7 @@ impl State { | ... | @@ -27,7 +31,7 @@ impl State { |
| 27 | return Ok(()); | 31 | return Ok(()); |
| 28 | } | 32 | } |
| 29 | }; | 33 | }; |
| 30 | if state.loading != loading { | 34 | if state.pane.loading != loading { |
| 31 | platform::alert( | 35 | platform::alert( |
| 32 | "File wasn't inserted", | 36 | "File wasn't inserted", |
| 33 | "Return to the page and insert the file again.", | 37 | "Return to the page and insert the file again.", |
| ... | @@ -37,7 +41,7 @@ impl State { | ... | @@ -37,7 +41,7 @@ impl State { |
| 37 | if state | 41 | if state |
| 38 | .session | 42 | .session |
| 39 | .as_ref() | 43 | .as_ref() |
| 40 | .is_some_and(crate::Session::read_only) | 44 | .is_some_and(|session| session.read_only(&state.pane.showing)) |
| 41 | { | 45 | { |
| 42 | return Ok(()); | 46 | return Ok(()); |
| 43 | } | 47 | } |
| ... | @@ -60,8 +64,11 @@ impl State { | ... | @@ -60,8 +64,11 @@ impl State { |
| 60 | tags: Vec::new(), | 64 | tags: Vec::new(), |
| 61 | }; | 65 | }; |
| 62 | let response = match at { | 66 | let response = match at { |
| 63 | Some(point) => state.view.drop_attachment(state.page_point(point), file)?, | 67 | Some(point) => state |
| 64 | None => state.view.insert_attachment(file)?, | 68 | .pane |
| 69 | .view | ||
| 70 | .drop_attachment(state.page_point(point), file)?, | ||
| 71 | None => state.pane.view.insert_attachment(file)?, | ||
| 65 | }; | 72 | }; |
| 66 | state.respond(response); | 73 | state.respond(response); |
| 67 | Ok(()) | 74 | Ok(()) |
crates/snowbound/src/commands.rs+44-42| ... | @@ -1070,7 +1070,7 @@ impl State { | ... | @@ -1070,7 +1070,7 @@ impl State { |
| 1070 | } | 1070 | } |
| 1071 | 1071 | ||
| 1072 | pub(crate) fn format_state(&self) -> FormatState { | 1072 | pub(crate) fn format_state(&self) -> FormatState { |
| 1073 | self.view.editor.format_state().unwrap_or_default() | 1073 | self.pane.view.editor.format_state().unwrap_or_default() |
| 1074 | } | 1074 | } |
| 1075 | 1075 | ||
| 1076 | /// Queues `choice` to run after this frame's input, as a key or a click on it does. | 1076 | /// Queues `choice` to run after this frame's input, as a key or a click on it does. |
| ... | @@ -1127,7 +1127,7 @@ impl State { | ... | @@ -1127,7 +1127,7 @@ impl State { |
| 1127 | 1127 | ||
| 1128 | pub(crate) fn status(&self, choice: &Choice, format: &FormatState) -> Status { | 1128 | pub(crate) fn status(&self, choice: &Choice, format: &FormatState) -> Status { |
| 1129 | let session = self.session.as_ref(); | 1129 | let session = self.session.as_ref(); |
| 1130 | let welcome = session.is_none() && !self.temporary && self.sectionless.is_none(); | 1130 | let welcome = session.is_none() && !self.temporary && self.pane.sectionless.is_none(); |
| 1131 | let modal = self.options.is_some() | 1131 | let modal = self.options.is_some() |
| 1132 | || self.password.is_some() | 1132 | || self.password.is_some() |
| 1133 | || self.themes.is_some() | 1133 | || self.themes.is_some() |
| ... | @@ -1138,13 +1138,14 @@ impl State { | ... | @@ -1138,13 +1138,14 @@ impl State { |
| 1138 | #[cfg(any(windows, target_os = "linux"))] | 1138 | #[cfg(any(windows, target_os = "linux"))] |
| 1139 | let modal = modal || self.prompt.strong_count() != 0; | 1139 | let modal = modal || self.prompt.strong_count() != 0; |
| 1140 | let page = (session.is_some() || self.temporary) && !modal; | 1140 | let page = (session.is_some() || self.temporary) && !modal; |
| 1141 | let writable = page && !session.is_some_and(|session| session.read_only()); | 1141 | let writable = |
| 1142 | page && !session.is_some_and(|session| session.read_only(&self.pane.showing)); | ||
| 1142 | // Picked drawings leave the text selection behind them unseen, so it takes no edits. | 1143 | // Picked drawings leave the text selection behind them unseen, so it takes no edits. |
| 1143 | let typing = self.view.accepts_text() && self.view.ink_selection().is_empty(); | 1144 | let typing = self.pane.view.accepts_text() && self.pane.view.ink_selection().is_empty(); |
| 1144 | let text = writable && typing; | 1145 | let text = writable && typing; |
| 1145 | let tagged = writable | 1146 | let tagged = writable |
| 1146 | && matches!( | 1147 | && matches!( |
| 1147 | self.view.object_focus(), | 1148 | self.pane.view.object_focus(), |
| 1148 | Some( | 1149 | Some( |
| 1149 | canvas::interaction::ObjectFocus::Image(_) | 1150 | canvas::interaction::ObjectFocus::Image(_) |
| 1150 | | canvas::interaction::ObjectFocus::File(_) | 1151 | | canvas::interaction::ObjectFocus::File(_) |
| ... | @@ -1152,12 +1153,12 @@ impl State { | ... | @@ -1152,12 +1153,12 @@ impl State { |
| 1152 | ); | 1153 | ); |
| 1153 | // Palette commands act after its search field returns focus to the editor. | 1154 | // Palette commands act after its search field returns focus to the editor. |
| 1154 | let field = self.ui.focused_field(Some(crate::palette::id())).is_some(); | 1155 | let field = self.ui.focused_field(Some(crate::palette::id())).is_some(); |
| 1155 | let [anchor, focus] = self.view.editor.selection().positions; | 1156 | let [anchor, focus] = self.pane.view.editor.selection().positions; |
| 1156 | let selected = page | 1157 | let selected = page |
| 1157 | && !field | 1158 | && !field |
| 1158 | && (typing && anchor != focus | 1159 | && (typing && anchor != focus |
| 1159 | || matches!( | 1160 | || matches!( |
| 1160 | self.view.object_focus(), | 1161 | self.pane.view.object_focus(), |
| 1161 | Some(canvas::interaction::ObjectFocus::Image(_)) | 1162 | Some(canvas::interaction::ObjectFocus::Image(_)) |
| 1162 | )); | 1163 | )); |
| 1163 | let enabled = |enabled| Status { | 1164 | let enabled = |enabled| Status { |
| ... | @@ -1178,7 +1179,7 @@ impl State { | ... | @@ -1178,7 +1179,7 @@ impl State { |
| 1178 | Choice::Pen(_) => Id::Pen, | 1179 | Choice::Pen(_) => Id::Pen, |
| 1179 | Choice::Symbol(_) => Id::Symbol, | 1180 | Choice::Symbol(_) => Id::Symbol, |
| 1180 | }; | 1181 | }; |
| 1181 | let tool = self.view.tool(); | 1182 | let tool = self.pane.view.tool(); |
| 1182 | match id { | 1183 | match id { |
| 1183 | Id::Settings | 1184 | Id::Settings |
| 1184 | | Id::NewNotebook | 1185 | | Id::NewNotebook |
| ... | @@ -1267,8 +1268,8 @@ impl State { | ... | @@ -1267,8 +1268,8 @@ impl State { |
| 1267 | checked: Some(false), | 1268 | checked: Some(false), |
| 1268 | }, | 1269 | }, |
| 1269 | |session| Status { | 1270 | |session| Status { |
| 1270 | enabled: !session.page_versions(session.space).is_empty(), | 1271 | enabled: !session.page_versions(self.pane.showing.space).is_empty(), |
| 1271 | checked: Some(session.shown_history == Some(session.space)), | 1272 | checked: Some(self.pane.showing.shown_history == Some(self.pane.showing.space)), |
| 1272 | }, | 1273 | }, |
| 1273 | ), | 1274 | ), |
| 1274 | Id::FullPageView => Status { | 1275 | Id::FullPageView => Status { |
| ... | @@ -1312,7 +1313,7 @@ impl State { | ... | @@ -1312,7 +1313,7 @@ impl State { |
| 1312 | }, | 1313 | }, |
| 1313 | Id::SnapToGrid => Status { | 1314 | Id::SnapToGrid => Status { |
| 1314 | enabled: !modal, | 1315 | enabled: !modal, |
| 1315 | checked: Some(self.view.snap_to_grid), | 1316 | checked: Some(self.pane.view.snap_to_grid), |
| 1316 | }, | 1317 | }, |
| 1317 | Id::HideSpelling => Status { | 1318 | Id::HideSpelling => Status { |
| 1318 | enabled: !modal && self.spelling.is_some(), | 1319 | enabled: !modal && self.spelling.is_some(), |
| ... | @@ -1320,9 +1321,9 @@ impl State { | ... | @@ -1320,9 +1321,9 @@ impl State { |
| 1320 | }, | 1321 | }, |
| 1321 | Id::MarkdownShortcuts => Status { | 1322 | Id::MarkdownShortcuts => Status { |
| 1322 | enabled: !modal, | 1323 | enabled: !modal, |
| 1323 | checked: Some(self.view.editor.markdown.is_some()), | 1324 | checked: Some(self.pane.view.editor.markdown.is_some()), |
| 1324 | }, | 1325 | }, |
| 1325 | Id::Spelling => enabled(text && self.view.spelling.is_some()), | 1326 | Id::Spelling => enabled(text && self.pane.view.spelling.is_some()), |
| 1326 | Id::FormatPainter => Status { | 1327 | Id::FormatPainter => Status { |
| 1327 | enabled: text, | 1328 | enabled: text, |
| 1328 | checked: Some(self.painter.is_some()), | 1329 | checked: Some(self.painter.is_some()), |
| ... | @@ -1343,7 +1344,7 @@ impl State { | ... | @@ -1343,7 +1344,7 @@ impl State { |
| 1343 | Id::Transport(transport) => self.transport_status(transport), | 1344 | Id::Transport(transport) => self.transport_status(transport), |
| 1344 | Id::InsertSpace => Status { | 1345 | Id::InsertSpace => Status { |
| 1345 | enabled: writable, | 1346 | enabled: writable, |
| 1346 | checked: Some(self.view.inserting_space()), | 1347 | checked: Some(self.pane.view.inserting_space()), |
| 1347 | }, | 1348 | }, |
| 1348 | Id::SelectType => Status { | 1349 | Id::SelectType => Status { |
| 1349 | enabled: page, | 1350 | enabled: page, |
| ... | @@ -1427,7 +1428,7 @@ impl State { | ... | @@ -1427,7 +1428,7 @@ impl State { |
| 1427 | if matches!(formatting, Formatting::Font(_)) || self.toolbar != kept { | 1428 | if matches!(formatting, Formatting::Font(_)) || self.toolbar != kept { |
| 1428 | self.save_settings(); | 1429 | self.save_settings(); |
| 1429 | } | 1430 | } |
| 1430 | let response = self.view.format(formatting)?; | 1431 | let response = self.pane.view.format(formatting)?; |
| 1431 | self.respond(response); | 1432 | self.respond(response); |
| 1432 | Ok(()) | 1433 | Ok(()) |
| 1433 | } | 1434 | } |
| ... | @@ -1442,7 +1443,7 @@ impl State { | ... | @@ -1442,7 +1443,7 @@ impl State { |
| 1442 | self.session | 1443 | self.session |
| 1443 | .as_ref() | 1444 | .as_ref() |
| 1444 | .map(|session| &session.library) | 1445 | .map(|session| &session.library) |
| 1445 | .or(self.sectionless.as_ref()) | 1446 | .or(self.pane.sectionless.as_ref()) |
| 1446 | } | 1447 | } |
| 1447 | 1448 | ||
| 1448 | /// Runs `choice` where it applies now. | 1449 | /// Runs `choice` where it applies now. |
| ... | @@ -1463,22 +1464,22 @@ impl State { | ... | @@ -1463,22 +1464,22 @@ impl State { |
| 1463 | Choice::Color(color) => return format(self, Formatting::Color(color)), | 1464 | Choice::Color(color) => return format(self, Formatting::Color(color)), |
| 1464 | Choice::List(style) => return format(self, Formatting::List(style)), | 1465 | Choice::List(style) => return format(self, Formatting::List(style)), |
| 1465 | Choice::Table([rows, columns]) => { | 1466 | Choice::Table([rows, columns]) => { |
| 1466 | let response = self.view.insert_table(rows, columns)?; | 1467 | let response = self.pane.view.insert_table(rows, columns)?; |
| 1467 | self.respond(response); | 1468 | self.respond(response); |
| 1468 | return Ok(()); | 1469 | return Ok(()); |
| 1469 | } | 1470 | } |
| 1470 | Choice::PageColor(color) => { | 1471 | Choice::PageColor(color) => { |
| 1471 | return self.paper_page(color, self.view.editor.rule_lines(), None); | 1472 | return self.paper_page(color, self.pane.view.editor.rule_lines(), None); |
| 1472 | } | 1473 | } |
| 1473 | Choice::Art(name) => { | 1474 | Choice::Art(name) => { |
| 1474 | return self.with_art(name.and_then(canvas::template::find), |state, art| { | 1475 | return self.with_art(name.and_then(canvas::template::find), |state, art| { |
| 1475 | let editor = &state.view.editor; | 1476 | let editor = &state.pane.view.editor; |
| 1476 | state.paper_page(editor.page_color(), editor.rule_lines(), Some(art)) | 1477 | state.paper_page(editor.page_color(), editor.rule_lines(), Some(art)) |
| 1477 | }); | 1478 | }); |
| 1478 | } | 1479 | } |
| 1479 | Choice::RuleLines(lines) => { | 1480 | Choice::RuleLines(lines) => { |
| 1480 | let lines = lines.map(|index| canvas::template::RULE_LINES[index].1); | 1481 | let lines = lines.map(|index| canvas::template::RULE_LINES[index].1); |
| 1481 | return self.paper_page(self.view.editor.page_color(), lines, None); | 1482 | return self.paper_page(self.pane.view.editor.page_color(), lines, None); |
| 1482 | } | 1483 | } |
| 1483 | Choice::Pen(place) => { | 1484 | Choice::Pen(place) => { |
| 1484 | self.toolbar.pen = place; | 1485 | self.toolbar.pen = place; |
| ... | @@ -1495,7 +1496,7 @@ impl State { | ... | @@ -1495,7 +1496,7 @@ impl State { |
| 1495 | recent.insert(0, symbol); | 1496 | recent.insert(0, symbol); |
| 1496 | recent.truncate(crate::symbol::GALLERY); | 1497 | recent.truncate(crate::symbol::GALLERY); |
| 1497 | self.save_settings(); | 1498 | self.save_settings(); |
| 1498 | let response = self.view.insert_symbol(symbol)?; | 1499 | let response = self.pane.view.insert_symbol(symbol)?; |
| 1499 | self.respond(response); | 1500 | self.respond(response); |
| 1500 | return Ok(()); | 1501 | return Ok(()); |
| 1501 | } | 1502 | } |
| ... | @@ -1544,18 +1545,19 @@ impl State { | ... | @@ -1544,18 +1545,19 @@ impl State { |
| 1544 | title: String::new(), | 1545 | title: String::new(), |
| 1545 | }, | 1546 | }, |
| 1546 | Id::NewSubpage => Work::NewPage { | 1547 | Id::NewSubpage => Work::NewPage { |
| 1547 | under: self.session.as_ref().map(|session| session.space), | 1548 | under: self.session.as_ref().map(|_| self.pane.showing.space), |
| 1548 | title: String::new(), | 1549 | title: String::new(), |
| 1549 | }, | 1550 | }, |
| 1550 | Id::PageVersions => { | 1551 | Id::PageVersions => { |
| 1551 | let session = self.session.as_ref().ok_or("No notebook is open")?; | 1552 | self.session.as_ref().ok_or("No notebook is open")?; |
| 1552 | Work::History { | 1553 | Work::History { |
| 1553 | page: session.space, | 1554 | page: self.pane.showing.space, |
| 1554 | show: session.shown_history != Some(session.space), | 1555 | show: self.pane.showing.shown_history != Some(self.pane.showing.space), |
| 1555 | } | 1556 | } |
| 1556 | } | 1557 | } |
| 1557 | Id::CopyPageLink => { | 1558 | Id::CopyPageLink => { |
| 1558 | let space = self.session.as_ref().ok_or("No notebook is open")?.space; | 1559 | self.session.as_ref().ok_or("No notebook is open")?; |
| 1560 | let space = self.pane.showing.space; | ||
| 1559 | let link = self.page_link(space, None)?; | 1561 | let link = self.page_link(space, None)?; |
| 1560 | self.clipboard.set_text(link)?; | 1562 | self.clipboard.set_text(link)?; |
| 1561 | return Ok(()); | 1563 | return Ok(()); |
| ... | @@ -1604,7 +1606,7 @@ impl State { | ... | @@ -1604,7 +1606,7 @@ impl State { |
| 1604 | } | 1606 | } |
| 1605 | Id::Undo | Id::Redo => return self.step(id == Id::Redo), | 1607 | Id::Undo | Id::Redo => return self.step(id == Id::Redo), |
| 1606 | Id::Cut | Id::Copy => { | 1608 | Id::Cut | Id::Copy => { |
| 1607 | let response = self.view.copy(id == Id::Cut)?; | 1609 | let response = self.pane.view.copy(id == Id::Cut)?; |
| 1608 | self.respond(response); | 1610 | self.respond(response); |
| 1609 | return Ok(()); | 1611 | return Ok(()); |
| 1610 | } | 1612 | } |
| ... | @@ -1624,7 +1626,7 @@ impl State { | ... | @@ -1624,7 +1626,7 @@ impl State { |
| 1624 | Id::FormatPainter => { | 1626 | Id::FormatPainter => { |
| 1625 | self.painter = match self.painter { | 1627 | self.painter = match self.painter { |
| 1626 | Some(_) => None, | 1628 | Some(_) => None, |
| 1627 | None => Some(self.view.editor.painted_format()?), | 1629 | None => Some(self.pane.view.editor.painted_format()?), |
| 1628 | }; | 1630 | }; |
| 1629 | return Ok(()); | 1631 | return Ok(()); |
| 1630 | } | 1632 | } |
| ... | @@ -1632,7 +1634,7 @@ impl State { | ... | @@ -1632,7 +1634,7 @@ impl State { |
| 1632 | match field { | 1634 | match field { |
| 1633 | Some(field) => self.ui.focus_all(field), | 1635 | Some(field) => self.ui.focus_all(field), |
| 1634 | None => { | 1636 | None => { |
| 1635 | let response = self.view.widen_selection()?; | 1637 | let response = self.pane.view.widen_selection()?; |
| 1636 | self.respond(response); | 1638 | self.respond(response); |
| 1637 | } | 1639 | } |
| 1638 | } | 1640 | } |
| ... | @@ -1668,11 +1670,11 @@ impl State { | ... | @@ -1668,11 +1670,11 @@ impl State { |
| 1668 | } | 1670 | } |
| 1669 | Id::ZoomIn | Id::ZoomOut | Id::ActualSize => { | 1671 | Id::ZoomIn | Id::ZoomOut | Id::ActualSize => { |
| 1670 | let zoom = match id { | 1672 | let zoom = match id { |
| 1671 | Id::ZoomIn => self.view.zoom() * 1.1, | 1673 | Id::ZoomIn => self.pane.view.zoom() * 1.1, |
| 1672 | Id::ZoomOut => self.view.zoom() / 1.1, | 1674 | Id::ZoomOut => self.pane.view.zoom() / 1.1, |
| 1673 | _ => 1.0, | 1675 | _ => 1.0, |
| 1674 | }; | 1676 | }; |
| 1675 | let response = self.view.set_zoom(zoom)?; | 1677 | let response = self.pane.view.set_zoom(zoom)?; |
| 1676 | self.respond(response); | 1678 | self.respond(response); |
| 1677 | return Ok(()); | 1679 | return Ok(()); |
| 1678 | } | 1680 | } |
| ... | @@ -1725,12 +1727,12 @@ impl State { | ... | @@ -1725,12 +1727,12 @@ impl State { |
| 1725 | return Ok(()); | 1727 | return Ok(()); |
| 1726 | } | 1728 | } |
| 1727 | Id::SnapToGrid => { | 1729 | Id::SnapToGrid => { |
| 1728 | self.view.snap_to_grid = !self.view.snap_to_grid; | 1730 | self.pane.view.snap_to_grid = !self.pane.view.snap_to_grid; |
| 1729 | self.save_settings(); | 1731 | self.save_settings(); |
| 1730 | return Ok(()); | 1732 | return Ok(()); |
| 1731 | } | 1733 | } |
| 1732 | Id::MarkdownShortcuts => { | 1734 | Id::MarkdownShortcuts => { |
| 1733 | self.view.editor.markdown = markdown(self.view.editor.markdown.is_none()); | 1735 | self.pane.view.editor.markdown = markdown(self.pane.view.editor.markdown.is_none()); |
| 1734 | self.save_settings(); | 1736 | self.save_settings(); |
| 1735 | return Ok(()); | 1737 | return Ok(()); |
| 1736 | } | 1738 | } |
| ... | @@ -1743,7 +1745,7 @@ impl State { | ... | @@ -1743,7 +1745,7 @@ impl State { |
| 1743 | Id::RecordAudio | Id::RecordVideo => return self.record(id == Id::RecordVideo), | 1745 | Id::RecordAudio | Id::RecordVideo => return self.record(id == Id::RecordVideo), |
| 1744 | Id::Transport(transport) => return self.run_transport(transport), | 1746 | Id::Transport(transport) => return self.run_transport(transport), |
| 1745 | Id::InsertSpace => { | 1747 | Id::InsertSpace => { |
| 1746 | let response = self.view.insert_space(); | 1748 | let response = self.pane.view.insert_space(); |
| 1747 | self.respond(response); | 1749 | self.respond(response); |
| 1748 | return Ok(()); | 1750 | return Ok(()); |
| 1749 | } | 1751 | } |
| ... | @@ -1756,7 +1758,7 @@ impl State { | ... | @@ -1756,7 +1758,7 @@ impl State { |
| 1756 | Id::Shape(kind) => Tool::Shape(kind, pens[0].shape()), | 1758 | Id::Shape(kind) => Tool::Shape(kind, pens[0].shape()), |
| 1757 | _ => Tool::Select, | 1759 | _ => Tool::Select, |
| 1758 | }; | 1760 | }; |
| 1759 | let response = self.view.set_tool(tool); | 1761 | let response = self.pane.view.set_tool(tool); |
| 1760 | self.respond(response); | 1762 | self.respond(response); |
| 1761 | return Ok(()); | 1763 | return Ok(()); |
| 1762 | } | 1764 | } |
| ... | @@ -1765,7 +1767,7 @@ impl State { | ... | @@ -1765,7 +1767,7 @@ impl State { |
| 1765 | return Ok(()); | 1767 | return Ok(()); |
| 1766 | } | 1768 | } |
| 1767 | Id::Equation => { | 1769 | Id::Equation => { |
| 1768 | let response = self.view.insert_equation()?; | 1770 | let response = self.pane.view.insert_equation()?; |
| 1769 | self.respond(response); | 1771 | self.respond(response); |
| 1770 | return Ok(()); | 1772 | return Ok(()); |
| 1771 | } | 1773 | } |
| ... | @@ -1779,7 +1781,7 @@ impl State { | ... | @@ -1779,7 +1781,7 @@ impl State { |
| 1779 | Id::Time => time, | 1781 | Id::Time => time, |
| 1780 | _ => format!("{date} {time}"), | 1782 | _ => format!("{date} {time}"), |
| 1781 | }; | 1783 | }; |
| 1782 | let response = self.view.insert_text(format!("{text} "))?; | 1784 | let response = self.pane.view.insert_text(format!("{text} "))?; |
| 1783 | self.respond(response); | 1785 | self.respond(response); |
| 1784 | return Ok(()); | 1786 | return Ok(()); |
| 1785 | } | 1787 | } |
| ... | @@ -1803,7 +1805,7 @@ impl State { | ... | @@ -1803,7 +1805,7 @@ impl State { |
| 1803 | // Clear Formatting at a caret makes the paragraph Normal, as OneNote 2010's | 1805 | // Clear Formatting at a caret makes the paragraph Normal, as OneNote 2010's |
| 1804 | // Ctrl+Shift+N does; over a selection it clears the characters' formatting. | 1806 | // Ctrl+Shift+N does; over a selection it clears the characters' formatting. |
| 1805 | Id::ClearFormatting => { | 1807 | Id::ClearFormatting => { |
| 1806 | let [anchor, focus] = self.view.editor.selection().positions; | 1808 | let [anchor, focus] = self.pane.view.editor.selection().positions; |
| 1807 | if anchor == focus { | 1809 | if anchor == focus { |
| 1808 | return format(self, Formatting::Style(self.gallery_style("p"))); | 1810 | return format(self, Formatting::Style(self.gallery_style("p"))); |
| 1809 | } | 1811 | } |
| ... | @@ -2096,9 +2098,9 @@ mod tests { | ... | @@ -2096,9 +2098,9 @@ mod tests { |
| 2096 | for effect in [ | 2098 | for effect in [ |
| 2097 | "self.respond(", | 2099 | "self.respond(", |
| 2098 | "self.changed", | 2100 | "self.changed", |
| 2099 | "self.view.format(", | 2101 | "self.pane.view.format(", |
| 2100 | "self.view.set_zoom(", | 2102 | "self.pane.view.set_zoom(", |
| 2101 | "self.view.insert", | 2103 | "self.pane.view.insert", |
| 2102 | "self.open_link_dialog(", | 2104 | "self.open_link_dialog(", |
| 2103 | ] { | 2105 | ] { |
| 2104 | assert!( | 2106 | assert!( |
crates/snowbound/src/conflict_render.rs+21-15| ... | @@ -10,7 +10,7 @@ const SCALE: f32 = 2.0; | ... | @@ -10,7 +10,7 @@ const SCALE: f32 = 2.0; |
| 10 | 10 | ||
| 11 | /// One view laid out: the bar and the page on the left, the page list on the right; `menu` | 11 | /// One view laid out: the bar and the page on the left, the page list on the right; `menu` |
| 12 | /// opens the conflict page's menu. | 12 | /// opens the conflict page's menu. |
| 13 | fn layout(ui: &mut Ui, session: &Session, menu: bool) -> Vec<Command> { | 13 | fn layout(ui: &mut Ui, session: &Session, showing: &Showing, menu: bool) -> Vec<Command> { |
| 14 | let theme = ui.theme.clone(); | 14 | let theme = ui.theme.clone(); |
| 15 | let section = theme.section(section_color(None)); | 15 | let section = theme.section(section_color(None)); |
| 16 | ui.begin(SIZE, SCALE, Instant::now()); | 16 | ui.begin(SIZE, SCALE, Instant::now()); |
| ... | @@ -36,7 +36,7 @@ fn layout(ui: &mut Ui, session: &Session, menu: bool) -> Vec<Command> { | ... | @@ -36,7 +36,7 @@ fn layout(ui: &mut Ui, session: &Session, menu: bool) -> Vec<Command> { |
| 36 | }, | 36 | }, |
| 37 | ); | 37 | ); |
| 38 | let mut commands: Vec<Command> = session | 38 | let mut commands: Vec<Command> = session |
| 39 | .bar() | 39 | .bar(showing) |
| 40 | .and_then(|bar| conflict_bar(ui, bar, &["synthetic", "Songs"], [false, true])) | 40 | .and_then(|bar| conflict_bar(ui, bar, &["synthetic", "Songs"], [false, true])) |
| 41 | .into_iter() | 41 | .into_iter() |
| 42 | .collect(); | 42 | .collect(); |
| ... | @@ -64,6 +64,7 @@ fn layout(ui: &mut Ui, session: &Session, menu: bool) -> Vec<Command> { | ... | @@ -64,6 +64,7 @@ fn layout(ui: &mut Ui, session: &Session, menu: bool) -> Vec<Command> { |
| 64 | &theme, | 64 | &theme, |
| 65 | &section, | 65 | &section, |
| 66 | session, | 66 | session, |
| 67 | showing, | ||
| 67 | [&HashSet::new(); 2], | 68 | [&HashSet::new(); 2], |
| 68 | &HashMap::new(), | 69 | &HashMap::new(), |
| 69 | (0.0, false), | 70 | (0.0, false), |
| ... | @@ -242,8 +243,7 @@ fn conflict_views_offer_the_versions_and_render_offscreen() { | ... | @@ -242,8 +243,7 @@ fn conflict_views_offer_the_versions_and_render_offscreen() { |
| 242 | let library = Arc::new(crate::Library::section(&file, &directory.join("cache"))); | 243 | let library = Arc::new(crate::Library::section(&file, &directory.join("cache"))); |
| 243 | let section = library.open(&library.location, || {}).unwrap(); | 244 | let section = library.open(&library.location, || {}).unwrap(); |
| 244 | let path = library.location.clone(); | 245 | let path = library.location.clone(); |
| 245 | let (mut session, _) = read_session(section, library, path, None).unwrap(); | 246 | let (session, page, _) = read_session(section, library, path, None).unwrap(); |
| 246 | let page = session.space; | ||
| 247 | let version = session.versions(page)[0].space; | 247 | let version = session.versions(page)[0].space; |
| 248 | let output = std::env::var_os("SNOWBOUND_CONFLICT_RENDER").map(PathBuf::from); | 248 | let output = std::env::var_os("SNOWBOUND_CONFLICT_RENDER").map(PathBuf::from); |
| 249 | let mut gpu = output.as_deref().map(gpu); | 249 | let mut gpu = output.as_deref().map(gpu); |
| ... | @@ -262,15 +262,18 @@ fn conflict_views_offer_the_versions_and_render_offscreen() { | ... | @@ -262,15 +262,18 @@ fn conflict_views_offer_the_versions_and_render_offscreen() { |
| 262 | Theme::dark(), | 262 | Theme::dark(), |
| 263 | ), | 263 | ), |
| 264 | ] { | 264 | ] { |
| 265 | session.space = space; | 265 | let showing = Showing { |
| 266 | session.shown = shown; | 266 | space, |
| 267 | shown, | ||
| 268 | ..Showing::default() | ||
| 269 | }; | ||
| 267 | let mut ui = Ui::new(appearance, std::time::Duration::from_millis(500)); | 270 | let mut ui = Ui::new(appearance, std::time::Duration::from_millis(500)); |
| 268 | // Later frames lay out with earlier frames' measurements, and a menu fades in. | 271 | // Later frames lay out with earlier frames' measurements, and a menu fades in. |
| 269 | for _ in 0..8 { | 272 | for _ in 0..8 { |
| 270 | layout(&mut ui, &session, menu); | 273 | layout(&mut ui, &session, &showing, menu); |
| 271 | std::thread::sleep(std::time::Duration::from_millis(30)); | 274 | std::thread::sleep(std::time::Duration::from_millis(30)); |
| 272 | } | 275 | } |
| 273 | let bar = session.bar(); | 276 | let bar = session.bar(&showing); |
| 274 | match (name, bar) { | 277 | match (name, bar) { |
| 275 | ("page" | "page-dark", Some(Bar::Page { shown: false, .. })) | 278 | ("page" | "page-dark", Some(Bar::Page { shown: false, .. })) |
| 276 | | ("versions", Some(Bar::Page { shown: true, .. })) | 279 | | ("versions", Some(Bar::Page { shown: true, .. })) |
| ... | @@ -326,8 +329,7 @@ fn page_versions_list_open_read_only_and_render_offscreen() { | ... | @@ -326,8 +329,7 @@ fn page_versions_list_open_read_only_and_render_offscreen() { |
| 326 | let library = Arc::new(crate::Library::section(&file, &directory.join("cache"))); | 329 | let library = Arc::new(crate::Library::section(&file, &directory.join("cache"))); |
| 327 | let section = library.open(&library.location, || {}).unwrap(); | 330 | let section = library.open(&library.location, || {}).unwrap(); |
| 328 | let path = library.location.clone(); | 331 | let path = library.location.clone(); |
| 329 | let (mut session, _) = read_session(section, library, path, None).unwrap(); | 332 | let (session, page, _) = read_session(section, library, path, None).unwrap(); |
| 330 | let page = session.space; | ||
| 331 | let versions: Vec<_> = session | 333 | let versions: Vec<_> = session |
| 332 | .page_versions(page) | 334 | .page_versions(page) |
| 333 | .iter() | 335 | .iter() |
| ... | @@ -374,10 +376,14 @@ fn page_versions_list_open_read_only_and_render_offscreen() { | ... | @@ -374,10 +376,14 @@ fn page_versions_list_open_read_only_and_render_offscreen() { |
| 374 | Theme::dark(), | 376 | Theme::dark(), |
| 375 | ), | 377 | ), |
| 376 | ] { | 378 | ] { |
| 377 | session.shown_history = shown; | 379 | let showing = Showing { |
| 378 | session.version = version; | 380 | space: page, |
| 379 | assert_eq!(session.read_only(), version.is_some(), "{name}"); | 381 | version, |
| 380 | match (session.bar(), version) { | 382 | shown_history: shown, |
| 383 | ..Showing::default() | ||
| 384 | }; | ||
| 385 | assert_eq!(session.read_only(&showing), version.is_some(), "{name}"); | ||
| 386 | match (session.bar(&showing), version) { | ||
| 381 | ( | 387 | ( |
| 382 | Some(Bar::History { | 388 | Some(Bar::History { |
| 383 | version: open, | 389 | version: open, |
| ... | @@ -393,7 +399,7 @@ fn page_versions_list_open_read_only_and_render_offscreen() { | ... | @@ -393,7 +399,7 @@ fn page_versions_list_open_read_only_and_render_offscreen() { |
| 393 | } | 399 | } |
| 394 | let mut ui = Ui::new(appearance, std::time::Duration::from_millis(500)); | 400 | let mut ui = Ui::new(appearance, std::time::Duration::from_millis(500)); |
| 395 | for _ in 0..8 { | 401 | for _ in 0..8 { |
| 396 | layout(&mut ui, &session, menu); | 402 | layout(&mut ui, &session, &showing, menu); |
| 397 | std::thread::sleep(std::time::Duration::from_millis(30)); | 403 | std::thread::sleep(std::time::Duration::from_millis(30)); |
| 398 | } | 404 | } |
| 399 | for context in &versions { | 405 | for context in &versions { |
crates/snowbound/src/history.rs+16-14| ... | @@ -25,9 +25,9 @@ impl Session { | ... | @@ -25,9 +25,9 @@ impl Session { |
| 25 | 25 | ||
| 26 | /// Whether the open page takes no edits: a conflict page, a version, or a page of the | 26 | /// Whether the open page takes no edits: a conflict page, a version, or a page of the |
| 27 | /// recycle bin. | 27 | /// recycle bin. |
| 28 | pub(crate) fn read_only(&self) -> bool { | 28 | pub(crate) fn read_only(&self, showing: &Showing) -> bool { |
| 29 | self.version.is_some() | 29 | showing.version.is_some() |
| 30 | || self.conflict(self.space).is_some() | 30 | || self.conflict(showing.space).is_some() |
| 31 | || crate::recycle::binned(&self.tabs[self.tab].path) | 31 | || crate::recycle::binned(&self.tabs[self.tab].path) |
| 32 | } | 32 | } |
| 33 | 33 | ||
| ... | @@ -54,13 +54,13 @@ impl Session { | ... | @@ -54,13 +54,13 @@ impl Session { |
| 54 | } | 54 | } |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | pub(crate) fn refresh_history(&mut self) -> Result<(), Box<dyn Error>> { | 57 | pub(crate) fn refresh_history(&mut self, showing: &mut Showing) -> Result<(), Box<dyn Error>> { |
| 58 | self.history = self.section.versions()?; | 58 | self.history = self.section.versions()?; |
| 59 | if self | 59 | if showing |
| 60 | .shown_history | 60 | .shown_history |
| 61 | .is_some_and(|page| self.page_versions(page).is_empty()) | 61 | .is_some_and(|page| self.page_versions(page).is_empty()) |
| 62 | { | 62 | { |
| 63 | self.shown_history = None; | 63 | showing.shown_history = None; |
| 64 | } | 64 | } |
| 65 | Ok(()) | 65 | Ok(()) |
| 66 | } | 66 | } |
| ... | @@ -142,9 +142,9 @@ pub fn menu( | ... | @@ -142,9 +142,9 @@ pub fn menu( |
| 142 | impl State { | 142 | impl State { |
| 143 | /// Shows or hides a page's versions under it; hiding leaves an open version for its page. | 143 | /// Shows or hides a page's versions under it; hiding leaves an open version for its page. |
| 144 | pub(crate) fn show_history(&mut self, page: ExGuid, show: bool) -> Result<(), Box<dyn Error>> { | 144 | pub(crate) fn show_history(&mut self, page: ExGuid, show: bool) -> Result<(), Box<dyn Error>> { |
| 145 | let session = self.session.as_mut().ok_or("No section is open")?; | 145 | self.session.as_ref().ok_or("No section is open")?; |
| 146 | session.shown_history = show.then_some(page); | 146 | self.pane.showing.shown_history = show.then_some(page); |
| 147 | if !show && session.version.is_some() { | 147 | if !show && self.pane.showing.version.is_some() { |
| 148 | self.commands.push(Command::OpenPage(page)); | 148 | self.commands.push(Command::OpenPage(page)); |
| 149 | } | 149 | } |
| 150 | Ok(()) | 150 | Ok(()) |
| ... | @@ -174,7 +174,7 @@ impl State { | ... | @@ -174,7 +174,7 @@ impl State { |
| 174 | .restore_version_async(page, version, &self.author); | 174 | .restore_version_async(page, version, &self.author); |
| 175 | self.section_edit(operation, move |state, _| { | 175 | self.section_edit(operation, move |state, _| { |
| 176 | if let Some(session) = &mut state.session { | 176 | if let Some(session) = &mut state.session { |
| 177 | session.refresh_history()?; | 177 | session.refresh_history(&mut state.pane.showing)?; |
| 178 | } | 178 | } |
| 179 | state.edited(vec![page]); | 179 | state.edited(vec![page]); |
| 180 | state.commands.push(Command::OpenPage(page)); | 180 | state.commands.push(Command::OpenPage(page)); |
| ... | @@ -193,7 +193,7 @@ impl State { | ... | @@ -193,7 +193,7 @@ impl State { |
| 193 | .delete_versions_async(&[(page, vec![version])]); | 193 | .delete_versions_async(&[(page, vec![version])]); |
| 194 | self.section_edit(operation, move |state, _| { | 194 | self.section_edit(operation, move |state, _| { |
| 195 | if let Some(session) = &mut state.session { | 195 | if let Some(session) = &mut state.session { |
| 196 | session.refresh_history()?; | 196 | session.refresh_history(&mut state.pane.showing)?; |
| 197 | } | 197 | } |
| 198 | state.commands.push(Command::OpenPage(page)); | 198 | state.commands.push(Command::OpenPage(page)); |
| 199 | Ok(()) | 199 | Ok(()) |
| ... | @@ -274,9 +274,11 @@ impl State { | ... | @@ -274,9 +274,11 @@ impl State { |
| 274 | }; | 274 | }; |
| 275 | self.section_edit(operation, move |state, ()| { | 275 | self.section_edit(operation, move |state, ()| { |
| 276 | if let Some(session) = &mut state.session { | 276 | if let Some(session) = &mut state.session { |
| 277 | session.refresh_history()?; | 277 | session.refresh_history(&mut state.pane.showing)?; |
| 278 | if session.version.is_some() { | 278 | if state.pane.showing.version.is_some() { |
| 279 | state.commands.push(Command::OpenPage(session.space)); | 279 | state |
| 280 | .commands | ||
| 281 | .push(Command::OpenPage(state.pane.showing.space)); | ||
| 280 | } | 282 | } |
| 281 | } | 283 | } |
| 282 | Ok(()) | 284 | Ok(()) |
crates/snowbound/src/link.rs+14-13| ... | @@ -75,7 +75,7 @@ impl State { | ... | @@ -75,7 +75,7 @@ impl State { |
| 75 | /// Ctrl+K and the toolbar's Link: the dialog opens on what the selection links. | 75 | /// Ctrl+K and the toolbar's Link: the dialog opens on what the selection links. |
| 76 | pub(crate) fn open_link_dialog(&mut self) { | 76 | pub(crate) fn open_link_dialog(&mut self) { |
| 77 | // As OneNote 2010's, Link does nothing over a selection across paragraphs. | 77 | // As OneNote 2010's, Link does nothing over a selection across paragraphs. |
| 78 | let Some((text, address)) = self.view.link_prefill() else { | 78 | let Some((text, address)) = self.pane.view.link_prefill() else { |
| 79 | return; | 79 | return; |
| 80 | }; | 80 | }; |
| 81 | self.link = Some(LinkDialog { | 81 | self.link = Some(LinkDialog { |
| ... | @@ -239,7 +239,7 @@ impl State { | ... | @@ -239,7 +239,7 @@ impl State { |
| 239 | } | 239 | } |
| 240 | let dialog = self.link.as_ref().unwrap(); | 240 | let dialog = self.link.as_ref().unwrap(); |
| 241 | if ok && !dialog.address.trim().is_empty() { | 241 | if ok && !dialog.address.trim().is_empty() { |
| 242 | let response = self.view.set_link(&dialog.text, &dialog.address)?; | 242 | let response = self.pane.view.set_link(&dialog.text, &dialog.address)?; |
| 243 | self.respond(response); | 243 | self.respond(response); |
| 244 | } else if !cancel { | 244 | } else if !cancel { |
| 245 | return Ok(()); | 245 | return Ok(()); |
| ... | @@ -252,7 +252,7 @@ impl State { | ... | @@ -252,7 +252,7 @@ impl State { |
| 252 | 252 | ||
| 253 | /// A secondary press on text, a picture or a file opens its context menu there. | 253 | /// A secondary press on text, a picture or a file opens its context menu there. |
| 254 | pub(crate) fn open_text_menu(&mut self) -> Result<(), Box<dyn Error>> { | 254 | pub(crate) fn open_text_menu(&mut self) -> Result<(), Box<dyn Error>> { |
| 255 | let Some((response, context)) = self.view.context()? else { | 255 | let Some((response, context)) = self.pane.view.context()? else { |
| 256 | return Ok(()); | 256 | return Ok(()); |
| 257 | }; | 257 | }; |
| 258 | self.respond(response); | 258 | self.respond(response); |
| ... | @@ -338,15 +338,15 @@ impl State { | ... | @@ -338,15 +338,15 @@ impl State { |
| 338 | let correction = context.spelling.unwrap(); | 338 | let correction = context.spelling.unwrap(); |
| 339 | let response = match chosen_text.as_str() { | 339 | let response = match chosen_text.as_str() { |
| 340 | _ if chosen < correction.suggestions.len() => { | 340 | _ if chosen < correction.suggestions.len() => { |
| 341 | self.view.correct(&correction, &chosen_text)? | 341 | self.pane.view.correct(&correction, &chosen_text)? |
| 342 | } | 342 | } |
| 343 | "Delete Repeated Word" => self.view.correct(&correction, "")?, | 343 | "Delete Repeated Word" => self.pane.view.correct(&correction, "")?, |
| 344 | "Spelling" => { | 344 | "Spelling" => { |
| 345 | self.open_spelling_pane(); | 345 | self.open_spelling_pane(); |
| 346 | return Ok(()); | 346 | return Ok(()); |
| 347 | } | 347 | } |
| 348 | text => { | 348 | text => { |
| 349 | if let Some(spelling) = &self.view.spelling { | 349 | if let Some(spelling) = &self.pane.view.spelling { |
| 350 | if text == "Ignore" { | 350 | if text == "Ignore" { |
| 351 | spelling.ignore(&correction.word); | 351 | spelling.ignore(&correction.word); |
| 352 | } else { | 352 | } else { |
| ... | @@ -407,7 +407,8 @@ impl State { | ... | @@ -407,7 +407,8 @@ impl State { |
| 407 | separated: true, | 407 | separated: true, |
| 408 | disabled: disabled(commands::Id::Cut) | 408 | disabled: disabled(commands::Id::Cut) |
| 409 | || image.size.is_none_or(|size| { | 409 | || image.size.is_none_or(|size| { |
| 410 | self.view | 410 | self.pane |
| 411 | .view | ||
| 411 | .editor | 412 | .editor |
| 412 | .image_placement(image.id) | 413 | .image_placement(image.id) |
| 413 | .is_none_or(|(_, shown)| shown == size) | 414 | .is_none_or(|(_, shown)| shown == size) |
| ... | @@ -554,7 +555,7 @@ impl State { | ... | @@ -554,7 +555,7 @@ impl State { |
| 554 | return Ok(()); | 555 | return Ok(()); |
| 555 | } | 556 | } |
| 556 | "Restore to Original Size" => { | 557 | "Restore to Original Size" => { |
| 557 | let response = self.view.restore_picture_size(image.id)?; | 558 | let response = self.pane.view.restore_picture_size(image.id)?; |
| 558 | self.respond(response); | 559 | self.respond(response); |
| 559 | return Ok(()); | 560 | return Ok(()); |
| 560 | } | 561 | } |
| ... | @@ -581,15 +582,15 @@ impl State { | ... | @@ -581,15 +582,15 @@ impl State { |
| 581 | return Ok(()); | 582 | return Ok(()); |
| 582 | } | 583 | } |
| 583 | "Copy Link to Paragraph" => { | 584 | "Copy Link to Paragraph" => { |
| 584 | if let Some(session) = &self.session { | 585 | if self.session.is_some() { |
| 585 | let link = self.page_link(session.space, context.paragraph)?; | 586 | let link = self.page_link(self.pane.showing.space, context.paragraph)?; |
| 586 | self.clipboard.set_text(link)?; | 587 | self.clipboard.set_text(link)?; |
| 587 | } | 588 | } |
| 588 | return Ok(()); | 589 | return Ok(()); |
| 589 | } | 590 | } |
| 590 | "Select Link" => self.view.unlink(true)?, | 591 | "Select Link" => self.pane.view.unlink(true)?, |
| 591 | "Remove Link" => self.view.unlink(false)?, | 592 | "Remove Link" => self.pane.view.unlink(false)?, |
| 592 | text => self.view.switch_equation(text == "Linear")?, | 593 | text => self.pane.view.switch_equation(text == "Linear")?, |
| 593 | }; | 594 | }; |
| 594 | self.respond(response); | 595 | self.respond(response); |
| 595 | Ok(()) | 596 | Ok(()) |
crates/snowbound/src/live.rs+12-11| ... | @@ -590,8 +590,9 @@ impl State { | ... | @@ -590,8 +590,9 @@ impl State { |
| 590 | let Some(session) = &self.session else { | 590 | let Some(session) = &self.session else { |
| 591 | return live::Presence::default(); | 591 | return live::Presence::default(); |
| 592 | }; | 592 | }; |
| 593 | let outline = self.view.editor.active_outline(); | 593 | let outline = self.pane.view.editor.active_outline(); |
| 594 | let [anchor, focus] = self | 594 | let [anchor, focus] = self |
| 595 | .pane | ||
| 595 | .view | 596 | .view |
| 596 | .editor | 597 | .editor |
| 597 | .selection() | 598 | .selection() |
| ... | @@ -599,7 +600,7 @@ impl State { | ... | @@ -599,7 +600,7 @@ impl State { |
| 599 | .map(|position| spot(outline, position)); | 600 | .map(|position| spot(outline, position)); |
| 600 | live::Presence { | 601 | live::Presence { |
| 601 | section: section(session), | 602 | section: section(session), |
| 602 | page: Some(session.space.into()), | 603 | page: Some(self.pane.showing.space.into()), |
| 603 | caret: anchor | 604 | caret: anchor |
| 604 | .zip(focus) | 605 | .zip(focus) |
| 605 | .map(|(anchor, focus)| Caret { anchor, focus }), | 606 | .map(|(anchor, focus)| Caret { anchor, focus }), |
| ... | @@ -815,26 +816,26 @@ impl State { | ... | @@ -815,26 +816,26 @@ impl State { |
| 815 | self.commands | 816 | self.commands |
| 816 | .push(Command::OpenSection(library, tab.path.clone())); | 817 | .push(Command::OpenSection(library, tab.path.clone())); |
| 817 | } | 818 | } |
| 818 | } else if presence.page != Some(session.space.into()) { | 819 | } else if presence.page != Some(self.pane.showing.space.into()) { |
| 819 | if asked != Some(place) { | 820 | if asked != Some(place) { |
| 820 | self.commands.push(Command::OpenPage(presence.page?.into())); | 821 | self.commands.push(Command::OpenPage(presence.page?.into())); |
| 821 | } | 822 | } |
| 822 | } else { | 823 | } else { |
| 823 | self.peers.going = None; | 824 | self.peers.going = None; |
| 824 | let caret = presence.caret?; | 825 | let caret = presence.caret?; |
| 825 | let (outline, focus) = find(&self.view.editor, caret.focus)?; | 826 | let (outline, focus) = find(&self.pane.view.editor, caret.focus)?; |
| 826 | let rect = outline | 827 | let rect = outline |
| 827 | .caret_at(focus, parley::Affinity::Downstream, 0.0) | 828 | .caret_at(focus, parley::Affinity::Downstream, 0.0) |
| 828 | .ok()?; | 829 | .ok()?; |
| 829 | let [x, y] = outline.origin(); | 830 | let [x, y] = outline.origin(); |
| 830 | let viewport = self.view.viewport; | 831 | let viewport = self.pane.view.viewport; |
| 831 | let [width, height] = viewport.size.map(|side| side as f32); | 832 | let [width, height] = viewport.size.map(|side| side as f32); |
| 832 | let middle = ((rect.y0 + rect.y1) as f32 / 2.0 + y) * viewport.scale; | 833 | let middle = ((rect.y0 + rect.y1) as f32 / 2.0 + y) * viewport.scale; |
| 833 | let mut responses = vec![self.view.scroll_to(1, middle - height / 2.0).ok()?]; | 834 | let mut responses = vec![self.pane.view.scroll_to(1, middle - height / 2.0).ok()?]; |
| 834 | let across = (rect.x0 as f32 + x) * viewport.scale + viewport.origin[0]; | 835 | let across = (rect.x0 as f32 + x) * viewport.scale + viewport.origin[0]; |
| 835 | if !(0.0..width).contains(&across) { | 836 | if !(0.0..width).contains(&across) { |
| 836 | let left = (rect.x0 as f32 + x) * viewport.scale - width / 2.0; | 837 | let left = (rect.x0 as f32 + x) * viewport.scale - width / 2.0; |
| 837 | responses.push(self.view.scroll_to(0, left).ok()?); | 838 | responses.push(self.pane.view.scroll_to(0, left).ok()?); |
| 838 | } | 839 | } |
| 839 | for response in responses { | 840 | for response in responses { |
| 840 | self.respond(response); | 841 | self.respond(response); |
| ... | @@ -924,9 +925,9 @@ impl State { | ... | @@ -924,9 +925,9 @@ impl State { |
| 924 | let Some([left, top, right, bottom]) = self.ui.laid_out(page()) else { | 925 | let Some([left, top, right, bottom]) = self.ui.laid_out(page()) else { |
| 925 | return; | 926 | return; |
| 926 | }; | 927 | }; |
| 927 | let here = (section(session), Some(session.space.into())); | 928 | let here = (section(session), Some(self.pane.showing.space.into())); |
| 928 | let scale = self.ui.scale(); | 929 | let scale = self.ui.scale(); |
| 929 | let viewport = self.view.viewport; | 930 | let viewport = self.pane.view.viewport; |
| 930 | // A point on the page in points from the page box's corner. | 931 | // A point on the page in points from the page box's corner. |
| 931 | let shown = |[x, y]: [f32; 2]| { | 932 | let shown = |[x, y]: [f32; 2]| { |
| 932 | [ | 933 | [ |
| ... | @@ -943,7 +944,7 @@ impl State { | ... | @@ -943,7 +944,7 @@ impl State { |
| 943 | else { | 944 | else { |
| 944 | continue; | 945 | continue; |
| 945 | }; | 946 | }; |
| 946 | let Some((outline, focus)) = find(&self.view.editor, caret.focus) else { | 947 | let Some((outline, focus)) = find(&self.pane.view.editor, caret.focus) else { |
| 947 | continue; | 948 | continue; |
| 948 | }; | 949 | }; |
| 949 | let Ok(rect) = outline.caret_at(focus, parley::Affinity::Downstream, 0.0) else { | 950 | let Ok(rect) = outline.caret_at(focus, parley::Affinity::Downstream, 0.0) else { |
| ... | @@ -971,7 +972,7 @@ impl State { | ... | @@ -971,7 +972,7 @@ impl State { |
| 971 | if !(0.0..right - left).contains(&x0) { | 972 | if !(0.0..right - left).contains(&x0) { |
| 972 | continue; | 973 | continue; |
| 973 | } | 974 | } |
| 974 | let selection = find(&self.view.editor, caret.anchor) | 975 | let selection = find(&self.pane.view.editor, caret.anchor) |
| 975 | .filter(|(other, _)| other.id == outline.id) | 976 | .filter(|(other, _)| other.id == outline.id) |
| 976 | .and_then(|(_, anchor)| outline.range_rects([anchor, focus].into()).ok()) | 977 | .and_then(|(_, anchor)| outline.range_rects([anchor, focus].into()).ok()) |
| 977 | .unwrap_or_default() | 978 | .unwrap_or_default() |
crates/snowbound/src/main.rs+318-252| ... | @@ -539,7 +539,44 @@ fn icloud_listed(proxy: EventLoopProxy<UserEvent>) -> impl Fn() + Send + Sync + | ... | @@ -539,7 +539,44 @@ fn icloud_listed(proxy: EventLoopProxy<UserEvent>) -> impl Fn() + Send + Sync + |
| 539 | } | 539 | } |
| 540 | } | 540 | } |
| 541 | 541 | ||
| 542 | /// The open section and the page the editor shows. | 542 | /// A page view, and which page, locked section or empty notebook it shows. |
| 543 | struct Pane { | ||
| 544 | view: PageView, | ||
| 545 | showing: Showing, | ||
| 546 | /// The newest read requested; older ones are dropped when they finish. | ||
| 547 | loading: u64, | ||
| 548 | opening: Option<Opening>, | ||
| 549 | /// The section tab, if another, that the newest read opens, and when it was asked for. | ||
| 550 | switching: Option<(Option<usize>, Instant)>, | ||
| 551 | /// Pages visited, for Back and Forward. | ||
| 552 | trail: navigation::Trail, | ||
| 553 | /// The stored page changed elsewhere while marked text was pending; it is shown once | ||
| 554 | /// the composition ends. | ||
| 555 | stale: bool, | ||
| 556 | /// Whether the page last heard it had keyboard focus. | ||
| 557 | page_focused: bool, | ||
| 558 | /// The notebook shown while it has no section open, as one whose last section was | ||
| 559 | /// deleted. | ||
| 560 | sectionless: Option<Arc<Library>>, | ||
| 561 | /// A password-protected section shown locked, in place of an open one. | ||
| 562 | locked: Option<protection::Locked>, | ||
| 563 | } | ||
| 564 | |||
| 565 | /// Which page of the open section a pane shows, and what the page list shows beneath it. | ||
| 566 | #[derive(Default)] | ||
| 567 | struct Showing { | ||
| 568 | space: ExGuid, | ||
| 569 | /// The version of page `space` open, by its context. | ||
| 570 | version: Option<ExGuid>, | ||
| 571 | /// The page whose conflict pages the list shows beneath it. | ||
| 572 | shown: Option<ExGuid>, | ||
| 573 | /// Which of the open conflict page's conflicting changes is selected, in page order. | ||
| 574 | change: Option<usize>, | ||
| 575 | /// The page whose versions the list shows beneath it (Show Page Versions). | ||
| 576 | shown_history: Option<ExGuid>, | ||
| 577 | } | ||
| 578 | |||
| 579 | /// An open section. | ||
| 543 | struct Session { | 580 | struct Session { |
| 544 | section: notebook::session::Section, | 581 | section: notebook::session::Section, |
| 545 | /// The notebook the section belongs to. | 582 | /// The notebook the section belongs to. |
| ... | @@ -549,21 +586,12 @@ struct Session { | ... | @@ -549,21 +586,12 @@ struct Session { |
| 549 | tab: usize, | 586 | tab: usize, |
| 550 | /// Spaces, titles and outline levels in section order. | 587 | /// Spaces, titles and outline levels in section order. |
| 551 | pages: Vec<(ExGuid, String, u32)>, | 588 | pages: Vec<(ExGuid, String, u32)>, |
| 552 | space: ExGuid, | ||
| 553 | /// The section's sync status as last read. | 589 | /// The section's sync status as last read. |
| 554 | sync: notebook::session::SyncStatus, | 590 | sync: notebook::session::SyncStatus, |
| 555 | /// Each page's conflict pages, the versions a merge could not take. | 591 | /// Each page's conflict pages, the versions a merge could not take. |
| 556 | conflicts: Vec<(ExGuid, Vec<onestore::ConflictPage>)>, | 592 | conflicts: Vec<(ExGuid, Vec<onestore::ConflictPage>)>, |
| 557 | /// The page whose conflict pages the list shows beneath it. | ||
| 558 | shown: Option<ExGuid>, | ||
| 559 | /// Which of the open conflict page's conflicting changes is selected, in page order. | ||
| 560 | change: Option<usize>, | ||
| 561 | /// Each page's versions, newest first. | 593 | /// Each page's versions, newest first. |
| 562 | history: Vec<(ExGuid, Vec<onestore::PageVersion>)>, | 594 | history: Vec<(ExGuid, Vec<onestore::PageVersion>)>, |
| 563 | /// The page whose versions the list shows beneath it (Show Page Versions). | ||
| 564 | shown_history: Option<ExGuid>, | ||
| 565 | /// The version of page `space` open, by its context. | ||
| 566 | version: Option<ExGuid>, | ||
| 567 | } | 595 | } |
| 568 | 596 | ||
| 569 | /// The information bar above a page with conflict pages, or above one of them. | 597 | /// The information bar above a page with conflict pages, or above one of them. |
| ... | @@ -602,13 +630,13 @@ impl Session { | ... | @@ -602,13 +630,13 @@ impl Session { |
| 602 | Ok(()) | 630 | Ok(()) |
| 603 | } | 631 | } |
| 604 | 632 | ||
| 605 | fn title(&self) -> &str { | 633 | fn title(&self, space: ExGuid) -> &str { |
| 606 | match self.conflict(self.space) { | 634 | match self.conflict(space) { |
| 607 | Some((_, version)) => &version.title, | 635 | Some((_, version)) => &version.title, |
| 608 | None => self | 636 | None => self |
| 609 | .pages | 637 | .pages |
| 610 | .iter() | 638 | .iter() |
| 611 | .find(|(space, ..)| *space == self.space) | 639 | .find(|(listed, ..)| *listed == space) |
| 612 | .map_or("", |(_, title, _)| title), | 640 | .map_or("", |(_, title, _)| title), |
| 613 | } | 641 | } |
| 614 | } | 642 | } |
| ... | @@ -630,33 +658,34 @@ impl Session { | ... | @@ -630,33 +658,34 @@ impl Session { |
| 630 | }) | 658 | }) |
| 631 | } | 659 | } |
| 632 | 660 | ||
| 633 | fn bar(&self) -> Option<Bar> { | 661 | fn bar(&self, showing: &Showing) -> Option<Bar> { |
| 634 | if let Some(version) = self.version { | 662 | let space = showing.space; |
| 663 | if let Some(version) = showing.version { | ||
| 635 | return Some(Bar::History { | 664 | return Some(Bar::History { |
| 636 | page: self.space, | 665 | page: space, |
| 637 | version, | 666 | version, |
| 638 | grouped: self.grouped(), | 667 | grouped: self.grouped(), |
| 639 | }); | 668 | }); |
| 640 | } | 669 | } |
| 641 | match self.conflict(self.space) { | 670 | match self.conflict(space) { |
| 642 | Some((page, _)) => Some(Bar::Version { | 671 | Some((page, _)) => Some(Bar::Version { |
| 643 | page, | 672 | page, |
| 644 | version: self.space, | 673 | version: space, |
| 645 | }), | 674 | }), |
| 646 | None => (!self.versions(self.space).is_empty()).then_some(Bar::Page { | 675 | None => (!self.versions(space).is_empty()).then_some(Bar::Page { |
| 647 | page: self.space, | 676 | page: space, |
| 648 | shown: self.shown == Some(self.space), | 677 | shown: showing.shown == Some(space), |
| 649 | }), | 678 | }), |
| 650 | } | 679 | } |
| 651 | } | 680 | } |
| 652 | 681 | ||
| 653 | fn refresh_conflicts(&mut self) -> Result<(), Box<dyn Error>> { | 682 | fn refresh_conflicts(&mut self, showing: &mut Showing) -> Result<(), Box<dyn Error>> { |
| 654 | self.conflicts = self.section.conflicts()?; | 683 | self.conflicts = self.section.conflicts()?; |
| 655 | if self | 684 | if showing |
| 656 | .shown | 685 | .shown |
| 657 | .is_some_and(|page| self.versions(page).is_empty()) | 686 | .is_some_and(|page| self.versions(page).is_empty()) |
| 658 | { | 687 | { |
| 659 | self.shown = None; | 688 | showing.shown = None; |
| 660 | } | 689 | } |
| 661 | Ok(()) | 690 | Ok(()) |
| 662 | } | 691 | } |
| ... | @@ -685,9 +714,9 @@ impl Session { | ... | @@ -685,9 +714,9 @@ impl Session { |
| 685 | /// section, or a notebook read again after a change, with the catalog path the open | 714 | /// section, or a notebook read again after a change, with the catalog path the open |
| 686 | /// section has in it. | 715 | /// section has in it. |
| 687 | enum Loaded { | 716 | enum Loaded { |
| 688 | Section(Box<Session>, Page), | 717 | Section(Box<Session>, ExGuid, Page), |
| 689 | /// A section just created, whose name opens for renaming as OneNote's New Section does. | 718 | /// A section just created, whose name opens for renaming as OneNote's New Section does. |
| 690 | Created(Box<Session>, Page), | 719 | Created(Box<Session>, ExGuid, Page), |
| 691 | Page(ExGuid, Page), | 720 | Page(ExGuid, Page), |
| 692 | /// A page's version, by the page's space and the version's context. | 721 | /// A page's version, by the page's space and the version's context. |
| 693 | Version(ExGuid, ExGuid, Page), | 722 | Version(ExGuid, ExGuid, Page), |
| ... | @@ -697,8 +726,8 @@ enum Loaded { | ... | @@ -697,8 +726,8 @@ enum Loaded { |
| 697 | 726 | ||
| 698 | /// What an opening page replaces: the section, or the page of the open one. | 727 | /// What an opening page replaces: the section, or the page of the open one. |
| 699 | enum Shown { | 728 | enum Shown { |
| 700 | Section(Box<Session>), | 729 | Section(Box<Session>, ExGuid), |
| 701 | Created(Box<Session>), | 730 | Created(Box<Session>, ExGuid), |
| 702 | Page(ExGuid), | 731 | Page(ExGuid), |
| 703 | Version(ExGuid, ExGuid), | 732 | Version(ExGuid, ExGuid), |
| 704 | } | 733 | } |
| ... | @@ -822,19 +851,14 @@ struct State { | ... | @@ -822,19 +851,14 @@ struct State { |
| 822 | loads: (mpsc::Sender<Read>, mpsc::Receiver<Read>), | 851 | loads: (mpsc::Sender<Read>, mpsc::Receiver<Read>), |
| 823 | /// Lays out pages the loader threads read, off the frame. | 852 | /// Lays out pages the loader threads read, off the frame. |
| 824 | layouts: Arc<Mutex<TextEngine>>, | 853 | layouts: Arc<Mutex<TextEngine>>, |
| 825 | /// The newest read requested; older ones are dropped when they finish. | ||
| 826 | loading: u64, | ||
| 827 | opening: Option<Opening>, | ||
| 828 | /// The section tab, if another, that the newest read opens, and when it was asked for. | ||
| 829 | switching: Option<(Option<usize>, Instant)>, | ||
| 830 | prefetch: prefetch::Prefetch, | 854 | prefetch: prefetch::Prefetch, |
| 831 | /// Where each page was left this run, by `Library::key` and page space, as OneNote | 855 | /// Where each page was left this run, by `Library::key` and page space, as OneNote |
| 832 | /// returns to it until the notebook closes. | 856 | /// returns to it until the notebook closes. |
| 833 | places: HashMap<(String, ExGuid), Place>, | 857 | places: HashMap<(String, ExGuid), Place>, |
| 834 | /// The page each section showed last this run, by `Library::key`. | 858 | /// The page each section showed last this run, by `Library::key`. |
| 835 | last_pages: HashMap<String, ExGuid>, | 859 | last_pages: HashMap<String, ExGuid>, |
| 836 | /// Pages visited, for Back and Forward. | 860 | /// Each page shown lately once, latest first, kept between launches. |
| 837 | trail: navigation::Trail, | 861 | recent: Vec<navigation::Place>, |
| 838 | /// What Undo and Redo take back across pages and sections. | 862 | /// What Undo and Redo take back across pages and sections. |
| 839 | undo: undo::Timeline, | 863 | undo: undo::Timeline, |
| 840 | swipe: navigation::Swipe, | 864 | swipe: navigation::Swipe, |
| ... | @@ -853,9 +877,11 @@ struct State { | ... | @@ -853,9 +877,11 @@ struct State { |
| 853 | /// Backends that failed to start this run, and why. | 877 | /// Backends that failed to start this run, and why. |
| 854 | unavailable: Vec<(settings::Backend, String)>, | 878 | unavailable: Vec<(settings::Backend, String)>, |
| 855 | ui: Ui, | 879 | ui: Ui, |
| 856 | view: PageView, | 880 | /// The page view the window shows. |
| 881 | pane: Pane, | ||
| 857 | /// The open notebooks in the sidebar's order. | 882 | /// The open notebooks in the sidebar's order. |
| 858 | notebooks: Vec<Arc<Library>>, | 883 | notebooks: Vec<Arc<Library>>, |
| 884 | /// The open section, the one the pane shows. | ||
| 859 | session: Option<Session>, | 885 | session: Option<Session>, |
| 860 | /// A page not kept in any notebook, from a text file or `--page`. | 886 | /// A page not kept in any notebook, from a text file or `--page`. |
| 861 | temporary: bool, | 887 | temporary: bool, |
| ... | @@ -938,11 +964,6 @@ struct State { | ... | @@ -938,11 +964,6 @@ struct State { |
| 938 | titlebar: [[f32; 4]; 2], | 964 | titlebar: [[f32; 4]; 2], |
| 939 | /// A section or group being renamed in the sidebar. | 965 | /// A section or group being renamed in the sidebar. |
| 940 | renaming: Option<rename::Renaming>, | 966 | renaming: Option<rename::Renaming>, |
| 941 | /// The notebook shown while it has no section open, as one whose last section was | ||
| 942 | /// deleted. | ||
| 943 | sectionless: Option<Arc<Library>>, | ||
| 944 | /// A password-protected section shown locked, in place of an open one. | ||
| 945 | locked: Option<protection::Locked>, | ||
| 946 | /// The password dialog while it is open. | 967 | /// The password dialog while it is open. |
| 947 | password: Option<protection::Asking>, | 968 | password: Option<protection::Asking>, |
| 948 | /// Options' Passwords. | 969 | /// Options' Passwords. |
| ... | @@ -977,11 +998,6 @@ struct State { | ... | @@ -977,11 +998,6 @@ struct State { |
| 977 | changed: bool, | 998 | changed: bool, |
| 978 | /// Only the view scrolled or zoomed during this frame, to be announced after it. | 999 | /// Only the view scrolled or zoomed during this frame, to be announced after it. |
| 979 | moved: bool, | 1000 | moved: bool, |
| 980 | /// The stored page changed elsewhere while marked text was pending; it is shown once | ||
| 981 | /// the composition ends. | ||
| 982 | stale: bool, | ||
| 983 | /// Whether the page last heard it had keyboard focus. | ||
| 984 | page_focused: bool, | ||
| 985 | /// The pointer in logical pixels, for window drags from the strip. | 1001 | /// The pointer in logical pixels, for window drags from the strip. |
| 986 | pointer: [f32; 2], | 1002 | pointer: [f32; 2], |
| 987 | /// When the strip was last pressed, to zoom on a double click. | 1003 | /// When the strip was last pressed, to zoom on a double click. |
| ... | @@ -1180,6 +1196,7 @@ impl State { | ... | @@ -1180,6 +1196,7 @@ impl State { |
| 1180 | icloud::look_up(icloud_listed(proxy.clone())); | 1196 | icloud::look_up(icloud_listed(proxy.clone())); |
| 1181 | let mut notebooks = Vec::new(); | 1197 | let mut notebooks = Vec::new(); |
| 1182 | let mut session = None; | 1198 | let mut session = None; |
| 1199 | let mut showing = Showing::default(); | ||
| 1183 | let mut sectionless = None; | 1200 | let mut sectionless = None; |
| 1184 | let mut locked = None; | 1201 | let mut locked = None; |
| 1185 | let (editor, scene) = match input { | 1202 | let (editor, scene) = match input { |
| ... | @@ -1213,9 +1230,10 @@ impl State { | ... | @@ -1213,9 +1230,10 @@ impl State { |
| 1213 | .0; | 1230 | .0; |
| 1214 | notebooks.push(Arc::clone(&library)); | 1231 | notebooks.push(Arc::clone(&library)); |
| 1215 | let path = library.location.clone(); | 1232 | let path = library.location.clone(); |
| 1216 | let (opened, page) = read_session(section, library, path, Some(space))?; | 1233 | let (opened, space, page) = read_session(section, library, path, Some(space))?; |
| 1217 | let (scene, editor) = PageScene::from_page(page, &mut engine)?; | 1234 | let (scene, editor) = PageScene::from_page(page, &mut engine)?; |
| 1218 | session = Some(opened); | 1235 | session = Some(opened); |
| 1236 | showing.space = space; | ||
| 1219 | (editor, Some((scene, [0.0; 2]))) | 1237 | (editor, Some((scene, [0.0; 2]))) |
| 1220 | } | 1238 | } |
| 1221 | Input::Notebooks { locations, current } => { | 1239 | Input::Notebooks { locations, current } => { |
| ... | @@ -1249,10 +1267,11 @@ impl State { | ... | @@ -1249,10 +1267,11 @@ impl State { |
| 1249 | match shown { | 1267 | match shown { |
| 1250 | Some((library, path, left)) if !library.locked(&path) => { | 1268 | Some((library, path, left)) if !library.locked(&path) => { |
| 1251 | let section = library.open_async(&path, notify(proxy.clone())).await?; | 1269 | let section = library.open_async(&path, notify(proxy.clone())).await?; |
| 1252 | let (opened, page) = | 1270 | let (opened, space, page) = |
| 1253 | read_session(section, Arc::clone(library), path, left)?; | 1271 | read_session(section, Arc::clone(library), path, left)?; |
| 1254 | let (scene, editor) = PageScene::from_page(page, &mut engine)?; | 1272 | let (scene, editor) = PageScene::from_page(page, &mut engine)?; |
| 1255 | session = Some(opened); | 1273 | session = Some(opened); |
| 1274 | showing.space = space; | ||
| 1256 | (editor, Some((scene, [0.0; 2]))) | 1275 | (editor, Some((scene, [0.0; 2]))) |
| 1257 | } | 1276 | } |
| 1258 | shown => { | 1277 | shown => { |
| ... | @@ -1348,14 +1367,25 @@ impl State { | ... | @@ -1348,14 +1367,25 @@ impl State { |
| 1348 | drawing, | 1367 | drawing, |
| 1349 | unavailable, | 1368 | unavailable, |
| 1350 | ui, | 1369 | ui, |
| 1351 | view: PageView::new( | 1370 | pane: Pane { |
| 1352 | editor, | 1371 | view: PageView::new( |
| 1353 | engine, | 1372 | editor, |
| 1354 | scene, | 1373 | engine, |
| 1355 | [size.width, size.height], | 1374 | scene, |
| 1356 | dpr, | 1375 | [size.width, size.height], |
| 1357 | platform::double_click_interval(), | 1376 | dpr, |
| 1358 | ), | 1377 | platform::double_click_interval(), |
| 1378 | ), | ||
| 1379 | showing, | ||
| 1380 | loading: 0, | ||
| 1381 | opening: None, | ||
| 1382 | switching: None, | ||
| 1383 | trail: navigation::Trail::default(), | ||
| 1384 | stale: false, | ||
| 1385 | page_focused: true, | ||
| 1386 | sectionless, | ||
| 1387 | locked, | ||
| 1388 | }, | ||
| 1359 | notebooks, | 1389 | notebooks, |
| 1360 | session, | 1390 | session, |
| 1361 | temporary, | 1391 | temporary, |
| ... | @@ -1398,8 +1428,6 @@ impl State { | ... | @@ -1398,8 +1428,6 @@ impl State { |
| 1398 | light_pages: stored.light_pages, | 1428 | light_pages: stored.light_pages, |
| 1399 | titlebar, | 1429 | titlebar, |
| 1400 | renaming: None, | 1430 | renaming: None, |
| 1401 | sectionless, | ||
| 1402 | locked, | ||
| 1403 | password: None, | 1431 | password: None, |
| 1404 | passwords: stored.passwords, | 1432 | passwords: stored.passwords, |
| 1405 | notebook_theme: (stored.notebook_theme.0) | 1433 | notebook_theme: (stored.notebook_theme.0) |
| ... | @@ -1420,8 +1448,6 @@ impl State { | ... | @@ -1420,8 +1448,6 @@ impl State { |
| 1420 | commands: Vec::new(), | 1448 | commands: Vec::new(), |
| 1421 | changed: false, | 1449 | changed: false, |
| 1422 | moved: false, | 1450 | moved: false, |
| 1423 | stale: false, | ||
| 1424 | page_focused: true, | ||
| 1425 | pointer: [0.0; 2], | 1451 | pointer: [0.0; 2], |
| 1426 | strip_press: None, | 1452 | strip_press: None, |
| 1427 | strip_held: false, | 1453 | strip_held: false, |
| ... | @@ -1439,18 +1465,11 @@ impl State { | ... | @@ -1439,18 +1465,11 @@ impl State { |
| 1439 | loads: mpsc::channel(), | 1465 | loads: mpsc::channel(), |
| 1440 | prefetch, | 1466 | prefetch, |
| 1441 | layouts, | 1467 | layouts, |
| 1442 | loading: 0, | ||
| 1443 | opening: None, | ||
| 1444 | switching: None, | ||
| 1445 | places: HashMap::new(), | 1468 | places: HashMap::new(), |
| 1446 | last_pages: (stored.recent.iter().rev()) | 1469 | last_pages: (stored.recent.iter().rev()) |
| 1447 | .map(|place| (library::key(&place.notebook, &place.section), place.page)) | 1470 | .map(|place| (library::key(&place.notebook, &place.section), place.page)) |
| 1448 | .collect(), | 1471 | .collect(), |
| 1449 | trail: { | 1472 | recent: stored.recent, |
| 1450 | let mut trail = navigation::Trail::default(); | ||
| 1451 | trail.recent = stored.recent; | ||
| 1452 | trail | ||
| 1453 | }, | ||
| 1454 | undo: undo::Timeline::default(), | 1473 | undo: undo::Timeline::default(), |
| 1455 | swipe: navigation::Swipe::default(), | 1474 | swipe: navigation::Swipe::default(), |
| 1456 | access_adapter, | 1475 | access_adapter, |
| ... | @@ -1464,9 +1483,9 @@ impl State { | ... | @@ -1464,9 +1483,9 @@ impl State { |
| 1464 | servers: stored.servers, | 1483 | servers: stored.servers, |
| 1465 | correction: None, | 1484 | correction: None, |
| 1466 | }; | 1485 | }; |
| 1467 | state.view.snap_to_grid = !stored.ignore_grid; | 1486 | state.pane.view.snap_to_grid = !stored.ignore_grid; |
| 1468 | state.view.editor.default_font = stored.default_font; | 1487 | state.pane.view.editor.default_font = stored.default_font; |
| 1469 | state.view.editor.markdown = commands::markdown(!stored.ignore_markdown); | 1488 | state.pane.view.editor.markdown = commands::markdown(!stored.ignore_markdown); |
| 1470 | // A notebook opened from its server that couldn't sign in asks to, as the Finder does. | 1489 | // A notebook opened from its server that couldn't sign in asks to, as the Finder does. |
| 1471 | let unsigned = state.notebooks.iter().find(|library| { | 1490 | let unsigned = state.notebooks.iter().find(|library| { |
| 1472 | library.notebook.is_err() && library::server_address(&library.location).is_some() | 1491 | library.notebook.is_err() && library::server_address(&library.location).is_some() |
| ... | @@ -1557,7 +1576,7 @@ impl State { | ... | @@ -1557,7 +1576,7 @@ impl State { |
| 1557 | let scale = self.scale(); | 1576 | let scale = self.scale(); |
| 1558 | if scale != self.ui.scale() { | 1577 | if scale != self.ui.scale() { |
| 1559 | self.renderer.clear_glyph_cache(); | 1578 | self.renderer.clear_glyph_cache(); |
| 1560 | let response = self.view.scale_factor_changed(scale)?; | 1579 | let response = self.pane.view.scale_factor_changed(scale)?; |
| 1561 | self.respond(response); | 1580 | self.respond(response); |
| 1562 | } | 1581 | } |
| 1563 | #[cfg(feature = "live")] | 1582 | #[cfg(feature = "live")] |
| ... | @@ -1572,10 +1591,10 @@ impl State { | ... | @@ -1572,10 +1591,10 @@ impl State { |
| 1572 | platform::resize_direction(&self.window, self.pointer) | 1591 | platform::resize_direction(&self.window, self.pointer) |
| 1573 | .map(CursorIcon::from) | 1592 | .map(CursorIcon::from) |
| 1574 | .or_else(|| self.ui.cursor()) | 1593 | .or_else(|| self.ui.cursor()) |
| 1575 | .unwrap_or_else(|| cursor_icon(self.view.cursor())), | 1594 | .unwrap_or_else(|| cursor_icon(self.pane.view.cursor())), |
| 1576 | ); | 1595 | ); |
| 1577 | let ime = if self.ui.focused() == Some(page()) { | 1596 | let ime = if self.ui.focused() == Some(page()) { |
| 1578 | self.view.accepts_text() | 1597 | self.pane.view.accepts_text() |
| 1579 | } else { | 1598 | } else { |
| 1580 | search::takes_text(self.ui.focused()) | 1599 | search::takes_text(self.ui.focused()) |
| 1581 | }; | 1600 | }; |
| ... | @@ -1590,8 +1609,8 @@ impl State { | ... | @@ -1590,8 +1609,8 @@ impl State { |
| 1590 | let commands = std::mem::take(&mut self.commands); | 1609 | let commands = std::mem::take(&mut self.commands); |
| 1591 | let follow = !commands.is_empty() | 1610 | let follow = !commands.is_empty() |
| 1592 | || self.ui.wants_frame() | 1611 | || self.ui.wants_frame() |
| 1593 | || self.opening.is_some() | 1612 | || self.pane.opening.is_some() |
| 1594 | || self.switching.is_some(); | 1613 | || self.pane.switching.is_some(); |
| 1595 | for command in commands { | 1614 | for command in commands { |
| 1596 | self.apply(command)?; | 1615 | self.apply(command)?; |
| 1597 | } | 1616 | } |
| ... | @@ -1601,7 +1620,7 @@ impl State { | ... | @@ -1601,7 +1620,7 @@ impl State { |
| 1601 | self.after_move()?; | 1620 | self.after_move()?; |
| 1602 | } | 1621 | } |
| 1603 | self.moved = false; | 1622 | self.moved = false; |
| 1604 | if self.stale { | 1623 | if self.pane.stale { |
| 1605 | self.refresh()?; | 1624 | self.refresh()?; |
| 1606 | } | 1625 | } |
| 1607 | // A covered window shows nothing, so animations wait for it to be uncovered. | 1626 | // A covered window shows nothing, so animations wait for it to be uncovered. |
| ... | @@ -1669,8 +1688,8 @@ impl State { | ... | @@ -1669,8 +1688,8 @@ impl State { |
| 1669 | ((rect[2] - rect[0]) * scale).round() as u32, | 1688 | ((rect[2] - rect[0]) * scale).round() as u32, |
| 1670 | ((rect[3] - rect[1]) * scale).round() as u32, | 1689 | ((rect[3] - rect[1]) * scale).round() as u32, |
| 1671 | ]; | 1690 | ]; |
| 1672 | if size != self.view.viewport.size { | 1691 | if size != self.pane.view.viewport.size { |
| 1673 | let response = self.view.resized(size)?; | 1692 | let response = self.pane.view.resized(size)?; |
| 1674 | self.respond(response); | 1693 | self.respond(response); |
| 1675 | } | 1694 | } |
| 1676 | } | 1695 | } |
| ... | @@ -1743,8 +1762,8 @@ impl State { | ... | @@ -1743,8 +1762,8 @@ impl State { |
| 1743 | platform::update_menu(|| self.statuses()); | 1762 | platform::update_menu(|| self.statuses()); |
| 1744 | let welcome = self.session.is_none() | 1763 | let welcome = self.session.is_none() |
| 1745 | && !self.temporary | 1764 | && !self.temporary |
| 1746 | && self.sectionless.is_none() | 1765 | && self.pane.sectionless.is_none() |
| 1747 | && self.locked.is_none(); | 1766 | && self.pane.locked.is_none(); |
| 1748 | self.toolbar(&theme, !welcome)?; | 1767 | self.toolbar(&theme, !welcome)?; |
| 1749 | if welcome { | 1768 | if welcome { |
| 1750 | self.welcome(); | 1769 | self.welcome(); |
| ... | @@ -1916,7 +1935,7 @@ impl State { | ... | @@ -1916,7 +1935,7 @@ impl State { |
| 1916 | } | 1935 | } |
| 1917 | (clicked, open_tab) | 1936 | (clicked, open_tab) |
| 1918 | } | 1937 | } |
| 1919 | None if self.locked.is_some() => self.locked_tabs(row, &section, theme.strip), | 1938 | None if self.pane.locked.is_some() => self.locked_tabs(row, &section, theme.strip), |
| 1920 | None => { | 1939 | None => { |
| 1921 | let tabs = [("Temporary page", section_color(None), false)]; | 1940 | let tabs = [("Temporary page", section_color(None), false)]; |
| 1922 | let shown = if self.temporary { &tabs[..] } else { &[] }; | 1941 | let shown = if self.temporary { &tabs[..] } else { &[] }; |
| ... | @@ -1994,13 +2013,16 @@ impl State { | ... | @@ -1994,13 +2013,16 @@ impl State { |
| 1994 | }, | 2013 | }, |
| 1995 | ); | 2014 | ); |
| 1996 | if let Some(session) = &self.session | 2015 | if let Some(session) = &self.session |
| 1997 | && let Some(bar) = session.bar() | 2016 | && let Some(bar) = session.bar(&self.pane.showing) |
| 1998 | { | 2017 | { |
| 1999 | let changes = self.changes().len(); | 2018 | let changes = self.changes().len(); |
| 2000 | let sections: Vec<&str> = session.tabs.iter().map(|tab| tab.name.as_str()).collect(); | 2019 | let sections: Vec<&str> = session.tabs.iter().map(|tab| tab.name.as_str()).collect(); |
| 2001 | let steps = [ | 2020 | let steps = [ |
| 2002 | session.change.is_some_and(|at| at > 0), | 2021 | self.pane.showing.change.is_some_and(|at| at > 0), |
| 2003 | session.change.map_or(changes > 0, |at| at + 1 < changes), | 2022 | self.pane |
| 2023 | .showing | ||
| 2024 | .change | ||
| 2025 | .map_or(changes > 0, |at| at + 1 < changes), | ||
| 2004 | ]; | 2026 | ]; |
| 2005 | self.commands | 2027 | self.commands |
| 2006 | .extend(conflict_bar(&mut self.ui, bar, &sections, steps)); | 2028 | .extend(conflict_bar(&mut self.ui, bar, &sections, steps)); |
| ... | @@ -2008,7 +2030,7 @@ impl State { | ... | @@ -2008,7 +2030,7 @@ impl State { |
| 2008 | if self.in_recycle_bin() { | 2030 | if self.in_recycle_bin() { |
| 2009 | recycle::bar(&mut self.ui); | 2031 | recycle::bar(&mut self.ui); |
| 2010 | } | 2032 | } |
| 2011 | if self.session.is_none() && self.locked.is_some() { | 2033 | if self.session.is_none() && self.pane.locked.is_some() { |
| 2012 | self.locked_page(); | 2034 | self.locked_page(); |
| 2013 | self.ui.close(); | 2035 | self.ui.close(); |
| 2014 | self.ui.close(); | 2036 | self.ui.close(); |
| ... | @@ -2021,7 +2043,12 @@ impl State { | ... | @@ -2021,7 +2043,12 @@ impl State { |
| 2021 | self.context_menu(); | 2043 | self.context_menu(); |
| 2022 | return Ok((section, open_tab, None)); | 2044 | return Ok((section, open_tab, None)); |
| 2023 | } | 2045 | } |
| 2024 | if let Some(library) = self.sectionless.clone().filter(|_| self.session.is_none()) { | 2046 | if let Some(library) = self |
| 2047 | .pane | ||
| 2048 | .sectionless | ||
| 2049 | .clone() | ||
| 2050 | .filter(|_| self.session.is_none()) | ||
| 2051 | { | ||
| 2025 | self.no_sections(library); | 2052 | self.no_sections(library); |
| 2026 | self.ui.close(); | 2053 | self.ui.close(); |
| 2027 | self.ui.close(); | 2054 | self.ui.close(); |
| ... | @@ -2053,7 +2080,7 @@ impl State { | ... | @@ -2053,7 +2080,7 @@ impl State { |
| 2053 | self.template_strip(&theme); | 2080 | self.template_strip(&theme); |
| 2054 | } | 2081 | } |
| 2055 | self.transport(&theme)?; | 2082 | self.transport(&theme)?; |
| 2056 | let scroll = self.view.scroll(); | 2083 | let scroll = self.pane.view.scroll(); |
| 2057 | for (index, axis) in [Axis::X, Axis::Y] | 2084 | for (index, axis) in [Axis::X, Axis::Y] |
| 2058 | .into_iter() | 2085 | .into_iter() |
| 2059 | .filter(|_| !opening) | 2086 | .filter(|_| !opening) |
| ... | @@ -2063,13 +2090,13 @@ impl State { | ... | @@ -2063,13 +2090,13 @@ impl State { |
| 2063 | &mut self.ui, | 2090 | &mut self.ui, |
| 2064 | index, | 2091 | index, |
| 2065 | axis, | 2092 | axis, |
| 2066 | -self.view.viewport.origin[index], | 2093 | -self.pane.view.viewport.origin[index], |
| 2067 | [scroll.min[index], scroll.max[index]], | 2094 | [scroll.min[index], scroll.max[index]], |
| 2068 | self.view.viewport.size[index] as f32, | 2095 | self.pane.view.viewport.size[index] as f32, |
| 2069 | scroll.max[1 - index] > scroll.min[1 - index], | 2096 | scroll.max[1 - index] > scroll.min[1 - index], |
| 2070 | [0.18, 0.18, 0.18, 0.45], | 2097 | [0.18, 0.18, 0.18, 0.45], |
| 2071 | ) { | 2098 | ) { |
| 2072 | let response = self.view.scroll_to(index, offset)?; | 2099 | let response = self.pane.view.scroll_to(index, offset)?; |
| 2073 | self.respond(response); | 2100 | self.respond(response); |
| 2074 | } | 2101 | } |
| 2075 | } | 2102 | } |
| ... | @@ -2077,7 +2104,7 @@ impl State { | ... | @@ -2077,7 +2104,7 @@ impl State { |
| 2077 | if !opening { | 2104 | if !opening { |
| 2078 | self.peer_carets(); | 2105 | self.peer_carets(); |
| 2079 | } | 2106 | } |
| 2080 | let task = self.view.task_under_pointer().map(|[x0, y0, x1, y1]| { | 2107 | let task = self.pane.view.task_under_pointer().map(|[x0, y0, x1, y1]| { |
| 2081 | let scale = self.ui.scale(); | 2108 | let scale = self.ui.scale(); |
| 2082 | self.ui.leaf( | 2109 | self.ui.leaf( |
| 2083 | "task", | 2110 | "task", |
| ... | @@ -2101,9 +2128,9 @@ impl State { | ... | @@ -2101,9 +2128,9 @@ impl State { |
| 2101 | } | 2128 | } |
| 2102 | let signal = self.ui.signal(page()); | 2129 | let signal = self.ui.signal(page()); |
| 2103 | let focused = self.ui.window_focused && self.ui.focused() == Some(page()); | 2130 | let focused = self.ui.window_focused && self.ui.focused() == Some(page()); |
| 2104 | if focused != self.page_focused { | 2131 | if focused != self.pane.page_focused { |
| 2105 | self.page_focused = focused; | 2132 | self.pane.page_focused = focused; |
| 2106 | let response = self.view.focus_changed(focused)?; | 2133 | let response = self.pane.view.focus_changed(focused)?; |
| 2107 | self.respond(response); | 2134 | self.respond(response); |
| 2108 | } | 2135 | } |
| 2109 | self.page_events(signal.events)?; | 2136 | self.page_events(signal.events)?; |
| ... | @@ -2427,7 +2454,7 @@ impl State { | ... | @@ -2427,7 +2454,7 @@ impl State { |
| 2427 | let fonts = &self.fonts; | 2454 | let fonts = &self.fonts; |
| 2428 | let recent_fonts = &self.recent_fonts; | 2455 | let recent_fonts = &self.recent_fonts; |
| 2429 | let pens = &self.toolbar; | 2456 | let pens = &self.toolbar; |
| 2430 | let engine = &self.view.engine; | 2457 | let engine = &self.pane.view.engine; |
| 2431 | let session = self.session.as_ref(); | 2458 | let session = self.session.as_ref(); |
| 2432 | let update = self.updates.status(); | 2459 | let update = self.updates.status(); |
| 2433 | let sheet = self.gallery_sheet(); | 2460 | let sheet = self.gallery_sheet(); |
| ... | @@ -3126,8 +3153,8 @@ impl State { | ... | @@ -3126,8 +3153,8 @@ impl State { |
| 3126 | color: ui.theme.paper, | 3153 | color: ui.theme.paper, |
| 3127 | ink: ui.theme.paper_ink, | 3154 | ink: ui.theme.paper_ink, |
| 3128 | }, | 3155 | }, |
| 3129 | self.view.editor.page_color(), | 3156 | self.pane.view.editor.page_color(), |
| 3130 | self.view.editor.rule_lines(), | 3157 | self.pane.view.editor.rule_lines(), |
| 3131 | false, | 3158 | false, |
| 3132 | &mut self.page_color_preview, | 3159 | &mut self.page_color_preview, |
| 3133 | ) | 3160 | ) |
| ... | @@ -3187,7 +3214,7 @@ impl State { | ... | @@ -3187,7 +3214,7 @@ impl State { |
| 3187 | let format = self.format_state(); | 3214 | let format = self.format_state(); |
| 3188 | let [out, into] = | 3215 | let [out, into] = |
| 3189 | [Cmd::ZoomOut, Cmd::ZoomIn].map(|id| self.status(&Choice::Command(id), &format)); | 3216 | [Cmd::ZoomOut, Cmd::ZoomIn].map(|id| self.status(&Choice::Command(id), &format)); |
| 3190 | let level = format!("{:.0}%", self.view.zoom() * 100.0); | 3217 | let level = format!("{:.0}%", self.pane.view.zoom() * 100.0); |
| 3191 | let field = self.ui.id("zoom field"); | 3218 | let field = self.ui.id("zoom field"); |
| 3192 | if self.ui.focused() != Some(field) { | 3219 | if self.ui.focused() != Some(field) { |
| 3193 | self.zoom_typed = None; | 3220 | self.zoom_typed = None; |
| ... | @@ -3303,7 +3330,7 @@ impl State { | ... | @@ -3303,7 +3330,7 @@ impl State { |
| 3303 | self.choose(choice); | 3330 | self.choose(choice); |
| 3304 | } | 3331 | } |
| 3305 | if let Some(percent) = zoom { | 3332 | if let Some(percent) = zoom { |
| 3306 | let response = self.view.set_zoom(percent / 100.0)?; | 3333 | let response = self.pane.view.set_zoom(percent / 100.0)?; |
| 3307 | self.respond(response); | 3334 | self.respond(response); |
| 3308 | } | 3335 | } |
| 3309 | Ok(()) | 3336 | Ok(()) |
| ... | @@ -3402,7 +3429,7 @@ impl State { | ... | @@ -3402,7 +3429,7 @@ impl State { |
| 3402 | node.set_orientation(accesskit::Orientation::Vertical); | 3429 | node.set_orientation(accesskit::Orientation::Vertical); |
| 3403 | } | 3430 | } |
| 3404 | // Another section's pages are on their way. | 3431 | // Another section's pages are on their way. |
| 3405 | if self.loading().is_some() && self.switching.is_some_and(|(tab, _)| tab.is_some()) { | 3432 | if self.loading().is_some() && self.pane.switching.is_some_and(|(tab, _)| tab.is_some()) { |
| 3406 | self.ui.close(); | 3433 | self.ui.close(); |
| 3407 | return None; | 3434 | return None; |
| 3408 | } | 3435 | } |
| ... | @@ -3419,6 +3446,7 @@ impl State { | ... | @@ -3419,6 +3446,7 @@ impl State { |
| 3419 | theme, | 3446 | theme, |
| 3420 | section, | 3447 | section, |
| 3421 | session, | 3448 | session, |
| 3449 | &self.pane.showing, | ||
| 3422 | [&found, &unread], | 3450 | [&found, &unread], |
| 3423 | &peers, | 3451 | &peers, |
| 3424 | (rounding, self.page_tabs_left), | 3452 | (rounding, self.page_tabs_left), |
| ... | @@ -3461,7 +3489,10 @@ impl State { | ... | @@ -3461,7 +3489,10 @@ impl State { |
| 3461 | if self.loading().is_some() { | 3489 | if self.loading().is_some() { |
| 3462 | return Ok(()); | 3490 | return Ok(()); |
| 3463 | } | 3491 | } |
| 3464 | let read_only = self.session.as_ref().is_some_and(Session::read_only); | 3492 | let read_only = self |
| 3493 | .session | ||
| 3494 | .as_ref() | ||
| 3495 | .is_some_and(|session| session.read_only(&self.pane.showing)); | ||
| 3465 | let scale = self.ui.scale(); | 3496 | let scale = self.ui.scale(); |
| 3466 | let corner = self.ui.laid_out(page()).unwrap_or_default(); | 3497 | let corner = self.ui.laid_out(page()).unwrap_or_default(); |
| 3467 | let device = |point: [f32; 2]| { | 3498 | let device = |point: [f32; 2]| { |
| ... | @@ -3472,30 +3503,32 @@ impl State { | ... | @@ -3472,30 +3503,32 @@ impl State { |
| 3472 | }; | 3503 | }; |
| 3473 | for event in events { | 3504 | for event in events { |
| 3474 | let response = match event { | 3505 | let response = match event { |
| 3475 | ui::Event::PointerMoved(point) => self.view.pointer_moved(device(point))?, | 3506 | ui::Event::PointerMoved(point) => self.pane.view.pointer_moved(device(point))?, |
| 3476 | ui::Event::Pressure(pressure) => { | 3507 | ui::Event::Pressure(pressure) => { |
| 3477 | self.view | 3508 | self.pane |
| 3509 | .view | ||
| 3478 | .set_pressure(pressure.filter(|_| self.pen_pressure)); | 3510 | .set_pressure(pressure.filter(|_| self.pen_pressure)); |
| 3479 | continue; | 3511 | continue; |
| 3480 | } | 3512 | } |
| 3481 | ui::Event::PointerLeft => self.view.pointer_left(), | 3513 | ui::Event::PointerLeft => self.pane.view.pointer_left(), |
| 3482 | ui::Event::Button { | 3514 | ui::Event::Button { |
| 3483 | button: MouseButton::Left, | 3515 | button: MouseButton::Left, |
| 3484 | pressed, | 3516 | pressed, |
| 3485 | at, | 3517 | at, |
| 3486 | } => { | 3518 | } => { |
| 3487 | if pressed { | 3519 | if pressed { |
| 3488 | let response = self.view.pointer_pressed(at)?; | 3520 | let response = self.pane.view.pointer_pressed(at)?; |
| 3489 | // Drawing or clicking anywhere but the title puts the templates away. | 3521 | // Drawing or clicking anywhere but the title puts the templates away. |
| 3490 | if (self.view.inking() || !self.view.editor.active_outline().title) | 3522 | if (self.pane.view.inking() |
| 3491 | && let Some(session) = &self.session | 3523 | || !self.pane.view.editor.active_outline().title) |
| 3524 | && self.session.is_some() | ||
| 3492 | { | 3525 | { |
| 3493 | self.dismissed.insert(session.space); | 3526 | self.dismissed.insert(self.pane.showing.space); |
| 3494 | } | 3527 | } |
| 3495 | response | 3528 | response |
| 3496 | } else { | 3529 | } else { |
| 3497 | let response = self.view.pointer_released()?; | 3530 | let response = self.pane.view.pointer_released()?; |
| 3498 | let [anchor, focus] = self.view.editor.selection().positions; | 3531 | let [anchor, focus] = self.pane.view.editor.selection().positions; |
| 3499 | if anchor != focus | 3532 | if anchor != focus |
| 3500 | && let Some(painted) = self.painter.take() | 3533 | && let Some(painted) = self.painter.take() |
| 3501 | { | 3534 | { |
| ... | @@ -3521,7 +3554,7 @@ impl State { | ... | @@ -3521,7 +3554,7 @@ impl State { |
| 3521 | ui::Event::Ime(Ime::Preedit(..) | Ime::Commit(_)) if read_only => continue, | 3554 | ui::Event::Ime(Ime::Preedit(..) | Ime::Commit(_)) if read_only => continue, |
| 3522 | ui::Event::Key { ref key, .. } | 3555 | ui::Event::Key { ref key, .. } |
| 3523 | if read_only | 3556 | if read_only |
| 3524 | && !self.view.modifiers().command | 3557 | && !self.pane.view.modifiers().command |
| 3525 | && !matches!( | 3558 | && !matches!( |
| 3526 | key, | 3559 | key, |
| 3527 | Key::Named( | 3560 | Key::Named( |
| ... | @@ -3538,20 +3571,25 @@ impl State { | ... | @@ -3538,20 +3571,25 @@ impl State { |
| 3538 | { | 3571 | { |
| 3539 | continue; | 3572 | continue; |
| 3540 | } | 3573 | } |
| 3541 | ui::Event::Wheel(delta) => self.view.wheel([delta[0] * scale, delta[1] * scale])?, | 3574 | ui::Event::Wheel(delta) => { |
| 3575 | self.pane.view.wheel([delta[0] * scale, delta[1] * scale])? | ||
| 3576 | } | ||
| 3542 | ui::Event::Key { key, text } => { | 3577 | ui::Event::Key { key, text } => { |
| 3543 | self.view.key(&ui::edit_key(&key), text.as_deref())? | 3578 | self.pane.view.key(&ui::edit_key(&key), text.as_deref())? |
| 3544 | } | 3579 | } |
| 3545 | ui::Event::Ime(Ime::Preedit(text, cursor)) => self.view.compose(text, cursor)?, | 3580 | ui::Event::Ime(Ime::Preedit(text, cursor)) => { |
| 3546 | ui::Event::Ime(Ime::Commit(text)) => self.view.commit_text(text)?, | 3581 | self.pane.view.compose(text, cursor)? |
| 3547 | ui::Event::Ime(Ime::Disabled) => self.view.cancel_composition()?, | ||
| 3548 | ui::Event::Modifiers(modifiers) => { | ||
| 3549 | self.view.modifiers_changed(ui::edit_modifiers(modifiers))? | ||
| 3550 | } | 3582 | } |
| 3583 | ui::Event::Ime(Ime::Commit(text)) => self.pane.view.commit_text(text)?, | ||
| 3584 | ui::Event::Ime(Ime::Disabled) => self.pane.view.cancel_composition()?, | ||
| 3585 | ui::Event::Modifiers(modifiers) => self | ||
| 3586 | .pane | ||
| 3587 | .view | ||
| 3588 | .modifiers_changed(ui::edit_modifiers(modifiers))?, | ||
| 3551 | }; | 3589 | }; |
| 3552 | self.respond(response); | 3590 | self.respond(response); |
| 3553 | } | 3591 | } |
| 3554 | if self.view.editor.marked_range().is_none() { | 3592 | if self.pane.view.editor.marked_range().is_none() { |
| 3555 | platform::clear_marked_text(&self.window); | 3593 | platform::clear_marked_text(&self.window); |
| 3556 | } | 3594 | } |
| 3557 | Ok(()) | 3595 | Ok(()) |
| ... | @@ -3579,7 +3617,7 @@ impl State { | ... | @@ -3579,7 +3617,7 @@ impl State { |
| 3579 | let tab = match &self.session { | 3617 | let tab = match &self.session { |
| 3580 | // Back to the open section before another one opened. | 3618 | // Back to the open section before another one opened. |
| 3581 | Some(session) | 3619 | Some(session) |
| 3582 | if self.switching.is_some() | 3620 | if self.pane.switching.is_some() |
| 3583 | && Arc::ptr_eq(&session.library, &library) | 3621 | && Arc::ptr_eq(&session.library, &library) |
| 3584 | && session.tabs[session.tab].path == path => | 3622 | && session.tabs[session.tab].path == path => |
| 3585 | { | 3623 | { |
| ... | @@ -3591,34 +3629,37 @@ impl State { | ... | @@ -3591,34 +3629,37 @@ impl State { |
| 3591 | } | 3629 | } |
| 3592 | _ => None, | 3630 | _ => None, |
| 3593 | }; | 3631 | }; |
| 3594 | self.switching = Some((tab, Instant::now())); | 3632 | self.pane.switching = Some((tab, Instant::now())); |
| 3595 | let notify = notify(self.proxy.clone()); | 3633 | let notify = notify(self.proxy.clone()); |
| 3596 | let last = self.last_pages.get(&library.key(&path)).copied(); | 3634 | let last = self.last_pages.get(&library.key(&path)).copied(); |
| 3597 | self.load_async(move || async move { | 3635 | self.load_async(move || async move { |
| 3598 | let start = Instant::now(); | 3636 | let start = Instant::now(); |
| 3599 | let section = library.open_async(&path, notify).await?; | 3637 | let section = library.open_async(&path, notify).await?; |
| 3600 | lap("switch section open", start); | 3638 | lap("switch section open", start); |
| 3601 | let (session, page) = read_session(section, library, path, last)?; | 3639 | let (session, space, page) = read_session(section, library, path, last)?; |
| 3602 | Ok(Loaded::Section(Box::new(session), page)) | 3640 | Ok(Loaded::Section(Box::new(session), space, page)) |
| 3603 | }); | 3641 | }); |
| 3604 | } | 3642 | } |
| 3605 | Command::OpenPage(space) => { | 3643 | Command::OpenPage(space) => { |
| 3606 | let session = self.session.as_ref().ok_or("No section is open")?; | 3644 | let session = self.session.as_ref().ok_or("No section is open")?; |
| 3607 | if self.switching.is_some() && session.space == space && session.version.is_none() { | 3645 | if self.pane.switching.is_some() |
| 3646 | && self.pane.showing.space == space | ||
| 3647 | && self.pane.showing.version.is_none() | ||
| 3648 | { | ||
| 3608 | self.stop_loading(); | 3649 | self.stop_loading(); |
| 3609 | return Ok(()); | 3650 | return Ok(()); |
| 3610 | } | 3651 | } |
| 3611 | self.switching = Some((None, Instant::now())); | 3652 | self.pane.switching = Some((None, Instant::now())); |
| 3612 | let read = session.reader(space); | 3653 | let read = session.reader(space); |
| 3613 | self.load(move || Ok(Loaded::Page(space, read()?))); | 3654 | self.load(move || Ok(Loaded::Page(space, read()?))); |
| 3614 | } | 3655 | } |
| 3615 | Command::Versions { page, show } => { | 3656 | Command::Versions { page, show } => { |
| 3616 | let session = self.session.as_mut().ok_or("No section is open")?; | 3657 | let session = self.session.as_mut().ok_or("No section is open")?; |
| 3617 | session.shown = show.then_some(page); | 3658 | self.pane.showing.shown = show.then_some(page); |
| 3618 | let open = if show { | 3659 | let open = if show { |
| 3619 | session.versions(page).first().map(|version| version.space) | 3660 | session.versions(page).first().map(|version| version.space) |
| 3620 | } else { | 3661 | } else { |
| 3621 | (session.space != page).then_some(page) | 3662 | (self.pane.showing.space != page).then_some(page) |
| 3622 | }; | 3663 | }; |
| 3623 | self.commands.extend(open.map(Command::OpenPage)); | 3664 | self.commands.extend(open.map(Command::OpenPage)); |
| 3624 | } | 3665 | } |
| ... | @@ -3627,7 +3668,7 @@ impl State { | ... | @@ -3627,7 +3668,7 @@ impl State { |
| 3627 | let operation = session.section.delete_pages_async(&[version]); | 3668 | let operation = session.section.delete_pages_async(&[version]); |
| 3628 | self.section_edit(operation, move |state, _| { | 3669 | self.section_edit(operation, move |state, _| { |
| 3629 | if let Some(session) = &mut state.session { | 3670 | if let Some(session) = &mut state.session { |
| 3630 | session.refresh_conflicts()?; | 3671 | session.refresh_conflicts(&mut state.pane.showing)?; |
| 3631 | } | 3672 | } |
| 3632 | state.commands.push(Command::OpenPage(page)); | 3673 | state.commands.push(Command::OpenPage(page)); |
| 3633 | Ok(()) | 3674 | Ok(()) |
| ... | @@ -3635,9 +3676,9 @@ impl State { | ... | @@ -3635,9 +3676,9 @@ impl State { |
| 3635 | } | 3676 | } |
| 3636 | Command::CopyVersion { version, tab } => { | 3677 | Command::CopyVersion { version, tab } => { |
| 3637 | let session = self.session.as_mut().ok_or("No section is open")?; | 3678 | let session = self.session.as_mut().ok_or("No section is open")?; |
| 3638 | let page = match session.version { | 3679 | let page = match self.pane.showing.version { |
| 3639 | Some(open) if open == version => { | 3680 | Some(open) if open == version => { |
| 3640 | session.section.version(session.space, open)? | 3681 | session.section.version(self.pane.showing.space, open)? |
| 3641 | } | 3682 | } |
| 3642 | _ => session.section.page(version)?, | 3683 | _ => session.section.page(version)?, |
| 3643 | }; | 3684 | }; |
| ... | @@ -3729,7 +3770,7 @@ impl State { | ... | @@ -3729,7 +3770,7 @@ impl State { |
| 3729 | Command::Choose(choice) => self.run(choice)?, | 3770 | Command::Choose(choice) => self.run(choice)?, |
| 3730 | Command::TagPicture => self.pick_tag_picture(), | 3771 | Command::TagPicture => self.pick_tag_picture(), |
| 3731 | Command::Text(text) => { | 3772 | Command::Text(text) => { |
| 3732 | if let Some(context) = self.view.caret_context() { | 3773 | if let Some(context) = self.pane.view.caret_context() { |
| 3733 | self.text_command(context, text)?; | 3774 | self.text_command(context, text)?; |
| 3734 | } | 3775 | } |
| 3735 | } | 3776 | } |
| ... | @@ -3747,8 +3788,8 @@ impl State { | ... | @@ -3747,8 +3788,8 @@ impl State { |
| 3747 | &mut self, | 3788 | &mut self, |
| 3748 | read: impl FnOnce() -> F + Send + 'static, | 3789 | read: impl FnOnce() -> F + Send + 'static, |
| 3749 | ) { | 3790 | ) { |
| 3750 | self.loading += 1; | 3791 | self.pane.loading += 1; |
| 3751 | let (id, sender, redraw) = (self.loading, self.loads.0.clone(), self.redraw.clone()); | 3792 | let (id, sender, redraw) = (self.pane.loading, self.loads.0.clone(), self.redraw.clone()); |
| 3752 | let layouts = Arc::clone(&self.layouts); | 3793 | let layouts = Arc::clone(&self.layouts); |
| 3753 | let requested = Instant::now(); | 3794 | let requested = Instant::now(); |
| 3754 | let scenes = Arc::clone(&self.prefetch.scenes); | 3795 | let scenes = Arc::clone(&self.prefetch.scenes); |
| ... | @@ -3757,8 +3798,8 @@ impl State { | ... | @@ -3757,8 +3798,8 @@ impl State { |
| 3757 | let laid = read().await.and_then(|loaded| { | 3798 | let laid = read().await.and_then(|loaded| { |
| 3758 | lap("switch read", requested); | 3799 | lap("switch read", requested); |
| 3759 | let (shown, page) = match loaded { | 3800 | let (shown, page) = match loaded { |
| 3760 | Loaded::Section(session, page) => (Shown::Section(session), page), | 3801 | Loaded::Section(session, space, page) => (Shown::Section(session, space), page), |
| 3761 | Loaded::Created(session, page) => (Shown::Created(session), page), | 3802 | Loaded::Created(session, space, page) => (Shown::Created(session, space), page), |
| 3762 | Loaded::Page(space, page) => (Shown::Page(space), page), | 3803 | Loaded::Page(space, page) => (Shown::Page(space), page), |
| 3763 | Loaded::Version(space, version, page) => (Shown::Version(space, version), page), | 3804 | Loaded::Version(space, version, page) => (Shown::Version(space, version), page), |
| 3764 | Loaded::Library(library, path) => return Ok(Laid::Library(library, path)), | 3805 | Loaded::Library(library, path) => return Ok(Laid::Library(library, path)), |
| ... | @@ -3766,8 +3807,8 @@ impl State { | ... | @@ -3766,8 +3807,8 @@ impl State { |
| 3766 | let mut engine = layouts.lock().map_err(|_| "Page layout failed")?; | 3807 | let mut engine = layouts.lock().map_err(|_| "Page layout failed")?; |
| 3767 | let start = Instant::now(); | 3808 | let start = Instant::now(); |
| 3768 | let key = match &shown { | 3809 | let key = match &shown { |
| 3769 | Shown::Section(session) | Shown::Created(session) => { | 3810 | Shown::Section(session, space) | Shown::Created(session, space) => { |
| 3770 | Some((session.key(), session.space)) | 3811 | Some((session.key(), *space)) |
| 3771 | } | 3812 | } |
| 3772 | Shown::Page(space) => open.map(|open| (open, *space)), | 3813 | Shown::Page(space) => open.map(|open| (open, *space)), |
| 3773 | Shown::Version(..) => None, | 3814 | Shown::Version(..) => None, |
| ... | @@ -3846,12 +3887,12 @@ impl State { | ... | @@ -3846,12 +3887,12 @@ impl State { |
| 3846 | for (id, loaded) in reads { | 3887 | for (id, loaded) in reads { |
| 3847 | match loaded { | 3888 | match loaded { |
| 3848 | // A later load supersedes what this one shows, but not its failure. | 3889 | // A later load supersedes what this one shows, but not its failure. |
| 3849 | Ok(_) if id != self.loading => {} | 3890 | Ok(_) if id != self.pane.loading => {} |
| 3850 | Ok(Laid::Page(opening)) => self.opening = Some(*opening), | 3891 | Ok(Laid::Page(opening)) => self.pane.opening = Some(*opening), |
| 3851 | Ok(Laid::Library(library, path)) => self.adopt(library, path.as_deref())?, | 3892 | Ok(Laid::Library(library, path)) => self.adopt(library, path.as_deref())?, |
| 3852 | Err(error) => { | 3893 | Err(error) => { |
| 3853 | if id == self.loading { | 3894 | if id == self.pane.loading { |
| 3854 | self.switching = None; | 3895 | self.pane.switching = None; |
| 3855 | } | 3896 | } |
| 3856 | platform::alert("Couldn't open", &error); | 3897 | platform::alert("Couldn't open", &error); |
| 3857 | } | 3898 | } |
| ... | @@ -3861,17 +3902,18 @@ impl State { | ... | @@ -3861,17 +3902,18 @@ impl State { |
| 3861 | color: self.ui.theme.paper, | 3902 | color: self.ui.theme.paper, |
| 3862 | ink: self.ui.theme.paper_ink, | 3903 | ink: self.ui.theme.paper_ink, |
| 3863 | }; | 3904 | }; |
| 3864 | self.prefetch.draw(&self.view, paper, &self.redraw); | 3905 | self.prefetch.draw(&self.pane.view, paper, &self.redraw); |
| 3865 | let Some(mut opening) = self.opening.take() else { | 3906 | let Some(mut opening) = self.pane.opening.take() else { |
| 3866 | return Ok(()); | 3907 | return Ok(()); |
| 3867 | }; | 3908 | }; |
| 3868 | let paper = paper.colored(opening.editor.page_color()); | 3909 | let paper = paper.colored(opening.editor.page_color()); |
| 3869 | if !self | 3910 | if !self |
| 3911 | .pane | ||
| 3870 | .view | 3912 | .view |
| 3871 | .prepare(&mut opening.scene, &opening.editor, paper, &self.redraw) | 3913 | .prepare(&mut opening.scene, &opening.editor, paper, &self.redraw) |
| 3872 | && opening.since.elapsed() < HOLD | 3914 | && opening.since.elapsed() < HOLD |
| 3873 | { | 3915 | { |
| 3874 | self.opening = Some(opening); | 3916 | self.pane.opening = Some(opening); |
| 3875 | return Ok(()); | 3917 | return Ok(()); |
| 3876 | } | 3918 | } |
| 3877 | lap("switch pictures", opening.since); | 3919 | lap("switch pictures", opening.since); |
| ... | @@ -3880,7 +3922,7 @@ impl State { | ... | @@ -3880,7 +3922,7 @@ impl State { |
| 3880 | 3922 | ||
| 3881 | fn show_page(&mut self, mut opening: Opening) -> Result<(), Box<dyn Error>> { | 3923 | fn show_page(&mut self, mut opening: Opening) -> Result<(), Box<dyn Error>> { |
| 3882 | let requested = opening.requested; | 3924 | let requested = opening.requested; |
| 3883 | if let Shown::Section(session) | Shown::Created(session) = &mut opening.loaded | 3925 | if let Shown::Section(session, _) | Shown::Created(session, _) = &mut opening.loaded |
| 3884 | && let Some(library) = self | 3926 | && let Some(library) = self |
| 3885 | .notebooks | 3927 | .notebooks |
| 3886 | .iter() | 3928 | .iter() |
| ... | @@ -3905,7 +3947,7 @@ impl State { | ... | @@ -3905,7 +3947,7 @@ impl State { |
| 3905 | } else if let Some(path) = library.first_section() { | 3947 | } else if let Some(path) = library.first_section() { |
| 3906 | return self.apply(Command::OpenSection(library, path)); | 3948 | return self.apply(Command::OpenSection(library, path)); |
| 3907 | } else { | 3949 | } else { |
| 3908 | self.switching = None; | 3950 | self.pane.switching = None; |
| 3909 | return self.adopt(library, None); | 3951 | return self.adopt(library, None); |
| 3910 | } | 3952 | } |
| 3911 | } | 3953 | } |
| ... | @@ -3916,22 +3958,24 @@ impl State { | ... | @@ -3916,22 +3958,24 @@ impl State { |
| 3916 | let mut leaving = self | 3958 | let mut leaving = self |
| 3917 | .session | 3959 | .session |
| 3918 | .as_ref() | 3960 | .as_ref() |
| 3919 | .filter(|session| !session.read_only()) | 3961 | .filter(|session| !session.read_only(&self.pane.showing)) |
| 3920 | .map(|session| session.space); | 3962 | .map(|_| self.pane.showing.space); |
| 3921 | if let Some(session) = &self.session { | 3963 | if let Some(session) = &self.session { |
| 3922 | let key = session.key(); | 3964 | let key = session.key(); |
| 3923 | self.places | 3965 | self.places.insert( |
| 3924 | .insert((key.clone(), session.space), self.view.place()); | 3966 | (key.clone(), self.pane.showing.space), |
| 3925 | self.last_pages.insert(key.clone(), session.space); | 3967 | self.pane.view.place(), |
| 3926 | if session.version.is_none() | 3968 | ); |
| 3927 | && let Some(scene) = self.view.scene.take() | 3969 | self.last_pages.insert(key.clone(), self.pane.showing.space); |
| 3970 | if self.pane.showing.version.is_none() | ||
| 3971 | && let Some(scene) = self.pane.view.scene.take() | ||
| 3928 | { | 3972 | { |
| 3929 | self.prefetch.keep(key, session.space, scene); | 3973 | self.prefetch.keep(key, self.pane.showing.space, scene); |
| 3930 | } | 3974 | } |
| 3931 | } | 3975 | } |
| 3932 | let created = matches!(opening.loaded, Shown::Created(_)); | 3976 | let created = matches!(opening.loaded, Shown::Created(..)); |
| 3933 | match opening.loaded { | 3977 | match opening.loaded { |
| 3934 | Shown::Section(session) | Shown::Created(session) => { | 3978 | Shown::Section(session, space) | Shown::Created(session, space) => { |
| 3935 | // The settings keep the notebook shown, not the section. | 3979 | // The settings keep the notebook shown, not the section. |
| 3936 | let other_notebook = self | 3980 | let other_notebook = self |
| 3937 | .session | 3981 | .session |
| ... | @@ -3940,7 +3984,7 @@ impl State { | ... | @@ -3940,7 +3984,7 @@ impl State { |
| 3940 | self.list(&session.library); | 3984 | self.list(&session.library); |
| 3941 | // The section left stays open a while for coming back to; the notebook's | 3985 | // The section left stays open a while for coming back to; the notebook's |
| 3942 | // background sync takes it over once it is closed. | 3986 | // background sync takes it over once it is closed. |
| 3943 | self.locked = None; | 3987 | self.pane.locked = None; |
| 3944 | if let Some(previous) = self.session.replace(*session) { | 3988 | if let Some(previous) = self.session.replace(*session) { |
| 3945 | let path = &previous.tabs[previous.tab].path; | 3989 | let path = &previous.tabs[previous.tab].path; |
| 3946 | let locked = self.leave(&previous.library, path); | 3990 | let locked = self.leave(&previous.library, path); |
| ... | @@ -3956,7 +4000,11 @@ impl State { | ... | @@ -3956,7 +4000,11 @@ impl State { |
| 3956 | }); | 4000 | }); |
| 3957 | } | 4001 | } |
| 3958 | } | 4002 | } |
| 3959 | self.sectionless = None; | 4003 | self.pane.showing = Showing { |
| 4004 | space, | ||
| 4005 | ..Showing::default() | ||
| 4006 | }; | ||
| 4007 | self.pane.sectionless = None; | ||
| 3960 | if other_notebook { | 4008 | if other_notebook { |
| 3961 | self.save_settings(); | 4009 | self.save_settings(); |
| 3962 | } | 4010 | } |
| ... | @@ -3964,12 +4012,12 @@ impl State { | ... | @@ -3964,12 +4012,12 @@ impl State { |
| 3964 | Shown::Page(space) | Shown::Version(space, _) => { | 4012 | Shown::Page(space) | Shown::Version(space, _) => { |
| 3965 | let session = self.session.as_mut().ok_or("No section is open")?; | 4013 | let session = self.session.as_mut().ok_or("No section is open")?; |
| 3966 | let pages = session.section.pages()?; | 4014 | let pages = session.section.pages()?; |
| 3967 | session.space = space; | 4015 | self.pane.showing.space = space; |
| 3968 | session.version = match opening.loaded { | 4016 | self.pane.showing.version = match opening.loaded { |
| 3969 | Shown::Version(_, version) => Some(version), | 4017 | Shown::Version(_, version) => Some(version), |
| 3970 | _ => None, | 4018 | _ => None, |
| 3971 | }; | 4019 | }; |
| 3972 | session.change = None; | 4020 | self.pane.showing.change = None; |
| 3973 | // Pages created, moved or deleted since the list was read. | 4021 | // Pages created, moved or deleted since the list was read. |
| 3974 | session.pages = pages; | 4022 | session.pages = pages; |
| 3975 | leaving = leaving.filter(|left| session.pages.iter().any(|(s, ..)| s == left)); | 4023 | leaving = leaving.filter(|left| session.pages.iter().any(|(s, ..)| s == left)); |
| ... | @@ -3977,9 +4025,12 @@ impl State { | ... | @@ -3977,9 +4025,12 @@ impl State { |
| 3977 | } | 4025 | } |
| 3978 | let place = self.session.as_ref().and_then(|session| { | 4026 | let place = self.session.as_ref().and_then(|session| { |
| 3979 | let key = session.key(); | 4027 | let key = session.key(); |
| 3980 | self.places.get(&(key, session.space)).copied() | 4028 | self.places.get(&(key, self.pane.showing.space)).copied() |
| 3981 | }); | 4029 | }); |
| 3982 | let left = self.view.open(opening.editor, Some(opening.scene), place); | 4030 | let left = self |
| 4031 | .pane | ||
| 4032 | .view | ||
| 4033 | .open(opening.editor, Some(opening.scene), place); | ||
| 3983 | // Each page takes up the history it was left with. | 4034 | // Each page takes up the history it was left with. |
| 3984 | if let Some(leaving) = leaving { | 4035 | if let Some(leaving) = leaving { |
| 3985 | self.undo.park(leaving, left); | 4036 | self.undo.park(leaving, left); |
| ... | @@ -3987,19 +4038,19 @@ impl State { | ... | @@ -3987,19 +4038,19 @@ impl State { |
| 3987 | let shown = self | 4038 | let shown = self |
| 3988 | .session | 4039 | .session |
| 3989 | .as_ref() | 4040 | .as_ref() |
| 3990 | .filter(|session| !session.read_only()) | 4041 | .filter(|session| !session.read_only(&self.pane.showing)) |
| 3991 | .map(|session| session.space); | 4042 | .map(|_| self.pane.showing.space); |
| 3992 | if let Some(parked) = shown.and_then(|space| self.undo.resume(space)) { | 4043 | if let Some(parked) = shown.and_then(|space| self.undo.resume(space)) { |
| 3993 | self.view.resume(parked)?; | 4044 | self.pane.view.resume(parked)?; |
| 3994 | } | 4045 | } |
| 3995 | lap("switch shown", requested); | 4046 | lap("switch shown", requested); |
| 3996 | self.switching = None; | 4047 | self.pane.switching = None; |
| 3997 | self.visited(); | 4048 | self.visited(); |
| 3998 | if let Some((space, action)) = self.after_open.take() | 4049 | if let Some((space, action)) = self.after_open.take() |
| 3999 | && let Some(session) = self | 4050 | && let Some(session) = self |
| 4000 | .session | 4051 | .session |
| 4001 | .as_ref() | 4052 | .as_ref() |
| 4002 | .filter(|session| session.space == space) | 4053 | .filter(|_| self.pane.showing.space == space) |
| 4003 | { | 4054 | { |
| 4004 | let target = menus::Target::Page { | 4055 | let target = menus::Target::Page { |
| 4005 | library: Arc::clone(&session.library), | 4056 | library: Arc::clone(&session.library), |
| ... | @@ -4029,7 +4080,7 @@ impl State { | ... | @@ -4029,7 +4080,7 @@ impl State { |
| 4029 | self.list(&library); | 4080 | self.list(&library); |
| 4030 | let shown = |shown: &Library| shown.location == library.location; | 4081 | let shown = |shown: &Library| shown.location == library.location; |
| 4031 | let Some(path) = path else { | 4082 | let Some(path) = path else { |
| 4032 | if self.sectionless.as_deref().is_some_and(shown) | 4083 | if self.pane.sectionless.as_deref().is_some_and(shown) |
| 4033 | || self | 4084 | || self |
| 4034 | .session | 4085 | .session |
| 4035 | .as_ref() | 4086 | .as_ref() |
| ... | @@ -4043,7 +4094,7 @@ impl State { | ... | @@ -4043,7 +4094,7 @@ impl State { |
| 4043 | } | 4094 | } |
| 4044 | }); | 4095 | }); |
| 4045 | } | 4096 | } |
| 4046 | self.sectionless = Some(Arc::clone(&library)); | 4097 | self.pane.sectionless = Some(Arc::clone(&library)); |
| 4047 | self.title(); | 4098 | self.title(); |
| 4048 | } | 4099 | } |
| 4049 | self.show_arrived(&library); | 4100 | self.show_arrived(&library); |
| ... | @@ -4104,7 +4155,7 @@ impl State { | ... | @@ -4104,7 +4155,7 @@ impl State { |
| 4104 | fn after_move(&mut self) -> Result<(), Box<dyn Error>> { | 4155 | fn after_move(&mut self) -> Result<(), Box<dyn Error>> { |
| 4105 | let scale = self.ui.scale(); | 4156 | let scale = self.ui.scale(); |
| 4106 | let corner = self.ui.laid_out(page()).unwrap_or_default(); | 4157 | let corner = self.ui.laid_out(page()).unwrap_or_default(); |
| 4107 | let [x0, y0, x1, y1] = self.view.caret_area()?; | 4158 | let [x0, y0, x1, y1] = self.pane.view.caret_area()?; |
| 4108 | self.window.set_ime_cursor_area( | 4159 | self.window.set_ime_cursor_area( |
| 4109 | LogicalPosition::new(x0 / scale + corner[0], y0 / scale + corner[1]), | 4160 | LogicalPosition::new(x0 / scale + corner[0], y0 / scale + corner[1]), |
| 4110 | LogicalSize::new((x1 - x0) / scale, (y1 - y0) / scale), | 4161 | LogicalSize::new((x1 - x0) / scale, (y1 - y0) / scale), |
| ... | @@ -4132,11 +4183,11 @@ impl State { | ... | @@ -4132,11 +4183,11 @@ impl State { |
| 4132 | } else { | 4183 | } else { |
| 4133 | "" | 4184 | "" |
| 4134 | }; | 4185 | }; |
| 4135 | format!("{}{bin} · {file}", session.title()) | 4186 | format!("{}{bin} · {file}", session.title(self.pane.showing.space)) |
| 4136 | } | 4187 | } |
| 4137 | None if !self.temporary => "Snowbound".to_owned(), | 4188 | None if !self.temporary => "Snowbound".to_owned(), |
| 4138 | None => { | 4189 | None => { |
| 4139 | let editor = &self.view.editor; | 4190 | let editor = &self.pane.view.editor; |
| 4140 | if !editor.active_outline().title { | 4191 | if !editor.active_outline().title { |
| 4141 | return; | 4192 | return; |
| 4142 | } | 4193 | } |
| ... | @@ -4169,27 +4220,27 @@ impl State { | ... | @@ -4169,27 +4220,27 @@ impl State { |
| 4169 | } | 4220 | } |
| 4170 | 4221 | ||
| 4171 | fn edit_date(&mut self, field: DateField) { | 4222 | fn edit_date(&mut self, field: DateField) { |
| 4172 | let Some(date) = self.view.editor.date() else { | 4223 | let Some(date) = self.pane.view.editor.date() else { |
| 4173 | return; | 4224 | return; |
| 4174 | }; | 4225 | }; |
| 4175 | let timestamp = date.timestamp(); | 4226 | let timestamp = date.timestamp(); |
| 4176 | self.view.editor.finish_composition(); | 4227 | self.pane.view.editor.finish_composition(); |
| 4177 | platform::clear_marked_text(&self.window); | 4228 | platform::clear_marked_text(&self.window); |
| 4178 | let title = match field { | 4229 | let title = match field { |
| 4179 | DateField::Date => "Change Page Date", | 4230 | DateField::Date => "Change Page Date", |
| 4180 | DateField::Time => "Change Page Time", | 4231 | DateField::Time => "Change Page Time", |
| 4181 | }; | 4232 | }; |
| 4182 | let page = self.session.as_ref().map(|session| session.space); | 4233 | let page = self.session.as_ref().map(|_| self.pane.showing.space); |
| 4183 | let reply = self.reply( | 4234 | let reply = self.reply( |
| 4184 | move |state, chosen: Result<(u64, [String; 2]), &'static str>| { | 4235 | move |state, chosen: Result<(u64, [String; 2]), &'static str>| { |
| 4185 | let (changed, text) = chosen?; | 4236 | let (changed, text) = chosen?; |
| 4186 | // The answer belongs to the page that asked, unless another has opened since. | 4237 | // The answer belongs to the page that asked, unless another has opened since. |
| 4187 | if state.session.as_ref().map(|session| session.space) != page | 4238 | if state.session.as_ref().map(|_| state.pane.showing.space) != page |
| 4188 | || state.view.editor.date().map(|date| date.timestamp()) != Some(timestamp) | 4239 | || state.pane.view.editor.date().map(|date| date.timestamp()) != Some(timestamp) |
| 4189 | { | 4240 | { |
| 4190 | return Ok(()); | 4241 | return Ok(()); |
| 4191 | } | 4242 | } |
| 4192 | let response = state.view.change_date(changed, text)?; | 4243 | let response = state.pane.view.change_date(changed, text)?; |
| 4193 | state.respond(response); | 4244 | state.respond(response); |
| 4194 | Ok(()) | 4245 | Ok(()) |
| 4195 | }, | 4246 | }, |
| ... | @@ -4211,13 +4262,13 @@ impl State { | ... | @@ -4211,13 +4262,13 @@ impl State { |
| 4211 | let Some((_, version)) = self | 4262 | let Some((_, version)) = self |
| 4212 | .session | 4263 | .session |
| 4213 | .as_ref() | 4264 | .as_ref() |
| 4214 | .and_then(|session| session.conflict(session.space)) | 4265 | .and_then(|session| session.conflict(self.pane.showing.space)) |
| 4215 | else { | 4266 | else { |
| 4216 | return Vec::new(); | 4267 | return Vec::new(); |
| 4217 | }; | 4268 | }; |
| 4218 | let marked = |id: &ExGuid| version.objects.contains(id); | 4269 | let marked = |id: &ExGuid| version.objects.contains(id); |
| 4219 | let mut changes = Vec::new(); | 4270 | let mut changes = Vec::new(); |
| 4220 | for outline in self.view.editor.outlines() { | 4271 | for outline in self.pane.view.editor.outlines() { |
| 4221 | for (at, node) in outline.document().text_nodes().enumerate() { | 4272 | for (at, node) in outline.document().text_nodes().enumerate() { |
| 4222 | let Some(text) = node.text() else { | 4273 | let Some(text) = node.text() else { |
| 4223 | continue; | 4274 | continue; |
| ... | @@ -4234,10 +4285,10 @@ impl State { | ... | @@ -4234,10 +4285,10 @@ impl State { |
| 4234 | /// Selects the next or previous conflicting change, as OneNote's conflict menu does. | 4285 | /// Selects the next or previous conflicting change, as OneNote's conflict menu does. |
| 4235 | fn select_change(&mut self, forward: bool) -> Result<(), Box<dyn Error>> { | 4286 | fn select_change(&mut self, forward: bool) -> Result<(), Box<dyn Error>> { |
| 4236 | let changes = self.changes(); | 4287 | let changes = self.changes(); |
| 4237 | let Some(session) = &mut self.session else { | 4288 | if self.session.is_none() { |
| 4238 | return Ok(()); | 4289 | return Ok(()); |
| 4239 | }; | 4290 | } |
| 4240 | let next = match (session.change, forward) { | 4291 | let next = match (self.pane.showing.change, forward) { |
| 4241 | (None, true) => 0, | 4292 | (None, true) => 0, |
| 4242 | (Some(at), true) => at + 1, | 4293 | (Some(at), true) => at + 1, |
| 4243 | (Some(at), false) if at > 0 => at - 1, | 4294 | (Some(at), false) if at > 0 => at - 1, |
| ... | @@ -4246,12 +4297,12 @@ impl State { | ... | @@ -4246,12 +4297,12 @@ impl State { |
| 4246 | let Some(&(outline, paragraph, length)) = changes.get(next) else { | 4297 | let Some(&(outline, paragraph, length)) = changes.get(next) else { |
| 4247 | return Ok(()); | 4298 | return Ok(()); |
| 4248 | }; | 4299 | }; |
| 4249 | session.change = Some(next); | 4300 | self.pane.showing.change = Some(next); |
| 4250 | let at = |offset| canvas::document::TextPosition { paragraph, offset }; | 4301 | let at = |offset| canvas::document::TextPosition { paragraph, offset }; |
| 4251 | self.view.editor.focus_outline(outline)?; | 4302 | self.pane.view.editor.focus_outline(outline)?; |
| 4252 | self.view.editor.select([at(0), at(length)].into())?; | 4303 | self.pane.view.editor.select([at(0), at(length)].into())?; |
| 4253 | self.ui.set_focus(Some(page())); | 4304 | self.ui.set_focus(Some(page())); |
| 4254 | let response = self.view.focus_text()?; | 4305 | let response = self.pane.view.focus_text()?; |
| 4255 | self.respond(response); | 4306 | self.respond(response); |
| 4256 | self.window.request_redraw(); | 4307 | self.window.request_redraw(); |
| 4257 | Ok(()) | 4308 | Ok(()) |
| ... | @@ -4281,7 +4332,7 @@ impl State { | ... | @@ -4281,7 +4332,7 @@ impl State { |
| 4281 | /// Hands the section the ops the editor recorded since the last call, as one edit; the | 4332 | /// Hands the section the ops the editor recorded since the last call, as one edit; the |
| 4282 | /// section thread stores it. | 4333 | /// section thread stores it. |
| 4283 | fn persist(&mut self) -> Result<(), Box<dyn Error>> { | 4334 | fn persist(&mut self) -> Result<(), Box<dyn Error>> { |
| 4284 | let ops = match self.view.editor.take_ops() { | 4335 | let ops = match self.pane.view.editor.take_ops() { |
| 4285 | Ok(ops) => ops, | 4336 | Ok(ops) => ops, |
| 4286 | Err(error) => return self.refused(&error.to_string()), | 4337 | Err(error) => return self.refused(&error.to_string()), |
| 4287 | }; | 4338 | }; |
| ... | @@ -4291,12 +4342,12 @@ impl State { | ... | @@ -4291,12 +4342,12 @@ impl State { |
| 4291 | if ops.is_empty() { | 4342 | if ops.is_empty() { |
| 4292 | return Ok(()); | 4343 | return Ok(()); |
| 4293 | } | 4344 | } |
| 4294 | if session.read_only() { | 4345 | if session.read_only(&self.pane.showing) { |
| 4295 | // A conflict page or a version is read-only: it returns to what is stored. | 4346 | // A conflict page or a version is read-only: it returns to what is stored. |
| 4296 | self.stale = true; | 4347 | self.pane.stale = true; |
| 4297 | return Ok(()); | 4348 | return Ok(()); |
| 4298 | } | 4349 | } |
| 4299 | let space = session.space; | 4350 | let space = self.pane.showing.space; |
| 4300 | session.section.apply( | 4351 | session.section.apply( |
| 4301 | &self.author, | 4352 | &self.author, |
| 4302 | onestore::op::Edit { | 4353 | onestore::op::Edit { |
| ... | @@ -4308,18 +4359,20 @@ impl State { | ... | @@ -4308,18 +4359,20 @@ impl State { |
| 4308 | }, | 4359 | }, |
| 4309 | )?; | 4360 | )?; |
| 4310 | // The page list shows the title as it is typed, even when the caret left it since. | 4361 | // The page list shows the title as it is typed, even when the caret left it since. |
| 4311 | let editor = &self.view.editor; | 4362 | let editor = &self.pane.view.editor; |
| 4312 | let title = (editor.outlines().iter()) | 4363 | let title = (editor.outlines().iter()) |
| 4313 | .find(|outline| outline.title) | 4364 | .find(|outline| outline.title) |
| 4314 | .and_then(|outline| outline.document().paragraphs().last()) | 4365 | .and_then(|outline| outline.document().paragraphs().last()) |
| 4315 | .map(|paragraph| onestore::op::title_line(paragraph.text())); | 4366 | .map(|paragraph| onestore::op::title_line(paragraph.text())); |
| 4316 | if editor.active_outline().title | 4367 | if editor.active_outline().title |
| 4317 | || title.is_some_and(|title| !title.is_empty() && title != session.title()) | 4368 | || title.is_some_and(|title| { |
| 4369 | !title.is_empty() && title != session.title(self.pane.showing.space) | ||
| 4370 | }) | ||
| 4318 | { | 4371 | { |
| 4319 | session.pages = session.section.pages()?; | 4372 | session.pages = session.section.pages()?; |
| 4320 | } | 4373 | } |
| 4321 | self.edited(vec![space]); | 4374 | self.edited(vec![space]); |
| 4322 | let editor = &self.view.editor; | 4375 | let editor = &self.pane.view.editor; |
| 4323 | self.undo | 4376 | self.undo |
| 4324 | .edited(space, editor.history_depth(), editor.typing().is_some()); | 4377 | .edited(space, editor.history_depth(), editor.typing().is_some()); |
| 4325 | Ok(()) | 4378 | Ok(()) |
| ... | @@ -4328,17 +4381,17 @@ impl State { | ... | @@ -4328,17 +4381,17 @@ impl State { |
| 4328 | /// Shows the page as stored after a change made elsewhere, in place: the caret, | 4381 | /// Shows the page as stored after a change made elsewhere, in place: the caret, |
| 4329 | /// selection, scroll and drawn pictures stay. Waits for marked text to end. | 4382 | /// selection, scroll and drawn pictures stay. Waits for marked text to end. |
| 4330 | fn refresh(&mut self) -> Result<(), Box<dyn Error>> { | 4383 | fn refresh(&mut self) -> Result<(), Box<dyn Error>> { |
| 4331 | if self.view.editor.marked_range().is_some() { | 4384 | if self.pane.view.editor.marked_range().is_some() { |
| 4332 | self.stale = true; | 4385 | self.pane.stale = true; |
| 4333 | return Ok(()); | 4386 | return Ok(()); |
| 4334 | } | 4387 | } |
| 4335 | self.stale = false; | 4388 | self.pane.stale = false; |
| 4336 | self.persist()?; | 4389 | self.persist()?; |
| 4337 | let Some(session) = &self.session else { | 4390 | let Some(session) = &self.session else { |
| 4338 | return Ok(()); | 4391 | return Ok(()); |
| 4339 | }; | 4392 | }; |
| 4340 | let page = session.reader(session.space)()?; | 4393 | let page = session.reader(self.pane.showing.space)()?; |
| 4341 | let response = self.view.refresh(page)?; | 4394 | let response = self.pane.view.refresh(page)?; |
| 4342 | self.respond(response); | 4395 | self.respond(response); |
| 4343 | self.title(); | 4396 | self.title(); |
| 4344 | self.window.request_redraw(); | 4397 | self.window.request_redraw(); |
| ... | @@ -4349,10 +4402,13 @@ impl State { | ... | @@ -4349,10 +4402,13 @@ impl State { |
| 4349 | /// text the editor showed goes on the clipboard so nothing typed is lost unannounced. | 4402 | /// text the editor showed goes on the clipboard so nothing typed is lost unannounced. |
| 4350 | fn refused(&mut self, error: &str) -> Result<(), Box<dyn Error>> { | 4403 | fn refused(&mut self, error: &str) -> Result<(), Box<dyn Error>> { |
| 4351 | eprintln!("Saving failed: {error}"); | 4404 | eprintln!("Saving failed: {error}"); |
| 4352 | self.clipboard.set_text(page_text(&self.view.editor))?; | 4405 | self.clipboard.set_text(page_text(&self.pane.view.editor))?; |
| 4353 | // What the editor recorded since builds on the refused edit. | 4406 | // What the editor recorded since builds on the refused edit. |
| 4354 | let _ = self.view.editor.take_ops(); | 4407 | let _ = self.pane.view.editor.take_ops(); |
| 4355 | self.view.editor.cancel_composition(&mut self.view.engine)?; | 4408 | self.pane |
| 4409 | .view | ||
| 4410 | .editor | ||
| 4411 | .cancel_composition(&mut self.pane.view.engine)?; | ||
| 4356 | platform::clear_marked_text(&self.window); | 4412 | platform::clear_marked_text(&self.window); |
| 4357 | self.refresh()?; | 4413 | self.refresh()?; |
| 4358 | platform::alert( | 4414 | platform::alert( |
| ... | @@ -4413,13 +4469,13 @@ impl State { | ... | @@ -4413,13 +4469,13 @@ impl State { |
| 4413 | match event { | 4469 | match event { |
| 4414 | Event::Changed(spaces) => { | 4470 | Event::Changed(spaces) => { |
| 4415 | listed = true; | 4471 | listed = true; |
| 4416 | changed |= spaces.contains(&session.space); | 4472 | changed |= spaces.contains(&self.pane.showing.space); |
| 4417 | elsewhere.extend_from_slice(&spaces); | 4473 | elsewhere.extend_from_slice(&spaces); |
| 4418 | self.search | 4474 | self.search |
| 4419 | .changed(session.key(), session.section.replica(), spaces); | 4475 | .changed(session.key(), session.section.replica(), spaces); |
| 4420 | } | 4476 | } |
| 4421 | Event::Rejected { spaces, error } => { | 4477 | Event::Rejected { spaces, error } => { |
| 4422 | if spaces.contains(&session.space) { | 4478 | if spaces.contains(&self.pane.showing.space) { |
| 4423 | rejected = Some(error); | 4479 | rejected = Some(error); |
| 4424 | } else { | 4480 | } else { |
| 4425 | eprintln!("Saving failed: {error}"); | 4481 | eprintln!("Saving failed: {error}"); |
| ... | @@ -4447,23 +4503,26 @@ impl State { | ... | @@ -4447,23 +4503,26 @@ impl State { |
| 4447 | return Ok(()); | 4503 | return Ok(()); |
| 4448 | }; | 4504 | }; |
| 4449 | if listed { | 4505 | if listed { |
| 4450 | let back = session.shown; | 4506 | let back = self.pane.showing.shown; |
| 4451 | session.pages = session.section.pages()?; | 4507 | session.pages = session.section.pages()?; |
| 4452 | session.refresh_conflicts()?; | 4508 | session.refresh_conflicts(&mut self.pane.showing)?; |
| 4453 | session.refresh_history()?; | 4509 | session.refresh_history(&mut self.pane.showing)?; |
| 4454 | let listed = |space: ExGuid| session.pages.iter().any(|(page, ..)| *page == space); | 4510 | let listed = |space: ExGuid| session.pages.iter().any(|(page, ..)| *page == space); |
| 4455 | let gone = session.version.is_some_and(|version| { | 4511 | let gone = self.pane.showing.version.is_some_and(|version| { |
| 4456 | !session | 4512 | !session |
| 4457 | .page_versions(session.space) | 4513 | .page_versions(self.pane.showing.space) |
| 4458 | .iter() | 4514 | .iter() |
| 4459 | .any(|listed| listed.context == version) | 4515 | .any(|listed| listed.context == version) |
| 4460 | }); | 4516 | }); |
| 4461 | if gone && listed(session.space) { | 4517 | if gone && listed(self.pane.showing.space) { |
| 4462 | // A version deleted elsewhere returns to its page. | 4518 | // A version deleted elsewhere returns to its page. |
| 4463 | self.commands.push(Command::OpenPage(session.space)); | 4519 | self.commands |
| 4520 | .push(Command::OpenPage(self.pane.showing.space)); | ||
| 4464 | return Ok(()); | 4521 | return Ok(()); |
| 4465 | } | 4522 | } |
| 4466 | if !listed(session.space) && session.conflict(session.space).is_none() { | 4523 | if !listed(self.pane.showing.space) |
| 4524 | && session.conflict(self.pane.showing.space).is_none() | ||
| 4525 | { | ||
| 4467 | // A page removed elsewhere, or a conflict page deleted elsewhere, which | 4526 | // A page removed elsewhere, or a conflict page deleted elsewhere, which |
| 4468 | // returns to its page. | 4527 | // returns to its page. |
| 4469 | let open = back | 4528 | let open = back |
| ... | @@ -4511,7 +4570,7 @@ impl State { | ... | @@ -4511,7 +4570,7 @@ impl State { |
| 4511 | } | 4570 | } |
| 4512 | let mut error = None; | 4571 | let mut error = None; |
| 4513 | let mut sent = false; | 4572 | let mut sent = false; |
| 4514 | let (view, ui, window) = (&self.view, &self.ui, &self.window); | 4573 | let (view, ui, window) = (&self.pane.view, &self.ui, &self.window); |
| 4515 | self.access_adapter.update_if_active(|| { | 4574 | self.access_adapter.update_if_active(|| { |
| 4516 | sent = true; | 4575 | sent = true; |
| 4517 | page_tree(view, ui, window, &mut self.accessibility).unwrap_or_else(|failure| { | 4576 | page_tree(view, ui, window, &mut self.accessibility).unwrap_or_else(|failure| { |
| ... | @@ -4592,7 +4651,7 @@ impl State { | ... | @@ -4592,7 +4651,7 @@ impl State { |
| 4592 | let mut tree = Tree::new(chrome, true); | 4651 | let mut tree = Tree::new(chrome, true); |
| 4593 | if self.ui.laid_out(page()).is_some() { | 4652 | if self.ui.laid_out(page()).is_some() { |
| 4594 | let mut fresh = accessibility::Accessibility::default(); | 4653 | let mut fresh = accessibility::Accessibility::default(); |
| 4595 | let page = page_tree(&self.view, &self.ui, &self.window, &mut fresh)?; | 4654 | let page = page_tree(&self.pane.view, &self.ui, &self.window, &mut fresh)?; |
| 4596 | tree.update_and_process_changes(page, &mut Unwatched); | 4655 | tree.update_and_process_changes(page, &mut Unwatched); |
| 4597 | } | 4656 | } |
| 4598 | if focus != graft || self.ui.laid_out(page()).is_some() { | 4657 | if focus != graft || self.ui.laid_out(page()).is_some() { |
| ... | @@ -4638,7 +4697,7 @@ impl State { | ... | @@ -4638,7 +4697,7 @@ impl State { |
| 4638 | if request.action == Action::Focus { | 4697 | if request.action == Action::Focus { |
| 4639 | self.window.focus_window(); | 4698 | self.window.focus_window(); |
| 4640 | self.ui.set_focus(Some(page())); | 4699 | self.ui.set_focus(Some(page())); |
| 4641 | let response = self.view.focus_read_only(index)?; | 4700 | let response = self.pane.view.focus_read_only(index)?; |
| 4642 | self.respond(response); | 4701 | self.respond(response); |
| 4643 | self.window.request_redraw(); | 4702 | self.window.request_redraw(); |
| 4644 | } | 4703 | } |
| ... | @@ -4647,7 +4706,7 @@ impl State { | ... | @@ -4647,7 +4706,7 @@ impl State { |
| 4647 | let Some(outline) = self.accessibility.outline_for_node(request.target_node) else { | 4706 | let Some(outline) = self.accessibility.outline_for_node(request.target_node) else { |
| 4648 | return Ok(()); | 4707 | return Ok(()); |
| 4649 | }; | 4708 | }; |
| 4650 | let view = &mut self.view; | 4709 | let view = &mut self.pane.view; |
| 4651 | match (request.action, request.data) { | 4710 | match (request.action, request.data) { |
| 4652 | (Action::Focus, _) => { | 4711 | (Action::Focus, _) => { |
| 4653 | view.editor.focus_outline(outline)?; | 4712 | view.editor.focus_outline(outline)?; |
| ... | @@ -4671,7 +4730,7 @@ impl State { | ... | @@ -4671,7 +4730,7 @@ impl State { |
| 4671 | _ => return Ok(()), | 4730 | _ => return Ok(()), |
| 4672 | } | 4731 | } |
| 4673 | self.ui.set_focus(Some(page())); | 4732 | self.ui.set_focus(Some(page())); |
| 4674 | let response = self.view.focus_text()?; | 4733 | let response = self.pane.view.focus_text()?; |
| 4675 | self.respond(response); | 4734 | self.respond(response); |
| 4676 | self.window.request_redraw(); | 4735 | self.window.request_redraw(); |
| 4677 | Ok(()) | 4736 | Ok(()) |
| ... | @@ -4682,8 +4741,12 @@ impl State { | ... | @@ -4682,8 +4741,12 @@ impl State { |
| 4682 | if let Some(path) = self.snapshot.take() { | 4741 | if let Some(path) = self.snapshot.take() { |
| 4683 | // A hidden window never paints, so nothing polls the pictures between snapshots. | 4742 | // A hidden window never paints, so nothing polls the pictures between snapshots. |
| 4684 | let paper = self.paper(); | 4743 | let paper = self.paper(); |
| 4685 | if let Some((scene, _)) = &mut self.view.scene { | 4744 | if let Some((scene, _)) = &mut self.pane.view.scene { |
| 4686 | scene.settle(Some(&self.view.editor), self.view.viewport.scale, paper); | 4745 | scene.settle( |
| 4746 | Some(&self.pane.view.editor), | ||
| 4747 | self.pane.view.viewport.scale, | ||
| 4748 | paper, | ||
| 4749 | ); | ||
| 4687 | } | 4750 | } |
| 4688 | self.snapshot(&path)?; | 4751 | self.snapshot(&path)?; |
| 4689 | } | 4752 | } |
| ... | @@ -4724,7 +4787,7 @@ impl State { | ... | @@ -4724,7 +4787,7 @@ impl State { |
| 4724 | 4787 | ||
| 4725 | /// Marks misspelled words on the page unless Hide Spelling Errors is on. | 4788 | /// Marks misspelled words on the page unless Hide Spelling Errors is on. |
| 4726 | fn show_spelling(&mut self) { | 4789 | fn show_spelling(&mut self) { |
| 4727 | self.view.spelling = self.spelling.clone().filter(|_| !self.hide_spelling); | 4790 | self.pane.view.spelling = self.spelling.clone().filter(|_| !self.hide_spelling); |
| 4728 | self.window.request_redraw(); | 4791 | self.window.request_redraw(); |
| 4729 | } | 4792 | } |
| 4730 | 4793 | ||
| ... | @@ -4736,7 +4799,7 @@ impl State { | ... | @@ -4736,7 +4799,7 @@ impl State { |
| 4736 | } | 4799 | } |
| 4737 | .colored( | 4800 | .colored( |
| 4738 | self.page_color_preview | 4801 | self.page_color_preview |
| 4739 | .unwrap_or_else(|| self.view.editor.page_color()), | 4802 | .unwrap_or_else(|| self.pane.view.editor.page_color()), |
| 4740 | ) | 4803 | ) |
| 4741 | } | 4804 | } |
| 4742 | 4805 | ||
| ... | @@ -4758,9 +4821,9 @@ impl State { | ... | @@ -4758,9 +4821,9 @@ impl State { |
| 4758 | fn paint(&mut self, target: &draw::Target) -> Result<(), Box<dyn Error>> { | 4821 | fn paint(&mut self, target: &draw::Target) -> Result<(), Box<dyn Error>> { |
| 4759 | let start = Instant::now(); | 4822 | let start = Instant::now(); |
| 4760 | let paper = self.paper(); | 4823 | let paper = self.paper(); |
| 4761 | self.view.update_pictures(paper, &self.redraw); | 4824 | self.pane.view.update_pictures(paper, &self.redraw); |
| 4762 | if let Some(session) = &self.session { | 4825 | if let Some(session) = &self.session { |
| 4763 | self.view.tag_art = session.library.tag_art(); | 4826 | self.pane.view.tag_art = session.library.tag_art(); |
| 4764 | } | 4827 | } |
| 4765 | let theme = &self.ui.theme; | 4828 | let theme = &self.ui.theme; |
| 4766 | let scale = self.ui.scale(); | 4829 | let scale = self.ui.scale(); |
| ... | @@ -4772,20 +4835,20 @@ impl State { | ... | @@ -4772,20 +4835,20 @@ impl State { |
| 4772 | canvas::gpu::Viewport { | 4835 | canvas::gpu::Viewport { |
| 4773 | scale, | 4836 | scale, |
| 4774 | origin: [0.0; 2], | 4837 | origin: [0.0; 2], |
| 4775 | ..self.view.viewport | 4838 | ..self.pane.view.viewport |
| 4776 | }, | 4839 | }, |
| 4777 | ), | 4840 | ), |
| 4778 | None => ( | 4841 | None => ( |
| 4779 | self.view.primitives(TextColors { | 4842 | self.pane.view.primitives(TextColors { |
| 4780 | caret: theme.caret, | 4843 | caret: theme.caret, |
| 4781 | selection: if self.page_focused { | 4844 | selection: if self.pane.page_focused { |
| 4782 | theme.selection | 4845 | theme.selection |
| 4783 | } else { | 4846 | } else { |
| 4784 | theme.inactive_selection | 4847 | theme.inactive_selection |
| 4785 | }, | 4848 | }, |
| 4786 | paper, | 4849 | paper, |
| 4787 | })?, | 4850 | })?, |
| 4788 | self.view.viewport, | 4851 | self.pane.view.viewport, |
| 4789 | ), | 4852 | ), |
| 4790 | }; | 4853 | }; |
| 4791 | lap("page primitives", start); | 4854 | lap("page primitives", start); |
| ... | @@ -4853,14 +4916,17 @@ impl State { | ... | @@ -4853,14 +4916,17 @@ impl State { |
| 4853 | fn pinch(&mut self, factor: f32) -> Result<(), Box<dyn Error>> { | 4916 | fn pinch(&mut self, factor: f32) -> Result<(), Box<dyn Error>> { |
| 4854 | if self.over_page() { | 4917 | if self.over_page() { |
| 4855 | // The pointer here, as the page's own lags by the moves queued for the next frame. | 4918 | // The pointer here, as the page's own lags by the moves queued for the next frame. |
| 4856 | let response = self.view.pinch(factor, self.page_point(self.pointer))?; | 4919 | let response = self |
| 4920 | .pane | ||
| 4921 | .view | ||
| 4922 | .pinch(factor, self.page_point(self.pointer))?; | ||
| 4857 | self.respond(response); | 4923 | self.respond(response); |
| 4858 | } | 4924 | } |
| 4859 | Ok(()) | 4925 | Ok(()) |
| 4860 | } | 4926 | } |
| 4861 | 4927 | ||
| 4862 | fn swipe_scroll(&mut self, phase: winit::event::TouchPhase, delta: [f32; 2]) { | 4928 | fn swipe_scroll(&mut self, phase: winit::event::TouchPhase, delta: [f32; 2]) { |
| 4863 | let scroll = self.view.scroll(); | 4929 | let scroll = self.pane.view.scroll(); |
| 4864 | let armed = scroll.max[0] <= scroll.min[0] && self.over_page(); | 4930 | let armed = scroll.max[0] <= scroll.min[0] && self.over_page(); |
| 4865 | if let Some(forward) = self.swipe.scroll(phase, delta, armed, Instant::now()) { | 4931 | if let Some(forward) = self.swipe.scroll(phase, delta, armed, Instant::now()) { |
| 4866 | self.travel(forward); | 4932 | self.travel(forward); |
| ... | @@ -5209,6 +5275,7 @@ fn page_rows( | ... | @@ -5209,6 +5275,7 @@ fn page_rows( |
| 5209 | theme: &Theme, | 5275 | theme: &Theme, |
| 5210 | section: &ui::Section, | 5276 | section: &ui::Section, |
| 5211 | session: &Session, | 5277 | session: &Session, |
| 5278 | showing: &Showing, | ||
| 5212 | marked: [&HashSet<ExGuid>; 2], | 5279 | marked: [&HashSet<ExGuid>; 2], |
| 5213 | peers: &HashMap<ExGuid, Vec<[f32; 4]>>, | 5280 | peers: &HashMap<ExGuid, Vec<[f32; 4]>>, |
| 5214 | shape: (f32, bool), | 5281 | shape: (f32, bool), |
| ... | @@ -5255,6 +5322,7 @@ fn page_rows( | ... | @@ -5255,6 +5322,7 @@ fn page_rows( |
| 5255 | theme, | 5322 | theme, |
| 5256 | section, | 5323 | section, |
| 5257 | session, | 5324 | session, |
| 5325 | showing, | ||
| 5258 | marked, | 5326 | marked, |
| 5259 | peers, | 5327 | peers, |
| 5260 | shape, | 5328 | shape, |
| ... | @@ -5283,6 +5351,7 @@ fn page_rows( | ... | @@ -5283,6 +5351,7 @@ fn page_rows( |
| 5283 | theme, | 5351 | theme, |
| 5284 | section, | 5352 | section, |
| 5285 | session, | 5353 | session, |
| 5354 | showing, | ||
| 5286 | marked, | 5355 | marked, |
| 5287 | peers, | 5356 | peers, |
| 5288 | shape, | 5357 | shape, |
| ... | @@ -5305,6 +5374,7 @@ fn page_row( | ... | @@ -5305,6 +5374,7 @@ fn page_row( |
| 5305 | theme: &Theme, | 5374 | theme: &Theme, |
| 5306 | section: &ui::Section, | 5375 | section: &ui::Section, |
| 5307 | session: &Session, | 5376 | session: &Session, |
| 5377 | showing: &Showing, | ||
| 5308 | [found, unread]: [&HashSet<ExGuid>; 2], | 5378 | [found, unread]: [&HashSet<ExGuid>; 2], |
| 5309 | peers: &HashMap<ExGuid, Vec<[f32; 4]>>, | 5379 | peers: &HashMap<ExGuid, Vec<[f32; 4]>>, |
| 5310 | shape: (f32, bool), | 5380 | shape: (f32, bool), |
| ... | @@ -5334,7 +5404,7 @@ fn page_row( | ... | @@ -5334,7 +5404,7 @@ fn page_row( |
| 5334 | shift, | 5404 | shift, |
| 5335 | lifted, | 5405 | lifted, |
| 5336 | }; | 5406 | }; |
| 5337 | let selected = *space == session.space && session.version.is_none(); | 5407 | let selected = *space == showing.space && showing.version.is_none(); |
| 5338 | if selected && !lifted { | 5408 | if selected && !lifted { |
| 5339 | rows.open = Some(ui.id(space)); | 5409 | rows.open = Some(ui.id(space)); |
| 5340 | } | 5410 | } |
| ... | @@ -5366,19 +5436,19 @@ fn page_row( | ... | @@ -5366,19 +5436,19 @@ fn page_row( |
| 5366 | }; | 5436 | }; |
| 5367 | // Each row: its id, its label, whether it is open, and whether it is a page version. | 5437 | // Each row: its id, its label, whether it is open, and whether it is a page version. |
| 5368 | let history = (session.page_versions(*space).iter()) | 5438 | let history = (session.page_versions(*space).iter()) |
| 5369 | .filter(|_| session.shown_history == Some(*space)) | 5439 | .filter(|_| showing.shown_history == Some(*space)) |
| 5370 | .map(|version| { | 5440 | .map(|version| { |
| 5371 | let open = session.version == Some(version.context); | 5441 | let open = showing.version == Some(version.context); |
| 5372 | (version.context, history::label(version), open, true) | 5442 | (version.context, history::label(version), open, true) |
| 5373 | }); | 5443 | }); |
| 5374 | let conflicts = (versions.iter()) | 5444 | let conflicts = (versions.iter()) |
| 5375 | .filter(|_| session.shown == Some(*space)) | 5445 | .filter(|_| showing.shown == Some(*space)) |
| 5376 | .map(|version| { | 5446 | .map(|version| { |
| 5377 | let label = match version.created { | 5447 | let label = match version.created { |
| 5378 | Some(created) => format!("{} {}", platform::short_date(created), version.user), | 5448 | Some(created) => format!("{} {}", platform::short_date(created), version.user), |
| 5379 | None => version.user.clone(), | 5449 | None => version.user.clone(), |
| 5380 | }; | 5450 | }; |
| 5381 | (version.space, label, version.space == session.space, false) | 5451 | (version.space, label, version.space == showing.space, false) |
| 5382 | }); | 5452 | }); |
| 5383 | for (id, label, selected, version) in history.chain(conflicts) { | 5453 | for (id, label, selected, version) in history.chain(conflicts) { |
| 5384 | let tab = PageTab { | 5454 | let tab = PageTab { |
| ... | @@ -6236,14 +6306,14 @@ pub fn plain(error: &(dyn Error + 'static), thing: &str) -> String { | ... | @@ -6236,14 +6306,14 @@ pub fn plain(error: &(dyn Error + 'static), thing: &str) -> String { |
| 6236 | } | 6306 | } |
| 6237 | } | 6307 | } |
| 6238 | 6308 | ||
| 6239 | /// The session for `section`, at catalog `path` in `library`, showing `space` or its first | 6309 | /// The session for `section`, at catalog `path` in `library`, with `space` or its first |
| 6240 | /// page, and that page. | 6310 | /// page and that page. |
| 6241 | fn read_session( | 6311 | fn read_session( |
| 6242 | section: notebook::session::Section, | 6312 | section: notebook::session::Section, |
| 6243 | library: Arc<Library>, | 6313 | library: Arc<Library>, |
| 6244 | path: String, | 6314 | path: String, |
| 6245 | space: Option<ExGuid>, | 6315 | space: Option<ExGuid>, |
| 6246 | ) -> Result<(Session, Page), Box<dyn Error>> { | 6316 | ) -> Result<(Session, ExGuid, Page), Box<dyn Error>> { |
| 6247 | let folder = path.rsplit_once('/').map_or("", |(folder, _)| folder); | 6317 | let folder = path.rsplit_once('/').map_or("", |(folder, _)| folder); |
| 6248 | let tabs = library.tabs(folder); | 6318 | let tabs = library.tabs(folder); |
| 6249 | let tab = tabs | 6319 | let tab = tabs |
| ... | @@ -6266,15 +6336,11 @@ fn read_session( | ... | @@ -6266,15 +6336,11 @@ fn read_session( |
| 6266 | tabs, | 6336 | tabs, |
| 6267 | tab, | 6337 | tab, |
| 6268 | pages, | 6338 | pages, |
| 6269 | space, | ||
| 6270 | sync, | 6339 | sync, |
| 6271 | conflicts, | 6340 | conflicts, |
| 6272 | shown: None, | ||
| 6273 | change: None, | ||
| 6274 | history, | 6341 | history, |
| 6275 | shown_history: None, | ||
| 6276 | version: None, | ||
| 6277 | }, | 6342 | }, |
| 6343 | space, | ||
| 6278 | page, | 6344 | page, |
| 6279 | )) | 6345 | )) |
| 6280 | } | 6346 | } |
| ... | @@ -6371,7 +6437,7 @@ impl State { | ... | @@ -6371,7 +6437,7 @@ impl State { |
| 6371 | } | 6437 | } |
| 6372 | UserEvent::InsertText(text) => { | 6438 | UserEvent::InsertText(text) => { |
| 6373 | if self.ui.focused() == Some(page()) { | 6439 | if self.ui.focused() == Some(page()) { |
| 6374 | match self.view.insert_text(text) { | 6440 | match self.pane.view.insert_text(text) { |
| 6375 | Ok(response) => self.respond(response), | 6441 | Ok(response) => self.respond(response), |
| 6376 | Err(error) => eprintln!("{error}"), | 6442 | Err(error) => eprintln!("{error}"), |
| 6377 | } | 6443 | } |
| ... | @@ -6488,7 +6554,7 @@ impl State { | ... | @@ -6488,7 +6554,7 @@ impl State { |
| 6488 | // Quiet still when the mark sent on finding it quiet arrives, what a thread sent | 6554 | // Quiet still when the mark sent on finding it quiet arrives, what a thread sent |
| 6489 | // as it ended has been taken, however many frames wait in the queue before it. | 6555 | // as it ended has been taken, however many frames wait in the queue before it. |
| 6490 | let quiet = self.settled() | 6556 | let quiet = self.settled() |
| 6491 | && self.opening.is_none() | 6557 | && self.pane.opening.is_none() |
| 6492 | && RUNNING.load(Ordering::Acquire) == 0 | 6558 | && RUNNING.load(Ordering::Acquire) == 0 |
| 6493 | && self.search.pending.load(Ordering::Relaxed) == 0; | 6559 | && self.search.pending.load(Ordering::Relaxed) == 0; |
| 6494 | if let Some((path, settled, sent)) = self.replay_settle.take() { | 6560 | if let Some((path, settled, sent)) = self.replay_settle.take() { |
| ... | @@ -6522,7 +6588,7 @@ impl State { | ... | @@ -6522,7 +6588,7 @@ impl State { |
| 6522 | if event.window_id != self.window.id() { | 6588 | if event.window_id != self.window.id() { |
| 6523 | return; | 6589 | return; |
| 6524 | } | 6590 | } |
| 6525 | let was_marked = self.view.editor.marked_range().is_some(); | 6591 | let was_marked = self.pane.view.editor.marked_range().is_some(); |
| 6526 | let result = match event.window_event { | 6592 | let result = match event.window_event { |
| 6527 | accesskit_winit::WindowEvent::InitialTreeRequested => { | 6593 | accesskit_winit::WindowEvent::InitialTreeRequested => { |
| 6528 | self.deactivate_accessibility(); | 6594 | self.deactivate_accessibility(); |
| ... | @@ -6534,7 +6600,7 @@ impl State { | ... | @@ -6534,7 +6600,7 @@ impl State { |
| 6534 | Ok(()) | 6600 | Ok(()) |
| 6535 | } | 6601 | } |
| 6536 | }; | 6602 | }; |
| 6537 | if was_marked && self.view.editor.marked_range().is_none() { | 6603 | if was_marked && self.pane.view.editor.marked_range().is_none() { |
| 6538 | platform::clear_marked_text(&self.window); | 6604 | platform::clear_marked_text(&self.window); |
| 6539 | } | 6605 | } |
| 6540 | if let Err(error) = result { | 6606 | if let Err(error) = result { |
| ... | @@ -6547,7 +6613,7 @@ impl State { | ... | @@ -6547,7 +6613,7 @@ impl State { |
| 6547 | impl State { | 6613 | impl State { |
| 6548 | /// Quits, first asking whether to discard a temporary page holding edits. | 6614 | /// Quits, first asking whether to discard a temporary page holding edits. |
| 6549 | fn quit(&self) { | 6615 | fn quit(&self) { |
| 6550 | let editor = &self.view.editor; | 6616 | let editor = &self.pane.view.editor; |
| 6551 | let unchanged = editor.caret_outline().is_none_or(TextOutline::is_empty) | 6617 | let unchanged = editor.caret_outline().is_none_or(TextOutline::is_empty) |
| 6552 | && self.initial_date == editor.date().map(|date| date.timestamp()) | 6618 | && self.initial_date == editor.date().map(|date| date.timestamp()) |
| 6553 | && (self.initial_layouts.iter()) | 6619 | && (self.initial_layouts.iter()) |
| ... | @@ -6804,7 +6870,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -6804,7 +6870,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 6804 | WindowEvent::PinchGesture { delta, .. } => state.pinch(1.0 + delta as f32)?, | 6870 | WindowEvent::PinchGesture { delta, .. } => state.pinch(1.0 + delta as f32)?, |
| 6805 | // Smart zoom: to 200% about the fingers, or back to 100%. | 6871 | // Smart zoom: to 200% about the fingers, or back to 100%. |
| 6806 | WindowEvent::DoubleTapGesture { .. } => { | 6872 | WindowEvent::DoubleTapGesture { .. } => { |
| 6807 | let zoom = state.view.zoom(); | 6873 | let zoom = state.pane.view.zoom(); |
| 6808 | let target = if (zoom - 1.0).abs() < 0.01 { 2.0 } else { 1.0 }; | 6874 | let target = if (zoom - 1.0).abs() < 0.01 { 2.0 } else { 1.0 }; |
| 6809 | state.pinch(target / zoom)? | 6875 | state.pinch(target / zoom)? |
| 6810 | } | 6876 | } |
| ... | @@ -6826,7 +6892,7 @@ impl ApplicationHandler<UserEvent> for App { | ... | @@ -6826,7 +6892,7 @@ impl ApplicationHandler<UserEvent> for App { |
| 6826 | return; | 6892 | return; |
| 6827 | } | 6893 | } |
| 6828 | let now = Instant::now(); | 6894 | let now = Instant::now(); |
| 6829 | let (repaint, blink) = state.view.blink(now); | 6895 | let (repaint, blink) = state.pane.view.blink(now); |
| 6830 | // A due interface change waits on the frame that shows it, which sets the next. | 6896 | // A due interface change waits on the frame that shows it, which sets the next. |
| 6831 | let wake = state.ui.wake_at(); | 6897 | let wake = state.ui.wake_at(); |
| 6832 | if repaint || wake.is_some_and(|wake| wake <= now) { | 6898 | if repaint || wake.is_some_and(|wake| wake <= now) { |
| ... | @@ -7562,7 +7628,7 @@ mod tests { | ... | @@ -7562,7 +7628,7 @@ mod tests { |
| 7562 | let section = library.open(&tab.path, || {}).unwrap(); | 7628 | let section = library.open(&tab.path, || {}).unwrap(); |
| 7563 | let opened = ms(start); | 7629 | let opened = ms(start); |
| 7564 | let start = Instant::now(); | 7630 | let start = Instant::now(); |
| 7565 | let (session, page) = | 7631 | let (session, _, page) = |
| 7566 | read_session(section, Arc::clone(&library), tab.path.clone(), None).unwrap(); | 7632 | read_session(section, Arc::clone(&library), tab.path.clone(), None).unwrap(); |
| 7567 | let read = ms(start); | 7633 | let read = ms(start); |
| 7568 | let start = Instant::now(); | 7634 | let start = Instant::now(); |
crates/snowbound/src/manage.rs+50-41| ... | @@ -177,8 +177,8 @@ impl State { | ... | @@ -177,8 +177,8 @@ impl State { |
| 177 | title: &str, | 177 | title: &str, |
| 178 | ) -> Result<PageCreation, Box<dyn Error>> { | 178 | ) -> Result<PageCreation, Box<dyn Error>> { |
| 179 | let creation = PageCreation::new(before, Some(title), &self.author)?.titled_in( | 179 | let creation = PageCreation::new(before, Some(title), &self.author)?.titled_in( |
| 180 | &self.view.editor.default_font.face, | 180 | &self.pane.view.editor.default_font.face, |
| 181 | self.view.editor.default_font.color, | 181 | self.pane.view.editor.default_font.color, |
| 182 | )?; | 182 | )?; |
| 183 | let [date, time] = platform::date_text(creation.created()); | 183 | let [date, time] = platform::date_text(creation.created()); |
| 184 | Ok(creation.dated(&date, &time)?) | 184 | Ok(creation.dated(&date, &time)?) |
| ... | @@ -228,7 +228,7 @@ impl State { | ... | @@ -228,7 +228,7 @@ impl State { |
| 228 | session.section.replica().apply(&self.author, edit)?; | 228 | session.section.replica().apply(&self.author, edit)?; |
| 229 | #[cfg(target_arch = "wasm32")] | 229 | #[cfg(target_arch = "wasm32")] |
| 230 | session.section.apply(&self.author, edit)?; | 230 | session.section.apply(&self.author, edit)?; |
| 231 | let shown = session.space; | 231 | let shown = self.pane.showing.space; |
| 232 | self.made(Change::Delete { | 232 | self.made(Change::Delete { |
| 233 | pages: vec![space], | 233 | pages: vec![space], |
| 234 | show: Some(shown), | 234 | show: Some(shown), |
| ... | @@ -236,7 +236,8 @@ impl State { | ... | @@ -236,7 +236,8 @@ impl State { |
| 236 | }); | 236 | }); |
| 237 | let session = self.session.as_ref().ok_or("No section is open")?; | 237 | let session = self.session.as_ref().ok_or("No section is open")?; |
| 238 | let page = session.section.page(space)?; | 238 | let page = session.section.page(space)?; |
| 239 | let (scene, editor) = canvas::gpu::page::PageScene::from_page(page, &mut self.view.engine)?; | 239 | let (scene, editor) = |
| 240 | canvas::gpu::page::PageScene::from_page(page, &mut self.pane.view.engine)?; | ||
| 240 | self.stop_loading(); | 241 | self.stop_loading(); |
| 241 | let now = crate::Instant::now(); | 242 | let now = crate::Instant::now(); |
| 242 | self.show_page(crate::Opening { | 243 | self.show_page(crate::Opening { |
| ... | @@ -248,14 +249,15 @@ impl State { | ... | @@ -248,14 +249,15 @@ impl State { |
| 248 | })?; | 249 | })?; |
| 249 | self.ui.set_focus(Some(crate::page())); | 250 | self.ui.set_focus(Some(crate::page())); |
| 250 | if let Some(title) = self | 251 | if let Some(title) = self |
| 252 | .pane | ||
| 251 | .view | 253 | .view |
| 252 | .editor | 254 | .editor |
| 253 | .outlines() | 255 | .outlines() |
| 254 | .iter() | 256 | .iter() |
| 255 | .find(|outline| outline.title) | 257 | .find(|outline| outline.title) |
| 256 | { | 258 | { |
| 257 | self.view.editor.focus_outline(title.id)?; | 259 | self.pane.view.editor.focus_outline(title.id)?; |
| 258 | let response = self.view.focus_text()?; | 260 | let response = self.pane.view.focus_text()?; |
| 259 | self.respond(response); | 261 | self.respond(response); |
| 260 | } | 262 | } |
| 261 | Ok(()) | 263 | Ok(()) |
| ... | @@ -267,7 +269,7 @@ impl State { | ... | @@ -267,7 +269,7 @@ impl State { |
| 267 | self.with_art(Some(template), move |state, art| { | 269 | self.with_art(Some(template), move |state, art| { |
| 268 | state.persist()?; | 270 | state.persist()?; |
| 269 | let session = state.session.as_ref().ok_or("No section is open")?; | 271 | let session = state.session.as_ref().ok_or("No section is open")?; |
| 270 | let ops = template_ops(&session.section.page(session.space)?, name, art)?; | 272 | let ops = template_ops(&session.section.page(state.pane.showing.space)?, name, art)?; |
| 271 | state.edit_page(ops) | 273 | state.edit_page(ops) |
| 272 | }) | 274 | }) |
| 273 | } | 275 | } |
| ... | @@ -283,16 +285,13 @@ impl State { | ... | @@ -283,16 +285,13 @@ impl State { |
| 283 | let Some(template) = template else { | 285 | let Some(template) = template else { |
| 284 | return then(self, Vec::new()); | 286 | return then(self, Vec::new()); |
| 285 | }; | 287 | }; |
| 286 | let space = self.session.as_ref().ok_or("No section is open")?.space; | 288 | self.session.as_ref().ok_or("No section is open")?; |
| 289 | let space = self.pane.showing.space; | ||
| 287 | let proxy = self.proxy.clone(); | 290 | let proxy = self.proxy.clone(); |
| 288 | crate::spawn(move || { | 291 | crate::spawn(move || { |
| 289 | let art = template.pictures().map_err(|error| error.to_string()); | 292 | let art = template.pictures().map_err(|error| error.to_string()); |
| 290 | let _ = proxy.send_event(crate::UserEvent::Then(Box::new(move |state| { | 293 | let _ = proxy.send_event(crate::UserEvent::Then(Box::new(move |state| { |
| 291 | if state | 294 | if state.session.is_some() && state.pane.showing.space == space { |
| 292 | .session | ||
| 293 | .as_ref() | ||
| 294 | .is_some_and(|open| open.space == space) | ||
| 295 | { | ||
| 296 | then(state, art?) | 295 | then(state, art?) |
| 297 | } else { | 296 | } else { |
| 298 | Ok(()) | 297 | Ok(()) |
| ... | @@ -310,7 +309,7 @@ impl State { | ... | @@ -310,7 +309,7 @@ impl State { |
| 310 | rule_lines: Option<onestore::page::RuleLines>, | 309 | rule_lines: Option<onestore::page::RuleLines>, |
| 311 | art: Option<Vec<Image>>, | 310 | art: Option<Vec<Image>>, |
| 312 | ) -> Result<(), Box<dyn Error>> { | 311 | ) -> Result<(), Box<dyn Error>> { |
| 313 | let response = self.view.set_paper(color, rule_lines, art)?; | 312 | let response = self.pane.view.set_paper(color, rule_lines, art)?; |
| 314 | self.respond(response); | 313 | self.respond(response); |
| 315 | Ok(()) | 314 | Ok(()) |
| 316 | } | 315 | } |
| ... | @@ -321,7 +320,7 @@ impl State { | ... | @@ -321,7 +320,7 @@ impl State { |
| 321 | return Ok(()); | 320 | return Ok(()); |
| 322 | } | 321 | } |
| 323 | let session = self.session.as_ref().ok_or("No section is open")?; | 322 | let session = self.session.as_ref().ok_or("No section is open")?; |
| 324 | let space = session.space; | 323 | let space = self.pane.showing.space; |
| 325 | session.section.apply( | 324 | session.section.apply( |
| 326 | &self.author, | 325 | &self.author, |
| 327 | Edit { | 326 | Edit { |
| ... | @@ -469,6 +468,7 @@ impl State { | ... | @@ -469,6 +468,7 @@ impl State { |
| 469 | pub(crate) fn show_arrived(&mut self, library: &Arc<Library>) { | 468 | pub(crate) fn show_arrived(&mut self, library: &Arc<Library>) { |
| 470 | if self.session.is_some() | 469 | if self.session.is_some() |
| 471 | || self | 470 | || self |
| 471 | .pane | ||
| 472 | .sectionless | 472 | .sectionless |
| 473 | .as_ref() | 473 | .as_ref() |
| 474 | .is_some_and(|shown| shown.location != library.location) | 474 | .is_some_and(|shown| shown.location != library.location) |
| ... | @@ -476,7 +476,7 @@ impl State { | ... | @@ -476,7 +476,7 @@ impl State { |
| 476 | return; | 476 | return; |
| 477 | } | 477 | } |
| 478 | let first = library.first_section(); | 478 | let first = library.first_section(); |
| 479 | self.sectionless = Some(Arc::clone(library)); | 479 | self.pane.sectionless = Some(Arc::clone(library)); |
| 480 | self.title(); | 480 | self.title(); |
| 481 | if let Some(path) = first { | 481 | if let Some(path) = first { |
| 482 | self.commands | 482 | self.commands |
| ... | @@ -572,8 +572,8 @@ impl State { | ... | @@ -572,8 +572,8 @@ impl State { |
| 572 | .first_section() | 572 | .first_section() |
| 573 | .ok_or("The new notebook has no section")?; | 573 | .ok_or("The new notebook has no section")?; |
| 574 | let section = library.open_async(&path, notify).await?; | 574 | let section = library.open_async(&path, notify).await?; |
| 575 | let (session, page) = read_session(section, library, path, None)?; | 575 | let (session, space, page) = read_session(section, library, path, None)?; |
| 576 | Ok(Loaded::Section(Box::new(session), page)) | 576 | Ok(Loaded::Section(Box::new(session), space, page)) |
| 577 | }); | 577 | }); |
| 578 | Ok(()) | 578 | Ok(()) |
| 579 | } | 579 | } |
| ... | @@ -623,7 +623,7 @@ impl State { | ... | @@ -623,7 +623,7 @@ impl State { |
| 623 | background.discard(); | 623 | background.discard(); |
| 624 | } | 624 | } |
| 625 | let shown = |shown: &Arc<Library>| shown.location == library.location; | 625 | let shown = |shown: &Arc<Library>| shown.location == library.location; |
| 626 | if self.sectionless.as_ref().is_some_and(shown) | 626 | if self.pane.sectionless.as_ref().is_some_and(shown) |
| 627 | || self | 627 | || self |
| 628 | .session | 628 | .session |
| 629 | .as_ref() | 629 | .as_ref() |
| ... | @@ -631,7 +631,7 @@ impl State { | ... | @@ -631,7 +631,7 @@ impl State { |
| 631 | { | 631 | { |
| 632 | self.persist().unwrap_or_else(|error| eprintln!("{error}")); | 632 | self.persist().unwrap_or_else(|error| eprintln!("{error}")); |
| 633 | self.session = None; | 633 | self.session = None; |
| 634 | self.sectionless = None; | 634 | self.pane.sectionless = None; |
| 635 | match self | 635 | match self |
| 636 | .notebooks | 636 | .notebooks |
| 637 | .iter() | 637 | .iter() |
| ... | @@ -639,7 +639,7 @@ impl State { | ... | @@ -639,7 +639,7 @@ impl State { |
| 639 | { | 639 | { |
| 640 | Some((open, path)) => self.commands.push(Command::OpenSection(open, path)), | 640 | Some((open, path)) => self.commands.push(Command::OpenSection(open, path)), |
| 641 | None => { | 641 | None => { |
| 642 | self.sectionless = self.notebooks.first().cloned(); | 642 | self.pane.sectionless = self.notebooks.first().cloned(); |
| 643 | self.title(); | 643 | self.title(); |
| 644 | } | 644 | } |
| 645 | } | 645 | } |
| ... | @@ -695,15 +695,16 @@ impl State { | ... | @@ -695,15 +695,16 @@ impl State { |
| 695 | (self.session).take_if(|session| Arc::ptr_eq(&session.library, &library)) | 695 | (self.session).take_if(|session| Arc::ptr_eq(&session.library, &library)) |
| 696 | { | 696 | { |
| 697 | // Its replica goes with it, so nothing may hold it. | 697 | // Its replica goes with it, so nothing may hold it. |
| 698 | let _ = self.view.editor.take_ops(); | 698 | let _ = self.pane.view.editor.take_ops(); |
| 699 | if let Err(error) = session.section.close() { | 699 | if let Err(error) = session.section.close() { |
| 700 | eprintln!("{location}: {error}"); | 700 | eprintln!("{location}: {error}"); |
| 701 | } | 701 | } |
| 702 | self.sectionless = Some(Arc::clone(&library)); | 702 | self.pane.sectionless = Some(Arc::clone(&library)); |
| 703 | } | 703 | } |
| 704 | self.trashing.insert(location.clone()); | 704 | self.trashing.insert(location.clone()); |
| 705 | self.close_notebook(&library); | 705 | self.close_notebook(&library); |
| 706 | self.trail.forget(&location); | 706 | self.pane.trail.forget(&location); |
| 707 | self.recent.retain(|place| place.notebook != location); | ||
| 707 | self.reads.forget(&location); | 708 | self.reads.forget(&location); |
| 708 | let ours = crate::library::key(&location, ""); | 709 | let ours = crate::library::key(&location, ""); |
| 709 | self.folded.retain(|key| !key.starts_with(&ours)); | 710 | self.folded.retain(|key| !key.starts_with(&ours)); |
| ... | @@ -754,13 +755,13 @@ impl State { | ... | @@ -754,13 +755,13 @@ impl State { |
| 754 | .as_ref() | 755 | .as_ref() |
| 755 | .and_then(|_| self.session.take()) | 756 | .and_then(|_| self.session.take()) |
| 756 | .map(|session| session.section); | 757 | .map(|session| session.section); |
| 757 | let showing = shown.is_some() || self.sectionless.as_ref().is_some_and(ours); | 758 | let showing = shown.is_some() || self.pane.sectionless.as_ref().is_some_and(ours); |
| 758 | if showing { | 759 | if showing { |
| 759 | self.sectionless = Some(Arc::clone(&library)); | 760 | self.pane.sectionless = Some(Arc::clone(&library)); |
| 760 | self.title(); | 761 | self.title(); |
| 761 | } | 762 | } |
| 762 | self.folder_rename = Some((library.location.clone(), to.clone())); | 763 | self.folder_rename = Some((library.location.clone(), to.clone())); |
| 763 | let (cache, proxy, requested) = (self.cache.clone(), self.proxy.clone(), self.loading); | 764 | let (cache, proxy, requested) = (self.cache.clone(), self.proxy.clone(), self.pane.loading); |
| 764 | crate::spawn_async(move || async move { | 765 | crate::spawn_async(move || async move { |
| 765 | let structure = Arc::clone(&library.structure); | 766 | let structure = Arc::clone(&library.structure); |
| 766 | let _structure = structure.lock().await; | 767 | let _structure = structure.lock().await; |
| ... | @@ -805,7 +806,7 @@ impl State { | ... | @@ -805,7 +806,7 @@ impl State { |
| 805 | state.folder_rename = None; | 806 | state.folder_rename = None; |
| 806 | } | 807 | } |
| 807 | let restore = showing | 808 | let restore = showing |
| 808 | && requested == state.loading | 809 | && requested == state.pane.loading |
| 809 | && state.notebook().is_some_and(|current| { | 810 | && state.notebook().is_some_and(|current| { |
| 810 | current.location == library.location | 811 | current.location == library.location |
| 811 | && Arc::ptr_eq(&current.structure, &library.structure) | 812 | && Arc::ptr_eq(&current.structure, &library.structure) |
| ... | @@ -839,7 +840,14 @@ impl State { | ... | @@ -839,7 +840,14 @@ impl State { |
| 839 | .retain(|open| open.location != to || !std::mem::replace(&mut listed, true)); | 840 | .retain(|open| open.location != to || !std::mem::replace(&mut listed, true)); |
| 840 | if from != to { | 841 | if from != to { |
| 841 | self.undo.close(&from); | 842 | self.undo.close(&from); |
| 842 | self.trail.moved(&from, &to); | 843 | self.pane.trail.moved(&from, &to); |
| 844 | for place in self | ||
| 845 | .recent | ||
| 846 | .iter_mut() | ||
| 847 | .filter(|place| place.notebook == from) | ||
| 848 | { | ||
| 849 | place.notebook.clone_from(&to); | ||
| 850 | } | ||
| 843 | self.reads.moved(&from, &to); | 851 | self.reads.moved(&from, &to); |
| 844 | let (from, to) = (crate::library::key(&from, ""), crate::library::key(&to, "")); | 852 | let (from, to) = (crate::library::key(&from, ""), crate::library::key(&to, "")); |
| 845 | let rekey = |key: &String| match key.strip_prefix(&from) { | 853 | let rekey = |key: &String| match key.strip_prefix(&from) { |
| ... | @@ -852,7 +860,7 @@ impl State { | ... | @@ -852,7 +860,7 @@ impl State { |
| 852 | .collect(); | 860 | .collect(); |
| 853 | } | 861 | } |
| 854 | if let Some(shown) = shown { | 862 | if let Some(shown) = shown { |
| 855 | self.sectionless = Some(Arc::clone(&reopened)); | 863 | self.pane.sectionless = Some(Arc::clone(&reopened)); |
| 856 | self.title(); | 864 | self.title(); |
| 857 | if let Some(path) = shown | 865 | if let Some(path) = shown |
| 858 | .filter(|path| reopened.contains(path)) | 866 | .filter(|path| reopened.contains(path)) |
| ... | @@ -885,8 +893,8 @@ impl State { | ... | @@ -885,8 +893,8 @@ impl State { |
| 885 | .filter(|session| Arc::ptr_eq(&session.library, &library)) | 893 | .filter(|session| Arc::ptr_eq(&session.library, &library)) |
| 886 | .map(|session| session.tabs[session.tab].path.clone()); | 894 | .map(|session| session.tabs[session.tab].path.clone()); |
| 887 | let notify = notify(self.proxy.clone()); | 895 | let notify = notify(self.proxy.clone()); |
| 888 | let (proxy, requested) = (self.proxy.clone(), self.loading); | 896 | let (proxy, requested) = (self.proxy.clone(), self.pane.loading); |
| 889 | let showing = self.switching.is_none() && self.opening.is_none(); | 897 | let showing = self.pane.switching.is_none() && self.pane.opening.is_none(); |
| 890 | let structure = Arc::clone(&library.structure); | 898 | let structure = Arc::clone(&library.structure); |
| 891 | let pending = match &change { | 899 | let pending = match &change { |
| 892 | Structure::Rename { path, name } => library.entry_identity(path).map(|identity| { | 900 | Structure::Rename { path, name } => library.entry_identity(path).map(|identity| { |
| ... | @@ -991,8 +999,8 @@ impl State { | ... | @@ -991,8 +999,8 @@ impl State { |
| 991 | library.keep_key(&path, key); | 999 | library.keep_key(&path, key); |
| 992 | } | 1000 | } |
| 993 | let section = library.open_async(&path, notify).await?; | 1001 | let section = library.open_async(&path, notify).await?; |
| 994 | let (session, page) = read_session(section, library, path, None)?; | 1002 | let (session, space, page) = read_session(section, library, path, None)?; |
| 995 | return Ok(Loaded::Section(Box::new(session), page)); | 1003 | return Ok(Loaded::Section(Box::new(session), space, page)); |
| 996 | } | 1004 | } |
| 997 | Structure::Properties { name, color } => { | 1005 | Structure::Properties { name, color } => { |
| 998 | if name != library.name { | 1006 | if name != library.name { |
| ... | @@ -1018,11 +1026,11 @@ impl State { | ... | @@ -1018,11 +1026,11 @@ impl State { |
| 1018 | return Ok(Loaded::Library(library, Some(path))); | 1026 | return Ok(Loaded::Library(library, Some(path))); |
| 1019 | } | 1027 | } |
| 1020 | let section = library.open_async(&path, notify).await?; | 1028 | let section = library.open_async(&path, notify).await?; |
| 1021 | let (session, page) = read_session(section, library, path, None)?; | 1029 | let (session, space, page) = read_session(section, library, path, None)?; |
| 1022 | Ok(if fresh { | 1030 | Ok(if fresh { |
| 1023 | Loaded::Created(Box::new(session), page) | 1031 | Loaded::Created(Box::new(session), space, page) |
| 1024 | } else { | 1032 | } else { |
| 1025 | Loaded::Section(Box::new(session), page) | 1033 | Loaded::Section(Box::new(session), space, page) |
| 1026 | }) | 1034 | }) |
| 1027 | }; | 1035 | }; |
| 1028 | crate::spawn_async(move || async move { | 1036 | crate::spawn_async(move || async move { |
| ... | @@ -1040,7 +1048,7 @@ impl State { | ... | @@ -1040,7 +1048,7 @@ impl State { |
| 1040 | }; | 1048 | }; |
| 1041 | let library = match &loaded { | 1049 | let library = match &loaded { |
| 1042 | Loaded::Library(library, _) => Arc::clone(library), | 1050 | Loaded::Library(library, _) => Arc::clone(library), |
| 1043 | Loaded::Section(session, _) | Loaded::Created(session, _) => { | 1051 | Loaded::Section(session, ..) | Loaded::Created(session, ..) => { |
| 1044 | Arc::clone(&session.library) | 1052 | Arc::clone(&session.library) |
| 1045 | } | 1053 | } |
| 1046 | _ => unreachable!("A structure change returns its notebook"), | 1054 | _ => unreachable!("A structure change returns its notebook"), |
| ... | @@ -1071,7 +1079,7 @@ impl State { | ... | @@ -1071,7 +1079,7 @@ impl State { |
| 1071 | }); | 1079 | }); |
| 1072 | if let Some(path) = path { | 1080 | if let Some(path) = path { |
| 1073 | state.adopt(library, Some(&path))?; | 1081 | state.adopt(library, Some(&path))?; |
| 1074 | } else if requested != state.loading || !showing { | 1082 | } else if requested != state.pane.loading || !showing { |
| 1075 | if let Some(path) = library.first_section() { | 1083 | if let Some(path) = library.first_section() { |
| 1076 | state.commands.push(Command::OpenSection(library, path)); | 1084 | state.commands.push(Command::OpenSection(library, path)); |
| 1077 | } else { | 1085 | } else { |
| ... | @@ -1079,13 +1087,14 @@ impl State { | ... | @@ -1079,13 +1087,14 @@ impl State { |
| 1079 | } | 1087 | } |
| 1080 | } | 1088 | } |
| 1081 | } else if state | 1089 | } else if state |
| 1090 | .pane | ||
| 1082 | .sectionless | 1091 | .sectionless |
| 1083 | .as_ref() | 1092 | .as_ref() |
| 1084 | .is_some_and(|shown| shown.location == library.location) | 1093 | .is_some_and(|shown| shown.location == library.location) |
| 1085 | { | 1094 | { |
| 1086 | state.sectionless = Some(library); | 1095 | state.pane.sectionless = Some(library); |
| 1087 | } | 1096 | } |
| 1088 | if showing && requested == state.loading { | 1097 | if showing && requested == state.pane.loading { |
| 1089 | match loaded { | 1098 | match loaded { |
| 1090 | Loaded::Library(library, path) => state.adopt(library, path.as_deref())?, | 1099 | Loaded::Library(library, path) => state.adopt(library, path.as_deref())?, |
| 1091 | loaded => state.load(move || Ok(loaded)), | 1100 | loaded => state.load(move || Ok(loaded)), |
crates/snowbound/src/menus.rs+2-2| ... | @@ -489,7 +489,7 @@ impl State { | ... | @@ -489,7 +489,7 @@ impl State { |
| 489 | let above = at | 489 | let above = at |
| 490 | .and_then(|at| at.checked_sub(1)) | 490 | .and_then(|at| at.checked_sub(1)) |
| 491 | .map_or(0, |above| pages[above].2); | 491 | .map_or(0, |above| pages[above].2); |
| 492 | let shown = session.shown_history == Some(*space); | 492 | let shown = self.pane.showing.shown_history == Some(*space); |
| 493 | let versions = if shown { | 493 | let versions = if shown { |
| 494 | "Hide Page Versions" | 494 | "Hide Page Versions" |
| 495 | } else { | 495 | } else { |
| ... | @@ -1039,7 +1039,7 @@ impl State { | ... | @@ -1039,7 +1039,7 @@ impl State { |
| 1039 | new(&action, String::new()).map(|structure| Command::Structure(library, structure)) | 1039 | new(&action, String::new()).map(|structure| Command::Structure(library, structure)) |
| 1040 | } | 1040 | } |
| 1041 | (Target::Closed(location), Action::Delete) => { | 1041 | (Target::Closed(location), Action::Delete) => { |
| 1042 | self.trail.recent.retain(|place| place.notebook != location); | 1042 | self.recent.retain(|place| place.notebook != location); |
| 1043 | self.save_settings(); | 1043 | self.save_settings(); |
| 1044 | None | 1044 | None |
| 1045 | } | 1045 | } |
crates/snowbound/src/navigation.rs+46-50| ... | @@ -39,19 +39,17 @@ impl Visit { | ... | @@ -39,19 +39,17 @@ impl Visit { |
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | /// Pages visited before and after the one shown, and those shown lately. | 42 | /// Pages visited before and after the one shown. |
| 43 | #[derive(Default)] | 43 | #[derive(Default)] |
| 44 | pub struct Trail { | 44 | pub struct Trail { |
| 45 | back: Vec<Visit>, | 45 | back: Vec<Visit>, |
| 46 | here: Option<Visit>, | 46 | here: Option<Visit>, |
| 47 | forward: Vec<Visit>, | 47 | forward: Vec<Visit>, |
| 48 | /// Each page shown lately once, latest first, kept between launches. | ||
| 49 | pub recent: Vec<Place>, | ||
| 50 | } | 48 | } |
| 51 | 49 | ||
| 52 | /// Places Back keeps, at most. | 50 | /// Places Back keeps, at most. |
| 53 | const KEPT: usize = 100; | 51 | const KEPT: usize = 100; |
| 54 | /// Pages `Trail::recent` keeps, at most. | 52 | /// Pages `State::recent` keeps, at most. |
| 55 | const RECENT: usize = 8; | 53 | const RECENT: usize = 8; |
| 56 | 54 | ||
| 57 | impl Trail { | 55 | impl Trail { |
| ... | @@ -61,8 +59,7 @@ impl Trail { | ... | @@ -61,8 +59,7 @@ impl Trail { |
| 61 | .chain(&mut self.here) | 59 | .chain(&mut self.here) |
| 62 | .chain(&mut self.forward) | 60 | .chain(&mut self.forward) |
| 63 | .map(|visit| &mut visit.notebook); | 61 | .map(|visit| &mut visit.notebook); |
| 64 | let places = self.recent.iter_mut().map(|place| &mut place.notebook); | 62 | for notebook in visits.filter(|notebook| *notebook == from) { |
| 65 | for notebook in visits.chain(places).filter(|notebook| *notebook == from) { | ||
| 66 | *notebook = to.to_owned(); | 63 | *notebook = to.to_owned(); |
| 67 | } | 64 | } |
| 68 | } | 65 | } |
| ... | @@ -73,14 +70,6 @@ impl Trail { | ... | @@ -73,14 +70,6 @@ impl Trail { |
| 73 | self.back.retain(elsewhere); | 70 | self.back.retain(elsewhere); |
| 74 | self.forward.retain(elsewhere); | 71 | self.forward.retain(elsewhere); |
| 75 | self.here = self.here.take().filter(elsewhere); | 72 | self.here = self.here.take().filter(elsewhere); |
| 76 | self.recent.retain(|place| place.notebook != location); | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Notes `place` shown, as the recent pages list it. | ||
| 80 | pub fn remember(&mut self, place: Place) { | ||
| 81 | self.recent.retain(|recent| *recent != place); | ||
| 82 | self.recent.insert(0, place); | ||
| 83 | self.recent.truncate(RECENT); | ||
| 84 | } | 73 | } |
| 85 | 74 | ||
| 86 | /// Notes `visit` shown. Arriving anywhere but where Back or Forward went drops the pages | 75 | /// Notes `visit` shown. Arriving anywhere but where Back or Forward went drops the pages |
| ... | @@ -122,23 +111,30 @@ impl Trail { | ... | @@ -122,23 +111,30 @@ impl Trail { |
| 122 | pub fn open(&self, exists: impl Fn(&Visit) -> bool) -> [bool; 2] { | 111 | pub fn open(&self, exists: impl Fn(&Visit) -> bool) -> [bool; 2] { |
| 123 | [&self.back, &self.forward].map(|side| side.iter().rev().any(&exists)) | 112 | [&self.back, &self.forward].map(|side| side.iter().rev().any(&exists)) |
| 124 | } | 113 | } |
| 114 | } | ||
| 125 | 115 | ||
| 126 | /// Drops the recent pages whose section `listed` refuses, and those whose section `index` | 116 | /// Notes `place` shown, as the recent pages list it. |
| 127 | /// has read without them. Returns whether any went. | 117 | pub fn remember(recent: &mut Vec<Place>, place: Place) { |
| 128 | pub fn prune(&mut self, index: &Index, listed: impl Fn(&Place) -> bool) -> bool { | 118 | recent.retain(|known| *known != place); |
| 129 | let read: HashSet<&str> = (index.entries().iter()) | 119 | recent.insert(0, place); |
| 130 | .map(|entry| entry.section.as_str()) | 120 | recent.truncate(RECENT); |
| 131 | .collect(); | 121 | } |
| 132 | let before = self.recent.len(); | 122 | |
| 133 | self.recent.retain(|place| { | 123 | /// Drops the recent pages whose section `listed` refuses, and those whose section `index` |
| 134 | let key = crate::library::key(&place.notebook, &place.section); | 124 | /// has read without them. Returns whether any went. |
| 135 | listed(place) | 125 | pub fn prune(recent: &mut Vec<Place>, index: &Index, listed: impl Fn(&Place) -> bool) -> bool { |
| 136 | && (!read.contains(key.as_str()) | 126 | let read: HashSet<&str> = (index.entries().iter()) |
| 137 | || (index.entries().iter()) | 127 | .map(|entry| entry.section.as_str()) |
| 138 | .any(|entry| entry.space == place.page && entry.section == key)) | 128 | .collect(); |
| 139 | }); | 129 | let before = recent.len(); |
| 140 | self.recent.len() != before | 130 | recent.retain(|place| { |
| 141 | } | 131 | let key = crate::library::key(&place.notebook, &place.section); |
| 132 | listed(place) | ||
| 133 | && (!read.contains(key.as_str()) | ||
| 134 | || (index.entries().iter()) | ||
| 135 | .any(|entry| entry.space == place.page && entry.section == key)) | ||
| 136 | }); | ||
| 137 | recent.len() != before | ||
| 142 | } | 138 | } |
| 143 | 139 | ||
| 144 | /// Where `visit`'s page is in `notebooks`, as `index` has read them and `open` tells whether | 140 | /// Where `visit`'s page is in `notebooks`, as `index` has read them and `open` tells whether |
| ... | @@ -189,19 +185,19 @@ impl State { | ... | @@ -189,19 +185,19 @@ impl State { |
| 189 | let place = Place { | 185 | let place = Place { |
| 190 | notebook: session.library.location.clone(), | 186 | notebook: session.library.location.clone(), |
| 191 | section: path.clone(), | 187 | section: path.clone(), |
| 192 | page: session.space, | 188 | page: self.pane.showing.space, |
| 193 | }; | 189 | }; |
| 194 | if let Ok(section) = session.section.identity() { | 190 | if let Ok(section) = session.section.identity() { |
| 195 | self.trail.visit(Visit { | 191 | self.pane.trail.visit(Visit { |
| 196 | notebook: place.notebook.clone(), | 192 | notebook: place.notebook.clone(), |
| 197 | section, | 193 | section, |
| 198 | space: session.space, | 194 | space: self.pane.showing.space, |
| 199 | page: self.view.editor.identity(), | 195 | page: self.pane.view.editor.identity(), |
| 200 | binned: crate::recycle::binned(path), | 196 | binned: crate::recycle::binned(path), |
| 201 | }); | 197 | }); |
| 202 | } | 198 | } |
| 203 | let moved = self.trail.recent.first() != Some(&place); | 199 | let moved = self.recent.first() != Some(&place); |
| 204 | self.trail.remember(place); | 200 | remember(&mut self.recent, place); |
| 205 | if moved { | 201 | if moved { |
| 206 | self.save_settings(); | 202 | self.save_settings(); |
| 207 | } | 203 | } |
| ... | @@ -222,7 +218,7 @@ impl State { | ... | @@ -222,7 +218,7 @@ impl State { |
| 222 | 218 | ||
| 223 | /// Whether Back and Forward have a page to go to. | 219 | /// Whether Back and Forward have a page to go to. |
| 224 | pub(crate) fn can_travel(&self) -> [bool; 2] { | 220 | pub(crate) fn can_travel(&self) -> [bool; 2] { |
| 225 | self.trail.open(|visit| self.find(visit).is_some()) | 221 | self.pane.trail.open(|visit| self.find(visit).is_some()) |
| 226 | } | 222 | } |
| 227 | 223 | ||
| 228 | /// Whether `library`'s section at `path` is the one open. | 224 | /// Whether `library`'s section at `path` is the one open. |
| ... | @@ -246,9 +242,9 @@ impl State { | ... | @@ -246,9 +242,9 @@ impl State { |
| 246 | /// Shows the page visited before the one shown, or after it going `forward`, wherever it | 242 | /// Shows the page visited before the one shown, or after it going `forward`, wherever it |
| 247 | /// went since. Pages since deleted, and those of notebooks since closed, are passed over. | 243 | /// went since. Pages since deleted, and those of notebooks since closed, are passed over. |
| 248 | pub(crate) fn travel(&mut self, forward: bool) { | 244 | pub(crate) fn travel(&mut self, forward: bool) { |
| 249 | let mut trail = std::mem::take(&mut self.trail); | 245 | let mut trail = std::mem::take(&mut self.pane.trail); |
| 250 | let visit = trail.step(forward, |visit| self.find(visit).is_some()); | 246 | let visit = trail.step(forward, |visit| self.find(visit).is_some()); |
| 251 | self.trail = trail; | 247 | self.pane.trail = trail; |
| 252 | if let Some((library, path, space)) = visit.and_then(|visit| self.find(&visit)) { | 248 | if let Some((library, path, space)) = visit.and_then(|visit| self.find(&visit)) { |
| 253 | self.go(library, path, space); | 249 | self.go(library, path, space); |
| 254 | } | 250 | } |
| ... | @@ -418,16 +414,16 @@ mod tests { | ... | @@ -418,16 +414,16 @@ mod tests { |
| 418 | 414 | ||
| 419 | #[test] | 415 | #[test] |
| 420 | fn recent_pages_are_kept_once_latest_first_and_bounded() { | 416 | fn recent_pages_are_kept_once_latest_first_and_bounded() { |
| 421 | let mut trail = Trail::default(); | 417 | let mut recent = Vec::new(); |
| 422 | for page in [1, 2, 1] { | 418 | for page in [1, 2, 1] { |
| 423 | trail.remember(place("A.one", page)); | 419 | remember(&mut recent, place("A.one", page)); |
| 424 | } | 420 | } |
| 425 | assert_eq!(trail.recent, [place("A.one", 1), place("A.one", 2)]); | 421 | assert_eq!(recent, [place("A.one", 1), place("A.one", 2)]); |
| 426 | for page in 0..20 { | 422 | for page in 0..20 { |
| 427 | trail.remember(place("B.one", page)); | 423 | remember(&mut recent, place("B.one", page)); |
| 428 | } | 424 | } |
| 429 | assert_eq!(trail.recent.len(), RECENT); | 425 | assert_eq!(recent.len(), RECENT); |
| 430 | assert_eq!(trail.recent[0], place("B.one", 19)); | 426 | assert_eq!(recent[0], place("B.one", 19)); |
| 431 | } | 427 | } |
| 432 | 428 | ||
| 433 | #[test] | 429 | #[test] |
| ... | @@ -452,7 +448,7 @@ mod tests { | ... | @@ -452,7 +448,7 @@ mod tests { |
| 452 | &page("Kept"), | 448 | &page("Kept"), |
| 453 | 0, | 449 | 0, |
| 454 | )); | 450 | )); |
| 455 | let mut trail = Trail::default(); | 451 | let mut recent = Vec::new(); |
| 456 | // Visited oldest first: the index has read A without page 2, not yet B, and C is gone. | 452 | // Visited oldest first: the index has read A without page 2, not yet B, and C is gone. |
| 457 | for gone in [ | 453 | for gone in [ |
| 458 | place("C.one", 1), | 454 | place("C.one", 1), |
| ... | @@ -460,12 +456,12 @@ mod tests { | ... | @@ -460,12 +456,12 @@ mod tests { |
| 460 | place("A.one", 2), | 456 | place("A.one", 2), |
| 461 | kept.clone(), | 457 | kept.clone(), |
| 462 | ] { | 458 | ] { |
| 463 | trail.remember(gone); | 459 | remember(&mut recent, gone); |
| 464 | } | 460 | } |
| 465 | let listed = |place: &Place| place.section != "C.one"; | 461 | let listed = |place: &Place| place.section != "C.one"; |
| 466 | assert!(trail.prune(&index, listed)); | 462 | assert!(prune(&mut recent, &index, listed)); |
| 467 | assert_eq!(trail.recent, [kept, place("B.one", 1)]); | 463 | assert_eq!(recent, [kept, place("B.one", 1)]); |
| 468 | assert!(!trail.prune(&index, listed), "nothing more goes"); | 464 | assert!(!prune(&mut recent, &index, listed), "nothing more goes"); |
| 469 | } | 465 | } |
| 470 | 466 | ||
| 471 | #[test] | 467 | #[test] |
crates/snowbound/src/options.rs+4-4| ... | @@ -390,10 +390,10 @@ impl State { | ... | @@ -390,10 +390,10 @@ impl State { |
| 390 | color_scheme: self.color_scheme, | 390 | color_scheme: self.color_scheme, |
| 391 | pages_match: !self.light_pages, | 391 | pages_match: !self.light_pages, |
| 392 | hide_spelling: self.hide_spelling, | 392 | hide_spelling: self.hide_spelling, |
| 393 | markdown: self.view.editor.markdown.is_some(), | 393 | markdown: self.pane.view.editor.markdown.is_some(), |
| 394 | automatic_updates: self.updates.automatic(), | 394 | automatic_updates: self.updates.automatic(), |
| 395 | pen_pressure: self.pen_pressure, | 395 | pen_pressure: self.pen_pressure, |
| 396 | default_font: self.view.editor.default_font.clone(), | 396 | default_font: self.pane.view.editor.default_font.clone(), |
| 397 | page_tabs_left: self.page_tabs_left, | 397 | page_tabs_left: self.page_tabs_left, |
| 398 | navigation_bar_left: !self.navigation_bar_right, | 398 | navigation_bar_left: !self.navigation_bar_right, |
| 399 | lock_after: self.passwords.lock_after, | 399 | lock_after: self.passwords.lock_after, |
| ... | @@ -692,9 +692,9 @@ impl State { | ... | @@ -692,9 +692,9 @@ impl State { |
| 692 | self.color_scheme = options.color_scheme; | 692 | self.color_scheme = options.color_scheme; |
| 693 | self.light_pages = !options.pages_match; | 693 | self.light_pages = !options.pages_match; |
| 694 | self.hide_spelling = options.hide_spelling; | 694 | self.hide_spelling = options.hide_spelling; |
| 695 | self.view.editor.markdown = crate::commands::markdown(options.markdown); | 695 | self.pane.view.editor.markdown = crate::commands::markdown(options.markdown); |
| 696 | self.pen_pressure = options.pen_pressure; | 696 | self.pen_pressure = options.pen_pressure; |
| 697 | self.view.editor.default_font = options.default_font; | 697 | self.pane.view.editor.default_font = options.default_font; |
| 698 | self.page_tabs_left = options.page_tabs_left; | 698 | self.page_tabs_left = options.page_tabs_left; |
| 699 | self.navigation_bar_right = !options.navigation_bar_left; | 699 | self.navigation_bar_right = !options.navigation_bar_left; |
| 700 | self.passwords = crate::settings::Passwords { | 700 | self.passwords = crate::settings::Passwords { |
crates/snowbound/src/palette.rs+5-5| ... | @@ -114,7 +114,7 @@ impl State { | ... | @@ -114,7 +114,7 @@ impl State { |
| 114 | } | 114 | } |
| 115 | } | 115 | } |
| 116 | // And what the page's context menu does at the caret, or to the file selected. | 116 | // And what the page's context menu does at the caret, or to the file selected. |
| 117 | let text = self.view.caret_context(); | 117 | let text = self.pane.view.caret_context(); |
| 118 | for (item, title) in text.iter().flat_map(|context| self.text_items(context)) { | 118 | for (item, title) in text.iter().flat_map(|context| self.text_items(context)) { |
| 119 | if !(commands::COMMANDS.iter()).any(|command| command.title == title) { | 119 | if !(commands::COMMANDS.iter()).any(|command| command.title == title) { |
| 120 | commands.push(Row { | 120 | commands.push(Row { |
| ... | @@ -334,7 +334,7 @@ impl State { | ... | @@ -334,7 +334,7 @@ impl State { |
| 334 | Target::Page { | 334 | Target::Page { |
| 335 | library: Arc::clone(library), | 335 | library: Arc::clone(library), |
| 336 | path: path.clone(), | 336 | path: path.clone(), |
| 337 | space: session.space, | 337 | space: self.pane.showing.space, |
| 338 | }, | 338 | }, |
| 339 | Target::Section { | 339 | Target::Section { |
| 340 | library: Arc::clone(library), | 340 | library: Arc::clone(library), |
| ... | @@ -367,7 +367,7 @@ impl State { | ... | @@ -367,7 +367,7 @@ impl State { |
| 367 | .map(Arc::clone) | 367 | .map(Arc::clone) |
| 368 | }; | 368 | }; |
| 369 | // A closed notebook keeps its places to open it again, an unreadable one until it reads. | 369 | // A closed notebook keeps its places to open it again, an unreadable one until it reads. |
| 370 | let pruned = self.trail.prune(&index, |place| { | 370 | let pruned = crate::navigation::prune(&mut self.recent, &index, |place| { |
| 371 | library(&place.notebook) | 371 | library(&place.notebook) |
| 372 | .is_none_or(|library| library.notebook.is_err() || library.contains(&place.section)) | 372 | .is_none_or(|library| library.notebook.is_err() || library.contains(&place.section)) |
| 373 | }); | 373 | }); |
| ... | @@ -375,7 +375,7 @@ impl State { | ... | @@ -375,7 +375,7 @@ impl State { |
| 375 | ( | 375 | ( |
| 376 | session.library.location.as_str(), | 376 | session.library.location.as_str(), |
| 377 | session.tabs[session.tab].path.as_str(), | 377 | session.tabs[session.tab].path.as_str(), |
| 378 | session.space, | 378 | self.pane.showing.space, |
| 379 | ) | 379 | ) |
| 380 | }); | 380 | }); |
| 381 | let theme = &self.ui.theme; | 381 | let theme = &self.ui.theme; |
| ... | @@ -393,7 +393,7 @@ impl State { | ... | @@ -393,7 +393,7 @@ impl State { |
| 393 | _ => false, | 393 | _ => false, |
| 394 | }) | 394 | }) |
| 395 | }; | 395 | }; |
| 396 | for place in &self.trail.recent { | 396 | for place in &self.recent { |
| 397 | let Some(library) = library(&place.notebook) else { | 397 | let Some(library) = library(&place.notebook) else { |
| 398 | let target = Target::Closed(place.notebook.clone()); | 398 | let target = Target::Closed(place.notebook.clone()); |
| 399 | if !listed(&closed, &target) { | 399 | if !listed(&closed, &target) { |
crates/snowbound/src/paste.rs+7-6| ... | @@ -124,7 +124,7 @@ impl State { | ... | @@ -124,7 +124,7 @@ impl State { |
| 124 | } | 124 | } |
| 125 | } | 125 | } |
| 126 | Some(Pasted::Clip(clip)) => { | 126 | Some(Pasted::Clip(clip)) => { |
| 127 | let response = self.view.paste_clip(clip)?; | 127 | let response = self.pane.view.paste_clip(clip)?; |
| 128 | self.respond(response); | 128 | self.respond(response); |
| 129 | } | 129 | } |
| 130 | Some(Pasted::Page(html, text)) => { | 130 | Some(Pasted::Page(html, text)) => { |
| ... | @@ -142,12 +142,12 @@ impl State { | ... | @@ -142,12 +142,12 @@ impl State { |
| 142 | }); | 142 | }); |
| 143 | if pieces.is_empty() { | 143 | if pieces.is_empty() { |
| 144 | if let Some(text) = text { | 144 | if let Some(text) = text { |
| 145 | let response = self.view.paste(&text, language)?; | 145 | let response = self.pane.view.paste(&text, language)?; |
| 146 | self.respond(response); | 146 | self.respond(response); |
| 147 | } | 147 | } |
| 148 | return Ok(()); | 148 | return Ok(()); |
| 149 | } | 149 | } |
| 150 | let (places, response) = self.view.paste_pieces(pieces)?; | 150 | let (places, response) = self.pane.view.paste_pieces(pieces)?; |
| 151 | self.respond(response); | 151 | self.respond(response); |
| 152 | for (place, (source, css)) in places.into_iter().zip(fetched) { | 152 | for (place, (source, css)) in places.into_iter().zip(fetched) { |
| 153 | let proxy = self.proxy.clone(); | 153 | let proxy = self.proxy.clone(); |
| ... | @@ -159,7 +159,7 @@ impl State { | ... | @@ -159,7 +159,7 @@ impl State { |
| 159 | return; | 159 | return; |
| 160 | }; | 160 | }; |
| 161 | let _ = proxy.send_event(crate::UserEvent::Then(Box::new(move |state| { | 161 | let _ = proxy.send_event(crate::UserEvent::Then(Box::new(move |state| { |
| 162 | let response = state.view.insert_awaited(place, bytes, size)?; | 162 | let response = state.pane.view.insert_awaited(place, bytes, size)?; |
| 163 | state.respond(response); | 163 | state.respond(response); |
| 164 | Ok(()) | 164 | Ok(()) |
| 165 | }))); | 165 | }))); |
| ... | @@ -168,7 +168,7 @@ impl State { | ... | @@ -168,7 +168,7 @@ impl State { |
| 168 | } | 168 | } |
| 169 | Some(Pasted::Text(text)) => { | 169 | Some(Pasted::Text(text)) => { |
| 170 | let language = canvas::language::lcid(&platform::input_language()); | 170 | let language = canvas::language::lcid(&platform::input_language()); |
| 171 | let response = self.view.paste(&text, language)?; | 171 | let response = self.pane.view.paste(&text, language)?; |
| 172 | self.respond(response); | 172 | self.respond(response); |
| 173 | } | 173 | } |
| 174 | Some(Pasted::Picture(bytes)) => self.insert_picture(bytes, None)?, | 174 | Some(Pasted::Picture(bytes)) => self.insert_picture(bytes, None)?, |
| ... | @@ -194,9 +194,10 @@ impl State { | ... | @@ -194,9 +194,10 @@ impl State { |
| 194 | let size = picture_size(&bytes, pixels); | 194 | let size = picture_size(&bytes, pixels); |
| 195 | let response = match at { | 195 | let response = match at { |
| 196 | Some(point) => self | 196 | Some(point) => self |
| 197 | .pane | ||
| 197 | .view | 198 | .view |
| 198 | .drop_picture(self.page_point(point), bytes, size)?, | 199 | .drop_picture(self.page_point(point), bytes, size)?, |
| 199 | None => self.view.insert_picture(bytes, size)?, | 200 | None => self.pane.view.insert_picture(bytes, size)?, |
| 200 | }; | 201 | }; |
| 201 | self.respond(response); | 202 | self.respond(response); |
| 202 | Ok(()) | 203 | Ok(()) |
crates/snowbound/src/prefetch.rs+7-7| ... | @@ -277,7 +277,7 @@ impl State { | ... | @@ -277,7 +277,7 @@ impl State { |
| 277 | /// The section tab shown open: one being opened, from the moment it is asked for. | 277 | /// The section tab shown open: one being opened, from the moment it is asked for. |
| 278 | pub fn open_tab(&self) -> Option<usize> { | 278 | pub fn open_tab(&self) -> Option<usize> { |
| 279 | let session = self.session.as_ref()?; | 279 | let session = self.session.as_ref()?; |
| 280 | let opening = self.switching.and_then(|(tab, _)| tab); | 280 | let opening = self.pane.switching.and_then(|(tab, _)| tab); |
| 281 | Some( | 281 | Some( |
| 282 | opening | 282 | opening |
| 283 | .filter(|tab| *tab < session.tabs.len()) | 283 | .filter(|tab| *tab < session.tabs.len()) |
| ... | @@ -287,15 +287,15 @@ impl State { | ... | @@ -287,15 +287,15 @@ impl State { |
| 287 | 287 | ||
| 288 | /// Since when the page area shows that a page is opening, once it has taken a while. | 288 | /// Since when the page area shows that a page is opening, once it has taken a while. |
| 289 | pub fn loading(&self) -> Option<Instant> { | 289 | pub fn loading(&self) -> Option<Instant> { |
| 290 | let (_, since) = self.switching?; | 290 | let (_, since) = self.pane.switching?; |
| 291 | (since.elapsed() >= PATIENCE).then_some(since) | 291 | (since.elapsed() >= PATIENCE).then_some(since) |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | /// Leaves the page shown as it is, dropping whatever was opening in its place. | 294 | /// Leaves the page shown as it is, dropping whatever was opening in its place. |
| 295 | pub fn stop_loading(&mut self) { | 295 | pub fn stop_loading(&mut self) { |
| 296 | self.loading += 1; | 296 | self.pane.loading += 1; |
| 297 | self.opening = None; | 297 | self.pane.opening = None; |
| 298 | self.switching = None; | 298 | self.pane.switching = None; |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | /// Readies the pages beside the open one and the sections beside the open section. | 301 | /// Readies the pages beside the open one and the sections beside the open section. |
| ... | @@ -306,7 +306,7 @@ impl State { | ... | @@ -306,7 +306,7 @@ impl State { |
| 306 | let at = session | 306 | let at = session |
| 307 | .pages | 307 | .pages |
| 308 | .iter() | 308 | .iter() |
| 309 | .position(|(space, ..)| *space == session.space); | 309 | .position(|(space, ..)| *space == self.pane.showing.space); |
| 310 | let pages: Vec<ExGuid> = at | 310 | let pages: Vec<ExGuid> = at |
| 311 | .into_iter() | 311 | .into_iter() |
| 312 | .flat_map(|at| [at + 1, at.wrapping_sub(1)]) | 312 | .flat_map(|at| [at + 1, at.wrapping_sub(1)]) |
| ... | @@ -330,7 +330,7 @@ impl State { | ... | @@ -330,7 +330,7 @@ impl State { |
| 330 | return; | 330 | return; |
| 331 | }; | 331 | }; |
| 332 | let key = session.key(); | 332 | let key = session.key(); |
| 333 | if space == session.space || !self.prefetch.ask((key.clone(), Some(space))) { | 333 | if space == self.pane.showing.space || !self.prefetch.ask((key.clone(), Some(space))) { |
| 334 | return; | 334 | return; |
| 335 | } | 335 | } |
| 336 | let read = session.reader(space); | 336 | let read = session.reader(space); |
crates/snowbound/src/print.rs+3-3| ... | @@ -281,11 +281,11 @@ impl State { | ... | @@ -281,11 +281,11 @@ impl State { |
| 281 | let session = self.session.as_ref().ok_or("No section is open")?; | 281 | let session = self.session.as_ref().ok_or("No section is open")?; |
| 282 | let tab = &session.tabs[session.tab]; | 282 | let tab = &session.tabs[session.tab]; |
| 283 | // The open page as the editor holds it; the others as stored. | 283 | // The open page as the editor holds it; the others as stored. |
| 284 | let open = (session.space, self.view.editor.page()?); | 284 | let open = (self.pane.showing.space, self.pane.view.editor.page()?); |
| 285 | let at = session | 285 | let at = session |
| 286 | .pages | 286 | .pages |
| 287 | .iter() | 287 | .iter() |
| 288 | .position(|(space, ..)| *space == session.space); | 288 | .position(|(space, ..)| *space == self.pane.showing.space); |
| 289 | let spaces: Vec<ExGuid> = match (setup.scope, at) { | 289 | let spaces: Vec<ExGuid> = match (setup.scope, at) { |
| 290 | (Scope::PageGroup, Some(at)) => { | 290 | (Scope::PageGroup, Some(at)) => { |
| 291 | let levels: Vec<u32> = session.pages.iter().map(|(.., level)| *level).collect(); | 291 | let levels: Vec<u32> = session.pages.iter().map(|(.., level)| *level).collect(); |
| ... | @@ -297,7 +297,7 @@ impl State { | ... | @@ -297,7 +297,7 @@ impl State { |
| 297 | (Scope::Section | Scope::Notebook, _) => { | 297 | (Scope::Section | Scope::Notebook, _) => { |
| 298 | session.pages.iter().map(|(space, ..)| *space).collect() | 298 | session.pages.iter().map(|(space, ..)| *space).collect() |
| 299 | } | 299 | } |
| 300 | _ => vec![session.space], | 300 | _ => vec![self.pane.showing.space], |
| 301 | }; | 301 | }; |
| 302 | let title = match setup.scope { | 302 | let title = match setup.scope { |
| 303 | Scope::Page | Scope::PageGroup => open.1.title.clone(), | 303 | Scope::Page | Scope::PageGroup => open.1.title.clone(), |
crates/snowbound/src/protection.rs+5-5| ... | @@ -89,8 +89,8 @@ impl State { | ... | @@ -89,8 +89,8 @@ impl State { |
| 89 | previous.library.keep(path, previous.section); | 89 | previous.library.keep(path, previous.section); |
| 90 | } | 90 | } |
| 91 | } | 91 | } |
| 92 | self.sectionless = None; | 92 | self.pane.sectionless = None; |
| 93 | self.locked = Some(Locked { library, path }); | 93 | self.pane.locked = Some(Locked { library, path }); |
| 94 | self.ui.set_focus(Some(notice())); | 94 | self.ui.set_focus(Some(notice())); |
| 95 | self.title(); | 95 | self.title(); |
| 96 | Ok(()) | 96 | Ok(()) |
| ... | @@ -113,7 +113,7 @@ impl State { | ... | @@ -113,7 +113,7 @@ impl State { |
| 113 | section: &ui::Section, | 113 | section: &ui::Section, |
| 114 | strip: [f32; 4], | 114 | strip: [f32; 4], |
| 115 | ) -> (Option<Command>, Id) { | 115 | ) -> (Option<Command>, Id) { |
| 116 | let Some(locked) = &self.locked else { | 116 | let Some(locked) = &self.pane.locked else { |
| 117 | return (None, row); | 117 | return (None, row); |
| 118 | }; | 118 | }; |
| 119 | let library = Arc::clone(&locked.library); | 119 | let library = Arc::clone(&locked.library); |
| ... | @@ -157,7 +157,7 @@ impl State { | ... | @@ -157,7 +157,7 @@ impl State { |
| 157 | 157 | ||
| 158 | /// The locked section's page, as OneNote 2010 draws it: a click or Enter unlocks. | 158 | /// The locked section's page, as OneNote 2010 draws it: a click or Enter unlocks. |
| 159 | pub(crate) fn locked_page(&mut self) { | 159 | pub(crate) fn locked_page(&mut self) { |
| 160 | let Some(locked) = &self.locked else { | 160 | let Some(locked) = &self.pane.locked else { |
| 161 | return; | 161 | return; |
| 162 | }; | 162 | }; |
| 163 | let (library, path) = (Arc::clone(&locked.library), locked.path.clone()); | 163 | let (library, path) = (Arc::clone(&locked.library), locked.path.clone()); |
| ... | @@ -266,7 +266,7 @@ impl State { | ... | @@ -266,7 +266,7 @@ impl State { |
| 266 | 266 | ||
| 267 | /// The section shown, open or locked, with its notebook. | 267 | /// The section shown, open or locked, with its notebook. |
| 268 | pub(crate) fn shown_section(&self) -> Option<(Arc<Library>, String)> { | 268 | pub(crate) fn shown_section(&self) -> Option<(Arc<Library>, String)> { |
| 269 | match (&self.session, &self.locked) { | 269 | match (&self.session, &self.pane.locked) { |
| 270 | (Some(session), _) if session.library.catalog().is_some() => Some(( | 270 | (Some(session), _) if session.library.catalog().is_some() => Some(( |
| 271 | Arc::clone(&session.library), | 271 | Arc::clone(&session.library), |
| 272 | session.tabs[session.tab].path.clone(), | 272 | session.tabs[session.tab].path.clone(), |
crates/snowbound/src/recording.rs+15-15| ... | @@ -93,7 +93,7 @@ impl State { | ... | @@ -93,7 +93,7 @@ impl State { |
| 93 | }; | 93 | }; |
| 94 | let [date, time] = platform::date_text(crate::filetime()); | 94 | let [date, time] = platform::date_text(crate::filetime()); |
| 95 | let label = canvas::recording::label(video, &date, &time); | 95 | let label = canvas::recording::label(video, &date, &time); |
| 96 | let (id, response) = self.view.start_recording(&label)?; | 96 | let (id, response) = self.pane.view.start_recording(&label)?; |
| 97 | self.respond(response); | 97 | self.respond(response); |
| 98 | if id.is_none() { | 98 | if id.is_none() { |
| 99 | recorder.stop()?()?; | 99 | recorder.stop()?()?; |
| ... | @@ -117,7 +117,7 @@ impl State { | ... | @@ -117,7 +117,7 @@ impl State { |
| 117 | path, | 117 | path, |
| 118 | } => { | 118 | } => { |
| 119 | // Nothing written while the file saves links to the recording. | 119 | // Nothing written while the file saves links to the recording. |
| 120 | self.view.editor.pause_recording(true); | 120 | self.pane.view.editor.pause_recording(true); |
| 121 | let finish = match recorder.stop() { | 121 | let finish = match recorder.stop() { |
| 122 | Ok(finish) => finish, | 122 | Ok(finish) => finish, |
| 123 | Err(detail) => { | 123 | Err(detail) => { |
| ... | @@ -126,8 +126,8 @@ impl State { | ... | @@ -126,8 +126,8 @@ impl State { |
| 126 | } | 126 | } |
| 127 | }; | 127 | }; |
| 128 | let recorded = path.clone(); | 128 | let recorded = path.clone(); |
| 129 | let id = self.view.editor.recording(); | 129 | let id = self.pane.view.editor.recording(); |
| 130 | let title = self.view.editor.page()?.title; | 130 | let title = self.pane.view.editor.page()?.title; |
| 131 | let job = std::thread::spawn(move || { | 131 | let job = std::thread::spawn(move || { |
| 132 | finish()?; | 132 | finish()?; |
| 133 | let recorded = | 133 | let recorded = |
| ... | @@ -150,7 +150,7 @@ impl State { | ... | @@ -150,7 +150,7 @@ impl State { |
| 150 | let saved = job | 150 | let saved = job |
| 151 | .join() | 151 | .join() |
| 152 | .unwrap_or_else(|_| Err("Try recording again.".into())); | 152 | .unwrap_or_else(|_| Err("Try recording again.".into())); |
| 153 | if self.view.editor.recording().is_none() { | 153 | if self.pane.view.editor.recording().is_none() { |
| 154 | return Ok(()); | 154 | return Ok(()); |
| 155 | } | 155 | } |
| 156 | let mut file = match saved { | 156 | let mut file = match saved { |
| ... | @@ -162,7 +162,7 @@ impl State { | ... | @@ -162,7 +162,7 @@ impl State { |
| 162 | }; | 162 | }; |
| 163 | file.preview = platform::file_icon(&path).map(Into::into); | 163 | file.preview = platform::file_icon(&path).map(Into::into); |
| 164 | notebook::fs::remove_file(&path)?; | 164 | notebook::fs::remove_file(&path)?; |
| 165 | let response = self.view.finish_recording(file)?; | 165 | let response = self.pane.view.finish_recording(file)?; |
| 166 | self.respond(response); | 166 | self.respond(response); |
| 167 | Ok(()) | 167 | Ok(()) |
| 168 | } | 168 | } |
| ... | @@ -225,7 +225,7 @@ impl State { | ... | @@ -225,7 +225,7 @@ impl State { |
| 225 | let (enabled, checked) = match transport { | 225 | let (enabled, checked) = match transport { |
| 226 | Transport::Pause => ( | 226 | Transport::Pause => ( |
| 227 | recording || playing.is_some(), | 227 | recording || playing.is_some(), |
| 228 | Some(self.view.editor.recording_paused() || playing == Some(false)), | 228 | Some(self.pane.view.editor.recording_paused() || playing == Some(false)), |
| 229 | ), | 229 | ), |
| 230 | Transport::Stop => (recording || playing.is_some(), None), | 230 | Transport::Stop => (recording || playing.is_some(), None), |
| 231 | Transport::Skip(_) | Transport::SeekTo => (playing.is_some(), None), | 231 | Transport::Skip(_) | Transport::SeekTo => (playing.is_some(), None), |
| ... | @@ -241,9 +241,9 @@ impl State { | ... | @@ -241,9 +241,9 @@ impl State { |
| 241 | (Transport::Stop, Media::Recording { .. }) => self.stop_recording(false)?, | 241 | (Transport::Stop, Media::Recording { .. }) => self.stop_recording(false)?, |
| 242 | (Transport::Stop, Media::Playing(_)) => self.media = Media::Idle, | 242 | (Transport::Stop, Media::Playing(_)) => self.media = Media::Idle, |
| 243 | (Transport::Pause, Media::Recording { recorder, .. }) => { | 243 | (Transport::Pause, Media::Recording { recorder, .. }) => { |
| 244 | let paused = !self.view.editor.recording_paused(); | 244 | let paused = !self.pane.view.editor.recording_paused(); |
| 245 | recorder.pause(paused); | 245 | recorder.pause(paused); |
| 246 | self.view.editor.pause_recording(paused); | 246 | self.pane.view.editor.pause_recording(paused); |
| 247 | } | 247 | } |
| 248 | (Transport::Pause, Media::Playing(playback)) => { | 248 | (Transport::Pause, Media::Playing(playback)) => { |
| 249 | let player = &mut playback.player; | 249 | let player = &mut playback.player; |
| ... | @@ -281,13 +281,13 @@ impl State { | ... | @@ -281,13 +281,13 @@ impl State { |
| 281 | let mut played = None; | 281 | let mut played = None; |
| 282 | let (clock_text, picture) = match &mut self.media { | 282 | let (clock_text, picture) = match &mut self.media { |
| 283 | Media::Idle => { | 283 | Media::Idle => { |
| 284 | let response = self.view.set_played(None)?; | 284 | let response = self.pane.view.set_played(None)?; |
| 285 | self.respond(response); | 285 | self.respond(response); |
| 286 | return Ok(()); | 286 | return Ok(()); |
| 287 | } | 287 | } |
| 288 | Media::Recording { video, .. } => { | 288 | Media::Recording { video, .. } => { |
| 289 | let at = self.view.editor.recording_ms().unwrap_or(0); | 289 | let at = self.pane.view.editor.recording_ms().unwrap_or(0); |
| 290 | let state = if self.view.editor.recording_paused() { | 290 | let state = if self.pane.view.editor.recording_paused() { |
| 291 | "Paused" | 291 | "Paused" |
| 292 | } else if *video { | 292 | } else if *video { |
| 293 | "Recording video" | 293 | "Recording video" |
| ... | @@ -311,8 +311,8 @@ impl State { | ... | @@ -311,8 +311,8 @@ impl State { |
| 311 | played = playback | 311 | played = playback |
| 312 | .recording | 312 | .recording |
| 313 | .filter(|_| self.see_playback) | 313 | .filter(|_| self.see_playback) |
| 314 | .and_then(|id| self.view.editor.played_note(id, at)) | 314 | .and_then(|id| self.pane.view.editor.played_note(id, at)) |
| 315 | .and_then(|note| canvas::search::paragraph_match(&self.view.editor, note)); | 315 | .and_then(|note| canvas::search::paragraph_match(&self.pane.view.editor, note)); |
| 316 | let mut picture = None; | 316 | let mut picture = None; |
| 317 | if let Some(video) = &mut playback.video { | 317 | if let Some(video) = &mut playback.video { |
| 318 | let frame = video.movie.frame(at); | 318 | let frame = video.movie.frame(at); |
| ... | @@ -344,7 +344,7 @@ impl State { | ... | @@ -344,7 +344,7 @@ impl State { |
| 344 | ) | 344 | ) |
| 345 | } | 345 | } |
| 346 | }; | 346 | }; |
| 347 | let response = self.view.set_played(played)?; | 347 | let response = self.pane.view.set_played(played)?; |
| 348 | self.respond(response); | 348 | self.respond(response); |
| 349 | let picture_height = picture | 349 | let picture_height = picture |
| 350 | .as_ref() | 350 | .as_ref() |
crates/snowbound/src/recycle.rs+5-5| ... | @@ -67,8 +67,8 @@ impl State { | ... | @@ -67,8 +67,8 @@ impl State { |
| 67 | section.close_async().await?; | 67 | section.close_async().await?; |
| 68 | continue; | 68 | continue; |
| 69 | } | 69 | } |
| 70 | let (session, page) = read_session(section, library, path, None)?; | 70 | let (session, space, page) = read_session(section, library, path, None)?; |
| 71 | return Ok(Loaded::Section(Box::new(session), page)); | 71 | return Ok(Loaded::Section(Box::new(session), space, page)); |
| 72 | } | 72 | } |
| 73 | Err("The Recycle Bin is empty.".into()) | 73 | Err("The Recycle Bin is empty.".into()) |
| 74 | }); | 74 | }); |
| ... | @@ -127,7 +127,7 @@ impl State { | ... | @@ -127,7 +127,7 @@ impl State { |
| 127 | let session = self.session.as_ref().ok_or("No section is open")?; | 127 | let session = self.session.as_ref().ok_or("No section is open")?; |
| 128 | let replica = Arc::clone(session.section.replica()); | 128 | let replica = Arc::clone(session.section.replica()); |
| 129 | let library = Arc::clone(&session.library); | 129 | let library = Arc::clone(&session.library); |
| 130 | let shown = session.space; | 130 | let shown = self.pane.showing.space; |
| 131 | let at = (session.pages.iter()).position(|(space, ..)| gone.contains(space)); | 131 | let at = (session.pages.iter()).position(|(space, ..)| gone.contains(space)); |
| 132 | let kept = |space: &ExGuid| !gone.contains(space); | 132 | let kept = |space: &ExGuid| !gone.contains(space); |
| 133 | let after = at.and_then(|at| { | 133 | let after = at.and_then(|at| { |
| ... | @@ -149,8 +149,8 @@ impl State { | ... | @@ -149,8 +149,8 @@ impl State { |
| 149 | .first_section() | 149 | .first_section() |
| 150 | .ok_or("The notebook has no sections")?; | 150 | .ok_or("The notebook has no sections")?; |
| 151 | let section = library.open_async(&path, notify(proxy)).await?; | 151 | let section = library.open_async(&path, notify(proxy)).await?; |
| 152 | let (session, page) = read_session(section, library, path, None)?; | 152 | let (session, space, page) = read_session(section, library, path, None)?; |
| 153 | Ok(Loaded::Section(Box::new(session), page)) | 153 | Ok(Loaded::Section(Box::new(session), space, page)) |
| 154 | }); | 154 | }); |
| 155 | Ok(()) | 155 | Ok(()) |
| 156 | } | 156 | } |
crates/snowbound/src/save_as.rs+6-2| ... | @@ -166,11 +166,15 @@ impl State { | ... | @@ -166,11 +166,15 @@ impl State { |
| 166 | .filter(|session| Arc::ptr_eq(&session.library, &library)) | 166 | .filter(|session| Arc::ptr_eq(&session.library, &library)) |
| 167 | .map(|session| { | 167 | .map(|session| { |
| 168 | let path = session.tabs[session.tab].path.clone(); | 168 | let path = session.tabs[session.tab].path.clone(); |
| 169 | (path, Arc::clone(session.section.replica()), session.space) | 169 | ( |
| 170 | path, | ||
| 171 | Arc::clone(session.section.replica()), | ||
| 172 | self.pane.showing.space, | ||
| 173 | ) | ||
| 170 | }); | 174 | }); |
| 171 | let section = dialog.section.unwrap_or_default(); | 175 | let section = dialog.section.unwrap_or_default(); |
| 172 | let title = match dialog.scope { | 176 | let title = match dialog.scope { |
| 173 | Scope::Page => self.view.editor.page()?.title, | 177 | Scope::Page => self.pane.view.editor.page()?.title, |
| 174 | Scope::Section => crate::library::section_name(&section, &None), | 178 | Scope::Section => crate::library::section_name(&section, &None), |
| 175 | Scope::Notebook => library.name.clone(), | 179 | Scope::Notebook => library.name.clone(), |
| 176 | }; | 180 | }; |
crates/snowbound/src/screenshot.rs+9-4| ... | @@ -59,17 +59,18 @@ impl State { | ... | @@ -59,17 +59,18 @@ impl State { |
| 59 | }; | 59 | }; |
| 60 | self.surface.size = size.map(|side| (side * scale).max(1.0) as u32); | 60 | self.surface.size = size.map(|side| (side * scale).max(1.0) as u32); |
| 61 | self.renderer.clear_glyph_cache(); | 61 | self.renderer.clear_glyph_cache(); |
| 62 | let response = self.view.scale_factor_changed(scale)?; | 62 | let response = self.pane.view.scale_factor_changed(scale)?; |
| 63 | self.respond(response); | 63 | self.respond(response); |
| 64 | self.ui.set_focus(None); | 64 | self.ui.set_focus(None); |
| 65 | if let Some(title) = self | 65 | if let Some(title) = self |
| 66 | .pane | ||
| 66 | .view | 67 | .view |
| 67 | .editor | 68 | .editor |
| 68 | .outlines() | 69 | .outlines() |
| 69 | .iter() | 70 | .iter() |
| 70 | .find(|outline| outline.title) | 71 | .find(|outline| outline.title) |
| 71 | { | 72 | { |
| 72 | self.view.editor.focus_outline(title.id)?; | 73 | self.pane.view.editor.focus_outline(title.id)?; |
| 73 | } | 74 | } |
| 74 | #[cfg(target_os = "macos")] | 75 | #[cfg(target_os = "macos")] |
| 75 | if snow_leopard() { | 76 | if snow_leopard() { |
| ... | @@ -99,8 +100,12 @@ impl State { | ... | @@ -99,8 +100,12 @@ impl State { |
| 99 | // The page's box sizes the view whose pictures settle. | 100 | // The page's box sizes the view whose pictures settle. |
| 100 | self.layout(size, scale)?; | 101 | self.layout(size, scale)?; |
| 101 | let paper = self.paper(); | 102 | let paper = self.paper(); |
| 102 | if let Some((scene, _)) = &mut self.view.scene { | 103 | if let Some((scene, _)) = &mut self.pane.view.scene { |
| 103 | scene.settle(Some(&self.view.editor), self.view.viewport.scale, paper); | 104 | scene.settle( |
| 105 | Some(&self.pane.view.editor), | ||
| 106 | self.pane.view.viewport.scale, | ||
| 107 | paper, | ||
| 108 | ); | ||
| 104 | } | 109 | } |
| 105 | // Later frames lay out with earlier frames' measurements while colours ease. | 110 | // Later frames lay out with earlier frames' measurements while colours ease. |
| 106 | let deadline = Instant::now() + std::time::Duration::from_secs(2); | 111 | let deadline = Instant::now() + std::time::Duration::from_secs(2); |
crates/snowbound/src/search.rs+15-15| ... | @@ -614,26 +614,26 @@ impl State { | ... | @@ -614,26 +614,26 @@ impl State { |
| 614 | /// Marks the query's matches on the page; with `select`, selects the match Find on | 614 | /// Marks the query's matches on the page; with `select`, selects the match Find on |
| 615 | /// This Page is at, or the first, or the paragraph, when a result opened the page. | 615 | /// This Page is at, or the first, or the paragraph, when a result opened the page. |
| 616 | pub(crate) fn refind(&mut self, select: bool) -> Result<(), Box<dyn Error>> { | 616 | pub(crate) fn refind(&mut self, select: bool) -> Result<(), Box<dyn Error>> { |
| 617 | self.view.found = page_matches(&self.view.editor, &self.search.shown()); | 617 | self.pane.view.found = page_matches(&self.pane.view.editor, &self.search.shown()); |
| 618 | if !select { | 618 | if !select { |
| 619 | return Ok(()); | 619 | return Ok(()); |
| 620 | } | 620 | } |
| 621 | let space = self.session.as_ref().map(|session| session.space); | 621 | let space = self.session.as_ref().map(|_| self.pane.showing.space); |
| 622 | let target = if self.search.finding { | 622 | let target = if self.search.finding { |
| 623 | self.search.current = match self.search.current { | 623 | self.search.current = match self.search.current { |
| 624 | Some(at) if at < self.view.found.len() => Some(at), | 624 | Some(at) if at < self.pane.view.found.len() => Some(at), |
| 625 | _ => (!self.view.found.is_empty()).then_some(0), | 625 | _ => (!self.pane.view.found.is_empty()).then_some(0), |
| 626 | }; | 626 | }; |
| 627 | self.search | 627 | self.search |
| 628 | .current | 628 | .current |
| 629 | .and_then(|at| self.view.found.get(at).copied()) | 629 | .and_then(|at| self.pane.view.found.get(at).copied()) |
| 630 | } else if let Some((_, paragraph)) = | 630 | } else if let Some((_, paragraph)) = |
| 631 | self.search.reveal.filter(|(page, _)| Some(*page) == space) | 631 | self.search.reveal.filter(|(page, _)| Some(*page) == space) |
| 632 | { | 632 | { |
| 633 | self.search.reveal = None; | 633 | self.search.reveal = None; |
| 634 | match paragraph { | 634 | match paragraph { |
| 635 | Some(paragraph) => paragraph_match(&self.view.editor, paragraph), | 635 | Some(paragraph) => paragraph_match(&self.pane.view.editor, paragraph), |
| 636 | None => self.view.found.first().copied(), | 636 | None => self.pane.view.found.first().copied(), |
| 637 | } | 637 | } |
| 638 | } else { | 638 | } else { |
| 639 | None | 639 | None |
| ... | @@ -645,9 +645,9 @@ impl State { | ... | @@ -645,9 +645,9 @@ impl State { |
| 645 | let Some((outline, selection)) = target else { | 645 | let Some((outline, selection)) = target else { |
| 646 | return Ok(()); | 646 | return Ok(()); |
| 647 | }; | 647 | }; |
| 648 | self.view.editor.focus_outline(outline)?; | 648 | self.pane.view.editor.focus_outline(outline)?; |
| 649 | self.view.editor.select(selection)?; | 649 | self.pane.view.editor.select(selection)?; |
| 650 | let response = self.view.focus_text()?; | 650 | let response = self.pane.view.focus_text()?; |
| 651 | self.respond(response); | 651 | self.respond(response); |
| 652 | Ok(()) | 652 | Ok(()) |
| 653 | } | 653 | } |
| ... | @@ -686,7 +686,7 @@ impl State { | ... | @@ -686,7 +686,7 @@ impl State { |
| 686 | if !matches!(self.search.task_pane, Some(TaskPane::Search { .. })) { | 686 | if !matches!(self.search.task_pane, Some(TaskPane::Search { .. })) { |
| 687 | self.search.query.clear(); | 687 | self.search.query.clear(); |
| 688 | self.search.scope = self.search.default; | 688 | self.search.scope = self.search.default; |
| 689 | self.view.found.clear(); | 689 | self.pane.view.found.clear(); |
| 690 | } | 690 | } |
| 691 | } | 691 | } |
| 692 | 692 | ||
| ... | @@ -711,7 +711,7 @@ impl State { | ... | @@ -711,7 +711,7 @@ impl State { |
| 711 | let open = self | 711 | let open = self |
| 712 | .session | 712 | .session |
| 713 | .as_ref() | 713 | .as_ref() |
| 714 | .map(|session| (session.key(), session.space)); | 714 | .map(|session| (session.key(), self.pane.showing.space)); |
| 715 | match open { | 715 | match open { |
| 716 | Some((key, shown)) if key == section && shown == space => self.refind(true)?, | 716 | Some((key, shown)) if key == section && shown == space => self.refind(true)?, |
| 717 | Some((key, _)) if key == section => self.commands.push(Command::OpenPage(space)), | 717 | Some((key, _)) if key == section => self.commands.push(Command::OpenPage(space)), |
| ... | @@ -1130,7 +1130,7 @@ impl State { | ... | @@ -1130,7 +1130,7 @@ impl State { |
| 1130 | &[NamedKey::Enter, NamedKey::F3, NamedKey::Escape], | 1130 | &[NamedKey::Enter, NamedKey::F3, NamedKey::Escape], |
| 1131 | ); | 1131 | ); |
| 1132 | let before = self.search.query.clone(); | 1132 | let before = self.search.query.clone(); |
| 1133 | let count = self.view.found.len(); | 1133 | let count = self.pane.view.found.len(); |
| 1134 | let label = if self.search.query.trim().is_empty() { | 1134 | let label = if self.search.query.trim().is_empty() { |
| 1135 | "Find on page".to_owned() | 1135 | "Find on page".to_owned() |
| 1136 | } else if count == 0 { | 1136 | } else if count == 0 { |
| ... | @@ -1211,7 +1211,7 @@ impl State { | ... | @@ -1211,7 +1211,7 @@ impl State { |
| 1211 | self.end_search(); | 1211 | self.end_search(); |
| 1212 | return Ok(()); | 1212 | return Ok(()); |
| 1213 | } | 1213 | } |
| 1214 | let shift = self.view.modifiers().shift; | 1214 | let shift = self.pane.view.modifiers().shift; |
| 1215 | for key in keys { | 1215 | for key in keys { |
| 1216 | match key { | 1216 | match key { |
| 1217 | NamedKey::Escape => { | 1217 | NamedKey::Escape => { |
| ... | @@ -1234,7 +1234,7 @@ impl State { | ... | @@ -1234,7 +1234,7 @@ impl State { |
| 1234 | (at + count - 1) % count | 1234 | (at + count - 1) % count |
| 1235 | }; | 1235 | }; |
| 1236 | self.search.current = Some(next); | 1236 | self.search.current = Some(next); |
| 1237 | self.select(self.view.found.get(next).copied())?; | 1237 | self.select(self.pane.view.found.get(next).copied())?; |
| 1238 | } | 1238 | } |
| 1239 | Ok(()) | 1239 | Ok(()) |
| 1240 | } | 1240 | } |
crates/snowbound/src/settings.rs+4-4| ... | @@ -307,18 +307,18 @@ impl crate::State { | ... | @@ -307,18 +307,18 @@ impl crate::State { |
| 307 | color_scheme: self.color_scheme, | 307 | color_scheme: self.color_scheme, |
| 308 | light_pages: self.light_pages, | 308 | light_pages: self.light_pages, |
| 309 | hide_spelling: self.hide_spelling, | 309 | hide_spelling: self.hide_spelling, |
| 310 | ignore_markdown: self.view.editor.markdown.is_none(), | 310 | ignore_markdown: self.pane.view.editor.markdown.is_none(), |
| 311 | search_scope: self.search.default, | 311 | search_scope: self.search.default, |
| 312 | tags: (self.tags != canvas::editor::NoteTag::defaults()).then(|| self.tags.clone()), | 312 | tags: (self.tags != canvas::editor::NoteTag::defaults()).then(|| self.tags.clone()), |
| 313 | manual_updates: !self.updates.automatic(), | 313 | manual_updates: !self.updates.automatic(), |
| 314 | ignore_pen_pressure: !self.pen_pressure, | 314 | ignore_pen_pressure: !self.pen_pressure, |
| 315 | ignore_grid: !self.view.snap_to_grid, | 315 | ignore_grid: !self.pane.view.snap_to_grid, |
| 316 | default_font: self.view.editor.default_font.clone(), | 316 | default_font: self.pane.view.editor.default_font.clone(), |
| 317 | page_tabs_left: self.page_tabs_left, | 317 | page_tabs_left: self.page_tabs_left, |
| 318 | navigation_bar_right: self.navigation_bar_right, | 318 | navigation_bar_right: self.navigation_bar_right, |
| 319 | servers: self.servers.clone(), | 319 | servers: self.servers.clone(), |
| 320 | keys: crate::commands::Keymap::current().saved(), | 320 | keys: crate::commands::Keymap::current().saved(), |
| 321 | recent: self.trail.recent.clone(), | 321 | recent: self.recent.clone(), |
| 322 | passwords: self.passwords, | 322 | passwords: self.passwords, |
| 323 | notebook_theme: NotebookTheme(self.notebook_theme.clone()), | 323 | notebook_theme: NotebookTheme(self.notebook_theme.clone()), |
| 324 | live: self.live_options.clone(), | 324 | live: self.live_options.clone(), |
crates/snowbound/src/sidebar.rs+4-4| ... | @@ -941,7 +941,7 @@ impl crate::State { | ... | @@ -941,7 +941,7 @@ impl crate::State { |
| 941 | }, | 941 | }, |
| 942 | ); | 942 | ); |
| 943 | crate::name(&mut self.ui, rows_id, "My Notebooks"); | 943 | crate::name(&mut self.ui, rows_id, "My Notebooks"); |
| 944 | let shown = match (&self.session, &self.locked) { | 944 | let shown = match (&self.session, &self.pane.locked) { |
| 945 | (Some(session), _) => Some((&session.library, session.tabs[session.tab].path.as_str())), | 945 | (Some(session), _) => Some((&session.library, session.tabs[session.tab].path.as_str())), |
| 946 | (None, Some(locked)) => Some((&locked.library, locked.path.as_str())), | 946 | (None, Some(locked)) => Some((&locked.library, locked.path.as_str())), |
| 947 | (None, None) => None, | 947 | (None, None) => None, |
| ... | @@ -1444,7 +1444,7 @@ impl crate::State { | ... | @@ -1444,7 +1444,7 @@ impl crate::State { |
| 1444 | read: impl FnOnce(&str, &std::path::Path) -> Result<Library, String> + Send + 'static, | 1444 | read: impl FnOnce(&str, &std::path::Path) -> Result<Library, String> + Send + 'static, |
| 1445 | ) { | 1445 | ) { |
| 1446 | let section = section.or_else(|| { | 1446 | let section = section.or_else(|| { |
| 1447 | (self.trail.recent.iter()) | 1447 | (self.recent.iter()) |
| 1448 | .find(|place| place.notebook == location) | 1448 | .find(|place| place.notebook == location) |
| 1449 | .map(|place| place.section.clone()) | 1449 | .map(|place| place.section.clone()) |
| 1450 | }); | 1450 | }); |
| ... | @@ -1477,8 +1477,8 @@ impl crate::State { | ... | @@ -1477,8 +1477,8 @@ impl crate::State { |
| 1477 | return Err("This folder holds no notebook sections.".into()); | 1477 | return Err("This folder holds no notebook sections.".into()); |
| 1478 | }; | 1478 | }; |
| 1479 | let section = library.open_async(&path, notify).await?; | 1479 | let section = library.open_async(&path, notify).await?; |
| 1480 | let (session, page) = crate::read_session(section, library, path, left)?; | 1480 | let (session, space, page) = crate::read_session(section, library, path, left)?; |
| 1481 | Ok(crate::Loaded::Section(Box::new(session), page)) | 1481 | Ok(crate::Loaded::Section(Box::new(session), space, page)) |
| 1482 | }); | 1482 | }); |
| 1483 | } | 1483 | } |
| 1484 | 1484 |
crates/snowbound/src/task_pane.rs+4-4| ... | @@ -445,7 +445,7 @@ impl State { | ... | @@ -445,7 +445,7 @@ impl State { |
| 445 | 445 | ||
| 446 | /// Shows the next marked word in the Spelling pane; past the last, the check is complete. | 446 | /// Shows the next marked word in the Spelling pane; past the last, the check is complete. |
| 447 | fn next_word(&mut self) { | 447 | fn next_word(&mut self) { |
| 448 | match self.view.next_correction() { | 448 | match self.pane.view.next_correction() { |
| 449 | Ok(Some((response, correction))) => { | 449 | Ok(Some((response, correction))) => { |
| 450 | self.respond(response); | 450 | self.respond(response); |
| 451 | self.correction = Some(correction); | 451 | self.correction = Some(correction); |
| ... | @@ -552,7 +552,7 @@ impl State { | ... | @@ -552,7 +552,7 @@ impl State { |
| 552 | else { | 552 | else { |
| 553 | return; | 553 | return; |
| 554 | }; | 554 | }; |
| 555 | let spelling = self.view.spelling.clone(); | 555 | let spelling = self.pane.view.spelling.clone(); |
| 556 | let text = match (correction.repeated, pressed) { | 556 | let text = match (correction.repeated, pressed) { |
| 557 | (_, _) if change => correction.suggestions.get(chosen).map(String::as_str), | 557 | (_, _) if change => correction.suggestions.get(chosen).map(String::as_str), |
| 558 | (true, [true, _]) => Some(""), | 558 | (true, [true, _]) => Some(""), |
| ... | @@ -566,7 +566,7 @@ impl State { | ... | @@ -566,7 +566,7 @@ impl State { |
| 566 | } | 566 | } |
| 567 | }; | 567 | }; |
| 568 | if let Some(text) = text { | 568 | if let Some(text) = text { |
| 569 | match self.view.correct(&correction, text) { | 569 | match self.pane.view.correct(&correction, text) { |
| 570 | Ok(response) => self.respond(response), | 570 | Ok(response) => self.respond(response), |
| 571 | Err(error) => eprintln!("{error}"), | 571 | Err(error) => eprintln!("{error}"), |
| 572 | } | 572 | } |
| ... | @@ -936,7 +936,7 @@ impl State { | ... | @@ -936,7 +936,7 @@ impl State { |
| 936 | let pages = &session.pages; | 936 | let pages = &session.pages; |
| 937 | let at = pages | 937 | let at = pages |
| 938 | .iter() | 938 | .iter() |
| 939 | .position(|(space, ..)| *space == session.space)?; | 939 | .position(|(space, ..)| *space == self.pane.showing.space)?; |
| 940 | let base = pages.iter().map(|(.., level)| *level).min()?; | 940 | let base = pages.iter().map(|(.., level)| *level).min()?; |
| 941 | let start = pages[..=at] | 941 | let start = pages[..=at] |
| 942 | .iter() | 942 | .iter() |
crates/snowbound/src/templates.rs+4-4| ... | @@ -387,8 +387,8 @@ impl crate::State { | ... | @@ -387,8 +387,8 @@ impl crate::State { |
| 387 | let Some(session) = &self.session else { | 387 | let Some(session) = &self.session else { |
| 388 | return; | 388 | return; |
| 389 | }; | 389 | }; |
| 390 | let space = session.space; | 390 | let space = self.pane.showing.space; |
| 391 | let read_only = session.read_only(); | 391 | let read_only = session.read_only(&self.pane.showing); |
| 392 | let Some(rect) = self.ui.laid_out(crate::page()) else { | 392 | let Some(rect) = self.ui.laid_out(crate::page()) else { |
| 393 | return; | 393 | return; |
| 394 | }; | 394 | }; |
| ... | @@ -409,7 +409,7 @@ impl crate::State { | ... | @@ -409,7 +409,7 @@ impl crate::State { |
| 409 | ) { | 409 | ) { |
| 410 | self.choose(crate::commands::Choice::Art(Some(name))); | 410 | self.choose(crate::commands::Choice::Art(Some(name))); |
| 411 | } | 411 | } |
| 412 | let editor = &self.view.editor; | 412 | let editor = &self.pane.view.editor; |
| 413 | let blank = editor | 413 | let blank = editor |
| 414 | .visible_outlines() | 414 | .visible_outlines() |
| 415 | .chain(editor.caret_outline()) | 415 | .chain(editor.caret_outline()) |
| ... | @@ -422,7 +422,7 @@ impl crate::State { | ... | @@ -422,7 +422,7 @@ impl crate::State { |
| 422 | return; | 422 | return; |
| 423 | }; | 423 | }; |
| 424 | let (color, rule_lines) = (editor.page_color(), editor.rule_lines()); | 424 | let (color, rule_lines) = (editor.page_color(), editor.rule_lines()); |
| 425 | let viewport = self.view.viewport; | 425 | let viewport = self.pane.view.viewport; |
| 426 | let scale = self.ui.scale(); | 426 | let scale = self.ui.scale(); |
| 427 | let position = [ | 427 | let position = [ |
| 428 | (viewport.origin[0] + start[0] * viewport.scale) / scale, | 428 | (viewport.origin[0] + start[0] * viewport.scale) / scale, |
crates/snowbound/src/themes.rs+6-6| ... | @@ -120,7 +120,7 @@ impl State { | ... | @@ -120,7 +120,7 @@ impl State { |
| 120 | let library = &session.library; | 120 | let library = &session.library; |
| 121 | library.catalog()?; | 121 | library.catalog()?; |
| 122 | let target = match scope { | 122 | let target = match scope { |
| 123 | Scope::Page => stored::Scope::page(self.view.editor.identity()?), | 123 | Scope::Page => stored::Scope::page(self.pane.view.editor.identity()?), |
| 124 | Scope::Section => { | 124 | Scope::Section => { |
| 125 | stored::Scope::section(library.section_identity(&session.tabs[session.tab].path)?) | 125 | stored::Scope::section(library.section_identity(&session.tabs[session.tab].path)?) |
| 126 | } | 126 | } |
| ... | @@ -144,7 +144,7 @@ impl State { | ... | @@ -144,7 +144,7 @@ impl State { |
| 144 | session | 144 | session |
| 145 | .library | 145 | .library |
| 146 | .themes() | 146 | .themes() |
| 147 | .effective(section, self.view.editor.identity()) | 147 | .effective(section, self.pane.view.editor.identity()) |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | /// The styles the gallery offers: the page's theme's, else OneNote 2010's. | 150 | /// The styles the gallery offers: the page's theme's, else OneNote 2010's. |
| ... | @@ -167,17 +167,17 @@ impl State { | ... | @@ -167,17 +167,17 @@ impl State { |
| 167 | let sheet = (theme.as_ref()) | 167 | let sheet = (theme.as_ref()) |
| 168 | .map(|theme| theme.sheet(self.section_color())) | 168 | .map(|theme| theme.sheet(self.section_color())) |
| 169 | .unwrap_or_default(); | 169 | .unwrap_or_default(); |
| 170 | if !force && self.view.editor.styles == sheet { | 170 | if !force && self.pane.view.editor.styles == sheet { |
| 171 | return Ok(()); | 171 | return Ok(()); |
| 172 | } | 172 | } |
| 173 | self.view.editor.styles = sheet.clone(); | 173 | self.pane.view.editor.styles = sheet.clone(); |
| 174 | let Some(session) = &self.session else { | 174 | let Some(session) = &self.session else { |
| 175 | return Ok(()); | 175 | return Ok(()); |
| 176 | }; | 176 | }; |
| 177 | if theme.is_none() || session.read_only() { | 177 | if theme.is_none() || session.read_only(&self.pane.showing) { |
| 178 | return Ok(()); | 178 | return Ok(()); |
| 179 | } | 179 | } |
| 180 | let page = session.section.page(session.space)?; | 180 | let page = session.section.page(self.pane.showing.space)?; |
| 181 | let ops = onestore::op::restyle(&page, &sheet)?; | 181 | let ops = onestore::op::restyle(&page, &sheet)?; |
| 182 | if ops.is_empty() { | 182 | if ops.is_empty() { |
| 183 | return Ok(()); | 183 | return Ok(()); |
crates/snowbound/src/undo.rs+10-9| ... | @@ -958,13 +958,13 @@ impl State { | ... | @@ -958,13 +958,13 @@ impl State { |
| 958 | 958 | ||
| 959 | /// Whether no action, page or command is on its way, which Undo and Redo wait for. | 959 | /// Whether no action, page or command is on its way, which Undo and Redo wait for. |
| 960 | pub(crate) fn settled(&self) -> bool { | 960 | pub(crate) fn settled(&self) -> bool { |
| 961 | self.undo.busy == 0 && self.switching.is_none() && self.commands.is_empty() | 961 | self.undo.busy == 0 && self.pane.switching.is_none() && self.commands.is_empty() |
| 962 | } | 962 | } |
| 963 | 963 | ||
| 964 | /// Whether Undo, or `redo` Redo, has something to take, or may once what is on its way | 964 | /// Whether Undo, or `redo` Redo, has something to take, or may once what is on its way |
| 965 | /// lands. | 965 | /// lands. |
| 966 | pub(crate) fn can_step(&self, redo: bool) -> bool { | 966 | pub(crate) fn can_step(&self, redo: bool) -> bool { |
| 967 | let editor = &self.view.editor; | 967 | let editor = &self.pane.view.editor; |
| 968 | !self.settled() | 968 | !self.settled() |
| 969 | || (if redo { | 969 | || (if redo { |
| 970 | editor.can_redo() | 970 | editor.can_redo() |
| ... | @@ -998,9 +998,9 @@ impl State { | ... | @@ -998,9 +998,9 @@ impl State { |
| 998 | /// user is, whichever came last. | 998 | /// user is, whichever came last. |
| 999 | fn take(&mut self, redo: bool) -> Result<(), Box<dyn Error>> { | 999 | fn take(&mut self, redo: bool) -> Result<(), Box<dyn Error>> { |
| 1000 | self.persist()?; | 1000 | self.persist()?; |
| 1001 | let depth = self.view.editor.history_depth(); | 1001 | let depth = self.pane.view.editor.history_depth(); |
| 1002 | // A page kept in no notebook has only its own history. | 1002 | // A page kept in no notebook has only its own history. |
| 1003 | let shown = self.session.as_ref().map(|session| session.space); | 1003 | let shown = self.session.as_ref().map(|_| self.pane.showing.space); |
| 1004 | let next = match (self.here(), shown) { | 1004 | let next = match (self.here(), shown) { |
| 1005 | (Some(here), Some(page)) => self.undo.next(redo, page, depth, &here), | 1005 | (Some(here), Some(page)) => self.undo.next(redo, page, depth, &here), |
| 1006 | _ => Some(Next::Edit), | 1006 | _ => Some(Next::Edit), |
| ... | @@ -1008,10 +1008,10 @@ impl State { | ... | @@ -1008,10 +1008,10 @@ impl State { |
| 1008 | match next { | 1008 | match next { |
| 1009 | None => {} | 1009 | None => {} |
| 1010 | Some(Next::Edit) => { | 1010 | Some(Next::Edit) => { |
| 1011 | let response = self.view.undo(redo)?; | 1011 | let response = self.pane.view.undo(redo)?; |
| 1012 | self.respond(response); | 1012 | self.respond(response); |
| 1013 | if let Some(page) = shown | 1013 | if let Some(page) = shown |
| 1014 | && self.view.editor.history_depth() != depth | 1014 | && self.pane.view.editor.history_depth() != depth |
| 1015 | { | 1015 | { |
| 1016 | self.undo.stepped(page, redo); | 1016 | self.undo.stepped(page, redo); |
| 1017 | } | 1017 | } |
| ... | @@ -1117,11 +1117,11 @@ impl State { | ... | @@ -1117,11 +1117,11 @@ impl State { |
| 1117 | author: self.author.clone(), | 1117 | author: self.author.clone(), |
| 1118 | fresh: self.dated_page(None, "")?, | 1118 | fresh: self.dated_page(None, "")?, |
| 1119 | }; | 1119 | }; |
| 1120 | let shown = session.space; | 1120 | let shown = self.pane.showing.space; |
| 1121 | let original = Arc::clone(&site.library); | 1121 | let original = Arc::clone(&site.library); |
| 1122 | let proxy = self.proxy.clone(); | 1122 | let proxy = self.proxy.clone(); |
| 1123 | self.undo.busy += 1; | 1123 | self.undo.busy += 1; |
| 1124 | self.switching = Some((None, web_time::Instant::now())); | 1124 | self.pane.switching = Some((None, web_time::Instant::now())); |
| 1125 | self.load_async(move || async move { | 1125 | self.load_async(move || async move { |
| 1126 | let applied = site.change(change, shown).await; | 1126 | let applied = site.change(change, shown).await; |
| 1127 | let show = match &applied { | 1127 | let show = match &applied { |
| ... | @@ -1150,11 +1150,12 @@ impl State { | ... | @@ -1150,11 +1150,12 @@ impl State { |
| 1150 | } else { | 1150 | } else { |
| 1151 | state.list(&library); | 1151 | state.list(&library); |
| 1152 | if state | 1152 | if state |
| 1153 | .pane | ||
| 1153 | .sectionless | 1154 | .sectionless |
| 1154 | .as_ref() | 1155 | .as_ref() |
| 1155 | .is_some_and(|listed| lineage(listed)) | 1156 | .is_some_and(|listed| lineage(listed)) |
| 1156 | { | 1157 | { |
| 1157 | state.sectionless = Some(library); | 1158 | state.pane.sectionless = Some(library); |
| 1158 | } | 1159 | } |
| 1159 | } | 1160 | } |
| 1160 | } | 1161 | } |
crates/snowbound/src/unread.rs+8-3| ... | @@ -274,8 +274,13 @@ impl State { | ... | @@ -274,8 +274,13 @@ impl State { |
| 274 | let viewing = self.session.as_ref().and_then(|session| { | 274 | let viewing = self.session.as_ref().and_then(|session| { |
| 275 | let path = &session.tabs[session.tab].path; | 275 | let path = &session.tabs[session.tab].path; |
| 276 | let section = session.library.section_identity(path)?; | 276 | let section = session.library.section_identity(path)?; |
| 277 | (session.version.is_none() && !recycle::binned(path)) | 277 | (self.pane.showing.version.is_none() && !recycle::binned(path)).then(|| { |
| 278 | .then(|| (session.library.location.clone(), section, session.space)) | 278 | ( |
| 279 | session.library.location.clone(), | ||
| 280 | section, | ||
| 281 | self.pane.showing.space, | ||
| 282 | ) | ||
| 283 | }) | ||
| 279 | }); | 284 | }); |
| 280 | let same = |(notebook, section, space, _): &(String, [u8; 16], ExGuid, bool)| { | 285 | let same = |(notebook, section, space, _): &(String, [u8; 16], ExGuid, bool)| { |
| 281 | viewing.as_ref() == Some(&(notebook.clone(), *section, *space)) | 286 | viewing.as_ref() == Some(&(notebook.clone(), *section, *space)) |
| ... | @@ -418,7 +423,7 @@ impl State { | ... | @@ -418,7 +423,7 @@ impl State { |
| 418 | let at = session | 423 | let at = session |
| 419 | .pages | 424 | .pages |
| 420 | .iter() | 425 | .iter() |
| 421 | .position(|(space, ..)| *space == session.space); | 426 | .position(|(space, ..)| *space == self.pane.showing.space); |
| 422 | let after = (session.pages.iter().skip(at.map_or(0, |at| at + 1))) | 427 | let after = (session.pages.iter().skip(at.map_or(0, |at| at + 1))) |
| 423 | .find(|(space, ..)| unread.contains(space)); | 428 | .find(|(space, ..)| unread.contains(space)); |
| 424 | if let Some((space, ..)) = after { | 429 | if let Some((space, ..)) = after { |
crates/snowbound/src/web.rs+10-10| ... | @@ -999,7 +999,7 @@ pub async fn start( | ... | @@ -999,7 +999,7 @@ pub async fn start( |
| 999 | if let Some(resume) = resumed().and_then(|resume| serde_json::from_str::<Resume>(&resume).ok()) | 999 | if let Some(resume) = resumed().and_then(|resume| serde_json::from_str::<Resume>(&resume).ok()) |
| 1000 | { | 1000 | { |
| 1001 | for (axis, offset) in resume.offset.into_iter().enumerate() { | 1001 | for (axis, offset) in resume.offset.into_iter().enumerate() { |
| 1002 | match state.view.scroll_to(axis, offset) { | 1002 | match state.pane.view.scroll_to(axis, offset) { |
| 1003 | Ok(response) => state.respond(response), | 1003 | Ok(response) => state.respond(response), |
| 1004 | Err(error) => report(error), | 1004 | Err(error) => report(error), |
| 1005 | } | 1005 | } |
| ... | @@ -1571,13 +1571,13 @@ fn turn(state: &mut State) -> Result<(), Box<dyn Error>> { | ... | @@ -1571,13 +1571,13 @@ fn turn(state: &mut State) -> Result<(), Box<dyn Error>> { |
| 1571 | state.frame()?; | 1571 | state.frame()?; |
| 1572 | remember_look(state); | 1572 | remember_look(state); |
| 1573 | } | 1573 | } |
| 1574 | let space = state.session.as_ref().map(|session| session.space); | 1574 | let space = state.session.as_ref().map(|_| state.pane.showing.space); |
| 1575 | if TYPED.take() || LOOKED.replace(space) != space { | 1575 | if TYPED.take() || LOOKED.replace(space) != space { |
| 1576 | fetch_fallbacks(&crate::page_text(&state.view.editor)); | 1576 | fetch_fallbacks(&crate::page_text(&state.pane.view.editor)); |
| 1577 | } | 1577 | } |
| 1578 | park_input(state); | 1578 | park_input(state); |
| 1579 | let now = Instant::now(); | 1579 | let now = Instant::now(); |
| 1580 | let (repaint, blink) = state.view.blink(now); | 1580 | let (repaint, blink) = state.pane.view.blink(now); |
| 1581 | let wake = state.ui.wake_at(); | 1581 | let wake = state.ui.wake_at(); |
| 1582 | if repaint || wake.is_some_and(|wake| wake <= now) { | 1582 | if repaint || wake.is_some_and(|wake| wake <= now) { |
| 1583 | state.window.request_redraw(); | 1583 | state.window.request_redraw(); |
| ... | @@ -1709,7 +1709,7 @@ pub fn update_now(state: &mut State) { | ... | @@ -1709,7 +1709,7 @@ pub fn update_now(state: &mut State) { |
| 1709 | return RELOADING.set(false); | 1709 | return RELOADING.set(false); |
| 1710 | }; | 1710 | }; |
| 1711 | let resume = Resume { | 1711 | let resume = Resume { |
| 1712 | offset: state.view.viewport.origin.map(|origin| -origin), | 1712 | offset: state.pane.view.viewport.origin.map(|origin| -origin), |
| 1713 | build, | 1713 | build, |
| 1714 | }; | 1714 | }; |
| 1715 | let reloading = reload_into(&serde_json::to_string(&resume).unwrap_or_default()); | 1715 | let reloading = reload_into(&serde_json::to_string(&resume).unwrap_or_default()); |
| ... | @@ -1737,8 +1737,8 @@ fn follow_updates(state: &mut State, now: Instant) -> Option<Instant> { | ... | @@ -1737,8 +1737,8 @@ fn follow_updates(state: &mut State, now: Instant) -> Option<Instant> { |
| 1737 | } | 1737 | } |
| 1738 | let idle = LAST_INPUT.get().map_or(now, |last| last + IDLE); | 1738 | let idle = LAST_INPUT.get().map_or(now, |last| last + IDLE); |
| 1739 | let quiet = !state.ui.popups_open() | 1739 | let quiet = !state.ui.popups_open() |
| 1740 | && state.opening.is_none() | 1740 | && state.pane.opening.is_none() |
| 1741 | && state.loading == 0 | 1741 | && state.pane.loading == 0 |
| 1742 | && matches!(state.media, crate::recording::Media::Idle); | 1742 | && matches!(state.media, crate::recording::Media::Idle); |
| 1743 | if update_ready() && (returned || idle <= now) && quiet { | 1743 | if update_ready() && (returned || idle <= now) && quiet { |
| 1744 | update_now(state); | 1744 | update_now(state); |
| ... | @@ -1783,7 +1783,7 @@ pub fn font_arrived(name: String, bytes: Vec<u8>) { | ... | @@ -1783,7 +1783,7 @@ pub fn font_arrived(name: String, bytes: Vec<u8>) { |
| 1783 | families | 1783 | families |
| 1784 | }; | 1784 | }; |
| 1785 | send(UserEvent::Then(Box::new(move |state| { | 1785 | send(UserEvent::Then(Box::new(move |state| { |
| 1786 | let families = register(&mut state.view.engine.fonts.collection, face.clone()); | 1786 | let families = register(&mut state.pane.view.engine.fonts.collection, face.clone()); |
| 1787 | if let Ok(mut layouts) = state.layouts.lock() { | 1787 | if let Ok(mut layouts) = state.layouts.lock() { |
| 1788 | register(&mut layouts.fonts.collection, face.clone()); | 1788 | register(&mut layouts.fonts.collection, face.clone()); |
| 1789 | } | 1789 | } |
| ... | @@ -1792,7 +1792,7 @@ pub fn font_arrived(name: String, bytes: Vec<u8>) { | ... | @@ -1792,7 +1792,7 @@ pub fn font_arrived(name: String, bytes: Vec<u8>) { |
| 1792 | } | 1792 | } |
| 1793 | state.ui.fall_back_to(&families); | 1793 | state.ui.fall_back_to(&families); |
| 1794 | state.renderer.clear_glyph_cache(); | 1794 | state.renderer.clear_glyph_cache(); |
| 1795 | let response = state.view.relayout()?; | 1795 | let response = state.pane.view.relayout()?; |
| 1796 | state.respond(response); | 1796 | state.respond(response); |
| 1797 | state.window.request_redraw(); | 1797 | state.window.request_redraw(); |
| 1798 | Ok(()) | 1798 | Ok(()) |
| ... | @@ -1816,7 +1816,7 @@ pub fn dictionary_arrived(name: String, affix: String, words: String) { | ... | @@ -1816,7 +1816,7 @@ pub fn dictionary_arrived(name: String, affix: String, words: String) { |
| 1816 | /// Parks the text area while the page takes no text, keeping it writable for the | 1816 | /// Parks the text area while the page takes no text, keeping it writable for the |
| 1817 | /// interface's fields. | 1817 | /// interface's fields. |
| 1818 | fn park_input(state: &State) { | 1818 | fn park_input(state: &State) { |
| 1819 | if state.ui.focused() != Some(page()) || !state.view.accepts_text() { | 1819 | if state.ui.focused() != Some(page()) || !state.pane.view.accepts_text() { |
| 1820 | place_input(0.0, 0.0, 1.0, state.ui.focused().is_some()); | 1820 | place_input(0.0, 0.0, 1.0, state.ui.focused().is_some()); |
| 1821 | } | 1821 | } |
| 1822 | } | 1822 | } |