Table-like Custom Widget: Placing elements

I’m trying to create a table-like widget, where I have 3 columns of data (each row will have a button as well) and x number of rows inside of a list widget (on the feature/list-widget-reloaded branch). And I have a somewhat working example, where it shows 1 of my 3 pieces of data

I am just a bit confused on the proper set up for this idea. My initial hope was to have a very abstracted interface for app, where I have a struct Data, and my app holds a iced::widget::list::Content<Data>, and creates the widget based on that.

After some messing around, I cannot seem to get much further than placing widgets and things in the top right corner of each row. Introducing row![] appears to be the wrong approach just based on the trait errors I was getting. Not sure if I’m approaching this wrong and I should have like 3 ‘cell’ widgets inside of one ‘row’ widget, but since my app will make no difference between the 3 cells (something something performance?) I decided to do the ‘single row’ approach

It would also be nice if there was super long text in a ‘cell’, it wouldn’t just push the other cells away, like some sort of cut-off.

Code: (trying to only include important parts cause it is pretty long)

// row.rs

pub struct RowData {
    pub title: String,
    pub author: String,
    pub row_num: usize
}

pub struct RowWidget<'a, Message, Theme, Renderer>
where
    Renderer: iced::advanced::Renderer + iced::advanced::text::Renderer,
    Theme: Catalog + iced::widget::text::Catalog + iced::widget::button::Catalog,
{
// like should rowdata be something else?
    rowdata: Element<'a, Message, Theme, Renderer>,
    on_left_click: Option<Message>,
    on_right_click: Option<Message>,
    row_num: usize,
}


impl<'a, Message, Theme, Renderer> RowWidget<'a, Message, Theme, Renderer>
where
    Renderer:
        'a + iced::advanced::Renderer + iced::advanced::text::Renderer,
    Theme: 'a + Catalog + iced::widget::text::Catalog + iced::widget::button::Catalog,
    Message: 'a + Clone,
{
    pub fn new(
        rowdata: RowData,
        on_left_click: Option<Message>,
        on_right_click: Option<Message>,
        row_num: usize,
    ) -> Self {
        let main_part = row![text(rowdata.title.clone()), text(rowdata.author.clone())];
        RowWidget {
            rowdata: main_part.into(),
            on_left_click,
            on_right_click,
            row_num,
        }
    }
}

// this is inside of the impl for `Widget`, where I was trying to 'place' my text and whatnot
    fn draw(
            &self,
            tree: &iced::advanced::widget::Tree,
            renderer: &mut Renderer,
            theme: &Theme,
            style: &renderer::Style,
            layout: layout::Layout<'_>,
            cursor: iced::advanced::mouse::Cursor,
            viewport: &iced::Rectangle,
        ) {
        // varying cell colors
        let cell_color = if self.row_num % 2 == 0 {
            Color {
                    r: 0.3,
                    g: 0.3,
                    b: 0.3,
                    a: 1.0 
            }        
        }
            else {
                
                Color {
                    r: 0.5,
                    g: 0.5,
                    b: 0.5,
                    a: 1.0
                }
            };
        // create the cell color       
        renderer.fill_quad(renderer::Quad {
            bounds: layout.bounds(),
            border: Border::default(), 
            ..renderer::Quad::default()             
        }, cell_color);
        self.rowdata.as_widget().draw(
                &tree.children[0],
            renderer,
            theme,
            &renderer::Style {
                text_color: Color::WHITE
            },
            layout,
            cursor,
            &viewport
            );
// is this the right idea to place a button? All it did was change the color of my cell (and take up all of it)
       // <iced::widget::Button<'_, Message, Theme, Renderer> as Widget<Message, Theme, Renderer>>::draw(&button(text("yello!!")), &tree.children[0], renderer, theme, &renderer::Style { text_color: Color::WHITE}, layout, cursor, &viewport);

impl<'a, Message, Theme, Renderer> RowWidget<'a, Message, Theme, Renderer>
where
    Renderer:
        'a + iced::advanced::Renderer + iced::advanced::text::Renderer,
    Theme: 'a + Catalog + iced::widget::text::Catalog + iced::widget::button::Catalog,
    Message: 'a + Clone,
{
    pub fn new(
        rowdata: RowData,
        on_left_click: Option<Message>,
        on_right_click: Option<Message>,
        row_num: usize,
    ) -> Self {
        let main_part = row![text(rowdata.title.clone()), text(rowdata.author.clone())];
        RowWidget {
            rowdata: main_part.into(),
            on_left_click,
            on_right_click,
            row_num,
        }
    }
}


    }

// main.rs

// (inside `view`)

            scrollable(list(&self.content, |index, item| {
                row::RowWidget::new(
                    row::RowData {
                        title: format!("testing: {} ", index),
                        author: format!("author: {}", index),
                        row_num: index,
                    },
                    None,
                    None,
                    index,
                )
                .into()
            }))

TLDR: How do I ‘spread’ buttons & text out inside of a custom widget?

(Yes I am aware I am doing a very similar widget to Tarkah/iced_table, I wanted some extra functionality, remove some of their features and also practice making widgets)

Well, I got it to sort of work, not very effective though:
image

There seemed to be two main components to getting the data to actually display.

The children function needs to be correct (and I assume hold the parent widget)

    fn children(&self) -> Vec<Tree> {
        vec![Tree::new(&self.rowdata)]
    }

And inside the draw function, I needed to pass in the correct layout child:

// inside `draw`
        self.rowdata.as_widget().draw(
            // <-- draws all of them
            &tree.children[0],
            renderer,
            theme,
            &renderer::Style {
                text_color: Color::WHITE,
            },
            layout.children().next().unwrap(), // this being just `layout` caused issues
            cursor,
            &viewport,
        );

It isn’t exactly what I wanted, since I had the pass in the rows from inside my programs view function:

        container(column![
            row![text("First data"), text("Second Data"), text("Third data")].spacing(140),
            scrollable(list(&self.content, |index, item| {
                row::RowWidget::new(
                    row![
                        text(format!("myrow: {}", index)).size(15.0).width(75.0),
                        text("second col").width(75.0),
                        text("final column").width(120.0)
                    ]
                    .spacing(150)
                    .into(),
                    None,
                    None,
                    index,
                )
                .into()
            }))
            .width(600)
        ])

Which I can live with, it would just be much better if I could offload these things (like the ‘headers’ at the top) inside of the custom widget, so it is more “batteries included” and you just pass in the data. So if anyone knows a proper way to do it. Let me know!!