From 697689c3ab4289810e1dc84c89245a70bf008c00 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Thu, 11 Jan 2024 00:57:18 +0000 Subject: [PATCH] Add listen search interface --- assets/images/album_art_test.jpg | 3 +++ shalom/src/main.rs | 4 +++- shalom/src/pages/room.rs | 16 +++++++--------- shalom/src/pages/room/listen.rs | 35 ++++++++++++++++++++++++----------- shalom/src/pages/room/listen/search.rs | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shalom/src/theme.rs | 2 ++ shalom/src/widgets/mouse_area.rs | 4 ++-- 7 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 assets/images/album_art_test.jpg create mode 100644 shalom/src/pages/room/listen/search.rs diff --git a/assets/images/album_art_test.jpg b/assets/images/album_art_test.jpg new file mode 100644 index 0000000..0aac16c --- /dev/null +++ b/assets/images/album_art_test.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cae52d3c493233ab1681825b15059315572c035937c42937c829e6b359188b8 +size 15475 diff --git a/shalom/src/main.rs b/shalom/src/main.rs index 973c15b..46b7a4e 100644 --- a/shalom/src/main.rs +++ b/shalom/src/main.rs @@ -25,6 +25,7 @@ pub struct Shalom { context_menu: Option, oracle: Option>, home_room: Option<&'static str>, + theme: Theme, } impl Shalom { @@ -175,6 +176,7 @@ impl Application for Shalom { context_menu: None, oracle: None, home_room: Some("living_room"), + theme: Theme::default(), }; // this is only best-effort to try and prevent blocking when loading @@ -251,7 +253,7 @@ impl Application for Shalom { fn view(&self) -> Element<'_, Self::Message, Renderer> { let page_content = match &self.page { ActivePage::Loading => Element::from(column!["Loading...",].spacing(20)), - ActivePage::Room(room) => room.view().map(Message::RoomEvent), + ActivePage::Room(room) => room.view(&self.theme).map(Message::RoomEvent), ActivePage::Omni(omni) => omni.view().map(Message::OmniEvent), }; diff --git a/shalom/src/pages/room.rs b/shalom/src/pages/room.rs index 406fc0c..bb8fafc 100644 --- a/shalom/src/pages/room.rs +++ b/shalom/src/pages/room.rs @@ -7,8 +7,8 @@ use iced::{ advanced::graphics::core::Element, font::{Stretch, Weight}, theme, - widget::{container, lazy, row, text, Column}, - Color, Font, Length, Renderer, Subscription, + widget::{container, row, text, Column}, + Color, Font, Length, Renderer, Subscription, Theme, }; use crate::{ @@ -58,7 +58,7 @@ impl Room { } } - pub fn view(&self) -> Element<'_, Message, Renderer> { + pub fn view(&self, style: &Theme) -> Element<'_, Message, Renderer> { let header = text(self.room.name.as_ref()) .size(60) .font(Font { @@ -69,11 +69,9 @@ impl Room { .style(theme::Text::Color(Color::WHITE)); let header = if let Page::Listen = self.current_page { - Element::from(lazy(self.room.name.as_ref(), move |_| { - self.listen - .header_magic(header.clone()) - .map(Message::Listen) - })) + self.listen + .header_magic(header.clone()) + .map(Message::Listen) } else { Element::from(header) }; @@ -84,7 +82,7 @@ impl Room { col = col.push(match self.current_page { Page::Climate => Element::from(row![]), - Page::Listen => self.listen.view().map(Message::Listen), + Page::Listen => self.listen.view(style).map(Message::Listen), Page::Lights => container(self.lights.view().map(Message::Lights)) .padding([0, 40, 0, 40]) .into(), diff --git a/shalom/src/pages/room/listen.rs b/shalom/src/pages/room/listen.rs index be060e5..71e3dc7 100644 --- a/shalom/src/pages/room/listen.rs +++ b/shalom/src/pages/room/listen.rs @@ -1,10 +1,12 @@ +mod search; + use std::{convert::identity, sync::Arc, time::Duration}; use iced::{ futures::StreamExt, subscription, - widget::{container, image::Handle, Column, Text}, - Element, Renderer, Subscription, + widget::{container, image::Handle, lazy, Column, Text}, + Element, Length, Renderer, Subscription, Theme, }; use url::Url; @@ -47,13 +49,18 @@ impl Listen { } } - pub fn header_magic<'a>(&self, text: Text<'a>) -> Element<'a, Message> { - header_search( - Message::OnSearchTerm, - Message::OnSearchVisibleChange, - self.search_open, - &self.search_query, - text, + pub fn header_magic(&self, text: Text<'static>) -> Element<'static, Message> { + lazy( + (self.search_open, self.search_query.clone()), + move |(open, query)| { + header_search( + Message::OnSearchTerm, + Message::OnSearchVisibleChange, + *open, + query, + text.clone(), + ) + }, ) .into() } @@ -159,13 +166,19 @@ impl Listen { } Message::OnSearchVisibleChange(v) => { self.search_open = v; + self.search_query = String::new(); None } } } - pub fn view(&self) -> Element<'_, Message, Renderer> { - if let Some((_, speaker)) = self.speaker.clone() { + pub fn view(&self, style: &Theme) -> Element<'_, Message, Renderer> { + if self.search_open && !self.search_query.is_empty() { + container(search::search().view(style)) + .padding([0, 40, 40, 40]) + .width(Length::Fill) + .into() + } else if let Some((_, speaker)) = self.speaker.clone() { container( widgets::media_player::media_player(speaker, self.album_art_image.clone()) .with_artist_logo( diff --git a/shalom/src/pages/room/listen/search.rs b/shalom/src/pages/room/listen/search.rs new file mode 100644 index 0000000..64d83cc --- /dev/null +++ b/shalom/src/pages/room/listen/search.rs @@ -0,0 +1,102 @@ +use iced::{ + theme, + widget::{ + column, container, container::Appearance, horizontal_rule, image, image::Handle, row, + scrollable, text, Column, + }, + Alignment, Background, Color, Element, Length, Renderer, Theme, +}; + +use crate::{theme::Image, widgets::mouse_area::mouse_area}; + +pub fn search() -> Search { + Search { + on_track_press: None, + } +} + +pub struct Search { + on_track_press: Option M>, +} + +impl Search { + pub fn view(&self, style: &Theme) -> Element<'static, M, Renderer> { + let mut col = Column::new(); + + for i in 0..20 { + if i != 0 { + col = col.push(hr()); + } + + let track = mouse_area(search_item_container(track_card( + "title", + "artist", + Image::AlbumArtTest, + style, + ))) + .on_press(self.on_track_press.map(|f| (f)("hello world".to_string()))); + + col = col.push(track); + } + + search_container(scrollable(col.spacing(10))) + } +} + +fn track_card( + title: &str, + artist: &str, + image_handle: impl Into, + style: &Theme, +) -> Element<'static, M, Renderer> { + let title = text(title).style(style.extended_palette().background.base.text); + let artist = text(artist).style(style.extended_palette().background.strong.color); + + row![ + image(image_handle).width(64).height(64), + column![title, artist,] + ] + .align_items(Alignment::Center) + .spacing(10) + .into() +} + +fn hr() -> Element<'static, M, Renderer> { + container(horizontal_rule(1)) + .width(Length::Fill) + .padding([10, 0, 10, 0]) + .into() +} + +fn search_item_container<'a, M: 'a>( + elem: impl Into>, +) -> Element<'a, M, Renderer> { + container(elem).padding([0, 20, 0, 20]).into() +} + +fn search_container<'a, M: 'a>( + elem: impl Into>, +) -> Element<'a, M, Renderer> { + container(elem) + .padding([20, 0, 20, 0]) + .width(Length::Fill) + .style(theme::Container::Custom(Box::new(SearchContainer))) + .into() +} + +#[allow(clippy::module_name_repetitions)] +pub struct SearchContainer; + +impl container::StyleSheet for SearchContainer { + type Style = Theme; + + fn appearance(&self, _style: &Self::Style) -> Appearance { + Appearance { + text_color: Some(Color::BLACK), + background: Some(Background::Color(Color::WHITE)), + border_radius: 20.0.into(), + border_width: 0.0, + border_color: Color::default(), + } + } +} diff --git a/shalom/src/theme.rs b/shalom/src/theme.rs index a977117..2728ef9 100644 --- a/shalom/src/theme.rs +++ b/shalom/src/theme.rs @@ -225,6 +225,7 @@ pub enum Image { Bedroom, DiningRoom, Sunset, + AlbumArtTest, } impl Image { @@ -249,6 +250,7 @@ impl Image { Image::Bedroom => image!("../../assets/images/bedroom.jpg"), Image::DiningRoom => image!("../../assets/images/dining_room.jpg"), Image::Sunset => image!("../../assets/images/sunset-blur.jpg"), + Image::AlbumArtTest => image!("../../assets/images/album_art_test.jpg"), } } diff --git a/shalom/src/widgets/mouse_area.rs b/shalom/src/widgets/mouse_area.rs index f6bbb59..a3aae21 100644 --- a/shalom/src/widgets/mouse_area.rs +++ b/shalom/src/widgets/mouse_area.rs @@ -43,8 +43,8 @@ pub struct MouseArea<'a, Message, Renderer> { impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { /// The message to emit on a left button press. #[must_use] - pub fn on_press(mut self, message: Message) -> Self { - self.on_press = Some(message); + pub fn on_press(mut self, message: impl Into>) -> Self { + self.on_press = message.into(); self } -- libgit2 1.7.2