- 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 <noreply@anthropic.com>
This commit is contained in:
1 parent
c38bfdd9b1
commit
819bc7d223
3 files changed
+56
-8
No files matched your search
+17
-1
@@ -1426,6 +1426,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>> {
|
||||
|
||||
// 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<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>> {
|
||||
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(),
|
||||
|
||||
+27
@@ -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] {
|
||||
|
||||
@@ -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!(
|
||||
|
||||
Reference in new issue
Block a user