✨ 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:
parent
f4e142a242
commit
86e4c4f110
13 changed files with 148 additions and 5 deletions
2
config/default.toml
Normal file
2
config/default.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[log]
|
||||||
|
tracing = "Info,pagetop=Debug"
|
3
src/base.rs
Normal file
3
src/base.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
//! Reúne temas listos para usar.
|
||||||
|
|
||||||
|
pub mod theme;
|
4
src/base/theme.rs
Normal file
4
src/base/theme.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
//! Temas básicos soportados por `PageTop`.
|
||||||
|
|
||||||
|
mod basic;
|
||||||
|
pub use basic::Basic;
|
14
src/base/theme/basic.rs
Normal file
14
src/base/theme/basic.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//! Es el tema básico que incluye `PageTop` por defecto.
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
/// Tema básico por defecto.
|
||||||
|
pub struct Basic;
|
||||||
|
|
||||||
|
impl ExtensionTrait for Basic {
|
||||||
|
fn theme(&self) -> Option<ThemeRef> {
|
||||||
|
Some(&Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeTrait for Basic {}
|
|
@ -1,4 +1,4 @@
|
||||||
//! Tipos y funciones esenciales para crear extensiones.
|
//! Tipos y funciones esenciales para crear extensiones y temas.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
|
@ -203,3 +203,6 @@ impl<T: ?Sized + AnyInfo> AnyCast for T {}
|
||||||
|
|
||||||
// API para añadir nuevas funcionalidades usando extensiones.
|
// API para añadir nuevas funcionalidades usando extensiones.
|
||||||
pub mod extension;
|
pub mod extension;
|
||||||
|
|
||||||
|
// API para añadir y gestionar nuevos temas.
|
||||||
|
pub mod theme;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::core::extension::ExtensionRef;
|
use crate::core::extension::ExtensionRef;
|
||||||
|
use crate::core::theme::all::THEMES;
|
||||||
use crate::{service, trace};
|
use crate::{service, trace};
|
||||||
|
|
||||||
use std::sync::{LazyLock, RwLock};
|
use std::sync::{LazyLock, RwLock};
|
||||||
|
@ -17,6 +18,9 @@ pub fn register_extensions(root_extension: Option<ExtensionRef>) {
|
||||||
// Prepara la lista de extensiones habilitadas.
|
// Prepara la lista de extensiones habilitadas.
|
||||||
let mut enabled_list: Vec<ExtensionRef> = Vec::new();
|
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.
|
// Si se proporciona una extensión raíz inicial, se añade a la lista de extensiones habilitadas.
|
||||||
if let Some(extension) = root_extension {
|
if let Some(extension) = root_extension {
|
||||||
add_to_enabled(&mut enabled_list, 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.
|
// Añade la propia extensión a la lista.
|
||||||
list.push(extension);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::core::theme::ThemeRef;
|
||||||
use crate::core::AnyInfo;
|
use crate::core::AnyInfo;
|
||||||
use crate::locale::L10n;
|
use crate::locale::L10n;
|
||||||
use crate::service;
|
use crate::service;
|
||||||
|
@ -36,6 +37,31 @@ pub trait ExtensionTrait: AnyInfo + Send + Sync {
|
||||||
L10n::default()
|
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.
|
/// Otras extensiones que deben habilitarse **antes** de esta.
|
||||||
///
|
///
|
||||||
/// `PageTop` las resolverá automáticamente respetando el orden durante el arranque de la
|
/// `PageTop` las resolverá automáticamente respetando el orden durante el arranque de la
|
||||||
|
|
6
src/core/theme.rs
Normal file
6
src/core/theme.rs
Normal 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
31
src/core/theme/all.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
30
src/core/theme/definition.rs
Normal file
30
src/core/theme/definition.rs
Normal 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 {}
|
|
@ -8,6 +8,7 @@ include_config!(SETTINGS: Settings => [
|
||||||
// [app]
|
// [app]
|
||||||
"app.name" => "Sample",
|
"app.name" => "Sample",
|
||||||
"app.description" => "Developed with the amazing PageTop framework.",
|
"app.description" => "Developed with the amazing PageTop framework.",
|
||||||
|
"app.theme" => "Basic",
|
||||||
"app.language" => "en-US",
|
"app.language" => "en-US",
|
||||||
"app.startup_banner" => "Slant",
|
"app.startup_banner" => "Slant",
|
||||||
|
|
||||||
|
@ -40,6 +41,8 @@ pub struct App {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Breve descripción de la aplicación.
|
/// Breve descripción de la aplicación.
|
||||||
pub description: String,
|
pub description: String,
|
||||||
|
/// Tema predeterminado.
|
||||||
|
pub theme: String,
|
||||||
/// Idioma predeterminado (localización).
|
/// Idioma predeterminado (localización).
|
||||||
pub language: String,
|
pub language: String,
|
||||||
/// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o
|
/// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o
|
||||||
|
|
|
@ -87,10 +87,12 @@ pub mod html;
|
||||||
pub mod locale;
|
pub mod locale;
|
||||||
// Soporte a fechas y horas.
|
// Soporte a fechas y horas.
|
||||||
pub mod datetime;
|
pub mod datetime;
|
||||||
// Tipos y funciones esenciales para crear extensiones.
|
// Tipos y funciones esenciales para crear extensiones y temas.
|
||||||
pub mod core;
|
pub mod core;
|
||||||
// Gestión del servidor y servicios web.
|
// Gestión del servidor y servicios web.
|
||||||
pub mod service;
|
pub mod service;
|
||||||
|
// Reúne temas listos para usar.
|
||||||
|
pub mod base;
|
||||||
// Prepara y ejecuta la aplicación.
|
// Prepara y ejecuta la aplicación.
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@ pub use crate::include_config;
|
||||||
pub use crate::include_locales;
|
pub use crate::include_locales;
|
||||||
// crate::service
|
// crate::service
|
||||||
pub use crate::{include_files, include_files_service};
|
pub use crate::{include_files, include_files_service};
|
||||||
// crate::core::action
|
|
||||||
//pub use crate::actions;
|
|
||||||
|
|
||||||
// API.
|
// API.
|
||||||
|
|
||||||
|
@ -37,7 +35,9 @@ pub use crate::service;
|
||||||
|
|
||||||
pub use crate::core::{AnyCast, AnyInfo, TypeInfo};
|
pub use crate::core::{AnyCast, AnyInfo, TypeInfo};
|
||||||
|
|
||||||
//pub use crate::core::action::*;
|
|
||||||
pub use crate::core::extension::*;
|
pub use crate::core::extension::*;
|
||||||
|
pub use crate::core::theme::*;
|
||||||
|
|
||||||
|
pub use crate::base::theme;
|
||||||
|
|
||||||
pub use crate::app::Application;
|
pub use crate::app::Application;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue