mirror of
https://github.com/mfontanini/presenterm.git
synced 2025-05-05 15:32:58 +00:00
feat: allow using images on right in footer (#554)
Some checks failed
Deploy docs / build-and-deploy (push) Has been cancelled
Merge checks / Checks (push) Has been cancelled
Merge checks / Validate nix flake (push) Has been cancelled
Merge checks / Validate bat assets (push) Has been cancelled
Merge checks / Validate JSON schemas (push) Has been cancelled
Some checks failed
Deploy docs / build-and-deploy (push) Has been cancelled
Merge checks / Checks (push) Has been cancelled
Merge checks / Validate nix flake (push) Has been cancelled
Merge checks / Validate bat assets (push) Has been cancelled
Merge checks / Validate JSON schemas (push) Has been cancelled
This adds support for footer images on the right, just like allowed for left/center. Now that there's tests for this and after being restructured fairly recently it's much easier to get _right_. The following presentation now renders like the following: ```markdown --- theme: override: footer: height: 5 style: template left: image: ../examples/doge.png center: image: ../examples/doge.png right: image: ../examples/doge.png --- Footer images === ```  Closes #549
This commit is contained in:
commit
31f7c6c1e2
@ -1,4 +1,6 @@
|
||||
use super::{RenderError, RenderResult, layout::Layout, properties::CursorPosition, text::TextDrawer};
|
||||
use super::{
|
||||
RenderError, RenderResult, layout::Layout, operation::ImagePosition, properties::CursorPosition, text::TextDrawer,
|
||||
};
|
||||
use crate::{
|
||||
config::{MaxColumnsAlignment, MaxRowsAlignment},
|
||||
markdown::{text::WeightedLine, text_style::Colors},
|
||||
@ -274,9 +276,10 @@ where
|
||||
(image_scale.columns, image_scale.rows)
|
||||
}
|
||||
};
|
||||
let cursor = match properties.center {
|
||||
true => Self::center_cursor(columns, &rect.dimensions, &starting_cursor),
|
||||
false => starting_cursor.clone(),
|
||||
let cursor = match &properties.position {
|
||||
ImagePosition::Cursor => starting_cursor.clone(),
|
||||
ImagePosition::Center => Self::center_cursor(columns, &rect.dimensions, &starting_cursor),
|
||||
ImagePosition::Right => Self::align_cursor_right(columns, &rect.dimensions, &starting_cursor),
|
||||
};
|
||||
self.terminal.execute(&TerminalCommand::MoveToColumn(cursor.column))?;
|
||||
|
||||
@ -303,6 +306,11 @@ where
|
||||
CursorPosition { row: cursor.row, column: start_column }
|
||||
}
|
||||
|
||||
fn align_cursor_right(columns: u16, window: &WindowSize, cursor: &CursorPosition) -> CursorPosition {
|
||||
let start_column = window.columns.saturating_sub(columns).saturating_add(cursor.column);
|
||||
CursorPosition { row: cursor.row, column: start_column }
|
||||
}
|
||||
|
||||
fn render_block_line(&mut self, operation: &BlockLine) -> RenderResult {
|
||||
let BlockLine {
|
||||
text,
|
||||
@ -806,8 +814,13 @@ mod tests {
|
||||
fn image(#[case] size: ImageSize) {
|
||||
let image = DynamicImage::new(2, 2, ColorType::Rgba8);
|
||||
let image = Image::new(TerminalImage::Ascii(image.into()), ImageSource::Generated);
|
||||
let properties =
|
||||
ImageRenderProperties { z_index: 0, size, restore_cursor: false, background_color: None, center: false };
|
||||
let properties = ImageRenderProperties {
|
||||
z_index: 0,
|
||||
size,
|
||||
restore_cursor: false,
|
||||
background_color: None,
|
||||
position: ImagePosition::Cursor,
|
||||
};
|
||||
let ops = render_with_max_size(&[RenderOperation::RenderImage(image, properties)]);
|
||||
let expected = [
|
||||
// centered 20x10, the image is 2x2 so we stand one away from center
|
||||
@ -835,8 +848,13 @@ mod tests {
|
||||
fn centered_image(#[case] size: ImageSize) {
|
||||
let image = DynamicImage::new(2, 2, ColorType::Rgba8);
|
||||
let image = Image::new(TerminalImage::Ascii(image.into()), ImageSource::Generated);
|
||||
let properties =
|
||||
ImageRenderProperties { z_index: 0, size, restore_cursor: false, background_color: None, center: true };
|
||||
let properties = ImageRenderProperties {
|
||||
z_index: 0,
|
||||
size,
|
||||
restore_cursor: false,
|
||||
background_color: None,
|
||||
position: ImagePosition::Center,
|
||||
};
|
||||
let ops = render_with_max_size(&[RenderOperation::RenderImage(image, properties)]);
|
||||
let expected = [
|
||||
// centered 20x10, the image is 2x2 so we stand one away from center
|
||||
@ -856,6 +874,40 @@ mod tests {
|
||||
assert_eq!(ops, expected);
|
||||
}
|
||||
|
||||
// same as the above but use right alignment
|
||||
#[rstest]
|
||||
#[case::shrink(ImageSize::ShrinkIfNeeded)]
|
||||
#[case::specific(ImageSize::Specific(2, 2))]
|
||||
#[case::width_scaled(ImageSize::WidthScaled { ratio: 1.0 })]
|
||||
fn right_aligned_image(#[case] size: ImageSize) {
|
||||
let image = DynamicImage::new(2, 2, ColorType::Rgba8);
|
||||
let image = Image::new(TerminalImage::Ascii(image.into()), ImageSource::Generated);
|
||||
let properties = ImageRenderProperties {
|
||||
z_index: 0,
|
||||
size,
|
||||
restore_cursor: false,
|
||||
background_color: None,
|
||||
position: ImagePosition::Right,
|
||||
};
|
||||
let ops = render_with_max_size(&[RenderOperation::RenderImage(image, properties)]);
|
||||
let expected = [
|
||||
// right aligned 20x10, the image is 2x2 so we stand one away from the right
|
||||
Instruction::MoveTo(40, 45),
|
||||
Instruction::MoveToColumn(58),
|
||||
Instruction::PrintImage(PrintOptions {
|
||||
columns: 2,
|
||||
rows: 2,
|
||||
z_index: 0,
|
||||
background_color: None,
|
||||
column_width: 2,
|
||||
row_height: 2,
|
||||
}),
|
||||
// place cursor after the image
|
||||
Instruction::MoveToRow(47),
|
||||
];
|
||||
assert_eq!(ops, expected);
|
||||
}
|
||||
|
||||
// same as the above but center it
|
||||
#[rstest]
|
||||
fn restore_cursor_after_image() {
|
||||
@ -866,7 +918,7 @@ mod tests {
|
||||
size: ImageSize::ShrinkIfNeeded,
|
||||
restore_cursor: true,
|
||||
background_color: None,
|
||||
center: true,
|
||||
position: ImagePosition::Center,
|
||||
};
|
||||
let ops = render_with_max_size(&[RenderOperation::RenderImage(image, properties)]);
|
||||
let expected = [
|
||||
|
@ -101,7 +101,7 @@ pub(crate) struct ImageRenderProperties {
|
||||
pub(crate) size: ImageSize,
|
||||
pub(crate) restore_cursor: bool,
|
||||
pub(crate) background_color: Option<Color>,
|
||||
pub(crate) center: bool,
|
||||
pub(crate) position: ImagePosition,
|
||||
}
|
||||
|
||||
impl Default for ImageRenderProperties {
|
||||
@ -111,11 +111,18 @@ impl Default for ImageRenderProperties {
|
||||
size: Default::default(),
|
||||
restore_cursor: false,
|
||||
background_color: None,
|
||||
center: true,
|
||||
position: ImagePosition::Cursor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) enum ImagePosition {
|
||||
Cursor,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
/// The size used when printing an image.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub(crate) enum ImageSize {
|
||||
|
@ -474,7 +474,7 @@ pub(crate) enum FooterStyle {
|
||||
Template {
|
||||
left: Option<FooterContent>,
|
||||
center: Option<FooterContent>,
|
||||
right: Option<FooterTemplate>,
|
||||
right: Option<FooterContent>,
|
||||
style: TextStyle,
|
||||
height: u16,
|
||||
},
|
||||
@ -496,7 +496,7 @@ impl FooterStyle {
|
||||
raw::FooterStyle::Template { left, center, right, colors, height } => {
|
||||
let left = left.as_ref().map(|t| FooterContent::new(t, resources)).transpose()?;
|
||||
let center = center.as_ref().map(|t| FooterContent::new(t, resources)).transpose()?;
|
||||
let right = right.clone();
|
||||
let right = right.as_ref().map(|t| FooterContent::new(t, resources)).transpose()?;
|
||||
let style = TextStyle::colored(colors.resolve(palette)?);
|
||||
let height = height.unwrap_or(DEFAULT_FOOTER_HEIGHT);
|
||||
Ok(Self::Template { left, center, right, style, height })
|
||||
|
@ -412,7 +412,7 @@ pub(super) enum FooterStyle {
|
||||
center: Option<FooterContent>,
|
||||
|
||||
/// The content to be put on the right.
|
||||
right: Option<FooterTemplate>,
|
||||
right: Option<FooterContent>,
|
||||
|
||||
/// The colors to be used.
|
||||
#[serde(default)]
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
text_style::{TextStyle, UndefinedPaletteColorError},
|
||||
},
|
||||
render::{
|
||||
operation::{AsRenderOperations, ImageRenderProperties, MarginProperties, RenderOperation},
|
||||
operation::{AsRenderOperations, ImagePosition, ImageRenderProperties, MarginProperties, RenderOperation},
|
||||
properties::WindowSize,
|
||||
},
|
||||
terminal::image::Image,
|
||||
@ -53,14 +53,8 @@ impl FooterGenerator {
|
||||
]);
|
||||
}
|
||||
|
||||
fn push_image(
|
||||
&self,
|
||||
image: &Image,
|
||||
alignment: Alignment,
|
||||
dimensions: &WindowSize,
|
||||
operations: &mut Vec<RenderOperation>,
|
||||
) {
|
||||
let mut properties = ImageRenderProperties { center: false, ..Default::default() };
|
||||
fn push_image(&self, image: &Image, alignment: Alignment, operations: &mut Vec<RenderOperation>) {
|
||||
let mut properties = ImageRenderProperties::default();
|
||||
|
||||
operations.push(RenderOperation::ApplyMargin(MarginProperties {
|
||||
horizontal: Margin::Fixed(0),
|
||||
@ -70,11 +64,12 @@ impl FooterGenerator {
|
||||
match alignment {
|
||||
Alignment::Left { .. } => {
|
||||
operations.push(RenderOperation::JumpToColumn { index: 0 });
|
||||
properties.position = ImagePosition::Cursor;
|
||||
}
|
||||
Alignment::Right { .. } => {
|
||||
operations.push(RenderOperation::JumpToColumn { index: dimensions.columns.saturating_sub(1) });
|
||||
properties.position = ImagePosition::Right;
|
||||
}
|
||||
Alignment::Center { .. } => properties.center = true,
|
||||
Alignment::Center { .. } => properties.position = ImagePosition::Center,
|
||||
};
|
||||
operations.extend([
|
||||
// Start printing the image at the top of the footer rect
|
||||
@ -101,23 +96,20 @@ impl AsRenderOperations for FooterGenerator {
|
||||
let alignments = [
|
||||
Alignment::Left { margin: Default::default() },
|
||||
Alignment::Center { minimum_size: 0, minimum_margin: Default::default() },
|
||||
Alignment::Right { margin: Default::default() },
|
||||
];
|
||||
for (content, alignment) in [left, center].iter().zip(alignments) {
|
||||
for (content, alignment) in [left, center, right].iter().zip(alignments) {
|
||||
if let Some(content) = content {
|
||||
match content {
|
||||
RenderedFooterContent::Line(line) => {
|
||||
Self::render_line(line, alignment, *height, &mut operations);
|
||||
}
|
||||
RenderedFooterContent::Image(image) => {
|
||||
self.push_image(image, alignment, dimensions, &mut operations);
|
||||
self.push_image(image, alignment, &mut operations);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
// We don't support images on the right so treat this differently
|
||||
if let Some(line) = right {
|
||||
Self::render_line(line, Alignment::Right { margin: Default::default() }, *height, &mut operations);
|
||||
}
|
||||
operations.push(RenderOperation::PopMargin);
|
||||
operations
|
||||
}
|
||||
@ -146,7 +138,7 @@ enum RenderedFooterStyle {
|
||||
Template {
|
||||
left: Option<RenderedFooterContent>,
|
||||
center: Option<RenderedFooterContent>,
|
||||
right: Option<FooterLine>,
|
||||
right: Option<RenderedFooterContent>,
|
||||
height: u16,
|
||||
},
|
||||
ProgressBar {
|
||||
@ -166,7 +158,7 @@ impl RenderedFooterStyle {
|
||||
FooterStyle::Template { left, center, right, style, height } => {
|
||||
let left = left.map(|c| RenderedFooterContent::new(c, &style, vars, palette)).transpose()?;
|
||||
let center = center.map(|c| RenderedFooterContent::new(c, &style, vars, palette)).transpose()?;
|
||||
let right = right.map(|c| FooterLine::new(c, &style, vars, palette)).transpose()?;
|
||||
let right = right.map(|c| RenderedFooterContent::new(c, &style, vars, palette)).transpose()?;
|
||||
Ok(Self::Template { left, center, right, height })
|
||||
}
|
||||
FooterStyle::ProgressBar { character, style } => Ok(Self::ProgressBar { character, style }),
|
||||
|
@ -9,7 +9,9 @@ use crate::{
|
||||
},
|
||||
presentation::PresentationState,
|
||||
render::{
|
||||
operation::{AsRenderOperations, ImageRenderProperties, ImageSize, MarginProperties, RenderOperation},
|
||||
operation::{
|
||||
AsRenderOperations, ImagePosition, ImageRenderProperties, ImageSize, MarginProperties, RenderOperation,
|
||||
},
|
||||
properties::WindowSize,
|
||||
},
|
||||
terminal::image::Image,
|
||||
@ -307,7 +309,7 @@ impl AsRenderOperations for CenterModalContent {
|
||||
size: ImageSize::Specific(self.content_width, content_height),
|
||||
restore_cursor: true,
|
||||
background_color: None,
|
||||
center: true,
|
||||
position: ImagePosition::Center,
|
||||
};
|
||||
operations.push(RenderOperation::RenderImage(image.clone(), properties));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user