Render a more reasonable-looking button

This commit is contained in:
Richard Feldman 2022-02-23 00:23:52 -05:00
parent 5b92eb87ec
commit be3bdae36c
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
2 changed files with 122 additions and 58 deletions

View file

@ -1,12 +1,12 @@
use crate::{ use crate::{
graphics::{ graphics::{
colors::{self, from_hsb, to_wgpu_color}, colors,
lowlevel::buffer::create_rect_buffers, lowlevel::buffer::create_rect_buffers,
lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer}, lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer},
lowlevel::{buffer::QUAD_INDICES, pipelines}, lowlevel::{buffer::QUAD_INDICES, pipelines},
primitives::{ primitives::{
rect::{Rect, RectElt}, rect::{Rect, RectElt},
text::{build_glyph_brush, Text}, text::build_glyph_brush,
}, },
}, },
roc::{RocElem, RocElemTag}, roc::{RocElem, RocElemTag},
@ -17,7 +17,7 @@ use pipelines::RectResources;
use roc_std::RocStr; use roc_std::RocStr;
use std::error::Error; use std::error::Error;
use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView}; use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView};
use wgpu_glyph::GlyphBrush; use wgpu_glyph::{GlyphBrush, GlyphCruncher};
use winit::{ use winit::{
dpi::PhysicalSize, dpi::PhysicalSize,
event, event,
@ -145,7 +145,7 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box<dyn Error>> {
} }
//Received Character //Received Character
Event::WindowEvent { Event::WindowEvent {
event: event::WindowEvent::ReceivedCharacter(ch), event: event::WindowEvent::ReceivedCharacter(_ch),
.. ..
} => { } => {
// let input_outcome_res = // let input_outcome_res =
@ -159,7 +159,7 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box<dyn Error>> {
} }
//Keyboard Input //Keyboard Input
Event::WindowEvent { Event::WindowEvent {
event: event::WindowEvent::KeyboardInput { input, .. }, event: event::WindowEvent::KeyboardInput { input: _, .. },
.. ..
} => { } => {
// if let Some(virtual_keycode) = input.virtual_keycode { // if let Some(virtual_keycode) = input.virtual_keycode {
@ -233,6 +233,10 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box<dyn Error>> {
display_elem( display_elem(
&root, &root,
Bounds {
width: size.width as f32,
height: size.height as f32,
},
&mut staging_belt, &mut staging_belt,
&mut glyph_brush, &mut glyph_brush,
&mut cmd_encoder, &mut cmd_encoder,
@ -240,6 +244,10 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box<dyn Error>> {
&gpu_device, &gpu_device,
&rect_resources, &rect_resources,
wgpu::LoadOp::Load, wgpu::LoadOp::Load,
Bounds {
width: size.width as f32,
height: size.height as f32,
},
); );
// for text_section in &rects_and_texts.text_sections_front { // for text_section in &rects_and_texts.text_sections_front {
@ -399,8 +407,29 @@ pub fn render(title: RocStr, root: RocElem) {
run_event_loop(title.as_str(), root).expect("Error running event loop"); run_event_loop(title.as_str(), root).expect("Error running event loop");
} }
#[derive(Copy, Clone, Debug)]
struct Bounds {
width: f32,
height: f32,
}
#[derive(Clone, Debug)]
struct Drawable {
bounds: Bounds,
content: DrawableContent,
}
#[derive(Clone, Debug)]
enum DrawableContent {
Text(OwnedSection),
FillRect,
// Row(Vec<(Vector2<f32>, Drawable)>),
// Col(Vec<(Vector2<f32>, Drawable)>),
}
fn display_elem( fn display_elem(
elem: &RocElem, elem: &RocElem,
bounds: Bounds,
staging_belt: &mut wgpu::util::StagingBelt, staging_belt: &mut wgpu::util::StagingBelt,
glyph_brush: &mut GlyphBrush<()>, glyph_brush: &mut GlyphBrush<()>,
cmd_encoder: &mut CommandEncoder, cmd_encoder: &mut CommandEncoder,
@ -408,15 +437,34 @@ fn display_elem(
gpu_device: &wgpu::Device, gpu_device: &wgpu::Device,
rect_resources: &RectResources, rect_resources: &RectResources,
load_op: LoadOp<wgpu::Color>, load_op: LoadOp<wgpu::Color>,
) { texture_size: Bounds,
) -> Drawable {
use RocElemTag::*; use RocElemTag::*;
match elem.tag() { match elem.tag() {
Button => { Button => {
let button = unsafe { &elem.entry().button }; let button = unsafe { &elem.entry().button };
let child = display_elem(
&*button.child,
bounds,
staging_belt,
glyph_brush,
cmd_encoder,
texture_view,
gpu_device,
rect_resources,
load_op,
texture_size,
);
let pos = (0.0, 0.0).into();
let rect_elt = RectElt { let rect_elt = RectElt {
rect: button.bounds, rect: Rect {
color: (0.2, 0.2, 0.5, 1.0), pos,
width: child.bounds.width,
height: child.bounds.height,
},
color: (0.2, 0.2, 0.5, 0.5),
border_width: 10.0, border_width: 10.0,
border_color: (0.2, 0.5, 0.5, 1.0), border_color: (0.2, 0.5, 0.5, 1.0),
}; };
@ -430,24 +478,46 @@ fn display_elem(
load_op, load_op,
); );
display_elem( Drawable {
&*button.child, bounds: child.bounds,
staging_belt, content: DrawableContent::FillRect,
glyph_brush, }
cmd_encoder,
texture_view,
gpu_device,
rect_resources,
load_op,
);
} }
Text => { Text => {
let text = unsafe { &elem.entry().text }; let text = unsafe { &elem.entry().text };
let is_centered = true; // TODO don't hardcode this
let layout = wgpu_glyph::Layout::default().h_align(if is_centered {
wgpu_glyph::HorizontalAlign::Center
} else {
wgpu_glyph::HorizontalAlign::Left
});
glyph_brush.queue(owned_section_from_str(text.as_str()).to_borrowed()); let section = owned_section_from_str(text.as_str(), bounds, layout);
// TODO don't hardcode any of this! // Calculate the bounds
let area_bounds = (200, 300); let text_bounds;
let offset;
match glyph_brush.glyph_bounds(section.to_borrowed()) {
Some(glyph_bounds) => {
text_bounds = Bounds {
width: glyph_bounds.max.x - glyph_bounds.min.x,
height: glyph_bounds.max.y - glyph_bounds.min.y,
};
offset = (-glyph_bounds.min.x, -glyph_bounds.min.y);
}
None => {
text_bounds = Bounds {
width: 0.0,
height: 0.0,
};
offset = (0.0, 0.0);
}
}
glyph_brush.queue(section.with_screen_position(offset).to_borrowed());
glyph_brush glyph_brush
.draw_queued( .draw_queued(
@ -455,10 +525,15 @@ fn display_elem(
staging_belt, staging_belt,
cmd_encoder, cmd_encoder,
texture_view, texture_view,
area_bounds.0, texture_size.width as u32, // TODO why do we make these be u32 and then cast to f32 in orthorgraphic_projection?
area_bounds.1, texture_size.height as u32,
) )
.expect("Failed to draw text element"); .expect("Failed to draw text element");
Drawable {
bounds: text_bounds,
content: DrawableContent::FillRect,
}
} }
Row => { Row => {
todo!("Row"); todo!("Row");
@ -469,18 +544,19 @@ fn display_elem(
} }
} }
fn owned_section_from_str(string: &str) -> OwnedSection { fn owned_section_from_str(
let layout = layout_from_str(string, false); string: &str,
bounds: Bounds,
layout: wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker>,
) -> OwnedSection {
let is_centered = false;
// TODO don't hardcode any of this! // TODO don't hardcode any of this!
let position: Vector2<f32> = Vector2::new(50.0, 60.0);
let area_bounds: Vector2<f32> = Vector2::new(200.0, 300.0); let area_bounds: Vector2<f32> = Vector2::new(200.0, 300.0);
let color /*: RgbaTup */ = colors::WHITE; let color /*: RgbaTup */ = colors::WHITE;
let size: f32 = 40.0; let size: f32 = 40.0;
OwnedSection { OwnedSection {
screen_position: position.into(), bounds: (bounds.width, bounds.height),
bounds: area_bounds.into(),
layout, layout,
..OwnedSection::default() ..OwnedSection::default()
} }
@ -490,14 +566,3 @@ fn owned_section_from_str(string: &str) -> OwnedSection {
.with_scale(size), .with_scale(size),
) )
} }
fn layout_from_str(
string: &str,
is_centered: bool,
) -> wgpu_glyph::Layout<wgpu_glyph::BuiltInLineBreaker> {
wgpu_glyph::Layout::default().h_align(if is_centered {
wgpu_glyph::HorizontalAlign::Center
} else {
wgpu_glyph::HorizontalAlign::Left
})
}

View file

@ -4,31 +4,30 @@ mod rects_and_texts;
mod roc; mod roc;
use crate::roc::RocElem; use crate::roc::RocElem;
use roc_std::RocStr;
extern "C" { extern "C" {
#[link_name = "roc__renderForHost_1_exposed"] #[link_name = "roc__renderForHost_1_exposed"]
fn roc_render() -> RocElem; fn roc_render() -> RocElem;
} }
enum Action<State> { // enum Action<State> {
Update(State), // Update(State),
DoNothing, // DoNothing,
} // }
enum Elem<State> { // enum Elem<State> {
Button(Key, Box<dyn Fn() -> Action<State>>, Box<Elem<State>>), // Button(Key, Box<dyn Fn() -> Action<State>>, Box<Elem<State>>),
Text(Key, RocStr), // Text(Key, RocStr),
TextInput { // TextInput {
key: Key, // key: Key,
/// current text that's been entered // /// current text that's been entered
text: RocStr, // text: RocStr,
/// event handler to run when the user changes the text // /// event handler to run when the user changes the text
on_change: Box<dyn Fn(RocStr) -> Action<State>>, // on_change: Box<dyn Fn(RocStr) -> Action<State>>,
}, // },
Col(Key, Vec<Elem<State>>), // Col(Key, Vec<Elem<State>>),
Row(Key, Vec<Elem<State>>), // Row(Key, Vec<Elem<State>>),
} // }
/// Either a number between 0 and `isize::MAX`, /// Either a number between 0 and `isize::MAX`,
/// or a "null" value (meaning no number was specified). /// or a "null" value (meaning no number was specified).