Implement iced_glutin 🎉

This commit is contained in:
Héctor Ramón Jiménez 2020-05-21 00:37:47 +02:00
parent a1a5fcfd46
commit e0e4ee73fe
31 changed files with 718 additions and 498 deletions

View file

@ -36,12 +36,6 @@ impl Backend {
}
}
/// Draws the provided primitives in the given [`Target`].
///
/// The text provided as overlay will be renderer on top of the primitives.
/// This is useful for rendering debug information.
///
/// [`Target`]: struct.Target.html
pub fn draw<T: AsRef<str>>(
&mut self,
gl: &glow::Context,
@ -50,6 +44,7 @@ impl Backend {
overlay_text: &[T],
) -> mouse::Interaction {
let viewport_size = viewport.physical_size();
let scale_factor = viewport.scale_factor() as f32;
let projection = viewport.projection();
let mut layers = Layer::generate(primitive, viewport);
@ -58,7 +53,7 @@ impl Backend {
for layer in layers {
self.flush(
gl,
viewport.scale_factor() as f32,
scale_factor,
projection,
&layer,
viewport_size.width,
@ -78,7 +73,8 @@ impl Backend {
target_width: u32,
target_height: u32,
) {
let bounds = (layer.bounds * scale_factor).round();
let mut bounds = (layer.bounds * scale_factor).round();
bounds.height = bounds.height.min(target_height);
if !layer.quads.is_empty() {
self.quad_pipeline.draw(
@ -204,3 +200,20 @@ impl backend::Text for Backend {
self.text_pipeline.space_width(size)
}
}
#[cfg(feature = "image")]
impl backend::Image for Backend {
fn dimensions(&self, _handle: &iced_native::image::Handle) -> (u32, u32) {
(50, 50)
}
}
#[cfg(feature = "svg")]
impl backend::Svg for Backend {
fn viewport_dimensions(
&self,
_handle: &iced_native::svg::Handle,
) -> (u32, u32) {
(50, 50)
}
}

View file

@ -38,7 +38,16 @@ pub use slider::Slider;
#[doc(no_inline)]
pub use text_input::TextInput;
pub use iced_native::{Image, Space, Text};
#[cfg(feature = "canvas")]
#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
pub mod canvas;
#[cfg(feature = "canvas")]
#[doc(no_inline)]
pub use canvas::Canvas;
pub use iced_native::{Image, Space};
pub type Column<'a, Message> = iced_native::Column<'a, Message, Renderer>;
pub type Row<'a, Message> = iced_native::Row<'a, Message, Renderer>;
pub type Text = iced_native::Text<Renderer>;

View file

@ -6,196 +6,4 @@
//!
//! [`Canvas`]: struct.Canvas.html
//! [`Frame`]: struct.Frame.html
use crate::{Defaults, Primitive, Renderer};
use iced_native::{
layout, Element, Hasher, Layout, Length, MouseCursor, Point, Size, Widget,
};
use std::hash::Hash;
pub mod layer;
pub mod path;
mod drawable;
mod fill;
mod frame;
mod stroke;
mod text;
pub use drawable::Drawable;
pub use fill::Fill;
pub use frame::Frame;
pub use layer::Layer;
pub use path::Path;
pub use stroke::{LineCap, LineJoin, Stroke};
pub use text::Text;
/// A widget capable of drawing 2D graphics.
///
/// A [`Canvas`] may contain multiple layers. A [`Layer`] is drawn using the
/// painter's algorithm. In other words, layers will be drawn on top of each
/// other in the same order they are pushed into the [`Canvas`].
///
/// [`Canvas`]: struct.Canvas.html
/// [`Layer`]: layer/trait.Layer.html
///
/// # Examples
/// The repository has a couple of [examples] showcasing how to use a
/// [`Canvas`]:
///
/// - [`clock`], an application that uses the [`Canvas`] widget to draw a clock
/// and its hands to display the current time.
/// - [`solar_system`], an animated solar system drawn using the [`Canvas`] widget
/// and showcasing how to compose different transforms.
///
/// [examples]: https://github.com/hecrj/iced/tree/0.1/examples
/// [`clock`]: https://github.com/hecrj/iced/tree/0.1/examples/clock
/// [`solar_system`]: https://github.com/hecrj/iced/tree/0.1/examples/solar_system
///
/// ## Drawing a simple circle
/// If you want to get a quick overview, here's how we can draw a simple circle:
///
/// ```no_run
/// # mod iced {
/// # pub use iced_wgpu::canvas;
/// # pub use iced_native::Color;
/// # }
/// use iced::canvas::{self, layer, Canvas, Drawable, Fill, Frame, Path};
/// use iced::Color;
///
/// // First, we define the data we need for drawing
/// #[derive(Debug)]
/// struct Circle {
/// radius: f32,
/// }
///
/// // Then, we implement the `Drawable` trait
/// impl Drawable for Circle {
/// fn draw(&self, frame: &mut Frame) {
/// // We create a `Path` representing a simple circle
/// let circle = Path::new(|p| p.circle(frame.center(), self.radius));
///
/// // And fill it with some color
/// frame.fill(&circle, Fill::Color(Color::BLACK));
/// }
/// }
///
/// // We can use a `Cache` to avoid unnecessary re-tessellation
/// let cache: layer::Cache<Circle> = layer::Cache::new();
///
/// // Finally, we simply provide the data to our `Cache` and push the resulting
/// // layer into a `Canvas`
/// let canvas = Canvas::new()
/// .push(cache.with(&Circle { radius: 50.0 }));
/// ```
#[derive(Debug)]
pub struct Canvas<'a> {
width: Length,
height: Length,
layers: Vec<Box<dyn Layer + 'a>>,
}
impl<'a> Canvas<'a> {
const DEFAULT_SIZE: u16 = 100;
/// Creates a new [`Canvas`] with no layers.
///
/// [`Canvas`]: struct.Canvas.html
pub fn new() -> Self {
Canvas {
width: Length::Units(Self::DEFAULT_SIZE),
height: Length::Units(Self::DEFAULT_SIZE),
layers: Vec::new(),
}
}
/// Sets the width of the [`Canvas`].
///
/// [`Canvas`]: struct.Canvas.html
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Canvas`].
///
/// [`Canvas`]: struct.Canvas.html
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
/// Adds a [`Layer`] to the [`Canvas`].
///
/// It will be drawn on top of previous layers.
///
/// [`Layer`]: layer/trait.Layer.html
/// [`Canvas`]: struct.Canvas.html
pub fn push(mut self, layer: impl Layer + 'a) -> Self {
self.layers.push(Box::new(layer));
self
}
}
impl<'a, Message> Widget<Message, Renderer> for Canvas<'a> {
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
}
fn layout(
&self,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(self.height);
let size = limits.resolve(Size::ZERO);
layout::Node::new(size)
}
fn draw(
&self,
_renderer: &mut Renderer,
_defaults: &Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> (Primitive, MouseCursor) {
let bounds = layout.bounds();
let origin = Point::new(bounds.x, bounds.y);
let size = Size::new(bounds.width, bounds.height);
(
Primitive::Group {
primitives: self
.layers
.iter()
.map(|layer| Primitive::Cached {
origin,
cache: layer.draw(size),
})
.collect(),
},
MouseCursor::Idle,
)
}
fn hash_layout(&self, state: &mut Hasher) {
std::any::TypeId::of::<Canvas<'static>>().hash(state);
self.width.hash(state);
self.height.hash(state);
}
}
impl<'a, Message> From<Canvas<'a>> for Element<'a, Message, Renderer>
where
Message: 'static,
{
fn from(canvas: Canvas<'a>) -> Element<'a, Message, Renderer> {
Element::new(canvas)
}
}
pub use iced_graphics::canvas::*;

View file

@ -1,180 +1,64 @@
use crate::{Renderer, Settings, Viewport};
use crate::{Backend, Renderer, Settings, Viewport};
use core::ffi::c_void;
use glow::HasContext;
use iced_graphics::Size;
use iced_native::mouse;
use raw_window_handle::HasRawWindowHandle;
/// A window graphics backend for iced powered by `glow`.
#[allow(missing_debug_implementations)]
pub struct Compositor {
connection: surfman::Connection,
device: surfman::Device,
gl_context: surfman::Context,
gl: Option<glow::Context>,
gl: glow::Context,
}
impl iced_graphics::window::Compositor for Compositor {
impl iced_graphics::window::GLCompositor for Compositor {
type Settings = Settings;
type Renderer = Renderer;
type Surface = ();
type SwapChain = ();
fn new(_settings: Self::Settings) -> Self {
let connection = surfman::Connection::new().expect("Create connection");
unsafe fn new(
settings: Self::Settings,
loader_function: impl FnMut(&str) -> *const c_void,
) -> (Self, Self::Renderer) {
let gl = glow::Context::from_loader_function(loader_function);
let adapter = connection
.create_hardware_adapter()
.expect("Create adapter");
gl.clear_color(1.0, 1.0, 1.0, 1.0);
let mut device =
connection.create_device(&adapter).expect("Create device");
// Enable auto-conversion from/to sRGB
gl.enable(glow::FRAMEBUFFER_SRGB);
let context_descriptor = device
.create_context_descriptor(&surfman::ContextAttributes {
version: surfman::GLVersion::new(3, 0),
flags: surfman::ContextAttributeFlags::empty(),
})
.expect("Create context descriptor");
// Enable alpha blending
gl.enable(glow::BLEND);
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
let gl_context = device
.create_context(&context_descriptor)
.expect("Create context");
let renderer = Renderer::new(Backend::new(&gl, settings));
Self {
connection,
device,
gl_context,
gl: None,
}
(Self { gl }, renderer)
}
fn create_renderer(&mut self, settings: Settings) -> Renderer {
self.device
.make_context_current(&self.gl_context)
.expect("Make context current");
Renderer::new(crate::Backend::new(self.gl.as_ref().unwrap(), settings))
}
fn create_surface<W: HasRawWindowHandle>(
&mut self,
window: &W,
) -> Self::Surface {
let native_widget = self
.connection
.create_native_widget_from_rwh(window.raw_window_handle())
.expect("Create widget");
let surface = self
.device
.create_surface(
&self.gl_context,
surfman::SurfaceAccess::GPUOnly,
surfman::SurfaceType::Widget { native_widget },
)
.expect("Create surface");
let surfman::SurfaceInfo { .. } = self.device.surface_info(&surface);
self.device
.bind_surface_to_context(&mut self.gl_context, surface)
.expect("Bind surface to context");
self.device
.make_context_current(&self.gl_context)
.expect("Make context current");
self.gl = Some(glow::Context::from_loader_function(|s| {
self.device.get_proc_address(&self.gl_context, s)
}));
//let mut framebuffer =
// skia_safe::gpu::gl::FramebufferInfo::from_fboid(framebuffer_object);
//framebuffer.format = gl::RGBA8;
//framebuffer
}
fn create_swap_chain(
&mut self,
_surface: &Self::Surface,
width: u32,
height: u32,
) -> Self::SwapChain {
let mut surface = self
.device
.unbind_surface_from_context(&mut self.gl_context)
.expect("Unbind surface")
.expect("Active surface");
self.device
.resize_surface(
&self.gl_context,
&mut surface,
euclid::Size2D::new(width as i32, height as i32),
)
.expect("Resize surface");
self.device
.bind_surface_to_context(&mut self.gl_context, surface)
.expect("Bind surface to context");
let gl = self.gl.as_ref().unwrap();
fn resize_viewport(&mut self, physical_size: Size<u32>) {
unsafe {
gl.viewport(0, 0, width as i32, height as i32);
gl.clear_color(1.0, 1.0, 1.0, 1.0);
// Enable auto-conversion from/to sRGB
gl.enable(glow::FRAMEBUFFER_SRGB);
// Enable alpha blending
gl.enable(glow::BLEND);
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
self.gl.viewport(
0,
0,
physical_size.width as i32,
physical_size.height as i32,
);
}
}
fn draw<T: AsRef<str>>(
&mut self,
renderer: &mut Self::Renderer,
swap_chain: &mut Self::SwapChain,
viewport: &Viewport,
output: &<Self::Renderer as iced_native::Renderer>::Output,
overlay: &[T],
) -> mouse::Interaction {
let gl = self.gl.as_ref().unwrap();
let gl = &self.gl;
unsafe {
gl.clear(glow::COLOR_BUFFER_BIT);
}
let mouse = renderer.backend_mut().draw(gl, viewport, output, overlay);
{
let mut surface = self
.device
.unbind_surface_from_context(&mut self.gl_context)
.expect("Unbind surface")
.expect("Active surface");
self.device
.present_surface(&self.gl_context, &mut surface)
.expect("Present surface");
self.device
.bind_surface_to_context(&mut self.gl_context, surface)
.expect("Bind surface to context");
}
mouse
}
}
impl Drop for Compositor {
fn drop(&mut self) {
self.device
.destroy_context(&mut self.gl_context)
.expect("Destroy context");
renderer.backend_mut().draw(gl, viewport, output, overlay)
}
}