Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layout Change: Queue View Widget #485

Open
wants to merge 58 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
2295cef
Add ability to add to queue
May 7, 2024
856a70a
add ability to take queued song to other playlists, TODO: make it so …
May 10, 2024
7f0bab4
Add to queue rework - Now queues are held
May 12, 2024
ecd8546
Fix if switching contexts e.g: playing a different song from a differ…
May 31, 2024
9a99249
Change added_items to user_added_items, Lint using clippy.
Jun 16, 2024
8f13fdd
Linting done with format
Jun 16, 2024
bc9791f
Var name to user_items
Jun 16, 2024
a00272b
Remove redundant comment
Jun 16, 2024
4f5296a
UI change starting
Jun 19, 2024
6f27d9b
Merge branch 'master' of https://github.com/SO9010/psst
Jun 19, 2024
456a319
Queue view added (INCOMPLETE)
Jun 30, 2024
2ffb38a
Clear up
Jun 30, 2024
1b0d7c5
Remove from quieue buttom
Jun 30, 2024
b6fb954
Clean up and consistency...
jacksongoode Jul 2, 2024
67a7536
Center queue
jacksongoode Jul 2, 2024
ecb5256
Merge remote-tracking branch 'upstream/master'
Jul 12, 2024
abbc357
Align dividers
Jul 12, 2024
b67267d
Add left and right dividers
Jul 12, 2024
98f1824
Dynamically resize left side bar
Jul 14, 2024
d56ffcc
Allow long titles to break
jacksongoode Jul 14, 2024
6a1bf9f
Fix constraint causing issues
Jul 18, 2024
81f23cd
Merge branch 'master' of https://github.com/SO9010/psst
Jul 18, 2024
a60f52b
Improved widget switching
Jul 20, 2024
81d6ba0
More function, but it needs improvment.
Jul 20, 2024
e58b263
Fix overlap, this also makes the change in size smoother
Jul 21, 2024
5f59715
Hide queue if there are no items.
Jul 21, 2024
faf93b7
Lint
Jul 21, 2024
1c1e7bd
aha oops wrong way round
Jul 21, 2024
e9100c5
Info alert added
Jul 21, 2024
e976452
Remove by index
Jul 24, 2024
708c14f
Improved skipping method
Jul 25, 2024
57bd9c5
Add option to settings
Jul 25, 2024
f383da8
Clear queue
Jul 25, 2024
b9977aa
Fix oopsie and introduce context menu
Jul 25, 2024
40d1b6d
Merge branch 'jpochyla:master' into master
SO9010 Jul 25, 2024
8c3b1ae
Merge branch 'jpochyla:master' into master
SO9010 Jul 29, 2024
ceb9caa
Context menu update
Jul 29, 2024
9a62df4
Lint
Jul 29, 2024
17e60ba
Linting
jacksongoode Jul 30, 2024
c091fa1
Merge branch 'jpochyla:master' into master
SO9010 Aug 7, 2024
13215a7
Remove first song, need to clear some bugs now with removing from queue.
Aug 7, 2024
b308119
Fix remove from queue
Aug 7, 2024
5137a34
Fix skip to place in queue
Aug 7, 2024
13d4567
Lint
Aug 7, 2024
6e0e748
Fix queue repeats
Aug 9, 2024
3f48cbf
Merge branch 'jpochyla:master' into master
SO9010 Aug 24, 2024
2c4acfe
Fix dodgy playing
Aug 24, 2024
6ab7708
Linty
Aug 24, 2024
b42c7c9
Fix mistake
Aug 24, 2024
6f2d9b5
start to fix problems
Aug 26, 2024
cddcee0
start making it play if theres nothing there
Aug 27, 2024
ba9cbdb
Play even when no playlist is playing
Aug 27, 2024
ed3702e
Improvement
Aug 28, 2024
4e35a6c
Merge branch 'jpochyla:master' into master
SO9010 Sep 3, 2024
c7128f1
Lint!
Sep 19, 2024
9c61420
Merge branch 'jpochyla:master' into master
SO9010 Sep 23, 2024
f69b626
Merge branch 'jpochyla:master' into master
SO9010 Sep 26, 2024
5bf8a69
neaten appstate
Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions psst-core/src/player/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ impl Player {
PlayerCommand::Seek { position } => self.seek(position),
PlayerCommand::Configure { config } => self.configure(config),
PlayerCommand::SetQueueBehavior { behavior } => self.queue.set_behaviour(behavior),
PlayerCommand::SkipToPlaceInQueue { item } => self.queue.skip_to_place_in_queue(item),
PlayerCommand::AddToQueue { item } => self.queue.add(item),
PlayerCommand::RemoveFromQueue { item } => self.queue.remove(item),
PlayerCommand::SetVolume { volume } => self.set_volume(volume),
}
}
Expand Down Expand Up @@ -423,9 +425,15 @@ pub enum PlayerCommand {
SetQueueBehavior {
behavior: QueueBehavior,
},
SkipToPlaceInQueue {
item: usize,
},
AddToQueue {
item: PlaybackItem,
},
RemoveFromQueue {
item: usize,
},
/// Change playback volume to a value in 0.0..=1.0 range.
SetVolume {
volume: f64,
Expand Down
8 changes: 8 additions & 0 deletions psst-core/src/player/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,19 @@ impl Queue {
self.position = position;
self.compute_positions();
}

pub fn skip_to_place_in_queue(&mut self, index: usize) {
self.user_items_position = self.user_items_position + (index - self.user_items_position);
}

pub fn add(&mut self, item: PlaybackItem) {
self.user_items.push(item);
}

pub fn remove(&mut self, index: usize) {
self.user_items.remove(index);
}

fn handle_added_queue(&mut self) {
if self.user_items.len() > self.user_items_position {
self.items.insert(
Expand Down
5 changes: 5 additions & 0 deletions psst-gui/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ pub const ADD_TO_QUEUE: Selector<(QueueEntry, PlaybackItem)> = Selector::new("ap
pub const PLAY_QUEUE_BEHAVIOR: Selector<QueueBehavior> = Selector::new("app.play-queue-behavior");
pub const PLAY_SEEK: Selector<f64> = Selector::new("app.play-seek");

// Queue control
// Todo Skip by
pub const REMOVE_FROM_QUEUE: Selector<QueueEntry> = Selector::new("app.remove-from-queue");
pub const SKIP_TO_PLACE_IN_QUEUE: Selector<QueueEntry> = Selector::new("app.skip-to-place-in-queue");

// Sorting control
pub const SORT_BY_DATE_ADDED: Selector = Selector::new("app.sort-by-date-added");
pub const SORT_BY_TITLE: Selector = Selector::new("app.sort-by-title");
Expand Down
46 changes: 45 additions & 1 deletion psst-gui/src/controller/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use druid::{
Code, ExtEventSink, InternalLifeCycle, KbKey, WindowHandle,
};
use psst_core::{
audio::{normalize::NormalizationLevel, output::DefaultAudioOutput},
audio::{normalize::{self, NormalizationLevel}, output::DefaultAudioOutput},
cache::Cache,
cdn::Cdn,
player::{item::PlaybackItem, PlaybackConfig, Player, PlayerCommand, PlayerEvent},
Expand Down Expand Up @@ -265,6 +265,10 @@ impl PlaybackController {
fn next(&mut self) {
self.send(PlayerEvent::Command(PlayerCommand::Next));
}
// Implemet properly, this will only skip by one, we need to use the inc val
fn skip_by(&mut self, inc: i32) {
self.send(PlayerEvent::Command(PlayerCommand::Next));
}

fn stop(&mut self) {
self.send(PlayerEvent::Command(PlayerCommand::Stop));
Expand Down Expand Up @@ -299,6 +303,16 @@ impl PlaybackController {
item: *item,
}));
}
fn skip_to_place_in_queue(&mut self, item: &usize) {
self.send(PlayerEvent::Command(PlayerCommand::SkipToPlaceInQueue {
item: *item,
}));
}
fn remove_from_queue(&mut self, item: &usize) {
self.send(PlayerEvent::Command(PlayerCommand::RemoveFromQueue {
item: *item,
}));
}

fn set_queue_behavior(&mut self, behavior: QueueBehavior) {
self.send(PlayerEvent::Command(PlayerCommand::SetQueueBehavior {
Expand Down Expand Up @@ -436,6 +450,36 @@ where
}
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::SKIP_TO_PLACE_IN_QUEUE) => {
let item = cmd.get_unchecked(cmd::SKIP_TO_PLACE_IN_QUEUE);

// We need a way so it starts playing even if theres no playlist being played from!
// We also need a block to stop it from erroring if they click one back.
for i in 0..data.added_queue.len() {
if data.added_queue[i].item.id() == item.item.id() {
self.skip_to_place_in_queue(&i);
break;
}
}

self.next();
ctx.set_handled();
}
Event::Command(cmd) if cmd.is(cmd::REMOVE_FROM_QUEUE) => {
let item = cmd.get_unchecked(cmd::REMOVE_FROM_QUEUE);

// This is the best i could figure out, But this currently just removes the first item that matches the id
// I think its the same issue as with the removal of a song from a playlist
// TODO: Change this to retain
for i in 0..data.added_queue.len() {
if data.added_queue[i].item.id() == item.item.id() {
data.added_queue.remove(i);
self.remove_from_queue(&i);
break;
}
}
ctx.set_handled();
}
// Keyboard shortcuts.
Event::KeyDown(key) if key.code == Code::Space => {
self.pause_or_resume();
Expand Down
7 changes: 7 additions & 0 deletions psst-gui/src/data/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ impl Playable {
}
}

pub fn artist(&self) -> Arc<String> {
match self {
Playable::Track(track) => Arc::new(track.artist_names()),
Playable::Episode(episode) => Arc::new(episode.show.name.to_string()),
}
}

pub fn duration(&self) -> Duration {
match self {
Playable::Track(track) => track.duration,
Expand Down
24 changes: 12 additions & 12 deletions psst-gui/src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::time::Duration;

use druid::{
im::Vector,
widget::{CrossAxisAlignment, Either, Flex, Label, List, Scroll, Slider, Split, ViewSwitcher},
Color, Env, Insets, Key, LensExt, Menu, MenuItem, Selector, Widget, WidgetExt, WindowDesc,
im::Vector, widget::{CrossAxisAlignment, Either, Flex, Label, List, Scroll, Slider, ViewSwitcher}, Color, Env, Insets, Key, LensExt, Menu, MenuItem, Selector, Widget, WidgetExt, WindowDesc
};
use druid_shell::Cursor;

Expand Down Expand Up @@ -37,6 +35,7 @@ pub mod theme;
pub mod track;
pub mod user;
pub mod utils;
pub mod queued;

pub fn main_window(config: &Config) -> WindowDesc<AppState> {
let win = WindowDesc::new(root_widget())
Expand Down Expand Up @@ -129,13 +128,13 @@ fn root_widget() -> impl Widget<AppState> {
.with_default_spacer()
.with_child(user::user_widget())
.center()
.fix_height(100.0)
.fix_height(88.0)
.background(Border::Top.with_color(theme::GREY_500));

let sidebar = Flex::column()
.with_flex_child(playlists, 1.0)
.with_child(controls)
.background(theme::BACKGROUND_DARK);
.background(Border::Right.with_color(theme::GREY_500));

let topbar = Flex::row()
.must_fill_main_axis(true)
Expand All @@ -150,18 +149,18 @@ fn root_widget() -> impl Widget<AppState> {
.with_flex_child(Overlay::bottom(route_widget(), alert_widget()), 1.0)
.with_child(playback::panel_widget())
.background(theme::BACKGROUND_LIGHT);

let split = Split::columns(sidebar, main)
.split_point(0.2)
.bar_size(1.0)
.min_size(150.0, 300.0)
.min_bar_area(1.0)
.solid_bar(true);
SO9010 marked this conversation as resolved.
Show resolved Hide resolved

let split = Flex::row()
.with_flex_child(sidebar, 0.3)
.with_flex_child(main, 1.0)
.with_child(queued::queue_widget())
.background(theme::BACKGROUND_DARK);

ThemeScope::new(split)
.controller(SessionController)
.controller(NavController)
.controller(SortController)

// .debug_invalidation()
// .debug_widget_id()
// .debug_paint_layout()
Expand Down Expand Up @@ -280,6 +279,7 @@ fn sidebar_menu_widget() -> impl Widget<AppState> {
fn sidebar_link_widget(title: &str, link_nav: Nav) -> impl Widget<AppState> {
Label::new(title)
.padding((theme::grid(2.0), theme::grid(1.0)))
// Not perfect, we need to find a way to stop it from clipping the side
.expand_width()
.link()
.env_scope({
Expand Down
93 changes: 93 additions & 0 deletions psst-gui/src/ui/queued.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::{
cmd,
data::{AppState, QueueEntry},
ui::Vector,
widget::{icons, Border, Empty, MyWidgetExt},
};

use druid::{
widget::{CrossAxisAlignment, Either, Flex, Label, LineBreaking, List, Scroll}, Env, Widget, WidgetExt
};
use druid_shell::Cursor;

use super::theme;

pub fn queue_widget() -> impl Widget<AppState> {
Either::new(
|data: &AppState, _env: &Env| data.config.window_size.width >= 700.0,
Flex::column()
.with_child(queue_header_widget())
.with_flex_child(
Scroll::new(queue_list_widget())
.vertical()
// (how will we handle it after the song has been played? will it remain or disappear?)
.lens(AppState::added_queue),
1.0,
)
.fix_width(185.0)
.background(Border::Left.with_color(theme::GREY_500)),
Empty
)
}

fn queue_header_widget() -> impl Widget<AppState> {
Flex::row()
.with_flex_child(
Label::new("Queue")
.with_font(theme::UI_FONT_MEDIUM)
.with_text_size(theme::TEXT_SIZE_LARGE)
.center(),
1.0,
)
.fix_height(32.0)
.padding(theme::grid(1.0))
.expand_width()
.background(Border::Bottom.with_color(theme::GREY_500))
}

fn queue_list_widget() -> impl Widget<Vector<QueueEntry>> {
List::new(|| {
Flex::row()
.with_flex_child(
Flex::column()
.cross_axis_alignment(CrossAxisAlignment::Start)
.with_child(
Label::new(|item: &QueueEntry, _env: &Env| item.item.name().to_string())
.with_font(theme::UI_FONT_MEDIUM)
.with_line_break_mode(LineBreaking::Clip)
.expand_width(),
)
.with_spacer(2.0)
.with_child(
Label::new(|item: &QueueEntry, _env: &Env| item.item.artist().to_string())
.with_text_size(theme::TEXT_SIZE_SMALL)
.with_text_color(theme::PLACEHOLDER_COLOR)
.with_line_break_mode(LineBreaking::Clip)
.expand_width(),
)
.on_click(|ctx, data: &mut QueueEntry, _| {
ctx.submit_command(
cmd::SKIP_TO_PLACE_IN_QUEUE.with(data.clone())
);
}),
1.0,
)
.with_default_spacer()
.with_child(
icons::CLOSE_CIRCLE
.scale((16.0, 16.0))
.link()
.rounded(100.0)
.on_click(|ctx, data: &mut QueueEntry, _| {
ctx.submit_command(
cmd::REMOVE_FROM_QUEUE.with(data.clone())
);
})
.with_cursor(Cursor::Pointer),
)
.padding(theme::grid(1.0))
.link()
.rounded(theme::BUTTON_BORDER_RADIUS)
})
.padding(theme::grid(1.0))
}
6 changes: 6 additions & 0 deletions psst-gui/src/widget/icons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ pub static HEART_OUTLINE: SvgIcon = SvgIcon {
svg_size: Size::new(24.0, 24.0),
op: PaintOp::Fill,
};
// Font Awesome - Close-circle
pub static CLOSE_CIRCLE: SvgIcon = SvgIcon {
svg_path: "M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12 13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z",
svg_size: Size::new(24.0, 24.0),
op: PaintOp::Fill,
};

#[derive(Copy, Clone)]
pub enum PaintOp {
Expand Down
28 changes: 26 additions & 2 deletions psst-gui/src/widget/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ impl<T: Data, S: Shape, W: Widget<T>> Widget<T> for Clip<S, W> {
pub enum Border {
Top,
Bottom,
// Todo implement this so then we can have borders on the sides.
// This is needed as the split widget had the border.
Left,
Right,
}

impl Border {
Expand All @@ -181,11 +185,31 @@ impl Border {

Painter::new(move |ctx, _, env| {
let h = 1.0;
let y = match self {
let w1 = match self {
Self::Top => 0.0,
Self::Bottom => 0.0,
Self::Left => h / 2.0,
Self::Right => ctx.size().width - h / 2.0,
};
let w2 = match self {
Self::Top => ctx.size().width,
Self::Bottom => ctx.size().width,
Self::Left => h / 2.0,
Self::Right => ctx.size().width - h / 2.0,
};
let y1 = match self {
Self::Top => h / 2.0,
Self::Bottom => ctx.size().height - h / 2.0,
Self::Left => ctx.size().height - h / 2.0,
Self::Right => ctx.size().height - h / 2.0,
};
let y2 = match self {
Self::Top => h / 2.0,
Self::Bottom => ctx.size().height - h / 2.0,
Self::Left => h / 2.0,
Self::Right => h / 2.0,
};
let line = Line::new((0.0, y), (ctx.size().width, y));
let line = Line::new((w1, y1), (w2, y2));
ctx.stroke(line, &color.resolve(env), h);
SO9010 marked this conversation as resolved.
Show resolved Hide resolved
})
}
Expand Down
Loading