summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-10-25 03:47:34 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-10-25 03:47:34 +0200
commit719c073fc67c87d6b2da1bc01b74751d3f5e59f0 (patch)
tree660613d215cafc01a30911c22a5ba107fb61b12c
parent4769272122e8cd0a4d666bb06c00cb27f8cad3c4 (diff)
downloadiced-719c073fc67c87d6b2da1bc01b74751d3f5e59f0.tar.gz
iced-719c073fc67c87d6b2da1bc01b74751d3f5e59f0.tar.bz2
iced-719c073fc67c87d6b2da1bc01b74751d3f5e59f0.zip
Draft `Scrollable` widget (no clipping yet!)
-rw-r--r--core/src/widget.rs4
-rw-r--r--core/src/widget/scrollable.rs122
-rw-r--r--examples/scroll.rs69
-rw-r--r--native/src/user_interface.rs2
-rw-r--r--native/src/widget.rs3
-rw-r--r--native/src/widget/scrollable.rs121
-rw-r--r--src/winit.rs4
-rw-r--r--wgpu/Cargo.toml1
-rw-r--r--wgpu/src/primitive.rs5
-rw-r--r--wgpu/src/renderer.rs97
-rw-r--r--wgpu/src/renderer/scrollable.rs70
-rw-r--r--wgpu/src/transformation.rs67
-rw-r--r--winit/src/application.rs11
13 files changed, 526 insertions, 50 deletions
diff --git a/core/src/widget.rs b/core/src/widget.rs
index f9d4bf2a..3ee8e347 100644
--- a/core/src/widget.rs
+++ b/core/src/widget.rs
@@ -14,6 +14,7 @@ mod radio;
mod row;
pub mod button;
+pub mod scrollable;
pub mod slider;
pub mod text;
@@ -26,6 +27,9 @@ pub use slider::Slider;
#[doc(no_inline)]
pub use text::Text;
+#[doc(no_inline)]
+pub use scrollable::Scrollable;
+
pub use checkbox::Checkbox;
pub use column::Column;
pub use image::Image;
diff --git a/core/src/widget/scrollable.rs b/core/src/widget/scrollable.rs
new file mode 100644
index 00000000..dd62b658
--- /dev/null
+++ b/core/src/widget/scrollable.rs
@@ -0,0 +1,122 @@
+use crate::{Align, Column, Justify, Length};
+
+#[derive(Debug)]
+pub struct Scrollable<'a, Element> {
+ pub state: &'a mut State,
+ pub height: Length,
+ pub align_self: Option<Align>,
+ pub align_items: Align,
+ pub content: Column<Element>,
+}
+
+impl<'a, Element> Scrollable<'a, Element> {
+ pub fn new(state: &'a mut State) -> Self {
+ Scrollable {
+ state,
+ height: Length::Shrink,
+ align_self: None,
+ align_items: Align::Start,
+ content: Column::new(),
+ }
+ }
+
+ /// Sets the vertical spacing _between_ elements.
+ ///
+ /// Custom margins per element do not exist in Iced. You should use this
+ /// method instead! While less flexible, it helps you keep spacing between
+ /// elements consistent.
+ pub fn spacing(mut self, units: u16) -> Self {
+ self.content = self.content.spacing(units);
+ self
+ }
+
+ /// Sets the padding of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn padding(mut self, units: u16) -> Self {
+ self.content = self.content.padding(units);
+ self
+ }
+
+ /// Sets the width of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.content = self.content.width(width);
+ self
+ }
+
+ /// Sets the height of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the maximum width of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn max_width(mut self, max_width: Length) -> Self {
+ self.content = self.content.max_width(max_width);
+ self
+ }
+
+ /// Sets the maximum height of the [`Column`] in pixels.
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn max_height(mut self, max_height: Length) -> Self {
+ self.content = self.content.max_height(max_height);
+ self
+ }
+
+ /// Sets the alignment of the [`Column`] itself.
+ ///
+ /// This is useful if you want to override the default alignment given by
+ /// the parent container.
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn align_self(mut self, align: Align) -> Self {
+ self.align_self = Some(align);
+ self
+ }
+
+ /// Sets the horizontal alignment of the contents of the [`Column`] .
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn align_items(mut self, align_items: Align) -> Self {
+ self.align_items = align_items;
+ self
+ }
+
+ /// Sets the vertical distribution strategy for the contents of the
+ /// [`Column`] .
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn justify_content(mut self, justify: Justify) -> Self {
+ self.content = self.content.justify_content(justify);
+ self
+ }
+
+ /// Adds an element to the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn push<E>(mut self, child: E) -> Scrollable<'a, Element>
+ where
+ E: Into<Element>,
+ {
+ self.content = self.content.push(child);
+ self
+ }
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct State {
+ pub offset: u32,
+}
+
+impl State {
+ pub fn new() -> Self {
+ State::default()
+ }
+}
diff --git a/examples/scroll.rs b/examples/scroll.rs
new file mode 100644
index 00000000..648b1d8f
--- /dev/null
+++ b/examples/scroll.rs
@@ -0,0 +1,69 @@
+use iced::{
+ button, scrollable, Align, Application, Button, Color, Element, Image,
+ Length, Scrollable, Text,
+};
+
+pub fn main() {
+ Example::default().run()
+}
+
+#[derive(Default)]
+struct Example {
+ paragraph_count: u16,
+
+ scroll: scrollable::State,
+ add_button: button::State,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Message {
+ AddParagraph,
+}
+
+impl Application for Example {
+ type Message = Message;
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::AddParagraph => {
+ self.paragraph_count += 1;
+ }
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ let content = Scrollable::new(&mut self.scroll)
+ .width(Length::Fill)
+ .max_width(Length::Units(600))
+ .spacing(20)
+ .padding(20)
+ .align_self(Align::Center);
+
+ //let content = (0..self.paragraph_count)
+ // .fold(content, |column, _| column.push(lorem_ipsum()))
+ // .push(
+ // Button::new(&mut self.add_button, Text::new("Add paragraph"))
+ // .on_press(Message::AddParagraph)
+ // .padding(20)
+ // .border_radius(5)
+ // .align_self(Align::Center),
+ // );
+
+ (0..10)
+ .fold(content, |content, _| {
+ content.push(
+ Image::new(format!(
+ "{}/examples/resources/ferris.png",
+ env!("CARGO_MANIFEST_DIR")
+ ))
+ .width(Length::Units(400))
+ .align_self(Align::Center),
+ )
+ })
+ .into()
+ }
+}
+
+fn lorem_ipsum() -> Text {
+ Text::new("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi in dui vel massa blandit interdum. Quisque placerat, odio ut vulputate sagittis, augue est facilisis ex, eget euismod felis magna in sapien. Nullam luctus consequat massa, ac interdum mauris blandit pellentesque. Nullam in est urna. Aliquam tristique lectus ac luctus feugiat. Aenean libero diam, euismod facilisis consequat quis, pellentesque luctus erat. Praesent vel tincidunt elit.")
+}
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 5675076d..5df0dc6a 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -347,7 +347,7 @@ impl Cache {
.0
.compute_layout(geometry::Size::undefined())
.unwrap(),
- cursor_position: Point::new(0.0, 0.0),
+ cursor_position: Point::new(-1.0, -1.0),
}
}
}
diff --git a/native/src/widget.rs b/native/src/widget.rs
index bcef2665..0d3f6d2c 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -26,6 +26,7 @@ pub mod column;
pub mod image;
pub mod radio;
pub mod row;
+pub mod scrollable;
pub mod slider;
pub mod text;
@@ -42,6 +43,8 @@ pub use radio::Radio;
#[doc(no_inline)]
pub use row::Row;
#[doc(no_inline)]
+pub use scrollable::Scrollable;
+#[doc(no_inline)]
pub use slider::Slider;
#[doc(no_inline)]
pub use text::Text;
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
new file mode 100644
index 00000000..4644282b
--- /dev/null
+++ b/native/src/widget/scrollable.rs
@@ -0,0 +1,121 @@
+use crate::{
+ column, input::mouse, Element, Event, Hasher, Layout, Node, Point, Style,
+ Widget,
+};
+
+pub use iced_core::scrollable::State;
+
+/// A scrollable [`Column`].
+///
+/// [`Column`]: ../column/struct.Column.html
+pub type Scrollable<'a, Message, Renderer> =
+ iced_core::Scrollable<'a, Element<'a, Message, Renderer>>;
+
+impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for Scrollable<'a, Message, Renderer>
+where
+ Renderer: self::Renderer + column::Renderer,
+{
+ fn node(&self, renderer: &Renderer) -> Node {
+ let mut content = self.content.node(renderer);
+
+ {
+ let mut style = content.0.style();
+ style.flex_shrink = 0.0;
+
+ content.0.set_style(style);
+ }
+
+ let mut style = Style::default()
+ .width(self.content.width)
+ .max_width(self.content.max_width)
+ .height(self.height)
+ .align_self(self.align_self)
+ .align_items(self.align_items);
+
+ style.0.flex_direction = stretch::style::FlexDirection::Column;
+
+ Node::with_children(style, vec![content])
+ }
+
+ fn on_event(
+ &mut self,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ messages: &mut Vec<Message>,
+ ) {
+ let bounds = layout.bounds();
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ let content = layout.children().next().unwrap();
+ let content_bounds = content.bounds();
+
+ if is_mouse_over {
+ match event {
+ Event::Mouse(mouse::Event::WheelScrolled {
+ delta_y, ..
+ }) => {
+ // TODO: Configurable speed (?)
+ self.state.offset = (self.state.offset as i32
+ - delta_y.round() as i32 * 15)
+ .max(0)
+ .min((content_bounds.height - bounds.height) as i32)
+ as u32;
+ }
+ _ => {}
+ }
+ }
+
+ let cursor_position = if is_mouse_over {
+ Point::new(
+ cursor_position.x,
+ cursor_position.y + self.state.offset as f32,
+ )
+ } else {
+ Point::new(cursor_position.x, -1.0)
+ };
+
+ self.content.on_event(
+ event,
+ layout.children().next().unwrap(),
+ cursor_position,
+ messages,
+ )
+ }
+
+ fn draw(
+ &self,
+ renderer: &mut Renderer,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) -> Renderer::Output {
+ self::Renderer::draw(renderer, &self, layout, cursor_position)
+ }
+
+ fn hash_layout(&self, state: &mut Hasher) {
+ self.content.hash_layout(state)
+ }
+}
+
+pub trait Renderer: crate::Renderer + Sized {
+ fn draw<Message>(
+ &mut self,
+ scrollable: &Scrollable<'_, Message, Self>,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) -> Self::Output;
+}
+
+impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
+ for Element<'a, Message, Renderer>
+where
+ Renderer: 'a + self::Renderer + column::Renderer,
+ Message: 'static,
+{
+ fn from(
+ scrollable: Scrollable<'a, Message, Renderer>,
+ ) -> Element<'a, Message, Renderer> {
+ Element::new(scrollable)
+ }
+}
diff --git a/src/winit.rs b/src/winit.rs
index 64e301f4..b2200551 100644
--- a/src/winit.rs
+++ b/src/winit.rs
@@ -1,8 +1,8 @@
pub use iced_wgpu::{Primitive, Renderer};
pub use iced_winit::{
- button, slider, text, winit, Align, Background, Checkbox, Color, Image,
- Justify, Length, Radio, Slider, Text,
+ button, scrollable, slider, text, winit, Align, Background, Checkbox,
+ Color, Image, Justify, Length, Radio, Scrollable, Slider, Text,
};
pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>;
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index cac5e113..ffb15ea2 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -13,4 +13,5 @@ wgpu = { version = "0.3", git = "https://github.com/gfx-rs/wgpu-rs", rev = "cb25
wgpu_glyph = { version = "0.4", git = "https://github.com/hecrj/wgpu_glyph", rev = "48daa98f5f785963838b4345e86ac40eac095ba9" }
raw-window-handle = "0.1"
image = "0.22"
+nalgebra = "0.18"
log = "0.4"
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
index 0b9e2c41..cdd87894 100644
--- a/wgpu/src/primitive.rs
+++ b/wgpu/src/primitive.rs
@@ -23,4 +23,9 @@ pub enum Primitive {
path: String,
bounds: Rectangle,
},
+ Scrollable {
+ bounds: Rectangle,
+ offset: u32,
+ content: Box<Primitive>,
+ },
}
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
index ab6f744f..bb0e7b27 100644
--- a/wgpu/src/renderer.rs
+++ b/wgpu/src/renderer.rs
@@ -20,6 +20,7 @@ mod column;
mod image;
mod radio;
mod row;
+mod scrollable;
mod slider;
mod text;
@@ -31,8 +32,6 @@ pub struct Renderer {
quad_pipeline: quad::Pipeline,
image_pipeline: crate::image::Pipeline,
- quads: Vec<Quad>,
- images: Vec<Image>,
glyph_brush: Rc<RefCell<GlyphBrush<'static, ()>>>,
}
@@ -43,6 +42,24 @@ pub struct Target {
swap_chain: SwapChain,
}
+pub struct Layer {
+ quads: Vec<Quad>,
+ images: Vec<Image>,
+ layers: Vec<Layer>,
+ y_offset: u32,
+}
+
+impl Layer {
+ pub fn new(y_offset: u32) -> Self {
+ Self {
+ quads: Vec::new(),
+ images: Vec::new(),
+ layers: Vec::new(),
+ y_offset,
+ }
+ }
+}
+
impl Renderer {
fn new<W: HasRawWindowHandle>(window: &W) -> Self {
let adapter = Adapter::request(&RequestAdapterOptions {
@@ -79,8 +96,6 @@ impl Renderer {
quad_pipeline,
image_pipeline,
- quads: Vec::new(),
- images: Vec::new(),
glyph_brush: Rc::new(RefCell::new(glyph_brush)),
}
}
@@ -132,27 +147,10 @@ impl Renderer {
depth_stencil_attachment: None,
});
- self.draw_primitive(primitive);
+ let mut layer = Layer::new(0);
- self.quad_pipeline.draw(
- &mut self.device,
- &mut encoder,
- &self.quads,
- target.transformation,
- &frame.view,
- );
-
- self.quads.clear();
-
- self.image_pipeline.draw(
- &mut self.device,
- &mut encoder,
- &self.images,
- target.transformation,
- &frame.view,
- );
-
- self.images.clear();
+ self.draw_primitive(primitive, &mut layer);
+ self.flush(target.transformation, &layer, &mut encoder, &frame.view);
self.glyph_brush
.borrow_mut()
@@ -170,13 +168,13 @@ impl Renderer {
*mouse_cursor
}
- fn draw_primitive(&mut self, primitive: &Primitive) {
+ fn draw_primitive(&mut self, primitive: &Primitive, layer: &mut Layer) {
match primitive {
Primitive::None => {}
Primitive::Group { primitives } => {
// TODO: Inspect a bit and regroup (?)
for primitive in primitives {
- self.draw_primitive(primitive)
+ self.draw_primitive(primitive, layer)
}
}
Primitive::Text {
@@ -244,7 +242,7 @@ impl Renderer {
background,
border_radius,
} => {
- self.quads.push(Quad {
+ layer.quads.push(Quad {
position: [bounds.x, bounds.y],
scale: [bounds.width, bounds.height],
color: match background {
@@ -254,12 +252,55 @@ impl Renderer {
});
}
Primitive::Image { path, bounds } => {
- self.images.push(Image {
+ layer.images.push(Image {
path: path.clone(),
position: [bounds.x, bounds.y],
scale: [bounds.width, bounds.height],
});
}
+ Primitive::Scrollable {
+ bounds,
+ offset,
+ content,
+ } => {
+ let mut new_layer = Layer::new(layer.y_offset + offset);
+
+ // TODO: Primitive culling
+ self.draw_primitive(content, &mut new_layer);
+
+ layer.layers.push(new_layer);
+ }
+ }
+ }
+
+ fn flush(
+ &mut self,
+ transformation: Transformation,
+ layer: &Layer,
+ encoder: &mut wgpu::CommandEncoder,
+ target: &wgpu::TextureView,
+ ) {
+ let translated = transformation
+ * Transformation::translate(0.0, -(layer.y_offset as f32));
+
+ self.quad_pipeline.draw(
+ &mut self.device,
+ encoder,
+ &layer.quads,
+ transformation,
+ target,
+ );
+
+ self.image_pipeline.draw(
+ &mut self.device,
+ encoder,
+ &layer.images,
+ translated,
+ target,
+ );
+
+ for layer in layer.layers.iter() {
+ self.flush(transformation, layer, encoder, target);
}
}
}
diff --git a/wgpu/src/renderer/scrollable.rs b/wgpu/src/renderer/scrollable.rs
new file mode 100644
index 00000000..14ff9ff4
--- /dev/null
+++ b/wgpu/src/renderer/scrollable.rs
@@ -0,0 +1,70 @@
+use crate::{Primitive, Renderer};
+use iced_native::{
+ scrollable, Background, Color, Layout, Point, Rectangle, Scrollable, Widget,
+};
+
+impl scrollable::Renderer for Renderer {
+ fn draw<Message>(
+ &mut self,
+ scrollable: &Scrollable<'_, Message, Self>,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) -> Self::Output {
+ let bounds = layout.bounds();
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ let content = layout.children().next().unwrap();
+ let content_bounds = content.bounds();
+
+ let cursor_position = if bounds.contains(cursor_position) {
+ Point::new(
+ cursor_position.x,
+ cursor_position.y + scrollable.state.offset as f32,
+ )
+ } else {
+ Point::new(cursor_position.x, -1.0)
+ };
+
+ let (content, mouse_cursor) =
+ scrollable.content.draw(self, content, cursor_position);
+
+ let primitive = Primitive::Scrollable {
+ bounds,
+ offset: scrollable.state.offset,
+ content: Box::new(content),
+ };
+
+ (
+ Primitive::Group {
+ primitives: if is_mouse_over
+ && content_bounds.height > bounds.height
+ {
+ let ratio = bounds.height / content_bounds.height;
+ let scrollbar_height = bounds.height * ratio;
+ let y_offset = scrollable.state.offset as f32 * ratio;
+
+ let scrollbar = Primitive::Quad {
+ bounds: Rectangle {
+ x: bounds.x + bounds.width - 12.0,
+ y: bounds.y + y_offset,
+ width: 10.0,
+ height: scrollbar_height,
+ },
+ background: Background::Color(Color {
+ r: 0.0,
+ g: 0.0,
+ b: 0.0,
+ a: 0.5,
+ }),
+ border_radius: 5,
+ };
+
+ vec![primitive, scrollbar]
+ } else {
+ vec![primitive]
+ },
+ },
+ mouse_cursor,
+ )
+ }
+}
diff --git a/wgpu/src/transformation.rs b/wgpu/src/transformation.rs
index 1101e135..ed80b31a 100644
--- a/wgpu/src/transformation.rs
+++ b/wgpu/src/transformation.rs
@@ -1,30 +1,59 @@
-#[derive(Debug, Clone, Copy)]
-pub struct Transformation([f32; 16]);
+use nalgebra::Matrix3;
+use std::ops::Mul;
+
+/// A 2D transformation matrix.
+///
+/// It can be used to apply a transformation to a [`Target`].
+///
+/// [`Target`]: struct.Target.html
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Transformation(Matrix3<f32>);
impl Transformation {
- #[rustfmt::skip]
- pub fn identity() -> Self {
- Transformation([
- 1.0, 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0, 0.0,
- 0.0, 0.0, 1.0, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- ])
+ /// Get the identity transformation.
+ pub fn identity() -> Transformation {
+ Transformation(Matrix3::identity())
}
+ /// Creates an orthographic projection.
+ ///
+ /// You should rarely need this. On creation, a [`Target`] is automatically
+ /// set up with the correct orthographic projection.
+ ///
+ /// [`Target`]: struct.Target.html
#[rustfmt::skip]
- pub fn orthographic(width: u16, height: u16) -> Self {
- Transformation([
- 2.0 / width as f32, 0.0, 0.0, 0.0,
- 0.0, 2.0 / height as f32, 0.0, 0.0,
- 0.0, 0.0, 1.0, 0.0,
- -1.0, -1.0, 0.0, 1.0,
- ])
+ pub fn orthographic(width: u16, height: u16) -> Transformation {
+ Transformation(nalgebra::Matrix3::new(
+ 2.0 / f32::from(width), 0.0, -1.0,
+ 0.0, 2.0 / f32::from(height), -1.0,
+ 0.0, 0.0, 1.0
+ ))
+ }
+
+ /// Creates a translate transformation.
+ ///
+ /// You can use this to pan your camera, for example.
+ pub fn translate(x: f32, y: f32) -> Transformation {
+ Transformation(Matrix3::new_translation(&nalgebra::Vector2::new(x, y)))
+ }
+}
+
+impl Mul for Transformation {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self {
+ Transformation(self.0 * rhs.0)
}
}
impl From<Transformation> for [f32; 16] {
- fn from(transformation: Transformation) -> [f32; 16] {
- transformation.0
+ #[rustfmt::skip]
+ fn from(t: Transformation) -> [f32; 16] {
+ [
+ t.0[0], t.0[1], 0.0, t.0[2],
+ t.0[3], t.0[4], 0.0, t.0[5],
+ 0.0, 0.0, -1.0, 0.0,
+ t.0[6], t.0[7], 0.0, t.0[8]
+ ]
}
}
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 418ee3c4..870f4868 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -136,6 +136,17 @@ pub trait Application {
state: conversion::button_state(state),
}));
}
+ WindowEvent::MouseWheel { delta, .. } => match delta {
+ winit::event::MouseScrollDelta::LineDelta(
+ delta_x,
+ delta_y,
+ ) => {
+ events.push(Event::Mouse(
+ mouse::Event::WheelScrolled { delta_x, delta_y },
+ ));
+ }
+ _ => {}
+ },
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}