Merged hover button with webrender's multi-window example
This commit is contained in:
parent
e1350f503d
commit
b19b73bb6e
|
@ -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.)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
21
src/main.rs
21
src/main.rs
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
450
src/window.rs
450
src/window.rs
|
@ -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() };
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue