use crate::{
config::{self, Theme},
key,
ui::{self, Orientation},
utils::filtered_items_from_query,
};
#[cfg(feature = "image")]
use crate::ui::cover_image::CoverImage;
#[cfg(feature = "image")]
use ratatui_image::picker::Picker;
pub type UIStateGuard<'a> = parking_lot::MutexGuard<'a, UIState>;
mod page;
mod popup;
pub use page::*;
pub use popup::*;
#[cfg(feature = "image")]
#[derive(Default)]
pub struct ImageRenderInfo {
pub url: String,
pub render_area: ratatui::layout::Rect,
pub state: Option<CoverImage>,
}
#[cfg(feature = "image")]
impl std::fmt::Debug for ImageRenderInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImageRenderInfo")
.field("url", &self.url)
.field("render_area", &self.render_area)
.field("state", &self.state.is_some())
.finish()
}
}
#[derive(Debug)]
pub struct UIState {
pub is_running: bool,
pub theme: config::Theme,
pub input_key_sequence: key::KeySequence,
pub orientation: ui::Orientation,
pub history: Vec<PageState>,
pub popup: Option<PopupState>,
pub playback_progress_bar_rect: ratatui::layout::Rect,
pub count_prefix: Option<usize>,
#[cfg(feature = "image")]
pub last_cover_image_render_info: ImageRenderInfo,
#[cfg(feature = "image")]
pub picker: Picker,
}
impl UIState {
pub fn current_page(&self) -> &PageState {
self.history.last().expect("non-empty history")
}
pub fn current_page_mut(&mut self) -> &mut PageState {
self.history.last_mut().expect("non-empty history")
}
pub fn new_search_popup(&mut self) {
self.current_page_mut().select(0);
self.popup = Some(PopupState::Search {
query: String::new(),
});
}
pub fn new_page(&mut self, page: PageState) {
self.popup = None;
if let Some(current_page) = self.history.last() {
if &page == current_page {
return;
}
}
self.history.push(page);
}
pub fn has_focused_popup(&self) -> bool {
match self.popup.as_ref() {
None => false,
Some(popup) => !matches!(popup, PopupState::Search { .. }),
}
}
pub fn search_filtered_items<'a, T: std::fmt::Display>(&self, items: &'a [T]) -> Vec<&'a T> {
match self.popup {
Some(PopupState::Search { ref query }) => filtered_items_from_query(query, items),
_ => items.iter().collect::<Vec<_>>(),
}
}
}
use ratatui::layout::Rect;
impl Default for UIState {
fn default() -> Self {
Self {
is_running: true,
theme: Theme::default(),
input_key_sequence: key::KeySequence { keys: vec![] },
orientation: match crossterm::terminal::size() {
Ok((columns, rows)) => ui::Orientation::from_size(columns, rows),
Err(err) => {
tracing::warn!("Unable to get terminal size, error: {err:#}");
Orientation::default()
}
},
history: vec![PageState::Library {
state: LibraryPageUIState::new(),
}],
popup: None,
playback_progress_bar_rect: Rect::default(),
count_prefix: None,
#[cfg(feature = "image")]
last_cover_image_render_info: ImageRenderInfo::default(),
#[cfg(feature = "image")]
picker: Picker::halfblocks(),
}
}
}