diff --git a/viewport/src/editor/mod.rs b/viewport/src/editor/mod.rs index 0dff97b..565c808 100644 --- a/viewport/src/editor/mod.rs +++ b/viewport/src/editor/mod.rs @@ -86,7 +86,10 @@ impl EditorState { } else { let editor = self.view_blocks(); match self.build_free_overlay() { - Some(overlay) => iced_widget::stack![editor, overlay].into(), + Some(overlay) => iced_widget::stack![editor, overlay] + .width(Length::Fill) + .height(Length::Fill) + .into(), None => editor, } }; @@ -146,12 +149,18 @@ impl EditorState { .into(); if self.settings_open { - return iced_widget::stack![body, self.settings_panel()].into(); + return iced_widget::stack![body, self.settings_panel()] + .width(Length::Fill) + .height(Length::Fill) + .into(); } #[cfg(any(target_os = "linux", target_os = "windows"))] if let Some(cat) = self.menu_open { - return iced_widget::stack![body, self.menu_dropdown(cat)].into(); + return iced_widget::stack![body, self.menu_dropdown(cat)] + .width(Length::Fill) + .height(Length::Fill) + .into(); } body @@ -376,8 +385,12 @@ impl EditorState { }; let spillover_layer = self.spillover_view().unwrap_or_else(empty_layer); - // the document scrollable stays at stack layer 0, never re-wrapped by an overlay - iced_widget::stack![inner, selection_tint, minimap_layer, ctx_layer, spillover_layer].into() + // the document scrollable stays at stack layer 0, never re-wrapped by an overlay. + // a Fill stack gives the scrollable the viewport height, not the zero intrinsic of a Shrink stack + iced_widget::stack![inner, selection_tint, minimap_layer, ctx_layer, spillover_layer] + .width(Length::Fill) + .height(Length::Fill) + .into() } /// builds the right-edge minimap as a clickable AST declaration list. @@ -1443,12 +1456,30 @@ fn splice_first_empty_slot(text: &str, addr: &str) -> Option { } fn macos_key_binding(key_press: KeyPress) -> Option> { - let KeyPress { key, modifiers, status, .. } = &key_press; + let KeyPress { key, modified_key, modifiers, status, .. } = &key_press; if !matches!(status, Status::Focused { .. }) { return None; } + // auto-pair resolves from the composed key, not the unshifted base key + if !modifiers.logo() && !modifiers.alt() && !modifiers.control() { + if let keyboard::Key::Character(c) = modified_key.as_ref() { + let pair = match c { + "[" => auto_pair::enabled(auto_pair::BRACKET).then_some(("[", "]")), + "{" => auto_pair::enabled(auto_pair::BRACE).then_some(("{", "}")), + "(" => auto_pair::enabled(auto_pair::PAREN).then_some(("(", ")")), + "'" => auto_pair::enabled(auto_pair::SINGLE).then_some(("'", "'")), + "\"" => auto_pair::enabled(auto_pair::DOUBLE).then_some(("\"", "\"")), + "`" => auto_pair::enabled(auto_pair::BACKTICK).then_some(("`", "`")), + _ => None, + }; + if let Some((open, close)) = pair { + return Some(Binding::Custom(Message::AutoPair(open, close))); + } + } + } + match key.as_ref() { keyboard::Key::Character("z") if modifiers.logo() && modifiers.shift() => { Some(Binding::Custom(Message::Redo)) @@ -1462,24 +1493,6 @@ fn macos_key_binding(key_press: KeyPress) -> Option> { keyboard::Key::Character("-") if modifiers.logo() => { Some(Binding::Custom(Message::ZoomOut)) } - keyboard::Key::Character("[") if !modifiers.logo() && !modifiers.alt() && !modifiers.control() && auto_pair::enabled(auto_pair::BRACKET) => { - Some(Binding::Custom(Message::AutoPair("[", "]"))) - } - keyboard::Key::Character("{") if !modifiers.logo() && !modifiers.alt() && !modifiers.control() && auto_pair::enabled(auto_pair::BRACE) => { - Some(Binding::Custom(Message::AutoPair("{", "}"))) - } - keyboard::Key::Character("(") if !modifiers.logo() && !modifiers.alt() && !modifiers.control() && auto_pair::enabled(auto_pair::PAREN) => { - Some(Binding::Custom(Message::AutoPair("(", ")"))) - } - keyboard::Key::Character("'") if !modifiers.logo() && !modifiers.alt() && !modifiers.control() && auto_pair::enabled(auto_pair::SINGLE) => { - Some(Binding::Custom(Message::AutoPair("'", "'"))) - } - keyboard::Key::Character("\"") if !modifiers.logo() && !modifiers.alt() && !modifiers.control() && auto_pair::enabled(auto_pair::DOUBLE) => { - Some(Binding::Custom(Message::AutoPair("\"", "\""))) - } - keyboard::Key::Character("`") if !modifiers.logo() && !modifiers.alt() && !modifiers.control() && auto_pair::enabled(auto_pair::BACKTICK) => { - Some(Binding::Custom(Message::AutoPair("`", "`"))) - } keyboard::Key::Named(key::Named::Backspace) if modifiers.alt() => { Some(Binding::Sequence(vec![ Binding::Select(Motion::WordLeft), diff --git a/viewport/src/handle.rs b/viewport/src/handle.rs index c931db1..aacb4df 100644 --- a/viewport/src/handle.rs +++ b/viewport/src/handle.rs @@ -629,6 +629,54 @@ pub fn render(handle: &mut ViewportHandle) { ); handle.events.clear(); + #[cfg(debug_assertions)] + { + use std::fmt::Write as _; + use iced_wgpu::core::widget::operation::{Operation, Scrollable}; + use iced_wgpu::core::widget::Id as CoreId; + use iced_wgpu::core::{Rectangle, Vector}; + + let s = &handle.state; + let mut out = String::new(); + let _ = writeln!(out, "render_mode={:?} layout={} free_placements={} preview={} settings_open={} font={}", + s.render_mode, s.layout.len(), s.free_placements.len(), s.preview, s.settings_open, s.font_size); + for (i, id) in s.layout.iter().enumerate() { + if let Some(b) = s.registry.get(id) { + let _ = write!(out, " [{i}] id={:?} kind={}", id, b.kind_tag()); + if let Some(tb) = b.as_any().downcast_ref::() { + let _ = write!(out, " rows={} cols={} widths={:?} read_only={} eval={}", + tb.rows.len(), tb.col_widths.len(), tb.col_widths, tb.read_only, tb.is_eval_result); + } + let _ = writeln!(out); + } + } + + // record laid-out bounds of scrollables and the first containers + struct BoundsProbe { scroll: Vec, conts: Vec } + impl Operation<()> for BoundsProbe { + fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<()>)) { + operate(self); + } + fn container(&mut self, id: Option<&CoreId>, bounds: Rectangle) { + if self.conts.len() < 24 { + self.conts.push(format!("cont id={:?} x={:.0} y={:.0} w={:.0} h={:.0}", + id, bounds.x, bounds.y, bounds.width, bounds.height)); + } + } + fn scrollable(&mut self, id: Option<&CoreId>, bounds: Rectangle, content: Rectangle, tx: Vector, _s: &mut dyn Scrollable) { + self.scroll.push(format!("scroll id={:?} bounds=({:.0},{:.0} {:.0}x{:.0}) content=({:.0},{:.0} {:.0}x{:.0}) tx=({:.0},{:.0})", + id, bounds.x, bounds.y, bounds.width, bounds.height, + content.x, content.y, content.width, content.height, tx.x, tx.y)); + } + } + let mut probe = BoundsProbe { scroll: Vec::new(), conts: Vec::new() }; + ui.operate(&handle.renderer, &mut probe); + for l in &probe.scroll { let _ = writeln!(out, " {l}"); } + for l in &probe.conts { let _ = writeln!(out, " {l}"); } + + let _ = std::fs::write("/tmp/acord-blank.log", out); + } + let focused_id = { use iced_wgpu::core::widget::operation::{Focusable, Operation}; use iced_wgpu::core::widget::Id as CoreId; diff --git a/viewport/src/sidecar.rs b/viewport/src/sidecar.rs index 2434e10..b1e05cd 100644 --- a/viewport/src/sidecar.rs +++ b/viewport/src/sidecar.rs @@ -320,7 +320,7 @@ fn wrap_base64(s: &str, width: usize) -> String { let mut i = 0; while i < bytes.len() { let end = (i + width).min(bytes.len()); - // Base64 is ASCII, slicing by byte == slicing by char. + // base64 byte offsets equal char offsets out.push_str(&s[i..end]); if end < bytes.len() { out.push('\n'); @@ -345,7 +345,7 @@ mod tests { formulas: HashMap::new(), }, ); - Sidecar { version: 1, tables } + Sidecar { version: 1, tables, ..Default::default() } } #[test] @@ -416,7 +416,7 @@ mod tests { formulas, }, ); - let sc = Sidecar { version: 1, tables }; + let sc = Sidecar { version: 1, tables, ..Default::default() }; let body = "# Doc\n"; let embedded = embed_archive(body, &sc, &[]);