From 60d423157555cd9d19574f2ee947c6c36ebbf45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kav=C3=ADk?= Date: Tue, 11 Jun 2024 22:42:14 +0200 Subject: [PATCH] zoom_or_pan, fix first row width --- frontend/src/platform.rs | 6 +-- frontend/src/platform/browser.rs | 6 +-- frontend/src/platform/tauri.rs | 10 ++--- frontend/src/waveform_panel.rs | 2 +- frontend/src/waveform_panel/pixi_canvas.rs | 32 ++++++++++----- frontend/typescript/bundles/pixi_canvas.js | 37 +++++++++-------- frontend/typescript/bundles/tauri_glue.js | 4 +- .../typescript/pixi_canvas/pixi_canvas.ts | 41 +++++++++++-------- frontend/typescript/tauri_glue/tauri_glue.ts | 4 +- shared/src/lib.rs | 2 +- shared/src/signal_to_timeline.rs | 21 ++++------ src-tauri/src/lib.rs | 6 +-- 12 files changed, 92 insertions(+), 79 deletions(-) diff --git a/frontend/src/platform.rs b/frontend/src/platform.rs index fbff934..58903f9 100644 --- a/frontend/src/platform.rs +++ b/frontend/src/platform.rs @@ -31,15 +31,15 @@ pub async fn get_hierarchy() -> wellen::Hierarchy { pub async fn load_signal_and_get_timeline( signal_ref: wellen::SignalRef, - timeline_width: u32, + timeline_zoom: f64, timeline_viewport_width: u32, - timeline_viewport_x: u32, + timeline_viewport_x: i32, block_height: u32, var_format: shared::VarFormat, ) -> shared::Timeline { platform::load_signal_and_get_timeline( signal_ref, - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height, diff --git a/frontend/src/platform/browser.rs b/frontend/src/platform/browser.rs index bfe9527..796f007 100644 --- a/frontend/src/platform/browser.rs +++ b/frontend/src/platform/browser.rs @@ -76,9 +76,9 @@ pub(super) async fn get_hierarchy() -> wellen::Hierarchy { pub(super) async fn load_signal_and_get_timeline( signal_ref: wellen::SignalRef, - timeline_width: u32, + timeline_zoom: f64, timeline_viewport_width: u32, - timeline_viewport_x: u32, + timeline_viewport_x: i32, block_height: u32, var_format: shared::VarFormat, ) -> shared::Timeline { @@ -91,7 +91,7 @@ pub(super) async fn load_signal_and_get_timeline( shared::signal_to_timeline( signal, time_table, - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height, diff --git a/frontend/src/platform/tauri.rs b/frontend/src/platform/tauri.rs index 43f48f4..30300be 100644 --- a/frontend/src/platform/tauri.rs +++ b/frontend/src/platform/tauri.rs @@ -19,9 +19,9 @@ pub(super) async fn get_hierarchy() -> wellen::Hierarchy { pub(super) async fn load_signal_and_get_timeline( signal_ref: wellen::SignalRef, - timeline_width: u32, + timeline_zoom: f64, timeline_viewport_width: u32, - timeline_viewport_x: u32, + timeline_viewport_x: i32, block_height: u32, var_format: shared::VarFormat, ) -> shared::Timeline { @@ -29,7 +29,7 @@ pub(super) async fn load_signal_and_get_timeline( serde_wasm_bindgen::from_value( tauri_glue::load_signal_and_get_timeline( signal_ref.index(), - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height, @@ -65,9 +65,9 @@ mod tauri_glue { #[wasm_bindgen(catch)] pub async fn load_signal_and_get_timeline( signal_ref_index: usize, - timeline_width: u32, + timeline_zoom: f64, timeline_viewport_width: u32, - timeline_viewport_x: u32, + timeline_viewport_x: i32, block_height: u32, var_format: JsValue, ) -> Result; diff --git a/frontend/src/waveform_panel.rs b/frontend/src/waveform_panel.rs index e8a95a6..db09a44 100644 --- a/frontend/src/waveform_panel.rs +++ b/frontend/src/waveform_panel.rs @@ -122,7 +122,7 @@ impl WaveformPanel { let signal_ref = var.signal_ref(); let timeline = platform::load_signal_and_get_timeline( signal_ref, - controller.get_timeline_width(), + controller.get_timeline_zoom(), controller.get_timeline_viewport_width(), controller.get_timeline_viewport_x(), ROW_HEIGHT, diff --git a/frontend/src/waveform_panel/pixi_canvas.rs b/frontend/src/waveform_panel/pixi_canvas.rs index 82390b3..eb9f26d 100644 --- a/frontend/src/waveform_panel/pixi_canvas.rs +++ b/frontend/src/waveform_panel/pixi_canvas.rs @@ -40,6 +40,7 @@ impl PixiCanvas { let width = width.signal(), let height = height.signal() => (*width, *height) } + .dedupe() .throttle(|| Timer::sleep(50)) .for_each( clone!((controller) move |(width, height)| clone!((controller) async move { @@ -52,12 +53,12 @@ impl PixiCanvas { let task_with_controller = Mutable::new(None); // -- FastWave-specific -- let timeline_getter = Rc::new(Closure::new( - |signal_ref_index, timeline_width, timeline_viewport_width, timeline_viewport_x, row_height, var_format| { + |signal_ref_index, timeline_zoom, timeline_viewport_width, timeline_viewport_x, row_height, var_format| { future_to_promise(async move { let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap_throw(); let timeline = platform::load_signal_and_get_timeline( signal_ref, - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, row_height, @@ -81,10 +82,18 @@ impl PixiCanvas { width.set_neq(new_width); height.set_neq(new_height); })) + .update_raw_el(|raw_el| { + // @TODO rewrite to a native Zoon API + raw_el.event_handler(clone!((controller) move |event: events_extra::WheelEvent| { + if let Some(controller) = controller.lock_ref().as_ref() { + controller.zoom_or_pan(event.delta_y(), event.shift_key()); + } + })) + }) .after_insert(clone!((controller, timeline_getter) move |element| { Task::start(async move { let pixi_controller = js_bridge::PixiController::new( - width.get(), + 1., width.get(), 0, row_height, @@ -122,13 +131,13 @@ mod js_bridge { type TimelinePromise = js_sys::Promise; type SignalRefIndex = usize; - type TimelineWidth = u32; + type TimelineZoom = f64; type TimelineViewportWidth = u32; - type TimelineViewportX = u32; + type TimelineViewportX = i32; type RowHeight = u32; type VarFormatJs = JsValue; type TimelineGetter = - Closure TimelinePromise>; + Closure TimelinePromise>; // Note: Add all corresponding methods to `frontend/typescript/pixi_canvas/pixi_canvas.ts` #[wasm_bindgen(module = "/typescript/bundles/pixi_canvas.js")] @@ -139,9 +148,9 @@ mod js_bridge { // @TODO `row_height` and `row_gap` is FastWave-specific #[wasm_bindgen(constructor)] pub fn new( - timeline_width: u32, + timeline_zoom: f64, timeline_viewport_width: u32, - timeline_viewport_x: u32, + timeline_viewport_x: i32, row_height: u32, row_gap: u32, timeline_getter: &TimelineGetter, @@ -157,19 +166,22 @@ mod js_bridge { pub fn destroy(this: &PixiController); #[wasm_bindgen(method)] - pub fn get_timeline_width(this: &PixiController) -> u32; + pub fn get_timeline_zoom(this: &PixiController) -> f64; #[wasm_bindgen(method)] pub fn get_timeline_viewport_width(this: &PixiController) -> u32; #[wasm_bindgen(method)] - pub fn get_timeline_viewport_x(this: &PixiController) -> u32; + pub fn get_timeline_viewport_x(this: &PixiController) -> i32; // -- FastWave-specific -- #[wasm_bindgen(method)] pub fn set_var_format(this: &PixiController, index: usize, var_format: JsValue); + #[wasm_bindgen(method)] + pub fn zoom_or_pan(this: &PixiController, wheel_delta_y: f64, shift_key: bool); + #[wasm_bindgen(method)] pub fn remove_var(this: &PixiController, index: usize); diff --git a/frontend/typescript/bundles/pixi_canvas.js b/frontend/typescript/bundles/pixi_canvas.js index c4b6731..4e4ad9d 100644 --- a/frontend/typescript/bundles/pixi_canvas.js +++ b/frontend/typescript/bundles/pixi_canvas.js @@ -35133,22 +35133,21 @@ var PixiController = class { // -- FastWave-specific -- var_signal_rows = []; var_signal_rows_container = new Container(); - timeline_width; + // @TODO reset `timeline_*` on file unload? + timeline_zoom; timeline_viewport_width; timeline_viewport_x; row_height; row_gap; - previous_parent_width; timeline_getter; - constructor(timeline_width, timeline_viewport_width, timeline_viewport_x, row_height, row_gap, timeline_getter) { + constructor(timeline_zoom, timeline_viewport_width, timeline_viewport_x, row_height, row_gap, timeline_getter) { this.app = new Application(); - this.timeline_width = timeline_width; + this.timeline_zoom = timeline_zoom; this.timeline_viewport_width = timeline_viewport_width; this.timeline_viewport_x = timeline_viewport_x; this.row_height = row_height; this.row_gap = row_gap; this.app.stage.addChild(this.var_signal_rows_container); - this.previous_parent_width = null; this.timeline_getter = timeline_getter; } async init(parent_element) { @@ -35157,14 +35156,10 @@ var PixiController = class { } // Default automatic Pixi resizing according to the parent is not reliable // and the `app.renderer`'s `resize` event is fired on every browser window size change - async resize(width, height) { - this.app.resize(); - const width_changed = width !== this.previous_parent_width; - this.previous_parent_width = width; - if (width_changed) { - this.timeline_viewport_width = width; - await this.redraw_all_rows(); - } + async resize(width, _height) { + this.timeline_viewport_width = width; + await this.redraw_all_rows(); + this.app.queueResize(); } destroy() { const rendererDestroyOptions = { @@ -35178,8 +35173,8 @@ var PixiController = class { }; this.app.destroy(rendererDestroyOptions, options); } - get_timeline_width() { - return this.timeline_width; + get_timeline_zoom() { + return this.timeline_zoom; } get_timeline_viewport_width() { return this.timeline_viewport_width; @@ -35192,7 +35187,7 @@ var PixiController = class { await Promise.all(this.var_signal_rows.map(async (row) => { const timeline = await this.timeline_getter( row.signal_ref_index, - this.timeline_width, + this.timeline_zoom, this.timeline_viewport_width, this.timeline_viewport_x, this.row_height, @@ -35206,7 +35201,7 @@ var PixiController = class { if (typeof row !== "undefined") { const timeline = await this.timeline_getter( row.signal_ref_index, - this.timeline_width, + this.timeline_zoom, this.timeline_viewport_width, this.timeline_viewport_x, this.row_height, @@ -35222,6 +35217,14 @@ var PixiController = class { this.redraw_row(index); } } + async zoom_or_pan(wheel_delta_y, shift_key) { + if (shift_key) { + this.timeline_viewport_x -= Math.sign(wheel_delta_y) * 20; + } else { + this.timeline_zoom -= Math.sign(wheel_delta_y) * 0.1; + } + this.redraw_all_rows(); + } remove_var(index) { if (typeof this.var_signal_rows[index] !== "undefined") { this.var_signal_rows[index].destroy(); diff --git a/frontend/typescript/bundles/tauri_glue.js b/frontend/typescript/bundles/tauri_glue.js index c24c802..a81bef0 100644 --- a/frontend/typescript/bundles/tauri_glue.js +++ b/frontend/typescript/bundles/tauri_glue.js @@ -2520,10 +2520,10 @@ async function pick_and_load_waveform() { async function get_hierarchy() { return await invoke2("get_hierarchy"); } -async function load_signal_and_get_timeline(signal_ref_index, timeline_width, timeline_viewport_width, timeline_viewport_x, block_height, var_format) { +async function load_signal_and_get_timeline(signal_ref_index, timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height, var_format) { return await invoke2("load_signal_and_get_timeline", { signal_ref_index, - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height, diff --git a/frontend/typescript/pixi_canvas/pixi_canvas.ts b/frontend/typescript/pixi_canvas/pixi_canvas.ts index 4d24b6b..bf3340c 100644 --- a/frontend/typescript/pixi_canvas/pixi_canvas.ts +++ b/frontend/typescript/pixi_canvas/pixi_canvas.ts @@ -29,7 +29,7 @@ enum VarFormat { type TimelineGetter = ( signal_ref_index: number, - timeline_width: number, + timeline_zoom: number, timeline_viewport_width: number, timeline_viewport_x: number, row_height: number, @@ -41,16 +41,16 @@ export class PixiController { // -- FastWave-specific -- var_signal_rows: Array = []; var_signal_rows_container = new Container(); - timeline_width: number; + // @TODO reset `timeline_*` on file unload? + timeline_zoom: number; timeline_viewport_width: number; timeline_viewport_x: number; row_height: number; row_gap: number; - previous_parent_width: number | null; timeline_getter: TimelineGetter; constructor( - timeline_width: number, + timeline_zoom: number, timeline_viewport_width: number, timeline_viewport_x: number, row_height: number, @@ -59,13 +59,12 @@ export class PixiController { ) { this.app = new Application(); // -- FastWave-specific -- - this.timeline_width = timeline_width; + this.timeline_zoom = timeline_zoom; this.timeline_viewport_width = timeline_viewport_width; this.timeline_viewport_x = timeline_viewport_x; this.row_height = row_height; this.row_gap = row_gap; this.app.stage.addChild(this.var_signal_rows_container); - this.previous_parent_width = null; this.timeline_getter = timeline_getter; } @@ -76,15 +75,12 @@ export class PixiController { // Default automatic Pixi resizing according to the parent is not reliable // and the `app.renderer`'s `resize` event is fired on every browser window size change - async resize(width: number, height: number) { - this.app.resize(); + async resize(width: number, _height: number) { // -- FastWave-specific -- - const width_changed = width !== this.previous_parent_width; - this.previous_parent_width = width; - if (width_changed) { - this.timeline_viewport_width = width; - await this.redraw_all_rows(); - } + this.timeline_viewport_width = width; + await this.redraw_all_rows(); + // -- // -- + this.app.queueResize(); } destroy() { @@ -100,8 +96,8 @@ export class PixiController { this.app.destroy(rendererDestroyOptions, options); } - get_timeline_width() { - return this.timeline_width; + get_timeline_zoom() { + return this.timeline_zoom; } get_timeline_viewport_width() { @@ -118,7 +114,7 @@ export class PixiController { await Promise.all(this.var_signal_rows.map(async row => { const timeline = await this.timeline_getter( row.signal_ref_index, - this.timeline_width, + this.timeline_zoom, this.timeline_viewport_width, this.timeline_viewport_x, this.row_height, @@ -133,7 +129,7 @@ export class PixiController { if (typeof row !== 'undefined') { const timeline = await this.timeline_getter( row.signal_ref_index, - this.timeline_width, + this.timeline_zoom, this.timeline_viewport_width, this.timeline_viewport_x, this.row_height, @@ -151,6 +147,15 @@ export class PixiController { } } + async zoom_or_pan(wheel_delta_y: number, shift_key: boolean) { + if (shift_key) { + this.timeline_viewport_x -= Math.sign(wheel_delta_y) * 20; + } else { + this.timeline_zoom -= Math.sign(wheel_delta_y) * 0.1; + } + this.redraw_all_rows(); + } + remove_var(index: number) { if (typeof this.var_signal_rows[index] !== 'undefined') { this.var_signal_rows[index].destroy(); diff --git a/frontend/typescript/tauri_glue/tauri_glue.ts b/frontend/typescript/tauri_glue/tauri_glue.ts index 37b6d1c..2945a41 100644 --- a/frontend/typescript/tauri_glue/tauri_glue.ts +++ b/frontend/typescript/tauri_glue/tauri_glue.ts @@ -23,7 +23,7 @@ export async function get_hierarchy(): Promise { export async function load_signal_and_get_timeline( signal_ref_index: number, - timeline_width: number, + timeline_zoom: number, timeline_viewport_width: number, timeline_viewport_x: number, block_height: number, @@ -31,7 +31,7 @@ export async function load_signal_and_get_timeline( ): Promise { return await invoke("load_signal_and_get_timeline", { signal_ref_index, - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height, diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 1a6d68a..63f2435 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -17,7 +17,7 @@ pub struct Timeline { #[derive(Serialize, Deserialize, Debug, Default)] #[serde(crate = "serde")] pub struct TimelineBlock { - pub x: u32, + pub x: i32, pub width: u32, pub height: u32, pub label: Option, diff --git a/shared/src/signal_to_timeline.rs b/shared/src/signal_to_timeline.rs index ceea93f..e49ed22 100644 --- a/shared/src/signal_to_timeline.rs +++ b/shared/src/signal_to_timeline.rs @@ -3,39 +3,32 @@ use crate::*; pub fn signal_to_timeline( signal: &wellen::Signal, time_table: &[wellen::Time], - mut timeline_width: u32, + timeline_zoom: f64, timeline_viewport_width: u32, - timeline_viewport_x: u32, + timeline_viewport_x: i32, block_height: u32, var_format: VarFormat, ) -> Timeline { - println!("timeline_width: {timeline_width}"); - println!("timeline_viewport_width: {timeline_viewport_width}"); - println!("timeline_viewport_x: {timeline_viewport_x}"); - println!("_____"); const MIN_BLOCK_WIDTH: u32 = 3; // Courier New, 16px, sync with `label_style` in `pixi_canvas.rs` const LETTER_WIDTH: f64 = 9.61; const LETTER_HEIGHT: u32 = 18; const LABEL_X_PADDING: u32 = 10; - if timeline_width == 0 { - timeline_width = timeline_viewport_width; - } - let Some(last_time) = time_table.last().copied() else { return Timeline::default(); }; let last_time = last_time as f64; - let timeline_width = timeline_width as f64; + let timeline_viewport_x = timeline_viewport_x as f64; + let timeline_width = timeline_viewport_width as f64 * timeline_zoom; let mut x_value_pairs = signal .iter_changes() .map(|(index, value)| { let index = index as usize; let time = time_table[index] as f64; - let x = time / last_time * timeline_width; + let x = time / last_time * timeline_width + timeline_viewport_x; (x, value) }) .peekable(); @@ -46,7 +39,7 @@ pub fn signal_to_timeline( let next_block_x = if let Some((next_block_x, _)) = x_value_pairs.peek() { *next_block_x } else { - timeline_width + timeline_width + timeline_viewport_x }; let block_width = (next_block_x - block_x) as u32; @@ -70,7 +63,7 @@ pub fn signal_to_timeline( }; let block = TimelineBlock { - x: block_x as u32, + x: block_x as i32, width: block_width, height: block_height, label, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ce5315f..3b45d05 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -42,9 +42,9 @@ async fn get_hierarchy(store: tauri::State<'_, Store>) -> Result, @@ -60,7 +60,7 @@ async fn load_signal_and_get_timeline( shared::signal_to_timeline( signal, time_table, - timeline_width, + timeline_zoom, timeline_viewport_width, timeline_viewport_x, block_height,