feat: auto-fit view to start region
This commit is contained in:
parent
714d00fbb9
commit
461b9cd905
2 changed files with 57 additions and 6 deletions
56
src/app.rs
56
src/app.rs
|
|
@ -28,6 +28,18 @@ pub struct TemplateApp {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
fps_ema: f32,
|
fps_ema: f32,
|
||||||
|
|
||||||
|
/// Whether we've auto-fitted the start viewport.
|
||||||
|
#[serde(skip)]
|
||||||
|
did_fit_start: bool,
|
||||||
|
|
||||||
|
/// Last known cursor position (for edge scrolling even without movement).
|
||||||
|
#[serde(skip)]
|
||||||
|
last_cursor_pos: Option<egui::Pos2>,
|
||||||
|
|
||||||
|
/// Whether a reset-to-start was requested (from UI)
|
||||||
|
#[serde(skip)]
|
||||||
|
reset_view_requested: bool,
|
||||||
|
|
||||||
/// Last pointer position for manual drag tracking (smoother than `Sense::drag`).
|
/// Last pointer position for manual drag tracking (smoother than `Sense::drag`).
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
last_pointer_pos: Option<egui::Pos2>,
|
last_pointer_pos: Option<egui::Pos2>,
|
||||||
|
|
@ -55,6 +67,9 @@ impl Default for TemplateApp {
|
||||||
last_pointer_pos: None,
|
last_pointer_pos: None,
|
||||||
is_dragging: false,
|
is_dragging: false,
|
||||||
text_cache: None,
|
text_cache: None,
|
||||||
|
did_fit_start: false,
|
||||||
|
last_cursor_pos: None,
|
||||||
|
reset_view_requested: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,9 +195,7 @@ impl eframe::App for TemplateApp {
|
||||||
|
|
||||||
ui.label(format!("Zoom: {:.0}%", self.zoom * 100.0));
|
ui.label(format!("Zoom: {:.0}%", self.zoom * 100.0));
|
||||||
if ui.button("Reset View").clicked() {
|
if ui.button("Reset View").clicked() {
|
||||||
self.pan_x = 0.0;
|
self.reset_view_requested = true;
|
||||||
self.pan_y = 0.0;
|
|
||||||
self.zoom = 1.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
@ -214,6 +227,40 @@ impl eframe::App for TemplateApp {
|
||||||
let canvas_rect = response.rect;
|
let canvas_rect = response.rect;
|
||||||
painter.rect_filled(canvas_rect, 0.0, background_color);
|
painter.rect_filled(canvas_rect, 0.0, background_color);
|
||||||
|
|
||||||
|
if !self.did_fit_start {
|
||||||
|
self.reset_view_requested = true;
|
||||||
|
self.did_fit_start = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut maybe_fit_view = |rect: &egui::Rect| {
|
||||||
|
if let Some(ref content) = self.svg_content {
|
||||||
|
if let Some((sx, sy, sw, sh)) = content.start_rect.or(content.viewbox) {
|
||||||
|
if sw > 0.0 && sh > 0.0 {
|
||||||
|
let scale_x = rect.width() / sw;
|
||||||
|
let scale_y = rect.height() / sh;
|
||||||
|
let fit_zoom = scale_x.min(scale_y).clamp(0.01, 100.0);
|
||||||
|
let visible_w = rect.width() / fit_zoom;
|
||||||
|
let visible_h = rect.height() / fit_zoom;
|
||||||
|
|
||||||
|
self.zoom = fit_zoom;
|
||||||
|
self.pan_x = -sx + (visible_w - sw) * 0.5;
|
||||||
|
self.pan_y = -sy + (visible_h - sh) * 0.5;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.reset_view_requested {
|
||||||
|
if maybe_fit_view(&canvas_rect) {
|
||||||
|
self.reset_view_requested = false;
|
||||||
|
} else {
|
||||||
|
// Nothing to fit, still clear the flag to avoid loops
|
||||||
|
self.reset_view_requested = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Drag handling
|
// Drag handling
|
||||||
if primary_pressed && response.hovered() {
|
if primary_pressed && response.hovered() {
|
||||||
self.is_dragging = true;
|
self.is_dragging = true;
|
||||||
|
|
@ -257,8 +304,7 @@ impl eframe::App for TemplateApp {
|
||||||
let internal_alpha = (0.3_f32 * 255.0).round() as u8;
|
let internal_alpha = (0.3_f32 * 255.0).round() as u8;
|
||||||
let video_scroll_color =
|
let video_scroll_color =
|
||||||
egui::Color32::from_rgba_unmultiplied(255, 0, 0, internal_alpha);
|
egui::Color32::from_rgba_unmultiplied(255, 0, 0, internal_alpha);
|
||||||
let audio_area_color =
|
let audio_area_color = egui::Color32::from_rgba_unmultiplied(0, 0, 255, internal_alpha);
|
||||||
egui::Color32::from_rgba_unmultiplied(0, 0, 255, internal_alpha);
|
|
||||||
let anchor_color = egui::Color32::from_rgba_unmultiplied(64, 255, 64, internal_alpha);
|
let anchor_color = egui::Color32::from_rgba_unmultiplied(64, 255, 64, internal_alpha);
|
||||||
let text_color = egui::Color32::from_rgb(255, 255, 255);
|
let text_color = egui::Color32::from_rgb(255, 255, 255);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! SVG parsing module for extracting special elements from SVG files.
|
//! SVG parsing module for extracting special elements from SVG files.
|
||||||
|
|
||||||
use quick_xml::Reader;
|
|
||||||
use quick_xml::events::Event;
|
use quick_xml::events::Event;
|
||||||
|
use quick_xml::Reader;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
/// Trait for elements that can be rendered with a bounding box.
|
/// Trait for elements that can be rendered with a bounding box.
|
||||||
|
|
@ -114,6 +114,7 @@ pub struct SvgContent {
|
||||||
pub texts: Vec<TextElement>,
|
pub texts: Vec<TextElement>,
|
||||||
pub viewbox: Option<(f32, f32, f32, f32)>, // (min_x, min_y, width, height)
|
pub viewbox: Option<(f32, f32, f32, f32)>, // (min_x, min_y, width, height)
|
||||||
pub background_color: Option<[u8; 3]>,
|
pub background_color: Option<[u8; 3]>,
|
||||||
|
pub start_rect: Option<(f32, f32, f32, f32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State for tracking current element during parsing.
|
/// State for tracking current element during parsing.
|
||||||
|
|
@ -287,6 +288,8 @@ impl SvgContent {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
});
|
});
|
||||||
|
} else if id == "start" {
|
||||||
|
svg_content.start_rect = Some((x, y, width, height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"text" => {
|
"text" => {
|
||||||
|
|
@ -364,6 +367,8 @@ impl SvgContent {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
});
|
});
|
||||||
|
} else if id == "start" {
|
||||||
|
svg_content.start_rect = Some((x, y, width, height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue