summaryrefslogtreecommitdiffstats
path: root/native/src
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2020-05-28 21:52:34 +0200
committerLibravatar GitHub <noreply@github.com>2020-05-28 21:52:34 +0200
commitd3db055583f4cbef1441fd66d07da70424bd1200 (patch)
tree9f695bd26f688a5aaf3b8fa687a0e3ff096ffe11 /native/src
parentead4186870d1b46015986f702dd63382498060fc (diff)
parent709ed1f3f7ad8cf67a176763e394aaae4e808e93 (diff)
downloadiced-d3db055583f4cbef1441fd66d07da70424bd1200.tar.gz
iced-d3db055583f4cbef1441fd66d07da70424bd1200.tar.bz2
iced-d3db055583f4cbef1441fd66d07da70424bd1200.zip
Merge pull request #354 from hecrj/feature/glow-renderer
OpenGL renderer and backend-agnostic graphics subcrate
Diffstat (limited to '')
-rw-r--r--graphics/src/window/compositor.rs (renamed from native/src/window/backend.rs)16
-rw-r--r--native/src/debug/basic.rs (renamed from winit/src/debug/basic.rs)5
-rw-r--r--native/src/debug/null.rs (renamed from winit/src/debug/null.rs)1
-rw-r--r--native/src/lib.rs18
-rw-r--r--native/src/program.rs39
-rw-r--r--native/src/program/state.rs185
-rw-r--r--native/src/user_interface.rs4
-rw-r--r--native/src/window.rs2
8 files changed, 254 insertions, 16 deletions
diff --git a/native/src/window/backend.rs b/graphics/src/window/compositor.rs
index 892d4bb9..d5920c95 100644
--- a/native/src/window/backend.rs
+++ b/graphics/src/window/compositor.rs
@@ -1,14 +1,14 @@
-use crate::mouse;
-
+use crate::Viewport;
+use iced_native::mouse;
use raw_window_handle::HasRawWindowHandle;
-/// A graphics backend that can render to windows.
-pub trait Backend: Sized {
+/// A graphics compositor that can draw to windows.
+pub trait Compositor: Sized {
/// The settings of the backend.
type Settings: Default;
/// The iced renderer of the backend.
- type Renderer: crate::Renderer;
+ type Renderer: iced_native::Renderer;
/// The surface of the backend.
type Surface;
@@ -16,7 +16,7 @@ pub trait Backend: Sized {
/// The swap chain of the backend.
type SwapChain;
- /// Creates a new [`Backend`] and an associated iced renderer.
+ /// Creates a new [`Backend`].
///
/// [`Backend`]: trait.Backend.html
fn new(settings: Self::Settings) -> (Self, Self::Renderer);
@@ -48,8 +48,8 @@ pub trait Backend: Sized {
&mut self,
renderer: &mut Self::Renderer,
swap_chain: &mut Self::SwapChain,
- output: &<Self::Renderer as crate::Renderer>::Output,
- scale_factor: f64,
+ viewport: &Viewport,
+ output: &<Self::Renderer as iced_native::Renderer>::Output,
overlay: &[T],
) -> mouse::Interaction;
}
diff --git a/winit/src/debug/basic.rs b/native/src/debug/basic.rs
index d46edba6..5338d0d9 100644
--- a/winit/src/debug/basic.rs
+++ b/native/src/debug/basic.rs
@@ -1,5 +1,7 @@
+#![allow(missing_docs)]
use std::{collections::VecDeque, time};
+/// A bunch of time measurements for debugging purposes.
#[derive(Debug)]
pub struct Debug {
is_enabled: bool,
@@ -30,6 +32,9 @@ pub struct Debug {
}
impl Debug {
+ /// Creates a new [`Debug`].
+ ///
+ /// [`Debug`]: struct.Debug.html
pub fn new() -> Self {
let now = time::Instant::now();
diff --git a/winit/src/debug/null.rs b/native/src/debug/null.rs
index 2a9430cd..60e6122d 100644
--- a/winit/src/debug/null.rs
+++ b/native/src/debug/null.rs
@@ -1,3 +1,4 @@
+#![allow(missing_docs)]
#[derive(Debug)]
pub struct Debug;
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 9882803f..b67ff2a1 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -9,14 +9,11 @@
//! - Event handling for all the built-in widgets
//! - A renderer-agnostic API
//!
-//! To achieve this, it introduces a bunch of reusable interfaces:
+//! To achieve this, it introduces a couple of reusable interfaces:
//!
//! - A [`Widget`] trait, which is used to implement new widgets: from layout
//! requirements to event and drawing logic.
//! - A bunch of `Renderer` traits, meant to keep the crate renderer-agnostic.
-//! - A [`window::Backend`] trait, leveraging [`raw-window-handle`], which can be
-//! implemented by graphical renderers that target _windows_. Window-based
-//! shells (like [`iced_winit`]) can use this trait to stay renderer-agnostic.
//!
//! # Usage
//! The strategy to use this crate depends on your particular use case. If you
@@ -31,7 +28,6 @@
//! [`druid`]: https://github.com/xi-editor/druid
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
//! [`Widget`]: widget/trait.Widget.html
-//! [`window::Backend`]: window/trait.Backend.html
//! [`UserInterface`]: struct.UserInterface.html
//! [renderer]: renderer/index.html
#![deny(missing_docs)]
@@ -42,6 +38,7 @@
pub mod keyboard;
pub mod layout;
pub mod mouse;
+pub mod program;
pub mod renderer;
pub mod subscription;
pub mod widget;
@@ -54,6 +51,15 @@ mod hasher;
mod runtime;
mod user_interface;
+// We disable debug capabilities on release builds unless the `debug` feature
+// is explicitly enabled.
+#[cfg(feature = "debug")]
+#[path = "debug/basic.rs"]
+mod debug;
+#[cfg(not(feature = "debug"))]
+#[path = "debug/null.rs"]
+mod debug;
+
pub use iced_core::{
Align, Background, Color, Font, HorizontalAlignment, Length, Point,
Rectangle, Size, Vector, VerticalAlignment,
@@ -64,10 +70,12 @@ pub use iced_futures::{executor, futures, Command};
pub use executor::Executor;
pub use clipboard::Clipboard;
+pub use debug::Debug;
pub use element::Element;
pub use event::Event;
pub use hasher::Hasher;
pub use layout::Layout;
+pub use program::Program;
pub use renderer::Renderer;
pub use runtime::Runtime;
pub use subscription::Subscription;
diff --git a/native/src/program.rs b/native/src/program.rs
new file mode 100644
index 00000000..14afcd84
--- /dev/null
+++ b/native/src/program.rs
@@ -0,0 +1,39 @@
+//! Build interactive programs using The Elm Architecture.
+use crate::{Command, Element, Renderer};
+
+mod state;
+
+pub use state::State;
+
+/// The core of a user interface application following The Elm Architecture.
+pub trait Program: Sized {
+ /// The graphics backend to use to draw the [`Program`].
+ ///
+ /// [`Program`]: trait.Program.html
+ type Renderer: Renderer;
+
+ /// The type of __messages__ your [`Program`] will produce.
+ ///
+ /// [`Program`]: trait.Program.html
+ type Message: std::fmt::Debug + Send;
+
+ /// Handles a __message__ and updates the state of the [`Program`].
+ ///
+ /// This is where you define your __update logic__. All the __messages__,
+ /// produced by either user interactions or commands, will be handled by
+ /// this method.
+ ///
+ /// Any [`Command`] returned will be executed immediately in the
+ /// background by shells.
+ ///
+ /// [`Program`]: trait.Application.html
+ /// [`Command`]: struct.Command.html
+ fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
+
+ /// Returns the widgets to display in the [`Program`].
+ ///
+ /// These widgets can produce __messages__ based on user interaction.
+ ///
+ /// [`Program`]: trait.Program.html
+ fn view(&mut self) -> Element<'_, Self::Message, Self::Renderer>;
+}
diff --git a/native/src/program/state.rs b/native/src/program/state.rs
new file mode 100644
index 00000000..8716d8b9
--- /dev/null
+++ b/native/src/program/state.rs
@@ -0,0 +1,185 @@
+use crate::{
+ Cache, Clipboard, Command, Debug, Event, Program, Renderer, Size,
+ UserInterface,
+};
+
+/// The execution state of a [`Program`]. It leverages caching, event
+/// processing, and rendering primitive storage.
+///
+/// [`Program`]: trait.Program.html
+#[allow(missing_debug_implementations)]
+pub struct State<P>
+where
+ P: Program + 'static,
+{
+ program: P,
+ cache: Option<Cache>,
+ primitive: <P::Renderer as Renderer>::Output,
+ queued_events: Vec<Event>,
+ queued_messages: Vec<P::Message>,
+}
+
+impl<P> State<P>
+where
+ P: Program + 'static,
+{
+ /// Creates a new [`State`] with the provided [`Program`], initializing its
+ /// primitive with the given logical bounds and renderer.
+ ///
+ /// [`State`]: struct.State.html
+ /// [`Program`]: trait.Program.html
+ pub fn new(
+ mut program: P,
+ bounds: Size,
+ renderer: &mut P::Renderer,
+ debug: &mut Debug,
+ ) -> Self {
+ let user_interface = build_user_interface(
+ &mut program,
+ Cache::default(),
+ renderer,
+ bounds,
+ debug,
+ );
+
+ debug.draw_started();
+ let primitive = user_interface.draw(renderer);
+ debug.draw_finished();
+
+ let cache = Some(user_interface.into_cache());
+
+ State {
+ program,
+ cache,
+ primitive,
+ queued_events: Vec::new(),
+ queued_messages: Vec::new(),
+ }
+ }
+
+ /// Returns a reference to the [`Program`] of the [`State`].
+ ///
+ /// [`Program`]: trait.Program.html
+ /// [`State`]: struct.State.html
+ pub fn program(&self) -> &P {
+ &self.program
+ }
+
+ /// Returns a reference to the current rendering primitive of the [`State`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn primitive(&self) -> &<P::Renderer as Renderer>::Output {
+ &self.primitive
+ }
+
+ /// Queues an event in the [`State`] for processing during an [`update`].
+ ///
+ /// [`State`]: struct.State.html
+ /// [`update`]: #method.update
+ pub fn queue_event(&mut self, event: Event) {
+ self.queued_events.push(event);
+ }
+
+ /// Queues a message in the [`State`] for processing during an [`update`].
+ ///
+ /// [`State`]: struct.State.html
+ /// [`update`]: #method.update
+ pub fn queue_message(&mut self, message: P::Message) {
+ self.queued_messages.push(message);
+ }
+
+ /// Processes all the queued events and messages, rebuilding and redrawing
+ /// the widgets of the linked [`Program`] if necessary.
+ ///
+ /// Returns the [`Command`] obtained from [`Program`] after updating it,
+ /// only if an update was necessary.
+ ///
+ /// [`Program`]: trait.Program.html
+ pub fn update(
+ &mut self,
+ clipboard: Option<&dyn Clipboard>,
+ bounds: Size,
+ renderer: &mut P::Renderer,
+ debug: &mut Debug,
+ ) -> Option<Command<P::Message>> {
+ if self.queued_events.is_empty() && self.queued_messages.is_empty() {
+ return None;
+ }
+
+ let mut user_interface = build_user_interface(
+ &mut self.program,
+ self.cache.take().unwrap(),
+ renderer,
+ bounds,
+ debug,
+ );
+
+ debug.event_processing_started();
+ let mut messages = user_interface.update(
+ self.queued_events.drain(..),
+ clipboard,
+ renderer,
+ );
+ messages.extend(self.queued_messages.drain(..));
+ debug.event_processing_finished();
+
+ if messages.is_empty() {
+ debug.draw_started();
+ self.primitive = user_interface.draw(renderer);
+ debug.draw_finished();
+
+ self.cache = Some(user_interface.into_cache());
+
+ None
+ } else {
+ // When there are messages, we are forced to rebuild twice
+ // for now :^)
+ let temp_cache = user_interface.into_cache();
+
+ let commands =
+ Command::batch(messages.into_iter().map(|message| {
+ debug.log_message(&message);
+
+ debug.update_started();
+ let command = self.program.update(message);
+ debug.update_finished();
+
+ command
+ }));
+
+ let user_interface = build_user_interface(
+ &mut self.program,
+ temp_cache,
+ renderer,
+ bounds,
+ debug,
+ );
+
+ debug.draw_started();
+ self.primitive = user_interface.draw(renderer);
+ debug.draw_finished();
+
+ self.cache = Some(user_interface.into_cache());
+
+ Some(commands)
+ }
+ }
+}
+
+fn build_user_interface<'a, P: Program>(
+ program: &'a mut P,
+ cache: Cache,
+ renderer: &mut P::Renderer,
+ size: Size,
+ debug: &mut Debug,
+) -> UserInterface<'a, P::Message, P::Renderer> {
+ debug.view_started();
+ let view = program.view();
+ debug.view_finished();
+
+ debug.layout_started();
+ let user_interface = UserInterface::build(view, size, cache, renderer);
+ debug.layout_finished();
+
+ user_interface
+}
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 48cd6111..e963b601 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -102,7 +102,9 @@ where
hasher.finish()
};
- let layout = if hash == cache.hash && bounds == cache.bounds {
+ let layout_is_cached = hash == cache.hash && bounds == cache.bounds;
+
+ let layout = if layout_is_cached {
cache.layout
} else {
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds))
diff --git a/native/src/window.rs b/native/src/window.rs
index 4dcae62f..220bb3be 100644
--- a/native/src/window.rs
+++ b/native/src/window.rs
@@ -1,6 +1,4 @@
//! Build window-based GUI applications.
-mod backend;
mod event;
-pub use backend::Backend;
pub use event::Event;