mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Move player bar to separate widget
This commit is contained in:
parent
f16a27e343
commit
232faa6a2f
8 changed files with 261 additions and 141 deletions
90
data/ui/player_bar.blp
Normal file
90
data/ui/player_bar.blp
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
using Gtk 4.0;
|
||||||
|
using Adw 1;
|
||||||
|
|
||||||
|
template $MusicusPlayerBar : Gtk.Box {
|
||||||
|
styles ["playerbar"]
|
||||||
|
orientation: vertical;
|
||||||
|
margin-start: 6;
|
||||||
|
margin-end: 6;
|
||||||
|
margin-top: 12;
|
||||||
|
margin-bottom: 12;
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Box {
|
||||||
|
valign: center;
|
||||||
|
hexpand: true;
|
||||||
|
margin-start: 10;
|
||||||
|
orientation: vertical;
|
||||||
|
|
||||||
|
Gtk.Label title_label {
|
||||||
|
styles ["title"]
|
||||||
|
halign: start;
|
||||||
|
label: _("Title");
|
||||||
|
ellipsize: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Label subtitle_label {
|
||||||
|
styles ["subtitle", "dim-label"]
|
||||||
|
halign: start;
|
||||||
|
label: _("Subtitle");
|
||||||
|
ellipsize: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Button back_button {
|
||||||
|
styles ["circular", "flat"]
|
||||||
|
valign: center;
|
||||||
|
icon-name: "media-skip-backward-symbolic";
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.ToggleButton playlist_button {
|
||||||
|
styles ["flat", "circular"]
|
||||||
|
valign: center;
|
||||||
|
icon-name: "playlist-symbolic";
|
||||||
|
clicked => $show_playlist() swapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Button forward_button {
|
||||||
|
styles ["circular", "flat"]
|
||||||
|
valign: center;
|
||||||
|
icon-name: "media-skip-forward-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Button play_button {
|
||||||
|
styles ["circular", "flat"]
|
||||||
|
valign: center;
|
||||||
|
icon-name: "media-playback-start-symbolic";
|
||||||
|
clicked => $play_pause() swapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Label current_time_label {
|
||||||
|
styles ["caption", "numeric"]
|
||||||
|
valign: center;
|
||||||
|
label: "00:00";
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Scale slider {
|
||||||
|
valign: center;
|
||||||
|
hexpand: true;
|
||||||
|
adjustment: Gtk.Adjustment {
|
||||||
|
lower: 0;
|
||||||
|
upper: 1;
|
||||||
|
value: 0.2;
|
||||||
|
step-increment: 0.01;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Label remaining_time_label {
|
||||||
|
styles ["caption", "numeric"]
|
||||||
|
valign: center;
|
||||||
|
label: "01:00";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,93 +22,6 @@ template $MusicusWindow : Adw.ApplicationWindow {
|
||||||
Gtk.Revealer player_bar_revealer {
|
Gtk.Revealer player_bar_revealer {
|
||||||
reveal-child: true;
|
reveal-child: true;
|
||||||
transition-type: slide_up;
|
transition-type: slide_up;
|
||||||
|
|
||||||
Gtk.Box {
|
|
||||||
styles ["playerbar"]
|
|
||||||
orientation: vertical;
|
|
||||||
margin-start: 6;
|
|
||||||
margin-end: 6;
|
|
||||||
margin-top: 12;
|
|
||||||
margin-bottom: 12;
|
|
||||||
spacing: 6;
|
|
||||||
|
|
||||||
Gtk.Box {
|
|
||||||
spacing: 6;
|
|
||||||
|
|
||||||
Gtk.Box {
|
|
||||||
valign: center;
|
|
||||||
hexpand: true;
|
|
||||||
margin-start: 10;
|
|
||||||
orientation: vertical;
|
|
||||||
|
|
||||||
Gtk.Label title_label {
|
|
||||||
styles ["title"]
|
|
||||||
halign: start;
|
|
||||||
label: _("Title");
|
|
||||||
ellipsize: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Label subtitle_label {
|
|
||||||
styles ["subtitle", "dim-label"]
|
|
||||||
halign: start;
|
|
||||||
label: _("Subtitle");
|
|
||||||
ellipsize: end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Button back_button {
|
|
||||||
styles ["circular", "flat"]
|
|
||||||
valign: center;
|
|
||||||
icon-name: "media-skip-backward-symbolic";
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.ToggleButton playlist_button {
|
|
||||||
styles ["flat", "circular"]
|
|
||||||
valign: center;
|
|
||||||
icon-name: "playlist-symbolic";
|
|
||||||
toggled => $show_playlist() swapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Button forward_button {
|
|
||||||
styles ["circular", "flat"]
|
|
||||||
valign: center;
|
|
||||||
icon-name: "media-skip-forward-symbolic";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Box {
|
|
||||||
spacing: 6;
|
|
||||||
|
|
||||||
Gtk.Button play_button {
|
|
||||||
styles ["circular", "flat"]
|
|
||||||
valign: center;
|
|
||||||
icon-name: "media-playback-start-symbolic";
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Label current_time_label {
|
|
||||||
styles ["caption", "numeric"]
|
|
||||||
valign: center;
|
|
||||||
label: "00:00";
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Scale slider {
|
|
||||||
valign: center;
|
|
||||||
hexpand: true;
|
|
||||||
adjustment: Gtk.Adjustment {
|
|
||||||
lower: 0;
|
|
||||||
upper: 1;
|
|
||||||
value: 0.2;
|
|
||||||
step-increment: 0.01;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Label remaining_time_label {
|
|
||||||
styles ["caption", "numeric"]
|
|
||||||
valign: center;
|
|
||||||
label: "01:00";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
data/ui/home_page.blp
|
data/ui/home_page.blp
|
||||||
|
data/ui/player_bar.blp
|
||||||
data/ui/playlist_page.blp
|
data/ui/playlist_page.blp
|
||||||
data/ui/playlist_tile.blp
|
data/ui/playlist_tile.blp
|
||||||
data/ui/recording_tile.blp
|
data/ui/recording_tile.blp
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ mod config;
|
||||||
mod home_page;
|
mod home_page;
|
||||||
mod library;
|
mod library;
|
||||||
mod player;
|
mod player;
|
||||||
|
mod player_bar;
|
||||||
mod playlist_item;
|
mod playlist_item;
|
||||||
mod playlist_page;
|
mod playlist_page;
|
||||||
mod playlist_tile;
|
mod playlist_tile;
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,19 @@ mod imp {
|
||||||
impl MusicusPlayer {
|
impl MusicusPlayer {
|
||||||
pub fn set_current_index(&self, index: u32) {
|
pub fn set_current_index(&self, index: u32) {
|
||||||
let playlist = self.playlist.get().unwrap();
|
let playlist = self.playlist.get().unwrap();
|
||||||
|
|
||||||
if let Some(item) = playlist.item(self.current_index.get()) {
|
if let Some(item) = playlist.item(self.current_index.get()) {
|
||||||
item.downcast::<PlaylistItem>().unwrap().set_is_playing(false);
|
item.downcast::<PlaylistItem>()
|
||||||
|
.unwrap()
|
||||||
|
.set_is_playing(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current_index.set(index);
|
self.current_index.set(index);
|
||||||
|
|
||||||
if let Some(item) = playlist.item(index) {
|
if let Some(item) = playlist.item(index) {
|
||||||
item.downcast::<PlaylistItem>().unwrap().set_is_playing(true);
|
item.downcast::<PlaylistItem>()
|
||||||
|
.unwrap()
|
||||||
|
.set_is_playing(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +64,7 @@ impl MusicusPlayer {
|
||||||
|
|
||||||
pub fn append(&self, tracks: Vec<PlaylistItem>) {
|
pub fn append(&self, tracks: Vec<PlaylistItem>) {
|
||||||
let playlist = self.playlist();
|
let playlist = self.playlist();
|
||||||
|
|
||||||
for track in tracks {
|
for track in tracks {
|
||||||
playlist.append(&track);
|
playlist.append(&track);
|
||||||
}
|
}
|
||||||
|
|
@ -75,6 +79,15 @@ impl MusicusPlayer {
|
||||||
pub fn pause(&self) {
|
pub fn pause(&self) {
|
||||||
self.set_playing(false)
|
self.set_playing(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_item(&self) -> Option<PlaylistItem> {
|
||||||
|
let imp = self.imp();
|
||||||
|
imp.playlist
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.item(imp.current_index.get())
|
||||||
|
.and_downcast::<PlaylistItem>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MusicusPlayer {
|
impl Default for MusicusPlayer {
|
||||||
|
|
|
||||||
128
src/player_bar.rs
Normal file
128
src/player_bar.rs
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
use crate::player::MusicusPlayer;
|
||||||
|
use gtk::{
|
||||||
|
glib::{self, subclass::Signal, Properties},
|
||||||
|
prelude::*,
|
||||||
|
subclass::prelude::*,
|
||||||
|
};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
mod imp {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||||
|
#[properties(wrapper_type = super::PlayerBar)]
|
||||||
|
#[template(file = "data/ui/player_bar.blp")]
|
||||||
|
pub struct PlayerBar {
|
||||||
|
#[property(get, construct_only)]
|
||||||
|
pub player: RefCell<MusicusPlayer>,
|
||||||
|
|
||||||
|
#[template_child]
|
||||||
|
pub title_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub subtitle_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub back_button: TemplateChild<gtk::Button>,
|
||||||
|
#[template_child]
|
||||||
|
pub playlist_button: TemplateChild<gtk::ToggleButton>,
|
||||||
|
#[template_child]
|
||||||
|
pub forward_button: TemplateChild<gtk::Button>,
|
||||||
|
#[template_child]
|
||||||
|
pub play_button: TemplateChild<gtk::Button>,
|
||||||
|
#[template_child]
|
||||||
|
pub current_time_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub slider: TemplateChild<gtk::Scale>,
|
||||||
|
#[template_child]
|
||||||
|
pub remaining_time_label: TemplateChild<gtk::Label>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for PlayerBar {
|
||||||
|
const NAME: &'static str = "MusicusPlayerBar";
|
||||||
|
type Type = super::PlayerBar;
|
||||||
|
type ParentType = gtk::Box;
|
||||||
|
|
||||||
|
fn class_init(klass: &mut Self::Class) {
|
||||||
|
klass.bind_template();
|
||||||
|
klass.bind_template_instance_callbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||||
|
obj.init_template();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::derived_properties]
|
||||||
|
impl ObjectImpl for PlayerBar {
|
||||||
|
fn signals() -> &'static [Signal] {
|
||||||
|
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||||
|
vec![Signal::builder("show-playlist")
|
||||||
|
.param_types([glib::Type::BOOL])
|
||||||
|
.build()]
|
||||||
|
});
|
||||||
|
|
||||||
|
SIGNALS.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constructed(&self) {
|
||||||
|
self.parent_constructed();
|
||||||
|
|
||||||
|
self.player
|
||||||
|
.borrow()
|
||||||
|
.bind_property("playing", &self.play_button.get(), "icon-name")
|
||||||
|
.transform_to(|_, playing| {
|
||||||
|
Some(if playing {
|
||||||
|
"media-playback-pause-symbolic"
|
||||||
|
} else {
|
||||||
|
"media-playback-start-symbolic"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.sync_create()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetImpl for PlayerBar {}
|
||||||
|
impl BoxImpl for PlayerBar {}
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct PlayerBar(ObjectSubclass<imp::PlayerBar>)
|
||||||
|
@extends gtk::Widget, adw::Bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gtk::template_callbacks]
|
||||||
|
impl PlayerBar {
|
||||||
|
pub fn new(player: &MusicusPlayer) -> Self {
|
||||||
|
glib::Object::builder().property("player", player).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_show_playlist<F: Fn(&Self, bool) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||||
|
self.connect_local("show-playlist", true, move |values| {
|
||||||
|
let obj = values[0].get::<Self>().unwrap();
|
||||||
|
let show = values[1].get::<bool>().unwrap();
|
||||||
|
f(&obj, show);
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn playlist_hidden(&self) {
|
||||||
|
self.imp().playlist_button.set_active(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_playlist(&self, button: >k::ToggleButton) {
|
||||||
|
self.emit_by_name::<()>("show-playlist", &[&button.is_active()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn play_pause(&self, _: >k::Button) {
|
||||||
|
let player = self.player();
|
||||||
|
if player.playing() {
|
||||||
|
player.pause();
|
||||||
|
} else {
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ mod imp {
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct PlaylistTile(ObjectSubclass<imp::PlaylistTile>)
|
pub struct PlaylistTile(ObjectSubclass<imp::PlaylistTile>)
|
||||||
@extends gtk::Widget, gtk::FlowBoxChild;
|
@extends gtk::Widget, gtk::Box;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaylistTile {
|
impl PlaylistTile {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
home_page::MusicusHomePage, library::MusicusLibrary, player::MusicusPlayer,
|
home_page::MusicusHomePage, library::MusicusLibrary, player::MusicusPlayer,
|
||||||
playlist_page::MusicusPlaylistPage, welcome_page::MusicusWelcomePage,
|
player_bar::PlayerBar, playlist_page::MusicusPlaylistPage, welcome_page::MusicusWelcomePage,
|
||||||
};
|
};
|
||||||
use adw::subclass::prelude::*;
|
use adw::subclass::prelude::*;
|
||||||
use gtk::{gio, glib, glib::clone, prelude::*};
|
use gtk::{gio, glib, glib::clone, prelude::*};
|
||||||
use std::cell::OnceCell;
|
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -13,7 +12,6 @@ mod imp {
|
||||||
#[template(file = "data/ui/window.blp")]
|
#[template(file = "data/ui/window.blp")]
|
||||||
pub struct MusicusWindow {
|
pub struct MusicusWindow {
|
||||||
pub player: MusicusPlayer,
|
pub player: MusicusPlayer,
|
||||||
pub playlist_page: OnceCell<MusicusPlaylistPage>,
|
|
||||||
|
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub stack: TemplateChild<gtk::Stack>,
|
pub stack: TemplateChild<gtk::Stack>,
|
||||||
|
|
@ -21,10 +19,6 @@ mod imp {
|
||||||
pub navigation_view: TemplateChild<adw::NavigationView>,
|
pub navigation_view: TemplateChild<adw::NavigationView>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub player_bar_revealer: TemplateChild<gtk::Revealer>,
|
pub player_bar_revealer: TemplateChild<gtk::Revealer>,
|
||||||
#[template_child]
|
|
||||||
pub play_button: TemplateChild<gtk::Button>,
|
|
||||||
#[template_child]
|
|
||||||
pub playlist_button: TemplateChild<gtk::ToggleButton>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
|
@ -48,39 +42,31 @@ mod imp {
|
||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
self.obj().load_window_state();
|
self.obj().load_window_state();
|
||||||
|
|
||||||
|
let player_bar = PlayerBar::new(&self.player);
|
||||||
|
self.player_bar_revealer.set_child(Some(&player_bar));
|
||||||
|
|
||||||
|
let playlist_page = MusicusPlaylistPage::new(&self.player);
|
||||||
|
self.stack.add_named(&playlist_page, Some("playlist"));
|
||||||
|
|
||||||
|
playlist_page.connect_close(clone!(@weak player_bar => move |_| {
|
||||||
|
player_bar.playlist_hidden();
|
||||||
|
}));
|
||||||
|
|
||||||
|
let stack = self.stack.get();
|
||||||
|
player_bar.connect_show_playlist(clone!(@weak playlist_page => move |_, show| {
|
||||||
|
if show {
|
||||||
|
playlist_page.scroll_to_current();
|
||||||
|
stack.set_visible_child_name("playlist");
|
||||||
|
} else {
|
||||||
|
stack.set_visible_child_name("navigation");
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
self.player
|
self.player
|
||||||
.bind_property("active", &self.player_bar_revealer.get(), "reveal-child")
|
.bind_property("active", &self.player_bar_revealer.get(), "reveal-child")
|
||||||
.sync_create()
|
.sync_create()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let play_button = self.play_button.get();
|
|
||||||
|
|
||||||
self.player
|
|
||||||
.connect_playing_notify(clone!(@weak play_button => move |player| {
|
|
||||||
play_button.set_icon_name(if player.playing() {
|
|
||||||
"media-playback-pause-symbolic"
|
|
||||||
} else {
|
|
||||||
"media-playback-start-symbolic"
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.play_button
|
|
||||||
.connect_clicked(clone!(@weak self.player as player => move |_| {
|
|
||||||
if player.playing() {
|
|
||||||
player.pause();
|
|
||||||
} else {
|
|
||||||
player.play();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let playlist_page = MusicusPlaylistPage::new(&self.player);
|
|
||||||
let playlist_button = self.playlist_button.get();
|
|
||||||
playlist_page.connect_close(move |_| {
|
|
||||||
playlist_button.set_active(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.stack.add_named(&playlist_page, Some("playlist"));
|
|
||||||
self.playlist_page.set(playlist_page).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,16 +125,4 @@ impl MusicusWindow {
|
||||||
.navigation_view
|
.navigation_view
|
||||||
.replace(&[MusicusHomePage::new(&library, &self.imp().player).into()]);
|
.replace(&[MusicusHomePage::new(&library, &self.imp().player).into()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[template_callback]
|
|
||||||
fn show_playlist(&self, button: >k::ToggleButton) {
|
|
||||||
let imp = self.imp();
|
|
||||||
|
|
||||||
if button.is_active() {
|
|
||||||
imp.playlist_page.get().unwrap().scroll_to_current();
|
|
||||||
imp.stack.set_visible_child_name("playlist");
|
|
||||||
} else {
|
|
||||||
imp.stack.set_visible_child_name("navigation");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue