diff --git a/res/icons/hicolor/16x16/apps/jump-backward-10-symbolic.svg b/res/icons/hicolor/16x16/apps/jump-backward-10-symbolic.svg
new file mode 100644
index 0000000..51c26ca
--- /dev/null
+++ b/res/icons/hicolor/16x16/apps/jump-backward-10-symbolic.svg
@@ -0,0 +1,5 @@
+
diff --git a/res/icons/hicolor/16x16/apps/jump-forward-10-symbolic.svg b/res/icons/hicolor/16x16/apps/jump-forward-10-symbolic.svg
new file mode 100644
index 0000000..bc4095f
--- /dev/null
+++ b/res/icons/hicolor/16x16/apps/jump-forward-10-symbolic.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/main.rs b/src/main.rs
index d689ce3..2c11f3d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,6 +47,12 @@ const GST_PLAY_FLAG_VIDEO: i32 = 1 << 0;
const GST_PLAY_FLAG_AUDIO: i32 = 1 << 1;
const GST_PLAY_FLAG_TEXT: i32 = 1 << 2;
+const JUMP_BACKWARD_ICON: &[u8] =
+ include_bytes!("../res/icons/hicolor/16x16/apps/jump-backward-10-symbolic.svg");
+
+const JUMP_FORWARD_ICON: &[u8] =
+ include_bytes!("../res/icons/hicolor/16x16/apps/jump-forward-10-symbolic.svg");
+
use std::error::Error;
fn language_name(code: &str) -> Option {
@@ -170,6 +176,8 @@ pub enum Action {
FolderOpenRecent(usize),
Fullscreen,
PlayPause,
+ PlayPrev,
+ PlayNext,
SeekBackward,
SeekForward,
NextFrame,
@@ -193,6 +201,8 @@ impl MenuAction for Action {
Self::FolderOpenRecent(index) => Message::FolderOpenRecent(*index),
Self::Fullscreen => Message::Fullscreen,
Self::PlayPause => Message::PlayPause,
+ Self::PlayPrev => Message::PlayPrev,
+ Self::PlayNext => Message::PlayNext,
Self::SeekBackward => Message::SeekRelative(-10.0),
Self::SeekForward => Message::SeekRelative(10.0),
Self::NextFrame => Message::NextFrame,
@@ -291,6 +301,7 @@ pub enum Message {
Seek(f64),
SeekRelative(f64),
SeekRelease,
+ PlayPrev,
PlayNext,
NextFrame,
PreviousFrame,
@@ -1338,6 +1349,55 @@ impl Application for App {
}
}
+ Message::PlayPrev => {
+ if self.flags.config_state.player_state.repeat == RepeatState::Track {
+ return Task::none();
+ }
+
+ //first we get info about current media id & position in nav_bar
+ let curr_id = self.nav_model.active();
+ let curr_position = match self.nav_model.position(curr_id) {
+ Some(pos) => pos,
+ None => {
+ log::warn!("Failed to get position of current media: {:?}", curr_id);
+ return self.update(Message::EndOfStream);
+ }
+ };
+
+ if let Some(video) = &mut self.video_opt {
+ self.position = video.position().as_secs_f64();
+
+ if self.position > 3.0 {
+ video.seek(0, false).expect("seek");
+ } else {
+ if self.nav_model.activate_position(curr_position - 1) {
+ let curr_id = self.nav_model.active();
+ match self.nav_model.data::(curr_id) {
+ //The prev one is a media file, we play it.
+ Some(ProjectNode::File { .. }) => {
+ return self.on_nav_select(curr_id);
+ }
+
+ //The prev one is a folder. We expand it and recall PlayPrev.
+ Some(ProjectNode::Folder { .. }) => {
+ let _ = self.on_nav_select(curr_id);
+ return self.update(Message::PlayPrev);
+ }
+
+ //Unknown type. We do nothing.
+ _ => log::warn!(
+ "unknown type: {:?}",
+ self.nav_model.data::(curr_id)
+ ),
+ }
+ } else {
+ // first file
+ video.seek(0, false).expect("seek");
+ }
+ }
+ }
+ }
+
Message::PlayNext => {
// TODO: known limitations:
// 1) if the user collapses the folder entry while a song is playing,
@@ -1779,19 +1839,55 @@ impl Application for App {
);
}
if self.controls {
- let mut row = widget::row::with_capacity(7)
+ let mut row = widget::row::with_capacity(9)
.align_y(Alignment::Center)
.spacing(space_xxs)
.push(
- widget::button::icon(
- if self.video_opt.as_ref().is_none_or(|video| video.paused()) {
- widget::icon::from_name("media-playback-start-symbolic").size(16)
- } else {
- widget::icon::from_name("media-playback-pause-symbolic").size(16)
- },
- )
- .on_press(Message::PlayPause),
+ if self
+ .video_opt
+ .as_ref()
+ .map_or(true, |video| video.has_video())
+ {
+ widget::button::icon(
+ widget::icon::from_svg_bytes(JUMP_BACKWARD_ICON).symbolic(true),
+ )
+ .on_press(Message::SeekRelative(-10.0))
+ } else {
+ widget::button::icon(
+ widget::icon::from_name("media-skip-backward-symbolic").size(16),
+ )
+ .on_press(Message::PlayPrev)
+ },
);
+ row = row.push(
+ widget::button::icon(
+ if self.video_opt.as_ref().map_or(true, |video| video.paused()) {
+ widget::icon::from_name("media-playback-start-symbolic").size(16)
+ } else {
+ widget::icon::from_name("media-playback-pause-symbolic").size(16)
+ },
+ )
+ .on_press(Message::PlayPause),
+ );
+
+ row = row.push(
+ if self
+ .video_opt
+ .as_ref()
+ .map_or(true, |video| video.has_video())
+ {
+ widget::button::icon(
+ widget::icon::from_svg_bytes(JUMP_FORWARD_ICON).symbolic(true),
+ )
+ .on_press(Message::SeekRelative(10.0))
+ } else {
+ widget::button::icon(
+ widget::icon::from_name("media-skip-forward-symbolic").size(16),
+ )
+ .on_press(Message::PlayNext)
+ },
+ );
+
row = row.push(widget::tooltip(
widget::button::icon(
widget::icon::from_name(match self.flags.config_state.player_state.repeat {
diff --git a/src/mpris.rs b/src/mpris.rs
index 31a088f..8c87244 100644
--- a/src/mpris.rs
+++ b/src/mpris.rs
@@ -150,12 +150,12 @@ impl RootInterface for Player {
impl PlayerInterface for Player {
async fn next(&self) -> fdo::Result<()> {
log::info!("Next");
- Ok(())
+ self.message(Message::PlayNext).await
}
async fn previous(&self) -> fdo::Result<()> {
log::info!("Previous");
- Ok(())
+ self.message(Message::PlayPrev).await
}
async fn pause(&self) -> fdo::Result<()> {
@@ -271,12 +271,12 @@ impl PlayerInterface for Player {
async fn can_go_next(&self) -> fdo::Result {
log::info!("CanGoNext");
- Ok(false)
+ Ok(true)
}
async fn can_go_previous(&self) -> fdo::Result {
log::info!("CanGoPrevious");
- Ok(false)
+ Ok(true)
}
async fn can_play(&self) -> fdo::Result {