mirror of
https://github.com/mfontanini/presenterm.git
synced 2025-05-05 15:32:58 +00:00
feat: add support for kitty's font size spec
This commit is contained in:
parent
33c7c9705c
commit
1235a26f75
@ -2,7 +2,8 @@ use crate::{
|
||||
code::snippet::SnippetLanguage,
|
||||
commands::keyboard::KeyBinding,
|
||||
terminal::{
|
||||
GraphicsMode, emulator::TerminalEmulator, image::protocols::kitty::KittyMode, query::TerminalCapabilities,
|
||||
GraphicsMode, capabilities::TerminalCapabilities, emulator::TerminalEmulator,
|
||||
image::protocols::kitty::KittyMode,
|
||||
},
|
||||
};
|
||||
use clap::ValueEnum;
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
builder::{BuildError, PresentationBuilder},
|
||||
},
|
||||
render::TerminalDrawer,
|
||||
terminal::TerminalWrite,
|
||||
terminal::{TerminalWrite, emulator::TerminalEmulator},
|
||||
};
|
||||
use std::{io, rc::Rc};
|
||||
|
||||
@ -104,7 +104,10 @@ impl<W: TerminalWrite> ThemesDemo<W> {
|
||||
let image_registry = ImageRegistry::default();
|
||||
let mut resources = Resources::new("non_existent", image_registry.clone());
|
||||
let mut third_party = ThirdPartyRender::default();
|
||||
let options = PresentationBuilderOptions::default();
|
||||
let options = PresentationBuilderOptions {
|
||||
font_size_supported: TerminalEmulator::capabilities().font_size,
|
||||
..Default::default()
|
||||
};
|
||||
let executer = Rc::new(SnippetExecutor::default());
|
||||
let bindings_config = Default::default();
|
||||
let builder = PresentationBuilder::new(
|
||||
|
@ -27,6 +27,7 @@ use std::{
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use terminal::emulator::TerminalEmulator;
|
||||
|
||||
mod code;
|
||||
mod commands;
|
||||
@ -258,6 +259,7 @@ impl CoreComponents {
|
||||
enable_snippet_execution_replace: config.snippet.exec_replace.enable,
|
||||
render_speaker_notes_only,
|
||||
auto_render_languages: config.options.auto_render_languages.clone(),
|
||||
font_size_supported: TerminalEmulator::capabilities().font_size,
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,6 +351,8 @@ fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("{}", String::from_utf8_lossy(acknowledgements));
|
||||
return Ok(());
|
||||
} else if cli.list_themes {
|
||||
// Load this ahead of time so we don't do it when we're already in raw mode.
|
||||
TerminalEmulator::capabilities();
|
||||
let Customizations { config, themes, .. } =
|
||||
Customizations::load(cli.config_file.clone().map(PathBuf::from), ¤t_dir()?)?;
|
||||
let bindings = config.bindings.try_into()?;
|
||||
|
@ -12,6 +12,7 @@ use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
pub(crate) struct WeightedLine {
|
||||
text: Vec<WeightedText>,
|
||||
width: usize,
|
||||
height: u8,
|
||||
}
|
||||
|
||||
impl WeightedLine {
|
||||
@ -25,6 +26,11 @@ impl WeightedLine {
|
||||
self.width
|
||||
}
|
||||
|
||||
/// The height of this line.
|
||||
pub(crate) fn height(&self) -> u8 {
|
||||
self.height
|
||||
}
|
||||
|
||||
/// Get an iterator to the underlying text chunks.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn iter_texts(&self) -> impl Iterator<Item = &WeightedText> {
|
||||
@ -43,6 +49,7 @@ impl From<Vec<Text>> for WeightedLine {
|
||||
let mut output = Vec::new();
|
||||
let mut index = 0;
|
||||
let mut width = 0;
|
||||
let mut height = 1;
|
||||
// Compact chunks so any consecutive chunk with the same style is merged into the same block.
|
||||
while index < texts.len() {
|
||||
let mut target = mem::replace(&mut texts[index], Text::from(""));
|
||||
@ -52,11 +59,13 @@ impl From<Vec<Text>> for WeightedLine {
|
||||
target.content.push_str(¤t_content);
|
||||
current += 1;
|
||||
}
|
||||
width += target.content.width();
|
||||
let size = target.style.size.max(1);
|
||||
width += target.content.width() * size as usize;
|
||||
output.push(target.into());
|
||||
index = current;
|
||||
height = height.max(size);
|
||||
}
|
||||
Self { text: output, width }
|
||||
Self { text: output, width, height }
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +73,7 @@ impl From<String> for WeightedLine {
|
||||
fn from(text: String) -> Self {
|
||||
let width = text.width();
|
||||
let text = vec![WeightedText::from(text)];
|
||||
Self { text, width }
|
||||
Self { text, width, height: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,7 +334,8 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn no_split_necessary() {
|
||||
let text = WeightedLine { text: vec![WeightedText::from("short"), WeightedText::from("text")], width: 0 };
|
||||
let text =
|
||||
WeightedLine { text: vec![WeightedText::from("short"), WeightedText::from("text")], width: 0, height: 1 };
|
||||
let lines = join_lines(text.split(50));
|
||||
let expected = vec!["short text"];
|
||||
assert_eq!(lines, expected);
|
||||
@ -333,7 +343,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn split_lines_single() {
|
||||
let text = WeightedLine { text: vec![WeightedText::from("this is a slightly long line")], width: 0 };
|
||||
let text = WeightedLine { text: vec![WeightedText::from("this is a slightly long line")], width: 0, height: 1 };
|
||||
let lines = join_lines(text.split(6));
|
||||
let expected = vec!["this", "is a", "slight", "ly", "long", "line"];
|
||||
assert_eq!(lines, expected);
|
||||
@ -348,6 +358,7 @@ mod test {
|
||||
WeightedText::from("yet some other piece"),
|
||||
],
|
||||
width: 0,
|
||||
height: 1,
|
||||
};
|
||||
let lines = join_lines(text.split(10));
|
||||
let expected = vec!["this is a", "slightly", "long line", "another", "chunk yet", "some other", "piece"];
|
||||
@ -363,6 +374,7 @@ mod test {
|
||||
WeightedText::from("yet some other piece"),
|
||||
],
|
||||
width: 0,
|
||||
height: 1,
|
||||
};
|
||||
let lines = join_lines(text.split(50));
|
||||
let expected = vec!["this is a slightly long line another chunk yet some", "other piece"];
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::theme::ColorPalette;
|
||||
use crossterm::style::Stylize;
|
||||
use crossterm::style::{StyledContent, Stylize};
|
||||
use hex::{FromHex, FromHexError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
@ -10,15 +10,27 @@ use std::{
|
||||
};
|
||||
|
||||
/// The style of a piece of text.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct TextStyle {
|
||||
flags: u8,
|
||||
pub(crate) colors: Colors,
|
||||
pub(crate) size: u8,
|
||||
}
|
||||
|
||||
impl Default for TextStyle {
|
||||
fn default() -> Self {
|
||||
Self { flags: Default::default(), colors: Default::default(), size: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl TextStyle {
|
||||
pub(crate) fn colored(colors: Colors) -> Self {
|
||||
Self { flags: Default::default(), colors }
|
||||
Self { colors, ..Default::default() }
|
||||
}
|
||||
|
||||
pub(crate) fn size(mut self, size: u8) -> Self {
|
||||
self.size = size.min(16);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add bold to this style.
|
||||
@ -107,13 +119,18 @@ impl TextStyle {
|
||||
/// Merge this style with another one.
|
||||
pub(crate) fn merge(&mut self, other: &TextStyle) {
|
||||
self.flags |= other.flags;
|
||||
self.size = self.size.max(other.size);
|
||||
self.colors.background = self.colors.background.or(other.colors.background);
|
||||
self.colors.foreground = self.colors.foreground.or(other.colors.foreground);
|
||||
}
|
||||
|
||||
/// Apply this style to a piece of text.
|
||||
pub(crate) fn apply<T: Into<String>>(&self, text: T) -> Result<<String as Stylize>::Styled, PaletteColorError> {
|
||||
let text: String = text.into();
|
||||
pub(crate) fn apply<T: Into<String>>(&self, text: T) -> Result<StyledContent<String>, PaletteColorError> {
|
||||
let text = text.into();
|
||||
let text = match self.size {
|
||||
0 | 1 => text,
|
||||
size => format!("\x1b]66;s={size};{text}\x1b\\"),
|
||||
};
|
||||
let mut styled = text.stylize();
|
||||
if self.is_bold() {
|
||||
styled = styled.bold();
|
||||
|
@ -77,6 +77,7 @@ pub struct PresentationBuilderOptions {
|
||||
pub enable_snippet_execution_replace: bool,
|
||||
pub render_speaker_notes_only: bool,
|
||||
pub auto_render_languages: Vec<SnippetLanguage>,
|
||||
pub font_size_supported: bool,
|
||||
}
|
||||
|
||||
impl PresentationBuilderOptions {
|
||||
@ -114,6 +115,7 @@ impl Default for PresentationBuilderOptions {
|
||||
enable_snippet_execution_replace: false,
|
||||
render_speaker_notes_only: false,
|
||||
auto_render_languages: Default::default(),
|
||||
font_size_supported: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -395,7 +397,10 @@ impl<'a> PresentationBuilder<'a> {
|
||||
let styles = self.theme.intro_slide.clone();
|
||||
let create_text =
|
||||
|text: Option<String>, style: TextStyle| -> Option<Text> { text.map(|text| Text::new(text, style)) };
|
||||
let title = create_text(metadata.title, TextStyle::default().bold().colors(styles.title.colors));
|
||||
let title = create_text(
|
||||
metadata.title,
|
||||
TextStyle::default().bold().colors(styles.title.colors).size(self.font_size(styles.title.font_size)),
|
||||
);
|
||||
let sub_title = create_text(metadata.sub_title, TextStyle::default().colors(styles.subtitle.colors));
|
||||
let event = create_text(metadata.event, TextStyle::default().colors(styles.event.colors));
|
||||
let location = create_text(metadata.location, TextStyle::default().colors(styles.location.colors));
|
||||
@ -572,6 +577,7 @@ impl<'a> PresentationBuilder<'a> {
|
||||
|
||||
let style = self.theme.slide_title.clone();
|
||||
let mut text_style = TextStyle::default().colors(style.colors);
|
||||
text_style = text_style.size(self.font_size(style.font_size));
|
||||
if style.bold.unwrap_or_default() {
|
||||
text_style = text_style.bold();
|
||||
}
|
||||
@ -613,7 +619,7 @@ impl<'a> PresentationBuilder<'a> {
|
||||
prefix.push(' ');
|
||||
text.0.insert(0, Text::from(prefix));
|
||||
}
|
||||
let text_style = TextStyle::default().bold().colors(style.colors);
|
||||
let text_style = TextStyle::default().bold().colors(style.colors).size(self.font_size(style.font_size));
|
||||
text.apply_style(&text_style);
|
||||
|
||||
self.push_text(text, element_type)?;
|
||||
@ -1155,6 +1161,11 @@ impl<'a> PresentationBuilder<'a> {
|
||||
_ => Err(ImageAttributeError::UnknownAttribute(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn font_size(&self, font_size: Option<u8>) -> u8 {
|
||||
let Some(font_size) = font_size else { return 1 };
|
||||
if self.options.font_size_supported { font_size.clamp(1, 7) } else { 1 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -188,7 +188,7 @@ where
|
||||
}
|
||||
|
||||
fn render_line_break(&mut self) -> RenderResult {
|
||||
self.terminal.move_to_next_line(1)?;
|
||||
self.terminal.move_to_next_line()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
text_style::{Color, Colors},
|
||||
},
|
||||
render::{RenderError, RenderResult, layout::Positioning},
|
||||
terminal::{Terminal, TerminalWrite},
|
||||
terminal::{Terminal, TerminalWrite, printer::TextProperties},
|
||||
};
|
||||
|
||||
const MINIMUM_LINE_LENGTH: u16 = 10;
|
||||
@ -23,6 +23,7 @@ pub(crate) struct TextDrawer<'a> {
|
||||
draw_block: bool,
|
||||
block_color: Option<Color>,
|
||||
repeat_prefix: bool,
|
||||
properties: TextProperties,
|
||||
}
|
||||
|
||||
impl<'a> TextDrawer<'a> {
|
||||
@ -56,6 +57,7 @@ impl<'a> TextDrawer<'a> {
|
||||
draw_block: false,
|
||||
block_color: None,
|
||||
repeat_prefix: false,
|
||||
properties: TextProperties { height: line.height() },
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -86,7 +88,7 @@ impl<'a> TextDrawer<'a> {
|
||||
style.apply(content)?
|
||||
};
|
||||
terminal.move_to_column(self.positioning.start_column)?;
|
||||
terminal.print_styled_line(styled_prefix.clone())?;
|
||||
terminal.print_styled_line(styled_prefix.clone(), &self.properties)?;
|
||||
|
||||
let start_column = self.positioning.start_column + self.prefix_length;
|
||||
for (line_index, line) in self.line.split(self.positioning.max_line_length as usize).enumerate() {
|
||||
@ -100,7 +102,7 @@ impl<'a> TextDrawer<'a> {
|
||||
if self.prefix_length > 0 {
|
||||
terminal.move_to_column(self.positioning.start_column)?;
|
||||
if self.repeat_prefix {
|
||||
terminal.print_styled_line(styled_prefix.clone())?;
|
||||
terminal.print_styled_line(styled_prefix.clone(), &self.properties)?;
|
||||
} else {
|
||||
self.print_block_background(self.prefix_length, terminal)?;
|
||||
}
|
||||
@ -112,7 +114,7 @@ impl<'a> TextDrawer<'a> {
|
||||
|
||||
let (text, style) = chunk.into_parts();
|
||||
let text = style.apply(text)?;
|
||||
terminal.print_styled_line(text)?;
|
||||
terminal.print_styled_line(text, &self.properties)?;
|
||||
|
||||
// Crossterm resets colors if any attributes are set so let's just re-apply colors
|
||||
// if the format has anything on it at all.
|
||||
@ -137,7 +139,7 @@ impl<'a> TextDrawer<'a> {
|
||||
terminal.set_background_color(color)?;
|
||||
}
|
||||
let text = " ".repeat(remaining as usize);
|
||||
terminal.print_line(&text)?;
|
||||
terminal.print_line(&text, &self.properties)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -1,6 +1,11 @@
|
||||
use super::image::protocols::kitty::{Action, ControlCommand, ControlOption, ImageFormat, TransmissionMedium};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||
use crossterm::{
|
||||
QueueableCommand,
|
||||
cursor::{self},
|
||||
style::Print,
|
||||
terminal,
|
||||
};
|
||||
use image::{DynamicImage, EncodableLayout};
|
||||
use std::{
|
||||
env,
|
||||
@ -8,12 +13,13 @@ use std::{
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub(crate) struct TerminalCapabilities {
|
||||
pub(crate) kitty_local: bool,
|
||||
pub(crate) kitty_remote: bool,
|
||||
pub(crate) sixel: bool,
|
||||
pub(crate) tmux: bool,
|
||||
pub(crate) font_size: bool,
|
||||
}
|
||||
|
||||
impl TerminalCapabilities {
|
||||
@ -48,6 +54,19 @@ impl TerminalCapabilities {
|
||||
|
||||
let mut response = Self::parse_response(io::stdin(), ids)?;
|
||||
response.tmux = tmux;
|
||||
|
||||
// Use kitty's font size protocol to write 1 character using size 2. If after writing the
|
||||
// cursor has moves 2 columns, the protocol is supported.
|
||||
stdout.queue(terminal::EnterAlternateScreen)?;
|
||||
stdout.queue(cursor::MoveTo(0, 0))?;
|
||||
stdout.queue(Print("\x1b]66;s=2; \x1b\\"))?;
|
||||
stdout.flush()?;
|
||||
let position = cursor::position()?;
|
||||
if position.0 == 2 {
|
||||
response.font_size = true;
|
||||
}
|
||||
stdout.queue(terminal::LeaveAlternateScreen)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@ -113,14 +132,14 @@ struct RawModeGuard;
|
||||
|
||||
impl RawModeGuard {
|
||||
fn new() -> io::Result<Self> {
|
||||
enable_raw_mode()?;
|
||||
terminal::enable_raw_mode()?;
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RawModeGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = disable_raw_mode();
|
||||
let _ = terminal::disable_raw_mode();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use super::{GraphicsMode, image::protocols::kitty::KittyMode, query::TerminalCapabilities};
|
||||
use super::{GraphicsMode, capabilities::TerminalCapabilities, image::protocols::kitty::KittyMode};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::env;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
static CAPABILITIES: Lazy<TerminalCapabilities> = Lazy::new(|| TerminalCapabilities::query().unwrap_or_default());
|
||||
|
||||
#[derive(Debug, strum::EnumIter)]
|
||||
pub enum TerminalEmulator {
|
||||
Iterm2,
|
||||
@ -30,8 +33,12 @@ impl TerminalEmulator {
|
||||
TerminalEmulator::Unknown
|
||||
}
|
||||
|
||||
pub(crate) fn capabilities() -> TerminalCapabilities {
|
||||
CAPABILITIES.clone()
|
||||
}
|
||||
|
||||
pub fn preferred_protocol(&self) -> GraphicsMode {
|
||||
let capabilities = TerminalCapabilities::query().unwrap_or_default();
|
||||
let capabilities = Self::capabilities();
|
||||
let modes = [
|
||||
GraphicsMode::Iterm2,
|
||||
GraphicsMode::Kitty { mode: KittyMode::Local, inside_tmux: capabilities.tmux },
|
||||
|
@ -1,8 +1,8 @@
|
||||
pub(crate) mod ansi;
|
||||
pub(crate) mod capabilities;
|
||||
pub(crate) mod emulator;
|
||||
pub(crate) mod image;
|
||||
pub(crate) mod printer;
|
||||
pub(crate) mod query;
|
||||
|
||||
pub(crate) use printer::{Terminal, TerminalWrite, should_hide_cursor};
|
||||
|
||||
|
@ -23,12 +23,13 @@ where
|
||||
writer: W,
|
||||
image_printer: Arc<ImagePrinter>,
|
||||
pub(crate) cursor_row: u16,
|
||||
current_row_height: u16,
|
||||
}
|
||||
|
||||
impl<W: TerminalWrite> Terminal<W> {
|
||||
pub(crate) fn new(mut writer: W, image_printer: Arc<ImagePrinter>) -> io::Result<Self> {
|
||||
writer.init()?;
|
||||
Ok(Self { writer, image_printer, cursor_row: 0 })
|
||||
Ok(Self { writer, image_printer, cursor_row: 0, current_row_height: 1 })
|
||||
}
|
||||
|
||||
pub(crate) fn begin_update(&mut self) -> io::Result<()> {
|
||||
@ -64,19 +65,27 @@ impl<W: TerminalWrite> Terminal<W> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn move_to_next_line(&mut self, amount: u16) -> io::Result<()> {
|
||||
pub(crate) fn move_to_next_line(&mut self) -> io::Result<()> {
|
||||
let amount = self.current_row_height;
|
||||
self.writer.queue(cursor::MoveToNextLine(amount))?;
|
||||
self.cursor_row += amount;
|
||||
self.current_row_height = 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_line(&mut self, text: &str) -> io::Result<()> {
|
||||
pub(crate) fn print_line(&mut self, text: &str, properties: &TextProperties) -> io::Result<()> {
|
||||
self.writer.queue(style::Print(text))?;
|
||||
self.current_row_height = self.current_row_height.max(properties.height as u16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn print_styled_line(&mut self, content: StyledContent<String>) -> io::Result<()> {
|
||||
self.writer.queue(style::PrintStyledContent(content))?;
|
||||
pub(crate) fn print_styled_line(
|
||||
&mut self,
|
||||
string: StyledContent<String>,
|
||||
properties: &TextProperties,
|
||||
) -> io::Result<()> {
|
||||
self.writer.queue(style::PrintStyledContent(string))?;
|
||||
self.current_row_height = self.current_row_height.max(properties.height as u16);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -120,6 +129,17 @@ impl<W: TerminalWrite> Terminal<W> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TextProperties {
|
||||
pub(crate) height: u8,
|
||||
}
|
||||
|
||||
impl Default for TextProperties {
|
||||
fn default() -> Self {
|
||||
Self { height: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> Drop for Terminal<W>
|
||||
where
|
||||
W: TerminalWrite,
|
||||
|
40
src/theme.rs
40
src/theme.rs
@ -303,6 +303,10 @@ pub(crate) struct SlideTitleStyle {
|
||||
/// Whether to use underlined font for slide titles.
|
||||
#[serde(default)]
|
||||
pub(crate) underlined: Option<bool>,
|
||||
|
||||
/// The font size to be used if the terminal supports it.
|
||||
#[serde(default)]
|
||||
pub(crate) font_size: Option<u8>,
|
||||
}
|
||||
|
||||
impl SlideTitleStyle {
|
||||
@ -366,11 +370,16 @@ pub(crate) struct HeadingStyle {
|
||||
/// The colors to be used.
|
||||
#[serde(default)]
|
||||
pub(crate) colors: Colors,
|
||||
|
||||
/// The font size to be used if the terminal supports it.
|
||||
#[serde(default)]
|
||||
pub(crate) font_size: Option<u8>,
|
||||
}
|
||||
|
||||
impl HeadingStyle {
|
||||
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
|
||||
self.colors = self.colors.resolve(palette)?;
|
||||
let Self { colors, alignment: _, prefix: _, font_size: _ } = self;
|
||||
*colors = colors.resolve(palette)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -643,7 +652,7 @@ impl AlertTypeProperties for CautionAlertType {
|
||||
pub(crate) struct IntroSlideStyle {
|
||||
/// The style of the title line.
|
||||
#[serde(default)]
|
||||
pub(crate) title: BasicStyle,
|
||||
pub(crate) title: IntroSlideTitleStyle,
|
||||
|
||||
/// The style of the subtitle line.
|
||||
#[serde(default)]
|
||||
@ -673,9 +682,10 @@ pub(crate) struct IntroSlideStyle {
|
||||
impl IntroSlideStyle {
|
||||
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
|
||||
let Self { title, subtitle, event, location, date, author, footer: _footer } = self;
|
||||
for s in [title, subtitle, event, location, date] {
|
||||
for s in [subtitle, event, location, date] {
|
||||
s.resolve_palette_colors(palette)?;
|
||||
}
|
||||
title.resolve_palette_colors(palette)?;
|
||||
author.resolve_palette_colors(palette)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -721,6 +731,30 @@ impl BasicStyle {
|
||||
}
|
||||
}
|
||||
|
||||
/// The intro slide title's style.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub(crate) struct IntroSlideTitleStyle {
|
||||
/// The alignment.
|
||||
#[serde(flatten, default)]
|
||||
pub(crate) alignment: Option<Alignment>,
|
||||
|
||||
/// The colors to be used.
|
||||
#[serde(default)]
|
||||
pub(crate) colors: Colors,
|
||||
|
||||
/// The font size to be used if the terminal supports it.
|
||||
#[serde(default)]
|
||||
pub(crate) font_size: Option<u8>,
|
||||
}
|
||||
|
||||
impl IntroSlideTitleStyle {
|
||||
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
|
||||
let Self { colors, alignment: _, font_size: _ } = self;
|
||||
*colors = colors.resolve(palette)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Text alignment.
|
||||
///
|
||||
/// This allows anchoring presentation elements to the left, center, or right of the screen.
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: "e5c890"
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -47,6 +48,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: "a6d189"
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: "df8e1d"
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -47,6 +48,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: "40a02b"
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: "eed49f"
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -47,6 +48,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: "a6da95"
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: "f9e2af"
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -47,6 +48,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: "a6e3a1"
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: palette:orange
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -48,6 +49,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: palette:light_blue
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: "f77f00"
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -48,6 +49,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: "52b788"
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: yellow
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -46,6 +47,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: green
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: dark_yellow
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -46,6 +47,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: dark_green
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
@ -13,6 +13,7 @@ slide_title:
|
||||
colors:
|
||||
foreground: "e0af68"
|
||||
bold: true
|
||||
font_size: 2
|
||||
|
||||
code:
|
||||
alignment: center
|
||||
@ -48,6 +49,7 @@ intro_slide:
|
||||
alignment: center
|
||||
colors:
|
||||
foreground: "7aa2f7"
|
||||
font_size: 2
|
||||
subtitle:
|
||||
alignment: center
|
||||
colors:
|
||||
|
Loading…
x
Reference in New Issue
Block a user