summaryrefslogtreecommitdiffstats
path: root/glow/src
diff options
context:
space:
mode:
Diffstat (limited to 'glow/src')
-rw-r--r--glow/src/backend.rs216
-rw-r--r--glow/src/lib.rs39
-rw-r--r--glow/src/program.rs39
-rw-r--r--glow/src/quad.rs235
-rw-r--r--glow/src/settings.rs25
-rw-r--r--glow/src/shader/quad.frag70
-rw-r--r--glow/src/shader/quad.vert47
-rw-r--r--glow/src/shader/triangle.frag9
-rw-r--r--glow/src/shader/triangle.vert13
-rw-r--r--glow/src/text.rs151
-rw-r--r--glow/src/triangle.rs292
-rw-r--r--glow/src/widget.rs58
-rw-r--r--glow/src/widget/button.rs15
-rw-r--r--glow/src/widget/canvas.rs9
-rw-r--r--glow/src/widget/checkbox.rs9
-rw-r--r--glow/src/widget/container.rs10
-rw-r--r--glow/src/widget/pane_grid.rs24
-rw-r--r--glow/src/widget/progress_bar.rs15
-rw-r--r--glow/src/widget/radio.rs10
-rw-r--r--glow/src/widget/scrollable.rs13
-rw-r--r--glow/src/widget/slider.rs16
-rw-r--r--glow/src/widget/text_input.rs15
-rw-r--r--glow/src/window.rs4
-rw-r--r--glow/src/window/compositor.rs74
24 files changed, 1408 insertions, 0 deletions
diff --git a/glow/src/backend.rs b/glow/src/backend.rs
new file mode 100644
index 00000000..6bd443ad
--- /dev/null
+++ b/glow/src/backend.rs
@@ -0,0 +1,216 @@
+use crate::quad;
+use crate::text;
+use crate::triangle;
+use crate::{Settings, Transformation, Viewport};
+use iced_graphics::backend;
+use iced_graphics::font;
+use iced_graphics::Layer;
+use iced_graphics::Primitive;
+use iced_native::mouse;
+use iced_native::{Font, HorizontalAlignment, Size, VerticalAlignment};
+
+/// A [`glow`] renderer.
+///
+/// [`glow`]: https://github.com/grovesNL/glow
+#[derive(Debug)]
+pub struct Backend {
+ quad_pipeline: quad::Pipeline,
+ text_pipeline: text::Pipeline,
+ triangle_pipeline: triangle::Pipeline,
+}
+
+impl Backend {
+ /// Creates a new [`Renderer`].
+ ///
+ /// [`Renderer`]: struct.Renderer.html
+ pub fn new(gl: &glow::Context, settings: Settings) -> Self {
+ let text_pipeline = text::Pipeline::new(gl, settings.default_font);
+ let quad_pipeline = quad::Pipeline::new(gl);
+ let triangle_pipeline = triangle::Pipeline::new(gl);
+
+ Self {
+ quad_pipeline,
+ text_pipeline,
+ triangle_pipeline,
+ }
+ }
+
+ pub fn draw<T: AsRef<str>>(
+ &mut self,
+ gl: &glow::Context,
+ viewport: &Viewport,
+ (primitive, mouse_interaction): &(Primitive, mouse::Interaction),
+ 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);
+ layers.push(Layer::overlay(overlay_text, viewport));
+
+ for layer in layers {
+ self.flush(
+ gl,
+ scale_factor,
+ projection,
+ &layer,
+ viewport_size.height,
+ );
+ }
+
+ *mouse_interaction
+ }
+
+ fn flush(
+ &mut self,
+ gl: &glow::Context,
+ scale_factor: f32,
+ transformation: Transformation,
+ layer: &Layer<'_>,
+ target_height: u32,
+ ) {
+ let mut bounds = (layer.bounds * scale_factor).round();
+ bounds.height = bounds.height.min(target_height);
+
+ if !layer.quads.is_empty() {
+ self.quad_pipeline.draw(
+ gl,
+ target_height,
+ &layer.quads,
+ transformation,
+ scale_factor,
+ bounds,
+ );
+ }
+
+ if !layer.meshes.is_empty() {
+ let scaled = transformation
+ * Transformation::scale(scale_factor, scale_factor);
+
+ self.triangle_pipeline.draw(
+ gl,
+ target_height,
+ scaled,
+ scale_factor,
+ &layer.meshes,
+ );
+ }
+
+ if !layer.text.is_empty() {
+ for text in layer.text.iter() {
+ // Target physical coordinates directly to avoid blurry text
+ let text = glow_glyph::Section {
+ // TODO: We `round` here to avoid rerasterizing text when
+ // its position changes slightly. This can make text feel a
+ // bit "jumpy". We may be able to do better once we improve
+ // our text rendering/caching pipeline.
+ screen_position: (
+ (text.bounds.x * scale_factor).round(),
+ (text.bounds.y * scale_factor).round(),
+ ),
+ // TODO: Fix precision issues with some scale factors.
+ //
+ // The `ceil` here can cause some words to render on the
+ // same line when they should not.
+ //
+ // Ideally, `wgpu_glyph` should be able to compute layout
+ // using logical positions, and then apply the proper
+ // scaling when rendering. This would ensure that both
+ // measuring and rendering follow the same layout rules.
+ bounds: (
+ (text.bounds.width * scale_factor).ceil(),
+ (text.bounds.height * scale_factor).ceil(),
+ ),
+ text: vec![glow_glyph::Text {
+ text: text.content,
+ scale: glow_glyph::ab_glyph::PxScale {
+ x: text.size * scale_factor,
+ y: text.size * scale_factor,
+ },
+ font_id: self.text_pipeline.find_font(text.font),
+ extra: glow_glyph::Extra {
+ color: text.color,
+ z: 0.0,
+ },
+ }],
+ layout: glow_glyph::Layout::default()
+ .h_align(match text.horizontal_alignment {
+ HorizontalAlignment::Left => {
+ glow_glyph::HorizontalAlign::Left
+ }
+ HorizontalAlignment::Center => {
+ glow_glyph::HorizontalAlign::Center
+ }
+ HorizontalAlignment::Right => {
+ glow_glyph::HorizontalAlign::Right
+ }
+ })
+ .v_align(match text.vertical_alignment {
+ VerticalAlignment::Top => {
+ glow_glyph::VerticalAlign::Top
+ }
+ VerticalAlignment::Center => {
+ glow_glyph::VerticalAlign::Center
+ }
+ VerticalAlignment::Bottom => {
+ glow_glyph::VerticalAlign::Bottom
+ }
+ }),
+ ..Default::default()
+ };
+
+ self.text_pipeline.queue(text);
+ }
+
+ self.text_pipeline.draw_queued(
+ gl,
+ transformation,
+ glow_glyph::Region {
+ x: bounds.x,
+ y: target_height - (bounds.y + bounds.height),
+ width: bounds.width,
+ height: bounds.height,
+ },
+ );
+ }
+ }
+}
+
+impl iced_graphics::Backend for Backend {
+ fn trim_measurements(&mut self) {
+ self.text_pipeline.trim_measurement_cache()
+ }
+}
+
+impl backend::Text for Backend {
+ const ICON_FONT: Font = font::ICONS;
+ const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
+
+ fn measure(
+ &self,
+ contents: &str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ ) -> (f32, f32) {
+ self.text_pipeline.measure(contents, size, font, bounds)
+ }
+}
+
+#[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)
+ }
+}
diff --git a/glow/src/lib.rs b/glow/src/lib.rs
new file mode 100644
index 00000000..c427d13a
--- /dev/null
+++ b/glow/src/lib.rs
@@ -0,0 +1,39 @@
+//! A [`glow`] renderer for [`iced_native`].
+//!
+//! [`glow`]: https://github.com/grovesNL/glow
+//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
+#![deny(missing_docs)]
+#![deny(missing_debug_implementations)]
+#![deny(unused_results)]
+#![forbid(rust_2018_idioms)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+
+mod backend;
+mod program;
+mod quad;
+mod text;
+mod triangle;
+
+pub mod settings;
+pub mod widget;
+pub mod window;
+
+pub use settings::Settings;
+
+pub(crate) use backend::Backend;
+pub(crate) use iced_graphics::Transformation;
+
+#[doc(no_inline)]
+pub use widget::*;
+
+pub use iced_graphics::Viewport;
+pub use iced_native::{
+ Background, Color, Command, HorizontalAlignment, Length, Vector,
+ VerticalAlignment,
+};
+
+/// A [`glow`] graphics renderer for [`iced`].
+///
+/// [`glow`]: https://github.com/grovesNL/glow
+/// [`iced`]: https://github.com/hecrj/iced
+pub type Renderer = iced_graphics::Renderer<Backend>;
diff --git a/glow/src/program.rs b/glow/src/program.rs
new file mode 100644
index 00000000..489a194f
--- /dev/null
+++ b/glow/src/program.rs
@@ -0,0 +1,39 @@
+use glow::HasContext;
+
+pub unsafe fn create(
+ gl: &glow::Context,
+ shader_sources: &[(u32, &str)],
+) -> <glow::Context as HasContext>::Program {
+ let program = gl.create_program().expect("Cannot create program");
+
+ let mut shaders = Vec::with_capacity(shader_sources.len());
+
+ for (shader_type, shader_source) in shader_sources.iter() {
+ let shader = gl
+ .create_shader(*shader_type)
+ .expect("Cannot create shader");
+
+ gl.shader_source(shader, shader_source);
+ gl.compile_shader(shader);
+
+ if !gl.get_shader_compile_status(shader) {
+ panic!(gl.get_shader_info_log(shader));
+ }
+
+ gl.attach_shader(program, shader);
+
+ shaders.push(shader);
+ }
+
+ gl.link_program(program);
+ if !gl.get_program_link_status(program) {
+ panic!(gl.get_program_info_log(program));
+ }
+
+ for shader in shaders {
+ gl.detach_shader(program, shader);
+ gl.delete_shader(shader);
+ }
+
+ program
+}
diff --git a/glow/src/quad.rs b/glow/src/quad.rs
new file mode 100644
index 00000000..3a65338a
--- /dev/null
+++ b/glow/src/quad.rs
@@ -0,0 +1,235 @@
+use crate::program;
+use crate::Transformation;
+use glow::HasContext;
+use iced_graphics::layer;
+use iced_native::Rectangle;
+
+const MAX_INSTANCES: usize = 100_000;
+
+#[derive(Debug)]
+pub struct Pipeline {
+ program: <glow::Context as HasContext>::Program,
+ vertex_array: <glow::Context as HasContext>::VertexArray,
+ instances: <glow::Context as HasContext>::Buffer,
+ transform_location: <glow::Context as HasContext>::UniformLocation,
+ scale_location: <glow::Context as HasContext>::UniformLocation,
+ screen_height_location: <glow::Context as HasContext>::UniformLocation,
+ current_transform: Transformation,
+ current_scale: f32,
+ current_target_height: u32,
+}
+
+impl Pipeline {
+ pub fn new(gl: &glow::Context) -> Pipeline {
+ let program = unsafe {
+ program::create(
+ gl,
+ &[
+ (glow::VERTEX_SHADER, include_str!("shader/quad.vert")),
+ (glow::FRAGMENT_SHADER, include_str!("shader/quad.frag")),
+ ],
+ )
+ };
+
+ let transform_location =
+ unsafe { gl.get_uniform_location(program, "u_Transform") }
+ .expect("Get transform location");
+
+ let scale_location =
+ unsafe { gl.get_uniform_location(program, "u_Scale") }
+ .expect("Get scale location");
+
+ let screen_height_location =
+ unsafe { gl.get_uniform_location(program, "u_ScreenHeight") }
+ .expect("Get target height location");
+
+ unsafe {
+ gl.use_program(Some(program));
+
+ let matrix: [f32; 16] = Transformation::identity().into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(transform_location),
+ false,
+ &matrix,
+ );
+
+ gl.uniform_1_f32(Some(scale_location), 1.0);
+ gl.uniform_1_f32(Some(screen_height_location), 0.0);
+
+ gl.use_program(None);
+ }
+
+ let (vertex_array, instances) =
+ unsafe { create_instance_buffer(gl, MAX_INSTANCES) };
+
+ Pipeline {
+ program,
+ vertex_array,
+ instances,
+ transform_location,
+ scale_location,
+ screen_height_location,
+ current_transform: Transformation::identity(),
+ current_scale: 1.0,
+ current_target_height: 0,
+ }
+ }
+
+ pub fn draw(
+ &mut self,
+ gl: &glow::Context,
+ target_height: u32,
+ instances: &[layer::Quad],
+ transformation: Transformation,
+ scale: f32,
+ bounds: Rectangle<u32>,
+ ) {
+ unsafe {
+ gl.enable(glow::SCISSOR_TEST);
+ gl.scissor(
+ bounds.x as i32,
+ (target_height - (bounds.y + bounds.height)) as i32,
+ bounds.width as i32,
+ bounds.height as i32,
+ );
+
+ gl.use_program(Some(self.program));
+ gl.bind_vertex_array(Some(self.vertex_array));
+ gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.instances));
+ }
+
+ if transformation != self.current_transform {
+ unsafe {
+ let matrix: [f32; 16] = transformation.into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(self.transform_location),
+ false,
+ &matrix,
+ );
+
+ self.current_transform = transformation;
+ }
+ }
+
+ if scale != self.current_scale {
+ unsafe {
+ gl.uniform_1_f32(Some(self.scale_location), scale);
+ }
+
+ self.current_scale = scale;
+ }
+
+ if target_height != self.current_target_height {
+ unsafe {
+ gl.uniform_1_f32(
+ Some(self.screen_height_location),
+ target_height as f32,
+ );
+ }
+
+ self.current_target_height = target_height;
+ }
+
+ let mut i = 0;
+ let total = instances.len();
+
+ while i < total {
+ let end = (i + MAX_INSTANCES).min(total);
+ let amount = end - i;
+
+ unsafe {
+ gl.buffer_sub_data_u8_slice(
+ glow::ARRAY_BUFFER,
+ 0,
+ bytemuck::cast_slice(&instances[i..end]),
+ );
+
+ gl.draw_arrays_instanced(
+ glow::TRIANGLE_STRIP,
+ 0,
+ 4,
+ amount as i32,
+ );
+ }
+
+ i += MAX_INSTANCES;
+ }
+
+ unsafe {
+ gl.bind_vertex_array(None);
+ gl.use_program(None);
+ gl.disable(glow::SCISSOR_TEST);
+ }
+ }
+}
+
+unsafe fn create_instance_buffer(
+ gl: &glow::Context,
+ size: usize,
+) -> (
+ <glow::Context as HasContext>::VertexArray,
+ <glow::Context as HasContext>::Buffer,
+) {
+ let vertex_array = gl.create_vertex_array().expect("Create vertex array");
+ let buffer = gl.create_buffer().expect("Create instance buffer");
+
+ gl.bind_vertex_array(Some(vertex_array));
+ gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));
+ gl.buffer_data_size(
+ glow::ARRAY_BUFFER,
+ (size * std::mem::size_of::<layer::Quad>()) as i32,
+ glow::DYNAMIC_DRAW,
+ );
+
+ let stride = std::mem::size_of::<layer::Quad>() as i32;
+
+ gl.enable_vertex_attrib_array(0);
+ gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
+ gl.vertex_attrib_divisor(0, 1);
+
+ gl.enable_vertex_attrib_array(1);
+ gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2);
+ gl.vertex_attrib_divisor(1, 1);
+
+ gl.enable_vertex_attrib_array(2);
+ gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
+ gl.vertex_attrib_divisor(2, 1);
+
+ gl.enable_vertex_attrib_array(3);
+ gl.vertex_attrib_pointer_f32(
+ 3,
+ 4,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4),
+ );
+ gl.vertex_attrib_divisor(3, 1);
+
+ gl.enable_vertex_attrib_array(4);
+ gl.vertex_attrib_pointer_f32(
+ 4,
+ 1,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4 + 4),
+ );
+ gl.vertex_attrib_divisor(4, 1);
+
+ gl.enable_vertex_attrib_array(5);
+ gl.vertex_attrib_pointer_f32(
+ 5,
+ 1,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4 + 4 + 1),
+ );
+ gl.vertex_attrib_divisor(5, 1);
+
+ gl.bind_vertex_array(None);
+ gl.bind_buffer(glow::ARRAY_BUFFER, None);
+
+ (vertex_array, buffer)
+}
diff --git a/glow/src/settings.rs b/glow/src/settings.rs
new file mode 100644
index 00000000..dce30029
--- /dev/null
+++ b/glow/src/settings.rs
@@ -0,0 +1,25 @@
+//! Configure a renderer.
+pub use iced_graphics::Antialiasing;
+
+/// The settings of a [`Renderer`].
+///
+/// [`Renderer`]: ../struct.Renderer.html
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Settings {
+ /// The bytes of the font that will be used by default.
+ ///
+ /// If `None` is provided, a default system font will be chosen.
+ pub default_font: Option<&'static [u8]>,
+
+ /// The antialiasing strategy that will be used for triangle primitives.
+ pub antialiasing: Option<Antialiasing>,
+}
+
+impl Default for Settings {
+ fn default() -> Settings {
+ Settings {
+ default_font: None,
+ antialiasing: None,
+ }
+ }
+}
diff --git a/glow/src/shader/quad.frag b/glow/src/shader/quad.frag
new file mode 100644
index 00000000..cea36bdc
--- /dev/null
+++ b/glow/src/shader/quad.frag
@@ -0,0 +1,70 @@
+#version 330
+
+uniform float u_ScreenHeight;
+
+in vec4 v_Color;
+in vec4 v_BorderColor;
+in vec2 v_Pos;
+in vec2 v_Scale;
+in float v_BorderRadius;
+in float v_BorderWidth;
+
+out vec4 o_Color;
+
+float distance(in vec2 frag_coord, in vec2 position, in vec2 size, float radius)
+{
+ // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
+ vec2 inner_size = size - vec2(radius, radius) * 2.0;
+ vec2 top_left = position + vec2(radius, radius);
+ vec2 bottom_right = top_left + inner_size;
+
+ vec2 top_left_distance = top_left - frag_coord;
+ vec2 bottom_right_distance = frag_coord - bottom_right;
+
+ vec2 distance = vec2(
+ max(max(top_left_distance.x, bottom_right_distance.x), 0.0),
+ max(max(top_left_distance.y, bottom_right_distance.y), 0.0)
+ );
+
+ return sqrt(distance.x * distance.x + distance.y * distance.y);
+}
+
+void main() {
+ vec4 mixed_color;
+
+ vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
+
+ // TODO: Remove branching (?)
+ if(v_BorderWidth > 0) {
+ float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0);
+
+ float internal_distance = distance(
+ fragCoord,
+ v_Pos + vec2(v_BorderWidth),
+ v_Scale - vec2(v_BorderWidth * 2.0),
+ internal_border
+ );
+
+ float border_mix = smoothstep(
+ max(internal_border - 0.5, 0.0),
+ internal_border + 0.5,
+ internal_distance
+ );
+
+ mixed_color = mix(v_Color, v_BorderColor, border_mix);
+ } else {
+ mixed_color = v_Color;
+ }
+
+ float d = distance(
+ fragCoord,
+ v_Pos,
+ v_Scale,
+ v_BorderRadius
+ );
+
+ float radius_alpha =
+ 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d);
+
+ o_Color = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
+}
diff --git a/glow/src/shader/quad.vert b/glow/src/shader/quad.vert
new file mode 100644
index 00000000..ce816550
--- /dev/null
+++ b/glow/src/shader/quad.vert
@@ -0,0 +1,47 @@
+#version 330
+
+uniform mat4 u_Transform;
+uniform float u_Scale;
+
+layout(location = 0) in vec2 i_Pos;
+layout(location = 1) in vec2 i_Scale;
+layout(location = 2) in vec4 i_Color;
+layout(location = 3) in vec4 i_BorderColor;
+layout(location = 4) in float i_BorderRadius;
+layout(location = 5) in float i_BorderWidth;
+
+out vec4 v_Color;
+out vec4 v_BorderColor;
+out vec2 v_Pos;
+out vec2 v_Scale;
+out float v_BorderRadius;
+out float v_BorderWidth;
+
+const vec2 positions[4] = vec2[](
+ vec2(0.0, 0.0),
+ vec2(0.0, 1.0),
+ vec2(1.0, 0.0),
+ vec2(1.0, 1.0)
+);
+
+void main() {
+ vec2 q_Pos = positions[gl_VertexID];
+ vec2 p_Pos = floor(i_Pos * u_Scale);
+ vec2 p_Scale = floor(i_Scale * u_Scale);
+
+ mat4 i_Transform = mat4(
+ vec4(p_Scale.x, 0.0, 0.0, 0.0),
+ vec4(0.0, p_Scale.y, 0.0, 0.0),
+ vec4(0.0, 0.0, 1.0, 0.0),
+ vec4(p_Pos, 0.0, 1.0)
+ );
+
+ v_Color = i_Color;
+ v_BorderColor = i_BorderColor;
+ v_Pos = p_Pos;
+ v_Scale = p_Scale;
+ v_BorderRadius = i_BorderRadius * u_Scale;
+ v_BorderWidth = floor(i_BorderWidth * u_Scale);
+
+ gl_Position = u_Transform * i_Transform * vec4(q_Pos, 0.0, 1.0);
+}
diff --git a/glow/src/shader/triangle.frag b/glow/src/shader/triangle.frag
new file mode 100644
index 00000000..d186784a
--- /dev/null
+++ b/glow/src/shader/triangle.frag
@@ -0,0 +1,9 @@
+#version 330
+
+in vec4 v_Color;
+
+out vec4 o_Color;
+
+void main() {
+ o_Color = v_Color;
+}
diff --git a/glow/src/shader/triangle.vert b/glow/src/shader/triangle.vert
new file mode 100644
index 00000000..5723436a
--- /dev/null
+++ b/glow/src/shader/triangle.vert
@@ -0,0 +1,13 @@
+#version 330
+
+uniform mat4 u_Transform;
+
+layout(location = 0) in vec2 i_Position;
+layout(location = 1) in vec4 i_Color;
+
+out vec4 v_Color;
+
+void main() {
+ gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
+ v_Color = i_Color;
+}
diff --git a/glow/src/text.rs b/glow/src/text.rs
new file mode 100644
index 00000000..6dc7882c
--- /dev/null
+++ b/glow/src/text.rs
@@ -0,0 +1,151 @@
+use crate::Transformation;
+use glow_glyph::ab_glyph;
+use iced_graphics::font;
+use std::{cell::RefCell, collections::HashMap};
+
+#[derive(Debug)]
+pub struct Pipeline {
+ draw_brush: RefCell<glow_glyph::GlyphBrush>,
+ draw_font_map: RefCell<HashMap<String, glow_glyph::FontId>>,
+ measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
+}
+
+impl Pipeline {
+ pub fn new(gl: &glow::Context, default_font: Option<&[u8]>) -> Self {
+ // TODO: Font customization
+ let font_source = font::Source::new();
+
+ let default_font =
+ default_font.map(|slice| slice.to_vec()).unwrap_or_else(|| {
+ font_source
+ .load(&[font::Family::SansSerif, font::Family::Serif])
+ .unwrap_or_else(|_| font::FALLBACK.to_vec())
+ });
+
+ let font = ab_glyph::FontArc::try_from_vec(default_font)
+ .unwrap_or_else(|_| {
+ log::warn!(
+ "System font failed to load. Falling back to \
+ embedded font..."
+ );
+
+ ab_glyph::FontArc::try_from_slice(font::FALLBACK)
+ .expect("Load fallback font")
+ });
+
+ let draw_brush =
+ glow_glyph::GlyphBrushBuilder::using_font(font.clone())
+ .initial_cache_size((2048, 2048))
+ .draw_cache_multithread(false) // TODO: Expose as a configuration flag
+ .build(&gl);
+
+ let measure_brush =
+ glyph_brush::GlyphBrushBuilder::using_font(font).build();
+
+ Pipeline {
+ draw_brush: RefCell::new(draw_brush),
+ draw_font_map: RefCell::new(HashMap::new()),
+ measure_brush: RefCell::new(measure_brush),
+ }
+ }
+
+ pub fn queue(&mut self, section: glow_glyph::Section<'_>) {
+ self.draw_brush.borrow_mut().queue(section);
+ }
+
+ pub fn draw_queued(
+ &mut self,
+ gl: &glow::Context,
+ transformation: Transformation,
+ region: glow_glyph::Region,
+ ) {
+ self.draw_brush
+ .borrow_mut()
+ .draw_queued_with_transform_and_scissoring(
+ gl,
+ transformation.into(),
+ region,
+ )
+ .expect("Draw text");
+ }
+
+ pub fn measure(
+ &self,
+ content: &str,
+ size: f32,
+ font: iced_native::Font,
+ bounds: iced_native::Size,
+ ) -> (f32, f32) {
+ use glow_glyph::GlyphCruncher;
+
+ let glow_glyph::FontId(font_id) = self.find_font(font);
+
+ let section = glow_glyph::Section {
+ bounds: (bounds.width, bounds.height),
+ text: vec![glow_glyph::Text {
+ text: content,
+ scale: size.into(),
+ font_id: glow_glyph::FontId(font_id),
+ extra: glow_glyph::Extra::default(),
+ }],
+ ..Default::default()
+ };
+
+ if let Some(bounds) =
+ self.measure_brush.borrow_mut().glyph_bounds(section)
+ {
+ (bounds.width().ceil(), bounds.height().ceil())
+ } else {
+ (0.0, 0.0)
+ }
+ }
+
+ pub fn trim_measurement_cache(&mut self) {
+ // TODO: We should probably use a `GlyphCalculator` for this. However,
+ // it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
+ // This makes stuff quite inconvenient. A manual method for trimming the
+ // cache would make our lives easier.
+ loop {
+ let action = self
+ .measure_brush
+ .borrow_mut()
+ .process_queued(|_, _| {}, |_| {});
+
+ match action {
+ Ok(_) => break,
+ Err(glyph_brush::BrushError::TextureTooSmall { suggested }) => {
+ let (width, height) = suggested;
+
+ self.measure_brush
+ .borrow_mut()
+ .resize_texture(width, height);
+ }
+ }
+ }
+ }
+
+ pub fn find_font(&self, font: iced_native::Font) -> glow_glyph::FontId {
+ match font {
+ iced_native::Font::Default => glow_glyph::FontId(0),
+ iced_native::Font::External { name, bytes } => {
+ if let Some(font_id) = self.draw_font_map.borrow().get(name) {
+ return *font_id;
+ }
+
+ let font = ab_glyph::FontArc::try_from_slice(bytes)
+ .expect("Load font");
+
+ let _ = self.measure_brush.borrow_mut().add_font(font.clone());
+
+ let font_id = self.draw_brush.borrow_mut().add_font(font);
+
+ let _ = self
+ .draw_font_map
+ .borrow_mut()
+ .insert(String::from(name), font_id);
+
+ font_id
+ }
+ }
+ }
+}
diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs
new file mode 100644
index 00000000..ee7faf83
--- /dev/null
+++ b/glow/src/triangle.rs
@@ -0,0 +1,292 @@
+//! Draw meshes of triangles.
+use crate::program;
+use crate::Transformation;
+use glow::HasContext;
+use iced_graphics::layer;
+use std::marker::PhantomData;
+
+pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
+
+const VERTEX_BUFFER_SIZE: usize = 10_000;
+const INDEX_BUFFER_SIZE: usize = 10_000;
+
+#[derive(Debug)]
+pub(crate) struct Pipeline {
+ program: <glow::Context as HasContext>::Program,
+ vertex_array: <glow::Context as HasContext>::VertexArray,
+ vertices: Buffer<Vertex2D>,
+ indices: Buffer<u32>,
+ transform_location: <glow::Context as HasContext>::UniformLocation,
+ current_transform: Transformation,
+}
+
+impl Pipeline {
+ pub fn new(gl: &glow::Context) -> Pipeline {
+ let program = unsafe {
+ program::create(
+ gl,
+ &[
+ (glow::VERTEX_SHADER, include_str!("shader/triangle.vert")),
+ (
+ glow::FRAGMENT_SHADER,
+ include_str!("shader/triangle.frag"),
+ ),
+ ],
+ )
+ };
+
+ let transform_location =
+ unsafe { gl.get_uniform_location(program, "u_Transform") }
+ .expect("Get transform location");
+
+ unsafe {
+ gl.use_program(Some(program));
+
+ let transform: [f32; 16] = Transformation::identity().into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(transform_location),
+ false,
+ &transform,
+ );
+
+ gl.use_program(None);
+ }
+
+ let vertex_array =
+ unsafe { gl.create_vertex_array().expect("Create vertex array") };
+
+ unsafe {
+ gl.bind_vertex_array(Some(vertex_array));
+ }
+
+ let vertices = unsafe {
+ Buffer::new(
+ gl,
+ glow::ARRAY_BUFFER,
+ glow::DYNAMIC_DRAW,
+ VERTEX_BUFFER_SIZE,
+ )
+ };
+
+ let indices = unsafe {
+ Buffer::new(
+ gl,
+ glow::ELEMENT_ARRAY_BUFFER,
+ glow::DYNAMIC_DRAW,
+ INDEX_BUFFER_SIZE,
+ )
+ };
+
+ unsafe {
+ let stride = std::mem::size_of::<Vertex2D>() as i32;
+
+ gl.enable_vertex_attrib_array(0);
+ gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
+
+ gl.enable_vertex_attrib_array(1);
+ gl.vertex_attrib_pointer_f32(
+ 1,
+ 4,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * 2,
+ );
+
+ gl.bind_vertex_array(None);
+ }
+
+ Pipeline {
+ program,
+ vertex_array,
+ vertices,
+ indices,
+ transform_location,
+ current_transform: Transformation::identity(),
+ }
+ }
+
+ pub fn draw(
+ &mut self,
+ gl: &glow::Context,
+ target_height: u32,
+ transformation: Transformation,
+ scale_factor: f32,
+ meshes: &[layer::Mesh<'_>],
+ ) {
+ unsafe {
+ gl.enable(glow::MULTISAMPLE);
+ gl.enable(glow::SCISSOR_TEST);
+ gl.use_program(Some(self.program));
+ gl.bind_vertex_array(Some(self.vertex_array));
+ }
+
+ // This looks a bit crazy, but we are just counting how many vertices
+ // and indices we will need to handle.
+ // TODO: Improve readability
+ let (total_vertices, total_indices) = meshes
+ .iter()
+ .map(|layer::Mesh { buffers, .. }| {
+ (buffers.vertices.len(), buffers.indices.len())
+ })
+ .fold((0, 0), |(total_v, total_i), (v, i)| {
+ (total_v + v, total_i + i)
+ });
+
+ // Then we ensure the current buffers are big enough, resizing if
+ // necessary
+ unsafe {
+ self.vertices.bind(gl, total_vertices);
+ self.indices.bind(gl, total_indices);
+ }
+
+ // We upload all the vertices and indices upfront
+ let mut last_vertex = 0;
+ let mut last_index = 0;
+
+ for layer::Mesh { buffers, .. } in meshes {
+ unsafe {
+ gl.buffer_sub_data_u8_slice(
+ glow::ARRAY_BUFFER,
+ (last_vertex * std::mem::size_of::<Vertex2D>()) as i32,
+ bytemuck::cast_slice(&buffers.vertices),
+ );
+
+ gl.buffer_sub_data_u8_slice(
+ glow::ELEMENT_ARRAY_BUFFER,
+ (last_index * std::mem::size_of::<u32>()) as i32,
+ bytemuck::cast_slice(&buffers.indices),
+ );
+
+ last_vertex += buffers.vertices.len();
+ last_index += buffers.indices.len();
+ }
+ }
+
+ // Then we draw each mesh using offsets
+ let mut last_vertex = 0;
+ let mut last_index = 0;
+
+ for layer::Mesh {
+ buffers,
+ origin,
+ clip_bounds,
+ } in meshes
+ {
+ let transform =
+ transformation * Transformation::translate(origin.x, origin.y);
+
+ let clip_bounds = (*clip_bounds * scale_factor).round();
+
+ unsafe {
+ if self.current_transform != transform {
+ let matrix: [f32; 16] = transform.into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(self.transform_location),
+ false,
+ &matrix,
+ );
+
+ self.current_transform = transform;
+ }
+
+ gl.scissor(
+ clip_bounds.x as i32,
+ (target_height - (clip_bounds.y + clip_bounds.height))
+ as i32,
+ clip_bounds.width as i32,
+ clip_bounds.height as i32,
+ );
+
+ gl.draw_elements_base_vertex(
+ glow::TRIANGLES,
+ buffers.indices.len() as i32,
+ glow::UNSIGNED_INT,
+ (last_index * std::mem::size_of::<u32>()) as i32,
+ last_vertex as i32,
+ );
+
+ last_vertex += buffers.vertices.len();
+ last_index += buffers.indices.len();
+ }
+ }
+
+ unsafe {
+ gl.bind_vertex_array(None);
+ gl.use_program(None);
+ gl.disable(glow::SCISSOR_TEST);
+ gl.disable(glow::MULTISAMPLE);
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+struct Uniforms {
+ transform: [f32; 16],
+}
+
+unsafe impl bytemuck::Zeroable for Uniforms {}
+unsafe impl bytemuck::Pod for Uniforms {}
+
+impl Default for Uniforms {
+ fn default() -> Self {
+ Self {
+ transform: *Transformation::identity().as_ref(),
+ }
+ }
+}
+
+impl From<Transformation> for Uniforms {
+ fn from(transformation: Transformation) -> Uniforms {
+ Self {
+ transform: transformation.into(),
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Buffer<T> {
+ raw: <glow::Context as HasContext>::Buffer,
+ target: u32,
+ usage: u32,
+ size: usize,
+ phantom: PhantomData<T>,
+}
+
+impl<T> Buffer<T> {
+ pub unsafe fn new(
+ gl: &glow::Context,
+ target: u32,
+ usage: u32,
+ size: usize,
+ ) -> Self {
+ let raw = gl.create_buffer().expect("Create buffer");
+
+ let mut buffer = Buffer {
+ raw,
+ target,
+ usage,
+ size: 0,
+ phantom: PhantomData,
+ };
+
+ buffer.bind(gl, size);
+
+ buffer
+ }
+
+ pub unsafe fn bind(&mut self, gl: &glow::Context, size: usize) {
+ gl.bind_buffer(self.target, Some(self.raw));
+
+ if self.size < size {
+ gl.buffer_data_size(
+ self.target,
+ (size * std::mem::size_of::<T>()) as i32,
+ self.usage,
+ );
+
+ self.size = size;
+ }
+ }
+}
diff --git a/glow/src/widget.rs b/glow/src/widget.rs
new file mode 100644
index 00000000..9968092b
--- /dev/null
+++ b/glow/src/widget.rs
@@ -0,0 +1,58 @@
+//! Use the widgets supported out-of-the-box.
+//!
+//! # Re-exports
+//! For convenience, the contents of this module are available at the root
+//! module. Therefore, you can directly type:
+//!
+//! ```
+//! use iced_glow::{button, Button};
+//! ```
+use crate::Renderer;
+
+pub mod button;
+pub mod checkbox;
+pub mod container;
+pub mod pane_grid;
+pub mod progress_bar;
+pub mod radio;
+pub mod scrollable;
+pub mod slider;
+pub mod text_input;
+
+#[doc(no_inline)]
+pub use button::Button;
+#[doc(no_inline)]
+pub use checkbox::Checkbox;
+#[doc(no_inline)]
+pub use container::Container;
+#[doc(no_inline)]
+pub use pane_grid::PaneGrid;
+#[doc(no_inline)]
+pub use progress_bar::ProgressBar;
+#[doc(no_inline)]
+pub use radio::Radio;
+#[doc(no_inline)]
+pub use scrollable::Scrollable;
+#[doc(no_inline)]
+pub use slider::Slider;
+#[doc(no_inline)]
+pub use text_input::TextInput;
+
+#[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};
+
+/// A container that distributes its contents vertically.
+pub type Column<'a, Message> = iced_native::Column<'a, Message, Renderer>;
+
+/// A container that distributes its contents horizontally.
+pub type Row<'a, Message> = iced_native::Row<'a, Message, Renderer>;
+
+/// A paragraph of text.
+pub type Text = iced_native::Text<Renderer>;
diff --git a/glow/src/widget/button.rs b/glow/src/widget/button.rs
new file mode 100644
index 00000000..fee7a7f8
--- /dev/null
+++ b/glow/src/widget/button.rs
@@ -0,0 +1,15 @@
+//! Allow your users to perform actions by pressing a button.
+//!
+//! A [`Button`] has some local [`State`].
+//!
+//! [`Button`]: type.Button.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_graphics::button::{Style, StyleSheet};
+pub use iced_native::button::State;
+
+/// A widget that produces a message when clicked.
+///
+/// This is an alias of an `iced_native` button with an `iced_wgpu::Renderer`.
+pub type Button<'a, Message> = iced_native::Button<'a, Message, Renderer>;
diff --git a/glow/src/widget/canvas.rs b/glow/src/widget/canvas.rs
new file mode 100644
index 00000000..bef34857
--- /dev/null
+++ b/glow/src/widget/canvas.rs
@@ -0,0 +1,9 @@
+//! Draw 2D graphics for your users.
+//!
+//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a
+//! [`Frame`]. It can be used for animation, data visualization, game graphics,
+//! and more!
+//!
+//! [`Canvas`]: struct.Canvas.html
+//! [`Frame`]: struct.Frame.html
+pub use iced_graphics::canvas::*;
diff --git a/glow/src/widget/checkbox.rs b/glow/src/widget/checkbox.rs
new file mode 100644
index 00000000..d27d77cc
--- /dev/null
+++ b/glow/src/widget/checkbox.rs
@@ -0,0 +1,9 @@
+//! Show toggle controls using checkboxes.
+use crate::Renderer;
+
+pub use iced_graphics::checkbox::{Style, StyleSheet};
+
+/// A box that can be checked.
+///
+/// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`.
+pub type Checkbox<Message> = iced_native::Checkbox<Message, Renderer>;
diff --git a/glow/src/widget/container.rs b/glow/src/widget/container.rs
new file mode 100644
index 00000000..bc26cef2
--- /dev/null
+++ b/glow/src/widget/container.rs
@@ -0,0 +1,10 @@
+//! Decorate content and apply alignment.
+use crate::Renderer;
+
+pub use iced_graphics::container::{Style, StyleSheet};
+
+/// An element decorating some content.
+///
+/// This is an alias of an `iced_native` container with a default
+/// `Renderer`.
+pub type Container<'a, Message> = iced_native::Container<'a, Message, Renderer>;
diff --git a/glow/src/widget/pane_grid.rs b/glow/src/widget/pane_grid.rs
new file mode 100644
index 00000000..578e8960
--- /dev/null
+++ b/glow/src/widget/pane_grid.rs
@@ -0,0 +1,24 @@
+//! Let your users split regions of your application and organize layout dynamically.
+//!
+//! [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish)
+//!
+//! # Example
+//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
+//! drag and drop, and hotkey support.
+//!
+//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.1/examples/pane_grid
+//! [`PaneGrid`]: type.PaneGrid.html
+use crate::Renderer;
+
+pub use iced_native::pane_grid::{
+ Axis, Direction, DragEvent, Focus, KeyPressEvent, Pane, ResizeEvent, Split,
+ State,
+};
+
+/// A collection of panes distributed using either vertical or horizontal splits
+/// to completely fill the space available.
+///
+/// [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish)
+///
+/// This is an alias of an `iced_native` pane grid with an `iced_wgpu::Renderer`.
+pub type PaneGrid<'a, Message> = iced_native::PaneGrid<'a, Message, Renderer>;
diff --git a/glow/src/widget/progress_bar.rs b/glow/src/widget/progress_bar.rs
new file mode 100644
index 00000000..5782103c
--- /dev/null
+++ b/glow/src/widget/progress_bar.rs
@@ -0,0 +1,15 @@
+//! Allow your users to perform actions by pressing a button.
+//!
+//! A [`Button`] has some local [`State`].
+//!
+//! [`Button`]: type.Button.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_graphics::progress_bar::{Style, StyleSheet};
+
+/// A bar that displays progress.
+///
+/// This is an alias of an `iced_native` progress bar with an
+/// `iced_wgpu::Renderer`.
+pub type ProgressBar = iced_native::ProgressBar<Renderer>;
diff --git a/glow/src/widget/radio.rs b/glow/src/widget/radio.rs
new file mode 100644
index 00000000..0b843d1f
--- /dev/null
+++ b/glow/src/widget/radio.rs
@@ -0,0 +1,10 @@
+//! Create choices using radio buttons.
+use crate::Renderer;
+
+pub use iced_graphics::radio::{Style, StyleSheet};
+
+/// A circular button representing a choice.
+///
+/// This is an alias of an `iced_native` radio button with an
+/// `iced_wgpu::Renderer`.
+pub type Radio<Message> = iced_native::Radio<Message, Renderer>;
diff --git a/glow/src/widget/scrollable.rs b/glow/src/widget/scrollable.rs
new file mode 100644
index 00000000..fabb4318
--- /dev/null
+++ b/glow/src/widget/scrollable.rs
@@ -0,0 +1,13 @@
+//! Navigate an endless amount of content with a scrollbar.
+use crate::Renderer;
+
+pub use iced_graphics::scrollable::{Scrollbar, Scroller, StyleSheet};
+pub use iced_native::scrollable::State;
+
+/// A widget that can vertically display an infinite amount of content
+/// with a scrollbar.
+///
+/// This is an alias of an `iced_native` scrollable with a default
+/// `Renderer`.
+pub type Scrollable<'a, Message> =
+ iced_native::Scrollable<'a, Message, Renderer>;
diff --git a/glow/src/widget/slider.rs b/glow/src/widget/slider.rs
new file mode 100644
index 00000000..cf036829
--- /dev/null
+++ b/glow/src/widget/slider.rs
@@ -0,0 +1,16 @@
+//! Display an interactive selector of a single value from a range of values.
+//!
+//! A [`Slider`] has some local [`State`].
+//!
+//! [`Slider`]: struct.Slider.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_graphics::slider::{Handle, HandleShape, Style, StyleSheet};
+pub use iced_native::slider::State;
+
+/// An horizontal bar and a handle that selects a single value from a range of
+/// values.
+///
+/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`.
+pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Renderer>;
diff --git a/glow/src/widget/text_input.rs b/glow/src/widget/text_input.rs
new file mode 100644
index 00000000..1da3fbe6
--- /dev/null
+++ b/glow/src/widget/text_input.rs
@@ -0,0 +1,15 @@
+//! Display fields that can be filled with text.
+//!
+//! A [`TextInput`] has some local [`State`].
+//!
+//! [`TextInput`]: struct.TextInput.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_graphics::text_input::{Style, StyleSheet};
+pub use iced_native::text_input::State;
+
+/// A field that can be filled with text.
+///
+/// This is an alias of an `iced_native` text input with an `iced_wgpu::Renderer`.
+pub type TextInput<'a, Message> = iced_native::TextInput<'a, Message, Renderer>;
diff --git a/glow/src/window.rs b/glow/src/window.rs
new file mode 100644
index 00000000..aac5fb9e
--- /dev/null
+++ b/glow/src/window.rs
@@ -0,0 +1,4 @@
+//! Display rendering results on windows.
+mod compositor;
+
+pub use compositor::Compositor;
diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs
new file mode 100644
index 00000000..2f504ff7
--- /dev/null
+++ b/glow/src/window/compositor.rs
@@ -0,0 +1,74 @@
+use crate::{Backend, Renderer, Settings, Viewport};
+
+use core::ffi::c_void;
+use glow::HasContext;
+use iced_graphics::{Antialiasing, Size};
+use iced_native::mouse;
+
+/// A window graphics backend for iced powered by `glow`.
+#[allow(missing_debug_implementations)]
+pub struct Compositor {
+ gl: glow::Context,
+}
+
+impl iced_graphics::window::GLCompositor for Compositor {
+ type Settings = Settings;
+ type Renderer = Renderer;
+
+ 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);
+
+ 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);
+
+ // Disable multisampling by default
+ gl.disable(glow::MULTISAMPLE);
+
+ let renderer = Renderer::new(Backend::new(&gl, settings));
+
+ (Self { gl }, renderer)
+ }
+
+ fn sample_count(settings: &Settings) -> u32 {
+ settings
+ .antialiasing
+ .map(Antialiasing::sample_count)
+ .unwrap_or(0)
+ }
+
+ fn resize_viewport(&mut self, physical_size: Size<u32>) {
+ unsafe {
+ 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,
+ viewport: &Viewport,
+ output: &<Self::Renderer as iced_native::Renderer>::Output,
+ overlay: &[T],
+ ) -> mouse::Interaction {
+ let gl = &self.gl;
+
+ unsafe {
+ gl.clear(glow::COLOR_BUFFER_BIT);
+ }
+
+ renderer.backend_mut().draw(gl, viewport, output, overlay)
+ }
+}