In the previous step, we created an ImageRow to display a row of images as part of building an image grid for our app. Recall that our image grid will be structured as follows: - Each image is stored in an `ImageItem`. - Multiple items are laid out horizontally within a `ImageRow`. - Multiple rows are laid out vertically within an `ImageGrid`. In this step, we'll group multiple `ImageRow`s in an `ImageGrid` to display a grid of images. The code for `ImageGrid` is highly similar to what we wrote for `ImageRow`, so we won't have to go over it in much detail. > **Note:** > If you don't feel like typing along, you can find all the code for this step here: > https://github.com/makepad/image_viewer/tree/main/step_5 ## What you will learn In this step, you will learn: - How to group items vertically. ## Defining an `ImageGrid` Let's start by adding a definition for an `ImageGrid`. An `ImageGrid` is responsible for laying out multiple `ImageRow`s vertically. In **app.rs**, add the following code to the live design block, after the definition of `ImageRow`: ```rust ImageGrid = {{ImageGrid}} { <PortalList> { flow: Down, ImageRow = <ImageRow> {} } } ``` As with `ImageRow`, within an `ImageGrid`, we use a `PortalList` to list its rows. The only difference is that we use `flow: Down`, which ensures its children are laid out from top to bottom. ## Updating `App` Now that we have an `ImageGrid`, let's update our definition of `App` to display an `ImageGrid` instead of an `ImageRow`. In **app.rs**, replace the definition of `App` in the live design block with the one here below: ```rust App = {{App}} { ui: <Root> { <Window> { body = <View> { <ImageGrid> {} } } } } ``` ## Adding the Rust Code Add the following code to **app.rs**: ```rust #[derive(Live, LiveHook, Widget)] pub struct ImageGrid { #[deref] view: View, } impl Widget for ImageGrid { fn draw_walk( &mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk, ) -> DrawStep { while let Some(item) = self.view.draw_walk(cx, scope, walk).step() { if let Some(mut list) = item.as_portal_list().borrow_mut() { list.set_item_range(cx, 0, 3); while let Some(row_idx) = list.next_visible_item(cx) { let row = list.item(cx, row_idx, live_id!(ImageRow)); row.draw_all(cx, &mut Scope::empty()); } } } DrawStep::done() } fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) { self.view.handle_event(cx, event, scope) } } ``` This defines the `ImageGrid` struct and implements the `Widget` trait for it. This code is highly similar to what we wrote for the `ImageRow` struct, so we won't go over it in detail. The only difference is that instead of drawing 4 `ImageItems`, we are drawing 3 `ImageRows`. ## Checking our Progress so far Let's check our progress so far. Make sure you're in your package directory, and run: ``` cargo run --release ``` If everything is working correctly, a window containing a grid of placeholder images should appear on your screen: ![[ImageGrid Placeholders.png]] ## What's next In this step, we created an `ImageGrid` to display a grid of images. We now have an initial implementation of an image grid. It's still very limited — it always displays both the same number of rows and number of items per row, and it only displays placeholder images, but we're off to a good start! In the next few steps, we'll remove these limitations and make the image grid dynamic.