1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// queue_page.rs
//
// Copyright 2023 nee <nee-git@patchouli.garden>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::controls::Controls;
use crate::data::Action;
use crate::send;
use adw::prelude::*;
use anyhow::{Context, Result};
use glib::clone;
use gtk::subclass::prelude::*;
use gtk::{gio, glib};
use mpd;
use std::cell::RefCell;
#[derive(Debug, Default)]
pub struct InternalState {
pub items: Vec<gtk::Button>,
pub last_active_track: Option<usize>,
}
mod imp {
use super::*;
use gtk::CompositeTemplate;
#[derive(Debug, CompositeTemplate, Default)]
#[template(resource = "/blue/hidamari/pmpdc/ui/queue_page.ui")]
pub struct QueuePage {
#[template_child]
pub menu_button: TemplateChild<gtk::MenuButton>, // TODO move out of tracklist to keep the menu open while changing servers
#[template_child]
pub scroll: TemplateChild<gtk::ScrolledWindow>,
#[template_child]
pub controls_bin: TemplateChild<adw::Bin>,
#[template_child]
pub open_filters_button: TemplateChild<gtk::Button>,
pub state: RefCell<Option<InternalState>>,
}
#[glib::object_subclass]
impl ObjectSubclass for QueuePage {
const NAME: &'static str = "QueuePage";
type Type = super::QueuePage;
type ParentType = gtk::Box;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
}
// You must call `Widget`'s `init_template()` within `instance_init()`.
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for QueuePage {}
impl WidgetImpl for QueuePage {}
impl BoxImpl for QueuePage {}
}
glib::wrapper! {
pub struct QueuePage(ObjectSubclass<imp::QueuePage>)
@extends gtk::Widget, gtk::Box,
@implements gio::ActionMap, gio::ActionGroup;
}
impl QueuePage {
pub fn new(
sender: &glib::Sender<Action>,
track_list: gtk::Box,
tracks: Vec<gtk::Button>,
controls: &Controls,
active_track: Option<usize>,
) -> Self {
let widget: Self = glib::Object::new();
widget.imp().scroll.set_child(Some(&track_list));
widget.imp().controls_bin.set_child(Some(controls));
widget
.imp()
.open_filters_button
.connect_clicked(clone!(@strong sender => move |_| {
send!(sender, Action::OpenFilterPage);
}));
*widget.imp().state.borrow_mut() = Some(InternalState {
items: tracks,
last_active_track: active_track,
});
widget
}
pub fn sync_with_status(&mut self, status: &mpd::Status) -> Result<()> {
let new_pos = status.song.as_ref().map(|s| s.pos);
let mut state = self.imp().state.borrow_mut();
let state = state.as_mut().context("failed to get queue_page state")?;
if state.last_active_track != new_pos {
let item = new_pos.and_then(|np| state.items.get(np));
item.map(|i| i.style_context().add_class("track_active"));
let last_item = state.last_active_track.and_then(|lp| state.items.get(lp));
last_item.map(|i| i.style_context().remove_class("track_active"));
state.last_active_track = new_pos;
}
Ok(())
}
}