Merged hover button with webrender's multi-window example

This commit is contained in:
Well 2019-08-06 01:02:23 -03:00
parent e1350f503d
commit b19b73bb6e
3 changed files with 260 additions and 260 deletions

49
src/button.rs Normal file
View File

@ -0,0 +1,49 @@
use crate::window::RenderContext;
use euclid::rect;
use webrender::api::*;
#[derive(Default)]
pub struct Button {
tag: (u64, u16),
is_hovered: bool,
}
impl Button {
pub fn on_event(&mut self, event: &glutin::WindowEvent, context: &RenderContext) -> bool {
match event {
glutin::WindowEvent::CursorMoved { position, .. } => {
let new_is_hovered = context.hit_test(
WorldPoint::new(position.x as f32, position.y as f32),
self.tag,
);
if self.is_hovered != new_is_hovered {
self.is_hovered = new_is_hovered;
return true;
}
}
glutin::WindowEvent::CursorLeft { .. } => {
if self.is_hovered {
self.is_hovered = false;
return true;
}
}
_ => {}
}
false
}
pub fn render(&self, pipeline_id: PipelineId, builder: &mut DisplayListBuilder) {
let mut layour_primitive_info = LayoutPrimitiveInfo::new(rect(80.0, 2.0, 554., 50.));
layour_primitive_info.tag = Some(self.tag);
builder.push_rect(
&layour_primitive_info,
&SpaceAndClipInfo::root_scroll(pipeline_id),
if self.is_hovered {
ColorF::new(0.2, 0.4, 0.1, 1.)
} else {
ColorF::new(0.5, 0., 0.7, 1.)
},
);
}
}

View File

@ -1,5 +1,24 @@
mod button;
mod window;
use webrender::api::*;
use window::Window;
fn main() {
window::Window::show().run();
let mut wins = vec![
Window::new("window1", ColorF::new(0.1, 0.2, 0.3, 1.0)),
Window::new("window2", ColorF::new(0.3, 0.2, 0.1, 1.0)),
];
while !wins.is_empty() {
let mut i = 0;
while i != wins.len() {
if wins[i].tick() {
let win = wins.remove(i);
win.deinit()
} else {
i += 1;
}
}
}
}

View File

@ -1,265 +1,9 @@
use euclid::rect;
use crate::button::Button;
use gleam::gl;
use glutin::dpi::LogicalSize;
use glutin::NotCurrent;
use webrender::api::*;
pub struct Window {
events_loop: glutin::EventsLoop,
w_context: glutin::ContextWrapper<glutin::PossiblyCurrent, glutin::Window>,
renderer: webrender::Renderer,
renderer_sender: RenderApiSender,
background: ColorF,
render: Option<RenderData>,
content: Option<Button>,
}
struct RenderData {
api: RenderApi,
doc: DocumentId,
epoch: Epoch,
pipe: PipelineId,
}
impl RenderData {
pub fn increase_epoch(&mut self) {
use std::u32;
const MAX_ID: u32 = u32::MAX - 1;
self.epoch = match self.epoch.0 {
MAX_ID => Epoch(0),
other => Epoch(other + 1),
};
}
}
struct Button {
tag: (u64, u16),
is_hovered: bool,
}
impl Button {
pub fn on_event(&mut self, event: &glutin::WindowEvent, render: &RenderData) -> bool {
match event {
glutin::WindowEvent::CursorMoved { position, .. } => {
let r = render.api.hit_test(
render.doc,
Some(render.pipe),
WorldPoint::new(position.x as f32, position.y as f32),
HitTestFlags::FIND_ALL,
);
let new_is_hovered = r.items.into_iter().any(|r| r.tag == self.tag);
if self.is_hovered != new_is_hovered {
self.is_hovered = new_is_hovered;
return true;
}
}
_ => {}
}
false
}
pub fn render(&self, render: &RenderData, builder: &mut DisplayListBuilder) {
let mut layour_primitive_info = LayoutPrimitiveInfo::new(rect(80.0, 2.0, 554., 50.));
layour_primitive_info.tag = Some(self.tag);
builder.push_rect(
&layour_primitive_info,
&SpaceAndClipInfo::root_scroll(render.pipe),
if self.is_hovered {
ColorF::new(0., 1., 0.4, 1.)
} else {
ColorF::new(1., 0., 0.4, 1.)
},
);
}
}
impl Window {
pub fn show() -> Window {
let events_loop = glutin::EventsLoop::new();
let w_context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
})
.build_windowed(
glutin::WindowBuilder::new()
.with_title("Test")
.with_dimensions(LogicalSize::new(800.0, 600.0))
.with_multitouch(),
&events_loop,
)
.expect("Error building windowed GL context");
let w_context = unsafe {
w_context
.make_current()
.expect("Error making context current")
};
let gl = match w_context.get_api() {
glutin::Api::OpenGl => unsafe {
gl::GlFns::load_with(|addr| w_context.get_proc_address(addr) as *const _)
},
glutin::Api::OpenGlEs => unsafe {
gl::GlesFns::load_with(|addr| w_context.get_proc_address(addr) as *const _)
},
_ => unreachable!(),
};
let dpi = w_context.window().get_hidpi_factor();
let background = ColorF::new(0., 0., 0., 1.);
let opts = webrender::RendererOptions {
device_pixel_ratio: dpi as f32,
clear_color: Some(background),
..Default::default()
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (renderer, renderer_sender) = webrender::Renderer::new(gl, notifier, opts, None)
.expect("Error creating web renderer");
Window {
events_loop,
w_context,
renderer,
renderer_sender,
background,
render: None,
content: None,
}
}
fn init_render(&mut self) {
let api = self.renderer_sender.create_api();
let doc = api.add_document(self.framebuffer_size(), 0);
let epoch = Epoch(0);
let pipe = PipelineId(0, 0);
let mut tsn = Transaction::new();
tsn.set_root_pipeline(pipe);
tsn.generate_frame();
api.send_transaction(doc, tsn);
self.render = Some(RenderData {
api,
doc,
epoch,
pipe,
});
}
fn init_content(&mut self) {
self.content = Some(Button {
tag: (0, 0),
is_hovered: false,
});
}
pub fn run(mut self) {
self.init_render();
self.init_content();
let mut events_loop = self.events_loop;
let mut content = self.content.unwrap();
let mut render = self.render.unwrap();
let w_context = self.w_context;
let mut run = true;
let mut first_render = true;
loop {
let mut render_content = first_render;
first_render = false;
events_loop.poll_events(|event| {
let w_event = match event {
glutin::Event::WindowEvent { event, .. } => event,
_ => return,
};
match w_event {
glutin::WindowEvent::CloseRequested => {
run = false;
return;
}
glutin::WindowEvent::Resized { .. } => render_content = true,
e => {
if content.on_event(&e, &render) {
render_content = true
}
}
}
});
if !run {
break;
}
let dpi = w_context.window().get_hidpi_factor();
let fsz = w_context
.window()
.get_inner_size()
.unwrap()
.to_physical(dpi);
let fsz = DeviceIntSize::new(fsz.width as i32, fsz.height as i32);
if true {
let dpi = dpi as f32;
render.increase_epoch();
let layout_size = fsz.to_f32() / euclid::TypedScale::new(dpi);
let mut tsn = Transaction::new();
let mut builder = DisplayListBuilder::new(render.pipe, layout_size);
content.render(&render, &mut builder);
render.api.set_window_parameters(
render.doc,
fsz,
DeviceIntRect::new(DeviceIntPoint::zero(), fsz),
dpi,
);
tsn.set_display_list(
render.epoch,
Some(self.background),
layout_size,
builder.finalize(),
true,
);
tsn.generate_frame();
render.api.send_transaction(render.doc, tsn);
self.renderer.update();
}
self.renderer.render(fsz).unwrap();
self.renderer.flush_pipeline_info();
w_context.swap_buffers().unwrap();
}
self.renderer.deinit();
}
fn dpi(&self) -> f64 {
self.w_context.window().get_hidpi_factor()
}
fn framebuffer_size(&self) -> DeviceIntSize {
let size = self
.w_context
.window()
.get_inner_size()
.unwrap()
.to_physical(self.dpi());
DeviceIntSize::new(size.width as i32, size.height as i32)
}
}
use webrender::DebugFlags;
struct Notifier {
events_proxy: glutin::EventsLoopProxy,
@ -279,6 +23,7 @@ impl RenderNotifier for Notifier {
}
fn wake_up(&self) {
#[cfg(not(target_os = "android"))]
let _ = self.events_proxy.wakeup();
}
@ -292,3 +37,190 @@ impl RenderNotifier for Notifier {
self.wake_up();
}
}
pub struct RenderContext {
api: RenderApi,
document_id: DocumentId,
epoch: Epoch,
pipeline_id: PipelineId,
renderer: webrender::Renderer,
}
impl RenderContext {
pub fn hit_test(&self, world_point: WorldPoint, tag: (u64, u16)) -> bool {
self.api
.hit_test(
self.document_id,
Some(self.pipeline_id),
world_point,
HitTestFlags::FIND_ALL,
)
.items
.into_iter()
.any(|r| r.tag == tag)
}
}
pub struct Window {
button: Button,
context: Option<glutin::WindowedContext<NotCurrent>>,
events_loop: glutin::EventsLoop, //TODO: share events loop?
name: &'static str,
render_context: RenderContext,
}
impl Window {
pub fn new(name: &'static str, clear_color: ColorF) -> Self {
let events_loop = glutin::EventsLoop::new();
let window_builder = glutin::WindowBuilder::new()
.with_title(name)
.with_multitouch()
.with_dimensions(LogicalSize::new(800., 600.));
let context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
})
.build_windowed(window_builder, &events_loop)
.unwrap();
let context = unsafe { context.make_current().unwrap() };
let gl = match context.get_api() {
glutin::Api::OpenGl => unsafe {
gl::GlFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
},
glutin::Api::OpenGlEs => unsafe {
gl::GlesFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
},
glutin::Api::WebGl => unimplemented!(),
};
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
let opts = webrender::RendererOptions {
device_pixel_ratio,
clear_color: Some(clear_color),
..webrender::RendererOptions::default()
};
let device_size = {
let size = context
.window()
.get_inner_size()
.unwrap()
.to_physical(device_pixel_ratio as f64);
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (renderer, sender) =
webrender::Renderer::new(gl.clone(), notifier, opts, None).unwrap();
let api = sender.create_api();
let document_id = api.add_document(device_size, 0);
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let txn = Transaction::new();
api.send_transaction(document_id, txn);
Window {
button: Button::default(),
context: Some(unsafe { context.make_not_current().unwrap() }),
events_loop,
name,
render_context: RenderContext {
api,
document_id,
epoch,
pipeline_id,
renderer,
},
}
}
pub fn tick(&mut self) -> bool {
let mut do_exit = false;
let my_name = &self.name;
let button = &mut self.button;
let render_context = &mut self.render_context;
self.events_loop
.poll_events(|global_event| match global_event {
glutin::Event::WindowEvent { event, .. } => match event {
glutin::WindowEvent::CloseRequested
| glutin::WindowEvent::KeyboardInput {
input:
glutin::KeyboardInput {
virtual_keycode: Some(glutin::VirtualKeyCode::Escape),
..
},
..
} => do_exit = true,
glutin::WindowEvent::KeyboardInput {
input:
glutin::KeyboardInput {
state: glutin::ElementState::Pressed,
virtual_keycode: Some(glutin::VirtualKeyCode::P),
..
},
..
} => {
println!("set flags {}", my_name);
render_context
.api
.send_debug_cmd(DebugCommand::SetFlags(DebugFlags::PROFILER_DBG))
}
_ => {
button.on_event(&event, render_context);
}
},
_ => {}
});
if do_exit {
return true;
}
let context = unsafe { self.context.take().unwrap().make_current().unwrap() };
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
let device_size = {
let size = context
.window()
.get_inner_size()
.unwrap()
.to_physical(device_pixel_ratio as f64);
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let layout_size = device_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut txn = Transaction::new();
let mut builder = DisplayListBuilder::new(render_context.pipeline_id, layout_size);
self.button.render(render_context.pipeline_id, &mut builder);
txn.set_display_list(
render_context.epoch,
None,
layout_size,
builder.finalize(),
true,
);
txn.set_root_pipeline(render_context.pipeline_id);
txn.generate_frame();
render_context
.api
.send_transaction(render_context.document_id, txn);
render_context.renderer.update();
render_context.renderer.render(device_size).unwrap();
context.swap_buffers().ok();
self.context = Some(unsafe { context.make_not_current().unwrap() });
false
}
pub fn deinit(mut self) {
let context = unsafe { self.context.take().unwrap().make_current().unwrap() };
self.render_context.renderer.deinit();
unsafe { context.make_not_current().unwrap() };
}
}