feat: add skip forward and backward playback controls
This commit is contained in:
parent
468b5f1459
commit
f3d6469bed
4 changed files with 119 additions and 13 deletions
114
src/main.rs
114
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<String> {
|
||||
|
|
@ -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::<ProjectNode>(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::<ProjectNode>(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 {
|
||||
|
|
|
|||
|
|
@ -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<bool> {
|
||||
log::info!("CanGoNext");
|
||||
Ok(false)
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn can_go_previous(&self) -> fdo::Result<bool> {
|
||||
log::info!("CanGoPrevious");
|
||||
Ok(false)
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn can_play(&self) -> fdo::Result<bool> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue