Auto hide controls
This commit is contained in:
parent
aae3a8398f
commit
8105bd3b48
1 changed files with 61 additions and 30 deletions
|
|
@ -8,6 +8,7 @@ use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
event::{self, Event},
|
event::{self, Event},
|
||||||
keyboard::{Event as KeyEvent, Key, Modifiers},
|
keyboard::{Event as KeyEvent, Key, Modifiers},
|
||||||
|
mouse::Event as MouseEvent,
|
||||||
subscription::{self, Subscription},
|
subscription::{self, Subscription},
|
||||||
window, Alignment, Color, Length, Limits, Size,
|
window, Alignment, Color, Length, Limits, Size,
|
||||||
},
|
},
|
||||||
|
|
@ -19,7 +20,11 @@ use iced_video_player::{
|
||||||
gst::{self, prelude::*},
|
gst::{self, prelude::*},
|
||||||
gst_pbutils, Video, VideoPlayer,
|
gst_pbutils, Video, VideoPlayer,
|
||||||
};
|
};
|
||||||
use std::{any::TypeId, collections::HashMap, time::Duration};
|
use std::{
|
||||||
|
any::TypeId,
|
||||||
|
collections::HashMap,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, CONFIG_VERSION},
|
config::{Config, CONFIG_VERSION},
|
||||||
|
|
@ -27,6 +32,8 @@ use crate::{
|
||||||
localize,
|
localize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static CONTROLS_TIMEOUT: Duration = Duration::new(2, 0);
|
||||||
|
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
@ -110,6 +117,7 @@ pub enum Message {
|
||||||
MissingPlugin(gst::Message),
|
MissingPlugin(gst::Message),
|
||||||
NewFrame,
|
NewFrame,
|
||||||
Reload,
|
Reload,
|
||||||
|
ShowControls,
|
||||||
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,6 +125,8 @@ pub enum Message {
|
||||||
pub struct App {
|
pub struct App {
|
||||||
core: Core,
|
core: Core,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
|
controls: bool,
|
||||||
|
controls_time: Instant,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
key_binds: HashMap<KeyBind, Action>,
|
key_binds: HashMap<KeyBind, Action>,
|
||||||
video_opt: Option<Video>,
|
video_opt: Option<Video>,
|
||||||
|
|
@ -159,12 +169,12 @@ impl App {
|
||||||
self.audio_codes = Vec::with_capacity(n_audio as usize);
|
self.audio_codes = Vec::with_capacity(n_audio as usize);
|
||||||
for i in 0..n_audio {
|
for i in 0..n_audio {
|
||||||
let tags: gst::TagList = pipeline.emit_by_name("get-audio-tags", &[&i]);
|
let tags: gst::TagList = pipeline.emit_by_name("get-audio-tags", &[&i]);
|
||||||
println!("audio stream {}: {:?}", i, tags);
|
log::info!("audio stream {i}: {tags:?}");
|
||||||
self.audio_codes.push(
|
self.audio_codes.push(
|
||||||
if let Some(language_code) = tags.get::<gst::tags::LanguageCode>() {
|
if let Some(language_code) = tags.get::<gst::tags::LanguageCode>() {
|
||||||
language_code.get().to_string()
|
language_code.get().to_string()
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
format!("Audio #{i}")
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -174,12 +184,12 @@ impl App {
|
||||||
self.text_codes = Vec::with_capacity(n_text as usize);
|
self.text_codes = Vec::with_capacity(n_text as usize);
|
||||||
for i in 0..n_text {
|
for i in 0..n_text {
|
||||||
let tags: gst::TagList = pipeline.emit_by_name("get-text-tags", &[&i]);
|
let tags: gst::TagList = pipeline.emit_by_name("get-text-tags", &[&i]);
|
||||||
println!("text stream {}: {:?}", i, tags);
|
log::info!("text stream {i}: {tags:?}");
|
||||||
self.text_codes.push(
|
self.text_codes.push(
|
||||||
if let Some(language_code) = tags.get::<gst::tags::LanguageCode>() {
|
if let Some(language_code) = tags.get::<gst::tags::LanguageCode>() {
|
||||||
language_code.get().to_string()
|
language_code.get().to_string()
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
format!("Subtitle #{i}")
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +201,15 @@ impl App {
|
||||||
self.update_title()
|
self.update_title()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_controls(&mut self, in_use: bool) {
|
||||||
|
if in_use {
|
||||||
|
self.controls = true;
|
||||||
|
self.controls_time = Instant::now();
|
||||||
|
} else if self.controls && self.controls_time.elapsed() > CONTROLS_TIMEOUT {
|
||||||
|
self.controls = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_config(&mut self) -> Command<Message> {
|
fn update_config(&mut self) -> Command<Message> {
|
||||||
cosmic::app::command::set_theme(self.flags.config.app_theme.theme())
|
cosmic::app::command::set_theme(self.flags.config.app_theme.theme())
|
||||||
}
|
}
|
||||||
|
|
@ -231,6 +250,8 @@ impl Application for App {
|
||||||
let mut app = App {
|
let mut app = App {
|
||||||
core,
|
core,
|
||||||
flags,
|
flags,
|
||||||
|
controls: true,
|
||||||
|
controls_time: Instant::now(),
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
key_binds: key_binds(),
|
key_binds: key_binds(),
|
||||||
video_opt: None,
|
video_opt: None,
|
||||||
|
|
@ -305,6 +326,7 @@ impl Application for App {
|
||||||
Message::TogglePause => {
|
Message::TogglePause => {
|
||||||
if let Some(video) = &mut self.video_opt {
|
if let Some(video) = &mut self.video_opt {
|
||||||
video.set_paused(!video.paused());
|
video.set_paused(!video.paused());
|
||||||
|
self.update_controls(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::ToggleLoop => {
|
Message::ToggleLoop => {
|
||||||
|
|
@ -319,6 +341,7 @@ impl Application for App {
|
||||||
video.set_paused(true);
|
video.set_paused(true);
|
||||||
let duration = Duration::try_from_secs_f64(self.position).unwrap_or_default();
|
let duration = Duration::try_from_secs_f64(self.position).unwrap_or_default();
|
||||||
video.seek(duration, true).expect("seek");
|
video.seek(duration, true).expect("seek");
|
||||||
|
self.update_controls(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::SeekRelative(secs) => {
|
Message::SeekRelative(secs) => {
|
||||||
|
|
@ -335,6 +358,7 @@ impl Application for App {
|
||||||
let duration = Duration::try_from_secs_f64(self.position).unwrap_or_default();
|
let duration = Duration::try_from_secs_f64(self.position).unwrap_or_default();
|
||||||
video.seek(duration, true).expect("seek");
|
video.seek(duration, true).expect("seek");
|
||||||
video.set_paused(false);
|
video.set_paused(false);
|
||||||
|
self.update_controls(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::EndOfStream => {
|
Message::EndOfStream => {
|
||||||
|
|
@ -377,15 +401,19 @@ impl Application for App {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Message::NewFrame => {
|
Message::NewFrame => {
|
||||||
if !self.dragging {
|
if let Some(video) = &self.video_opt {
|
||||||
if let Some(video) = &self.video_opt {
|
if !self.dragging {
|
||||||
self.position = video.position().as_secs_f64();
|
self.position = video.position().as_secs_f64();
|
||||||
|
self.update_controls(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Reload => {
|
Message::Reload => {
|
||||||
return self.load();
|
return self.load();
|
||||||
}
|
}
|
||||||
|
Message::ShowControls => {
|
||||||
|
self.update_controls(true);
|
||||||
|
}
|
||||||
Message::SystemThemeModeChange(_theme_mode) => {
|
Message::SystemThemeModeChange(_theme_mode) => {
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
|
|
@ -394,34 +422,28 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_start(&self) -> Vec<Element<Self::Message>> {
|
fn header_start(&self) -> Vec<Element<Self::Message>> {
|
||||||
vec![widget::row::with_children(vec![
|
let mut row = widget::row::with_capacity(4)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.spacing(8);
|
||||||
|
if !self.audio_codes.is_empty() {
|
||||||
//TODO: allow mute/unmute/change volume
|
//TODO: allow mute/unmute/change volume
|
||||||
widget::icon::from_name("audio-volume-high-symbolic")
|
row = row.push(widget::icon::from_name("audio-volume-high-symbolic").size(16));
|
||||||
.size(16)
|
row = row.push(widget::dropdown(
|
||||||
.into(),
|
|
||||||
widget::dropdown(
|
|
||||||
&self.audio_codes,
|
&self.audio_codes,
|
||||||
usize::try_from(self.current_audio).ok(),
|
usize::try_from(self.current_audio).ok(),
|
||||||
Message::AudioCode,
|
Message::AudioCode,
|
||||||
)
|
));
|
||||||
.into(),
|
}
|
||||||
|
if !self.text_codes.is_empty() {
|
||||||
//TODO: allow toggling subtitles
|
//TODO: allow toggling subtitles
|
||||||
widget::icon::from_name("media-view-subtitles-symbolic")
|
row = row.push(widget::icon::from_name("media-view-subtitles-symbolic").size(16));
|
||||||
.size(16)
|
row = row.push(widget::dropdown(
|
||||||
.into(),
|
|
||||||
widget::dropdown(
|
|
||||||
&self.text_codes,
|
&self.text_codes,
|
||||||
usize::try_from(self.current_text).ok(),
|
usize::try_from(self.current_text).ok(),
|
||||||
Message::TextCode,
|
Message::TextCode,
|
||||||
)
|
));
|
||||||
.into(),
|
}
|
||||||
widget::button::icon(widget::icon::from_name("view-fullscreen-symbolic").size(16))
|
vec![row.into()]
|
||||||
.on_press(Message::Fullscreen)
|
|
||||||
.into(),
|
|
||||||
])
|
|
||||||
.align_items(Alignment::Center)
|
|
||||||
.spacing(8)
|
|
||||||
.into()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a view after each update.
|
/// Creates a view after each update.
|
||||||
|
|
@ -450,11 +472,13 @@ impl Application for App {
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill);
|
.height(Length::Fill);
|
||||||
|
|
||||||
let mut popover = widget::popover(video_player).position(widget::popover::Position::Bottom);
|
let mouse_area = widget::mouse_area(video_player).on_double_press(Message::Fullscreen);
|
||||||
if !self.fullscreen {
|
|
||||||
|
let mut popover = widget::popover(mouse_area).position(widget::popover::Position::Bottom);
|
||||||
|
if self.controls {
|
||||||
popover = popover.popup(
|
popover = popover.popup(
|
||||||
widget::container(
|
widget::container(
|
||||||
widget::row::with_capacity(4)
|
widget::row::with_capacity(5)
|
||||||
.align_items(Alignment::Center)
|
.align_items(Alignment::Center)
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
.padding([0, 8])
|
.padding([0, 8])
|
||||||
|
|
@ -479,6 +503,12 @@ impl Application for App {
|
||||||
.push(
|
.push(
|
||||||
widget::text(format_time(self.duration - self.position))
|
widget::text(format_time(self.duration - self.position))
|
||||||
.font(font::mono()),
|
.font(font::mono()),
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
widget::button::icon(
|
||||||
|
widget::icon::from_name("view-fullscreen-symbolic").size(16),
|
||||||
|
)
|
||||||
|
.on_press(Message::Fullscreen),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.style(theme::Container::WindowBackground),
|
.style(theme::Container::WindowBackground),
|
||||||
|
|
@ -503,6 +533,7 @@ impl Application for App {
|
||||||
Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => {
|
Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => {
|
||||||
Some(Message::Key(modifiers, key))
|
Some(Message::Key(modifiers, key))
|
||||||
}
|
}
|
||||||
|
Event::Mouse(MouseEvent::CursorMoved { .. }) => Some(Message::ShowControls),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
cosmic_config::config_subscription(
|
cosmic_config::config_subscription(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue