@@ -213,17 +213,23 @@ impl MenuAction for Action {
213213 Self :: PlayPause => Message :: PlayPause ,
214214 Self :: PlayPrev => Message :: PlayPrev ,
215215 Self :: PlayNext => Message :: PlayNext ,
216- Self :: SeekBackward => Message :: SeekRelative ( -10.0 ) ,
217- Self :: SeekForward => Message :: SeekRelative ( 10.0 ) ,
218- Self :: NextFrame => Message :: NextFrame ,
219- Self :: PreviousFrame => Message :: PreviousFrame ,
220- Self :: AbRepeat => Message :: AbRepeat ,
221- Self :: TextCodeToggle => Message :: TextCodeToggle ,
222- Self :: AudioToggle => Message :: AudioToggle ,
216+ Self :: SeekBackward => Message :: ShortcutEvent ( Box :: new ( Message :: SeekRelative ( -10.0 ) ) ) ,
217+ Self :: SeekForward => Message :: ShortcutEvent ( Box :: new ( Message :: SeekRelative ( 10.0 ) ) ) ,
218+ Self :: NextFrame => Message :: ShortcutEvent ( Box :: new ( Message :: NextFrame ) ) ,
219+ Self :: PreviousFrame => Message :: ShortcutEvent ( Box :: new ( Message :: PreviousFrame ) ) ,
220+ Self :: AbRepeat => Message :: ShortcutEvent ( Box :: new ( Message :: AbRepeat ) ) ,
221+ Self :: TextCodeToggle => Message :: ShortcutEvent ( Box :: new ( Message :: TextCodeToggle ) ) ,
222+ Self :: AudioToggle => Message :: ShortcutEvent ( Box :: new ( Message :: AudioToggle ) ) ,
223+ Self :: ChangeVolume ( change) => {
224+ Message :: ShortcutEvent ( Box :: new ( Message :: ChangeVolumeRelative ( change. into_inner ( ) ) ) )
225+ }
226+ Self :: ChangeSpeed ( change) => {
227+ Message :: ShortcutEvent ( Box :: new ( Message :: ChangeSpeedRelative ( change. into_inner ( ) ) ) )
228+ }
229+ Self :: Seek ( value) => {
230+ Message :: ShortcutEvent ( Box :: new ( Message :: SeekPercent ( value. into_inner ( ) ) ) )
231+ }
223232 Self :: WindowClose => Message :: WindowClose ,
224- Self :: ChangeVolume ( change) => Message :: ChangeVolumeRelative ( change. into_inner ( ) ) ,
225- Self :: ChangeSpeed ( change) => Message :: ChangeSpeedRelative ( change. into_inner ( ) ) ,
226- Self :: Seek ( value) => Message :: SeekPercent ( value. into_inner ( ) ) ,
227233 }
228234 }
229235}
@@ -336,6 +342,7 @@ pub enum Message {
336342 SystemThemeModeChange ( cosmic_theme:: ThemeMode ) ,
337343 WindowClose ,
338344 TextCodeToggle ,
345+ ShortcutEvent ( Box < Message > ) ,
339346}
340347
341348/// The [`App`] stores application-specific state.
@@ -362,11 +369,14 @@ pub struct App {
362369 current_audio : i32 ,
363370 text_codes : Vec < TextCode > ,
364371 current_text : Option < i32 > ,
372+ last_text : Option < i32 > ,
365373 ab_repeat : Option < ( Option < f64 > , Option < f64 > ) > ,
366374 playback_speed : f64 ,
367375 #[ cfg( feature = "xdg-portal" ) ]
368376 inhibit : tokio:: sync:: watch:: Sender < bool > ,
369- previous_text : Option < i32 > ,
377+ overlay_text : String ,
378+ overlay_time : Instant ,
379+ overlay_visible : bool ,
370380}
371381
372382impl App {
@@ -655,6 +665,10 @@ impl App {
655665 self . core . window . show_headerbar = false ;
656666 self . controls = false ;
657667 }
668+
669+ if self . overlay_time . elapsed ( ) > CONTROLS_TIMEOUT {
670+ self . overlay_visible = false ;
671+ }
658672 self . update_mpris_state ( ) ;
659673 }
660674
@@ -882,6 +896,7 @@ impl App {
882896 log:: warn!( "failed to set playback speed {}: {}" , speed, err) ;
883897 }
884898 }
899+
885900}
886901
887902/// Implement [`cosmic::Application`] to integrate with COSMIC.
@@ -940,11 +955,14 @@ impl Application for App {
940955 current_audio : -1 ,
941956 text_codes : Vec :: new ( ) ,
942957 current_text : None ,
943- previous_text : None ,
958+ last_text : None ,
944959 ab_repeat : None ,
945960 playback_speed : 1.0 ,
946961 #[ cfg( feature = "xdg-portal" ) ]
947962 inhibit,
963+ overlay_text : "" . to_string ( ) ,
964+ overlay_visible : false ,
965+ overlay_time : Instant :: now ( ) ,
948966 } ;
949967
950968 // Do not show nav bar by default. Will be opened by open_project if needed
@@ -1212,6 +1230,12 @@ impl Application for App {
12121230
12131231 self . core . nav_bar_set_toggled ( true ) ;
12141232 }
1233+ Message :: ShortcutEvent ( message) => {
1234+ self . overlay_text = "" . to_string ( ) ;
1235+ self . overlay_time = Instant :: now ( ) ;
1236+ self . overlay_visible = true ;
1237+ return self . update ( * message) ;
1238+ }
12151239 Message :: Fullscreen => {
12161240 //TODO: cleanest way to close dropdowns
12171241 self . dropdown_opt = None ;
@@ -1248,6 +1272,7 @@ impl Application for App {
12481272 Message :: AudioToggle => {
12491273 if let Some ( video) = & mut self . video_opt {
12501274 video. set_muted ( !video. muted ( ) ) ;
1275+ self . overlay_text = fl ! ( "audio" ) ;
12511276 self . update_controls ( true ) ;
12521277 }
12531278 }
@@ -1257,13 +1282,17 @@ impl Application for App {
12571282 {
12581283 video. set_volume ( volume) ;
12591284 video. set_muted ( false ) ;
1285+ self . overlay_text = fl ! (
1286+ "volume-percent" ,
1287+ percent = ( ( volume * 100.0 ) . round( ) as i32 )
1288+ ) ;
12601289 self . update_controls ( true ) ;
12611290 }
12621291 }
12631292 Message :: ChangeVolumeRelative ( delta) => {
12641293 if let Some ( video) = & mut self . video_opt {
1265- let volume = video. volume ( ) ;
1266- return self . update ( Message :: AudioVolume ( volume + delta ) ) ;
1294+ let volume = ( video. volume ( ) + delta ) . clamp ( 0.0 , 1.0 ) ;
1295+ return self . update ( Message :: AudioVolume ( volume) ) ;
12671296 }
12681297 }
12691298 Message :: TextCode ( index) => {
@@ -1282,12 +1311,12 @@ impl Application for App {
12821311 }
12831312 Message :: TextCodeToggle => {
12841313 if self . current_text . is_some ( ) {
1285- self . previous_text = self . current_text ;
1314+ self . last_text = self . current_text ;
12861315 self . current_text = None ;
12871316 } else {
1288- self . current_text = self . previous_text ;
1317+ self . current_text = self . last_text ;
12891318 }
1290-
1319+ self . overlay_text = fl ! ( "subtitles" ) ;
12911320 self . update_flags ( ) ;
12921321 }
12931322 Message :: Pause | Message :: Play | Message :: PlayPause => {
@@ -1374,6 +1403,10 @@ impl Application for App {
13741403 video. set_paused ( true ) ;
13751404 let duration = Duration :: try_from_secs_f64 ( self . position ) . unwrap_or_default ( ) ;
13761405 video. seek ( duration, true ) . expect ( "seek" ) ;
1406+ self . overlay_text = fl ! (
1407+ "seek-percent" ,
1408+ percent = ( ( self . position / self . duration * 100.0 ) . round( ) as i32 )
1409+ ) ;
13771410 self . update_controls ( true ) ;
13781411 }
13791412 }
@@ -1383,6 +1416,7 @@ impl Application for App {
13831416 ( video. position ( ) . as_secs_f64 ( ) + secs) . clamp ( 0.0 , self . duration ) ;
13841417 let target = Duration :: try_from_secs_f64 ( self . position ) . unwrap_or_default ( ) ;
13851418 video. seek ( target, true ) . expect ( "seek" ) ;
1419+ self . overlay_text = fl ! ( "seek-relative" , seconds = secs) ;
13861420 }
13871421 }
13881422
@@ -1391,6 +1425,8 @@ impl Application for App {
13911425 self . position = ( self . duration * float) . clamp ( 0.0 , self . duration ) ;
13921426 let target = Duration :: try_from_secs_f64 ( self . position ) . unwrap_or_default ( ) ;
13931427 video. seek ( target, true ) . expect ( "seek" ) ;
1428+ self . overlay_text =
1429+ fl ! ( "seek-percent" , percent = ( ( float * 100.0 ) . round( ) as i32 ) ) ;
13941430 }
13951431 }
13961432
@@ -1526,6 +1562,7 @@ impl Application for App {
15261562
15271563 self . position = ( video. position ( ) . as_secs_f64 ( ) + frame_duration. as_secs_f64 ( ) )
15281564 . clamp ( 0.0 , self . duration ) ;
1565+ self . overlay_text = fl ! ( "next-frame" ) ;
15291566 self . update_controls ( true ) ;
15301567 }
15311568 }
@@ -1543,6 +1580,7 @@ impl Application for App {
15431580
15441581 self . position = target. as_secs_f64 ( ) . clamp ( 0.0 , self . duration ) ;
15451582 video. seek ( target, true ) . expect ( "seek" ) ;
1583+ self . overlay_text = fl ! ( "previous-frame" ) ;
15461584 self . update_controls ( true ) ;
15471585 }
15481586 }
@@ -1551,6 +1589,7 @@ impl Application for App {
15511589 self . dropdown_opt = None ;
15521590 if let Some ( video) = & mut self . video_opt {
15531591 Self :: apply_speed ( video, speed) ;
1592+ self . overlay_text = fl ! ( "playback-speed" , speed = format!( "{:.2}" , speed) ) ;
15541593 }
15551594 self . update_controls ( true ) ;
15561595 }
@@ -1567,12 +1606,15 @@ impl Application for App {
15671606 match self . ab_repeat {
15681607 None => {
15691608 self . ab_repeat = Some ( ( Some ( current) , None ) ) ;
1609+ self . overlay_text = fl ! ( "ab-repeat-set-a" ) ;
15701610 }
15711611 Some ( ( a, None ) ) => {
15721612 self . ab_repeat = Some ( ( a, Some ( current) ) ) ;
1613+ self . overlay_text = fl ! ( "ab-repeat-set-b" ) ;
15731614 }
15741615 Some ( _) => {
15751616 self . ab_repeat = None ;
1617+ self . overlay_text = fl ! ( "ab-repeat-clear" ) ;
15761618 }
15771619 }
15781620 self . update_controls ( true ) ;
@@ -1679,7 +1721,6 @@ impl Application for App {
16791721 let _ = video. seek ( Duration :: from_secs_f64 ( target_a) , true ) ;
16801722 }
16811723 }
1682-
16831724 self . update_controls ( self . dropdown_opt . is_some ( ) ) ;
16841725 }
16851726 }
@@ -2145,7 +2186,7 @@ impl Application for App {
21452186 popover = popover. popup ( widget:: column:: with_children ( popup_items) ) ;
21462187 }
21472188
2148- widget:: container ( popover)
2189+ let base : Element < _ > = widget:: container ( popover)
21492190 . width ( Length :: Fill )
21502191 . height ( Length :: Fill )
21512192 . class ( theme:: Container :: Custom ( Box :: new ( move |_theme| {
@@ -2156,7 +2197,36 @@ impl Application for App {
21562197 }
21572198 appearance
21582199 } ) ) )
2159- . into ( )
2200+ . into ( ) ;
2201+
2202+ if self . overlay_visible {
2203+ let osd = widget:: container (
2204+ widget:: container ( widget:: text ( & self . overlay_text ) . size ( 20 ) . font ( font:: bold ( ) ) )
2205+ . padding ( [ space_xxs, space_m] )
2206+ //TODO: move style to libcosmic
2207+ . class ( theme:: Container :: custom ( |theme| {
2208+ let cosmic = theme. cosmic ( ) ;
2209+ let component = & cosmic. background ( theme. transparent ) . component ;
2210+ widget:: container:: Style {
2211+ text_color : Some ( component. on . into ( ) ) ,
2212+ background : Some ( Background :: Color ( component. base . into ( ) ) ) ,
2213+ border : Border {
2214+ radius : 8.0 . into ( ) ,
2215+ ..Default :: default ( )
2216+ } ,
2217+ ..Default :: default ( )
2218+ }
2219+ } ) ) ,
2220+ )
2221+ . width ( Length :: Fill )
2222+ . align_x ( Alignment :: End )
2223+ . align_y ( Alignment :: Start )
2224+ . padding ( space_m) ;
2225+
2226+ cosmic:: iced:: widget:: stack![ base, osd] . into ( )
2227+ } else {
2228+ base
2229+ }
21602230 }
21612231
21622232 fn subscription ( & self ) -> Subscription < Self :: Message > {
@@ -2217,7 +2287,6 @@ impl Application for App {
22172287 cosmic:: iced:: time:: every ( Duration :: from_millis ( 64 ) ) . map ( |_| Message :: NewFrame ) ,
22182288 ) ;
22192289 }
2220-
22212290 #[ cfg( feature = "mpris-server" ) ]
22222291 {
22232292 subscriptions. push ( mpris:: subscription ( ) ) ;
0 commit comments