@@ -64,6 +64,8 @@ pub enum EngineAction {
6464 CloseUrlDialog ,
6565 SetUrlInput ( String ) ,
6666 LoadUrl ( String ) ,
67+ EditUrl ( String ) ,
68+ ClearFocusUrlInput ,
6769}
6870
6971#[ repr( C ) ]
@@ -1812,27 +1814,56 @@ impl<'a> VulkanEngine<'a> {
18121814 egui:: Window :: new ( "Open Network Stream" )
18131815 . collapsible ( false )
18141816 . resizable ( false )
1817+ . default_width ( 600.0 )
18151818 . anchor ( egui:: Align2 :: CENTER_CENTER , [ 0.0 , 0.0 ] )
18161819 . show ( ctx, |ui| {
18171820 ui. label ( "Enter Stream URL (e.g. .pls, .m3u, or direct stream link):" ) ;
1821+ ui. add_space ( 4.0 ) ;
18181822 let mut url_text = state. url_input_text . clone ( ) ;
1819- let text_resp = ui. add_sized ( [ 400.0 , 30.0 ] , egui:: TextEdit :: singleline ( & mut url_text) ) ;
1823+ let text_resp = ui. add (
1824+ egui:: TextEdit :: singleline ( & mut url_text)
1825+ . desired_width ( f32:: INFINITY )
1826+ . min_size ( egui:: vec2 ( 0.0 , 30.0 ) )
1827+ ) ;
1828+ if state. focus_url_input {
1829+ text_resp. request_focus ( ) ;
1830+ engine_action = EngineAction :: ClearFocusUrlInput ;
1831+ }
18201832 if text_resp. changed ( ) {
18211833 engine_action = EngineAction :: SetUrlInput ( url_text. clone ( ) ) ;
18221834 }
18231835 ui. add_space ( 10.0 ) ;
18241836 ui. horizontal ( |ui| {
1825- ui. allocate_ui_with_layout ( ui. available_size ( ) , egui:: Layout :: centered_and_justified ( egui:: Direction :: LeftToRight ) , |ui| {
1826- if ui. button ( "Cancel" ) . clicked ( ) {
1827- engine_action = EngineAction :: CloseUrlDialog ;
1828- }
1829- if ui. button ( "Open" ) . clicked ( ) || ( text_resp. lost_focus ( ) && ui. input ( |i| i. key_pressed ( egui:: Key :: Enter ) ) ) {
1837+ ui. with_layout ( egui:: Layout :: right_to_left ( egui:: Align :: Center ) , |ui| {
1838+ if ui. add_sized ( [ 80.0 , 30.0 ] , egui:: Button :: new ( "Open" ) ) . clicked ( ) || ( text_resp. lost_focus ( ) && ui. input ( |i| i. key_pressed ( egui:: Key :: Enter ) ) ) {
18301839 if !url_text. is_empty ( ) {
18311840 engine_action = EngineAction :: LoadUrl ( url_text) ;
18321841 }
18331842 }
1843+ if ui. add_sized ( [ 80.0 , 30.0 ] , egui:: Button :: new ( "Cancel" ) ) . clicked ( ) {
1844+ engine_action = EngineAction :: CloseUrlDialog ;
1845+ }
18341846 } ) ;
18351847 } ) ;
1848+
1849+ if !state. url_history . is_empty ( ) {
1850+ ui. separator ( ) ;
1851+ ui. label ( egui:: RichText :: new ( "Recent Streams:" ) . strong ( ) ) ;
1852+ egui:: ScrollArea :: vertical ( ) . max_height ( 150.0 ) . show ( ui, |ui| {
1853+ for ( url, title) in state. url_history . iter ( ) . rev ( ) {
1854+ ui. horizontal ( |ui| {
1855+ let width = ui. available_width ( ) - 38.0 ;
1856+ let btn = egui:: Button :: new ( format ! ( "{} ({})" , title, url) ) . truncate ( ) ;
1857+ if ui. add_sized ( [ width. max ( 0.0 ) , 30.0 ] , btn) . clicked ( ) {
1858+ engine_action = EngineAction :: LoadUrl ( url. clone ( ) ) ;
1859+ }
1860+ if ui. add_sized ( [ 30.0 , 30.0 ] , egui:: Button :: new ( "📝" ) ) . clicked ( ) {
1861+ engine_action = EngineAction :: EditUrl ( url. clone ( ) ) ;
1862+ }
1863+ } ) ;
1864+ }
1865+ } ) ;
1866+ }
18361867 } ) ;
18371868 }
18381869
@@ -2179,15 +2210,13 @@ impl<'a> VulkanEngine<'a> {
21792210 }
21802211
21812212 let mut append = state. append_to_playlist ;
2182- // Only update the file dialog UI when it's actually open to avoid layout overhead
2183- if state. is_file_picker_open || * file_dialog. state ( ) != egui_file_dialog:: DialogState :: Closed {
2184- file_dialog. update_with_right_panel_ui ( ctx, & mut |ui, _fd| {
2185- ui. add_space ( 10.0 ) ;
2186- ui. heading ( "Options" ) ;
2187- ui. separator ( ) ;
2188- ui. checkbox ( & mut append, "Add to Playlist instead of replacing" ) ;
2189- } ) ;
2190- }
2213+ file_dialog. update_with_right_panel_ui ( ctx, & mut |ui, _fd| {
2214+ ui. add_space ( 10.0 ) ;
2215+ ui. heading ( "Options" ) ;
2216+ ui. separator ( ) ;
2217+ ui. checkbox ( & mut append, "Add to Playlist instead of replacing" ) ;
2218+ } ) ;
2219+
21912220 if append != state. append_to_playlist {
21922221 engine_action = EngineAction :: SetAppendToPlaylist ( append) ;
21932222 }
@@ -2388,7 +2417,6 @@ impl<'a> VulkanEngine<'a> {
23882417
23892418 egui:: Area :: new ( egui:: Id :: new ( "splash_shortcuts_area" ) )
23902419 . anchor ( egui:: Align2 :: CENTER_BOTTOM , egui:: vec2 ( 0.0 , -40.0 ) )
2391- . order ( egui:: Order :: Foreground )
23922420 . show ( ctx, |ui| {
23932421 egui:: Frame :: NONE
23942422 . fill ( egui:: Color32 :: from_black_alpha ( 200 ) )
@@ -2595,13 +2623,12 @@ impl<'a> VulkanEngine<'a> {
25952623 }
25962624 ui. add_space ( 20.0 ) ;
25972625
2598- if !is_game_mode {
25992626 egui:: Frame :: NONE
26002627 . shadow ( egui:: Shadow { offset : [ 0 , 4 ] , blur : 12 , spread : 0 , color : egui:: Color32 :: from_black_alpha ( 200 ) } )
26012628 . corner_radius ( 6.0 )
26022629 . show ( ui, |ui| {
26032630 ui. horizontal ( |ui| {
2604- let total_width = 300.0 + 20.0 + 250 .0;
2631+ let total_width = 300.0 + 20.0 + 300 .0;
26052632 ui. add_space ( ( ui. available_width ( ) - total_width) / 2.0 ) ;
26062633
26072634 let btn = egui:: Button :: new (
@@ -2613,7 +2640,11 @@ impl<'a> VulkanEngine<'a> {
26132640 . fill ( egui:: Color32 :: from_rgb ( 0 , 100 , 200 ) )
26142641 . stroke ( egui:: Stroke :: new ( 1.0 , egui:: Color32 :: from_rgb ( 80 , 180 , 255 ) ) ) ;
26152642
2616- if ui. add_sized ( [ 300.0 , 60.0 ] , btn) . clicked ( ) {
2643+ let resp1 = ui. add_sized ( [ 300.0 , 60.0 ] , btn) ;
2644+ if resp1. has_focus ( ) {
2645+ ui. painter ( ) . rect ( resp1. rect . expand ( 2.0 ) , 6.0 , egui:: Color32 :: TRANSPARENT , egui:: Stroke :: new ( 3.0 , egui:: Color32 :: YELLOW ) , egui:: StrokeKind :: Outside ) ;
2646+ }
2647+ if resp1. clicked ( ) {
26172648 engine_action = EngineAction :: OpenFile ;
26182649 }
26192650
@@ -2628,14 +2659,17 @@ impl<'a> VulkanEngine<'a> {
26282659 . fill ( egui:: Color32 :: from_rgb ( 100 , 50 , 150 ) )
26292660 . stroke ( egui:: Stroke :: new ( 1.0 , egui:: Color32 :: from_rgb ( 180 , 80 , 255 ) ) ) ;
26302661
2631- if ui. add_sized ( [ 250.0 , 60.0 ] , url_btn) . clicked ( ) {
2662+ let resp2 = ui. add_sized ( [ 300.0 , 60.0 ] , url_btn) ;
2663+ if resp2. has_focus ( ) {
2664+ ui. painter ( ) . rect ( resp2. rect . expand ( 2.0 ) , 6.0 , egui:: Color32 :: TRANSPARENT , egui:: Stroke :: new ( 3.0 , egui:: Color32 :: YELLOW ) , egui:: StrokeKind :: Outside ) ;
2665+ }
2666+ if resp2. clicked ( ) {
26322667 engine_action = EngineAction :: OpenUrlDialog ;
26332668 }
26342669 } ) ;
26352670 } ) ;
26362671
26372672 ui. add_space ( 30.0 ) ;
2638- }
26392673
26402674 let mut force_stereo = state. force_stereo_downmix ;
26412675 if ui. checkbox ( & mut force_stereo, "Force Stereo Downmix (Fixes crackling on some devices)" ) . changed ( ) {
@@ -2745,10 +2779,16 @@ impl<'a> VulkanEngine<'a> {
27452779 format ! ( "{:02}:{:02}.{}" , m, s, f)
27462780 } ;
27472781
2782+ let time_text = if state. duration_seconds <= 0.0 {
2783+ format ! ( "{} / LIVE" , format_time( state. current_seconds) )
2784+ } else {
2785+ format ! ( "{} / {}" , format_time( state. current_seconds) , format_time( state. duration_seconds) )
2786+ } ;
2787+
27482788 painter. text (
27492789 rect. center ( ) ,
27502790 egui:: Align2 :: CENTER_CENTER ,
2751- format ! ( "{} / {}" , format_time ( state . current_seconds ) , format_time ( state . duration_seconds ) ) ,
2791+ time_text ,
27522792 egui:: FontId :: proportional ( 11.0 ) ,
27532793 egui:: Color32 :: WHITE ,
27542794 ) ;
@@ -2900,7 +2940,7 @@ impl<'a> VulkanEngine<'a> {
29002940 let title_path = std:: path:: Path :: new ( & state. song_title ) ;
29012941 let file_name = title_path. file_name ( ) . unwrap_or_default ( ) . to_string_lossy ( ) . to_string ( ) ;
29022942 let file_dir = title_path. parent ( ) . unwrap_or ( std:: path:: Path :: new ( "" ) ) . to_string_lossy ( ) . to_string ( ) ;
2903- columns[ 2 ] . horizontal ( |ui| { ui. label ( "File" ) ; ui. label ( & file_name) ; } ) ;
2943+ columns[ 2 ] . horizontal ( |ui| { ui. label ( "File/Title " ) ; ui. label ( & file_name) ; } ) ;
29042944 columns[ 2 ] . horizontal ( |ui| { ui. label ( "Artist" ) ; ui. label ( & state. artist ) ; } ) ;
29052945 columns[ 2 ] . horizontal ( |ui| { ui. label ( "Path" ) ; ui. label ( & file_dir) ; } ) ;
29062946 if state. playlist . len ( ) > 1 {
@@ -2954,7 +2994,14 @@ impl<'a> VulkanEngine<'a> {
29542994 }
29552995 } ) ;
29562996
2957- columns[ 2 ] . horizontal ( |ui| { ui. label ( "Length" ) ; ui. label ( format ! ( "{:.1}s" , state. duration_seconds) ) ; } ) ;
2997+ columns[ 2 ] . horizontal ( |ui| {
2998+ ui. label ( "Length" ) ;
2999+ if state. duration_seconds <= 0.0 {
3000+ ui. label ( "LIVE" ) ;
3001+ } else {
3002+ ui. label ( format ! ( "{:.1}s" , state. duration_seconds) ) ;
3003+ }
3004+ } ) ;
29583005 out_track_info_rect = Some ( columns[ 2 ] . min_rect ( ) ) ;
29593006 }
29603007 } ) ;
0 commit comments