From 6d11ee970552dc4bb4c5e26cb2b387973c484329 Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Mon, 1 Apr 2024 18:43:00 +0200 Subject: [PATCH] Add wip program tiles --- data/res/style.css | 49 ++++++++++++++++++++++ data/ui/home_page.blp | 10 +++++ data/ui/program_tile.blp | 32 ++++++++++++++ src/home_page.rs | 85 +++++++++++++++++++++++++++---------- src/library.rs | 10 +++++ src/main.rs | 1 + src/program_tile.rs | 90 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 255 insertions(+), 22 deletions(-) create mode 100644 data/ui/program_tile.blp create mode 100644 src/program_tile.rs diff --git a/data/res/style.css b/data/res/style.css index 4563c49..48526ed 100644 --- a/data/res/style.css +++ b/data/res/style.css @@ -64,6 +64,55 @@ font-size: smaller; } +.program { + padding: 12px; + min-width: 200px; +} + +.program .title { + margin-top: 6px; + font-size: larger; + font-weight: bold; +} + +.program.highlight { + color: white; + transition: transform 100ms; +} + +.program.highlight.program1 { + background: linear-gradient(-225deg, #ac32e4 0%, #7918f2 48%, #4801ff 100%); +} + +.program.highlight.program2 { + background: linear-gradient(145deg, #f12711, #f5af19); +} + +.program.highlight.program3 { + background: linear-gradient(-80deg, #ad5389, #3c1053); +} + +.program.highlight.program4 { + background: linear-gradient(140deg, #136797, #0b486b); +} + +.program.highlight.program5 { + background: linear-gradient(100deg, #6a9113, #141517); +} + +.program.highlight.program6 { + background: linear-gradient(120deg, #870000, #190a05); +} + + +.program.highlight:hover { + transform: scale(1.01); +} + +.program.highlight:active { + transform: scale(0.99); +} + .selector>contents { padding: 0; } diff --git a/data/ui/home_page.blp b/data/ui/home_page.blp index 645aa15..17dd4e1 100644 --- a/data/ui/home_page.blp +++ b/data/ui/home_page.blp @@ -94,6 +94,16 @@ template $MusicusHomePage: Adw.NavigationPage { margin-top: 24; margin-bottom: 68; + Gtk.FlowBox programs_flow_box { + margin-top: 12; + margin-bottom: 24; + column-spacing: 12; + row-spacing: 12; + homogeneous: true; + selection-mode: none; + child-activated => $program_selected() swapped; + } + Gtk.Label { styles [ "heading" diff --git a/data/ui/program_tile.blp b/data/ui/program_tile.blp new file mode 100644 index 0000000..e4b683d --- /dev/null +++ b/data/ui/program_tile.blp @@ -0,0 +1,32 @@ +using Gtk 4.0; + +template $MusicusProgramTile : Gtk.FlowBoxChild { + styles ["program", "card", "activatable"] + + Gtk.Box { + orientation: vertical; + + Gtk.Button edit_button { + styles ["flat", "circular"] + halign: end; + icon-name: "document-edit-symbolic"; + } + + Gtk.Label title_label { + styles ["title"] + halign: start; + margin-top: 24; + wrap: true; + max-width-chars: 0; + + } + + Gtk.Label description_label { + styles ["description"] + margin-top: 6; + halign: start; + wrap: true; + max-width-chars: 0; + } + } +} \ No newline at end of file diff --git a/src/home_page.rs b/src/home_page.rs index 46f90a4..621e542 100644 --- a/src/home_page.rs +++ b/src/home_page.rs @@ -5,6 +5,7 @@ use crate::{ library::{LibraryQuery, MusicusLibrary}, player::MusicusPlayer, playlist_item::PlaylistItem, + program_tile::{MusicusProgramTile, Program, ProgramTileDesign}, recording_tile::MusicusRecordingTile, search_entry::MusicusSearchEntry, search_tag::Tag, @@ -53,6 +54,8 @@ mod imp { #[template_child] pub subtitle_label: TemplateChild, #[template_child] + pub programs_flow_box: TemplateChild, + #[template_child] pub composers_flow_box: TemplateChild, #[template_child] pub performers_flow_box: TemplateChild, @@ -104,6 +107,30 @@ mod imp { .sync_create() .build(); + self.programs_flow_box + .append(&MusicusProgramTile::new(Program { + title: "Just play some music".to_string(), + description: "Randomly select some music. Customize programs using the button in the top right." + .to_string(), + design: Some(ProgramTileDesign::Program1) + }, + )); + + self.programs_flow_box + .append(&MusicusProgramTile::new(Program { + title: "What's new?".to_string(), + description: "Recordings that you recently added to your music library." + .to_string(), + design: Some(ProgramTileDesign::Program2), + })); + + self.programs_flow_box + .append(&MusicusProgramTile::new(Program { + title: "A long time ago".to_string(), + description: "Works that you haven't listend to for a long time.".to_string(), + design: Some(ProgramTileDesign::Program3), + })); + self.obj().query(&LibraryQuery::default()); } } @@ -167,32 +194,44 @@ impl MusicusHomePage { fn select(&self, search_entry: &MusicusSearchEntry) { let imp = self.imp(); - let (composer, performer, ensemble, work, recording, album) = { - ( - imp.composers.borrow().first().cloned(), - imp.performers.borrow().first().cloned(), - imp.ensembles.borrow().first().cloned(), - imp.works.borrow().first().cloned(), - imp.recordings.borrow().first().cloned(), - imp.albums.borrow().first().cloned(), - ) - }; + if imp.programs_flow_box.is_visible() { + log::info!("Program selected"); + } else { + let (composer, performer, ensemble, work, recording, album) = { + ( + imp.composers.borrow().first().cloned(), + imp.performers.borrow().first().cloned(), + imp.ensembles.borrow().first().cloned(), + imp.works.borrow().first().cloned(), + imp.recordings.borrow().first().cloned(), + imp.albums.borrow().first().cloned(), + ) + }; - if let Some(person) = composer { - search_entry.add_tag(Tag::Composer(person)); - } else if let Some(person) = performer { - search_entry.add_tag(Tag::Performer(person)); - } else if let Some(ensemble) = ensemble { - search_entry.add_tag(Tag::Ensemble(ensemble)); - } else if let Some(work) = work { - search_entry.add_tag(Tag::Work(work)); - } else if let Some(recording) = recording { - self.play_recording(&recording); - } else if let Some(album) = album { - self.show_album(&album); + if let Some(person) = composer { + search_entry.add_tag(Tag::Composer(person)); + } else if let Some(person) = performer { + search_entry.add_tag(Tag::Performer(person)); + } else if let Some(ensemble) = ensemble { + search_entry.add_tag(Tag::Ensemble(ensemble)); + } else if let Some(work) = work { + search_entry.add_tag(Tag::Work(work)); + } else if let Some(recording) = recording { + self.play_recording(&recording); + } else if let Some(album) = album { + self.show_album(&album); + } } } + #[template_callback] + fn program_selected(&self, tile: >k::FlowBoxChild, _: >k::FlowBox) { + log::info!( + "Program selected: {:?}", + tile.downcast_ref::().unwrap().program() + ); + } + #[template_callback] fn tile_selected(&self, tile: >k::FlowBoxChild, _: >k::FlowBox) { self.imp() @@ -303,6 +342,8 @@ impl MusicusHomePage { } } + imp.programs_flow_box.set_visible(query.is_empty()); + if let Some(tag) = imp.search_entry.tags().first() { match tag { Tag::Composer(person) | Tag::Performer(person) => { diff --git a/src/library.rs b/src/library.rs index 0027bf4..a59792c 100644 --- a/src/library.rs +++ b/src/library.rs @@ -496,6 +496,16 @@ pub struct LibraryQuery { pub search: String, } +impl LibraryQuery { + pub fn is_empty(&self) -> bool { + self.composer.is_none() + && self.performer.is_none() + && self.ensemble.is_none() + && self.work.is_none() + && self.search.is_empty() + } +} + #[derive(Default, Debug)] pub struct LibraryResults { pub composers: Vec, diff --git a/src/main.rs b/src/main.rs index d903ecf..8f416f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ mod player_bar; mod playlist_item; mod playlist_page; mod playlist_tile; +mod program_tile; mod recording_tile; mod search_entry; mod search_tag; diff --git a/src/program_tile.rs b/src/program_tile.rs new file mode 100644 index 0000000..967025b --- /dev/null +++ b/src/program_tile.rs @@ -0,0 +1,90 @@ +use adw::prelude::WidgetExt; +use gtk::{glib, subclass::prelude::*}; +use std::cell::OnceCell; + +mod imp { + use super::*; + + #[derive(Debug, Default, gtk::CompositeTemplate)] + #[template(file = "data/ui/program_tile.blp")] + pub struct MusicusProgramTile { + #[template_child] + pub edit_button: TemplateChild, + #[template_child] + pub title_label: TemplateChild, + #[template_child] + pub description_label: TemplateChild, + + pub program: OnceCell, + } + + #[glib::object_subclass] + impl ObjectSubclass for MusicusProgramTile { + const NAME: &'static str = "MusicusProgramTile"; + type Type = super::MusicusProgramTile; + type ParentType = gtk::FlowBoxChild; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for MusicusProgramTile {} + impl WidgetImpl for MusicusProgramTile {} + impl FlowBoxChildImpl for MusicusProgramTile {} +} + +glib::wrapper! { + pub struct MusicusProgramTile(ObjectSubclass) + @extends gtk::Widget, gtk::FlowBoxChild; +} + +impl MusicusProgramTile { + pub fn new(program: Program) -> Self { + let obj: Self = glib::Object::new(); + let imp = obj.imp(); + + if let Some(design) = program.design { + obj.add_css_class("highlight"); + obj.add_css_class(match design { + ProgramTileDesign::Program1 => "program1", + ProgramTileDesign::Program2 => "program2", + ProgramTileDesign::Program3 => "program3", + ProgramTileDesign::Program4 => "program4", + ProgramTileDesign::Program5 => "program5", + ProgramTileDesign::Program6 => "program6", + }) + } + + imp.title_label.set_label(&program.title); + imp.description_label.set_label(&program.description); + imp.program.set(program).unwrap(); + + obj + } + + pub fn program(&self) -> &Program { + self.imp().program.get().unwrap() + } +} + +#[derive(Debug, Default)] +pub struct Program { + pub title: String, + pub description: String, + pub design: Option, +} + +#[derive(Clone, Copy, Debug)] +pub enum ProgramTileDesign { + Program1, + Program2, + Program3, + Program4, + Program5, + Program6, +}