// Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only use derive_setters::Setters; use regex::Regex; use slab::Slab; use crate::{Binder, Page}; slotmap::new_key_type! { /// The unique ID of a section of page. pub struct Entity; } pub type ShowWhileFn = Box Fn(&'a dyn Page) -> bool>; pub type ViewFn = Box< dyn for<'a> Fn( &'a Binder, &'a dyn Page, &'a Section, ) -> cosmic::Element<'a, Message>, >; /// A searchable sub-component of a page. /// /// Searches can group multiple sections together. #[derive(Setters)] #[must_use] pub struct Section { #[setters(into)] pub title: String, #[setters(into)] pub descriptions: Slab, #[setters(skip)] pub show_while: Option>, #[setters(skip)] pub view_fn: ViewFn, #[setters(bool)] pub search_ignore: bool, } impl Default for Section { fn default() -> Self { Self { title: String::new(), descriptions: Slab::new(), show_while: None, view_fn: Box::new(unimplemented), search_ignore: false, } } } impl Section { #[must_use] #[inline] pub fn search_matches(&self, rule: &Regex) -> bool { if self.search_ignore { return false; } if rule.is_match(self.title.as_str()) { return true; } for (_, description) in &self.descriptions { if rule.is_match(description) { return true; } } false } #[inline] pub fn show_while>( mut self, func: impl for<'a> Fn(&'a Model) -> bool + 'static, ) -> Self { self.show_while = Some(Box::new(move |model: &dyn Page| { let model = model.downcast_ref::().unwrap_or_else(|| { panic!( "page model type mismatch: expected {}", std::any::type_name::() ) }); func(model) })); self } /// # Panics /// /// Will panic if the `Model` type does not match the page type. #[inline] pub fn view>( mut self, func: impl for<'a> Fn( &'a Binder, &'a Model, &'a Section, ) -> cosmic::Element<'a, Message> + 'static, ) -> Self { self.view_fn = Box::new(move |binder, model: &dyn Page, section| { let model = model.downcast_ref::().unwrap_or_else(|| { panic!( "page model type mismatch: expected {}", std::any::type_name::() ) }); func(binder, model, section) }); self } } #[must_use] #[inline] pub fn unimplemented<'a, Message: Clone + 'static>( _binder: &'a Binder, _page: &'a dyn Page, _section: &'a Section, ) -> cosmic::Element<'a, Message> { cosmic::widget::settings::view_column(vec![cosmic::widget::settings::section().into()]).into() }