diff --git a/editor/src/graphics/primitives/rect.rs b/editor/src/graphics/primitives/rect.rs index 7560c0d10a..ddf26f1199 100644 --- a/editor/src/graphics/primitives/rect.rs +++ b/editor/src/graphics/primitives/rect.rs @@ -1,9 +1,19 @@ use cgmath::Vector2; +/// These fields are ordered this way because in Roc, the corresponding stuct is: +/// +/// { top : F32, left : F32, width : F32, height : F32 } +/// +/// alphabetically, that's { height, left, top, width } - which works out to the same as: +/// +/// height: f32, pos: Vector2, width: f32 +/// +/// ...because Vector2 is a repr(C) struct of { x: f32, y: f32 } #[derive(Debug, Copy, Clone)] +#[repr(C)] pub struct Rect { + pub color: (f32, f32, f32, f32), + pub height: f32, pub top_left_coords: Vector2, pub width: f32, - pub height: f32, - pub color: (f32, f32, f32, f32), } diff --git a/examples/gui/Hello.roc b/examples/gui/Hello.roc index e2bb2e7999..d45cb87458 100644 --- a/examples/gui/Hello.roc +++ b/examples/gui/Hello.roc @@ -6,4 +6,4 @@ app "hello-gui" render = # btn = button { onPress : \prev, _ -> Action.none } (text "Hello, button!") - Button (Text "Hello, World!") + Button (Text "Hello, World!") { left: 300, top: 400, height: 300, width: 400 } diff --git a/examples/gui/platform/Package-Config.roc b/examples/gui/platform/Package-Config.roc index e8b25c7057..1d72e86d77 100644 --- a/examples/gui/platform/Package-Config.roc +++ b/examples/gui/platform/Package-Config.roc @@ -5,7 +5,9 @@ platform "examples/hello-world" imports [] provides [ renderForHost ] -Elem : [ Text Str, Button Elem ] +Dim : { left : F32, top : F32, width : F32, height : F32 } + +Elem : [ Button Elem Dim, Col (List Elem), Row (List Elem), Text Str ] renderForHost : Elem renderForHost = render diff --git a/examples/gui/platform/src/graphics/lowlevel/buffer.rs b/examples/gui/platform/src/graphics/lowlevel/buffer.rs index b1f3e2f2cc..5cd30e4052 100644 --- a/examples/gui/platform/src/graphics/lowlevel/buffer.rs +++ b/examples/gui/platform/src/graphics/lowlevel/buffer.rs @@ -4,7 +4,6 @@ // // Thank you, Benjamin! - // Contains parts of https://github.com/iced-rs/iced/blob/adce9e04213803bd775538efddf6e7908d1c605e/wgpu/src/shader/quad.wgsl // By Héctor Ramón, Iced contributors Licensed under the MIT license. // The license is included in the LEGAL_DETAILS file in the root directory of this distribution. @@ -13,9 +12,9 @@ use std::mem; -use super::{vertex::Vertex, quad::Quad}; +use super::{quad::Quad, vertex::Vertex}; use crate::graphics::{colors::to_slice, primitives::rect::RectElt}; -use wgpu::util::{ DeviceExt}; +use wgpu::util::DeviceExt; pub struct RectBuffers { pub vertex_buffer: wgpu::Buffer, @@ -47,7 +46,6 @@ pub fn create_rect_buffers( cmd_encoder: &mut wgpu::CommandEncoder, rects: &[RectElt], ) -> RectBuffers { - let vertex_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, contents: bytemuck::cast_slice(&QUAD_VERTS), @@ -67,10 +65,9 @@ pub fn create_rect_buffers( mapped_at_creation: false, }); - - let quads: Vec = rects.iter().map(|rect| {to_quad(rect)}).collect(); + let quads: Vec = rects.iter().map(|rect| to_quad(rect)).collect(); - let buffer_size = (quads.len() as u64 ) * Quad::SIZE; + let buffer_size = (quads.len() as u64) * Quad::SIZE; let staging_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, @@ -79,7 +76,6 @@ pub fn create_rect_buffers( }); cmd_encoder.copy_buffer_to_buffer(&staging_buffer, 0, &quad_buffer, 0, buffer_size); - RectBuffers { vertex_buffer, @@ -90,7 +86,7 @@ pub fn create_rect_buffers( pub fn to_quad(rect_elt: &RectElt) -> Quad { Quad { - pos: rect_elt.rect.top_left_coords.into(), + pos: rect_elt.rect.pos.into(), width: rect_elt.rect.width, height: rect_elt.rect.height, color: to_slice(rect_elt.color), diff --git a/examples/gui/platform/src/graphics/primitives/rect.rs b/examples/gui/platform/src/graphics/primitives/rect.rs index 89f6d2908b..6d2fad57e1 100644 --- a/examples/gui/platform/src/graphics/primitives/rect.rs +++ b/examples/gui/platform/src/graphics/primitives/rect.rs @@ -8,9 +8,19 @@ pub struct RectElt { pub border_color: (f32, f32, f32, f32), } +/// These fields are ordered this way because in Roc, the corresponding stuct is: +/// +/// { top : F32, left : F32, width : F32, height : F32 } +/// +/// alphabetically, that's { height, left, top, width } - which works out to the same as: +/// +/// struct Rect { height: f32, pos: Vector2, width: f32 } +/// +/// ...because Vector2 is a repr(C) struct of { x: f32, y: f32 } #[derive(Debug, Copy, Clone)] +#[repr(C)] pub struct Rect { - pub top_left_coords: Vector2, - pub width: f32, pub height: f32, + pub pos: Vector2, + pub width: f32, } diff --git a/examples/gui/platform/src/graphics/primitives/text.rs b/examples/gui/platform/src/graphics/primitives/text.rs index 91d5cdf0ae..f87aaf76d2 100644 --- a/examples/gui/platform/src/graphics/primitives/text.rs +++ b/examples/gui/platform/src/graphics/primitives/text.rs @@ -134,7 +134,7 @@ fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect { let top_y = glyph_top_y(&glyph.glyph); Rect { - top_left_coords: [position.x, top_y].into(), + pos: [position.x, top_y].into(), width, height, } diff --git a/examples/gui/platform/src/gui.rs b/examples/gui/platform/src/gui.rs index 1a688d8c8b..f6b8ee58ca 100644 --- a/examples/gui/platform/src/gui.rs +++ b/examples/gui/platform/src/gui.rs @@ -2,11 +2,14 @@ use crate::{ graphics::{ colors::{self, from_hsb, to_wgpu_color}, lowlevel::buffer::create_rect_buffers, - lowlevel::{ortho::update_ortho_buffer, buffer::MAX_QUADS}, - lowlevel::{pipelines, buffer::QUAD_INDICES}, - primitives::{text::{build_glyph_brush, Text}, rect::{Rect, RectElt}}, + lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer}, + lowlevel::{buffer::QUAD_INDICES, pipelines}, + primitives::{ + rect::{Rect, RectElt}, + text::{build_glyph_brush, Text}, + }, }, - rects_and_texts::RectsAndTexts, + roc::{RocElem, RocElemTag}, }; use pipelines::RectResources; use roc_std::RocStr; @@ -26,7 +29,7 @@ use winit::{ // // See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/ -fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box> { +fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box> { // Open window and create a surface let mut event_loop = winit::event_loop::EventLoop::new(); @@ -197,36 +200,36 @@ fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box .texture .create_view(&wgpu::TextureViewDescriptor::default()); - draw_rects( - &rects_and_texts.rects_behind, - &mut cmd_encoder, - &view, - &gpu_device, - &rect_resources, - wgpu::LoadOp::Clear(to_wgpu_color(from_hsb(240, 10, 19))), - ); + // for text_section in &rects_and_texts.text_sections_behind { + // let borrowed_text = text_section.to_borrowed(); - for text_section in &rects_and_texts.text_sections_behind { - let borrowed_text = text_section.to_borrowed(); - - glyph_brush.queue(borrowed_text); - } + // glyph_brush.queue(borrowed_text); + // } // draw first layer of text - glyph_brush - .draw_queued( - &gpu_device, - &mut staging_belt, - &mut cmd_encoder, - &view, - size.width, - size.height, - ) - .expect("Failed to draw first layer of text."); + // glyph_brush + // .draw_queued( + // &gpu_device, + // &mut staging_belt, + // &mut cmd_encoder, + // &view, + // size.width, + // size.height, + // ) + // .expect("Failed to draw first layer of text."); // draw rects on top of first text layer - draw_rects( - &rects_and_texts.rects_front, + // draw_rects( + // &rects_and_texts.rects_front, + // &mut cmd_encoder, + // &view, + // &gpu_device, + // &rect_resources, + // wgpu::LoadOp::Load, + // ); + + display_elem( + &root, &mut cmd_encoder, &view, &gpu_device, @@ -234,23 +237,23 @@ fn run_event_loop(title: &str, rects_and_texts: RectsAndTexts) -> Result<(), Box wgpu::LoadOp::Load, ); - for text_section in &rects_and_texts.text_sections_front { - let borrowed_text = text_section.to_borrowed(); + // for text_section in &rects_and_texts.text_sections_front { + // let borrowed_text = text_section.to_borrowed(); - glyph_brush.queue(borrowed_text); - } + // glyph_brush.queue(borrowed_text); + // } // draw text - glyph_brush - .draw_queued( - &gpu_device, - &mut staging_belt, - &mut cmd_encoder, - &view, - size.width, - size.height, - ) - .expect("Failed to draw queued text."); + // glyph_brush + // .draw_queued( + // &gpu_device, + // &mut staging_belt, + // &mut cmd_encoder, + // &view, + // size.width, + // size.height, + // ) + // .expect("Failed to draw queued text."); staging_belt.finish(); cmd_queue.submit(Some(cmd_encoder.finish())); @@ -319,74 +322,124 @@ fn begin_render_pass<'a>( }) } -pub fn render(title: RocStr) { - let rects_behind = vec![ - RectElt { - rect: Rect { - top_left_coords: (20.0, 20.0).into(), - width: 200.0, - height: 100.0 - }, - color: (0.4, 0.2, 0.5, 1.0), - border_width: 5.0, - border_color: (0.75, 0.5, 0.5, 1.0) - }, - RectElt { - rect: Rect { - top_left_coords: (420.0, 420.0).into(), - width: 150.0, - height: 150.0 - }, - color: (0.9, 0.2, 0.5, 1.0), - border_width: 10.0, - border_color: (0.2, 0.5, 0.5, 1.0) - }, - RectElt { - rect: Rect { - top_left_coords: (571.0, 420.0).into(), - width: 150.0, - height: 150.0 - }, - color: (0.2, 0.2, 0.5, 1.0), - border_width: 10.0, - border_color: (0.2, 0.5, 0.5, 1.0) - } - ]; +pub fn render(title: RocStr, root: RocElem) { + // let rects_behind = vec![ + // RectElt { + // rect: Rect { + // top_left_coords: (20.0, 20.0).into(), + // width: 200.0, + // height: 100.0 + // }, + // color: (0.4, 0.2, 0.5, 1.0), + // border_width: 5.0, + // border_color: (0.75, 0.5, 0.5, 1.0) + // }, + // RectElt { + // rect: Rect { + // top_left_coords: (420.0, 420.0).into(), + // width: 150.0, + // height: 150.0 + // }, + // color: (0.9, 0.2, 0.5, 1.0), + // border_width: 10.0, + // border_color: (0.2, 0.5, 0.5, 1.0) + // }, + // RectElt { + // rect: Rect { + // top_left_coords: (571.0, 420.0).into(), + // width: 150.0, + // height: 150.0 + // }, + // color: (0.2, 0.2, 0.5, 1.0), + // border_width: 10.0, + // border_color: (0.2, 0.5, 0.5, 1.0) + // } + // ]; - let texts_behind = vec![ - Text { - position: (50.0, 50.0).into(), - color: colors::WHITE, - text: "Back", - size: 40.0, - ..Default::default() - } - ]; + // let texts_behind = vec![ + // Text { + // position: (50.0, 50.0).into(), + // color: colors::WHITE, + // text: "Back", + // size: 40.0, + // ..Default::default() + // } + // ]; - let rects_front = vec![ - RectElt { - rect: Rect { - top_left_coords: (30.0, 30.0).into(), - width: 70.0, - height: 70.0 - }, - color: (0.7, 0.2, 0.2, 0.6), - border_width: 10.0, - border_color: (0.75, 0.5, 0.5, 1.0) - } - ]; + // let rects_front = vec![ + // RectElt { + // rect: Rect { + // top_left_coords: (30.0, 30.0).into(), + // width: 70.0, + // height: 70.0 + // }, + // color: (0.7, 0.2, 0.2, 0.6), + // border_width: 10.0, + // border_color: (0.75, 0.5, 0.5, 1.0) + // } + // ]; - let texts_front = vec![ - Text { - position: (70.0, 70.0).into(), - color: colors::WHITE, - text: "Front", - size: 40.0, - ..Default::default() - } - ]; + // let texts_front = vec![ + // Text { + // position: (70.0, 70.0).into(), + // color: colors::WHITE, + // text: "Front", + // size: 40.0, + // ..Default::default() + // } + // ]; - let rects_and_texts = RectsAndTexts::init(rects_behind, texts_behind, rects_front, texts_front); + // let rects_and_texts = RectsAndTexts::init(rects_behind, texts_behind, rects_front, texts_front); - run_event_loop(title.as_str(), rects_and_texts).expect("Error running event loop"); + run_event_loop(title.as_str(), root).expect("Error running event loop"); +} + +fn display_elem( + elem: &RocElem, + cmd_encoder: &mut CommandEncoder, + texture_view: &TextureView, + gpu_device: &wgpu::Device, + rect_resources: &RectResources, + load_op: LoadOp, +) { + use RocElemTag::*; + + match elem.tag() { + Button => { + let button = unsafe { &elem.entry().button }; + let rect_elt = RectElt { + rect: button.bounds, + color: (0.2, 0.2, 0.5, 1.0), + border_width: 10.0, + border_color: (0.2, 0.5, 0.5, 1.0), + }; + + draw_rects( + &[rect_elt], + cmd_encoder, + texture_view, + gpu_device, + rect_resources, + load_op, + ); + + display_elem( + &*button.child, + cmd_encoder, + texture_view, + gpu_device, + rect_resources, + load_op, + ); + } + Text => { + let text = unsafe { &elem.entry().text }; + } + Row => { + todo!("Row"); + } + Col => { + todo!("Col"); + } + } } diff --git a/examples/gui/platform/src/lib.rs b/examples/gui/platform/src/lib.rs index bbbc4c218b..8aaa9428dd 100644 --- a/examples/gui/platform/src/lib.rs +++ b/examples/gui/platform/src/lib.rs @@ -1,105 +1,16 @@ -#![allow(non_snake_case)] - -use core::ffi::c_void; -use core::mem::{self, ManuallyDrop}; -use roc_std::RocStr; -use std::ffi::CStr; -use std::os::raw::c_char; - mod graphics; mod gui; mod rects_and_texts; +mod roc; + +use crate::roc::RocElem; +use roc_std::RocStr; extern "C" { #[link_name = "roc__renderForHost_1_exposed"] fn roc_render() -> RocElem; } -#[repr(transparent)] -#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits -struct RocElem { - entry: *const RocElemEntry, -} - -impl RocElem { - #[cfg(target_pointer_width = "64")] - fn tag(&self) -> RocElemTag { - // On a 64-bit system, the last 3 bits of the pointer store the tag - unsafe { mem::transmute::((self.entry as u8) & 0b0000_0111) } - } - - pub fn entry(&self) -> &RocElemEntry { - // On a 64-bit system, the last 3 bits of the pointer store the tag - let cleared = self.entry as usize & !0b111; - - unsafe { &*(cleared as *const RocElemEntry) } - } - - #[cfg(target_pointer_width = "64")] - pub fn entry_mut(&mut self) -> &mut RocElemEntry { - // On a 64-bit system, the last 3 bits of the pointer store the tag - let cleared = self.entry as usize & !0b111; - - unsafe { &mut *(cleared as *mut RocElemEntry) } - } -} - -#[repr(u8)] -#[derive(Debug, Clone, Copy)] -enum RocElemTag { - Button = 0, - Text, -} - -#[repr(C)] -union RocElemEntry { - button: ManuallyDrop, - text: ManuallyDrop, -} - -#[no_mangle] -pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { - return libc::malloc(size); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_realloc( - c_ptr: *mut c_void, - new_size: usize, - _old_size: usize, - _alignment: u32, -) -> *mut c_void { - return libc::realloc(c_ptr, new_size); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { - return libc::free(c_ptr); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { - match tag_id { - 0 => { - let slice = CStr::from_ptr(c_ptr as *const c_char); - let string = slice.to_str().unwrap(); - eprintln!("Roc hit a panic: {}", string); - std::process::exit(1); - } - _ => todo!(), - } -} - -#[no_mangle] -pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void { - libc::memcpy(dst, src, n) -} - -#[no_mangle] -pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { - libc::memset(dst, c, n) -} - enum Action { Update(State), DoNothing, @@ -151,101 +62,9 @@ struct AppState { #[no_mangle] pub extern "C" fn rust_main() -> i32 { - println!("Calling roc_render()..."); + let root_elem = unsafe { roc_render() }; - let elem = unsafe { roc_render() }; - - fn display_elem(elem: &RocElem) { - use RocElemTag::*; - - println!("Got this tag: {:?}", elem.tag()); - - match elem.tag() { - Button => { - println!("Button!"); - - let child = unsafe { &elem.entry().button }; - - println!("Got this child:"); - - display_elem(child); - } - Text => { - let text = unsafe { &elem.entry().text }; - println!("Text: {}", (*text).as_str()); - } - } - } - - display_elem(&elem); - - // #[repr(C)] - // struct RocElem { - // entry: RocElemEntry - // tag: RocElemTag, - // } - - // #[repr(u8)] - // enum RocElemTag { - // Button = 0, - // Text, - // } - - // #[repr(C)] - // union RocElemEntry { - // text: ManuallyDrop, - // button: *const RocElem, - // } - - // fn render(clicks: i64) -> Elem { - // let txt = Elem::Text(Key::null(), format!("Clicks: {}", clicks).as_str().into()); - - // Elem::Button( - // Key::null(), - // Box::new(move || Action::Update(clicks + 1)), - // Box::new(txt), - // ) - // } - - // fn draw_elem(elem: Elem) { - // use Elem::*; - - // match elem { - // Button(_key, _on_click, label) => { - // print!("Drawing button label:\n\t"); - - // draw_elem(*label); - // } - // Text(_key, roc_str) => { - // println!("Drawing string \"{}\"", roc_str); - // } - // Col(_key, elems) => { - // println!("Drawing col contents..."); - - // for elem in elems { - // draw_elem(elem); - // } - // } - // Row(_key, elems) => { - // println!("Drawing row contents..."); - - // for elem in elems { - // draw_elem(elem); - // } - // } - // TextInput { - // key: _, - // text, - // on_change: _, - // } => { - // println!("Drawing text input with current text \"{}\"", text); - // } - // } - // } - - // draw_elem(render(0)); - - // gui::render("test title".into()); + gui::render("test title".into(), root_elem); // Exit code 0