From bff1e898d59bac1044c8d09750477aeeee24203b Mon Sep 17 00:00:00 2001 From: StarArawn <toasterthegamer@gmail.com> Date: Mon, 6 Dec 2021 19:45:58 -0500 Subject: [PATCH] Added in nine-patch images. --- .../src/render/unified/font/extract.rs | 2 + .../render/unified/font/font_texture_cache.rs | 1 - bevy_kayak_ui/src/render/unified/font/mod.rs | 3 - .../src/render/unified/image/extract.rs | 2 + .../src/render/unified/image/image_manager.rs | 2 +- bevy_kayak_ui/src/render/unified/mod.rs | 4 +- .../src/render/unified/nine_patch/extract.rs | 260 ++++++++++++++++++ .../src/render/unified/nine_patch/mod.rs | 15 + bevy_kayak_ui/src/render/unified/pipeline.rs | 27 +- .../src/render/unified/quad/extract.rs | 2 + bevy_kayak_ui/src/render/unified/shader.wgsl | 1 - examples/nine_patch.rs | 98 +++++++ kayak_components/src/lib.rs | 2 + kayak_components/src/nine_patch.rs | 24 ++ kayak_core/src/layout_cache.rs | 2 +- kayak_core/src/render_command.rs | 6 + kayak_core/src/render_primitive.rs | 13 +- 17 files changed, 444 insertions(+), 20 deletions(-) create mode 100644 bevy_kayak_ui/src/render/unified/nine_patch/extract.rs create mode 100644 bevy_kayak_ui/src/render/unified/nine_patch/mod.rs create mode 100644 examples/nine_patch.rs create mode 100644 kayak_components/src/nine_patch.rs diff --git a/bevy_kayak_ui/src/render/unified/font/extract.rs b/bevy_kayak_ui/src/render/unified/font/extract.rs index 5948efc..43bce7d 100644 --- a/bevy_kayak_ui/src/render/unified/font/extract.rs +++ b/bevy_kayak_ui/src/render/unified/font/extract.rs @@ -138,6 +138,8 @@ pub fn extract_texts( type_index: 0, border_radius: (0.0, 0.0, 0.0, 0.0), image: None, + uv_max: None, + uv_min: None, }, }); diff --git a/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs b/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs index 5726272..f9d526c 100644 --- a/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs +++ b/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs @@ -305,7 +305,6 @@ impl FontTextureCache { atlas_texture: &GpuImage, size: Vec2, ) { - dbg!(size); Self::create_texture( images, font_handle.clone_weak(), diff --git a/bevy_kayak_ui/src/render/unified/font/mod.rs b/bevy_kayak_ui/src/render/unified/font/mod.rs index 1ae275c..8b2a9ff 100644 --- a/bevy_kayak_ui/src/render/unified/font/mod.rs +++ b/bevy_kayak_ui/src/render/unified/font/mod.rs @@ -90,14 +90,11 @@ pub fn set_font_texture( ) { // quick and dirty, run this for all textures anytime a texture is created. for event in texture_events.iter() { - dbg!(&event); match event { AssetEvent::Created { handle } => { let handle_path = asset_server.get_handle_path(handle).unwrap(); - dbg!(&handle_path); if handle_path.path().to_str().unwrap().contains("roboto") { if let Some(mut texture) = textures.get_mut(handle) { - dbg!("Setting font texture!"); texture.texture_descriptor.format = TextureFormat::Rgba8Unorm; texture.sampler_descriptor.min_filter = FilterMode::Linear; texture.sampler_descriptor.mipmap_filter = FilterMode::Linear; diff --git a/bevy_kayak_ui/src/render/unified/image/extract.rs b/bevy_kayak_ui/src/render/unified/image/extract.rs index fee53b6..dfee468 100644 --- a/bevy_kayak_ui/src/render/unified/image/extract.rs +++ b/bevy_kayak_ui/src/render/unified/image/extract.rs @@ -51,6 +51,8 @@ pub fn extract_images( image: image_manager .get_handle(handle) .and_then(|a| Some(a.clone_weak())), + uv_max: None, + uv_min: None, }, }); } diff --git a/bevy_kayak_ui/src/render/unified/image/image_manager.rs b/bevy_kayak_ui/src/render/unified/image/image_manager.rs index b92b08e..0493f8d 100644 --- a/bevy_kayak_ui/src/render/unified/image/image_manager.rs +++ b/bevy_kayak_ui/src/render/unified/image/image_manager.rs @@ -22,7 +22,7 @@ impl ImageManager { } else { let id = self.count; self.count += 1; - self.mapping.insert(id, image_handle.clone_weak()); + self.mapping.insert(id, image_handle.clone()); self.reverse_mapping.insert(image_handle.clone_weak(), id); return id; } diff --git a/bevy_kayak_ui/src/render/unified/mod.rs b/bevy_kayak_ui/src/render/unified/mod.rs index c6ce51c..152bdd7 100644 --- a/bevy_kayak_ui/src/render/unified/mod.rs +++ b/bevy_kayak_ui/src/render/unified/mod.rs @@ -13,6 +13,7 @@ use self::pipeline::ImageBindGroups; pub mod font; pub mod image; +mod nine_patch; mod pipeline; mod quad; @@ -46,6 +47,7 @@ impl Plugin for UnifiedRenderPlugin { app.add_plugin(font::TextRendererPlugin) .add_plugin(quad::QuadRendererPlugin) - .add_plugin(image::ImageRendererPlugin); + .add_plugin(image::ImageRendererPlugin) + .add_plugin(nine_patch::NinePatchRendererPlugin); } } diff --git a/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs b/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs new file mode 100644 index 0000000..c2d75f9 --- /dev/null +++ b/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs @@ -0,0 +1,260 @@ +use bevy::{ + math::Vec2, + prelude::{Assets, Commands, Res}, + render2::{color::Color, texture::Image}, + sprite2::Rect, +}; +use kayak_core::render_primitive::RenderPrimitive; + +use crate::{ + render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType}, + BevyContext, ImageManager, +}; + +pub fn extract_nine_patch( + mut commands: Commands, + context: Res<BevyContext>, + image_manager: Res<ImageManager>, + images: Res<Assets<Image>>, +) { + let render_commands = if let Ok(context) = context.kayak_context.read() { + context.widget_manager.build_render_primitives() + } else { + vec![] + }; + + let image_commands: Vec<&RenderPrimitive> = render_commands + .iter() + .filter(|command| matches!(command, RenderPrimitive::NinePatch { .. })) + .collect::<Vec<_>>(); + + let mut extracted_quads = Vec::new(); + for render_primitive in image_commands { + let (layout, handle, border) = match render_primitive { + RenderPrimitive::NinePatch { + layout, + handle, + border, + } => (layout, handle, border), + _ => panic!(""), + }; + + let image_handle = image_manager + .get_handle(handle) + .and_then(|a| Some(a.clone_weak())); + + let image = images.get(image_handle.as_ref().unwrap()); + + if image.is_none() { + dbg!("Uh oh no image! :("); + continue; + } + + let image_size = image + .and_then(|i| { + Some(Vec2::new( + i.texture_descriptor.size.width as f32, + i.texture_descriptor.size.height as f32, + )) + }) + .unwrap(); + + let extracted_quad_template = ExtractedQuad { + rect: Rect { + min: Vec2::ZERO, + max: Vec2::ZERO, + }, + color: Color::WHITE, + vertex_index: 0, + char_id: 0, + z_index: layout.z_index, + font_handle: None, + quad_type: UIQuadType::Image, + type_index: 0, + border_radius: (0.0, 0.0, 0.0, 0.0), + image: image_handle, + uv_max: None, + uv_min: None, + }; + + // TOP + let top_left_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(layout.posx, layout.posy), + max: Vec2::new(layout.posx + border.left, layout.posy + border.top), + }, + uv_min: Some(Vec2::new(0.0, border.top / image_size.y)), + uv_max: Some(Vec2::new(border.left / image_size.x, 0.0)), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(top_left_quad); + + let top_right_pos_x = (layout.posx + layout.width) - border.left; + let top_right_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(top_right_pos_x, layout.posy), + max: Vec2::new(top_right_pos_x + border.left, layout.posy + border.top), + }, + uv_min: Some(Vec2::new( + (image_size.x - border.left) / image_size.x, + border.top / image_size.y, + )), + uv_max: Some(Vec2::new(1.0, 0.0)), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(top_right_quad); + + let top_middle_pos_x = layout.posx + border.left; + let top_middle_size_x = layout.width - (border.left + border.right); + let top_middle_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(top_middle_pos_x, layout.posy), + max: Vec2::new( + top_middle_pos_x + top_middle_size_x, + layout.posy + border.top, + ), + }, + uv_min: Some(Vec2::new( + border.left / image_size.x, + border.top / image_size.y, + )), + uv_max: Some(Vec2::new((image_size.x - border.left) / image_size.x, 0.0)), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(top_middle_quad); + + // Bottom + let bottom_y_pos = layout.posy + (layout.width - border.bottom); + let bottom_left_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(layout.posx, bottom_y_pos), + max: Vec2::new(layout.posx + border.left, bottom_y_pos + border.bottom), + }, + uv_min: Some(Vec2::new(0.0, 1.0)), + uv_max: Some(Vec2::new( + border.left / image_size.x, + (image_size.y - border.bottom) / image_size.y, + )), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(bottom_left_quad); + + let bottom_right_pos_x = (layout.posx + layout.width) - border.left; + let bottom_right_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(bottom_right_pos_x, bottom_y_pos), + max: Vec2::new(bottom_right_pos_x + border.left, bottom_y_pos + border.top), + }, + uv_min: Some(Vec2::new((image_size.x - border.left) / image_size.x, 1.0)), + uv_max: Some(Vec2::new( + 1.0, + (image_size.y - border.bottom) / image_size.y, + )), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(bottom_right_quad); + + let bottom_middle_pos_x = layout.posx + border.left; + let bottom_middle_size_x = layout.width - (border.left + border.right); + let bottom_middle_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(bottom_middle_pos_x, bottom_y_pos), + max: Vec2::new( + bottom_middle_pos_x + bottom_middle_size_x, + bottom_y_pos + border.top, + ), + }, + uv_min: Some(Vec2::new(border.left / image_size.x, 1.0)), + uv_max: Some(Vec2::new( + (image_size.x - border.left) / image_size.x, + (image_size.y - border.bottom) / image_size.y, + )), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(bottom_middle_quad); + + // Left + Right center + let left_middle_pos_y = layout.posy + border.top; + let left_middle_size_y = layout.height - (border.top + border.bottom); + let left_middle_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(layout.posx, left_middle_pos_y), + max: Vec2::new( + layout.posx + border.left, + left_middle_pos_y + left_middle_size_y, + ), + }, + uv_min: Some(Vec2::new( + 0.0, + (image_size.y - border.bottom) / image_size.y, + )), + uv_max: Some(Vec2::new( + border.left / image_size.x, + border.top / image_size.y, + )), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(left_middle_quad); + + let right_middle_pos_x = layout.posx + (layout.width - border.right); + let right_middle_pos_y = layout.posy + border.top; + let right_middle_size_y = layout.height - (border.top + border.bottom); + let right_middle_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(right_middle_pos_x, right_middle_pos_y), + max: Vec2::new( + right_middle_pos_x + border.left, + right_middle_pos_y + right_middle_size_y, + ), + }, + uv_min: Some(Vec2::new( + (image_size.x - border.left) / image_size.x, + (image_size.y - border.bottom) / image_size.y, + )), + uv_max: Some(Vec2::new(1.0, border.top / image_size.y)), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(right_middle_quad); + + // Last quad in middle. + let middle_pos_x = layout.posx + border.left; + let middle_pos_y = layout.posy + border.top; + let middle_size_x = layout.width - (border.left + border.right); + let middle_size_y = layout.height - (border.top + border.bottom); + let middle_quad = ExtractQuadBundle { + extracted_quad: ExtractedQuad { + rect: Rect { + min: Vec2::new(middle_pos_x, middle_pos_y), + max: Vec2::new(middle_pos_x + middle_size_x, middle_pos_y + middle_size_y), + }, + uv_min: Some(Vec2::new( + border.left / image_size.x, + border.bottom / image_size.y, + )), + uv_max: Some(Vec2::new( + (image_size.x - border.right) / image_size.x, + (image_size.y - border.bottom) / image_size.y, + )), + ..extracted_quad_template.clone() + }, + }; + extracted_quads.push(middle_quad); + } + commands.spawn_batch(extracted_quads); +} diff --git a/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs b/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs new file mode 100644 index 0000000..dfaec00 --- /dev/null +++ b/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs @@ -0,0 +1,15 @@ +use bevy::{ + prelude::Plugin, + render2::{RenderApp, RenderStage}, +}; + +mod extract; + +pub struct NinePatchRendererPlugin; + +impl Plugin for NinePatchRendererPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + let render_app = app.sub_app(RenderApp); + render_app.add_system_to_stage(RenderStage::Extract, extract::extract_nine_patch); + } +} diff --git a/bevy_kayak_ui/src/render/unified/pipeline.rs b/bevy_kayak_ui/src/render/unified/pipeline.rs index 483983c..a787e71 100644 --- a/bevy_kayak_ui/src/render/unified/pipeline.rs +++ b/bevy_kayak_ui/src/render/unified/pipeline.rs @@ -4,7 +4,7 @@ use bevy::{ lifetimeless::{Read, SQuery, SRes}, SystemState, }, - math::{const_vec3, Mat4, Quat, Vec3, Vec4}, + math::{const_vec3, Mat4, Quat, Vec2, Vec3, Vec4}, prelude::{Bundle, Component, Entity, FromWorld, Handle, Query, Res, ResMut, World}, render2::{ color::Color, @@ -289,7 +289,7 @@ impl FromWorld for UnifiedPipeline { } } -#[derive(Bundle)] +#[derive(Debug, Bundle)] pub struct ExtractQuadBundle { pub(crate) extracted_quad: ExtractedQuad, } @@ -301,7 +301,7 @@ pub enum UIQuadType { Image, } -#[derive(Component)] +#[derive(Debug, Component, Clone)] pub struct ExtractedQuad { pub rect: Rect, pub color: Color, @@ -313,6 +313,8 @@ pub struct ExtractedQuad { pub type_index: u32, pub border_radius: (f32, f32, f32, f32), pub image: Option<Handle<Image>>, + pub uv_min: Option<Vec2>, + pub uv_max: Option<Vec2>, } #[repr(C)] @@ -390,27 +392,30 @@ pub fn prepare_quads( UIQuadType::Image => extracted_sprite.type_index = image_type_offset, }; + let uv_min = extracted_sprite.uv_min.unwrap_or(Vec2::ZERO); + let uv_max = extracted_sprite.uv_max.unwrap_or(Vec2::ONE); + let bottom_left = Vec4::new( - 0.0, - 1.0, + uv_min.x, + uv_max.y, extracted_sprite.char_id as f32, extracted_sprite.border_radius.0, ); let top_left = Vec4::new( - 0.0, - 0.0, + uv_min.x, + uv_min.y, extracted_sprite.char_id as f32, extracted_sprite.border_radius.1, ); let top_right = Vec4::new( - 1.0, - 0.0, + uv_max.x, + uv_min.y, extracted_sprite.char_id as f32, extracted_sprite.border_radius.2, ); let bottom_right = Vec4::new( - 1.0, - 1.0, + uv_max.x, + uv_max.y, extracted_sprite.char_id as f32, extracted_sprite.border_radius.3, ); diff --git a/bevy_kayak_ui/src/render/unified/quad/extract.rs b/bevy_kayak_ui/src/render/unified/quad/extract.rs index b81a7d0..1a9fcf4 100644 --- a/bevy_kayak_ui/src/render/unified/quad/extract.rs +++ b/bevy_kayak_ui/src/render/unified/quad/extract.rs @@ -48,6 +48,8 @@ pub fn extract_quads(mut commands: Commands, context: Res<BevyContext>) { type_index: 0, border_radius: *border_radius, image: None, + uv_max: None, + uv_min: None, }, }); } diff --git a/bevy_kayak_ui/src/render/unified/shader.wgsl b/bevy_kayak_ui/src/render/unified/shader.wgsl index 92b5463..c32bbb8 100644 --- a/bevy_kayak_ui/src/render/unified/shader.wgsl +++ b/bevy_kayak_ui/src/render/unified/shader.wgsl @@ -38,7 +38,6 @@ fn vertex( out.uv = vertex_uv.xyz; out.size = vertex_pos_size.zw; out.border_radius = vertex_uv.w; - return out; } diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs new file mode 100644 index 0000000..0a14f1a --- /dev/null +++ b/examples/nine_patch.rs @@ -0,0 +1,98 @@ +use bevy::{ + math::Vec2, + prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut}, + window::{WindowDescriptor, Windows}, + PipelinedDefaultPlugins, +}; +use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle}; +use kayak_components::NinePatch; +use kayak_core::{ + layout_cache::Space, + styles::{Style, StyleProp, Units}, + Index, +}; +use kayak_ui::components::App; +use kayak_ui::core::rsx; + +fn startup( + mut commands: Commands, + windows: Res<Windows>, + asset_server: Res<AssetServer>, + mut image_manager: ResMut<ImageManager>, +) { + commands.spawn_bundle(UICameraBundle::new()); + + let window_size = if let Some(window) = windows.get_primary() { + Vec2::new(window.width(), window.height()) + } else { + panic!("Couldn't find primary window!"); + }; + + let handle: Handle<bevy::render2::texture::Image> = asset_server.load("panel.png"); + let ui_image_handle = image_manager.get(&handle); + + let context = BevyContext::new(window_size.x, window_size.y, |styles, context| { + // Hack to trick the proc macro for right now.. + let parent_id: Option<Index> = None; + + // The border prop splits up the image into 9 quadrants like so: + // 1----2----3 + // | | + // 4 9 5 + // | | + // 6----7----8 + // The sizes of sprites for a 15 pixel border are as follows: + // TopLeft = (15, 15) + // TopRight = (15, 15) + // LeftCenter = (15, image_height) + // RightCenter = (15, image_height) + // TopCenter = (image_width, 15) + // BottomCenter = (image_width, 15) + // BottomRight = (15, 15) + // BottomLeft = (15, 15) + // Middle = ( + // 30 being left border + right border + // image_width - 30 + // 30 being top border + bottom border + // image_height - 30 + // ) + // + + let nine_patch_styles = Style { + width: StyleProp::Value(Units::Pixels(512.0)), + height: StyleProp::Value(Units::Pixels(512.0)), + ..Style::default() + }; + + rsx! { + <App styles={Some(styles.clone())}> + <NinePatch + styles={Some(nine_patch_styles)} + border={Space { + left: 15.0, + right: 15.0, + top: 15.0, + bottom: 15.0, + }} + handle={ui_image_handle} + /> + </App> + } + }); + + commands.insert_resource(context); +} + +fn main() { + BevyApp::new() + .insert_resource(WindowDescriptor { + width: 1270.0, + height: 720.0, + title: String::from("UI Example"), + ..Default::default() + }) + .add_plugins(PipelinedDefaultPlugins) + .add_plugin(BevyKayakUIPlugin) + .add_startup_system(startup) + .run(); +} diff --git a/kayak_components/src/lib.rs b/kayak_components/src/lib.rs index 47472bd..d59d930 100644 --- a/kayak_components/src/lib.rs +++ b/kayak_components/src/lib.rs @@ -3,6 +3,7 @@ mod background; mod button; mod clip; mod image; +mod nine_patch; mod text; mod window; @@ -11,5 +12,6 @@ pub use background::*; pub use button::*; pub use clip::*; pub use image::*; +pub use nine_patch::*; pub use text::*; pub use window::*; diff --git a/kayak_components/src/nine_patch.rs b/kayak_components/src/nine_patch.rs new file mode 100644 index 0000000..8d652ab --- /dev/null +++ b/kayak_components/src/nine_patch.rs @@ -0,0 +1,24 @@ +use kayak_core::{ + layout_cache::Space, + render_command::RenderCommand, + rsx, + styles::{Style, StyleProp}, + widget, Children, +}; + +#[widget] +pub fn NinePatch(handle: u16, border: Space, children: Children) { + *styles = Some(Style { + render_command: StyleProp::Value(RenderCommand::NinePatch { + handle: *handle, + border: *border, + }), + ..styles.clone().unwrap_or_default() + }); + + rsx! { + <> + {children} + </> + } +} diff --git a/kayak_core/src/layout_cache.rs b/kayak_core/src/layout_cache.rs index c843be7..9b31c08 100644 --- a/kayak_core/src/layout_cache.rs +++ b/kayak_core/src/layout_cache.rs @@ -20,7 +20,7 @@ impl Rect { } } -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Space { pub left: f32, pub right: f32, diff --git a/kayak_core/src/render_command.rs b/kayak_core/src/render_command.rs index cb6839f..c717a2f 100644 --- a/kayak_core/src/render_command.rs +++ b/kayak_core/src/render_command.rs @@ -1,3 +1,5 @@ +use crate::layout_cache::Space; + #[derive(Debug, Clone, PartialEq)] pub enum RenderCommand { Empty, @@ -12,6 +14,10 @@ pub enum RenderCommand { Image { handle: u16, }, + NinePatch { + border: Space, + handle: u16, + }, } impl Default for RenderCommand { diff --git a/kayak_core/src/render_primitive.rs b/kayak_core/src/render_primitive.rs index 457c75e..54b3224 100644 --- a/kayak_core/src/render_primitive.rs +++ b/kayak_core/src/render_primitive.rs @@ -1,6 +1,6 @@ use crate::{ color::Color, - layout_cache::Rect, + layout_cache::{Rect, Space}, render_command::RenderCommand, styles::{Style, StyleProp}, }; @@ -27,6 +27,11 @@ pub enum RenderPrimitive { layout: Rect, handle: u16, }, + NinePatch { + border: Space, + layout: Rect, + handle: u16, + }, } impl RenderPrimitive { @@ -36,6 +41,7 @@ impl RenderPrimitive { RenderPrimitive::Quad { layout, .. } => *layout = new_layout, RenderPrimitive::Text { layout, .. } => *layout = new_layout, RenderPrimitive::Image { layout, .. } => *layout = new_layout, + RenderPrimitive::NinePatch { layout, .. } => *layout = new_layout, _ => (), } } @@ -77,6 +83,11 @@ impl From<&Style> for RenderPrimitive { layout: Rect::default(), handle, }, + RenderCommand::NinePatch { handle, border } => Self::NinePatch { + border, + layout: Rect::default(), + handle, + }, } } } -- GitLab