700 lines
16 KiB
Rust
700 lines
16 KiB
Rust
// TODO: Things that should be done.
|
|
//
|
|
// * Wherever there's a function that take 2 or more arguments of the same type,
|
|
// eg: fn new(total_size: gtk::Label, local_size: gtk::Label ..)
|
|
// Wrap the types into Struct-tuples and imple deref so it won't be possible to pass
|
|
// the wrong argument to the wrong position.
|
|
|
|
use chrono;
|
|
use gtk;
|
|
use gtk::prelude::*;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct UnInitialized;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Shown;
|
|
#[derive(Debug, Clone)]
|
|
pub struct Hidden;
|
|
|
|
pub trait Visibility {}
|
|
|
|
impl Visibility for Shown {}
|
|
impl Visibility for Hidden {}
|
|
|
|
impl From<Hidden> for Shown {
|
|
fn from(_: Hidden) -> Self {
|
|
Shown {}
|
|
}
|
|
}
|
|
|
|
impl From<Shown> for Hidden {
|
|
fn from(_: Shown) -> Self {
|
|
Hidden {}
|
|
}
|
|
}
|
|
|
|
impl Into<Hidden> for UnInitialized {
|
|
fn into(self) -> Hidden {
|
|
Hidden {}
|
|
}
|
|
}
|
|
|
|
impl Into<Shown> for UnInitialized {
|
|
fn into(self) -> Shown {
|
|
Shown {}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Normal;
|
|
#[derive(Debug, Clone)]
|
|
pub struct GreyedOut;
|
|
|
|
impl From<Normal> for GreyedOut {
|
|
fn from(_: Normal) -> Self {
|
|
GreyedOut {}
|
|
}
|
|
}
|
|
|
|
impl From<GreyedOut> for Normal {
|
|
fn from(_: GreyedOut) -> Self {
|
|
Normal {}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Title<S> {
|
|
title: gtk::Label,
|
|
state: S,
|
|
}
|
|
|
|
impl<S> Title<S> {
|
|
#[allow(unused_must_use)]
|
|
// This does not need to be &mut since gtk-rs does not model ownership
|
|
// But I think it wouldn't heart if we treat it as a Rust api.
|
|
fn set_title(&mut self, s: &str) {
|
|
self.title.set_text(s);
|
|
}
|
|
}
|
|
|
|
impl Title<Normal> {
|
|
fn new(title: gtk::Label) -> Self {
|
|
Title {
|
|
title,
|
|
state: Normal {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Title<Normal>> for Title<GreyedOut> {
|
|
fn from(f: Title<Normal>) -> Self {
|
|
f.title
|
|
.get_style_context()
|
|
.map(|c| c.add_class("dim-label"));
|
|
|
|
Title {
|
|
title: f.title,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Title<GreyedOut>> for Title<Normal> {
|
|
fn from(f: Title<GreyedOut>) -> Self {
|
|
f.title
|
|
.get_style_context()
|
|
.map(|c| c.remove_class("dim-label"));
|
|
|
|
Title {
|
|
title: f.title,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum TitleMachine {
|
|
Normal(Title<Normal>),
|
|
GreyedOut(Title<GreyedOut>),
|
|
}
|
|
|
|
impl TitleMachine {
|
|
pub fn new(label: gtk::Label, is_played: bool) -> Self {
|
|
let m = TitleMachine::Normal(Title::<Normal>::new(label));
|
|
m.determine_state(is_played)
|
|
}
|
|
|
|
pub fn determine_state(self, is_played: bool) -> Self {
|
|
use self::TitleMachine::*;
|
|
|
|
match (self, is_played) {
|
|
(title @ Normal(_), false) => title,
|
|
(title @ GreyedOut(_), true) => title,
|
|
(Normal(val), true) => GreyedOut(val.into()),
|
|
(GreyedOut(val), false) => Normal(val.into()),
|
|
}
|
|
}
|
|
|
|
pub fn set_title(&mut self, s: &str) {
|
|
use self::TitleMachine::*;
|
|
|
|
match *self {
|
|
Normal(ref mut val) => val.set_title(s),
|
|
GreyedOut(ref mut val) => val.set_title(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Duration<S: Visibility> {
|
|
// TODO: make duration and separator diff types
|
|
duration: gtk::Label,
|
|
separator: gtk::Label,
|
|
state: S,
|
|
}
|
|
|
|
impl<S: Visibility> Duration<S> {
|
|
// This needs a better name.
|
|
// TODO: make me mut
|
|
fn set_duration(&self, minutes: i64) {
|
|
self.duration.set_text(&format!("{} min", minutes));
|
|
}
|
|
}
|
|
|
|
impl Duration<Hidden> {
|
|
fn new(duration: gtk::Label, separator: gtk::Label) -> Self {
|
|
duration.hide();
|
|
separator.hide();
|
|
|
|
Duration {
|
|
duration,
|
|
separator,
|
|
state: Hidden {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Duration<Hidden>> for Duration<Shown> {
|
|
fn from(f: Duration<Hidden>) -> Self {
|
|
f.duration.show();
|
|
f.separator.show();
|
|
|
|
Duration {
|
|
duration: f.duration,
|
|
separator: f.separator,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Duration<Shown>> for Duration<Hidden> {
|
|
fn from(f: Duration<Shown>) -> Self {
|
|
f.duration.hide();
|
|
f.separator.hide();
|
|
|
|
Duration {
|
|
duration: f.duration,
|
|
separator: f.separator,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum DurationMachine {
|
|
Hidden(Duration<Hidden>),
|
|
Shown(Duration<Shown>),
|
|
}
|
|
|
|
impl DurationMachine {
|
|
pub fn new(duration: gtk::Label, separator: gtk::Label, seconds: Option<i32>) -> Self {
|
|
let m = DurationMachine::Hidden(Duration::<Hidden>::new(duration, separator));
|
|
m.determine_state(seconds)
|
|
}
|
|
|
|
pub fn determine_state(self, seconds: Option<i32>) -> Self {
|
|
match (self, seconds) {
|
|
(DurationMachine::Hidden(val), None) => DurationMachine::Hidden(val.into()),
|
|
(DurationMachine::Shown(val), None) => DurationMachine::Hidden(val.into()),
|
|
(DurationMachine::Hidden(val), Some(s)) => {
|
|
let minutes = chrono::Duration::seconds(s.into()).num_minutes();
|
|
if minutes == 0 {
|
|
DurationMachine::Hidden(val.into())
|
|
} else {
|
|
val.set_duration(minutes);
|
|
DurationMachine::Shown(val.into())
|
|
}
|
|
}
|
|
(DurationMachine::Shown(val), Some(s)) => {
|
|
let minutes = chrono::Duration::seconds(s.into()).num_minutes();
|
|
if minutes == 0 {
|
|
DurationMachine::Hidden(val.into())
|
|
} else {
|
|
val.set_duration(minutes);
|
|
DurationMachine::Shown(val.into())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Size<S> {
|
|
size: gtk::Label,
|
|
separator: gtk::Label,
|
|
state: S,
|
|
}
|
|
|
|
impl Size<Shown> {
|
|
fn set_size(self, s: &str) -> Size<Shown> {
|
|
self.size.set_text(s);
|
|
self.separator.show();
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
impl Size<Hidden> {
|
|
fn set_size(self, s: &str) -> Size<Shown> {
|
|
self.size.set_text(s);
|
|
self.separator.show();
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
impl Size<UnInitialized> {
|
|
fn new(size: gtk::Label, separator: gtk::Label) -> Self {
|
|
size.hide();
|
|
separator.hide();
|
|
|
|
Size {
|
|
size,
|
|
separator,
|
|
state: UnInitialized {},
|
|
}
|
|
}
|
|
|
|
fn set_size(self, s: &str) -> Size<Shown> {
|
|
self.size.set_text(s);
|
|
self.separator.show();
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
impl From<Size<Shown>> for Size<Hidden> {
|
|
fn from(f: Size<Shown>) -> Self {
|
|
f.size.hide();
|
|
f.separator.hide();
|
|
|
|
Size {
|
|
size: f.size,
|
|
separator: f.separator,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Size<Hidden>> for Size<Shown> {
|
|
fn from(f: Size<Hidden>) -> Self {
|
|
f.size.show();
|
|
f.separator.show();
|
|
|
|
Size {
|
|
size: f.size,
|
|
separator: f.separator,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Size<UnInitialized>> for Size<Shown> {
|
|
/// This is suposed to be called only from Size::<UnInitialized>::set_size.
|
|
fn from(f: Size<UnInitialized>) -> Self {
|
|
f.size.show();
|
|
f.separator.show();
|
|
|
|
Size {
|
|
size: f.size,
|
|
separator: f.separator,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Size<UnInitialized>> for Size<Hidden> {
|
|
/// This is suposed to be called only from Size::<UnInitialized>::set_size.
|
|
fn from(f: Size<UnInitialized>) -> Self {
|
|
f.size.hide();
|
|
f.separator.hide();
|
|
|
|
Size {
|
|
size: f.size,
|
|
separator: f.separator,
|
|
state: f.state.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Download;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Play;
|
|
|
|
#[derive(Debug, Clone)]
|
|
// FIXME: Needs better name.
|
|
// Should each button also has it's own type and machine?
|
|
pub struct DownloadPlay<S> {
|
|
play: gtk::Button,
|
|
download: gtk::Button,
|
|
state: S,
|
|
}
|
|
|
|
impl DownloadPlay<UnInitialized> {
|
|
fn new(play: gtk::Button, download: gtk::Button) -> Self {
|
|
play.hide();
|
|
download.hide();
|
|
|
|
DownloadPlay {
|
|
play,
|
|
download,
|
|
state: UnInitialized {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<Play>> for DownloadPlay<Download> {
|
|
fn from(f: DownloadPlay<Play>) -> Self {
|
|
f.play.hide();
|
|
f.download.show();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Download {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<Download>> for DownloadPlay<Play> {
|
|
fn from(f: DownloadPlay<Download>) -> Self {
|
|
f.play.show();
|
|
f.download.hide();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Play {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<Play>> for DownloadPlay<Hidden> {
|
|
fn from(f: DownloadPlay<Play>) -> Self {
|
|
f.play.hide();
|
|
f.download.hide();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Hidden {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<Download>> for DownloadPlay<Hidden> {
|
|
fn from(f: DownloadPlay<Download>) -> Self {
|
|
f.play.hide();
|
|
f.download.hide();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Hidden {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<Hidden>> for DownloadPlay<Download> {
|
|
fn from(f: DownloadPlay<Hidden>) -> Self {
|
|
f.play.hide();
|
|
f.download.show();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Download {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<Hidden>> for DownloadPlay<Play> {
|
|
fn from(f: DownloadPlay<Hidden>) -> Self {
|
|
f.play.show();
|
|
f.download.show();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Play {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<UnInitialized>> for DownloadPlay<Download> {
|
|
fn from(f: DownloadPlay<UnInitialized>) -> Self {
|
|
f.play.hide();
|
|
f.download.show();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Download {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<UnInitialized>> for DownloadPlay<Play> {
|
|
fn from(f: DownloadPlay<UnInitialized>) -> Self {
|
|
f.play.show();
|
|
f.download.show();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Play {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DownloadPlay<UnInitialized>> for DownloadPlay<Hidden> {
|
|
fn from(f: DownloadPlay<UnInitialized>) -> Self {
|
|
f.play.hide();
|
|
f.download.hide();
|
|
|
|
DownloadPlay {
|
|
play: f.play,
|
|
download: f.download,
|
|
state: Hidden {},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Progress<S> {
|
|
bar: gtk::ProgressBar,
|
|
cancel: gtk::Button,
|
|
local_size: gtk::Label,
|
|
prog_separator: gtk::Label,
|
|
state: S,
|
|
}
|
|
|
|
impl Progress<UnInitialized> {
|
|
fn new(
|
|
bar: gtk::ProgressBar,
|
|
cancel: gtk::Button,
|
|
local_size: gtk::Label,
|
|
prog_separator: gtk::Label,
|
|
) -> Self {
|
|
bar.hide();
|
|
cancel.hide();
|
|
local_size.hide();
|
|
prog_separator.hide();
|
|
|
|
Progress {
|
|
bar,
|
|
cancel,
|
|
local_size,
|
|
prog_separator,
|
|
state: UnInitialized {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Progress<Hidden>> for Progress<Shown> {
|
|
fn from(f: Progress<Hidden>) -> Self {
|
|
f.bar.show();
|
|
f.cancel.show();
|
|
f.local_size.show();
|
|
f.prog_separator.show();
|
|
|
|
Progress {
|
|
bar: f.bar,
|
|
cancel: f.cancel,
|
|
local_size: f.local_size,
|
|
prog_separator: f.prog_separator,
|
|
state: Shown {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Progress<Shown>> for Progress<Hidden> {
|
|
fn from(f: Progress<Shown>) -> Self {
|
|
f.bar.hide();
|
|
f.cancel.hide();
|
|
f.local_size.hide();
|
|
f.prog_separator.hide();
|
|
|
|
Progress {
|
|
bar: f.bar,
|
|
cancel: f.cancel,
|
|
local_size: f.local_size,
|
|
prog_separator: f.prog_separator,
|
|
state: Hidden {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Progress<UnInitialized>> for Progress<Shown> {
|
|
fn from(f: Progress<UnInitialized>) -> Self {
|
|
f.bar.show();
|
|
f.cancel.show();
|
|
f.local_size.show();
|
|
f.prog_separator.show();
|
|
|
|
Progress {
|
|
bar: f.bar,
|
|
cancel: f.cancel,
|
|
local_size: f.local_size,
|
|
prog_separator: f.prog_separator,
|
|
state: Shown {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Progress<UnInitialized>> for Progress<Hidden> {
|
|
fn from(f: Progress<UnInitialized>) -> Self {
|
|
f.bar.hide();
|
|
f.cancel.hide();
|
|
f.local_size.hide();
|
|
f.prog_separator.hide();
|
|
|
|
Progress {
|
|
bar: f.bar,
|
|
cancel: f.cancel,
|
|
local_size: f.local_size,
|
|
prog_separator: f.prog_separator,
|
|
state: Hidden {},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Media<X, Y, Z> {
|
|
dl: DownloadPlay<X>,
|
|
size: Size<Y>,
|
|
progress: Progress<Z>,
|
|
}
|
|
|
|
// From New from InProgress
|
|
impl From<Media<Download, Hidden, Shown>> for Media<Hidden, Shown, Shown> {
|
|
fn from(f: Media<Download, Hidden, Shown>) -> Self {
|
|
Media {
|
|
dl: f.dl.into(),
|
|
size: f.size.into(),
|
|
progress: f.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// From NewWithoutSize from InProgress
|
|
impl From<Media<Download, Hidden, Hidden>> for Media<Hidden, Shown, Shown> {
|
|
fn from(f: Media<Download, Hidden, Hidden>) -> Self {
|
|
Media {
|
|
dl: f.dl.into(),
|
|
size: f.size.into(),
|
|
progress: f.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Into New
|
|
impl Into<Media<Download, Hidden, Shown>> for Media<UnInitialized, UnInitialized, UnInitialized> {
|
|
fn into(self) -> Media<Download, Hidden, Shown> {
|
|
Media {
|
|
dl: self.dl.into(),
|
|
size: self.size.into(),
|
|
progress: self.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Into NewWithoutSize
|
|
impl Into<Media<Download, Hidden, Hidden>> for Media<UnInitialized, UnInitialized, UnInitialized> {
|
|
fn into(self) -> Media<Download, Hidden, Hidden> {
|
|
Media {
|
|
dl: self.dl.into(),
|
|
size: self.size.into(),
|
|
progress: self.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Into Playable
|
|
impl Into<Media<Play, Hidden, Shown>> for Media<UnInitialized, UnInitialized, UnInitialized> {
|
|
fn into(self) -> Media<Play, Hidden, Shown> {
|
|
Media {
|
|
dl: self.dl.into(),
|
|
size: self.size.into(),
|
|
progress: self.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Into PlayableWithoutSize
|
|
impl Into<Media<Play, Hidden, Hidden>> for Media<UnInitialized, UnInitialized, UnInitialized> {
|
|
fn into(self) -> Media<Play, Hidden, Hidden> {
|
|
Media {
|
|
dl: self.dl.into(),
|
|
size: self.size.into(),
|
|
progress: self.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Into InProgress
|
|
impl Into<Media<Hidden, Shown, Shown>> for Media<UnInitialized, UnInitialized, UnInitialized> {
|
|
fn into(self) -> Media<Hidden, Shown, Shown> {
|
|
Media {
|
|
dl: self.dl.into(),
|
|
size: self.size.into(),
|
|
progress: self.progress.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum MediaMachine {
|
|
UnInitialized(Media<UnInitialized, UnInitialized, UnInitialized>),
|
|
New(Media<Download, Shown, Hidden>),
|
|
NewWithoutSize(Media<Download, Hidden, Hidden>),
|
|
Playable(Media<Play, Shown, Hidden>),
|
|
PlayableWithoutSize(Media<Play, Hidden, Hidden>),
|
|
InProgress(Media<Hidden, Shown, Shown>),
|
|
}
|
|
|
|
impl MediaMachine {
|
|
pub fn new(
|
|
play: gtk::Button,
|
|
download: gtk::Button,
|
|
bar: gtk::ProgressBar,
|
|
cancel: gtk::Button,
|
|
total_size: gtk::Label,
|
|
local_size: gtk::Label,
|
|
separator: gtk::Label,
|
|
prog_separator: gtk::Label,
|
|
) -> Self {
|
|
let dl = DownloadPlay::<UnInitialized>::new(play, download);
|
|
let progress = Progress::<UnInitialized>::new(bar, cancel, local_size, prog_separator);
|
|
let size = Size::<UnInitialized>::new(total_size, separator);
|
|
|
|
MediaMachine::UnInitialized(Media { dl, progress, size })
|
|
}
|
|
|
|
pub fn determine_state(self, is_downloaded: bool, is_active: bool) -> Self {
|
|
// use self::MediaMachine::*;
|
|
|
|
unimplemented!()
|
|
}
|
|
}
|