~ireas/public-inbox

Add support for horizontal LinearLayouts v2 PROPOSED

Erik Funder Carstensen: 1
 Add support for horizontal LinearLayouts

 2 files changed, 59 insertions(+), 6 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~ireas/public-inbox/patches/23385/mbox | git am -3
Learn more about email & git

[PATCH v2] Add support for horizontal LinearLayouts Export this patch

---
The closing '```' had been forgotton for the added example

 src/elements.rs | 54 +++++++++++++++++++++++++++++++++++++++++++------
 src/lib.rs      | 11 ++++++++++
 2 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/src/elements.rs b/src/elements.rs
index 2f20e34..9541012 100644
--- a/src/elements.rs
+++ b/src/elements.rs
@@ -74,10 +74,13 @@ impl IntoBoxedElement for Box<dyn Element> {
    }
}

enum RenderDirection {
    Horizontal,
    Vertical,
}

/// Arranges a list of elements sequentially.
///
/// Currently, elements can only be arranged vertically.
///
/// # Examples
///
/// With setters:
@@ -96,22 +99,37 @@ impl IntoBoxedElement for Box<dyn Element> {
///     .element(elements::Paragraph::new("Test2"));
/// ```
///
/// Horizontal:
/// ```
/// use genpdf::elements;
/// let layout = elements::LinearLayout::horizontal()
///     .element(elements::Paragraph::new("Test1"))
///     .element(elements::Paragraph::new("Test2"));
/// ```
///
pub struct LinearLayout {
    elements: Vec<Box<dyn Element>>,
    render_idx: usize,
    render_direction: RenderDirection,
}

impl LinearLayout {
    fn new() -> LinearLayout {
    fn new(render_direction: RenderDirection) -> LinearLayout {
        LinearLayout {
            elements: Vec::new(),
            render_idx: 0,
            render_direction,
        }
    }

    /// Creates a new linear layout that arranges its elements vertically.
    pub fn vertical() -> LinearLayout {
        LinearLayout::new()
        Self::new(RenderDirection::Vertical)
    }

    /// Creates a new linear layout that arranges its elements horizontally.
    pub fn horizontal() -> LinearLayout {
        Self::new(RenderDirection::Horizontal)
    }

    /// Adds the given element to this layout.
@@ -146,6 +164,28 @@ impl LinearLayout {
        result.has_more = self.render_idx < self.elements.len();
        Ok(result)
    }

    fn render_horizontal(
        &mut self,
        context: &Context,
        mut area: render::Area<'_>,
        style: Style,
    ) -> Result<RenderResult, Error> {
        let mut result = RenderResult::default();
        while area.size().width > Mm(0.0) && self.render_idx < self.elements.len() {
            let element_result =
                self.elements[self.render_idx].render(context, area.clone(), style)?;
            area.add_offset(Position::new(element_result.size.width, 0));
            result.size = result.size.stack_horizontal(element_result.size);
            if element_result.has_more {
                result.has_more = true;
                return Ok(result);
            }
            self.render_idx += 1;
        }
        result.has_more = self.render_idx < self.elements.len();
        Ok(result)
    }
}

impl Element for LinearLayout {
@@ -155,8 +195,10 @@ impl Element for LinearLayout {
        area: render::Area<'_>,
        style: Style,
    ) -> Result<RenderResult, Error> {
        // TODO: add horizontal layout
        self.render_vertical(context, area, style)
        match self.render_direction {
            RenderDirection::Vertical => self.render_vertical(context, area, style),
            RenderDirection::Horizontal => self.render_horizontal(context, area, style),
        }
    }
}

diff --git a/src/lib.rs b/src/lib.rs
index 4a16461..aef7e03 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -435,6 +435,17 @@ impl Size {
        self.height += other.height;
        self
    }

    /// Stacks the given size horizontally on this size and returns the result.
    ///
    /// This means that the height is set to the maximum of the heights and the width is set to the
    /// sum of the widths.
    #[must_use]
    pub fn stack_horizontal(mut self, other: Size) -> Size {
        self.width += other.width;
        self.height = self.height.max(other.height);
        self
    }
}

impl<W: Into<Mm>, H: Into<Mm>> From<(W, H)> for Size {
-- 
2.32.0
Hi Erik,

thank you for the patch!  I’ve received a nearly identical patch three
days ago, see:
	https://lists.sr.ht/~ireas/public-inbox/%3C162370213657.22528.15833839867279751390-0%40git.sr.ht%3E
	https://lists.sr.ht/~ireas/public-inbox/%3C162370324348.13472.14147745046729338585-0%40git.sr.ht%3E
	https://lists.sr.ht/~ireas/public-inbox/%3CCAP_ocU-as%2BiWb1Sw8HkqCgbec3NpfyszJ2ud491GwEYn9V5Rrw%40mail.gmail.com%3E

I think the issue raised in that discussion also applies for this patch,
namely what happens if the elements of the layout do not fit
horizontally and/or vertically.  Please let me know what you think about
this and, if you are interested in implementing a solution, coordinate
with Ferdinand.

/Robin