Skip to content

Commit 3b1f135

Browse files
committed
show overlay
1 parent 05abe4e commit 3b1f135

2 files changed

Lines changed: 95 additions & 21 deletions

File tree

i18n/en/cosmic_player.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,8 @@ ab-repeat-clear = Clear A-B Repeat
5151
xdg-name = COSMIC Media Player
5252
xdg-comment = Media player for the COSMIC desktop
5353
xdg-keywords = Audio;Film;Movie;Music;Sound;Video;
54+
55+
volume-percent = Volume { $percent }%
56+
seek-percent = Seek { $percent }%
57+
playback-speed = Speed { $speed }x
58+
seek-relative = Seek { $seconds }s

src/main.rs

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -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

372382
impl 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

Comments
 (0)