@@ -49,6 +49,8 @@ pub enum EngineAction {
4949 OpenFile ,
5050 LoadFiles ( Vec < String > , bool ) ,
5151 Seek ( f32 ) ,
52+ ScrubPreview ( f32 , f64 ) ,
53+ ScrubEnd ,
5254 SetForceStereo ( bool ) ,
5355 #[ allow( dead_code) ]
5456 SetPassthrough ( bool ) ,
@@ -58,7 +60,6 @@ pub enum EngineAction {
5860 VisPickerToggleEnabled ( usize ) ,
5961 VisPickerSetCursor ( usize ) ,
6062 VisPickerEnableAll ,
61- SeekWithScrub ( f32 , f64 ) ,
6263 VisPickerEnableNone ,
6364 OpenUrlDialog ,
6465 CloseUrlDialog ,
@@ -192,8 +193,8 @@ struct MeshBuffers {
192193 index_count : u32 ,
193194}
194195
195- pub struct VulkanEngine < ' a > {
196- surface : wgpu:: Surface < ' a > ,
196+ pub struct VulkanEngine {
197+ surface : Option < wgpu:: Surface < ' static > > ,
197198 device : wgpu:: Device ,
198199 queue : wgpu:: Queue ,
199200 pub config : wgpu:: SurfaceConfiguration ,
@@ -386,7 +387,7 @@ pub(crate) fn generate_lamp_mesh() -> (Vec<Vertex>, Vec<u32>) {
386387 ( vertices, indices)
387388}
388389
389- impl < ' a > VulkanEngine < ' a > {
390+ impl VulkanEngine {
390391 pub async fn new ( window : Arc < Window > ) -> Self {
391392 let size = window. inner_size ( ) ;
392393
@@ -2008,7 +2009,7 @@ impl<'a> VulkanEngine<'a> {
20082009 // --- END 3D Engine Extensions Init ---
20092010
20102011 Self {
2011- surface,
2012+ surface : Some ( surface ) ,
20122013 device,
20132014 queue,
20142015 config,
@@ -2104,7 +2105,9 @@ impl<'a> VulkanEngine<'a> {
21042105 self . size = new_size;
21052106 self . config . width = new_size. width ;
21062107 self . config . height = new_size. height ;
2107- self . surface . configure ( & self . device , & self . config ) ;
2108+ if let Some ( surface) = & self . surface {
2109+ surface. configure ( & self . device , & self . config ) ;
2110+ }
21082111
21092112 let depth_texture = self . device . create_texture ( & wgpu:: TextureDescriptor {
21102113 label : Some ( "Depth Texture" ) ,
@@ -2120,6 +2123,7 @@ impl<'a> VulkanEngine<'a> {
21202123 }
21212124 }
21222125
2126+
21232127 pub fn clear_video_state ( & mut self ) {
21242128 self . video_state = None ;
21252129 }
@@ -2171,7 +2175,7 @@ impl<'a> VulkanEngine<'a> {
21712175 display_order : [ 0 ; 16 ] ,
21722176 num_channels : state. channel_vus . len ( ) . min ( 32 ) as u32 ,
21732177 mode : state. visualizer_mode ,
2174- time : state. current_seconds as f32 ,
2178+ time : state. scrub_target_seconds . unwrap_or ( state . current_seconds ) as f32 ,
21752179 duration : state. duration_seconds as f32 ,
21762180 smooth_time : self . play_time as f32 ,
21772181 heatmap_row : self . heatmap_row ,
@@ -2529,8 +2533,13 @@ impl<'a> VulkanEngine<'a> {
25292533 file_dialog : & mut egui_file_dialog:: FileDialog ,
25302534 gamepad_events : Vec < egui:: Event >
25312535 ) -> Result < ( EngineAction , f32 , f32 , Option < f32 > , Option < f32 > , Option < f32 > , f32 , f32 , f32 , f32 ) , wgpu:: SurfaceStatus > {
2536+ let physical_size = window. inner_size ( ) ;
2537+ if physical_size. width > 0 && physical_size. height > 0 && physical_size != self . size {
2538+ self . resize ( physical_size) ;
2539+ }
25322540 let surface_start = std:: time:: Instant :: now ( ) ;
2533- let output = self . surface . get_current_texture ( ) ;
2541+ let surface = self . surface . as_ref ( ) . ok_or ( wgpu:: SurfaceStatus :: Lost ) ?;
2542+ let output = surface. get_current_texture ( ) ;
25342543 let surface_texture = match output {
25352544 wgpu:: CurrentSurfaceTexture :: Success ( tex) | wgpu:: CurrentSurfaceTexture :: Suboptimal ( tex) => tex,
25362545 wgpu:: CurrentSurfaceTexture :: Lost => return Err ( wgpu:: SurfaceStatus :: Lost ) ,
@@ -3580,12 +3589,20 @@ impl<'a> VulkanEngine<'a> {
35803589 let ( rect, response) = columns[ 0 ] . allocate_exact_size ( egui:: vec2 ( columns[ 0 ] . available_width ( ) , 16.0 ) , egui:: Sense :: click_and_drag ( ) ) ;
35813590 out_fire_rect = Some ( rect) ;
35823591
3583- if response. dragged ( ) || response. clicked ( ) {
3592+ if response. drag_stopped ( ) || response. clicked ( ) {
35843593 if let Some ( mouse_pos) = response. interact_pointer_pos ( ) {
35853594 let rel_x = ( mouse_pos. x - rect. left ( ) ) . clamp ( 0.0 , rect. width ( ) ) ;
35863595 let pct = rel_x / rect. width ( ) ;
35873596 engine_action = EngineAction :: Seek ( pct) ;
35883597 }
3598+ } else if response. dragged ( ) {
3599+ if let Some ( mouse_pos) = response. interact_pointer_pos ( ) {
3600+ let rel_x = ( mouse_pos. x - rect. left ( ) ) . clamp ( 0.0 , rect. width ( ) ) ;
3601+ let pct = rel_x / rect. width ( ) ;
3602+ engine_action = EngineAction :: ScrubPreview ( pct, 0.0 ) ;
3603+ }
3604+ } else if !response. is_pointer_button_down_on ( ) {
3605+ engine_action = EngineAction :: ScrubEnd ;
35893606 }
35903607
35913608 let painter = columns[ 0 ] . painter ( ) ;
@@ -3596,10 +3613,11 @@ impl<'a> VulkanEngine<'a> {
35963613 format ! ( "{:02}:{:02}.{}" , m, s, f)
35973614 } ;
35983615
3616+ let display_secs = state. scrub_target_seconds . unwrap_or ( state. current_seconds ) ;
35993617 let time_text = if state. duration_seconds <= 0.0 {
3600- format ! ( "{} / LIVE" , format_time( state . current_seconds ) )
3618+ format ! ( "{} / LIVE" , format_time( display_secs ) )
36013619 } else {
3602- format ! ( "{} / {}" , format_time( state . current_seconds ) , format_time( state. duration_seconds) )
3620+ format ! ( "{} / {}" , format_time( display_secs ) , format_time( state. duration_seconds) )
36033621 } ;
36043622
36053623 painter. text (
@@ -3983,14 +4001,29 @@ impl<'a> VulkanEngine<'a> {
39834001 central_rect = rect;
39844002
39854003 // Mouse drag scrubbing using global input to avoid interception by other transparent areas
3986- #[ allow( deprecated) ]
3987- let wants_pointer = ui. ctx ( ) . wants_pointer_input ( ) ;
3988- if ui. input ( |i| i. pointer . primary_down ( ) ) && !wants_pointer && state. duration_seconds > 0.0 {
3989- let delta_x = ui. input ( |i| i. pointer . delta ( ) . x ) ;
3990- if delta_x. abs ( ) > 0.0 {
3991- let scrub_amount = delta_x * 0.1 ; // 0.1s per pixel
3992- let new_time = ( state. current_seconds + scrub_amount as f64 ) . clamp ( 0.0 , state. duration_seconds ) ;
3993- engine_action = EngineAction :: SeekWithScrub ( ( new_time / state. duration_seconds ) as f32 , scrub_amount as f64 ) ;
4004+ let wants_pointer = ui. ctx ( ) . egui_wants_pointer_input ( ) ;
4005+ let is_primary_down = ui. input ( |i| i. pointer . primary_down ( ) ) ;
4006+ let is_primary_released = ui. input ( |i| i. pointer . primary_released ( ) ) ;
4007+
4008+ // If we are currently scrubbing, ignore wants_pointer so we don't miss the release event!
4009+ let is_scrubbing = state. scrub_target_seconds . is_some ( ) ;
4010+ let should_process = ( is_primary_down || is_primary_released) && ( !wants_pointer || is_scrubbing) && state. duration_seconds > 0.0 ;
4011+
4012+ if should_process {
4013+ if is_primary_released {
4014+ if let Some ( target) = state. scrub_target_seconds {
4015+ engine_action = EngineAction :: Seek ( ( target / state. duration_seconds ) as f32 ) ;
4016+ } else {
4017+ engine_action = EngineAction :: ScrubEnd ;
4018+ }
4019+ } else {
4020+ let delta_x = ui. input ( |i| i. pointer . delta ( ) . x ) ;
4021+ if delta_x. abs ( ) > 0.0 || is_scrubbing {
4022+ let scrub_amount = delta_x * 0.1 ; // 0.1s per pixel
4023+ let base_time = state. scrub_target_seconds . unwrap_or ( state. current_seconds ) ;
4024+ let new_time = ( base_time + scrub_amount as f64 ) . clamp ( 0.0 , state. duration_seconds ) ;
4025+ engine_action = EngineAction :: ScrubPreview ( ( new_time / state. duration_seconds ) as f32 , scrub_amount as f64 ) ;
4026+ }
39944027 }
39954028 }
39964029
@@ -4040,7 +4073,7 @@ impl<'a> VulkanEngine<'a> {
40404073
40414074 } ) ;
40424075
4043- let scale = window . scale_factor ( ) as f32 ;
4076+ let scale = egui_ctx . pixels_per_point ( ) ;
40444077 let w = self . config . width as f32 ;
40454078 let h = self . config . height as f32 ;
40464079
@@ -4074,7 +4107,7 @@ impl<'a> VulkanEngine<'a> {
40744107
40754108 let screen_descriptor = egui_wgpu:: ScreenDescriptor {
40764109 size_in_pixels : [ self . config . width , self . config . height ] ,
4077- pixels_per_point : window . scale_factor ( ) as f32 ,
4110+ pixels_per_point : egui_ctx . pixels_per_point ( ) ,
40784111 } ;
40794112 let render_start = std:: time:: Instant :: now ( ) ;
40804113
@@ -4270,7 +4303,7 @@ impl<'a> VulkanEngine<'a> {
42704303 multiview_mask : None ,
42714304 } ) ;
42724305
4273- let scale_factor = window . scale_factor ( ) as f32 ;
4306+ let scale_factor = egui_ctx . pixels_per_point ( ) ;
42744307 let vp_x = ( ( central_rect. min . x * scale_factor) . clamp ( 0.0 , self . config . width as f32 ) ) . round ( ) ;
42754308 let vp_y = ( ( central_rect. min . y * scale_factor) . clamp ( 0.0 , self . config . height as f32 ) ) . round ( ) ;
42764309 let max_w = ( self . config . width as f32 - vp_x) . max ( 1.0 ) ;
@@ -4401,10 +4434,20 @@ impl<'a> VulkanEngine<'a> {
44014434 if let Some ( rect) = r {
44024435 let x = ( ( rect. min . x * scale_factor) . clamp ( 0.0 , self . config . width as f32 ) ) . round ( ) as u32 ;
44034436 let y = ( ( rect. min . y * scale_factor) . clamp ( 0.0 , self . config . height as f32 ) ) . round ( ) as u32 ;
4404- let max_w = ( self . config . width as f32 - x as f32 ) . max ( 1.0 ) ;
4405- let w = ( ( rect. width ( ) * scale_factor) . clamp ( 1.0 , max_w) ) . round ( ) as u32 ;
4406- let max_h = ( self . config . height as f32 - y as f32 ) . max ( 1.0 ) ;
4407- let h = ( ( rect. height ( ) * scale_factor) . clamp ( 1.0 , max_h) ) . round ( ) as u32 ;
4437+
4438+ let w = if x < self . config . width {
4439+ let max_w = self . config . width - x;
4440+ ( ( rect. width ( ) * scale_factor) . round ( ) as u32 ) . clamp ( 1 , max_w)
4441+ } else {
4442+ 0
4443+ } ;
4444+
4445+ let h = if y < self . config . height {
4446+ let max_h = self . config . height - y;
4447+ ( ( rect. height ( ) * scale_factor) . round ( ) as u32 ) . clamp ( 1 , max_h)
4448+ } else {
4449+ 0
4450+ } ;
44084451
44094452 if w > 0 && h > 0 {
44104453 render_pass. set_scissor_rect ( x, y, w, h) ;
0 commit comments