diff --git a/cursive-core/src/align.rs b/cursive-core/src/align.rs index 77f46201..d13adc3e 100644 --- a/cursive-core/src/align.rs +++ b/cursive-core/src/align.rs @@ -61,6 +61,24 @@ impl Align { } } +impl std::str::FromStr for Align { + type Err = (); + fn from_str(s: &str) -> Result { + Ok(match s { + "top_left" => Self::top_left(), + "top_center" => Self::top_center(), + "top_right" => Self::top_right(), + "center_left" => Self::center_left(), + "center" => Self::center(), + "center_right" => Self::center_right(), + "bot_left" | "bottom_left" => Self::bot_left(), + "bot_center" | "bottom_center" => Self::bot_center(), + "bot_right" | "bottom_right" => Self::bot_right(), + _ => return Err(()), + }) + } +} + /// Horizontal alignment #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum HAlign { @@ -72,6 +90,18 @@ pub enum HAlign { Right, } +impl std::str::FromStr for HAlign { + type Err = (); + fn from_str(s: &str) -> Result { + Ok(match s { + "left" | "Left" => Self::Left, + "center" | "Center" => Self::Center, + "right" | "Right" => Self::Right, + _ => return Err(()), + }) + } +} + /// Vertical alignment #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum VAlign { @@ -83,6 +113,18 @@ pub enum VAlign { Bottom, } +impl std::str::FromStr for VAlign { + type Err = (); + fn from_str(s: &str) -> Result { + Ok(match s { + "Top" | "top" => Self::Top, + "Center" | "center" => Self::Center, + "Bottom" | "bottom" | "Bot" | "bot" => Self::Bottom, + _ => return Err(()), + }) + } +} + impl HAlign { /// Returns the offset required to position a view. /// diff --git a/cursive-core/src/builder.rs b/cursive-core/src/builder.rs index 454d7758..3d61f1c8 100644 --- a/cursive-core/src/builder.rs +++ b/cursive-core/src/builder.rs @@ -938,7 +938,21 @@ macro_rules! manual_blueprint { #[cfg(feature = "builder")] #[macro_export] -/// Define a blueprint to build this view from a config file. +/// Define a blueprint to manually build this view from a config file. +/// +/// Note: this is entirely ignored (not even type-checked) if the `builder` feature is not +/// enabled. +/// +/// There are 3 variants of this macro: +/// +/// * `manual_blueprint!(Identifier, |config, context| make_the_view(...))` +/// This registers the recipe under `Identifier`, and uses the given closure to build +/// the view. +/// * `manual_blueprint!(Identifier from { parse_some_config(...) })` +/// This register under `Identifier` a recipe that forwards the creation to another +/// config using [`Context::build_template`]. +/// * `manual_blueprint`(with Identifier, |config, context| Ok(|view| wrap_the_view(view, ...)))` +/// This register a "with" blueprint under `Identifier`, which will prepare a view wrapper. macro_rules! manual_blueprint { // Remember to keep the inactive version above in sync ($name:ident from $config_builder:expr) => { diff --git a/cursive-core/src/builder/resolvable.rs b/cursive-core/src/builder/resolvable.rs index d8ced928..362b9581 100644 --- a/cursive-core/src/builder/resolvable.rs +++ b/cursive-core/src/builder/resolvable.rs @@ -1009,22 +1009,51 @@ impl Resolvable for crate::view::Margins { } } +impl Resolvable for crate::align::Align { + fn from_config(config: &Config, context: &Context) -> Result { + // Try a string shortcut + if let Some(config_str) = config.as_str() { + if let Ok(align) = config_str.parse() { + return Ok(align); + } + + return Err(Error::invalid_config("Unexpected align string", config)); + } + + let h = context.resolve(&config["h"])?; + let v = context.resolve(&config["v"])?; + + Ok(Self { h, v }) + } +} + +impl Resolvable for crate::align::VAlign { + fn from_config(config: &Config, _context: &Context) -> Result { + if let Some(config) = config.as_str() { + if let Ok(align) = config.parse() { + return Ok(align); + } + } + + Err(Error::invalid_config( + "Expected top, center or bottom", + config, + )) + } +} + impl Resolvable for crate::align::HAlign { fn from_config(config: &Config, _context: &Context) -> Result { - // TODO: also resolve single-value configs like strings. - // Also when resolving a variable with the wrong type, fallback on loading the type with - // the variable name. - Ok(match config.as_str() { - Some(config) if config == "Left" || config == "left" => Self::Left, - Some(config) if config == "Center" || config == "center" => Self::Center, - Some(config) if config == "Right" || config == "right" => Self::Right, - _ => { - return Err(Error::invalid_config( - "Expected left, center or right", - config, - )) + if let Some(config) = config.as_str() { + if let Ok(align) = config.parse() { + return Ok(align); } - }) + } + + Err(Error::invalid_config( + "Expected left, center or right", + config, + )) } }