xmpd/xmpd-gui/src/components/player.rs

160 lines
6.5 KiB
Rust

use egui::{RichText, Sense, Stroke, Vec2};
use xmpd_manifest::store::BaseStore;
use super::{song_list::SongList, CompGetter, CompUi};
#[derive(Debug)]
pub struct Player {
slider_progress: usize,
old_slider_progress: usize,
volume_slider: f64,
}
impl Default for Player {
fn default() -> Self {
Self {
volume_slider: 1.0,
old_slider_progress: 0,
slider_progress: 0
}
}
}
component_register!(Player);
impl CompUi for Player {
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
let theme = xmpd_settings::Settings::get()?.theme.clone();
let full_avail = ui.available_size();
ui.horizontal_centered(|ui| {
ui.add_space(10.0);
let icon = egui::Image::new(crate::data::NOTE_ICON)
.tint(theme.accent_color)
.sense(Sense::click())
.fit_to_exact_size(Vec2::new(32.0, 32.0));
ui.add(icon);
ui.vertical(|ui| {
ui.add_space(5.0);
let sid = &handle_error_ui!(SongList::get()).selected_sid;
if let Some(song) = state.manifest.store().get_song(sid) {
let mut name = song.name().to_string();
if name.len() > 16 {
name = (&name)[..16].to_string();
name.push_str("...");
}
ui.label(
RichText::new(name)
.size(12.0)
);
ui.label(
RichText::new(song.author())
.size(8.0)
.monospace()
);
}
});
ui.vertical_centered_justified(|ui| {
let avail = ui.available_size();
let song_info_w = full_avail.x - avail.x;
ui.add_space(3.0);
ui.horizontal(|ui| {
{
let slider_width = full_avail.x * 0.60;
ui.add_space((((full_avail.x / 2.0) - song_info_w) - slider_width / 2.0).clamp(0.0, f32::MAX));
ui.style_mut().spacing.slider_width = avail.x * 0.75;
let s = Stroke {
color: theme.accent_color,
width: 2.0
};
ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
ui.style_mut().visuals.widgets.active.fg_stroke = s;
ui.style_mut().visuals.widgets.hovered.fg_stroke = s;
let mut slf = handle_error_ui!(Player::get());
ui.add(
egui::Slider::new(&mut slf.slider_progress, 0..=100)
.show_value(false)
);
if slf.slider_progress == slf.old_slider_progress {
slf.slider_progress = (state.player.get_played_f() * 100.0) as usize;
slf.old_slider_progress = slf.slider_progress;
} else {
handle_error_ui!(state.player.seek_to_f(slf.slider_progress as f64 / 100.0 ));
slf.old_slider_progress = slf.slider_progress;
}
let secs_left = state.player.get_ms_left() as f64 / 1000.0;
let h = (secs_left/60.0/60.0).floor();
let m = ((secs_left - h * 60.0)/60.0).floor();
let s = (secs_left - m * 60.0).floor();
ui.label(format!("{h:02}:{m:02}:{s:02}"));
}
});
ui.horizontal(|ui| {
let icon_size = 16.0;
ui.add_space(((full_avail.x / 2.0) - song_info_w) - icon_size * 1.5 - ui.spacing().item_spacing.x);
let pp = if state.player.is_paused() {
crate::data::PLAY_ICON
} else {
crate::data::PAUSE_ICON
};
let prev = egui::Image::new(crate::data::PREV_ICON)
.tint(theme.accent_color)
.sense(Sense::click())
.max_size(Vec2::new(icon_size, icon_size));
let pp = egui::Image::new(pp)
.tint(theme.accent_color)
.sense(Sense::click())
.max_size(Vec2::new(icon_size, icon_size));
let next = egui::Image::new(crate::data::NEXT_ICON)
.tint(theme.accent_color)
.sense(Sense::click())
.max_size(Vec2::new(icon_size, icon_size));
if ui.add(prev).clicked() {
handle_error_ui!(handle_error_ui!(SongList::get()).play_prev(state));
}
if ui.add(pp).clicked() {
if state.player.is_paused() {
state.player.play();
} else {
state.player.pause();
}
}
if ui.add(next).clicked() || state.player.just_stopped() {
handle_error_ui!(handle_error_ui!(SongList::get()).play_next(state));
}
ui.add_space(15.0);
ui.style_mut().spacing.slider_width = avail.x * 0.15;
let s = Stroke {
color: theme.accent_color,
width: 1.0
};
ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
ui.style_mut().visuals.widgets.active.fg_stroke = s;
ui.style_mut().visuals.widgets.hovered.fg_stroke = s;
let mut slf = handle_error_ui!(Player::get());
let slider =ui.add(
egui::Slider::new(&mut slf.volume_slider, 0.0..=1.0)
.show_value(false)
);
if slider.changed() {
state.player.set_volume(slf.volume_slider);
}
});
ui.add_space(3.0);
});
});
Ok(())
}
}