Añade soporte a temas en la API de extensiones

- Incluye una opción de configuración para definir el tema por defecto.
- Añade un tema básico predeterminado.
This commit is contained in:
Manuel Cillero 2025-07-20 23:51:15 +02:00
parent f4e142a242
commit 86e4c4f110
13 changed files with 148 additions and 5 deletions

View file

@ -1,4 +1,5 @@
use crate::core::extension::ExtensionRef;
use crate::core::theme::all::THEMES;
use crate::{service, trace};
use std::sync::{LazyLock, RwLock};
@ -17,6 +18,9 @@ pub fn register_extensions(root_extension: Option<ExtensionRef>) {
// Prepara la lista de extensiones habilitadas.
let mut enabled_list: Vec<ExtensionRef> = Vec::new();
// Primero añade el tema básico a la lista de extensiones habilitadas.
add_to_enabled(&mut enabled_list, &crate::base::theme::Basic);
// Si se proporciona una extensión raíz inicial, se añade a la lista de extensiones habilitadas.
if let Some(extension) = root_extension {
add_to_enabled(&mut enabled_list, extension);
@ -53,6 +57,21 @@ fn add_to_enabled(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
// Añade la propia extensión a la lista.
list.push(extension);
// Comprueba si la extensión tiene un tema asociado que deba registrarse.
if let Some(theme) = extension.theme() {
let mut registered_themes = THEMES.write().unwrap();
// Asegura que el tema no esté ya registrado para evitar duplicados.
if !registered_themes
.iter()
.any(|t| t.type_id() == theme.type_id())
{
registered_themes.push(theme);
trace::debug!("Enabling \"{}\" theme", theme.short_name());
}
} else {
trace::debug!("Enabling \"{}\" extension", extension.short_name());
}
}
}

View file

@ -1,3 +1,4 @@
use crate::core::theme::ThemeRef;
use crate::core::AnyInfo;
use crate::locale::L10n;
use crate::service;
@ -36,6 +37,31 @@ pub trait ExtensionTrait: AnyInfo + Send + Sync {
L10n::default()
}
/// Los temas son extensiones que implementan [`ExtensionTrait`] y también
/// [`ThemeTrait`](crate::core::theme::ThemeTrait).
///
/// Si la extensión no es un tema, este método devuelve `None` por defecto.
///
/// En caso contrario, este método debe implementarse para devolver una referencia de sí mismo
/// como tema. Por ejemplo:
///
/// ```rust
/// use pagetop::prelude::*;
///
/// pub struct MyTheme;
///
/// impl ExtensionTrait for MyTheme {
/// fn theme(&self) -> Option<ThemeRef> {
/// Some(&Self)
/// }
/// }
///
/// impl ThemeTrait for MyTheme {}
/// ```
fn theme(&self) -> Option<ThemeRef> {
None
}
/// Otras extensiones que deben habilitarse **antes** de esta.
///
/// `PageTop` las resolverá automáticamente respetando el orden durante el arranque de la

6
src/core/theme.rs Normal file
View file

@ -0,0 +1,6 @@
//! API para añadir y gestionar nuevos temas.
mod definition;
pub use definition::{ThemeRef, ThemeTrait};
pub(crate) mod all;

31
src/core/theme/all.rs Normal file
View file

@ -0,0 +1,31 @@
use crate::core::theme::ThemeRef;
use crate::global;
use std::sync::{LazyLock, RwLock};
// TEMAS *******************************************************************************************
pub static THEMES: LazyLock<RwLock<Vec<ThemeRef>>> = LazyLock::new(|| RwLock::new(Vec::new()));
// TEMA PREDETERMINADO *****************************************************************************
pub static DEFAULT_THEME: LazyLock<ThemeRef> =
LazyLock::new(|| match theme_by_short_name(&global::SETTINGS.app.theme) {
Some(theme) => theme,
None => &crate::base::theme::Basic,
});
// TEMA POR NOMBRE *********************************************************************************
pub fn theme_by_short_name(short_name: impl AsRef<str>) -> Option<ThemeRef> {
let short_name = short_name.as_ref().to_lowercase();
match THEMES
.read()
.unwrap()
.iter()
.find(|t| t.short_name().to_lowercase() == short_name)
{
Some(theme) => Some(*theme),
_ => None,
}
}

View file

@ -0,0 +1,30 @@
use crate::core::extension::ExtensionTrait;
/// Representa una referencia a un tema.
///
/// Los temas son también extensiones. Por tanto se deben definir igual, es decir, como instancias
/// estáticas globales que implementan [`ThemeTrait`], pero también [`ExtensionTrait`].
pub type ThemeRef = &'static dyn ThemeTrait;
/// Interfaz común que debe implementar cualquier tema de `PageTop`.
///
/// Un tema implementará [`ThemeTrait`] y los métodos que sean necesarios de [`ExtensionTrait`],
/// aunque el único obligatorio es [`theme()`](crate::core::extension::ExtensionTrait#method.theme).
///
/// ```rust
/// use pagetop::prelude::*;
///
/// pub struct MyTheme;
///
/// impl ExtensionTrait for MyTheme {
/// fn name(&self) -> L10n { L10n::n("My theme") }
/// fn description(&self) -> L10n { L10n::n("Un tema personal") }
///
/// fn theme(&self) -> Option<ThemeRef> {
/// Some(&Self)
/// }
/// }
///
/// impl ThemeTrait for MyTheme {}
/// ```
pub trait ThemeTrait: ExtensionTrait + Send + Sync {}