From 819bc7d2238fba099306faf13a42ee03c67b6837 Mon Sep 17 00:00:00 2001 From: Harald Date: Wed, 21 Oct 2026 11:44:39 +0200 Subject: [PATCH] fix: larger toolbar buttons, text field grows with the text (Issues #35, #34, #28) - Toolbar: 2 px gaps and 3 px dividers instead of 3 px and 7 px, and the size estimate now matches the measured layout exactly. On a 900 px screen the buttons are back to 30 px despite the new view-only button. - Text tool: a new field starts three letters wide and grows with the longest line up to the wrap width at the page edge. - INK_NOTES_DEBUG_FLING now confirms it is active and logs every finger lift, including touches ignored because the pen is near. Co-Authored-By: Claude Opus 5.5 --- src/main.rs | 18 +++++++++++++++++- src/text.rs | 27 +++++++++++++++++++++++++++ src/ui.rs | 19 ++++++++++++------- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index a8d7f9f..d22666e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1426,6 +1426,11 @@ fn main() -> Result<(), Box> { let mut touch_pan_last_pos = (0.0_f32, 0.0_f32); // Nachgleiten nach schnellem Wischen mit dem Finger (Issue #28) let mut fling = ink_notes::viewport::Fling::default(); + // Diagnosis for Issue #28: log every finger lift and the swipe speed + let debug_fling = std::env::var_os("INK_NOTES_DEBUG_FLING").is_some(); + if debug_fling { + eprintln!("[Nachgleiten] Diagnose aktiv – bitte mit dem Finger wischen"); + } // Stylus / Digitizer Zustand (Octotablet / Wayland Tablet Protocol) let mut is_stylus_drawing = false; @@ -2843,6 +2848,9 @@ fn main() -> Result<(), Box> { // Hardware-Palm-Rejection: Wenn der Stylus in Proximity ist oder zeichnet, Touch ignorieren if is_stylus_drawing || is_stylus_in_proximity { + if debug_fling && touch.phase != TouchPhase::Moved { + eprintln!("[Nachgleiten] Finger ignoriert ({:?}): Stift in der Nähe", touch.phase); + } return; } @@ -3058,6 +3066,14 @@ fn main() -> Result<(), Box> { TouchPhase::Ended | TouchPhase::Cancelled => { active_touches.remove(&touch.id); long_press.cancel(); + if debug_fling { + eprintln!( + "[Nachgleiten] Finger abgehoben ({:?}), verschiebt: {}, weitere Finger: {}", + touch.phase, + is_touch_panning, + active_touches.len() + ); + } if active_touches.is_empty() { is_pinch_gesturing = false; @@ -3069,7 +3085,7 @@ fn main() -> Result<(), Box> { canvas.pan(dx, dy); fling.track(Vec2::new(dx, dy), Instant::now()); let velocity = fling.release(Instant::now()); - if std::env::var_os("INK_NOTES_DEBUG_FLING").is_some() { + if debug_fling { eprintln!( "[Nachgleiten] {:.0} px/s beim Abheben, gleitet: {}", velocity.length(), diff --git a/src/text.rs b/src/text.rs index 656d72d..8f71cff 100644 --- a/src/text.rs +++ b/src/text.rs @@ -151,6 +151,20 @@ impl TextBox { glam::Vec2::new(self.wrap_width.max(self.size), self.layout().height()) } + /// Width of the field while it is being edited: as wide as the longest line plus room for + /// the next character, at most the wrap width, so a new field starts short (Issue #34). + pub fn edit_width(&self) -> f32 { + let widest = self + .layout() + .lines + .iter() + .map(|line| line.width) + .fold(0.0, f32::max); + (widest + self.size) + .max(3.0 * self.size) + .min(self.wrap_width.max(self.size)) + } + /// Setzt den Text mit Zeilenumbruch an Leerzeichen; zu lange Wörter werden zeichenweise /// umbrochen, Zeilenumbrüche im Text bleiben erhalten. pub fn layout(&self) -> TextLayout { @@ -409,6 +423,19 @@ mod tests { TextBox::new(content.to_string(), 50.0, TextFont::Sans, wrap_width) } + #[test] + fn test_edit_width_grows_with_the_text() { + let empty = sample("", 1500.0); + assert!((empty.edit_width() - 150.0).abs() < 1e-3); + let short = sample("Hallo", 1500.0); + let longer = sample("Hallo Welt, wie geht es", 1500.0); + assert!(short.edit_width() < longer.edit_width()); + assert!(longer.edit_width() < 1500.0); + // Long text wraps at the page edge and fills the full width + let long = sample(&"Wort ".repeat(100), 1500.0); + assert!((long.edit_width() - 1500.0).abs() < 1e-3); + } + #[test] fn test_fonts_load() { for font in [TextFont::Sans, TextFont::Mono] { diff --git a/src/ui.rs b/src/ui.rs index dbc0859..9438829 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -639,13 +639,16 @@ impl UiState { /// Länge der längsten Farbpalette; so viel Platz hält die Leiste immer frei. pub const MAX_PALETTE_LEN: usize = 5; + /// Vertical gap between toolbar buttons and the height of a divider line. + pub const SIDEBAR_SPACING: f32 = 2.0; + pub const SIDEBAR_DIVIDER: f32 = 3.0; /// Durchmesser der Farbtupfer passend zur Knopfgröße. pub fn sidebar_swatch_size(button_size: f32) -> f32 { button_size - 10.0 } - /// Größte Knopfgröße (40 px, schrittweise bis 28 px), bei der die Leiste mit allen Elementen + /// Größte Knopfgröße (40 px, schrittweise bis 26 px), bei der die Leiste mit allen Elementen /// des aktiven Werkzeugs in `max_height` passt. `continuous`: durchgehende Ansicht (ohne /// Blätterpfeile). pub fn sidebar_button_size(tool: ActiveTool, max_height: f32, continuous: bool) -> f32 { @@ -655,11 +658,11 @@ impl UiState { let page_buttons = if continuous { 2 } else { 4 }; let buttons = 12 + page_buttons + if select { 4 } else { 0 }; let dividers = 5 + usize::from(select); - let spacing = 3.0; + let spacing = Self::SIDEBAR_SPACING; let needed = |b: f32| { buttons as f32 * (b + spacing) + Self::MAX_PALETTE_LEN as f32 * (Self::sidebar_swatch_size(b) + spacing) - + dividers as f32 * (7.0 + spacing) + + dividers as f32 * (Self::SIDEBAR_DIVIDER + spacing) // frame, margins and the spacing egui adds (measured with the selection tool) + 40.0 }; @@ -1092,8 +1095,10 @@ fn sidebar_button( /// Waagerechte Trennlinie zwischen Gruppen der Werkzeugleiste. fn sidebar_divider(ui: &mut egui::Ui, button_size: f32) { - let (rect, _) = - ui.allocate_exact_size(egui::vec2(button_size - 12.0, 7.0), egui::Sense::hover()); + let (rect, _) = ui.allocate_exact_size( + egui::vec2(button_size - 12.0, UiState::SIDEBAR_DIVIDER), + egui::Sense::hover(), + ); ui.painter().hline( rect.x_range(), rect.center().y, @@ -1502,7 +1507,7 @@ fn text_edit_overlay(ctx: &egui::Context, state: &mut UiState) { // anpassen, damit die Zeilen an denselben Stellen umbrechen wie auf dem Blatt let height_px = font_size * ctx.pixels_per_point() * edit.text.font.height_per_em(); let rounding = height_px.round().max(1.0) / height_px; - let width = (edit.text.wrap_width.max(edit.text.size) * sx * view.scale * rounding).max(8.0); + let width = (edit.text.edit_width() * sx * view.scale * rounding).max(8.0); let color = color32_from(if adapt { adapt_ink_color(edit.color) } else { @@ -3004,7 +3009,7 @@ pub fn render_ui( // Zentriertes Layout nimmt sonst die volle Fensterbreite ein ui.set_width(btn); ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { - ui.spacing_mut().item_spacing = egui::vec2(0.0, 3.0); + ui.spacing_mut().item_spacing = egui::vec2(0.0, UiState::SIDEBAR_SPACING); // --- Notizbücher --- let doc_tooltip = t!(