From d4743183d40c6044ce6fa39e2a52919a32912cda Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 May 2020 14:23:28 +0200 Subject: Draft first working version of `iced_glow` :tada: --- native/src/window/backend.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'native') diff --git a/native/src/window/backend.rs b/native/src/window/backend.rs index 892d4bb9..d8726fd4 100644 --- a/native/src/window/backend.rs +++ b/native/src/window/backend.rs @@ -5,7 +5,7 @@ use raw_window_handle::HasRawWindowHandle; /// A graphics backend that can render to windows. pub trait Backend: Sized { /// The settings of the backend. - type Settings: Default; + type Settings: Default + Clone; /// The iced renderer of the backend. type Renderer: crate::Renderer; @@ -16,10 +16,10 @@ 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); + fn new(settings: Self::Settings) -> Self; /// Crates a new [`Surface`] for the given window. /// @@ -29,6 +29,11 @@ pub trait Backend: Sized { window: &W, ) -> Self::Surface; + /// Crates a new [`Renderer`]. + /// + /// [`Renderer`]: #associatedtype.Renderer + fn create_renderer(&mut self, settings: Self::Settings) -> Self::Renderer; + /// Crates a new [`SwapChain`] for the given [`Surface`]. /// /// [`SwapChain`]: #associatedtype.SwapChain -- cgit From 05af8d00d4c0f7b8e0ece85224fd90a92da86da8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 May 2020 17:15:44 +0200 Subject: Draft new `iced_graphics` crate :tada: --- native/src/user_interface.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'native') 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)) -- cgit From 4aed0fa4b6d63b739b5557ef16f6077988cd2758 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 May 2020 20:01:55 +0200 Subject: Rename `window::Backend` to `Compositor` --- native/src/window.rs | 4 +-- native/src/window/backend.rs | 60 ----------------------------------------- native/src/window/compositor.rs | 60 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 62 deletions(-) delete mode 100644 native/src/window/backend.rs create mode 100644 native/src/window/compositor.rs (limited to 'native') diff --git a/native/src/window.rs b/native/src/window.rs index 4dcae62f..84269fbf 100644 --- a/native/src/window.rs +++ b/native/src/window.rs @@ -1,6 +1,6 @@ //! Build window-based GUI applications. -mod backend; +mod compositor; mod event; -pub use backend::Backend; +pub use compositor::Compositor; pub use event::Event; diff --git a/native/src/window/backend.rs b/native/src/window/backend.rs deleted file mode 100644 index d8726fd4..00000000 --- a/native/src/window/backend.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::mouse; - -use raw_window_handle::HasRawWindowHandle; - -/// A graphics backend that can render to windows. -pub trait Backend: Sized { - /// The settings of the backend. - type Settings: Default + Clone; - - /// The iced renderer of the backend. - type Renderer: crate::Renderer; - - /// The surface of the backend. - type Surface; - - /// The swap chain of the backend. - type SwapChain; - - /// Creates a new [`Backend`]. - /// - /// [`Backend`]: trait.Backend.html - fn new(settings: Self::Settings) -> Self; - - /// Crates a new [`Surface`] for the given window. - /// - /// [`Surface`]: #associatedtype.Surface - fn create_surface( - &mut self, - window: &W, - ) -> Self::Surface; - - /// Crates a new [`Renderer`]. - /// - /// [`Renderer`]: #associatedtype.Renderer - fn create_renderer(&mut self, settings: Self::Settings) -> Self::Renderer; - - /// Crates a new [`SwapChain`] for the given [`Surface`]. - /// - /// [`SwapChain`]: #associatedtype.SwapChain - /// [`Surface`]: #associatedtype.Surface - fn create_swap_chain( - &mut self, - surface: &Self::Surface, - width: u32, - height: u32, - ) -> Self::SwapChain; - - /// Draws the output primitives to the next frame of the given [`SwapChain`]. - /// - /// [`SwapChain`]: #associatedtype.SwapChain - /// [`Surface`]: #associatedtype.Surface - fn draw>( - &mut self, - renderer: &mut Self::Renderer, - swap_chain: &mut Self::SwapChain, - output: &::Output, - scale_factor: f64, - overlay: &[T], - ) -> mouse::Interaction; -} diff --git a/native/src/window/compositor.rs b/native/src/window/compositor.rs new file mode 100644 index 00000000..ae010c89 --- /dev/null +++ b/native/src/window/compositor.rs @@ -0,0 +1,60 @@ +use crate::mouse; + +use raw_window_handle::HasRawWindowHandle; + +/// A graphics compositor that can draw to windows. +pub trait Compositor: Sized { + /// The settings of the backend. + type Settings: Default + Clone; + + /// The iced renderer of the backend. + type Renderer: crate::Renderer; + + /// The surface of the backend. + type Surface; + + /// The swap chain of the backend. + type SwapChain; + + /// Creates a new [`Backend`]. + /// + /// [`Backend`]: trait.Backend.html + fn new(settings: Self::Settings) -> Self; + + /// Crates a new [`Surface`] for the given window. + /// + /// [`Surface`]: #associatedtype.Surface + fn create_surface( + &mut self, + window: &W, + ) -> Self::Surface; + + /// Crates a new [`Renderer`]. + /// + /// [`Renderer`]: #associatedtype.Renderer + fn create_renderer(&mut self, settings: Self::Settings) -> Self::Renderer; + + /// Crates a new [`SwapChain`] for the given [`Surface`]. + /// + /// [`SwapChain`]: #associatedtype.SwapChain + /// [`Surface`]: #associatedtype.Surface + fn create_swap_chain( + &mut self, + surface: &Self::Surface, + width: u32, + height: u32, + ) -> Self::SwapChain; + + /// Draws the output primitives to the next frame of the given [`SwapChain`]. + /// + /// [`SwapChain`]: #associatedtype.SwapChain + /// [`Surface`]: #associatedtype.Surface + fn draw>( + &mut self, + renderer: &mut Self::Renderer, + swap_chain: &mut Self::SwapChain, + output: &::Output, + scale_factor: f64, + overlay: &[T], + ) -> mouse::Interaction; +} -- cgit From a1a5fcfd46622d5b18d1716aa2adb4659835ccf3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 May 2020 20:28:35 +0200 Subject: Refactor `Viewport` and `Compositor` --- native/Cargo.toml | 1 - native/src/window.rs | 2 -- native/src/window/compositor.rs | 60 ----------------------------------------- 3 files changed, 63 deletions(-) delete mode 100644 native/src/window/compositor.rs (limited to 'native') diff --git a/native/Cargo.toml b/native/Cargo.toml index 7e9c2a5a..067b8708 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -9,7 +9,6 @@ repository = "https://github.com/hecrj/iced" [dependencies] twox-hash = "1.5" -raw-window-handle = "0.3" unicode-segmentation = "1.6" [dependencies.iced_core] diff --git a/native/src/window.rs b/native/src/window.rs index 84269fbf..220bb3be 100644 --- a/native/src/window.rs +++ b/native/src/window.rs @@ -1,6 +1,4 @@ //! Build window-based GUI applications. -mod compositor; mod event; -pub use compositor::Compositor; pub use event::Event; diff --git a/native/src/window/compositor.rs b/native/src/window/compositor.rs deleted file mode 100644 index ae010c89..00000000 --- a/native/src/window/compositor.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::mouse; - -use raw_window_handle::HasRawWindowHandle; - -/// A graphics compositor that can draw to windows. -pub trait Compositor: Sized { - /// The settings of the backend. - type Settings: Default + Clone; - - /// The iced renderer of the backend. - type Renderer: crate::Renderer; - - /// The surface of the backend. - type Surface; - - /// The swap chain of the backend. - type SwapChain; - - /// Creates a new [`Backend`]. - /// - /// [`Backend`]: trait.Backend.html - fn new(settings: Self::Settings) -> Self; - - /// Crates a new [`Surface`] for the given window. - /// - /// [`Surface`]: #associatedtype.Surface - fn create_surface( - &mut self, - window: &W, - ) -> Self::Surface; - - /// Crates a new [`Renderer`]. - /// - /// [`Renderer`]: #associatedtype.Renderer - fn create_renderer(&mut self, settings: Self::Settings) -> Self::Renderer; - - /// Crates a new [`SwapChain`] for the given [`Surface`]. - /// - /// [`SwapChain`]: #associatedtype.SwapChain - /// [`Surface`]: #associatedtype.Surface - fn create_swap_chain( - &mut self, - surface: &Self::Surface, - width: u32, - height: u32, - ) -> Self::SwapChain; - - /// Draws the output primitives to the next frame of the given [`SwapChain`]. - /// - /// [`SwapChain`]: #associatedtype.SwapChain - /// [`Surface`]: #associatedtype.Surface - fn draw>( - &mut self, - renderer: &mut Self::Renderer, - swap_chain: &mut Self::SwapChain, - output: &::Output, - scale_factor: f64, - overlay: &[T], - ) -> mouse::Interaction; -} -- cgit From ae5e2c6c734894d71b2034a498a858b7997c5d3d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 May 2020 04:27:31 +0200 Subject: Introduce `Program` and `State` --- native/Cargo.toml | 3 + native/src/debug/basic.rs | 211 ++++++++++++++++++++++++++++++++++++++++++++ native/src/debug/null.rs | 46 ++++++++++ native/src/lib.rs | 14 ++- native/src/program.rs | 39 ++++++++ native/src/program/state.rs | 154 ++++++++++++++++++++++++++++++++ 6 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 native/src/debug/basic.rs create mode 100644 native/src/debug/null.rs create mode 100644 native/src/program.rs create mode 100644 native/src/program/state.rs (limited to 'native') diff --git a/native/Cargo.toml b/native/Cargo.toml index 067b8708..75b4a56b 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -7,6 +7,9 @@ description = "A renderer-agnostic library for native GUIs" license = "MIT" repository = "https://github.com/hecrj/iced" +[features] +debug = [] + [dependencies] twox-hash = "1.5" unicode-segmentation = "1.6" diff --git a/native/src/debug/basic.rs b/native/src/debug/basic.rs new file mode 100644 index 00000000..d46edba6 --- /dev/null +++ b/native/src/debug/basic.rs @@ -0,0 +1,211 @@ +use std::{collections::VecDeque, time}; + +#[derive(Debug)] +pub struct Debug { + is_enabled: bool, + + startup_start: time::Instant, + startup_duration: time::Duration, + + update_start: time::Instant, + update_durations: TimeBuffer, + + view_start: time::Instant, + view_durations: TimeBuffer, + + layout_start: time::Instant, + layout_durations: TimeBuffer, + + event_start: time::Instant, + event_durations: TimeBuffer, + + draw_start: time::Instant, + draw_durations: TimeBuffer, + + render_start: time::Instant, + render_durations: TimeBuffer, + + message_count: usize, + last_messages: VecDeque, +} + +impl Debug { + pub fn new() -> Self { + let now = time::Instant::now(); + + Self { + is_enabled: false, + startup_start: now, + startup_duration: time::Duration::from_secs(0), + + update_start: now, + update_durations: TimeBuffer::new(200), + + view_start: now, + view_durations: TimeBuffer::new(200), + + layout_start: now, + layout_durations: TimeBuffer::new(200), + + event_start: now, + event_durations: TimeBuffer::new(200), + + draw_start: now, + draw_durations: TimeBuffer::new(200), + + render_start: now, + render_durations: TimeBuffer::new(50), + + message_count: 0, + last_messages: VecDeque::new(), + } + } + + pub fn toggle(&mut self) { + self.is_enabled = !self.is_enabled; + } + + pub fn startup_started(&mut self) { + self.startup_start = time::Instant::now(); + } + + pub fn startup_finished(&mut self) { + self.startup_duration = time::Instant::now() - self.startup_start; + } + + pub fn update_started(&mut self) { + self.update_start = time::Instant::now(); + } + + pub fn update_finished(&mut self) { + self.update_durations + .push(time::Instant::now() - self.update_start); + } + + pub fn view_started(&mut self) { + self.view_start = time::Instant::now(); + } + + pub fn view_finished(&mut self) { + self.view_durations + .push(time::Instant::now() - self.view_start); + } + + pub fn layout_started(&mut self) { + self.layout_start = time::Instant::now(); + } + + pub fn layout_finished(&mut self) { + self.layout_durations + .push(time::Instant::now() - self.layout_start); + } + + pub fn event_processing_started(&mut self) { + self.event_start = time::Instant::now(); + } + + pub fn event_processing_finished(&mut self) { + self.event_durations + .push(time::Instant::now() - self.event_start); + } + + pub fn draw_started(&mut self) { + self.draw_start = time::Instant::now(); + } + + pub fn draw_finished(&mut self) { + self.draw_durations + .push(time::Instant::now() - self.draw_start); + } + + pub fn render_started(&mut self) { + self.render_start = time::Instant::now(); + } + + pub fn render_finished(&mut self) { + self.render_durations + .push(time::Instant::now() - self.render_start); + } + + pub fn log_message(&mut self, message: &Message) { + self.last_messages.push_back(format!("{:?}", message)); + + if self.last_messages.len() > 10 { + let _ = self.last_messages.pop_front(); + } + + self.message_count += 1; + } + + pub fn overlay(&self) -> Vec { + if !self.is_enabled { + return Vec::new(); + } + + let mut lines = Vec::new(); + + fn key_value(key: &str, value: T) -> String { + format!("{} {:?}", key, value) + } + + lines.push(format!( + "{} {} - {}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_REPOSITORY"), + )); + lines.push(key_value("Startup:", self.startup_duration)); + lines.push(key_value("Update:", self.update_durations.average())); + lines.push(key_value("View:", self.view_durations.average())); + lines.push(key_value("Layout:", self.layout_durations.average())); + lines.push(key_value( + "Event processing:", + self.event_durations.average(), + )); + lines.push(key_value( + "Primitive generation:", + self.draw_durations.average(), + )); + lines.push(key_value("Render:", self.render_durations.average())); + lines.push(key_value("Message count:", self.message_count)); + lines.push(String::from("Last messages:")); + lines.extend( + self.last_messages.iter().map(|msg| format!(" {}", msg)), + ); + + lines + } +} + +#[derive(Debug)] +struct TimeBuffer { + head: usize, + size: usize, + contents: Vec, +} + +impl TimeBuffer { + fn new(capacity: usize) -> TimeBuffer { + TimeBuffer { + head: 0, + size: 0, + contents: vec![time::Duration::from_secs(0); capacity], + } + } + + fn push(&mut self, duration: time::Duration) { + self.head = (self.head + 1) % self.contents.len(); + self.contents[self.head] = duration; + self.size = (self.size + 1).min(self.contents.len()); + } + + fn average(&self) -> time::Duration { + let sum: time::Duration = if self.size == self.contents.len() { + self.contents[..].iter().sum() + } else { + self.contents[..self.size].iter().sum() + }; + + sum / self.size.max(1) as u32 + } +} diff --git a/native/src/debug/null.rs b/native/src/debug/null.rs new file mode 100644 index 00000000..2a9430cd --- /dev/null +++ b/native/src/debug/null.rs @@ -0,0 +1,46 @@ +#[derive(Debug)] +pub struct Debug; + +impl Debug { + pub fn new() -> Self { + Self + } + + pub fn startup_started(&mut self) {} + + pub fn startup_finished(&mut self) {} + + pub fn update_started(&mut self) {} + + pub fn update_finished(&mut self) {} + + pub fn view_started(&mut self) {} + + pub fn view_finished(&mut self) {} + + pub fn layout_started(&mut self) {} + + pub fn layout_finished(&mut self) {} + + pub fn event_processing_started(&mut self) {} + + pub fn event_processing_finished(&mut self) {} + + pub fn draw_started(&mut self) {} + + pub fn draw_finished(&mut self) {} + + pub fn render_started(&mut self) {} + + pub fn render_finished(&mut self) {} + + pub fn log_message( + &mut self, + _message: &Message, + ) { + } + + pub fn overlay(&self) -> Vec { + Vec::new() + } +} diff --git a/native/src/lib.rs b/native/src/lib.rs index 9882803f..bea7d17e 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -34,7 +34,7 @@ //! [`window::Backend`]: window/trait.Backend.html //! [`UserInterface`]: struct.UserInterface.html //! [renderer]: renderer/index.html -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] @@ -42,6 +42,7 @@ pub mod keyboard; pub mod layout; pub mod mouse; +pub mod program; pub mod renderer; pub mod subscription; pub mod widget; @@ -54,6 +55,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 +74,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..2652dee9 --- /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; + +/// An interactive, native cross-platform program. +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. + /// + /// [`Application`]: 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; + + /// Returns the widgets to display in the [`Program`]. + /// + /// These widgets can produce __messages__ based on user interaction. + /// + /// [`Program`]: trait.Application.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..bcb7212d --- /dev/null +++ b/native/src/program/state.rs @@ -0,0 +1,154 @@ +use crate::{ + Cache, Clipboard, Command, Debug, Event, Program, Renderer, Size, + UserInterface, +}; + +#[allow(missing_debug_implementations)] +pub struct State

+where + P: Program + 'static, +{ + program: P, + cache: Option, + primitive: ::Output, + queued_events: Vec, + queued_messages: Vec, +} + +impl

State

+where + P: Program + 'static, +{ + 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(), + } + } + + pub fn program(&self) -> &P { + &self.program + } + + pub fn primitive(&self) -> &::Output { + &self.primitive + } + + pub fn queue_event(&mut self, event: Event) { + self.queued_events.push(event); + } + + pub fn queue_message(&mut self, message: P::Message) { + self.queued_messages.push(message); + } + + pub fn update( + &mut self, + clipboard: Option<&dyn Clipboard>, + bounds: Size, + renderer: &mut P::Renderer, + debug: &mut Debug, + ) -> Option> { + 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 +} -- cgit From 334dd098170d56c004d7a97bdfbbffe6f63f0ebd Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 26 May 2020 16:11:49 -0500 Subject: Pane Grid spacing applied prior to rounding On low-DPI screens, the rounding order of operations made it impossible to produce an odd-pixel spacing. Specifying 1, for instance, produced zero space between panes. This approach subtracts half the spacing from the first pane prior to rounding and uses the whole spacing for the second pane size and coordinate. --- native/src/widget/pane_grid/axis.rs | 24 +++++++++++++----------- native/src/widget/pane_grid/node.rs | 22 ++++++++++------------ 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'native') diff --git a/native/src/widget/pane_grid/axis.rs b/native/src/widget/pane_grid/axis.rs index f0e3f362..7181f9bf 100644 --- a/native/src/widget/pane_grid/axis.rs +++ b/native/src/widget/pane_grid/axis.rs @@ -14,37 +14,39 @@ impl Axis { &self, rectangle: &Rectangle, ratio: f32, - halved_spacing: f32, + spacing: f32, ) -> (Rectangle, Rectangle) { match self { Axis::Horizontal => { - let height_top = (rectangle.height * ratio).round(); - let height_bottom = rectangle.height - height_top; + let height_top = + (rectangle.height * ratio - spacing / 2.0).round(); + let height_bottom = rectangle.height - height_top - spacing; ( Rectangle { - height: height_top - halved_spacing, + height: height_top, ..*rectangle }, Rectangle { - y: rectangle.y + height_top + halved_spacing, - height: height_bottom - halved_spacing, + y: rectangle.y + height_top + spacing, + height: height_bottom, ..*rectangle }, ) } Axis::Vertical => { - let width_left = (rectangle.width * ratio).round(); - let width_right = rectangle.width - width_left; + let width_left = + (rectangle.width * ratio - spacing / 2.0).round(); + let width_right = rectangle.width - width_left - spacing; ( Rectangle { - width: width_left - halved_spacing, + width: width_left, ..*rectangle }, Rectangle { - x: rectangle.x + width_left + halved_spacing, - width: width_right - halved_spacing, + x: rectangle.x + width_left + spacing, + width: width_right, ..*rectangle }, ) diff --git a/native/src/widget/pane_grid/node.rs b/native/src/widget/pane_grid/node.rs index 723ec393..b13c5e26 100644 --- a/native/src/widget/pane_grid/node.rs +++ b/native/src/widget/pane_grid/node.rs @@ -56,7 +56,7 @@ impl Node { let mut regions = HashMap::new(); self.compute_regions( - spacing / 2.0, + spacing, &Rectangle { x: 0.0, y: 0.0, @@ -83,7 +83,7 @@ impl Node { let mut splits = HashMap::new(); self.compute_splits( - spacing / 2.0, + spacing, &Rectangle { x: 0.0, y: 0.0, @@ -185,7 +185,7 @@ impl Node { fn compute_regions( &self, - halved_spacing: f32, + spacing: f32, current: &Rectangle, regions: &mut HashMap, ) { @@ -193,11 +193,10 @@ impl Node { Node::Split { axis, ratio, a, b, .. } => { - let (region_a, region_b) = - axis.split(current, *ratio, halved_spacing); + let (region_a, region_b) = axis.split(current, *ratio, spacing); - a.compute_regions(halved_spacing, ®ion_a, regions); - b.compute_regions(halved_spacing, ®ion_b, regions); + a.compute_regions(spacing, ®ion_a, regions); + b.compute_regions(spacing, ®ion_b, regions); } Node::Pane(pane) => { let _ = regions.insert(*pane, *current); @@ -207,7 +206,7 @@ impl Node { fn compute_splits( &self, - halved_spacing: f32, + spacing: f32, current: &Rectangle, splits: &mut HashMap, ) { @@ -219,13 +218,12 @@ impl Node { b, id, } => { - let (region_a, region_b) = - axis.split(current, *ratio, halved_spacing); + let (region_a, region_b) = axis.split(current, *ratio, spacing); let _ = splits.insert(*id, (*axis, *current, *ratio)); - a.compute_splits(halved_spacing, ®ion_a, splits); - b.compute_splits(halved_spacing, ®ion_b, splits); + a.compute_splits(spacing, ®ion_a, splits); + b.compute_splits(spacing, ®ion_b, splits); } Node::Pane(_) => {} } -- cgit From 9079014974e7dab83d70d2c08adb1dc50a49629c Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Tue, 26 May 2020 16:47:29 -0500 Subject: Tests for axis split --- native/src/widget/pane_grid/axis.rs | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'native') diff --git a/native/src/widget/pane_grid/axis.rs b/native/src/widget/pane_grid/axis.rs index 7181f9bf..f8c7b661 100644 --- a/native/src/widget/pane_grid/axis.rs +++ b/native/src/widget/pane_grid/axis.rs @@ -54,3 +54,90 @@ impl Axis { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn split_horizontal() { + let a = Axis::Horizontal; + // rectangle height, spacing, top height, bottom y, bottom height + let cases = vec![ + // Even height, even spacing + (10.0, 2.0, 4.0, 6.0, 4.0), + // Odd height, even spacing + (9.0, 2.0, 4.0, 6.0, 3.0), + // Even height, odd spacing + (10.0, 1.0, 5.0, 6.0, 4.0), + // Odd height, odd spacing + (9.0, 1.0, 4.0, 5.0, 4.0), + ]; + for case in cases { + let (h0, spacing, h1_top, y_bottom, h1_bottom) = case; + let r = Rectangle { + x: 0.0, + y: 0.0, + width: 10.0, + height: h0, + }; + let (top, bottom) = a.split(&r, 0.5, spacing); + assert_eq!( + top, + Rectangle { + height: h1_top, + ..r + } + ); + assert_eq!( + bottom, + Rectangle { + y: y_bottom, + height: h1_bottom, + ..r + } + ); + } + } + + #[test] + fn split_vertical() { + let a = Axis::Vertical; + // rectangle width, spacing, left width, right x, right width + let cases = vec![ + // Even width, even spacing + (10.0, 2.0, 4.0, 6.0, 4.0), + // Odd width, even spacing + (9.0, 2.0, 4.0, 6.0, 3.0), + // Even width, odd spacing + (10.0, 1.0, 5.0, 6.0, 4.0), + // Odd width, odd spacing + (9.0, 1.0, 4.0, 5.0, 4.0), + ]; + for case in cases { + let (w0, spacing, w1_left, x_right, w1_right) = case; + let r = Rectangle { + x: 0.0, + y: 0.0, + width: w0, + height: 10.0, + }; + let (left, right) = a.split(&r, 0.5, spacing); + assert_eq!( + left, + Rectangle { + width: w1_left, + ..r + } + ); + assert_eq!( + right, + Rectangle { + x: x_right, + width: w1_right, + ..r + } + ); + } + } +} -- cgit From 858eafe22e6f17962c935a3d58749dbd839c46ae Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Wed, 27 May 2020 14:24:33 -0500 Subject: Structured test cases --- native/src/widget/pane_grid/axis.rs | 201 ++++++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 65 deletions(-) (limited to 'native') diff --git a/native/src/widget/pane_grid/axis.rs b/native/src/widget/pane_grid/axis.rs index f8c7b661..b3a306d5 100644 --- a/native/src/widget/pane_grid/axis.rs +++ b/native/src/widget/pane_grid/axis.rs @@ -59,85 +59,156 @@ impl Axis { mod tests { use super::*; + enum Case { + Horizontal { + overall_height: f32, + spacing: f32, + top_height: f32, + bottom_y: f32, + bottom_height: f32, + }, + Vertical { + overall_width: f32, + spacing: f32, + left_width: f32, + right_x: f32, + right_width: f32, + }, + } + #[test] - fn split_horizontal() { - let a = Axis::Horizontal; - // rectangle height, spacing, top height, bottom y, bottom height + fn split() { let cases = vec![ // Even height, even spacing - (10.0, 2.0, 4.0, 6.0, 4.0), + Case::Horizontal { + overall_height: 10.0, + spacing: 2.0, + top_height: 4.0, + bottom_y: 6.0, + bottom_height: 4.0, + }, // Odd height, even spacing - (9.0, 2.0, 4.0, 6.0, 3.0), + Case::Horizontal { + overall_height: 9.0, + spacing: 2.0, + top_height: 4.0, + bottom_y: 6.0, + bottom_height: 3.0, + }, // Even height, odd spacing - (10.0, 1.0, 5.0, 6.0, 4.0), + Case::Horizontal { + overall_height: 10.0, + spacing: 1.0, + top_height: 5.0, + bottom_y: 6.0, + bottom_height: 4.0, + }, // Odd height, odd spacing - (9.0, 1.0, 4.0, 5.0, 4.0), - ]; - for case in cases { - let (h0, spacing, h1_top, y_bottom, h1_bottom) = case; - let r = Rectangle { - x: 0.0, - y: 0.0, - width: 10.0, - height: h0, - }; - let (top, bottom) = a.split(&r, 0.5, spacing); - assert_eq!( - top, - Rectangle { - height: h1_top, - ..r - } - ); - assert_eq!( - bottom, - Rectangle { - y: y_bottom, - height: h1_bottom, - ..r - } - ); - } - } - - #[test] - fn split_vertical() { - let a = Axis::Vertical; - // rectangle width, spacing, left width, right x, right width - let cases = vec![ + Case::Horizontal { + overall_height: 9.0, + spacing: 1.0, + top_height: 4.0, + bottom_y: 5.0, + bottom_height: 4.0, + }, // Even width, even spacing - (10.0, 2.0, 4.0, 6.0, 4.0), + Case::Vertical { + overall_width: 10.0, + spacing: 2.0, + left_width: 4.0, + right_x: 6.0, + right_width: 4.0, + }, // Odd width, even spacing - (9.0, 2.0, 4.0, 6.0, 3.0), + Case::Vertical { + overall_width: 9.0, + spacing: 2.0, + left_width: 4.0, + right_x: 6.0, + right_width: 3.0, + }, // Even width, odd spacing - (10.0, 1.0, 5.0, 6.0, 4.0), + Case::Vertical { + overall_width: 10.0, + spacing: 1.0, + left_width: 5.0, + right_x: 6.0, + right_width: 4.0, + }, // Odd width, odd spacing - (9.0, 1.0, 4.0, 5.0, 4.0), + Case::Vertical { + overall_width: 9.0, + spacing: 1.0, + left_width: 4.0, + right_x: 5.0, + right_width: 4.0, + }, ]; for case in cases { - let (w0, spacing, w1_left, x_right, w1_right) = case; - let r = Rectangle { - x: 0.0, - y: 0.0, - width: w0, - height: 10.0, - }; - let (left, right) = a.split(&r, 0.5, spacing); - assert_eq!( - left, - Rectangle { - width: w1_left, - ..r + match case { + Case::Horizontal { + overall_height, + spacing, + top_height, + bottom_y, + bottom_height, + } => { + let a = Axis::Horizontal; + let r = Rectangle { + x: 0.0, + y: 0.0, + width: 10.0, + height: overall_height, + }; + let (top, bottom) = a.split(&r, 0.5, spacing); + assert_eq!( + top, + Rectangle { + height: top_height, + ..r + } + ); + assert_eq!( + bottom, + Rectangle { + y: bottom_y, + height: bottom_height, + ..r + } + ); } - ); - assert_eq!( - right, - Rectangle { - x: x_right, - width: w1_right, - ..r + Case::Vertical { + overall_width, + spacing, + left_width, + right_x, + right_width, + } => { + let a = Axis::Vertical; + let r = Rectangle { + x: 0.0, + y: 0.0, + width: overall_width, + height: 10.0, + }; + let (left, right) = a.split(&r, 0.5, spacing); + assert_eq!( + left, + Rectangle { + width: left_width, + ..r + } + ); + assert_eq!( + right, + Rectangle { + x: right_x, + width: right_width, + ..r + } + ); } - ); + } } } } -- cgit From 508128436c5da88d4b4f384a9fe73288320eb9ec Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 May 2020 02:04:31 +0200 Subject: Write documentation for new `iced_native` API --- native/src/debug/basic.rs | 5 +++++ native/src/lib.rs | 2 +- native/src/program.rs | 4 ++-- native/src/program/state.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) (limited to 'native') diff --git a/native/src/debug/basic.rs b/native/src/debug/basic.rs index d46edba6..5338d0d9 100644 --- a/native/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/native/src/lib.rs b/native/src/lib.rs index bea7d17e..f9a8c477 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -34,7 +34,7 @@ //! [`window::Backend`]: window/trait.Backend.html //! [`UserInterface`]: struct.UserInterface.html //! [renderer]: renderer/index.html -//#![deny(missing_docs)] +#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/native/src/program.rs b/native/src/program.rs index 2652dee9..a28ff2a3 100644 --- a/native/src/program.rs +++ b/native/src/program.rs @@ -14,7 +14,7 @@ pub trait Program: Sized { /// The type of __messages__ your [`Program`] will produce. /// - /// [`Application`]: trait.Program.html + /// [`Program`]: trait.Program.html type Message: std::fmt::Debug + Send; /// Handles a __message__ and updates the state of the [`Program`]. @@ -34,6 +34,6 @@ pub trait Program: Sized { /// /// These widgets can produce __messages__ based on user interaction. /// - /// [`Program`]: trait.Application.html + /// [`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 index bcb7212d..8716d8b9 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -3,6 +3,10 @@ use crate::{ 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

where @@ -19,6 +23,11 @@ impl

State

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, @@ -48,22 +57,44 @@ where } } + /// 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) -> &::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>, -- cgit From ef28347f1c816d8ad9e772303467de489efb802b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 May 2020 02:49:32 +0200 Subject: Write documentation for new `iced_winit` API --- native/src/debug/null.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'native') diff --git a/native/src/debug/null.rs b/native/src/debug/null.rs index 2a9430cd..60e6122d 100644 --- a/native/src/debug/null.rs +++ b/native/src/debug/null.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)] #[derive(Debug)] pub struct Debug; -- cgit From 4aa0d7a13a6fbf04e3fa24c444d562c2f1085b5b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 May 2020 02:57:03 +0200 Subject: Write documentation for `iced_glutin` --- native/src/program.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'native') diff --git a/native/src/program.rs b/native/src/program.rs index a28ff2a3..14afcd84 100644 --- a/native/src/program.rs +++ b/native/src/program.rs @@ -5,7 +5,7 @@ mod state; pub use state::State; -/// An interactive, native cross-platform program. +/// The core of a user interface application following The Elm Architecture. pub trait Program: Sized { /// The graphics backend to use to draw the [`Program`]. /// -- cgit From 709ed1f3f7ad8cf67a176763e394aaae4e808e93 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 May 2020 21:30:33 +0200 Subject: Fix `iced_native` mention of old `window::Backend` --- native/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'native') diff --git a/native/src/lib.rs b/native/src/lib.rs index f9a8c477..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)] -- cgit