slint/sixtyfps_runtime/corelib/graphics.rs
Olivier Goffart caca0d0ba4 Put the component in a Pin<>
Removed the drop and create from the ComponentVTable:
since we are not using VBox<ComponentVTable>, this simplifies a bit
the code of the interpreter and everything else.

But there is still a lot of changes everywhere to support that the Component
is pinned.
This is just for the component. Which would be required if later we want
to access the properties as Pin<Property<_>>. But we have not yet ability
to do projections
2020-06-24 14:13:27 +02:00

242 lines
7.8 KiB
Rust

extern crate alloc;
use crate::abi::datastructures::{Color, RenderingPrimitive};
use cgmath::Matrix4;
use std::cell::RefCell;
use std::rc::Rc;
pub enum FillStyle {
SolidColor(Color),
}
pub trait HasRenderingPrimitive {
fn primitive(&self) -> &RenderingPrimitive;
}
pub trait Frame {
type LowLevelRenderingPrimitive: HasRenderingPrimitive;
fn render_primitive(
&mut self,
primitive: &Self::LowLevelRenderingPrimitive,
transform: &Matrix4<f32>,
);
}
pub trait RenderingPrimitivesBuilder {
type LowLevelRenderingPrimitive: HasRenderingPrimitive;
fn create(&mut self, primitive: RenderingPrimitive) -> Self::LowLevelRenderingPrimitive;
}
pub trait GraphicsBackend: Sized {
type LowLevelRenderingPrimitive: HasRenderingPrimitive;
type Frame: Frame<LowLevelRenderingPrimitive = Self::LowLevelRenderingPrimitive>;
type RenderingPrimitivesBuilder: RenderingPrimitivesBuilder<
LowLevelRenderingPrimitive = Self::LowLevelRenderingPrimitive,
>;
fn new_rendering_primitives_builder(&mut self) -> Self::RenderingPrimitivesBuilder;
fn finish_primitives(&mut self, builder: Self::RenderingPrimitivesBuilder);
fn new_frame(&mut self, width: u32, height: u32, clear_color: &Color) -> Self::Frame;
fn present_frame(&mut self, frame: Self::Frame);
fn window(&self) -> &winit::window::Window;
}
enum RenderingCacheEntry<RenderingPrimitive> {
AllocateEntry(RenderingPrimitive),
FreeEntry(Option<usize>), // contains next free index if exists
}
pub struct RenderingCache<Backend: GraphicsBackend> {
nodes: Vec<RenderingCacheEntry<Backend::LowLevelRenderingPrimitive>>,
next_free: Option<usize>,
len: usize,
}
impl<Backend: GraphicsBackend> Default for RenderingCache<Backend> {
fn default() -> Self {
Self { nodes: vec![], next_free: None, len: 0 }
}
}
impl<Backend: GraphicsBackend> RenderingCache<Backend> {
pub fn allocate_entry(&mut self, content: Backend::LowLevelRenderingPrimitive) -> usize {
let idx = {
if let Some(free_idx) = self.next_free {
let node = &mut self.nodes[free_idx];
if let RenderingCacheEntry::FreeEntry(next_free) = node {
self.next_free = *next_free;
} else {
unreachable!();
}
*node = RenderingCacheEntry::AllocateEntry(content);
free_idx
} else {
self.nodes.push(RenderingCacheEntry::AllocateEntry(content));
self.nodes.len() - 1
}
};
self.len = self.len + 1;
idx
}
pub fn entry_at(&self, idx: usize) -> &Backend::LowLevelRenderingPrimitive {
match self.nodes[idx] {
RenderingCacheEntry::AllocateEntry(ref data) => return data,
_ => unreachable!(),
}
}
pub fn set_entry_at(&mut self, idx: usize, primitive: Backend::LowLevelRenderingPrimitive) {
match self.nodes[idx] {
RenderingCacheEntry::AllocateEntry(ref mut data) => *data = primitive,
_ => unreachable!(),
}
}
pub fn free_entry(&mut self, idx: usize) {
self.len = self.len - 1;
self.nodes[idx] = RenderingCacheEntry::FreeEntry(self.next_free);
self.next_free = Some(idx);
}
pub fn len(&self) -> usize {
self.len
}
}
pub struct GraphicsWindow<Backend: GraphicsBackend + 'static> {
graphics_backend_factory:
Box<dyn Fn(&crate::eventloop::EventLoop, winit::window::WindowBuilder) -> Backend>,
graphics_backend: Option<Backend>,
rendering_cache: RenderingCache<Backend>,
}
impl<Backend: GraphicsBackend + 'static> GraphicsWindow<Backend> {
pub fn new(
graphics_backend_factory: impl Fn(&crate::eventloop::EventLoop, winit::window::WindowBuilder) -> Backend
+ 'static,
) -> Rc<RefCell<Self>> {
let this = Rc::new(RefCell::new(Self {
graphics_backend_factory: Box::new(graphics_backend_factory),
graphics_backend: None,
rendering_cache: RenderingCache::default(),
}));
this
}
pub fn id(&self) -> Option<winit::window::WindowId> {
self.graphics_backend.as_ref().map(|backend| backend.window().id())
}
}
impl<Backend: GraphicsBackend> Drop for GraphicsWindow<Backend> {
fn drop(&mut self) {
if let Some(backend) = self.graphics_backend.as_ref() {
crate::eventloop::unregister_window(backend.window().id());
}
}
}
impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow
for RefCell<GraphicsWindow<Backend>>
{
fn draw(&self, component: crate::ComponentRefPin) {
// FIXME: we should do that only if some property change
component.as_ref().compute_layout(&crate::EvaluationContext::for_root_component(component));
let mut this = self.borrow_mut();
{
let mut rendering_primitives_builder =
this.graphics_backend.as_mut().unwrap().new_rendering_primitives_builder();
// Generate cached rendering data once
crate::item_tree::visit_items(
component,
|ctx, item, _| {
crate::item_rendering::update_item_rendering_data(
ctx,
item,
&mut this.rendering_cache,
&mut rendering_primitives_builder,
);
},
(),
);
this.graphics_backend.as_mut().unwrap().finish_primitives(rendering_primitives_builder);
}
let window = this.graphics_backend.as_ref().unwrap().window();
let size = window.inner_size();
let mut frame = this.graphics_backend.as_mut().unwrap().new_frame(
size.width,
size.height,
&Color::WHITE,
);
crate::item_rendering::render_component_items(
component,
&mut frame,
&mut this.rendering_cache,
);
this.graphics_backend.as_mut().unwrap().present_frame(frame);
}
fn process_mouse_input(
&self,
pos: winit::dpi::PhysicalPosition<f64>,
state: winit::event::ElementState,
component: crate::ComponentRefPin,
) {
crate::input::process_mouse_event(
component,
crate::abi::datastructures::MouseEvent {
pos: euclid::point2(pos.x as _, pos.y as _),
what: match state {
winit::event::ElementState::Pressed => {
crate::abi::datastructures::MouseEventType::MousePressed
}
winit::event::ElementState::Released => {
crate::abi::datastructures::MouseEventType::MouseReleased
}
},
},
);
}
fn window_handle(&self) -> std::cell::Ref<winit::window::Window> {
std::cell::Ref::map(self.borrow(), |mw| mw.graphics_backend.as_ref().unwrap().window())
}
fn map_window(self: Rc<Self>, event_loop: &crate::eventloop::EventLoop) {
if self.borrow().graphics_backend.is_some() {
return;
}
let id = {
let window_builder = winit::window::WindowBuilder::new();
let mut this = self.borrow_mut();
let factory = this.graphics_backend_factory.as_mut();
let backend = factory(&event_loop, window_builder);
let window_id = backend.window().id();
this.graphics_backend = Some(backend);
window_id
};
crate::eventloop::register_window(
id,
self.clone() as Rc<dyn crate::eventloop::GenericWindow>,
);
}
fn request_redraw(&self) {
if let Some(backend) = self.borrow().graphics_backend.as_ref() {
backend.window().request_redraw();
}
}
}