Pārlūkot izejas kodu

Rearchitecture using tiny_skia, softbuffer, and winit.

Kestrel 5 mēneši atpakaļ
vecāks
revīzija
09f4dee1be

+ 18 - 3
Cargo.toml

@@ -1,3 +1,18 @@
-[workspace]
-resolver = "2"
-members = ["patina", "patina-sdl", "patina-unaccel"]
+[package]
+name = "patina"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+log = "0.4"
+jumprope = "1.1"
+
+# for window initialization
+winit = { version = "0.30.0", default-features = false, features = ["x11", "wayland", "rwh_06"] }
+# for rendering
+softbuffer = "0.4.2"
+tiny-skia = "0.11"
+
+[dev-dependencies]

+ 67 - 0
examples/simplest.rs

@@ -0,0 +1,67 @@
+use patina::{
+    prelude::*,
+    window::{Window, WindowComponent},
+};
+
+struct SimpleWindow {
+    widget: <Self as WindowComponent>::RootWidget,
+}
+
+impl Component for SimpleWindow {
+    type ParentMsg = ();
+    type Msg = ();
+
+    fn process(&mut self, msg: Self::Msg) -> Vec<Self::ParentMsg> {
+        vec![()]
+    }
+}
+
+impl WindowComponent for SimpleWindow {
+    fn map_window_event(&self, we: patina::window::WindowEvent) -> Option<Self::Msg> {
+        Some(())
+    }
+
+    type RootWidget = patina::widget::Spacer<Self>;
+    fn root_widget(&self) -> &Self::RootWidget {
+        &self.widget
+    }
+    fn root_widget_mut(&mut self) -> &mut Self::RootWidget {
+        &mut self.widget
+    }
+}
+
+struct Simplest {
+    main_window: Option<Window<SimpleWindow>>,
+}
+
+impl Component for Simplest {
+    type ParentMsg = patina::UIControlMsg;
+    type Msg = ();
+
+    fn process(&mut self, msg: Self::Msg) -> Vec<Self::ParentMsg> {
+        vec![patina::UIControlMsg::Terminate]
+    }
+}
+
+impl patina::UIComponent for Simplest {
+    fn init<'l>(&mut self, mut ui_handle: patina::UIHandle<'_>) {
+        let mut widget = patina::widget::Spacer::new(&ui_handle);
+        widget.layout_node_mut().set_width_policy(patina::layout::SizePolicy { minimum: 0, desired: 0, slack_weight: 1 });
+        widget.layout_node_mut().set_height_policy(patina::layout::SizePolicy { minimum: 0, desired: 0, slack_weight: 1 });
+
+        self.main_window = Some(ui_handle.window_builder().build(|uih| SimpleWindow {
+            widget
+        }));
+    }
+    fn poll(&mut self) -> Vec<patina::UIControlMsg> {
+        let Some(mw) = self.main_window.as_mut() else { return vec![] };
+        let evts = mw.poll();
+        self.process_all(evts.into_iter())
+    }
+}
+
+fn main() {
+    let ui = patina::UI::<Simplest>::new(Simplest { main_window: None });
+
+    ui.run();
+}

+ 0 - 11
patina-sdl/Cargo.toml

@@ -1,11 +0,0 @@
-[package]
-name = "patina-sdl"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-patina = { path = "../patina/" }
-patina-unaccel = { path = "../patina-unaccel" }
-sdl2 = { version = "0.36" }

+ 0 - 47
patina-sdl/src/convert.rs

@@ -1,47 +0,0 @@
-use patina::geom::*;
-use patina::platform::render as pr;
-
-pub trait ToSDL {
-    type SDLEquivalent;
-    fn to_sdl(self) -> Self::SDLEquivalent;
-}
-
-impl ToSDL for pr::Colour {
-    type SDLEquivalent = sdl2::pixels::Color;
-    fn to_sdl(self) -> Self::SDLEquivalent {
-        sdl2::pixels::Color::RGBA(self.r(), self.g(), self.b(), self.a())
-    }
-}
-
-impl ToSDL for IRect {
-    type SDLEquivalent = sdl2::rect::Rect;
-    fn to_sdl(self) -> Self::SDLEquivalent {
-        sdl2::rect::Rect::new(
-            self.base().x,
-            self.base().y,
-            self.size().x as u32,
-            self.size().y as u32,
-        )
-    }
-}
-
-pub trait FromSDL {
-    type PatinaEquivalent;
-    fn from_sdl(self) -> Self::PatinaEquivalent;
-}
-
-impl FromSDL for sdl2::rect::Rect {
-    type PatinaEquivalent = IRect;
-    fn from_sdl(self) -> Self::PatinaEquivalent {
-        IRect::new_from_size(
-            IPoint {
-                x: self.x,
-                y: self.y,
-            },
-            IVector {
-                x: self.w,
-                y: self.h,
-            },
-        )
-    }
-}

+ 0 - 150
patina-sdl/src/lib.rs

@@ -1,150 +0,0 @@
-use patina::platform;
-use patina_unaccel::{UnaccelSurface, UnaccelText};
-use patina::platform::render::Surface;
-
-mod convert;
-// mod render;
-
-mod prelude {
-    pub use crate::convert::{FromSDL, ToSDL};
-}
-
-pub struct SDLInitFlags {
-    pub window_width: usize,
-    pub window_height: usize,
-    pub window_resizable: bool,
-}
-
-impl Default for SDLInitFlags {
-    fn default() -> Self {
-        Self {
-            window_width: 800,
-            window_height: 600,
-            window_resizable: false,
-        }
-    }
-}
-
-pub struct SDLRenderInterface {
-    sdl: sdl2::Sdl,
-    video: sdl2::VideoSubsystem,
-    // window: sdl2::video::Window,
-    canvas: sdl2::render::WindowCanvas,
-    // canvas: render::WindowSurface,
-    surf: UnaccelSurface,
-}
-
-impl SDLRenderInterface {
-    pub fn new(sdlif: SDLInitFlags) -> (Self, SDLEventInterface) {
-        let sdl = sdl2::init().unwrap();
-        let evt = sdl.event_pump().unwrap();
-        let video = sdl.video().unwrap();
-
-        let window = video
-            .window(
-                "title",
-                sdlif.window_width as u32,
-                sdlif.window_height as u32,
-            )
-            .position_centered()
-            .build()
-            .unwrap();
-
-        let canvas = window.into_canvas().present_vsync().build().unwrap();
-
-        (
-            Self {
-                sdl,
-                video,
-                canvas,
-                surf: UnaccelSurface::new(sdlif.window_width, sdlif.window_height),
-                // canvas: canvas.into(),
-            },
-            SDLEventInterface { evt, next: None },
-        )
-    }
-}
-
-impl platform::RenderInterface for SDLRenderInterface {
-    type Surface = UnaccelSurface;
-
-    fn render_target_mut(&mut self) -> &mut Self::Surface {
-        &mut self.surf
-    }
-
-    fn submit(&mut self) {
-        let txc = self.canvas.texture_creator();
-        let mut tex = txc.create_texture_target(sdl2::pixels::PixelFormatEnum::RGBA32, self.surf.size().x as u32, self.surf.size().y as u32).unwrap();
-        tex.update(None, self.surf.raw_argb32_data(), self.surf.size().x as usize * 4).unwrap();
-        self.canvas.copy(&tex, None, None).unwrap();
-        self.canvas.present();
-    }
-}
-
-pub struct SDLEventInterface {
-    evt: sdl2::EventPump,
-    next: Option<sdl2::event::Event>,
-}
-
-impl SDLEventInterface {
-    fn translate(&self, sdlevent: sdl2::event::Event) -> Option<platform::event::WindowEvent> {
-        match sdlevent {
-            sdl2::event::Event::Quit { .. } => Some(platform::event::WindowEvent::CloseRequest),
-            _ => None,
-        }
-    }
-
-    fn poll_internal(&mut self) -> Option<sdl2::event::Event> {
-        self.next.take().or_else(|| self.evt.poll_event())
-    }
-}
-
-impl platform::EventInterface for SDLEventInterface {
-    fn poll_event(&mut self) -> Option<platform::event::WindowEvent> {
-        while let Some(evt) = self.poll_internal() {
-            if let Some(wevt) = self.translate(evt) {
-                return Some(wevt);
-            }
-        }
-        None
-    }
-    fn wait_for_event(&mut self) {
-        if self.next.is_some() {
-            return;
-        }
-        self.next = Some(self.evt.wait_event());
-    }
-}
-
-pub type SDLPlatformSpec = (SDLRenderInterface, SDLEventInterface, UnaccelText);
-pub fn init_sdl_platform(sdlif: SDLInitFlags) -> SDLPlatformSpec {
-    let (sdl, event) = SDLRenderInterface::new(sdlif);
-    let text = UnaccelText::new();
-    (sdl, event, text)
-}
-
-/*
-pub struct SDLTextInterface {
-    ctx: sdl2::ttf::Sdl2TtfContext,
-}
-
-impl platform::TextInterface for SDLTextInterface {
-    type Surface = render::SDLSurface;
-
-    fn render_text(&self, what: &str, _sz: f32) -> Box<dyn patina::platform::render::Surface + '_> {
-        let font = self.ctx.load_font("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16).unwrap();
-        let render = font.render(what);
-        let rendered = render.solid(sdl2::pixels::Color::RGB(255, 255, 255)).unwrap();
-        Box::new(render::SDLSurface { surface: rendered })
-    }
-}
-
-pub fn init_sdl_platform(
-    sdlif: SDLInitFlags,
-) -> (SDLRenderInterface, SDLEventInterface, SDLTextInterface) {
-    let (sdl, event) = SDLRenderInterface::new(sdlif);
-    let ttf = SDLTextInterface { ctx: sdl2::ttf::init().unwrap() };
-    (sdl, event, ttf)
-}
-
-*/

+ 0 - 97
patina-sdl/src/render.rs

@@ -1,97 +0,0 @@
-use crate::prelude::*;
-
-use patina::geom::{IPoint, IRect, IVector};
-use patina::platform::render as pr;
-
-pub enum SDLSurface {
-    Window(sdl2::render::WindowCanvas),
-    Surface(()),
-}
-
-impl pr::Surface for SDLSurface {
-    fn size(&self) -> IVector {
-        let sz = match self {
-            Self::Window(canvas) => {
-                canvas.logical_size()
-            }
-            Self::Surface(surf) => {
-                surf.size()
-            },
-        };
-        IVector::new(sz.0 as i32, sz.1 as i32)
-    }
-
-    fn painter(&mut self) -> Option<Self::Painter<'_>> {
-        None
-    }
-
-    type Painter<'p> = SDLPainter<'p> where Self: 'p;
-}
-
-pub enum SDLPainter<'l> {
-    Window(sdl2::render::WindowCanvas),
-    Surface(sdl2::surface::Surface<'l>),
-}
-
-impl<'p> pr::Painter for SDLPainter<'p> {
-    type Surface<'s> = SDLSurface<'s> where Self: 's;
-
-    fn blit(&mut self, source: &Self::Surface<'p>, source_area: IRect, target: IPoint) { }
-    fn fill(&mut self, area: IRect, col: pr::Colour) { }
-    fn outline_rect(&mut self, rect: IRect, col: pr::Colour) {}
-}
-
-/*
-pub struct WindowSurface {
-    canvas: sdl2::render::WindowCanvas,
-}
-
-impl From<sdl2::render::WindowCanvas> for WindowSurface {
-    fn from(canvas: sdl2::render::WindowCanvas) -> Self {
-        Self { canvas }
-    }
-}
-
-impl<'l> pr::Surface<'l> for WindowSurface {
-    type Painter = CanvasPainter
-    fn size(&self) -> IVector {
-        let sz = self.canvas.window().size();
-        IVector {
-            x: sz.0 as i32,
-            y: sz.1 as i32,
-        }
-    }
-
-    fn painter(&mut self) -> Option<Box<dyn pr::Painter + '_>> {
-        Some(Box::new(CanvasPainter {
-            canvas: &mut self.canvas,
-            _ghost: Default::default(),
-        }))
-    }
-}
-*/
-
-/*
-pub struct CanvasPainter<'l> {
-    canvas: &'l mut sdl2::render::WindowCanvas,
-    _ghost: std::marker::PhantomData<(&'l (),)>,
-}
-
-impl<'l> pr::Painter for CanvasPainter<'l> {
-    fn blit(&mut self, source: &dyn pr::Surface, source_area: IRect, target: IPoint) {
-        // self.canvas.copy
-        // self.canvas.copy(source)
-    }
-    fn fill(&mut self, area: IRect, col: pr::Colour) {
-        self.canvas.set_draw_color(col.to_sdl());
-        self.canvas.fill_rect(area.to_sdl()).expect("couldn't draw");
-    }
-    fn outline_rect(&mut self, rect: IRect, col: pr::Colour) {}
-}
-
-impl<'l> Drop for CanvasPainter<'l> {
-    fn drop(&mut self) {
-        self.canvas.present()
-    }
-}
-*/

+ 0 - 13
patina-unaccel/Cargo.toml

@@ -1,13 +0,0 @@
-[package]
-name = "patina-unaccel"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-patina = { path = "../patina/" }
-schrift-rs = { version = "0.1.0" }
-
-[dev-dependencies]
-ductr = "0.0.1"

+ 0 - 23
patina-unaccel/render_output.pgm

@@ -1,23 +0,0 @@
-P2
-279 20
-255
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 29 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 29 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 38 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 22 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 126 241 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 31 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 187 179 0 0 0 0 0 0 31 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 31 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 187 179 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 126 241 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 139 229 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 126 241 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 187 179 0 0 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 187 179 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 83 160 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 139 229 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 126 241 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 187 179 0 0 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 187 179 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 139 229 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 123 152 0 0 0 0 143 131 0 0 105 172 28 168 225 164 60 0 0 0 0 42 158 216 233 163 75 5 0 0 0 0 24 156 212 236 207 113 0 0 0 24 156 212 236 207 113 0 0 0 24 158 214 229 180 46 0 0 0 126 241 0 0 0 24 158 214 229 180 46 0 0 105 172 31 169 225 135 0 0 42 158 216 233 163 75 5 0 109 224 251 191 191 170 0 0 0 24 158 214 229 180 46 0 0 0 0 60 191 231 185 61 187 179 0 0 0 0 0 109 224 251 191 191 170 0 0 0 24 158 214 229 180 46 0 11 175 139 0 0 0 38 190 96 109 224 251 191 191 170 0 0 0 0 0 0 105 172 31 169 225 135 0 0 0 24 158 214 229 180 46 0 0 105 172 28 168 225 164 60 0 0 0 0 0 60 191 231 185 61 187 179 0 0 0 24 158 214 229 180 46 0 0 105 172 31 169 225 135 0 0 94 181 0 105 172 28 168 225 164 60 0 0 0 0 0 20 129 224 184 61 140 134 0 0 0 0 0 0 0 42 158 216 233 163 75 5 0 0 139 229 28 168 225 164 60 0 0 0 0 0 44 177 226 217 158 22 0 71 191 29 0 0 0 0 130 161 0
- 0 163 203 0 0 0 0 191 175 0 0 139 240 214 167 101 194 255 40 0 0 0 91 159 95 81 147 237 144 0 0 0 31 237 243 154 79 112 157 0 0 31 237 243 154 79 112 157 0 0 29 235 218 128 102 204 248 43 0 0 126 241 0 0 29 235 218 128 102 204 248 43 0 139 241 220 181 96 71 0 0 91 159 95 81 147 237 144 0 53 177 244 94 94 83 0 0 29 235 218 128 102 204 248 43 0 0 54 249 224 106 150 236 225 179 0 0 0 0 0 53 177 244 94 94 83 0 0 29 235 218 128 102 204 248 43 0 82 254 96 0 8 207 209 9 53 177 244 94 94 83 0 0 0 0 0 0 139 241 220 181 96 71 0 0 29 235 218 128 102 204 248 43 0 139 240 214 167 101 194 255 40 0 0 0 54 249 224 106 150 236 225 179 0 0 29 235 218 128 102 204 248 43 0 139 241 220 181 96 71 0 0 126 241 0 139 240 214 167 101 194 255 40 0 0 0 47 244 218 104 145 233 225 179 0 0 0 0 0 0 0 91 159 95 81 147 237 144 0 0 139 240 214 167 101 194 255 40 0 0 0 49 246 232 117 142 246 227 15 14 243 127 0 0 0 17 246 125 0
- 0 163 203 0 0 0 0 191 175 0 0 139 255 106 0 0 7 245 112 0 0 0 0 0 0 0 0 112 223 0 0 0 171 251 25 0 0 0 0 0 0 171 251 25 0 0 0 0 0 0 166 243 16 0 0 3 231 172 0 0 126 241 0 0 166 243 16 0 0 3 231 172 0 139 255 116 0 0 0 0 0 0 0 0 0 0 112 223 0 0 131 237 0 0 0 0 0 166 243 16 0 0 3 231 172 0 0 184 235 6 0 0 80 255 179 0 0 0 0 0 0 131 237 0 0 0 0 0 166 243 16 0 0 3 231 172 0 0 146 244 42 150 243 40 0 0 131 237 0 0 0 0 0 0 0 0 0 139 255 116 0 0 0 0 0 166 243 16 0 0 3 231 172 0 139 255 106 0 0 7 245 112 0 0 0 184 235 6 0 0 80 255 179 0 0 166 243 16 0 0 3 231 172 0 139 255 116 0 0 0 0 0 126 241 0 139 255 106 0 0 7 245 112 0 0 0 129 230 3 0 0 72 255 179 0 0 0 0 0 0 0 0 0 0 0 0 112 223 0 0 139 255 106 0 0 7 245 112 0 0 0 183 243 12 0 0 55 255 131 0 153 226 3 0 0 110 251 26 0
- 0 163 203 0 0 0 0 191 175 0 0 139 252 24 0 0 0 187 181 0 0 0 0 1 43 107 133 150 255 49 0 0 240 191 0 0 0 0 0 0 0 240 191 0 0 0 0 0 0 0 238 171 38 39 39 39 167 224 0 0 126 241 0 0 238 171 38 39 39 39 167 224 0 139 255 39 0 0 0 0 0 0 1 43 107 133 150 255 49 0 131 237 0 0 0 0 0 238 171 38 39 39 39 167 224 0 1 245 169 0 0 0 13 250 179 0 0 0 0 0 0 131 237 0 0 0 0 0 238 171 38 39 39 39 167 224 0 0 7 204 230 255 93 0 0 0 131 237 0 0 0 0 0 0 0 0 0 139 255 39 0 0 0 0 0 238 171 38 39 39 39 167 224 0 139 252 24 0 0 0 187 181 0 0 1 245 169 0 0 0 13 250 179 0 0 238 171 38 39 39 39 167 224 0 139 255 39 0 0 0 0 0 126 241 0 139 252 24 0 0 0 187 181 0 0 0 206 163 0 0 0 9 247 179 0 0 0 0 0 0 0 0 1 43 107 133 150 255 49 0 139 252 24 0 0 0 187 181 0 0 1 244 180 0 0 0 1 233 193 0 48 255 76 0 0 211 175 0 0
- 0 163 203 0 0 0 0 191 175 0 0 139 229 0 0 0 0 167 199 0 0 0 44 232 237 195 158 162 255 90 0 19 255 123 0 0 0 0 0 0 19 255 123 0 0 0 0 0 0 18 255 250 247 247 247 247 247 244 0 0 126 241 0 18 255 250 247 247 247 247 247 244 0 139 235 0 0 0 0 0 0 44 232 237 195 158 162 255 90 0 131 237 0 0 0 0 18 255 250 247 247 247 247 247 244 0 21 255 112 0 0 0 0 205 179 0 0 0 0 0 0 131 237 0 0 0 0 18 255 250 247 247 247 247 247 244 0 0 0 84 255 205 0 0 0 0 131 237 0 0 0 0 0 0 0 0 0 139 235 0 0 0 0 0 18 255 250 247 247 247 247 247 244 0 139 229 0 0 0 0 167 199 0 0 21 255 112 0 0 0 0 205 179 0 18 255 250 247 247 247 247 247 244 0 139 235 0 0 0 0 0 0 126 241 0 139 229 0 0 0 0 167 199 0 0 12 254 111 0 0 0 0 204 179 0 0 0 0 0 0 0 44 232 237 195 158 162 255 90 0 139 229 0 0 0 0 167 199 0 0 20 255 120 0 0 0 0 174 224 0 0 198 178 0 58 255 72 0 0
- 0 156 212 0 0 0 0 218 175 0 0 139 229 0 0 0 0 167 199 0 0 0 157 209 1 0 0 69 255 90 0 3 248 170 0 0 0 0 0 0 3 248 170 0 0 0 0 0 0 4 248 151 0 0 0 0 0 0 0 0 126 241 0 4 248 151 0 0 0 0 0 0 0 139 229 0 0 0 0 0 0 157 209 1 0 0 69 255 90 0 131 237 0 0 0 0 4 248 151 0 0 0 0 0 0 0 4 251 152 0 0 0 4 241 179 0 0 0 0 0 0 131 237 0 0 0 0 4 248 151 0 0 0 0 0 0 0 0 17 223 219 255 90 0 0 0 131 237 0 0 0 0 0 0 0 0 0 139 229 0 0 0 0 0 4 248 151 0 0 0 0 0 0 0 139 229 0 0 0 0 167 199 0 0 4 251 152 0 0 0 4 241 179 0 4 248 151 0 0 0 0 0 0 0 139 229 0 0 0 0 0 0 126 241 0 139 229 0 0 0 0 167 199 0 0 0 209 160 0 0 0 8 246 179 0 0 0 0 0 0 0 157 209 1 0 0 69 255 90 0 139 229 0 0 0 0 167 199 0 0 4 250 161 0 0 0 0 216 202 0 0 93 252 28 160 223 2 0 0
- 0 94 253 17 0 0 46 255 175 0 0 139 229 0 0 0 0 167 199 0 0 1 237 131 0 0 0 151 255 90 0 0 199 242 7 0 0 0 0 0 0 199 242 7 0 0 0 0 0 0 199 241 11 0 0 0 0 0 0 0 126 241 0 0 199 241 11 0 0 0 0 0 0 139 229 0 0 0 0 0 1 237 131 0 0 0 151 255 90 0 104 250 8 0 0 0 0 199 241 11 0 0 0 0 0 0 0 209 218 0 0 0 58 255 179 0 0 0 0 0 0 104 250 8 0 0 0 0 199 241 11 0 0 0 0 0 0 1 174 232 25 154 242 37 0 0 104 250 8 0 0 0 0 0 0 0 0 139 229 0 0 0 0 0 0 199 241 11 0 0 0 0 0 0 139 229 0 0 0 0 167 199 0 0 0 209 218 0 0 0 58 255 179 0 0 199 241 11 0 0 0 0 0 0 139 229 0 0 0 0 0 0 126 241 0 139 229 0 0 0 0 167 199 0 0 0 132 228 1 0 0 68 255 179 0 0 0 0 0 0 1 237 131 0 0 0 151 255 90 0 139 229 0 0 0 0 167 199 0 0 0 208 230 1 0 0 30 255 156 0 0 8 235 143 246 122 0 0 0
- 0 23 253 158 39 85 200 241 175 0 0 139 229 0 0 0 0 167 199 0 0 0 160 223 59 27 132 229 255 90 0 0 63 254 193 86 12 44 102 0 0 63 254 193 86 12 44 102 0 0 58 253 187 88 13 30 89 113 0 0 126 241 0 0 58 253 187 88 13 30 89 113 0 139 229 0 0 0 0 0 0 160 223 59 27 132 229 255 90 0 33 255 131 57 46 0 0 58 253 187 88 13 30 89 113 0 0 91 255 163 39 82 212 243 179 0 0 0 0 0 0 33 255 131 57 46 0 0 58 253 187 88 13 30 89 113 0 109 253 70 0 9 210 205 7 0 33 255 131 57 46 0 0 0 0 0 0 139 229 0 0 0 0 0 0 58 253 187 88 13 30 89 113 0 139 229 0 0 0 0 167 199 0 0 0 91 255 163 39 82 212 243 179 0 0 58 253 187 88 13 30 89 113 0 139 229 0 0 0 0 0 0 126 241 0 139 229 0 0 0 0 167 199 0 0 0 50 248 210 94 135 231 228 179 0 0 0 0 0 0 0 160 223 59 27 132 229 255 90 0 139 229 0 0 0 0 167 199 0 0 0 84 255 175 49 74 203 250 37 0 0 0 139 254 250 25 0 0 0
- 0 0 105 215 255 243 100 191 175 0 0 139 229 0 0 0 0 167 199 0 0 0 44 188 252 255 217 62 255 90 0 0 0 75 226 255 255 253 164 0 0 0 75 226 255 255 253 164 0 0 0 67 215 255 255 255 232 110 0 0 126 241 0 0 0 67 215 255 255 255 232 110 0 139 229 0 0 0 0 0 0 44 188 252 255 217 62 255 90 0 0 117 192 249 227 0 0 0 67 215 255 255 255 232 110 0 0 1 122 250 255 246 117 187 179 0 0 0 0 0 0 0 117 192 249 227 0 0 0 67 215 255 255 255 232 110 50 248 134 0 0 0 41 244 147 0 0 117 192 249 227 0 0 0 0 0 0 139 229 0 0 0 0 0 0 0 67 215 255 255 255 232 110 0 139 229 0 0 0 0 167 199 0 0 0 1 122 250 255 246 117 187 179 0 0 0 67 215 255 255 255 232 110 0 139 229 0 0 0 0 0 0 126 241 0 139 229 0 0 0 0 167 199 0 0 0 0 26 139 234 194 68 215 160 0 0 0 0 0 0 0 44 188 252 255 217 62 255 90 0 139 229 0 0 0 0 167 199 0 0 0 0 102 242 255 255 225 66 0 0 0 0 36 255 172 0 0 0 0
- 0 0 0 1 38 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 25 0 0 0 0 0 0 0 0 0 27 48 19 0 0 0 0 0 0 27 48 19 0 0 0 0 0 0 20 50 29 2 0 0 0 0 0 0 0 0 0 0 20 50 29 2 0 0 0 0 0 0 0 0 0 0 0 0 25 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 50 29 2 0 0 0 0 0 9 43 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 50 29 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 50 29 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 43 6 0 0 0 0 0 0 0 0 20 50 29 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 39 255 127 0 0 0 0 0 0 0 0 0 25 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 39 30 0 0 0 0 0 0 65 255 69 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 77 3 1 58 177 249 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 199 190 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 249 248 235 255 237 89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 164 245 244 46 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 59 77 51 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 61 70 9 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

+ 0 - 224
patina-unaccel/src/lib.rs

@@ -1,224 +0,0 @@
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use patina::prelude::*;
-use patina::geom::*;
-use patina::platform as pp;
-
-/// ABGR32 surface (assumes little-endian CPU)
-///
-/// Colourmasks:
-/// - 0x000000ff: alpha
-/// - 0x0000ff00: blue
-/// - 0x00ff0000: green
-/// - 0xff000000: red
-pub struct UnaccelSurface {
-    data: Vec<u32>,
-    width: usize,
-    height: usize,
-}
-
-impl UnaccelSurface {
-    pub fn new(width: usize, height: usize) -> Self {
-        let mut data = vec![];
-        data.resize(width * height, 0);
-        Self {
-            width,
-            height,
-            data,
-        }
-    }
-
-    pub fn get_pixel(&self, x: usize, y: usize) -> u32 {
-        self.data[x + y * self.width]
-    }
-    pub fn set_pixel(&mut self, x: usize, y: usize, val: u32) {
-        self.data[x + y * self.width] = val;
-    }
-
-    pub fn raw_argb32_data(&self) -> &[u8] {
-        let buf = self.data.as_slice().as_ptr();
-        let buflen = self.data.len() * 4;
-        unsafe { std::slice::from_raw_parts(buf as *const u8, buflen) }
-    }
-}
-
-impl pp::render::Surface for UnaccelSurface {
-    fn size(&self) -> IVector {
-        IVector::new(self.width as i32, self.height as i32)
-    }
-
-    type Painter<'l> = UnaccelPainter<'l>;
-    fn painter(&mut self) -> Option<Self::Painter<'_>> {
-        Some(UnaccelPainter { surf: self })
-    }
-}
-
-pub struct UnaccelPainter<'l> {
-    surf: &'l mut UnaccelSurface,
-}
-
-impl<'l> pp::render::Painter for UnaccelPainter<'l> {
-    type Surface = UnaccelSurface;
-
-    fn blit(&mut self, source: &Self::Surface, source_area: IRect, target: IPoint) {
-        for y in 0..source_area.height() {
-            for x in 0..source_area.width() {
-                let data = source.get_pixel(
-                    (source_area.left() + x) as usize,
-                    (source_area.top() + y) as usize,
-                );
-                let tx = x + target.x;
-                let ty = y + target.y;
-                if tx < 0 || ty < 0 || tx >= self.surf.width as i32 || ty >= self.surf.height as i32
-                {
-                    continue;
-                } else {
-                    self.surf.set_pixel(tx as usize, ty as usize, data);
-                }
-            }
-        }
-    }
-
-    fn fill(&mut self, area: IRect, col: pp::render::Colour) {
-        let rgba = col.to_rgba();
-        for yoff in 0..area.height() {
-            for xoff in 0..area.width() {
-                self.surf.set_pixel(
-                    (xoff + area.left()) as usize,
-                    (yoff + area.top()) as usize,
-                    rgba,
-                );
-            }
-        }
-    }
-    fn outline_rect(&mut self, rect: IRect, col: pp::render::Colour) {
-        todo!()
-    }
-}
-
-// text rendering via cosmic-text
-pub struct UnaccelText {
-    font: Rc<schrift_rs::Font>,
-    cached: RefCell<schrift_rs::CachedSchrift>,
-}
-
-impl UnaccelText {
-    pub fn new() -> Self {
-        let font =
-            schrift_rs::Font::load_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf")
-                .unwrap();
-        let cached = schrift_rs::CachedSchrift::new(
-            schrift_rs::Schrift::build(font.clone())
-                .with_scale(16.0)
-                .flag_y_downwards()
-                .build(),
-        );
-
-        Self {
-            font,
-            cached: cached.into(),
-        }
-    }
-}
-
-impl pp::TextInterface for UnaccelText {
-    type Surface = UnaccelSurface;
-
-    fn render_text<'l>(&'l self, what: &pp::text::TextBuffer, sz: f32) -> Self::Surface {
-        let line = what.line_iter().next().unwrap();
-
-        let mut sch = self.cached.borrow_mut();
-
-        let lm = sch.line_metrics();
-        let ascender_offset = lm.ascender;
-        let descender_offset = lm.descender;
-
-        // two passes: first, compute the needed width
-        let mut cumulative_width = 0;
-        // let mut max_height = 0;
-        let glyphs = line
-            .chars()
-            .map(|v| sch.glyph_lookup(v))
-            .collect::<Vec<_>>();
-        for glyph in glyphs.iter() {
-            let gm = sch.glyph_metrics(*glyph);
-            cumulative_width += gm.advance.round() as usize;
-        }
-        // add one pixel of padding
-        cumulative_width += 1;
-        // use ascender and descender sizes for image height, with one pixel of padding
-        let height = (ascender_offset.abs().round() + descender_offset.abs().round() + 1.0) as usize;
-
-        let mut surf = UnaccelSurface::new(cumulative_width, height);
-
-        // second pass: copy the glyph images over
-        let mut cursor = 0;
-        let mut last_glyph = None;
-        for glyph in glyphs.iter() {
-            let kerning_offset = match last_glyph.take() {
-                Some(lglyph) => {
-                    let gk = sch.glyph_kerning(lglyph, *glyph);
-                    (gk.x_shift, gk.y_shift)
-                }
-                None => {
-                    (0., 0.)
-                }
-            };
-            let (left_bearing, advance, y_offset) = {
-                let gm = sch.glyph_metrics(*glyph);
-                (gm.left_bearing, gm.advance, gm.y_offset + (kerning_offset.1 + ascender_offset).round() as i32)
-            };
-            let img = sch.glyph_image(*glyph);
-            let x_offset = (left_bearing + kerning_offset.0).round() as i32;
-            for x in 0..img.1 {
-                for y in 0..img.2 {
-                    let intensity = img.0[(y * img.1 + x) as usize] as u32;
-                    surf.set_pixel(
-                        ((cursor + x).checked_add_signed(x_offset).unwrap()) as usize,
-                        (y as i32 + y_offset) as usize,
-                        // 0xffffff00 | intensity,
-                        intensity
-                        // 0x00ffffff | (intensity << 24)
-                    );
-                }
-            }
-            cursor += advance.round() as u32;
-            last_glyph = Some(*glyph);
-        }
-
-        surf
-    }
-}
-
-#[cfg(test)]
-#[test]
-fn test_unaccel_text() {
-    use pp::render::Surface;
-    use pp::TextInterface;
-    let ut = UnaccelText::new();
-
-    let tb = pp::text::TextBuffer::new("unaccelerated text rendering ahoy");
-
-    let surf = ut.render_text(&tb, 16.0);
-    println!("{:?}", surf.size());
-
-    let mut pgm = String::new();
-    pgm.push_str("P2\n");
-    pgm.push_str(format!("{} {}\n", surf.size().x, surf.size().y).as_str());
-    pgm.push_str("255\n");
-    for y in 0..surf.size().y {
-        for x in 0..surf.size().x {
-            pgm.push_str(format!(" {}", surf.get_pixel(x as usize, y as usize) & 0xff).as_str());
-        }
-        pgm.push('\n');
-    }
-
-    std::fs::write("render_output.pgm", pgm).unwrap();
-
-    // surf
-
-    // ductr::AnymapImage::ppm(data, 255, height, width);
-
-    // let rendered = write_as_ascii("unaccelerated.ppm").unwrap();
-}

+ 0 - 78
patina/Cargo.lock

@@ -1,78 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "libc"
-version = "0.2.154"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
-
-[[package]]
-name = "log"
-version = "0.4.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
-
-[[package]]
-name = "patina"
-version = "0.1.0"
-dependencies = [
- "log",
- "patina-sdl",
-]
-
-[[package]]
-name = "patina-sdl"
-version = "0.1.0"
-dependencies = [
- "patina",
- "sdl2",
-]
-
-[[package]]
-name = "sdl2"
-version = "0.36.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
-dependencies = [
- "bitflags",
- "lazy_static",
- "libc",
- "sdl2-sys",
-]
-
-[[package]]
-name = "sdl2-sys"
-version = "0.36.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26bcacfdd45d539fb5785049feb0038a63931aa896c7763a2a12e125ec58bd29"
-dependencies = [
- "cfg-if",
- "libc",
- "version-compare",
-]
-
-[[package]]
-name = "version-compare"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"

+ 0 - 13
patina/Cargo.toml

@@ -1,13 +0,0 @@
-[package]
-name = "patina"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-log = "0.4"
-jumprope = "1.1"
-
-[dev-dependencies]
-patina-sdl = { path = "../patina-sdl/" }

+ 0 - 83
patina/examples/sdl_plain.rs

@@ -1,83 +0,0 @@
-// use patina::prelude::*;
-
-use patina::{layout::SizePolicy, widget::Widget};
-
-use patina_sdl::SDLPlatformSpec as PS;
-
-struct SimpleRoot {
-    frame: patina::widget::Frame<PS, Self>,
-}
-
-#[derive(Debug)]
-enum SimpleRootMsg {
-    CloseRequest,
-}
-
-impl patina::component::Component for SimpleRoot {
-    type ParentMsg = patina::UIControlMsg;
-    type Msg = SimpleRootMsg;
-
-    fn process(&mut self, msg: Self::Msg) -> Vec<Self::ParentMsg> {
-        match msg {
-            SimpleRootMsg::CloseRequest => vec![patina::UIControlMsg::Terminate],
-        }
-    }
-}
-
-impl patina::UIComponent<PS> for SimpleRoot {
-    fn build(bc: &patina::widget::BuildContext) -> Self {
-        let mut frame = patina::widget::Frame::<PS, Self>::new(bc);
-        frame
-            .layout_node_mut()
-            .set_width_policy(SizePolicy {
-                minimum: 100,
-                desired: 200,
-                slack_weight: 1,
-            })
-            .set_height_policy(SizePolicy {
-                minimum: 100,
-                desired: 200,
-                slack_weight: 1,
-            })
-            .set_label("frame");
-
-        let mut label = patina::widget::Label::<PS, Self>::new(bc);
-        label
-            .layout_node_mut()
-            .set_height_policy(SizePolicy {
-                minimum: 20,
-                desired: 20,
-                slack_weight: 0,
-            })
-            .set_width_policy(SizePolicy {
-                minimum: 10,
-                desired: 100,
-                slack_weight: 1,
-            })
-            .set_label("label");
-        frame.set_child(Box::new(label));
-
-        Self { frame }
-    }
-
-    fn map_ui_event(&self, uievent: patina::platform::event::UIEvent) -> Option<Self::Msg> {
-        match uievent {
-            patina::platform::event::UIEvent::CloseRequest => Some(SimpleRootMsg::CloseRequest),
-            _ => None,
-        }
-    }
-    type RootWidget = patina::widget::Frame<PS, Self>;
-    fn root_widget(&self) -> &Self::RootWidget {
-        &self.frame
-    }
-    fn root_widget_mut(&mut self) -> &mut Self::RootWidget {
-        &mut self.frame
-    }
-}
-
-fn main() {
-    let ui = patina::UI::<_, SimpleRoot>::new(patina_sdl::init_sdl_platform(
-        patina_sdl::SDLInitFlags::default(),
-    ));
-    ui.run();
-}

+ 0 - 148
patina/src/lib.rs

@@ -1,148 +0,0 @@
-use layout::LayoutNodeAccess;
-use platform::{
-    render::{Painter, Surface},
-    EventInterface, RenderInterface,
-};
-
-pub mod component;
-pub mod geom;
-pub mod input;
-pub mod layout;
-pub mod platform;
-pub mod widget;
-
-pub mod prelude {
-    pub use crate::component::Component;
-    pub use crate::widget::Widget;
-}
-use prelude::*;
-
-pub enum UIControlMsg {
-    Terminate,
-}
-
-pub trait UIComponent<P: platform::PlatformSpec>:
-    Sized + component::Component<ParentMsg = UIControlMsg>
-{
-    fn build(bc: &widget::BuildContext) -> Self;
-
-    fn map_ui_event(&self, uievent: platform::event::UIEvent) -> Option<Self::Msg>;
-
-    type RootWidget: widget::Widget<P, Self>;
-    fn root_widget(&self) -> &Self::RootWidget;
-    fn root_widget_mut(&mut self) -> &mut Self::RootWidget;
-}
-
-pub struct UI<P: platform::PlatformSpec, UIC: UIComponent<P>> {
-    layout_cache: std::rc::Rc<layout::LayoutCache>,
-    root_layout_node: layout::LayoutNode,
-    platform: P,
-    ui_component: UIC,
-    input_state: input::InputState,
-    ui_running: bool,
-}
-
-impl<P: platform::PlatformSpec, UIC: UIComponent<P>> UI<P, UIC> {
-    pub fn new(pt: P) -> Self {
-        let lcache = layout::LayoutCache::new();
-        let bc = widget::BuildContext {
-            lcache: lcache.clone(),
-        };
-        Self {
-            root_layout_node: layout::LayoutNode::new(lcache.clone()),
-            layout_cache: lcache,
-            platform: pt,
-            ui_component: UIC::build(&bc),
-            input_state: Default::default(),
-            ui_running: true,
-        }
-    }
-
-    pub fn is_ui_running(&self) -> bool {
-        self.ui_running
-    }
-
-    pub fn ui_component(&self) -> &UIC {
-        &self.ui_component
-    }
-    pub fn ui_component_mut(&mut self) -> &mut UIC {
-        &mut self.ui_component
-    }
-
-    /// Public helper function, delivers a message to the UI component and then processes whatever
-    /// control messages result.
-    pub fn deliver_msg(&mut self, msg: UIC::Msg) {
-        let cmsgs = self.ui_component.process(msg);
-        self.process_control_msgs(cmsgs.into_iter());
-    }
-
-    fn process_control_msgs(&mut self, msgs: impl Iterator<Item = UIControlMsg>) {
-        for cmsg in msgs {
-            match cmsg {
-                UIControlMsg::Terminate => {
-                    self.ui_running = false;
-                }
-            }
-        }
-    }
-
-    pub fn update(&mut self) {
-        let mut platform_events = vec![];
-        while let Some(evt) = self.platform.event_mut().poll_event() {
-            match evt {
-                platform::event::WindowEvent::CloseRequest => {
-                    platform_events.extend(
-                        self.ui_component()
-                            .map_ui_event(platform::event::UIEvent::CloseRequest)
-                            .into_iter(),
-                    );
-                }
-                _ => {}
-            }
-        }
-
-        // poll for state changes due to user input
-        let poll_msgs = self
-            .ui_component
-            .root_widget_mut()
-            .poll(Some(&self.input_state))
-            .into_iter();
-        let cmsgs = self
-            .ui_component
-            .process_all(platform_events.into_iter().chain(poll_msgs));
-        self.process_control_msgs(cmsgs.into_iter());
-
-        let tgt_size = self.platform.render_mut().render_target_mut().size();
-        self.root_layout_node
-            .set_behaviour(layout::NodeBehaviour::Fixed {
-                rect: geom::IRect::new_from_size(geom::IPoint::ORIGIN, tgt_size),
-            });
-
-        // first, make sure we can paint to the render target
-        let (ri, ti) = self.platform.render_text_mut();
-        if let Some(mut painter) = ri.render_target_mut().painter() {
-            // perform layout as needed
-            layout::recalculate(LayoutNodeAccess::new(&layout::LinearAccess::new(
-                &self.root_layout_node,
-                self.ui_component.root_widget().layout_node(),
-            )));
-
-            // now render as needed
-            self.ui_component.root_widget().render(&mut painter, ti);
-        } else {
-            log::warn!("Could not draw UI: no painter available");
-        }
-        ri.submit();
-    }
-
-    pub fn wait(&mut self) {
-        self.platform.event_mut().wait_for_event();
-    }
-
-    pub fn run(mut self) {
-        while self.ui_running {
-            self.update();
-            self.wait();
-        }
-    }
-}

+ 0 - 30
patina/src/widget.rs

@@ -1,30 +0,0 @@
-use crate::{
-    component::Component,
-    input::InputState,
-    layout::{LayoutCache, LayoutNode, LayoutNodeAccess},
-    platform::{
-        render::{Painter, Surface},
-        PlatformPainter, PlatformSpec, RenderInterface, TextInterface,
-    },
-};
-
-mod button;
-mod frame;
-mod group;
-mod label;
-
-pub use frame::Frame;
-pub use group::PlainGroup;
-pub use label::Label;
-
-pub trait Widget<P: PlatformSpec, C: Component> {
-    fn poll(&mut self, input_state: Option<&InputState>) -> Vec<C::Msg>;
-
-    fn layout_node(&self) -> LayoutNodeAccess;
-    fn layout_node_mut(&mut self) -> &mut LayoutNode;
-    fn render(&self, painter: &mut PlatformPainter<'_, P>, ti: &P::Text);
-}
-
-pub struct BuildContext {
-    pub(crate) lcache: std::rc::Rc<LayoutCache>,
-}

+ 0 - 0
patina/src/component.rs → src/component.rs


+ 0 - 0
patina/src/geom.rs → src/geom.rs


+ 3 - 8
patina/src/input.rs → src/input.rs

@@ -1,7 +1,6 @@
-use crate::geom::{IPoint, IVector};
-
 mod button;
 pub use button::ButtonSet;
+use tiny_skia::Point;
 
 #[derive(Clone, Copy, Debug)]
 #[repr(u8)]
@@ -25,17 +24,13 @@ impl Into<u8> for MouseButton {
 
 #[derive(Default)]
 pub struct MouseState {
-    pub pos: IPoint,
-    pub last_pos: IPoint,
+    pub pos: Point,
+    pub last_pos: Point,
     pub buttons: ButtonSet<MouseButton>,
     pub last_buttons: ButtonSet<MouseButton>,
 }
 
 impl MouseState {
-    pub fn shift(&self) -> IVector {
-        self.pos - self.last_pos
-    }
-
     pub fn pressed(&self) -> ButtonSet<MouseButton> {
         self.buttons & !self.last_buttons
     }

+ 0 - 0
patina/src/input/button.rs → src/input/button.rs


+ 3 - 4
patina/src/layout.rs → src/layout.rs

@@ -1,6 +1,5 @@
 use std::{ops::Deref, rc::Rc};
 
-use crate::geom::IRect;
 use cache::{Layer, LayoutCacheKey, NodeState};
 
 mod arr;
@@ -78,10 +77,10 @@ impl BoxMeasure {
 }
 
 /// What sort of layout does a node represent?
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Debug)]
 pub enum NodeBehaviour {
     /// A fixed rendering area, probably a root node.
-    Fixed { rect: IRect },
+    Fixed { rect: tiny_skia::IntRect },
     /// An ordinary box sitting inside another node, hinting its size and receiving a final
     /// size assignment from its parent.
     Element,
@@ -169,7 +168,7 @@ impl LayoutNode {
         self.cache.update_queue.borrow_mut().push(self.cache_key);
     }
 
-    pub fn cached_rect(&self) -> Option<IRect> {
+    pub fn cached_rect(&self) -> Option<tiny_skia::IntRect> {
         self.cache
             .with_state(self.cache_key, |ns| ns.rect)
             .flatten()

+ 15 - 29
patina/src/layout/arr.rs → src/layout/arr.rs

@@ -1,6 +1,6 @@
 use std::ops::Add;
 
-use crate::geom::{IPoint, IRect, IVector};
+use tiny_skia::IntRect;
 
 use super::{cache::NodeState, LayoutNodeAccess, SizePolicy};
 
@@ -10,7 +10,7 @@ pub trait ArrangementCalculator {
         node: LayoutNodeAccess,
         child_policies: Vec<(SizePolicy, SizePolicy)>,
     ) -> (SizePolicy, SizePolicy);
-    fn layout_step(&self, node: LayoutNodeAccess, inside: IRect);
+    fn layout_step(&self, node: LayoutNodeAccess, inside: IntRect);
 }
 
 #[derive(Clone, Debug)]
@@ -62,7 +62,7 @@ impl ArrangementCalculator for LineArrangement {
         )
     }
 
-    fn layout_step(&self, node: LayoutNodeAccess, inside: IRect) {
+    fn layout_step(&self, node: LayoutNodeAccess, inside: IntRect) {
         // do the final children layouts
         node.cache
             .with_state(node.cache_key, |ns| ns.rect = Some(inside));
@@ -82,38 +82,24 @@ impl ArrangementCalculator for LineArrangement {
             .child_iter()
             .map(|c| node.cache.with_state(c.cache_key, ee).unwrap());
         let mut last_offset = 0;
-        for (offset, child) in do_fit(
+        let fit = do_fit(
             if self.is_column() {
-                inside.height()
+                inside.height() as i32
             } else {
-                inside.width()
+                inside.width() as i32
             },
             policies,
-        )
-        .zip(node.child_iter())
-        {
+        );
+        for (offset, child) in fit.zip(node.child_iter()) {
             let crect = if self.is_column() {
-                IRect::new_from_size(
-                    IPoint {
-                        x: inside.base().x,
-                        y: inside.tl().y + last_offset,
-                    },
-                    IVector {
-                        x: inside.width(),
-                        y: (offset - last_offset),
-                    },
-                )
+                IntRect::from_xywh(inside.left(), inside.top() + last_offset, inside.width(), (offset - last_offset) as u32).unwrap()
             } else {
-                IRect::new_from_size(
-                    IPoint {
-                        x: inside.tl().x + last_offset,
-                        y: inside.base().y,
-                    },
-                    IVector {
-                        x: (offset - last_offset),
-                        y: inside.height(),
-                    },
-                )
+                IntRect::from_xywh(
+                    inside.left() + last_offset,
+                    inside.top(),
+                    (offset - last_offset) as u32,
+                    inside.height()
+                ).unwrap()
             };
 
             self.layout_step(child, crect);

+ 1 - 3
patina/src/layout/cache.rs → src/layout/cache.rs

@@ -1,7 +1,5 @@
 use std::{cell::RefCell, collections::HashMap, rc::Rc};
 
-use crate::geom::prelude::*;
-
 use super::SizePolicy;
 
 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
@@ -18,7 +16,7 @@ pub struct NodeState {
     pub(super) needs_update: bool,
     pub(super) net_policy: (SizePolicy, SizePolicy),
     pub(super) layer: Layer,
-    pub(super) rect: Option<IRect>,
+    pub(super) rect: Option<tiny_skia::IntRect>,
     pub(super) children: Vec<LayoutCacheKey>,
 }
 

+ 8 - 15
patina/src/layout/calc.rs → src/layout/calc.rs

@@ -1,5 +1,3 @@
-use crate::geom::{IRect, IVector};
-
 use super::{cache::LayoutCacheKey, Layer, LayoutNode, LayoutNodeAccess, NodeBehaviour, NodeState};
 
 pub fn recalculate(node: LayoutNodeAccess) {
@@ -60,13 +58,14 @@ fn arrangement_pass(parent: Option<LayoutCacheKey>, node: LayoutNodeAccess, laye
 
 #[cfg(test)]
 mod test {
-    use crate::{
-        geom::{IPoint, IRect, IVector},
+    use tiny_skia::IntRect;
+
+    use crate::
         layout::{
             cache::LayoutCache, ChildArrangement, LayoutNode, LayoutNodeAccess,
             LayoutNodeContainer, NodeBehaviour, SizePolicy,
-        },
-    };
+        }
+    ;
 
     use super::recalculate;
 
@@ -121,7 +120,7 @@ mod test {
             node: LayoutNode::new(cache.clone()),
         };
         root.node.set_behaviour(NodeBehaviour::Fixed {
-            rect: IRect::new_from_size(IPoint { x: 1, y: 1 }, IVector { x: 2, y: 5 }),
+            rect: IntRect::from_xywh(1, 1, 2, 5).unwrap()
         });
         root.node.child_arrangement = ChildArrangement::Column;
 
@@ -134,20 +133,14 @@ mod test {
             cache
                 .with_state(root.node.cache_key, |ns| ns.rect)
                 .flatten(),
-            Some(IRect::new_from_size(
-                IPoint { x: 1, y: 1 },
-                IVector { x: 2, y: 5 }
-            ))
+            Some(IntRect::from_xywh(1, 1, 2, 5).unwrap())
         );
 
         assert_eq!(
             cache
                 .with_state(root.children[0].node.cache_key, |ns| ns.rect)
                 .flatten(),
-            Some(IRect::new_from_size(
-                IPoint { x: 1, y: 1 },
-                IVector { x: 2, y: 2 }
-            ))
+            Some(IntRect::from_xywh(1, 1, 2, 2).unwrap())
         );
     }
 }

+ 223 - 0
src/lib.rs

@@ -0,0 +1,223 @@
+pub mod component;
+// pub mod geom;
+pub mod input;
+pub mod layout;
+// pub mod platform;
+pub mod widget;
+pub mod window;
+
+pub mod prelude {
+    pub use crate::component::Component;
+    pub use crate::widget::Widget;
+}
+use std::collections::HashMap;
+
+use prelude::*;
+
+#[derive(Debug)]
+pub enum UIControlMsg {
+    Terminate,
+}
+
+pub trait UIComponent: Sized + component::Component<ParentMsg = UIControlMsg> {
+    fn init(&mut self, ui_handle: UIHandle);
+    fn poll(&mut self) -> Vec<UIControlMsg>;
+    // fn build(bc: &widget::BuildContext) -> Self;
+    // fn map_ui_event(&self, uievent: platform::event::UIEvent) -> Option<Self::Msg>;
+}
+
+pub(crate) struct UIState {
+    pub(crate) layout_cache: std::rc::Rc<layout::LayoutCache>,
+    pub(crate) window_states:
+        HashMap<winit::window::WindowId, std::rc::Weak<dyn window::WindowStateAccess>>,
+}
+
+pub struct UIHandle<'l> {
+    state: &'l mut UIState,
+    eloop: &'l winit::event_loop::ActiveEventLoop,
+}
+
+impl<'l> UIHandle<'l> {
+    pub fn window_builder<'r>(&'r mut self) -> window::WindowBuilder<'r, 'l> {
+        window::WindowBuilder::new(self)
+    }
+
+    pub fn new_layout_node(&self) -> layout::LayoutNode {
+        layout::LayoutNode::new(self.state.layout_cache.clone())
+    }
+}
+
+pub struct UI<UIC: UIComponent> {
+    state: UIState,
+    event_loop: Option<winit::event_loop::EventLoop<()>>,
+    ui_component: UIC,
+    autoclose: bool,
+    initialized: bool,
+}
+
+impl<UIC: UIComponent> UI<UIC> {
+    pub fn new(uic: UIC) -> Self {
+        let lcache = layout::LayoutCache::new();
+
+        let eloop = winit::event_loop::EventLoop::builder().build().unwrap();
+        eloop.set_control_flow(winit::event_loop::ControlFlow::Wait);
+
+        Self {
+            state: UIState {
+                layout_cache: lcache,
+                window_states: Default::default(),
+            },
+            event_loop: Some(eloop),
+            // root_layout_node: layout::LayoutNode::new(lcache.clone()),
+            ui_component: uic,
+            autoclose: true,
+            initialized: false,
+        }
+    }
+
+    pub fn disable_autoclose(mut self) -> Self {
+        self.autoclose = false;
+        self
+    }
+
+    pub fn ui_component(&self) -> &UIC {
+        &self.ui_component
+    }
+    pub fn ui_component_mut(&mut self) -> &mut UIC {
+        &mut self.ui_component
+    }
+
+    /// Public helper function, delivers a message to the UI component and then processes whatever
+    /// control messages result.
+    pub fn deliver_msg(&mut self, msg: UIC::Msg) {
+        let cmsgs = self.ui_component.process(msg);
+        self.process_control_msgs(cmsgs.into_iter());
+    }
+
+    fn process_control_msgs(&mut self, msgs: impl Iterator<Item = UIControlMsg>) {
+        for cmsg in msgs {
+            match cmsg {
+                UIControlMsg::Terminate => {
+                    todo!()
+                    // self.ui_running = false;
+                }
+            }
+        }
+    }
+
+    pub fn update(&mut self) {
+        /*
+        let mut platform_events = vec![];
+        while let Some(evt) = self.platform.poll_event() {
+            match evt {
+                platform::event::WindowEvent::CloseRequest => {
+                    platform_events.extend(
+                        self.ui_component()
+                            .map_ui_event(platform::event::UIEvent::Shutdown)
+                            .into_iter(),
+                    );
+                }
+                _ => {}
+            }
+        }
+
+        // poll for state changes due to user input
+        let poll_msgs = self
+            .ui_component
+            .root_widget_mut()
+            .poll(Some(&self.input_state))
+            .into_iter();
+        let cmsgs = self
+            .ui_component
+            .process_all(platform_events.into_iter().chain(poll_msgs));
+        self.process_control_msgs(cmsgs.into_iter());
+
+        let tgt_size = self.platform.render_size();
+        self.root_layout_node
+            .set_behaviour(layout::NodeBehaviour::Fixed {
+                rect: geom::IRect::new_from_size(geom::IPoint::ORIGIN, tgt_size),
+            });
+        */
+
+        // first, make sure we can paint to the render target
+        /*
+        let (ri, ti) = self.platform.render_text_mut();
+        if let Some(mut painter) = ri.render_target_mut().painter() {
+            // perform layout as needed
+            layout::recalculate(LayoutNodeAccess::new(&layout::LinearAccess::new(
+                &self.root_layout_node,
+                self.ui_component.root_widget().layout_node(),
+            )));
+
+            // now render as needed
+            self.ui_component.root_widget().render(&mut painter, ti);
+        } else {
+            log::warn!("Could not draw UI: no painter available");
+        }
+        ri.submit();
+        */
+    }
+
+    pub fn wait(&mut self) {
+        // self.platform.event_mut().wait_for_event();
+    }
+
+    pub fn run(mut self) {
+        let eloop = self.event_loop.take().unwrap();
+        eloop.run_app(&mut self).unwrap();
+    }
+
+    fn pump_events(&mut self, eloop: &winit::event_loop::ActiveEventLoop) {
+        let evts = self.ui_component.poll();
+        for evt in evts.into_iter() {
+            match evt {
+                UIControlMsg::Terminate => eloop.exit(),
+            }
+        }
+    }
+}
+
+impl<UIC: UIComponent> winit::application::ApplicationHandler<()> for UI<UIC> {
+    fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
+        if !self.initialized {
+            self.ui_component.init(UIHandle {
+                eloop: event_loop,
+                state: &mut self.state,
+            });
+            self.initialized = true;
+        }
+    }
+
+    fn window_event(
+        &mut self,
+        event_loop: &winit::event_loop::ActiveEventLoop,
+        window_id: winit::window::WindowId,
+        event: winit::event::WindowEvent,
+    ) {
+        let Some(wsa) = self
+            .state
+            .window_states
+            .get(&window_id)
+            .map(std::rc::Weak::upgrade)
+            .flatten()
+        else {
+            println!("superfluous event: {event:?}");
+            return;
+        };
+
+        match event {
+            winit::event::WindowEvent::CloseRequested => {
+                wsa.push_event(window::WindowEvent::CloseRequested);
+                self.pump_events(event_loop);
+            }
+            winit::event::WindowEvent::Destroyed => {
+                self.state.window_states.remove(&window_id);
+            }
+            winit::event::WindowEvent::RedrawRequested => {
+                wsa.request_redraw();
+                self.pump_events(event_loop);
+            }
+            _ => (),
+        }
+    }
+}

+ 6 - 0
patina/src/platform.rs → src/platform.rs

@@ -1,3 +1,8 @@
+use crate::{geom::IVector, Component};
+
+pub mod event;
+
+/*
 pub mod render;
 
 pub trait RenderInterface {
@@ -81,3 +86,4 @@ impl<
         (&mut self.0, &mut self.2)
     }
 }
+*/

+ 3 - 8
patina/src/platform/event.rs → src/platform/event.rs

@@ -1,16 +1,11 @@
-// use crate::geom::IPoint;
-
 #[derive(Clone, Debug)]
 pub enum UIEvent {
-    CloseRequest,
-    Resize,
+    Shutdown,
 }
 
 #[derive(Clone, Debug)]
 pub enum WindowEvent {
     CloseRequest,
-    MouseUpdate,
-    KeyDown,
-    KeyUp,
-    TextInput,
+    Resize,
 }
+

+ 0 - 0
patina/src/platform/render.rs → src/platform/render.rs


+ 0 - 0
patina/src/platform/text.rs → src/platform/text.rs


+ 0 - 0
patina/src/surface.rs → src/surface.rs


+ 62 - 0
src/widget.rs

@@ -0,0 +1,62 @@
+use std::ops::DerefMut;
+
+use crate::{
+    component::Component,
+    input::InputState,
+    layout::{LayoutCache, LayoutNode, LayoutNodeAccess, LeafLayoutNode},
+    UIHandle,
+};
+
+/*mod button;
+mod frame;
+mod group;
+mod label;
+
+pub use frame::Frame;
+pub use group::PlainGroup;
+pub use label::Label;*/
+
+pub trait Widget<C: Component> {
+    fn poll(&mut self, input_state: Option<&InputState>) -> Vec<C::Msg>;
+
+    fn layout_node(&self) -> LayoutNodeAccess;
+    fn layout_node_mut(&mut self) -> &mut LayoutNode;
+    fn render(&self, target: &mut tiny_skia::Pixmap, ti: &());
+}
+
+pub struct Spacer<C: Component> {
+    lnode: LeafLayoutNode,
+    _ghost: std::marker::PhantomData<C>,
+}
+
+impl<C: Component> Spacer<C> {
+    pub fn new(uih: &UIHandle) -> Self {
+        Self {
+            lnode: LeafLayoutNode::new(uih.new_layout_node()),
+            _ghost: Default::default(),
+        }
+    }
+}
+
+impl<C: Component> Widget<C> for Spacer<C> {
+    fn poll(&mut self, input_state: Option<&InputState>) -> Vec<<C as Component>::Msg> {
+        vec![]
+    }
+
+    fn layout_node(&self) -> LayoutNodeAccess {
+        LayoutNodeAccess::new(&self.lnode)
+    }
+    fn layout_node_mut(&mut self) -> &mut LayoutNode {
+        self.lnode.deref_mut()
+    }
+
+    fn render(&self, target: &mut tiny_skia::Pixmap, ti: &()) {
+        let mut path = tiny_skia::PathBuilder::new();
+        path.push_rect(self.layout_node().cached_rect().unwrap().to_rect());
+        let path = path.finish().unwrap();
+        let mut paint = tiny_skia::Paint::default();
+        paint.set_color_rgba8(192, 168, 32, 255);
+        target.fill_path(&path, &paint, tiny_skia::FillRule::Winding, tiny_skia::Transform::identity(), None);
+        target.fill(tiny_skia::Color::from_rgba(1.0, 0.5, 0.5, 1.0).unwrap());
+    }
+}

+ 0 - 0
patina/src/widget/button.rs → src/widget/button.rs


+ 0 - 0
patina/src/widget/context.rs → src/widget/context.rs


+ 0 - 0
patina/src/widget/frame.rs → src/widget/frame.rs


+ 0 - 0
patina/src/widget/group.rs → src/widget/group.rs


+ 0 - 0
patina/src/widget/label.rs → src/widget/label.rs


+ 14 - 0
src/widget/window.rs

@@ -0,0 +1,14 @@
+use crate::Component;
+
+pub struct Window {
+    
+}
+
+impl Component for Window {
+    type Msg = ();
+    type ParentMsg = ();
+
+    fn process(&mut self, msg: Self::Msg) -> Vec<Self::ParentMsg> {
+        vec![]
+    }
+}

+ 142 - 0
src/window.rs

@@ -0,0 +1,142 @@
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use tiny_skia::IntRect;
+
+use crate::input::InputState;
+use crate::layout::{self, LayoutNode, LayoutNodeAccess, LinearAccess};
+use crate::widget::Widget;
+use crate::{Component, UIHandle};
+
+#[derive(Debug)]
+pub enum WindowEvent {
+    CloseRequested,
+    Resized,
+}
+
+pub trait WindowComponent: Sized + Component {
+    fn map_window_event(&self, we: WindowEvent) -> Option<Self::Msg>;
+
+    type RootWidget: Widget<Self>;
+    fn root_widget(&self) -> &Self::RootWidget;
+    fn root_widget_mut(&mut self) -> &mut Self::RootWidget;
+}
+
+pub(crate) struct WindowState<WC: WindowComponent> {
+    wc: WC,
+    root_node: LayoutNode,
+    window: Rc<winit::window::Window>,
+    events: Vec<WindowEvent>,
+    istate: InputState,
+    draw_pending: bool,
+    surface: softbuffer::Surface<Rc<winit::window::Window>, Rc<winit::window::Window>>,
+}
+
+impl<WC: WindowComponent> WindowState<WC> {
+    fn redraw(&mut self) {
+        self.draw_pending = false;
+
+        let size = self.window.inner_size();
+        self.surface.resize(size.width.try_into().unwrap(), size.height.try_into().unwrap()).unwrap();
+        let mut buf = self.surface.buffer_mut().unwrap();
+
+        self.root_node.set_behaviour(layout::NodeBehaviour::Fixed { rect: IntRect::from_xywh(0, 0, size.width, size.height).unwrap() });
+
+        let layout = LinearAccess::new(&self.root_node, self.wc.root_widget().layout_node());
+
+        let mut pixmap = tiny_skia::Pixmap::new(size.width, size.height).unwrap();
+        layout::recalculate(LayoutNodeAccess::new(&layout));
+        self.wc.root_widget().render(&mut pixmap, &());
+
+        // expensive ABGR -> RGB0 conversion
+        let mut pxdata = pixmap.data();
+        for t in buf.iter_mut() {
+            *t = u32::from_be_bytes(pxdata[0..4].try_into().unwrap()) >> 8; //((pxdata[0] as u32) << 16) | ((pxdata[1] as u32) << 8) | (pxdata[2] as u32);
+            pxdata = &pxdata[4..];
+        }
+
+        buf.present().unwrap();
+    }
+
+    fn poll(&mut self) -> Vec<WC::ParentMsg> {
+        let mut ret = vec![];
+        for we in self.events.drain(..) {
+            let mapped = self.wc.map_window_event(we);
+            ret.extend(self.wc.process_all(mapped.into_iter()).into_iter());
+        }
+
+        let evts = self.wc.root_widget_mut().poll(Some(&self.istate));
+        ret.extend(self.wc.process_all(evts.into_iter()));
+
+        if self.draw_pending {
+            self.redraw();
+        }
+
+        ret
+    }
+}
+
+pub(crate) trait WindowStateAccess {
+    fn push_event(&self, we: WindowEvent);
+    fn request_redraw(&self);
+}
+
+impl<WC: WindowComponent> WindowStateAccess for RefCell<WindowState<WC>> {
+    fn push_event(&self, we: WindowEvent) {
+        self.borrow_mut().events.push(we);
+    }
+    fn request_redraw(&self) {
+        self.borrow_mut().draw_pending = true;
+    }
+}
+
+pub struct WindowBuilder<'r, 'l: 'r> {
+    ui_handle: &'r mut UIHandle<'l>,
+}
+
+impl<'r, 'l: 'r> WindowBuilder<'r, 'l> {
+    pub(crate) fn new(ui_handle: &'r mut UIHandle<'l>) -> Self {
+        Self { ui_handle }
+    }
+
+    pub fn build<WC: WindowComponent>(self, wc: impl FnOnce(&UIHandle) -> WC) -> Window<WC> {
+        let window = Rc::new(self
+            .ui_handle
+            .eloop
+            .create_window(winit::window::WindowAttributes::default())
+            .unwrap());
+        let wid = window.id();
+
+        let ctx = softbuffer::Context::new(window.clone()).unwrap();
+        let surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap();
+
+        let wc = wc(&self.ui_handle);
+        let wstate = Rc::new(RefCell::new(WindowState {
+            wc,
+            root_node: LayoutNode::new(self.ui_handle.state.layout_cache.clone()),
+            window,
+            events: Default::default(),
+            istate: InputState::default().into(),
+            draw_pending: false.into(),
+            surface: surface.into(),
+        }));
+
+        self.ui_handle.state.window_states.insert(
+            wid,
+            Rc::downgrade(&(wstate.clone() as Rc<dyn WindowStateAccess>)),
+        );
+
+        Window { state: wstate }
+    }
+}
+
+pub struct Window<WC: WindowComponent> {
+    state: Rc<RefCell<WindowState<WC>>>,
+}
+
+impl<WC: WindowComponent> Window<WC> {
+    pub fn poll(&mut self) -> Vec<WC::ParentMsg> {
+        self.state.borrow_mut().poll()
+    }
+}
+