From 33de177f67bc75e403e296cea33365094af91d92 Mon Sep 17 00:00:00 2001
From: StarToaster <startoaster23@gmail.com>
Date: Thu, 27 Oct 2022 18:58:17 -0400
Subject: [PATCH] Updated to the latest bevy! And fixed a few issues.

---
 Cargo.toml                       |   3 +-
 examples/clipping.rs             |   9 +-
 examples/context.rs              |  11 +-
 examples/hello_world.rs          |   1 -
 examples/hello_world_no_macro.rs |   1 -
 examples/image.rs                |  14 +--
 examples/nine_patch.rs           |  10 +-
 examples/quads.rs                |  10 +-
 examples/scrolling.rs            |   8 +-
 examples/simple_state.rs         |  11 +-
 examples/tabs/tabs.rs            |   1 -
 examples/text.rs                 |  49 ++++----
 examples/text_box.rs             |  10 +-
 examples/texture_atlas.rs        |  16 +--
 examples/todo/items.rs           | 136 +++++++++++----------
 examples/vec.rs                  |  11 +-
 examples/widget_template.rs      |  51 --------
 kayak_font/Cargo.toml            |   4 +-
 kayak_font/examples/bevy.rs      |  18 +--
 kayak_ui_macros/Cargo.toml       |   2 +-
 src/camera/camera.rs             |   6 +-
 src/input.rs                     |  24 ++--
 src/render/mod.rs                | 130 +++++++++++++++++---
 src/render/ui_pass.rs            |  14 +--
 src/render/unified/mod.rs        |   5 +-
 src/render/unified/pipeline.rs   | 197 ++++++++++++++++++-------------
 src/styles/style.rs              |   4 +-
 src/widgets/image.rs             |  13 +-
 src/widgets/mod.rs               |  10 +-
 src/widgets/nine_patch.rs        |   5 +-
 src/widgets/scroll/scroll_bar.rs |   4 +-
 src/widgets/scroll/scroll_box.rs |   4 +-
 src/widgets/texture_atlas.rs     |  21 ++--
 src/widgets/window.rs            |   4 +-
 34 files changed, 420 insertions(+), 397 deletions(-)
 delete mode 100644 examples/widget_template.rs

diff --git a/Cargo.toml b/Cargo.toml
index be47d22..10084b0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,7 @@ members = ["kayak_ui_macros"]
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-bevy = { git = "https://github.com/bevyengine/bevy", rev="9423cb6a8d0c140e11364eb23c8feb7e576baa8c" }
+bevy = { git = "https://github.com/bevyengine/bevy", rev="4bcf49b2ea6fb5f42388b0e15d204020053ee5c7" }
 bytemuck = "1.12"
 dashmap = "5.4"
 kayak_font = { path = "./kayak_font" }
@@ -18,6 +18,7 @@ morphorm = { git = "https://github.com/geom3trik/morphorm", rev = "1243152d4cebe
 kayak_ui_macros = { path = "./kayak_ui_macros" }
 indexmap = "1.9"
 log = "0.4"
+bitflags = "1.3.2"
 
 [dev-dependencies]
 fastrand = "1.8"
diff --git a/examples/clipping.rs b/examples/clipping.rs
index 86d3027..47ab6c1 100644
--- a/examples/clipping.rs
+++ b/examples/clipping.rs
@@ -1,7 +1,4 @@
-use bevy::{
-    prelude::{App, AssetServer, Commands, ImageSettings, Res, ResMut},
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 fn startup(
@@ -64,8 +61,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque.
 
 fn main() {
     App::new()
-        .insert_resource(ImageSettings::default_nearest())
-        .add_plugins(DefaultPlugins)
+        .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
+        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
         .add_startup_system(startup)
diff --git a/examples/context.rs b/examples/context.rs
index 3e26f1f..3a2e654 100644
--- a/examples/context.rs
+++ b/examples/context.rs
@@ -7,13 +7,7 @@
 //! for better specificity and makes local contexts much easier to manage. In the case of theming,
 //! this allows us to have multiple active themes, even if they are nested within each other!
 
-use bevy::{
-    prelude::{
-        App as BevyApp, AssetServer, Bundle, Color, Commands, Component, Entity, ImageSettings, In,
-        Query, Res, ResMut, Vec2,
-    },
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 /// The color theme struct we will be using across our demo widgets
@@ -395,8 +389,7 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
-        .insert_resource(ImageSettings::default_nearest())
+    App::new()
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/hello_world.rs b/examples/hello_world.rs
index b4269ab..759faea 100644
--- a/examples/hello_world.rs
+++ b/examples/hello_world.rs
@@ -27,7 +27,6 @@ fn startup(
 
 fn main() {
     App::new()
-        .insert_resource(ImageSettings::default_nearest())
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs
index 7f951a4..02192bd 100644
--- a/examples/hello_world_no_macro.rs
+++ b/examples/hello_world_no_macro.rs
@@ -53,7 +53,6 @@ fn startup(
 }
 fn main() {
     App::new()
-        .insert_resource(ImageSettings::default_nearest())
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/image.rs b/examples/image.rs
index 7b89333..ec2cd59 100644
--- a/examples/image.rs
+++ b/examples/image.rs
@@ -1,7 +1,4 @@
-use bevy::{
-    prelude::{App as BevyApp, AssetServer, Commands, ImageSettings, Res, ResMut},
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 fn startup(
@@ -19,10 +16,10 @@ fn startup(
     let parent_id = None;
     rsx! {
         <KayakAppBundle>
-            <ImageBundle
+            <KImageBundle
                 image={Image(image.clone())}
                 style={KStyle {
-                    position_type: StyleProp::Value(PositionType::SelfDirected),
+                    position_type: StyleProp::Value(KPositionType::SelfDirected),
                     left: StyleProp::Value(Units::Pixels(10.0)),
                     top: StyleProp::Value(Units::Pixels(10.0)),
                     border_radius: StyleProp::Value(Corner::all(500.0)),
@@ -37,9 +34,8 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
-        .insert_resource(ImageSettings::default_nearest())
-        .add_plugins(DefaultPlugins)
+    App::new()
+        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
         .add_startup_system(startup)
diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs
index f2558dd..5dd452d 100644
--- a/examples/nine_patch.rs
+++ b/examples/nine_patch.rs
@@ -1,7 +1,4 @@
-use bevy::{
-    prelude::{App as BevyApp, AssetServer, Commands, ImageSettings, Res, ResMut},
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 fn startup(
@@ -59,9 +56,8 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
-        .insert_resource(ImageSettings::default_nearest())
-        .add_plugins(DefaultPlugins)
+    App::new()
+        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
         .add_startup_system(startup)
diff --git a/examples/quads.rs b/examples/quads.rs
index 4bd8422..1681416 100644
--- a/examples/quads.rs
+++ b/examples/quads.rs
@@ -1,10 +1,6 @@
 use bevy::{
     diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
-    prelude::{
-        App as BevyApp, AssetServer, Bundle, Color, Commands, Component, Entity, In, Query, Res,
-        ResMut, Vec2,
-    },
-    DefaultPlugins,
+    prelude::*,
 };
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
@@ -22,7 +18,7 @@ fn my_quad_update(
     if let Ok((quad, mut style, mut on_event)) = query.get_mut(entity) {
         if style.render_command.resolve() != RenderCommand::Quad {
             style.render_command = StyleProp::Value(RenderCommand::Quad);
-            style.position_type = StyleProp::Value(PositionType::SelfDirected);
+            style.position_type = StyleProp::Value(KPositionType::SelfDirected);
             style.left = StyleProp::Value(Units::Pixels(quad.pos.x));
             style.top = StyleProp::Value(Units::Pixels(quad.pos.y));
             style.width = StyleProp::Value(Units::Pixels(quad.size.x));
@@ -126,7 +122,7 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
+    App::new()
         .add_plugins(DefaultPlugins)
         .add_plugin(LogDiagnosticsPlugin::default())
         .add_plugin(FrameTimeDiagnosticsPlugin::default())
diff --git a/examples/scrolling.rs b/examples/scrolling.rs
index 3c04982..6c87b1b 100644
--- a/examples/scrolling.rs
+++ b/examples/scrolling.rs
@@ -1,7 +1,4 @@
-use bevy::{
-    prelude::{App, AssetServer, Commands, ImageSettings, Res, ResMut},
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 fn startup(
@@ -66,8 +63,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque.
 
 fn main() {
     App::new()
-        .insert_resource(ImageSettings::default_nearest())
-        .add_plugins(DefaultPlugins)
+        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
         .add_startup_system(startup)
diff --git a/examples/simple_state.rs b/examples/simple_state.rs
index bbae044..00fe63c 100644
--- a/examples/simple_state.rs
+++ b/examples/simple_state.rs
@@ -1,10 +1,4 @@
-use bevy::{
-    prelude::{
-        App as BevyApp, AssetServer, Bundle, Commands, Component, Entity, In, Query, Res, ResMut,
-        Vec2,
-    },
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, *};
 
 #[derive(Component, Default, PartialEq, Clone)]
@@ -125,7 +119,8 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
+    App::new()
+        .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs
index d0827ce..416f0a9 100644
--- a/examples/tabs/tabs.rs
+++ b/examples/tabs/tabs.rs
@@ -89,7 +89,6 @@ fn startup(
 
 fn main() {
     App::new()
-        .insert_resource(ImageSettings::default_nearest())
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/text.rs b/examples/text.rs
index b7d90b3..b53322d 100644
--- a/examples/text.rs
+++ b/examples/text.rs
@@ -1,10 +1,4 @@
-use bevy::{
-    prelude::{
-        App as BevyApp, AssetServer, Bundle, Commands, Component, Entity, In, Input, KeyCode,
-        Query, Res, ResMut, Resource,
-    },
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 #[derive(Component, Default, Clone, PartialEq)]
@@ -12,24 +6,35 @@ pub struct MyWidgetProps {
     pub foo: u32,
 }
 
-fn my_widget_1_update(
+fn my_widget_1_render(
     In((_widget_context, entity)): In<(KayakWidgetContext, Entity)>,
     my_resource: Res<MyResource>,
     mut query: Query<(&mut MyWidgetProps, &mut KStyle)>,
 ) -> bool {
-    if my_resource.is_changed() || my_resource.is_added() {
-        if let Ok((mut my_widget, mut style)) = query.get_mut(entity) {
-            my_widget.foo = my_resource.0;
-            dbg!(my_widget.foo);
-            style.render_command = StyleProp::Value(RenderCommand::Text {
-                content: format!("My number is: {}", my_widget.foo).to_string(),
-                alignment: Alignment::Start,
-            });
-            return true;
-        }
+    if let Ok((mut my_widget, mut style)) = query.get_mut(entity) {
+        my_widget.foo = my_resource.0;
+        dbg!(my_widget.foo);
+        // Note: We will see two updates because of the mutable change to styles.
+        // Which means when foo changes MyWidget will render twice!
+        style.render_command = StyleProp::Value(RenderCommand::Text {
+            content: format!("My number is: {}", my_widget.foo).to_string(),
+            alignment: Alignment::Start,
+        });
     }
 
-    false
+    true
+}
+
+// Our own version of widget_update that handles resource change events.
+pub fn widget_update_with_resource<
+    Props: PartialEq + Component + Clone,
+    State: PartialEq + Component + Clone,
+>(
+    In((widget_context, entity, previous_entity)): In<(KayakWidgetContext, Entity, Entity)>,
+    my_resource: Res<MyResource>,
+    widget_param: WidgetParam<Props, State>,
+) -> bool {
+    widget_param.has_changed(&widget_context, entity, previous_entity) || my_resource.is_changed()
 }
 
 impl Widget for MyWidgetProps {}
@@ -68,8 +73,8 @@ fn startup(
     widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
     widget_context.add_widget_system(
         MyWidgetProps::default().get_name(),
-        widget_update::<MyWidgetProps, EmptyState>,
-        my_widget_1_update,
+        widget_update_with_resource::<MyWidgetProps, EmptyState>,
+        my_widget_1_render,
     );
     rsx! {
         <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle>
@@ -84,7 +89,7 @@ fn update_resource(keyboard_input: Res<Input<KeyCode>>, mut my_resource: ResMut<
 }
 
 fn main() {
-    BevyApp::new()
+    App::new()
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/text_box.rs b/examples/text_box.rs
index 707d43d..0e3f409 100644
--- a/examples/text_box.rs
+++ b/examples/text_box.rs
@@ -1,10 +1,4 @@
-use bevy::{
-    prelude::{
-        App as BevyApp, AssetServer, Bundle, Commands, Component, Entity, In, Query, Res, ResMut,
-        Vec2,
-    },
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, *};
 
 #[derive(Component, Default, Clone, PartialEq)]
@@ -126,7 +120,7 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
+    App::new()
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs
index b609a87..369ad47 100644
--- a/examples/texture_atlas.rs
+++ b/examples/texture_atlas.rs
@@ -1,7 +1,4 @@
-use bevy::{
-    prelude::{App as BevyApp, AssetServer, Commands, ImageSettings, Res, ResMut},
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 fn startup(
@@ -37,7 +34,7 @@ fn startup(
     let parent_id = None;
 
     let atlas_styles = KStyle {
-        position_type: StyleProp::Value(PositionType::ParentDirected),
+        position_type: StyleProp::Value(KPositionType::ParentDirected),
         width: StyleProp::Value(Units::Pixels(200.0)),
         height: StyleProp::Value(Units::Pixels(200.0)),
         ..KStyle::default()
@@ -54,7 +51,7 @@ fn startup(
     rsx! {
         <KayakAppBundle>
             <TextureAtlasBundle
-                atlas={TextureAtlas {
+                atlas={TextureAtlasProps {
                     handle: image_handle.clone(),
                     position: sign_position,
                     tile_size: sign_size,
@@ -62,7 +59,7 @@ fn startup(
                 styles={atlas_styles.clone()}
             />
             <TextureAtlasBundle
-                atlas={TextureAtlas {
+                atlas={TextureAtlasProps {
                     handle: image_handle.clone(),
                     position: flower_position,
                     tile_size: flower_size,
@@ -76,9 +73,8 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
-        .insert_resource(ImageSettings::default_nearest())
-        .add_plugins(DefaultPlugins)
+    App::new()
+        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
         .add_startup_system(startup)
diff --git a/examples/todo/items.rs b/examples/todo/items.rs
index 900eee4..789589c 100644
--- a/examples/todo/items.rs
+++ b/examples/todo/items.rs
@@ -34,80 +34,76 @@ pub fn render_todo_items(
     In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
     mut commands: Commands,
     todo_list: Res<TodoList>,
-    query: Query<&TodoItemsProps, Or<(Changed<Style>, Changed<TodoItemsProps>, With<Mounted>)>>,
 ) -> bool {
-    if query.is_empty() || todo_list.is_changed() {
-        let parent_id = Some(entity);
-        rsx! {
-            <ElementBundle
-                styles={KStyle {
-                    height: Units::Auto.into(),
-                    ..Default::default()
-                }}
-            >
-                {todo_list.items.iter().enumerate().for_each(|(index, content)| {
-                    let handle_click = OnEvent::new(
-                        move |In((event_dispatcher_context, _, event, _)): In<(
-                            EventDispatcherContext,
-                            WidgetState,
-                            Event,
-                            Entity,
-                        )>,
-                            mut todo_list: ResMut<TodoList>,| {
-                            match event.event_type {
-                                EventType::Click(..) => {
-                                    todo_list.items.remove(index);
-                                },
-                                _ => {}
-                            }
-                            (event_dispatcher_context, event)
-                        },
-                    );
-                    constructor! {
-                        <ElementBundle
+    let parent_id = Some(entity);
+    rsx! {
+        <ElementBundle
+            styles={KStyle {
+                height: Units::Auto.into(),
+                ..Default::default()
+            }}
+        >
+            {todo_list.items.iter().enumerate().for_each(|(index, content)| {
+                let handle_click = OnEvent::new(
+                    move |In((event_dispatcher_context, _, event, _)): In<(
+                        EventDispatcherContext,
+                        WidgetState,
+                        Event,
+                        Entity,
+                    )>,
+                        mut todo_list: ResMut<TodoList>,| {
+                        match event.event_type {
+                            EventType::Click(..) => {
+                                todo_list.items.remove(index);
+                            },
+                            _ => {}
+                        }
+                        (event_dispatcher_context, event)
+                    },
+                );
+                constructor! {
+                    <ElementBundle
+                        styles={KStyle {
+                            render_command: StyleProp::Value(RenderCommand::Quad),
+                            background_color: StyleProp::Value(Color::rgba(0.0781, 0.0898, 0.101, 1.0)),
+                            border_radius: StyleProp::Value(Corner::all(3.0)),
+                            bottom: StyleProp::Value(Units::Pixels(5.0)),
+                            height: StyleProp::Value(Units::Auto),
+                            padding: StyleProp::Value(Edge::all(Units::Pixels(10.0))),
+                            layout_type: StyleProp::Value(LayoutType::Row),
+                            ..Default::default()
+                        }}
+                    >
+                        <TextWidgetBundle
+                            text={TextProps {
+                                content: content.clone(),
+                                ..Default::default()
+                            }}
                             styles={KStyle {
-                                render_command: StyleProp::Value(RenderCommand::Quad),
-                                background_color: StyleProp::Value(Color::rgba(0.0781, 0.0898, 0.101, 1.0)),
-                                border_radius: StyleProp::Value(Corner::all(3.0)),
-                                bottom: StyleProp::Value(Units::Pixels(5.0)),
-                                height: StyleProp::Value(Units::Auto),
-                                padding: StyleProp::Value(Edge::all(Units::Pixels(10.0))),
-                                layout_type: StyleProp::Value(LayoutType::Row),
+                                right: StyleProp::Value(Units::Stretch(1.0)),
+                                top: StyleProp::Value(Units::Stretch(1.0)),
+                                bottom: StyleProp::Value(Units::Stretch(1.0)),
                                 ..Default::default()
                             }}
+                        />
+                        <KButtonBundle
+                            styles={KStyle {
+                                width: StyleProp::Value(Units::Pixels(32.0)),
+                                height: StyleProp::Value(Units::Pixels(32.0)),
+                                left: StyleProp::Value(Units::Pixels(15.0)),
+                                ..Default::default()
+                            }}
+                            on_event={handle_click}
                         >
-                            <TextWidgetBundle
-                                text={TextProps {
-                                    content: content.clone(),
-                                    ..Default::default()
-                                }}
-                                styles={KStyle {
-                                    right: StyleProp::Value(Units::Stretch(1.0)),
-                                    top: StyleProp::Value(Units::Stretch(1.0)),
-                                    bottom: StyleProp::Value(Units::Stretch(1.0)),
-                                    ..Default::default()
-                                }}
-                            />
-                            <KButtonBundle
-                                styles={KStyle {
-                                    width: StyleProp::Value(Units::Pixels(32.0)),
-                                    height: StyleProp::Value(Units::Pixels(32.0)),
-                                    left: StyleProp::Value(Units::Pixels(15.0)),
-                                    ..Default::default()
-                                }}
-                                on_event={handle_click}
-                            >
-                                <TextWidgetBundle text={TextProps {
-                                    content: "X".into(),
-                                    ..Default::default()
-                                }} />
-                            </KButtonBundle>
-                        </ElementBundle>
-                    }
-                })}
-            </ElementBundle>
-        }
-        return true;
+                            <TextWidgetBundle text={TextProps {
+                                content: "X".into(),
+                                ..Default::default()
+                            }} />
+                        </KButtonBundle>
+                    </ElementBundle>
+                }
+            })}
+        </ElementBundle>
     }
-    false
+    true
 }
diff --git a/examples/vec.rs b/examples/vec.rs
index fa25565..732cb90 100644
--- a/examples/vec.rs
+++ b/examples/vec.rs
@@ -1,10 +1,4 @@
-use bevy::{
-    prelude::{
-        App as BevyApp, AssetServer, Bundle, Changed, Commands, Component, Entity, In, Or, Query,
-        Res, ResMut, With,
-    },
-    DefaultPlugins,
-};
+use bevy::prelude::*;
 use kayak_ui::prelude::{widgets::*, KStyle, *};
 
 #[derive(Component, Default, PartialEq, Clone)]
@@ -84,7 +78,8 @@ fn startup(
 }
 
 fn main() {
-    BevyApp::new()
+    App::new()
+        .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
         .add_plugins(DefaultPlugins)
         .add_plugin(KayakContextPlugin)
         .add_plugin(KayakWidgets)
diff --git a/examples/widget_template.rs b/examples/widget_template.rs
deleted file mode 100644
index 5743a9c..0000000
--- a/examples/widget_template.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-/// This is a simple widget template.
-/// It'll auto update if the props change.
-/// Don't forget to register the update system with kayak!
-use bevy::prelude::*;
-use kayak_ui::prelude::*;
-
-#[derive(Component, Default)]
-pub struct WidgetProps;
-
-impl Widget for WidgetProps {}
-
-#[derive(Bundle)]
-pub struct WidgetBundle {
-    pub widget: WidgetProps,
-    pub styles: KStyle,
-    pub children: KChildren,
-    pub widget_name: WidgetName,
-}
-
-impl Default for WidgetBundle {
-    fn default() -> Self {
-        Self {
-            widget: WidgetProps::default(),
-            styles: KStyle {
-                render_command: StyleProp::Value(RenderCommand::Clip),
-                height: StyleProp::Value(Units::Stretch(1.0)),
-                width: StyleProp::Value(Units::Stretch(1.0)),
-                ..KStyle::default()
-            },
-            children: KChildren::default(),
-            widget_name: WidgetProps::default().get_name(),
-        }
-    }
-}
-
-pub fn update_widget(
-    In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
-    _: Commands,
-    mut query: Query<
-        (&Style, &KChildren),
-        Or<(Changed<Style>, Changed<WidgetProps>, With<Mounted>)>,
-    >,
-) -> bool {
-    if let Ok((_, children)) = query.get_mut(entity) {
-        children.process(&widget_context, Some(entity));
-        return true;
-    }
-    false
-}
-
-fn main() {}
diff --git a/kayak_font/Cargo.toml b/kayak_font/Cargo.toml
index 40ce386..305c83c 100644
--- a/kayak_font/Cargo.toml
+++ b/kayak_font/Cargo.toml
@@ -15,8 +15,8 @@ unicode-segmentation = "1.9"
 # Provides UAX #14 line break segmentation
 xi-unicode = "0.3"
 
-bevy = { git = "https://github.com/bevyengine/bevy", rev="9423cb6a8d0c140e11364eb23c8feb7e576baa8c", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] }
+bevy = { git = "https://github.com/bevyengine/bevy", rev="4bcf49b2ea6fb5f42388b0e15d204020053ee5c7", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] }
 
 [dev-dependencies]
-bevy = { git = "https://github.com/bevyengine/bevy", rev="9423cb6a8d0c140e11364eb23c8feb7e576baa8c" }
+bevy = { git = "https://github.com/bevyengine/bevy", rev="4bcf49b2ea6fb5f42388b0e15d204020053ee5c7" }
 bytemuck = "1.12.0"
diff --git a/kayak_font/examples/bevy.rs b/kayak_font/examples/bevy.rs
index 7284566..5eb6a1a 100644
--- a/kayak_font/examples/bevy.rs
+++ b/kayak_font/examples/bevy.rs
@@ -2,10 +2,10 @@ use bevy::{
     math::Vec2,
     prelude::{
         App as BevyApp, AssetServer, Camera2dBundle, Commands, Component, Handle, Input, KeyCode,
-        Query, Res, ResMut, Sprite, SpriteBundle, Transform, With, Without,
+        PluginGroup, Query, Res, ResMut, Sprite, SpriteBundle, Transform, With, Without,
     },
     render::color::Color,
-    window::WindowDescriptor,
+    window::{WindowDescriptor, WindowPlugin},
     DefaultPlugins,
 };
 
@@ -119,13 +119,15 @@ fn control_text(
 
 fn main() {
     BevyApp::new()
-        .insert_resource(WindowDescriptor {
-            width: 1270.0,
-            height: 720.0,
-            title: String::from("UI Example"),
+        .add_plugins(DefaultPlugins.set(WindowPlugin {
+            window: WindowDescriptor {
+                width: 1270.0,
+                height: 720.0,
+                title: String::from("UI Example"),
+                ..Default::default()
+            },
             ..Default::default()
-        })
-        .add_plugins(DefaultPlugins)
+        }))
         .add_plugin(KayakFontPlugin)
         .add_plugin(FontRenderPlugin)
         .add_startup_system(startup)
diff --git a/kayak_ui_macros/Cargo.toml b/kayak_ui_macros/Cargo.toml
index 3b234a3..d0438b1 100644
--- a/kayak_ui_macros/Cargo.toml
+++ b/kayak_ui_macros/Cargo.toml
@@ -17,4 +17,4 @@ proc-macro-crate = "1.1"
 [dev-dependencies]
 kayak_ui = { path = "../", version = "0.1.0" }
 pretty_assertions = "1.2.1"
-bevy = { git = "https://github.com/bevyengine/bevy", rev="9423cb6a8d0c140e11364eb23c8feb7e576baa8c" }
\ No newline at end of file
+bevy = { git = "https://github.com/bevyengine/bevy", rev="4bcf49b2ea6fb5f42388b0e15d204020053ee5c7" }
\ No newline at end of file
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
index 99a1340..f04d3ab 100644
--- a/src/camera/camera.rs
+++ b/src/camera/camera.rs
@@ -1,6 +1,6 @@
 use bevy::{
     ecs::query::QueryItem,
-    prelude::{Bundle, Component, GlobalTransform, Transform, With},
+    prelude::{Bundle, Camera2d, Component, GlobalTransform, Transform, With},
     render::{
         camera::{Camera, CameraProjection, CameraRenderGraph, WindowOrigin},
         extract_component::ExtractComponent,
@@ -28,6 +28,7 @@ impl ExtractComponent for CameraUiKayak {
 #[derive(Bundle)]
 pub struct UICameraBundle {
     pub camera: Camera,
+    pub camera_2d: Camera2d,
     pub camera_render_graph: CameraRenderGraph,
     pub orthographic_projection: UIOrthographicProjection,
     pub visible_entities: VisibleEntities,
@@ -65,11 +66,12 @@ impl UICameraBundle {
                 priority: isize::MAX - 1,
                 ..Default::default()
             },
-            camera_render_graph: CameraRenderGraph::new(crate::render::draw_ui_graph::NAME),
+            camera_render_graph: CameraRenderGraph::new(bevy::core_pipeline::core_2d::graph::NAME),
             orthographic_projection,
             frustum,
             visible_entities: VisibleEntities::default(),
             transform,
+            camera_2d: Camera2d::default(),
             global_transform: Default::default(),
             marker: CameraUiKayak,
         }
diff --git a/src/input.rs b/src/input.rs
index c3eb172..3ef2709 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -14,17 +14,17 @@ use crate::{
 };
 
 pub(crate) fn process_events(world: &mut World) {
-    let window_size = if let Some(windows) = world.get_resource::<Windows>() {
-        if let Some(window) = windows.get_primary() {
-            Vec2::new(window.width(), window.height())
-        } else {
-            // log::warn!("Couldn't find primiary window!");
-            return;
-        }
-    } else {
-        // log::warn!("Couldn't find primiary window!");
-        return;
-    };
+    // let window_size = if let Some(windows) = world.get_resource::<Windows>() {
+    //     if let Some(window) = windows.get_primary() {
+    //         Vec2::new(window.width(), window.height())
+    //     } else {
+    //         // log::warn!("Couldn't find primiary window!");
+    //         return;
+    //     }
+    // } else {
+    //     // log::warn!("Couldn't find primiary window!");
+    //     return;
+    // };
 
     let mut input_events = Vec::new();
 
@@ -64,7 +64,7 @@ pub(crate) fn process_events(world: &mut World) {
                 // Currently, we can only handle a single MouseMoved event at a time so everything but the last needs to be skipped
                 input_events.push(InputEvent::MouseMoved((
                     event.position.x as f32,
-                    window_size.y - event.position.y as f32,
+                    event.position.y as f32,
                 )));
             }
 
diff --git a/src/render/mod.rs b/src/render/mod.rs
index d014cc5..40bbbe7 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -1,7 +1,7 @@
 use bevy::{
-    prelude::{Commands, Entity, Plugin, Query, With},
+    prelude::{App, Commands, Entity, Plugin, Query, With},
     render::{
-        render_graph::{RenderGraph, SlotInfo, SlotType},
+        render_graph::{RenderGraph, RunGraphOnViewNode, SlotInfo, SlotType},
         render_phase::{DrawFunctions, RenderPhase},
         Extract, RenderApp, RenderStage,
     },
@@ -46,26 +46,99 @@ impl Plugin for BevyKayakUIRenderPlugin {
             .add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases);
         // .add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<TransparentUI>);
 
-        let pass_node_ui = MainPassUINode::new(&mut render_app.world);
-        let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
+        // let pass_node_ui = MainPassUINode::new(&mut render_app.world);
+        // let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
 
-        let mut draw_ui_graph = RenderGraph::default();
-        draw_ui_graph.add_node(draw_ui_graph::node::MAIN_PASS, pass_node_ui);
-        let input_node_id = draw_ui_graph.set_input(vec![SlotInfo::new(
-            draw_ui_graph::input::VIEW_ENTITY,
-            SlotType::Entity,
-        )]);
-        draw_ui_graph
-            .add_slot_edge(
-                input_node_id,
-                draw_ui_graph::input::VIEW_ENTITY,
+        // let mut draw_ui_graph = RenderGraph::default();
+        // draw_ui_graph.add_node(draw_ui_graph::node::MAIN_PASS, pass_node_ui);
+        // let input_node_id = draw_ui_graph.set_input(vec![SlotInfo::new(
+        //     draw_ui_graph::input::VIEW_ENTITY,
+        //     SlotType::Entity,
+        // )]);
+        // draw_ui_graph
+        //     .add_slot_edge(
+        //         input_node_id,
+        //         draw_ui_graph::input::VIEW_ENTITY,
+        //         draw_ui_graph::node::MAIN_PASS,
+        //         MainPassUINode::IN_VIEW,
+        //     )
+        //     .unwrap();
+        // graph.add_sub_graph(draw_ui_graph::NAME, draw_ui_graph);
+
+        // // graph.add_node_edge(MAIN_PASS, draw_ui_graph::NAME).unwrap();
+
+        // Render graph
+        let ui_graph_2d = get_ui_graph(render_app);
+        let ui_graph_3d = get_ui_graph(render_app);
+        let mut graph = render_app.world.resource_mut::<RenderGraph>();
+
+        if let Some(graph_2d) = graph.get_sub_graph_mut(bevy::core_pipeline::core_2d::graph::NAME) {
+            graph_2d.add_sub_graph(draw_ui_graph::NAME, ui_graph_2d);
+            graph_2d.add_node(
                 draw_ui_graph::node::MAIN_PASS,
-                MainPassUINode::IN_VIEW,
-            )
-            .unwrap();
-        graph.add_sub_graph(draw_ui_graph::NAME, draw_ui_graph);
+                RunGraphOnViewNode::new(draw_ui_graph::NAME),
+            );
+            graph_2d
+                .add_node_edge(
+                    bevy::core_pipeline::core_2d::graph::node::MAIN_PASS,
+                    draw_ui_graph::node::MAIN_PASS,
+                )
+                .unwrap();
+            graph_2d
+                .add_slot_edge(
+                    graph_2d.input_node().unwrap().id,
+                    bevy::core_pipeline::core_2d::graph::input::VIEW_ENTITY,
+                    draw_ui_graph::node::MAIN_PASS,
+                    RunGraphOnViewNode::IN_VIEW,
+                )
+                .unwrap();
+            graph_2d
+                .add_node_edge(
+                    bevy::core_pipeline::core_2d::graph::node::TONEMAPPING,
+                    draw_ui_graph::node::MAIN_PASS,
+                )
+                .unwrap();
+            graph_2d
+                .add_node_edge(
+                    draw_ui_graph::node::MAIN_PASS,
+                    bevy::core_pipeline::core_2d::graph::node::UPSCALING,
+                )
+                .unwrap();
+        }
 
-        // graph.add_node_edge(MAIN_PASS, draw_ui_graph::NAME).unwrap();
+        if let Some(graph_3d) = graph.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME) {
+            graph_3d.add_sub_graph(draw_ui_graph::NAME, ui_graph_3d);
+            graph_3d.add_node(
+                draw_ui_graph::node::MAIN_PASS,
+                RunGraphOnViewNode::new(draw_ui_graph::NAME),
+            );
+            graph_3d
+                .add_node_edge(
+                    bevy::core_pipeline::core_3d::graph::node::MAIN_PASS,
+                    draw_ui_graph::node::MAIN_PASS,
+                )
+                .unwrap();
+            graph_3d
+                .add_node_edge(
+                    bevy::core_pipeline::core_3d::graph::node::TONEMAPPING,
+                    draw_ui_graph::node::MAIN_PASS,
+                )
+                .unwrap();
+            graph_3d
+                .add_node_edge(
+                    draw_ui_graph::node::MAIN_PASS,
+                    bevy::core_pipeline::core_3d::graph::node::UPSCALING,
+                )
+                .unwrap();
+            graph_3d
+                .add_slot_edge(
+                    graph_3d.input_node().unwrap().id,
+                    bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY,
+                    draw_ui_graph::node::MAIN_PASS,
+                    RunGraphOnViewNode::IN_VIEW,
+                )
+                .unwrap();
+        }
 
         app.add_plugin(font::TextRendererPlugin)
             .add_plugin(UnifiedRenderPlugin)
@@ -73,6 +146,25 @@ impl Plugin for BevyKayakUIRenderPlugin {
     }
 }
 
+fn get_ui_graph(render_app: &mut App) -> RenderGraph {
+    let ui_pass_node = MainPassUINode::new(&mut render_app.world);
+    let mut ui_graph = RenderGraph::default();
+    ui_graph.add_node(draw_ui_graph::node::MAIN_PASS, ui_pass_node);
+    let input_node_id = ui_graph.set_input(vec![SlotInfo::new(
+        draw_ui_graph::input::VIEW_ENTITY,
+        SlotType::Entity,
+    )]);
+    ui_graph
+        .add_slot_edge(
+            input_node_id,
+            draw_ui_graph::input::VIEW_ENTITY,
+            draw_ui_graph::node::MAIN_PASS,
+            MainPassUINode::IN_VIEW,
+        )
+        .unwrap();
+    ui_graph
+}
+
 pub fn extract_core_pipeline_camera_phases(
     mut commands: Commands,
     active_camera: Extract<Query<Entity, With<CameraUiKayak>>>,
diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs
index bc9ff4f..5595367 100644
--- a/src/render/ui_pass.rs
+++ b/src/render/ui_pass.rs
@@ -1,6 +1,6 @@
 use bevy::ecs::prelude::*;
 use bevy::render::render_phase::{DrawFunctionId, PhaseItem};
-use bevy::render::render_resource::{CachedRenderPipelineId, RenderPassColorAttachment};
+use bevy::render::render_resource::CachedRenderPipelineId;
 use bevy::render::{
     render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
     render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
@@ -72,14 +72,10 @@ impl Node for MainPassUINode {
         {
             let pass_descriptor = RenderPassDescriptor {
                 label: Some("main_transparent_pass_UI"),
-                color_attachments: &[Some(RenderPassColorAttachment {
-                    view: &target.view,
-                    resolve_target: None,
-                    ops: Operations {
-                        load: LoadOp::Load, //Clear(clear_color.0.into()),
-                        store: true,
-                    },
-                })],
+                color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
+                    load: LoadOp::Load,
+                    store: true,
+                }))],
                 depth_stencil_attachment: None,
             };
 
diff --git a/src/render/unified/mod.rs b/src/render/unified/mod.rs
index e62e670..b592ed3 100644
--- a/src/render/unified/mod.rs
+++ b/src/render/unified/mod.rs
@@ -2,7 +2,9 @@ use bevy::{
     prelude::{Assets, Commands, HandleUntyped, Plugin, Res, Resource},
     reflect::TypeUuid,
     render::{
-        render_phase::DrawFunctions, render_resource::Shader, Extract, RenderApp, RenderStage,
+        render_phase::DrawFunctions,
+        render_resource::{Shader, SpecializedRenderPipelines},
+        Extract, RenderApp, RenderStage,
     },
     window::Windows,
 };
@@ -37,6 +39,7 @@ impl Plugin for UnifiedRenderPlugin {
         render_app
             .init_resource::<ImageBindGroups>()
             .init_resource::<UnifiedPipeline>()
+            .init_resource::<SpecializedRenderPipelines<UnifiedPipeline>>()
             .init_resource::<QuadMeta>()
             .add_system_to_stage(RenderStage::Extract, extract_baseline)
             .add_system_to_stage(RenderStage::Prepare, pipeline::prepare_quads)
diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs
index 570c3e7..235d1c4 100644
--- a/src/render/unified/pipeline.rs
+++ b/src/render/unified/pipeline.rs
@@ -1,5 +1,7 @@
-use bevy::prelude::{Rect, Resource};
-use bevy::render::render_resource::{DynamicUniformBuffer, ShaderType};
+use bevy::prelude::{Msaa, Rect, Resource};
+use bevy::render::render_resource::{
+    DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
+};
 use bevy::utils::FloatOrd;
 use bevy::{
     ecs::system::{
@@ -16,13 +18,12 @@ use bevy::{
             BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
             BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType,
             BlendComponent, BlendFactor, BlendOperation, BlendState, BufferBindingType, BufferSize,
-            BufferUsages, BufferVec, CachedRenderPipelineId, ColorTargetState, ColorWrites,
-            Extent3d, FragmentState, FrontFace, MultisampleState, PipelineCache, PolygonMode,
-            PrimitiveState, PrimitiveTopology, RenderPipelineDescriptor, SamplerBindingType,
-            SamplerDescriptor, Shader, ShaderStages, TextureDescriptor, TextureDimension,
-            TextureFormat, TextureSampleType, TextureUsages, TextureViewDescriptor,
-            TextureViewDimension, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState,
-            VertexStepMode,
+            BufferUsages, BufferVec, ColorTargetState, ColorWrites, Extent3d, FragmentState,
+            FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState,
+            PrimitiveTopology, RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor,
+            Shader, ShaderStages, TextureDescriptor, TextureDimension, TextureFormat,
+            TextureSampleType, TextureUsages, TextureViewDescriptor, TextureViewDimension,
+            VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
         },
         renderer::{RenderDevice, RenderQueue},
         texture::{BevyDefault, GpuImage, Image},
@@ -47,7 +48,6 @@ pub struct UnifiedPipeline {
     types_layout: BindGroupLayout,
     pub(crate) font_image_layout: BindGroupLayout,
     image_layout: BindGroupLayout,
-    pipeline: CachedRenderPipelineId,
     empty_font_texture: (GpuImage, BindGroup),
     default_image: (GpuImage, BindGroup),
 }
@@ -67,11 +67,33 @@ impl FontRenderingPipeline for UnifiedPipeline {
     }
 }
 
+bitflags::bitflags! {
+    #[repr(transparent)]
+    pub struct UnifiedPipelineKey: u32 {
+        const NONE                        = 0;
+        const MSAA_RESERVED_BITS          = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
+    }
+}
+
+impl UnifiedPipelineKey {
+    const MSAA_MASK_BITS: u32 = 0b111;
+    const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
+
+    pub fn from_msaa_samples(msaa_samples: u32) -> Self {
+        let msaa_bits =
+            (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
+        Self::from_bits(msaa_bits).unwrap()
+    }
+
+    pub fn msaa_samples(&self) -> u32 {
+        1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
+    }
+}
+
 impl FromWorld for UnifiedPipeline {
     fn from_world(world: &mut World) -> Self {
         let world = world.cell();
         let render_device = world.get_resource::<RenderDevice>().unwrap();
-        let mut pipeline_cache = world.get_resource_mut::<PipelineCache>().unwrap();
 
         let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
             entries: &[BindGroupLayoutEntry {
@@ -151,6 +173,76 @@ impl FromWorld for UnifiedPipeline {
             label: Some("image_layout"),
         });
 
+        let empty_font_texture = FontTextureCache::get_empty(&render_device, &font_image_layout);
+
+        let texture_descriptor = TextureDescriptor {
+            label: Some("font_texture_array"),
+            size: Extent3d {
+                width: 1,
+                height: 1,
+                depth_or_array_layers: 1,
+            },
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: TextureDimension::D2,
+            format: TextureFormat::Rgba8UnormSrgb,
+            usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
+        };
+
+        let sampler_descriptor = SamplerDescriptor::default();
+
+        let texture = render_device.create_texture(&texture_descriptor);
+        let sampler = render_device.create_sampler(&sampler_descriptor);
+
+        let texture_view = texture.create_view(&TextureViewDescriptor {
+            label: Some("font_texture_array_view"),
+            format: Some(TextureFormat::Rgba8UnormSrgb),
+            dimension: Some(TextureViewDimension::D2),
+            aspect: bevy::render::render_resource::TextureAspect::All,
+            base_mip_level: 0,
+            base_array_layer: 0,
+            mip_level_count: None,
+            array_layer_count: None,
+        });
+
+        let image = GpuImage {
+            texture,
+            sampler,
+            texture_view,
+            size: Vec2::new(1.0, 1.0),
+            texture_format: TextureFormat::Rgba8UnormSrgb,
+        };
+
+        let binding = render_device.create_bind_group(&BindGroupDescriptor {
+            label: Some("text_image_bind_group"),
+            entries: &[
+                BindGroupEntry {
+                    binding: 0,
+                    resource: BindingResource::TextureView(&image.texture_view),
+                },
+                BindGroupEntry {
+                    binding: 1,
+                    resource: BindingResource::Sampler(&image.sampler),
+                },
+            ],
+            layout: &image_layout,
+        });
+
+        UnifiedPipeline {
+            view_layout,
+            font_image_layout,
+            empty_font_texture,
+            types_layout,
+            image_layout,
+            default_image: (image, binding),
+        }
+    }
+}
+
+impl SpecializedRenderPipeline for UnifiedPipeline {
+    type Key = UnifiedPipelineKey;
+
+    fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor {
         let vertex_buffer_layout = VertexBufferLayout {
             array_stride: 60,
             step_mode: VertexStepMode::Vertex,
@@ -178,9 +270,7 @@ impl FromWorld for UnifiedPipeline {
             ],
         };
 
-        let empty_font_texture = FontTextureCache::get_empty(&render_device, &font_image_layout);
-
-        let pipeline_desc = RenderPipelineDescriptor {
+        RenderPipelineDescriptor {
             vertex: VertexState {
                 shader: UNIFIED_SHADER_HANDLE.typed::<Shader>(),
                 entry_point: "vertex".into(),
@@ -209,10 +299,10 @@ impl FromWorld for UnifiedPipeline {
                 })],
             }),
             layout: Some(vec![
-                view_layout.clone(),
-                font_image_layout.clone(),
-                types_layout.clone(),
-                image_layout.clone(),
+                self.view_layout.clone(),
+                self.font_image_layout.clone(),
+                self.types_layout.clone(),
+                self.image_layout.clone(),
             ]),
             primitive: PrimitiveState {
                 front_face: FrontFace::Ccw,
@@ -230,69 +320,6 @@ impl FromWorld for UnifiedPipeline {
                 alpha_to_coverage_enabled: false,
             },
             label: Some("unified_pipeline".into()),
-        };
-
-        let texture_descriptor = TextureDescriptor {
-            label: Some("font_texture_array"),
-            size: Extent3d {
-                width: 1,
-                height: 1,
-                depth_or_array_layers: 1,
-            },
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: TextureDimension::D2,
-            format: TextureFormat::Rgba8UnormSrgb,
-            usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
-        };
-
-        let sampler_descriptor = SamplerDescriptor::default();
-
-        let texture = render_device.create_texture(&texture_descriptor);
-        let sampler = render_device.create_sampler(&sampler_descriptor);
-
-        let texture_view = texture.create_view(&TextureViewDescriptor {
-            label: Some("font_texture_array_view"),
-            format: Some(TextureFormat::Rgba8UnormSrgb),
-            dimension: Some(TextureViewDimension::D2),
-            aspect: bevy::render::render_resource::TextureAspect::All,
-            base_mip_level: 0,
-            base_array_layer: 0,
-            mip_level_count: None,
-            array_layer_count: None,
-        });
-
-        let image = GpuImage {
-            texture,
-            sampler,
-            texture_view,
-            size: Vec2::new(1.0, 1.0),
-            texture_format: TextureFormat::Rgba8UnormSrgb,
-        };
-
-        let binding = render_device.create_bind_group(&BindGroupDescriptor {
-            label: Some("text_image_bind_group"),
-            entries: &[
-                BindGroupEntry {
-                    binding: 0,
-                    resource: BindingResource::TextureView(&image.texture_view),
-                },
-                BindGroupEntry {
-                    binding: 1,
-                    resource: BindingResource::Sampler(&image.sampler),
-                },
-            ],
-            layout: &image_layout,
-        });
-
-        UnifiedPipeline {
-            pipeline: pipeline_cache.queue_render_pipeline(pipeline_desc),
-            view_layout,
-            font_image_layout,
-            empty_font_texture,
-            types_layout,
-            image_layout,
-            default_image: (image, binding),
         }
     }
 }
@@ -495,11 +522,14 @@ pub fn queue_quads(
     mut sprite_meta: ResMut<QuadMeta>,
     view_uniforms: Res<ViewUniforms>,
     quad_pipeline: Res<UnifiedPipeline>,
+    mut pipelines: ResMut<SpecializedRenderPipelines<UnifiedPipeline>>,
+    mut pipeline_cache: ResMut<PipelineCache>,
     mut extracted_sprites: Query<(Entity, &ExtractedQuad)>,
     mut views: Query<&mut RenderPhase<TransparentUI>>,
     mut image_bind_groups: ResMut<ImageBindGroups>,
     unified_pipeline: Res<UnifiedPipeline>,
     gpu_images: Res<RenderAssets<Image>>,
+    msaa: Res<Msaa>,
 ) {
     if let Some(type_binding) = sprite_meta.types_buffer.binding() {
         sprite_meta.types_bind_group =
@@ -523,6 +553,9 @@ pub fn queue_quads(
             layout: &quad_pipeline.view_layout,
         }));
 
+        let key = UnifiedPipelineKey::from_msaa_samples(msaa.samples);
+        let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key);
+
         let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
         for mut transparent_phase in views.iter_mut() {
             for (entity, quad) in extracted_sprites.iter_mut() {
@@ -553,7 +586,7 @@ pub fn queue_quads(
                 }
                 transparent_phase.add(TransparentUI {
                     draw_function: draw_quad,
-                    pipeline: quad_pipeline.pipeline,
+                    pipeline: spec_pipeline,
                     entity,
                     sort_key: FloatOrd(quad.z_index),
                 });
diff --git a/src/styles/style.rs b/src/styles/style.rs
index f02b567..dc8bb05 100644
--- a/src/styles/style.rs
+++ b/src/styles/style.rs
@@ -5,7 +5,7 @@ use std::ops::Add;
 use bevy::prelude::Color;
 use bevy::prelude::Component;
 use bevy::window::CursorIcon;
-pub use morphorm::{LayoutType, PositionType, Units};
+pub use morphorm::{LayoutType, PositionType as KPositionType, Units};
 
 use crate::cursor::PointerEvents;
 
@@ -348,7 +348,7 @@ define_styles! {
         /// the event to "pass through" to widgets below.
         pub pointer_events: StyleProp<PointerEvents>,
         /// The position type of the widget relative to its parent
-        pub position_type: StyleProp<PositionType>,
+        pub position_type: StyleProp<KPositionType>,
         /// The render method for this widget
         ///
         /// This controls what actually gets rendered and how it's rendered.
diff --git a/src/widgets/image.rs b/src/widgets/image.rs
index 2e8d934..375d52d 100644
--- a/src/widgets/image.rs
+++ b/src/widgets/image.rs
@@ -1,7 +1,7 @@
-use bevy::prelude::{Bundle, Changed, Component, Entity, Handle, In, Or, Query, With};
+use bevy::prelude::{Bundle, Component, Entity, Handle, In, Query};
 
 use crate::{
-    context::{Mounted, WidgetName},
+    context::WidgetName,
     prelude::KayakWidgetContext,
     styles::{KStyle, RenderCommand, StyleProp},
     widget::Widget,
@@ -15,13 +15,13 @@ pub struct Image(pub Handle<bevy::prelude::Image>);
 impl Widget for Image {}
 
 #[derive(Bundle)]
-pub struct ImageBundle {
+pub struct KImageBundle {
     pub image: Image,
     pub style: KStyle,
     pub widget_name: WidgetName,
 }
 
-impl Default for ImageBundle {
+impl Default for KImageBundle {
     fn default() -> Self {
         Self {
             image: Default::default(),
@@ -33,13 +33,12 @@ impl Default for ImageBundle {
 
 pub fn image_render(
     In((_widget_context, entity)): In<(KayakWidgetContext, Entity)>,
-    mut query: Query<(&mut KStyle, &Image), Or<((Changed<Image>, Changed<KStyle>), With<Mounted>)>>,
+    mut query: Query<(&mut KStyle, &Image)>,
 ) -> bool {
     if let Ok((mut style, image)) = query.get_mut(entity) {
         style.render_command = StyleProp::Value(RenderCommand::Image {
             handle: image.0.clone_weak(),
         });
-        return true;
     }
-    false
+    true
 }
diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs
index 8ff1680..0197591 100644
--- a/src/widgets/mod.rs
+++ b/src/widgets/mod.rs
@@ -40,7 +40,7 @@ pub use background::{Background, BackgroundBundle};
 pub use button::{KButton, KButtonBundle};
 pub use clip::{Clip, ClipBundle};
 pub use element::{Element, ElementBundle};
-pub use image::{Image, ImageBundle};
+pub use image::{Image, KImageBundle};
 pub use nine_patch::{NinePatch, NinePatchBundle};
 pub use scroll::{
     scroll_bar::{ScrollBarBundle, ScrollBarProps},
@@ -52,7 +52,7 @@ pub use scroll::{
 };
 pub use text::{TextProps, TextWidgetBundle};
 pub use text_box::{TextBoxBundle, TextBoxProps, TextBoxState};
-pub use texture_atlas::{TextureAtlas, TextureAtlasBundle};
+pub use texture_atlas::{TextureAtlasBundle, TextureAtlasProps};
 pub use window::{KWindow, WindowBundle};
 
 use app::{app_render, app_update};
@@ -94,7 +94,7 @@ fn add_widget_systems(mut context: ResMut<KayakRootContext>) {
     context.add_widget_data::<Background, EmptyState>();
     context.add_widget_data::<Clip, EmptyState>();
     context.add_widget_data::<Image, EmptyState>();
-    context.add_widget_data::<TextureAtlas, EmptyState>();
+    context.add_widget_data::<TextureAtlasProps, EmptyState>();
     context.add_widget_data::<NinePatch, EmptyState>();
     context.add_widget_data::<Element, EmptyState>();
     context.add_widget_data::<ScrollBarProps, EmptyState>();
@@ -135,8 +135,8 @@ fn add_widget_systems(mut context: ResMut<KayakRootContext>) {
         image_render,
     );
     context.add_widget_system(
-        TextureAtlas::default().get_name(),
-        widget_update::<TextureAtlas, EmptyState>,
+        TextureAtlasProps::default().get_name(),
+        widget_update::<TextureAtlasProps, EmptyState>,
         texture_atlas_render,
     );
     context.add_widget_system(
diff --git a/src/widgets/nine_patch.rs b/src/widgets/nine_patch.rs
index 68d7d05..bd3014a 100644
--- a/src/widgets/nine_patch.rs
+++ b/src/widgets/nine_patch.rs
@@ -78,8 +78,7 @@ pub fn nine_patch_render(
         });
 
         children.process(&widget_context, Some(entity));
-
-        return true;
     }
-    false
+
+    true
 }
diff --git a/src/widgets/scroll/scroll_bar.rs b/src/widgets/scroll/scroll_bar.rs
index 81ef57f..f56c754 100644
--- a/src/widgets/scroll/scroll_bar.rs
+++ b/src/widgets/scroll/scroll_bar.rs
@@ -7,7 +7,7 @@ use crate::{
     event_dispatcher::EventDispatcherContext,
     on_event::OnEvent,
     prelude::{KChildren, KayakWidgetContext},
-    styles::{Corner, Edge, KStyle, PositionType, RenderCommand, Units},
+    styles::{Corner, Edge, KPositionType, KStyle, RenderCommand, Units},
     widget::Widget,
     widget_state::WidgetState,
     widgets::{BackgroundBundle, ClipBundle},
@@ -156,7 +156,7 @@ pub fn scroll_bar_render(
 
                 let mut thumb_style = KStyle::default()
                     .with_style(KStyle {
-                        position_type: PositionType::SelfDirected.into(),
+                        position_type: KPositionType::SelfDirected.into(),
                         ..Default::default()
                     })
                     .with_style(&thumb_styles)
diff --git a/src/widgets/scroll/scroll_box.rs b/src/widgets/scroll/scroll_box.rs
index dba219b..b294e4a 100644
--- a/src/widgets/scroll/scroll_box.rs
+++ b/src/widgets/scroll/scroll_box.rs
@@ -10,7 +10,7 @@ use crate::{
     on_event::OnEvent,
     on_layout::OnLayout,
     prelude::{constructor, rsx, KayakWidgetContext},
-    styles::{KStyle, LayoutType, PositionType, RenderCommand, Units},
+    styles::{KPositionType, KStyle, LayoutType, RenderCommand, Units},
     widget::Widget,
     widget_state::WidgetState,
     widgets::{
@@ -165,7 +165,7 @@ pub fn scroll_box_render(
                 });
 
                 let content_styles = KStyle::default().with_style(KStyle {
-                    position_type: PositionType::SelfDirected.into(),
+                    position_type: KPositionType::SelfDirected.into(),
                     top: Units::Pixels(scroll_y).into(),
                     left: Units::Pixels(scroll_x).into(),
                     ..Default::default()
diff --git a/src/widgets/texture_atlas.rs b/src/widgets/texture_atlas.rs
index 8e410c0..eab78bf 100644
--- a/src/widgets/texture_atlas.rs
+++ b/src/widgets/texture_atlas.rs
@@ -1,7 +1,7 @@
-use bevy::prelude::{Bundle, Changed, Component, Entity, Handle, Image, In, Or, Query, Vec2, With};
+use bevy::prelude::{Bundle, Component, Entity, Handle, Image, In, Query, Vec2};
 
 use crate::{
-    context::{Mounted, WidgetName},
+    context::WidgetName,
     prelude::KayakWidgetContext,
     styles::{KStyle, RenderCommand, StyleProp},
     widget::Widget,
@@ -23,7 +23,7 @@ use crate::{
 /// | `focusable` | ✅        |
 ///
 #[derive(Component, PartialEq, Clone, Default, Debug)]
-pub struct TextureAtlas {
+pub struct TextureAtlasProps {
     /// The handle to image
     pub handle: Handle<Image>,
     /// The position of the tile (in pixels)
@@ -32,12 +32,12 @@ pub struct TextureAtlas {
     pub tile_size: Vec2,
 }
 
-impl Widget for TextureAtlas {}
+impl Widget for TextureAtlasProps {}
 
 /// A widget that renders a bevy texture atlas
 #[derive(Bundle)]
 pub struct TextureAtlasBundle {
-    pub atlas: TextureAtlas,
+    pub atlas: TextureAtlasProps,
     pub styles: KStyle,
     pub widget_name: WidgetName,
 }
@@ -47,17 +47,14 @@ impl Default for TextureAtlasBundle {
         Self {
             atlas: Default::default(),
             styles: Default::default(),
-            widget_name: TextureAtlas::default().get_name(),
+            widget_name: TextureAtlasProps::default().get_name(),
         }
     }
 }
 
 pub fn texture_atlas_render(
     In((_widget_context, entity)): In<(KayakWidgetContext, Entity)>,
-    mut query: Query<
-        (&mut KStyle, &TextureAtlas),
-        Or<(Changed<TextureAtlas>, Changed<KStyle>, With<Mounted>)>,
-    >,
+    mut query: Query<(&mut KStyle, &TextureAtlasProps)>,
 ) -> bool {
     if let Ok((mut styles, texture_atlas)) = query.get_mut(entity) {
         *styles = KStyle {
@@ -68,9 +65,7 @@ pub fn texture_atlas_render(
             }),
             ..styles.clone()
         };
-
-        return true;
     }
 
-    false
+    true
 }
diff --git a/src/widgets/window.rs b/src/widgets/window.rs
index 1a6dc82..5fa45fd 100644
--- a/src/widgets/window.rs
+++ b/src/widgets/window.rs
@@ -11,7 +11,7 @@ use crate::{
     event_dispatcher::EventDispatcherContext,
     on_event::OnEvent,
     prelude::KayakWidgetContext,
-    styles::{Corner, Edge, KCursorIcon, KStyle, PositionType, RenderCommand, StyleProp, Units},
+    styles::{Corner, Edge, KCursorIcon, KPositionType, KStyle, RenderCommand, StyleProp, Units},
     widget::Widget,
     widget_state::WidgetState,
 };
@@ -95,7 +95,7 @@ pub fn window_render(
                         border: StyleProp::Value(Edge::all(4.0)),
                         border_radius: StyleProp::Value(Corner::all(5.0)),
                         render_command: StyleProp::Value(RenderCommand::Quad),
-                        position_type: StyleProp::Value(PositionType::SelfDirected),
+                        position_type: StyleProp::Value(KPositionType::SelfDirected),
                         left: StyleProp::Value(Units::Pixels(state.position.x)),
                         top: StyleProp::Value(Units::Pixels(state.position.y)),
                         width: StyleProp::Value(Units::Pixels(window.size.x)),
-- 
GitLab