diff --git a/Cargo.toml b/Cargo.toml index 3166733..709d8a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members=[ ] [workspace.package] -version="2.1.1" +version="2.1.2" edition="2024" repository="https://git.mcorangehq.xyz/XOR64/xmpd/" license="GPL-3.0" diff --git a/xmpd-gui/src/components/song_list/header.rs b/xmpd-gui/src/components/song_list/header.rs index 65a643a..36157d3 100644 --- a/xmpd-gui/src/components/song_list/header.rs +++ b/xmpd-gui/src/components/song_list/header.rs @@ -1,8 +1,10 @@ +use eframe::Frame; +use egui::{Align, Align2, Area, Key, Layout, Order, Style}; use uuid::Uuid; use xmpd_cache::DlStatus; use xmpd_manifest::{song::Song, store::BaseStore}; -use crate::{components::{left_nav::LeftNav, toast::ToastType, CompGetter, CompUi}, windows::WindowId}; +use crate::{components::{CompGetter, CompUi, left_nav::LeftNav, toast::ToastType}, utils::SearchType, windows::WindowId}; use super::SongList; @@ -15,6 +17,8 @@ pub struct Header { component_register!(Header); + + impl CompUi for Header { fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> { let theme = xmpd_settings::Settings::get()?.theme.clone(); @@ -26,7 +30,53 @@ impl CompUi for Header { .tint(theme.accent_color); ui.add(search_icon); { - ui.text_edit_singleline(&mut handle_error_ui!(Header::get()).search_text); + let resp = ui.text_edit_singleline(&mut handle_error_ui!(Header::get()).search_text); + let popup_id = resp.id.with("mode_popup"); + if resp.clicked() { + ui.memory_mut(|m| m.open_popup(popup_id)); + } + + // NOTE: Genuinely cancerous code incoming + let search_text = handle_error_ui!(Header::get()).search_text.clone(); + if !search_text.is_empty() { + if ui.memory(|mem: &egui::Memory| mem.is_popup_open(popup_id)) { + Area::new(popup_id) + .order(Order::Foreground) + .constrain(true) + .fixed_pos(resp.rect.left_bottom()) + .pivot(Align2::LEFT_TOP) + .show(ui.ctx(), |ui| { + let tf = crate::main_window::get_themed_frame(&theme) + .stroke(egui::Stroke::new( + 1.5, + theme.secondary_bg_color, + )); + let frame_margin = tf.total_margin(); + tf.show(ui, |ui| { + ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| { + ui.set_width(resp.rect.width() - frame_margin.sum().x); + let st = SearchType::from_str(&search_text); + let style = ui.style_mut(); + style.visuals.selection.bg_fill = theme.secondary_bg_color; + style.visuals.selection.stroke.color = theme.accent_color; + for item in &[SearchType::Author, SearchType::Source, SearchType::Genre] { + if ui.selectable_label(st.0 == *item, item.new_query(&st.1)).clicked() { + { + handle_error_ui!(Header::get()).search_text = item.new_query(&st.1); + } + ui.memory_mut(|m| m.close_popup()); + } + } + }) + }) + }); + + if ui.input(|i| i.key_pressed(Key::Escape)) || resp.clicked_elsewhere() { + ui.memory_mut(|mem| mem.close_popup()); + } + } + + } } ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| { diff --git a/xmpd-gui/src/main_window.rs b/xmpd-gui/src/main_window.rs index ea4dc7e..9b91a55 100644 --- a/xmpd-gui/src/main_window.rs +++ b/xmpd-gui/src/main_window.rs @@ -94,7 +94,7 @@ pub fn draw(ctx: &egui::Context, state: &mut GuiState, cache_rx: &Receiver egui::Frame { +pub fn get_themed_frame(theme: &Theme) -> egui::Frame { egui::Frame::none() .fill(theme.primary_bg_color) .stroke(egui::Stroke::new( diff --git a/xmpd-gui/src/utils.rs b/xmpd-gui/src/utils.rs index b092e16..dd15ffb 100644 --- a/xmpd-gui/src/utils.rs +++ b/xmpd-gui/src/utils.rs @@ -1,9 +1,12 @@ +use std::fmt::Display; -#[derive(Debug, Clone)] + +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum SearchType { Normal, Author, Source, + Genre } impl SearchType { @@ -13,9 +16,27 @@ impl SearchType { (Self::Source, i.strip_prefix("source:").unwrap_or("").to_string().to_lowercase()), i @ _ if i.starts_with("author:") => (Self::Author, i.strip_prefix("author:").unwrap_or("").to_string().to_lowercase()), + i @ _ if i.starts_with("genre:") => + (Self::Author, i.strip_prefix("genre:").unwrap_or("").to_string().to_lowercase()), i @ _ => (Self::Normal, i.to_string().to_lowercase()) } } + pub fn new_query(&self, s: &str) -> String { + format!("{self}:{s}") + } +} + +impl Display for SearchType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Normal => "", + Self::Genre => "genre", + Self::Source => "source", + Self::Author => "author" + }; + f.write_str(s)?; + Ok(()) + } } pub fn super_separator(ui: &mut egui::Ui, color: egui::Color32, width: f32, height: f32) {