From 263daa076324ebf6591d1cd5e0553cbe7b470149 Mon Sep 17 00:00:00 2001 From: accusys Date: Thu, 19 Mar 2026 01:45:21 +0800 Subject: [PATCH] feat(player): add progress bar, fullscreen toggle, Home/End keys --- src/main.rs | 144 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 39 deletions(-) diff --git a/src/main.rs b/src/main.rs index a2089ad..3342d96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ fn run(config: &Config) -> Result<()> { let ttf_context = ttf::init().map_err(|e| anyhow::anyhow!("TTF init failed: {}", e))?; let font: Option = ttf_context - .load_font("/System/Library/Fonts/Supplemental/Arial.ttf", 18) + .load_font("/System/Library/Fonts/Supplemental/Arial.ttf", 16) .ok(); let window = video_subsystem @@ -63,6 +63,7 @@ fn run(config: &Config) -> Result<()> { let mut video_info = None; let mut asr: Option = None; let mut yolo: Option = None; + let mut is_fullscreen = false; if let Some(ref video_path) = config.video { info!("Loading video: {:?}", video_path); @@ -121,8 +122,8 @@ fn run(config: &Config) -> Result<()> { .event_pump() .map_err(|e| anyhow::anyhow!("Event pump failed: {}", e))?; - let font_size = 18; - let info_height: i32 = 50; + let header_height: i32 = 50; + let progress_height: i32 = 30; info!("Main loop started - waiting for events..."); @@ -173,24 +174,47 @@ fn run(config: &Config) -> Result<()> { sdl2::keyboard::Keycode::M => { player_state.muted = !player_state.muted; } + sdl2::keyboard::Keycode::F => { + is_fullscreen = !is_fullscreen; + } + sdl2::keyboard::Keycode::Home => { + if let Some(ref mut dec) = decoder { + dec.seek(0).ok(); + player_state.current_frame = 0; + player_state.current_time_ms = 0; + } + } + sdl2::keyboard::Keycode::End => { + if let Some(ref mut dec) = decoder { + let last_frame = player_state.total_frames.saturating_sub(1); + dec.seek( + ((last_frame as f64 / player_state.fps) * 1000.0) as u64, + ) + .ok(); + player_state.current_frame = last_frame; + player_state.current_time_ms = player_state.duration_ms; + } + } sdl2::keyboard::Keycode::Left => { let step = if shift { 60 } else { 1 }; if let Some(ref mut dec) = decoder { let current = player_state.current_frame.saturating_sub(step); - dec.seek( - ((current as f64 / player_state.fps) * 1000.0) as u64, - )?; + dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64) + .ok(); player_state.current_frame = current; + player_state.current_time_ms = + ((current as f64 / player_state.fps) * 1000.0) as u64; } } sdl2::keyboard::Keycode::Right => { let step = if shift { 60 } else { 1 }; if let Some(ref mut dec) = decoder { let current = player_state.current_frame + step; - dec.seek( - ((current as f64 / player_state.fps) * 1000.0) as u64, - )?; + dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64) + .ok(); player_state.current_frame = current; + player_state.current_time_ms = + ((current as f64 / player_state.fps) * 1000.0) as u64; } } sdl2::keyboard::Keycode::Up => { @@ -240,6 +264,13 @@ fn run(config: &Config) -> Result<()> { canvas.set_draw_color(sdl2::pixels::Color::BLACK); canvas.clear(); + let fs_type = if is_fullscreen { + sdl2::video::FullscreenType::Desktop + } else { + sdl2::video::FullscreenType::Off + }; + canvas.window_mut().set_fullscreen(fs_type).ok(); + if player_state.playback == PlaybackState::Playing { if let Some(ref mut dec) = decoder { if let Some(ref mut tex) = texture { @@ -252,7 +283,8 @@ fn run(config: &Config) -> Result<()> { as u64; tex.update(None, &data, (info.width * 3) as usize) - .map_err(|e| anyhow::anyhow!("Texture update failed: {}", e))?; + .map_err(|e| anyhow::anyhow!("Texture update failed: {}", e)) + .ok(); } } Ok(None) => { @@ -273,11 +305,12 @@ fn run(config: &Config) -> Result<()> { .map(|i| (i.width, i.height)) .unwrap_or((config.width, config.height)); + let video_area_height = config.height as i32 - header_height - progress_height; let scale: f64 = if player_state.zoom != 1.0 { player_state.zoom as f64 } else { let scale_x = config.width as f64 / vid_width as f64; - let scale_y = (config.height as i32 - info_height) as f64 / vid_height as f64; + let scale_y = video_area_height as f64 / vid_height as f64; scale_x.min(scale_y).min(1.0) }; @@ -285,7 +318,7 @@ fn run(config: &Config) -> Result<()> { let scaled_h = (vid_height as f64 * scale) as u32; let offset_x = ((config.width as i32 - scaled_w as i32) / 2) as i32 + player_state.pan_x as i32; - let offset_y = ((config.height as i32 - info_height - scaled_h as i32) / 2) as i32 + let offset_y = ((config.height as i32 - progress_height - scaled_h as i32) / 2) as i32 + player_state.pan_y as i32; if let Some(ref mut tex) = texture { @@ -316,7 +349,7 @@ fn run(config: &Config) -> Result<()> { texture_creator.create_texture_from_surface(&surface) { let lw = surface.width().min(w as u32); - let label_rect = Rect::new(x1, y1.saturating_sub(20), lw, 18); + let label_rect = Rect::new(x1, y1.saturating_sub(18), lw, 16); canvas.copy(&tex_label, None, Some(label_rect)).ok(); } } @@ -339,14 +372,18 @@ fn run(config: &Config) -> Result<()> { { let query = tex_label.query(); let x = (config.width - query.width) / 2; - let y = config.height - query.height - 20; - let rect = Rect::new(x as i32, y as i32, query.width, query.height); + let y = config.height as i32 + - progress_height + - query.height as i32 + - 10; + let rect = + Rect::new(x as i32, y as i32, query.width as u32, query.height); canvas.set_draw_color(sdl2::pixels::Color::RGBA(0, 0, 0, 200)); let _ = canvas.fill_rect(Rect::new( - rect.x() - 8, - rect.y() - 4, - rect.width() + 16, - rect.height() + 8, + rect.x() - 6, + rect.y() - 3, + rect.width() + 12, + rect.height() + 6, )); canvas.copy(&tex_label, None, Some(rect)).ok(); } @@ -358,37 +395,66 @@ fn run(config: &Config) -> Result<()> { if let Some(ref f) = font { let time_str = format_time(player_state.current_time_ms); + let duration_str = format_time(player_state.duration_ms); let line1 = format!( - "Time: {} Frame: {}/{} FPS: {:.1}", - time_str, player_state.current_frame, player_state.total_frames, player_state.fps + "{} / {} Frame: {}/{} {:.1}fps", + time_str, + duration_str, + player_state.current_frame, + player_state.total_frames, + player_state.fps ); let line2 = format!( - "[S]ubtitle:{} [Y]OLO:{} [C]hunks [M]ute [+/-]Zoom{:.1}x [`]Reset", + "[S]ub {} [Y]olo {} [C]hunks [M]ute [+/-]Zoom{:.1}x [F]ullscreen [`]Reset", if player_state.show_subtitle { - " ON " + "ON" } else { - " OFF" - }, - if player_state.show_yolo { - " ON " - } else { - " OFF" + "OFF" }, + if player_state.show_yolo { "ON" } else { "OFF" }, player_state.zoom ); - for (i, line) in [line1, line2].iter().enumerate() { - if let Ok(surface) = f - .render(line) - .solid(sdl2::pixels::Color::RGB(180, 180, 180)) - { - if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) { - let rect = - Rect::new(10, 8 + (i as i32 * 20), surface.width(), surface.height()); - canvas.copy(&tex_label, None, Some(rect)).ok(); - } + if let Ok(surface) = f + .render(&line1) + .solid(sdl2::pixels::Color::RGB(200, 200, 200)) + { + if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) { + let rect = Rect::new(10, 10, surface.width(), surface.height()); + canvas.copy(&tex_label, None, Some(rect)).ok(); } } + if let Ok(surface) = f + .render(&line2) + .solid(sdl2::pixels::Color::RGB(150, 150, 150)) + { + if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) { + let rect = Rect::new(10, 30, surface.width(), surface.height()); + canvas.copy(&tex_label, None, Some(rect)).ok(); + } + } + } + + let progress_y = config.height as i32 - progress_height + 5; + let progress_bar_height = 8i32; + canvas.set_draw_color(sdl2::pixels::Color::RGB(50, 50, 50)); + let _ = canvas.fill_rect(Rect::new( + 10, + progress_y, + (config.width - 20) as u32, + progress_bar_height as u32, + )); + + if player_state.total_frames > 0 { + let progress = player_state.current_frame as f64 / player_state.total_frames as f64; + let progress_width = ((config.width - 20) as f64 * progress) as u32; + canvas.set_draw_color(sdl2::pixels::Color::RGB(100, 150, 255)); + let _ = canvas.fill_rect(Rect::new( + 10, + progress_y, + progress_width, + progress_bar_height as u32, + )); } canvas.present();