From a99aa1dd61e15e28d93c0719037b6683f32e310e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 26 Nov 2019 15:32:11 +0100 Subject: Fallback to embedded font when system font fails --- wgpu/Cargo.toml | 4 +- wgpu/fonts/Lato-Regular.ttf | Bin 0 -> 75136 bytes wgpu/fonts/OFL.txt | 93 ++++++++++++++++++++++++++++++++++++++++++++ wgpu/src/text.rs | 36 +++++++++++------ 4 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 wgpu/fonts/Lato-Regular.ttf create mode 100644 wgpu/fonts/OFL.txt diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index f008a99c..47ec0537 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -4,14 +4,14 @@ version = "0.1.0" authors = ["Héctor Ramón Jiménez "] edition = "2018" description = "A wgpu renderer for Iced" -license = "MIT" +license = "MIT AND OFL-1.1" repository = "https://github.com/hecrj/iced" [dependencies] iced_native = { version = "0.1.0", path = "../native" } wgpu = "0.4" glyph_brush = "0.6" -wgpu_glyph = "0.6" +wgpu_glyph = { version = "0.7", git = "https://github.com/hecrj/wgpu_glyph", branch = "fix/font-load-panic" } raw-window-handle = "0.3" image = "0.22" glam = "0.8" diff --git a/wgpu/fonts/Lato-Regular.ttf b/wgpu/fonts/Lato-Regular.ttf new file mode 100644 index 00000000..33eba8b1 Binary files /dev/null and b/wgpu/fonts/Lato-Regular.ttf differ diff --git a/wgpu/fonts/OFL.txt b/wgpu/fonts/OFL.txt new file mode 100644 index 00000000..dfca0da4 --- /dev/null +++ b/wgpu/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index bf37abe5..f29440c7 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -11,6 +11,8 @@ pub const BUILTIN_ICONS: iced_native::Font = iced_native::Font::External { pub const CHECKMARK_ICON: char = '\u{F00C}'; +const FALLBACK_FONT: &[u8] = include_bytes!("../fonts/Lato-Regular.ttf"); + #[derive(Debug)] pub struct Pipeline { draw_brush: RefCell>, @@ -26,24 +28,36 @@ impl Pipeline { let default_font = font_source .load(&[font::Family::SansSerif, font::Family::Serif]) - .expect("Find sans-serif or serif font"); + .unwrap_or_else(|_| FALLBACK_FONT.to_vec()); let mono_font = font_source .load(&[font::Family::Monospace]) - .expect("Find monospace font"); + .unwrap_or_else(|_| FALLBACK_FONT.to_vec()); + + let load_glyph_brush = |font: Vec| { + let builder = + wgpu_glyph::GlyphBrushBuilder::using_fonts_bytes(vec![ + mono_font.clone(), + font.clone(), + ])?; + + Ok(( + builder, + glyph_brush::GlyphBrushBuilder::using_font_bytes(font).build(), + )) + }; + + let (brush_builder, measure_brush) = load_glyph_brush(default_font) + .unwrap_or_else(|_: wgpu_glyph::rusttype::Error| { + log::warn!("System font failed to load. Falling back to embedded font..."); - let draw_brush = - wgpu_glyph::GlyphBrushBuilder::using_fonts_bytes(vec![ - mono_font, - default_font.clone(), - ]) + load_glyph_brush(FALLBACK_FONT.to_vec()).expect("Load fallback font") + }); + + let draw_brush = brush_builder .initial_cache_size((2048, 2048)) .build(device, wgpu::TextureFormat::Bgra8UnormSrgb); - let measure_brush = - glyph_brush::GlyphBrushBuilder::using_font_bytes(default_font) - .build(); - Pipeline { draw_brush: RefCell::new(draw_brush), draw_font_map: RefCell::new(HashMap::new()), -- cgit From 80a8a574712f1652c7311554ad358c5bc15d5515 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 26 Nov 2019 15:33:38 +0100 Subject: Stop loading a mono font just for the debug view --- wgpu/src/text.rs | 18 +++--------------- winit/src/debug/basic.rs | 2 +- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index f29440c7..e9a1602f 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -30,15 +30,10 @@ impl Pipeline { .load(&[font::Family::SansSerif, font::Family::Serif]) .unwrap_or_else(|_| FALLBACK_FONT.to_vec()); - let mono_font = font_source - .load(&[font::Family::Monospace]) - .unwrap_or_else(|_| FALLBACK_FONT.to_vec()); - let load_glyph_brush = |font: Vec| { let builder = wgpu_glyph::GlyphBrushBuilder::using_fonts_bytes(vec![ - mono_font.clone(), - font.clone(), + font.clone() ])?; Ok(( @@ -109,14 +104,7 @@ impl Pipeline { text: content, scale: wgpu_glyph::Scale { x: size, y: size }, bounds: (bounds.width, bounds.height), - - // TODO: This is a bit hacky. We are loading the debug font as the - // first font in the `draw_brush`. The `measure_brush` does not - // contain this, hence we subtract 1. - // - // This should go away once we unify `draw_brush` and - // `measure_brush`. - font_id: wgpu_glyph::FontId(font_id - 1), + font_id: wgpu_glyph::FontId(font_id), ..Default::default() }; @@ -157,7 +145,7 @@ impl Pipeline { pub fn find_font(&self, font: iced_native::Font) -> wgpu_glyph::FontId { match font { - iced_native::Font::Default => wgpu_glyph::FontId(1), + iced_native::Font::Default => wgpu_glyph::FontId(0), iced_native::Font::External { name, bytes } => { if let Some(font_id) = self.draw_font_map.borrow().get(name) { return *font_id; diff --git a/winit/src/debug/basic.rs b/winit/src/debug/basic.rs index 67c6d8a2..c9da392c 100644 --- a/winit/src/debug/basic.rs +++ b/winit/src/debug/basic.rs @@ -146,7 +146,7 @@ impl Debug { let mut lines = Vec::new(); fn key_value(key: &str, value: T) -> String { - format!("{: <30} {:?}", key, value) + format!("{} {:?}", key, value) } lines.push(format!( -- cgit From 826bc374b1a2d3758432dc10663f0fbeed459383 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 28 Nov 2019 21:05:17 +0100 Subject: Process events only when necessary Additionally, this also fixes a bug where the old size was being used for layouting after a resize. --- winit/src/application.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/winit/src/application.rs b/winit/src/application.rs index 1042b412..fb40156a 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -110,7 +110,7 @@ pub trait Application: Sized { let dpi = window.hidpi_factor(); let mut size = window.inner_size(); - let mut new_size: Option = None; + let mut resized = false; let mut renderer = Self::Renderer::new(); @@ -143,6 +143,11 @@ pub trait Application: Sized { event_loop.run(move |event, _, control_flow| match event { event::Event::MainEventsCleared => { + if events.is_empty() && external_messages.is_empty() && !resized + { + return; + } + // TODO: We should be able to keep a user interface alive // between events once we remove state references. // @@ -217,9 +222,9 @@ pub trait Application: Sized { event::Event::RedrawRequested(_) => { debug.render_started(); - if let Some(new_size) = new_size.take() { + if resized { let dpi = window.hidpi_factor(); - let (width, height) = to_physical(new_size, dpi); + let (width, height) = to_physical(size, dpi); target.resize( width, @@ -228,7 +233,7 @@ pub trait Application: Sized { &renderer, ); - size = new_size; + resized = false; } let new_mouse_cursor = @@ -320,8 +325,9 @@ pub trait Application: Sized { WindowEvent::CloseRequested => { *control_flow = ControlFlow::Exit; } - WindowEvent::Resized(size) => { - new_size = Some(size.into()); + WindowEvent::Resized(new_size) => { + size = new_size; + resized = true; log::debug!("Resized: {:?}", new_size); } -- cgit From 62725a76ef4f567c16d569fb5da5ffcc0481cb2a Mon Sep 17 00:00:00 2001 From: Nikolai Vazquez Date: Fri, 29 Nov 2019 20:52:26 -0500 Subject: Add `From` impls for `Length`, `Point`, and `Size` --- core/src/length.rs | 6 ++++++ core/src/point.rs | 12 ++++++++++++ native/src/size.rs | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/core/src/length.rs b/core/src/length.rs index 63ba6207..10873e89 100644 --- a/core/src/length.rs +++ b/core/src/length.rs @@ -27,3 +27,9 @@ impl Length { } } } + +impl From for Length { + fn from(units: u16) -> Self { + Length::Units(units) + } +} diff --git a/core/src/point.rs b/core/src/point.rs index 183998dd..47c8b142 100644 --- a/core/src/point.rs +++ b/core/src/point.rs @@ -19,6 +19,18 @@ impl Point { } } +impl From<[f32; 2]> for Point { + fn from([x, y]: [f32; 2]) -> Self { + Point { x, y } + } +} + +impl From<[u16; 2]> for Point { + fn from([x, y]: [u16; 2]) -> Self { + Point::new(x.into(), y.into()) + } +} + impl std::ops::Add for Point { type Output = Self; diff --git a/native/src/size.rs b/native/src/size.rs index 30e2a57e..389b3247 100644 --- a/native/src/size.rs +++ b/native/src/size.rs @@ -37,3 +37,15 @@ impl Size { } } } + +impl From<[f32; 2]> for Size { + fn from([width, height]: [f32; 2]) -> Self { + Size { width, height } + } +} + +impl From<[u16; 2]> for Size { + fn from([width, height]: [u16; 2]) -> Self { + Size::new(width.into(), height.into()) + } +} -- cgit From f0a857ddde7cf4739c1acde57e8df502e983a254 Mon Sep 17 00:00:00 2001 From: hatoo Date: Sat, 30 Nov 2019 20:38:32 +0900 Subject: Add `decorations` to Setting --- src/settings.rs | 5 +++++ winit/src/application.rs | 1 + winit/src/settings.rs | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/src/settings.rs b/src/settings.rs index 2556c51b..c9ece5ef 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -19,6 +19,9 @@ pub struct Window { /// Whether the window should be resizable or not. pub resizable: bool, + + /// Whether the window should have a border, a title bar, etc. + pub decorations: bool, } impl Default for Window { @@ -26,6 +29,7 @@ impl Default for Window { Window { size: (1024, 768), resizable: true, + decorations: true, } } } @@ -37,6 +41,7 @@ impl From for iced_winit::Settings { window: iced_winit::settings::Window { size: settings.window.size, resizable: settings.window.resizable, + decorations: settings.window.decorations, }, } } diff --git a/winit/src/application.rs b/winit/src/application.rs index 1042b412..2a0e56b7 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -105,6 +105,7 @@ pub trait Application: Sized { height: f64::from(height), }) .with_resizable(settings.window.resizable) + .with_decorations(settings.window.decorations) .build(&event_loop) .expect("Open window"); diff --git a/winit/src/settings.rs b/winit/src/settings.rs index d257ecd8..503b9dea 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -17,6 +17,9 @@ pub struct Window { /// Whether the window should be resizable or not. pub resizable: bool, + + /// Whether the window should have a border, a title bar, etc. + pub decorations: bool, } impl Default for Window { @@ -24,6 +27,7 @@ impl Default for Window { Window { size: (1024, 768), resizable: true, + decorations: true, } } } -- cgit From 5077f1dc6a6aca5ab84dd89296fb70489393cf57 Mon Sep 17 00:00:00 2001 From: hatoo Date: Sat, 30 Nov 2019 21:32:46 +0900 Subject: Add platform specific settings --- Cargo.toml | 3 ++ src/settings.rs | 48 ----------------------------- src/settings/mod.rs | 63 +++++++++++++++++++++++++++++++++++++++ src/settings/not_windows.rs | 14 +++++++++ src/settings/windows.rs | 21 +++++++++++++ winit/Cargo.toml | 3 ++ winit/src/application.rs | 21 +++++++++++++ winit/src/settings.rs | 33 -------------------- winit/src/settings/mod.rs | 47 +++++++++++++++++++++++++++++ winit/src/settings/not_windows.rs | 6 ++++ winit/src/settings/windows.rs | 9 ++++++ 11 files changed, 187 insertions(+), 81 deletions(-) delete mode 100644 src/settings.rs create mode 100644 src/settings/mod.rs create mode 100644 src/settings/not_windows.rs create mode 100644 src/settings/windows.rs delete mode 100644 winit/src/settings.rs create mode 100644 winit/src/settings/mod.rs create mode 100644 winit/src/settings/not_windows.rs create mode 100644 winit/src/settings/windows.rs diff --git a/Cargo.toml b/Cargo.toml index 7039a5b3..5361133c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,9 @@ iced_wgpu = { version = "0.1.0", path = "wgpu" } [target.'cfg(target_arch = "wasm32")'.dependencies] iced_web = { version = "0.1.0", path = "web" } +[target.'cfg(target_os = "windows")'.dependencies.winapi] +version = "0.3.6" + [dev-dependencies] env_logger = "0.7" serde = { version = "1.0", features = ["derive"] } diff --git a/src/settings.rs b/src/settings.rs deleted file mode 100644 index c9ece5ef..00000000 --- a/src/settings.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Configure your application. - -/// The settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct Settings { - /// The [`Window`] settings. - /// - /// They will be ignored on the Web. - /// - /// [`Window`]: struct.Window.html - pub window: Window, -} - -/// The window settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Window { - /// The size of the window. - pub size: (u32, u32), - - /// Whether the window should be resizable or not. - pub resizable: bool, - - /// Whether the window should have a border, a title bar, etc. - pub decorations: bool, -} - -impl Default for Window { - fn default() -> Window { - Window { - size: (1024, 768), - resizable: true, - decorations: true, - } - } -} - -#[cfg(not(target_arch = "wasm32"))] -impl From for iced_winit::Settings { - fn from(settings: Settings) -> iced_winit::Settings { - iced_winit::Settings { - window: iced_winit::settings::Window { - size: settings.window.size, - resizable: settings.window.resizable, - decorations: settings.window.decorations, - }, - } - } -} diff --git a/src/settings/mod.rs b/src/settings/mod.rs new file mode 100644 index 00000000..d64cfa13 --- /dev/null +++ b/src/settings/mod.rs @@ -0,0 +1,63 @@ +//! Configure your application. + +#[cfg(target_os = "windows")] +#[path = "windows.rs"] +pub mod platform; + +#[cfg(not(target_os = "windows"))] +#[path = "not_windows.rs"] +pub mod platform; + +pub use platform::PlatformSpecific; + +/// The settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct Settings { + /// The [`Window`] settings. + /// + /// They will be ignored on the Web. + /// + /// [`Window`]: struct.Window.html + pub window: Window, +} + +/// The window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Window { + /// The size of the window. + pub size: (u32, u32), + + /// Whether the window should be resizable or not. + pub resizable: bool, + + /// Whether the window should have a border, a title bar, etc. + pub decorations: bool, + + /// Platform specific Setting. + pub platform_specific: PlatformSpecific, +} + +impl Default for Window { + fn default() -> Window { + Window { + size: (1024, 768), + resizable: true, + decorations: true, + platform_specific: Default::default(), + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl From for iced_winit::Settings { + fn from(settings: Settings) -> iced_winit::Settings { + iced_winit::Settings { + window: iced_winit::settings::Window { + size: settings.window.size, + resizable: settings.window.resizable, + decorations: settings.window.decorations, + platform_specific: settings.window.platform_specific.into(), + }, + } + } +} diff --git a/src/settings/not_windows.rs b/src/settings/not_windows.rs new file mode 100644 index 00000000..1ae14c7f --- /dev/null +++ b/src/settings/not_windows.rs @@ -0,0 +1,14 @@ +#![cfg(not(target_os = "windows"))] +//! Platform specific settings for not Windows. + +/// The platform specific window settings of an application. +#[cfg(not(target_os = "windows"))] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PlatformSpecific {} + +#[cfg(not(target_os = "windows"))] +impl From for iced_winit::settings::PlatformSpecific { + fn from(_: PlatformSpecific) -> iced_winit::settings::PlatformSpecific { + iced_winit::settings::PlatformSpecific {} + } +} diff --git a/src/settings/windows.rs b/src/settings/windows.rs new file mode 100644 index 00000000..0372111f --- /dev/null +++ b/src/settings/windows.rs @@ -0,0 +1,21 @@ +#![cfg(target_os = "windows")] +//! Platform specific settings for Windows. + +/// The platform specific window settings of an application. +#[cfg(target_os = "windows")] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PlatformSpecific { + /// Parent Window + pub parent: Option, +} + +#[cfg(target_os = "windows")] +impl From for iced_winit::settings::PlatformSpecific { + fn from( + platform_specific: PlatformSpecific, + ) -> iced_winit::settings::PlatformSpecific { + iced_winit::settings::PlatformSpecific { + parent: platform_specific.parent, + } + } +} diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 2a33255d..bc739c6d 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -15,3 +15,6 @@ iced_native = { version = "0.1.0-alpha", path = "../native" } winit = { version = "0.20.0-alpha3", git = "https://github.com/rust-windowing/winit", rev = "709808eb4e69044705fcb214bcc30556db761405"} futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" + +[target.'cfg(target_os = "windows")'.dependencies.winapi] +version = "0.3.6" \ No newline at end of file diff --git a/winit/src/application.rs b/winit/src/application.rs index 2a0e56b7..a75d57af 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -98,6 +98,7 @@ pub trait Application: Sized { let (width, height) = settings.window.size; + #[cfg(not(target_os = "windows"))] let window = WindowBuilder::new() .with_title(&title) .with_inner_size(winit::dpi::LogicalSize { @@ -109,6 +110,26 @@ pub trait Application: Sized { .build(&event_loop) .expect("Open window"); + #[cfg(target_os = "windows")] + let window = { + use winit::platform::windows::WindowBuilderExtWindows; + + let mut window_builder = WindowBuilder::new() + .with_title(&title) + .with_inner_size(winit::dpi::LogicalSize { + width: f64::from(width), + height: f64::from(height), + }) + .with_resizable(settings.window.resizable) + .with_decorations(settings.window.decorations); + + if let Some(parent) = settings.window.platform_specific.parent { + window_builder = window_builder.with_parent_window(parent); + } + + window_builder.build(&event_loop).expect("Open window") + }; + let dpi = window.hidpi_factor(); let mut size = window.inner_size(); let mut new_size: Option = None; diff --git a/winit/src/settings.rs b/winit/src/settings.rs deleted file mode 100644 index 503b9dea..00000000 --- a/winit/src/settings.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Configure your application. - -/// The settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct Settings { - /// The [`Window`] settings - /// - /// [`Window`]: struct.Window.html - pub window: Window, -} - -/// The window settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Window { - /// The size of the window. - pub size: (u32, u32), - - /// Whether the window should be resizable or not. - pub resizable: bool, - - /// Whether the window should have a border, a title bar, etc. - pub decorations: bool, -} - -impl Default for Window { - fn default() -> Window { - Window { - size: (1024, 768), - resizable: true, - decorations: true, - } - } -} diff --git a/winit/src/settings/mod.rs b/winit/src/settings/mod.rs new file mode 100644 index 00000000..61a67bdb --- /dev/null +++ b/winit/src/settings/mod.rs @@ -0,0 +1,47 @@ +//! Configure your application. + +#[cfg(target_os = "windows")] +#[path = "windows.rs"] +pub mod platform; + +#[cfg(not(target_os = "windows"))] +#[path = "not_windows.rs"] +pub mod platform; + +pub use platform::PlatformSpecific; + +/// The settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct Settings { + /// The [`Window`] settings + /// + /// [`Window`]: struct.Window.html + pub window: Window, +} + +/// The window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Window { + /// The size of the window. + pub size: (u32, u32), + + /// Whether the window should be resizable or not. + pub resizable: bool, + + /// Whether the window should have a border, a title bar, etc. + pub decorations: bool, + + /// Platform specific Setting. + pub platform_specific: platform::PlatformSpecific, +} + +impl Default for Window { + fn default() -> Window { + Window { + size: (1024, 768), + resizable: true, + decorations: true, + platform_specific: Default::default(), + } + } +} diff --git a/winit/src/settings/not_windows.rs b/winit/src/settings/not_windows.rs new file mode 100644 index 00000000..5d703f84 --- /dev/null +++ b/winit/src/settings/not_windows.rs @@ -0,0 +1,6 @@ +#![cfg(not(target_os = "windows"))] +//! Platform specific settings for not Windows. + +/// The platform specific window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PlatformSpecific {} diff --git a/winit/src/settings/windows.rs b/winit/src/settings/windows.rs new file mode 100644 index 00000000..76b8d067 --- /dev/null +++ b/winit/src/settings/windows.rs @@ -0,0 +1,9 @@ +#![cfg(target_os = "windows")] +//! Platform specific settings for Windows. + +/// The platform specific window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PlatformSpecific { + /// Parent Window + pub parent: Option, +} -- cgit From a33f49ff4b1c11e467ec492a96e1e05e4d32d6aa Mon Sep 17 00:00:00 2001 From: hatoo Date: Sun, 1 Dec 2019 14:55:05 +0900 Subject: Remove platform-specific logic from iced Make Window.platform_specific use iced_winit::settings::PlatformSpecific --- src/settings.rs | 53 ++++++++++++++++++++++++++++++++++++++ src/settings/mod.rs | 63 --------------------------------------------- src/settings/not_windows.rs | 14 ---------- src/settings/windows.rs | 21 --------------- 4 files changed, 53 insertions(+), 98 deletions(-) create mode 100644 src/settings.rs delete mode 100644 src/settings/mod.rs delete mode 100644 src/settings/not_windows.rs delete mode 100644 src/settings/windows.rs diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 00000000..a5820d87 --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,53 @@ +//! Configure your application. + +/// The settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct Settings { + /// The [`Window`] settings. + /// + /// They will be ignored on the Web. + /// + /// [`Window`]: struct.Window.html + pub window: Window, +} + +/// The window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Window { + /// The size of the window. + pub size: (u32, u32), + + /// Whether the window should be resizable or not. + pub resizable: bool, + + /// Whether the window should have a border, a title bar, etc. + pub decorations: bool, + + /// Platform specific Setting. + pub platform_specific: iced_winit::settings::PlatformSpecific, +} + +impl Default for Window { + fn default() -> Window { + Window { + size: (1024, 768), + resizable: true, + decorations: true, + platform_specific: Default::default(), + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl From for iced_winit::Settings { + fn from(settings: Settings) -> iced_winit::Settings { + iced_winit::Settings { + window: iced_winit::settings::Window { + size: settings.window.size, + resizable: settings.window.resizable, + decorations: settings.window.decorations, + platform_specific: settings.window.platform_specific, + }, + } + } +} diff --git a/src/settings/mod.rs b/src/settings/mod.rs deleted file mode 100644 index d64cfa13..00000000 --- a/src/settings/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Configure your application. - -#[cfg(target_os = "windows")] -#[path = "windows.rs"] -pub mod platform; - -#[cfg(not(target_os = "windows"))] -#[path = "not_windows.rs"] -pub mod platform; - -pub use platform::PlatformSpecific; - -/// The settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct Settings { - /// The [`Window`] settings. - /// - /// They will be ignored on the Web. - /// - /// [`Window`]: struct.Window.html - pub window: Window, -} - -/// The window settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Window { - /// The size of the window. - pub size: (u32, u32), - - /// Whether the window should be resizable or not. - pub resizable: bool, - - /// Whether the window should have a border, a title bar, etc. - pub decorations: bool, - - /// Platform specific Setting. - pub platform_specific: PlatformSpecific, -} - -impl Default for Window { - fn default() -> Window { - Window { - size: (1024, 768), - resizable: true, - decorations: true, - platform_specific: Default::default(), - } - } -} - -#[cfg(not(target_arch = "wasm32"))] -impl From for iced_winit::Settings { - fn from(settings: Settings) -> iced_winit::Settings { - iced_winit::Settings { - window: iced_winit::settings::Window { - size: settings.window.size, - resizable: settings.window.resizable, - decorations: settings.window.decorations, - platform_specific: settings.window.platform_specific.into(), - }, - } - } -} diff --git a/src/settings/not_windows.rs b/src/settings/not_windows.rs deleted file mode 100644 index 1ae14c7f..00000000 --- a/src/settings/not_windows.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![cfg(not(target_os = "windows"))] -//! Platform specific settings for not Windows. - -/// The platform specific window settings of an application. -#[cfg(not(target_os = "windows"))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct PlatformSpecific {} - -#[cfg(not(target_os = "windows"))] -impl From for iced_winit::settings::PlatformSpecific { - fn from(_: PlatformSpecific) -> iced_winit::settings::PlatformSpecific { - iced_winit::settings::PlatformSpecific {} - } -} diff --git a/src/settings/windows.rs b/src/settings/windows.rs deleted file mode 100644 index 0372111f..00000000 --- a/src/settings/windows.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![cfg(target_os = "windows")] -//! Platform specific settings for Windows. - -/// The platform specific window settings of an application. -#[cfg(target_os = "windows")] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct PlatformSpecific { - /// Parent Window - pub parent: Option, -} - -#[cfg(target_os = "windows")] -impl From for iced_winit::settings::PlatformSpecific { - fn from( - platform_specific: PlatformSpecific, - ) -> iced_winit::settings::PlatformSpecific { - iced_winit::settings::PlatformSpecific { - parent: platform_specific.parent, - } - } -} -- cgit From 2995c50b1c98922706278d3c4bf9a668e180b18c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 1 Dec 2019 18:03:35 +0100 Subject: Schedule render after `Command` futures finish --- web/src/lib.rs | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/web/src/lib.rs b/web/src/lib.rs index 8239ffc9..782bcf93 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -138,19 +138,9 @@ pub trait Application { Self: 'static + Sized, { let (app, command) = Self::new(); - let mut instance = Instance::new(app); - instance.spawn(command); - - let window = web_sys::window().unwrap(); - - let document = window.document().unwrap(); - document.set_title(&instance.title); - - let body = document.body().unwrap(); - let vdom = dodrio::Vdom::new(&body, instance); - - vdom.forget(); + let instance = Instance::new(app); + instance.run(command); } } @@ -158,6 +148,7 @@ pub trait Application { struct Instance { title: String, ui: Rc>>>, + vdom: Rc>>, } impl Instance @@ -168,6 +159,7 @@ where Self { title: ui.title(), ui: Rc::new(RefCell::new(Box::new(ui))), + vdom: Rc::new(RefCell::new(None)), } } @@ -192,11 +184,35 @@ where for future in command.futures() { let mut instance = self.clone(); - let future = future.map(move |message| instance.update(message)); + + let future = future.map(move |message| { + instance.update(message); + + if let Some(ref vdom) = *instance.vdom.borrow() { + vdom.schedule_render(); + } + }); wasm_bindgen_futures::spawn_local(future); } } + + fn run(mut self, command: Command) { + let window = web_sys::window().unwrap(); + + let document = window.document().unwrap(); + document.set_title(&self.title); + + let body = document.body().unwrap(); + + let weak = self.vdom.clone(); + self.spawn(command); + + let vdom = dodrio::Vdom::new(&body, self); + *weak.borrow_mut() = Some(vdom.weak()); + + vdom.forget(); + } } impl dodrio::Render for Instance -- cgit From dd0bd29b682359e0b588aa3716cf3e49c98150e7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 1 Dec 2019 18:50:29 +0100 Subject: Update `CHANGELOG` --- web/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/CHANGELOG.md b/web/CHANGELOG.md index 49ddf5a3..0bdb9c37 100644 --- a/web/CHANGELOG.md +++ b/web/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- Render not being scheduled after `Command` futures finishing. ## [0.1.0] - 2019-11-25 ### Added -- cgit From 67570a0a1de7df8d26a2e2a8c7c309f4d4599631 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 1 Dec 2019 19:03:05 +0100 Subject: Add `repr(C)` to GPU types --- wgpu/src/image.rs | 2 ++ wgpu/src/quad.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 5dc972ac..40d9c318 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -427,6 +427,7 @@ pub struct Image { pub scale: [f32; 2], } +#[repr(C)] #[derive(Clone, Copy)] pub struct Vertex { _position: [f32; 2], @@ -449,6 +450,7 @@ const QUAD_VERTS: [Vertex; 4] = [ }, ]; +#[repr(C)] #[derive(Clone, Copy)] struct Instance { _position: [f32; 2], diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 3d797758..c292dec3 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -248,6 +248,7 @@ impl Pipeline { } } +#[repr(C)] #[derive(Clone, Copy)] pub struct Vertex { _position: [f32; 2], @@ -270,6 +271,7 @@ const QUAD_VERTS: [Vertex; 4] = [ }, ]; +#[repr(C)] #[derive(Debug, Clone, Copy)] pub struct Quad { pub position: [f32; 2], -- cgit From e648de560dd7372626dfce33bf508e9d41da6409 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 1 Dec 2019 23:36:29 +0100 Subject: Use `PowerPreference::Default` in `iced_wgpu` --- wgpu/src/renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index f27a4b8a..268a3630 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -49,7 +49,7 @@ impl<'a> Layer<'a> { impl Renderer { fn new() -> Self { let adapter = Adapter::request(&RequestAdapterOptions { - power_preference: PowerPreference::LowPower, + power_preference: PowerPreference::Default, backends: BackendBit::all(), }) .expect("Request adapter"); -- cgit From f9abee391d894e4bec38a0fc28b267e27caea271 Mon Sep 17 00:00:00 2001 From: Nicholas Pienta Date: Sun, 1 Dec 2019 18:13:01 -0500 Subject: Added path to image hash so it updates when path changes --- native/src/widget/image.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 4c588c9d..bcfc40b2 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -100,6 +100,7 @@ where } fn hash_layout(&self, state: &mut Hasher) { + self.path.hash(state); self.width.hash(state); self.height.hash(state); } -- cgit From a1f9be3089536681324c1a5233c3a58f47367b9f Mon Sep 17 00:00:00 2001 From: hatoo Date: Mon, 2 Dec 2019 21:44:08 +0900 Subject: Remove unneeded dependency "winapi" from iced --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5361133c..7039a5b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,6 @@ iced_wgpu = { version = "0.1.0", path = "wgpu" } [target.'cfg(target_arch = "wasm32")'.dependencies] iced_web = { version = "0.1.0", path = "web" } -[target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3.6" - [dev-dependencies] env_logger = "0.7" serde = { version = "1.0", features = ["derive"] } -- cgit From 97f1f3dcf4af8325fd24fbd70c13dbde307fce72 Mon Sep 17 00:00:00 2001 From: hatoo Date: Mon, 2 Dec 2019 21:57:07 +0900 Subject: Modify src/settings.rs to keep original API --- src/settings.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/settings.rs b/src/settings.rs index a5820d87..bd13dcaa 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -19,12 +19,6 @@ pub struct Window { /// Whether the window should be resizable or not. pub resizable: bool, - - /// Whether the window should have a border, a title bar, etc. - pub decorations: bool, - - /// Platform specific Setting. - pub platform_specific: iced_winit::settings::PlatformSpecific, } impl Default for Window { @@ -32,8 +26,6 @@ impl Default for Window { Window { size: (1024, 768), resizable: true, - decorations: true, - platform_specific: Default::default(), } } } @@ -41,13 +33,9 @@ impl Default for Window { #[cfg(not(target_arch = "wasm32"))] impl From for iced_winit::Settings { fn from(settings: Settings) -> iced_winit::Settings { - iced_winit::Settings { - window: iced_winit::settings::Window { - size: settings.window.size, - resizable: settings.window.resizable, - decorations: settings.window.decorations, - platform_specific: settings.window.platform_specific, - }, - } + let mut iced_winit_settings = iced_winit::settings::Settings::default(); + iced_winit_settings.window.size = settings.window.size; + iced_winit_settings.window.resizable = settings.window.resizable; + iced_winit_settings } } -- cgit From 287f3ea99a41f5452ea0ea2fcd075742d6ca4285 Mon Sep 17 00:00:00 2001 From: hatoo Date: Mon, 2 Dec 2019 21:58:35 +0900 Subject: Add tailing newline to winit/Cargo.toml --- winit/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winit/Cargo.toml b/winit/Cargo.toml index bc739c6d..b5b07449 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -17,4 +17,4 @@ futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" [target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3.6" \ No newline at end of file +version = "0.3.6" -- cgit From 5eec3a8867f4fbd54e2e28e5d984c2ca7ec0fea4 Mon Sep 17 00:00:00 2001 From: Friz64 Date: Mon, 25 Nov 2019 20:27:15 +0100 Subject: First implementation --- examples/tour.rs | 2 +- native/src/renderer/null.rs | 9 ++-- native/src/widget.rs | 2 +- native/src/widget/scrollable.rs | 73 ++++++++++++++++++++--------- wgpu/src/renderer/widget/scrollable.rs | 84 ++++++++++++++++++++++++---------- 5 files changed, 119 insertions(+), 51 deletions(-) diff --git a/examples/tour.rs b/examples/tour.rs index 0121c3bd..6b366957 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -145,7 +145,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 0, + current: 6, } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 182f033a..c8852ad1 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,7 +1,7 @@ use crate::{ button, checkbox, column, radio, row, scrollable, text, text_input, Background, Color, Element, Font, HorizontalAlignment, Layout, Point, - Rectangle, Renderer, Size, VerticalAlignment, + Rectangle, Renderer, ScrollbarGrab, Size, VerticalAlignment, }; /// A renderer that does nothing. @@ -61,13 +61,14 @@ impl text::Renderer for Null { } impl scrollable::Renderer for Null { - fn is_mouse_over_scrollbar( + fn scrollbar_grab( &self, _bounds: Rectangle, _content_bounds: Rectangle, + _offset: u32, _cursor_position: Point, - ) -> bool { - false + ) -> Option { + None } fn draw( diff --git a/native/src/widget.rs b/native/src/widget.rs index 71dcdc0d..a2d3aa12 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -47,7 +47,7 @@ pub use radio::Radio; #[doc(no_inline)] pub use row::Row; #[doc(no_inline)] -pub use scrollable::Scrollable; +pub use scrollable::{Scrollable, ScrollbarGrab}; #[doc(no_inline)] pub use slider::Slider; #[doc(no_inline)] diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 678d837a..fed7f54e 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -150,9 +150,11 @@ where let content = layout.children().next().unwrap(); let content_bounds = content.bounds(); - let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar( + let offset = self.state.offset(bounds, content_bounds); + let scrollbar_grab = renderer.scrollbar_grab( bounds, content_bounds, + offset, cursor_position, ); @@ -174,25 +176,45 @@ where } } - if self.state.is_scrollbar_grabbed() || is_mouse_over_scrollbar { + if self.state.currently_grabbed() || scrollbar_grab.is_some() { match event { Event::Mouse(mouse::Event::Input { button: mouse::Button::Left, state, }) => match state { ButtonState::Pressed => { - self.state.scroll_to( - cursor_position.y / (bounds.y + bounds.height), + let (scrollbar_grab, scroller_bounds) = + scrollbar_grab.unwrap(); + + let scroller_grabbed_at = match scrollbar_grab { + ScrollbarGrab::Background => 0.5, + ScrollbarGrab::Scroller => { + (cursor_position.y - scroller_bounds.y) + / scroller_bounds.height + } + }; + + let scroll_percentage = (cursor_position.y + - (scroller_bounds.height * scroller_grabbed_at)) + / (bounds.height + - (scroller_bounds.height + * scroller_grabbed_at)); + + dbg!((scroll_percentage, scroller_grabbed_at)); + /*self.state.scroll_to( + scroll_percentage, bounds, content_bounds, - ); + );*/ - self.state.scrollbar_grabbed_at = Some(cursor_position); + self.state.scroller_grabbed_at = + Some(scroller_grabbed_at); } ButtonState::Released => { - self.state.scrollbar_grabbed_at = None; + self.state.scroller_grabbed_at = None; } }, + /* TODO: Implement dragging to scroll Event::Mouse(mouse::Event::CursorMoved { .. }) => { if let Some(scrollbar_grabbed_at) = self.state.scrollbar_grabbed_at @@ -209,13 +231,13 @@ where self.state.scrollbar_grabbed_at = Some(cursor_position); } } + */ _ => {} } } let cursor_position = if is_mouse_over - && !(is_mouse_over_scrollbar - || self.state.scrollbar_grabbed_at.is_some()) + && !(scrollbar_grab.is_some() || self.state.currently_grabbed()) { Point::new( cursor_position.x, @@ -251,11 +273,9 @@ where let offset = self.state.offset(bounds, content_bounds); let is_mouse_over = bounds.contains(cursor_position); - let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar( - bounds, - content_bounds, - cursor_position, - ); + let is_mouse_over_scrollbar = renderer + .scrollbar_grab(bounds, content_bounds, offset, cursor_position) + .is_some(); let content = { let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { @@ -294,7 +314,7 @@ where /// [`Scrollable`]: struct.Scrollable.html #[derive(Debug, Clone, Copy, Default)] pub struct State { - scrollbar_grabbed_at: Option, + scroller_grabbed_at: Option, offset: f32, } @@ -357,11 +377,20 @@ impl State { } /// Returns whether the scrollbar is currently grabbed or not. - pub fn is_scrollbar_grabbed(&self) -> bool { - self.scrollbar_grabbed_at.is_some() + pub fn currently_grabbed(&self) -> bool { + self.scroller_grabbed_at.is_some() } } +#[derive(Debug, Clone, Copy)] +/// What the mouse is grabbing on the scrollbar +pub enum ScrollbarGrab { + /// The mouse is grabbing the background + Background, + /// The mouse is grabbing the scroller + Scroller, +} + /// The renderer of a [`Scrollable`]. /// /// Your [renderer] will need to implement this trait before being @@ -370,16 +399,18 @@ impl State { /// [`Scrollable`]: struct.Scrollable.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { - /// Returns whether the mouse is over the scrollbar given the bounds of - /// the [`Scrollable`] and its contents. + /// Returns what part of the scrollbar is being grabbed by the mouse + /// given the bounds of the [`Scrollable`] and its contents together + /// with the current scroller bounds. /// /// [`Scrollable`]: struct.Scrollable.html - fn is_mouse_over_scrollbar( + fn scrollbar_grab( &self, bounds: Rectangle, content_bounds: Rectangle, + offset: u32, cursor_position: Point, - ) -> bool; + ) -> Option<(ScrollbarGrab, Rectangle)>; /// Draws the [`Scrollable`]. /// diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 58dc3df9..d069b799 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -1,12 +1,13 @@ use crate::{Primitive, Renderer}; use iced_native::{ - scrollable, Background, MouseCursor, Point, Rectangle, Vector, + scrollable, Background, MouseCursor, Point, Rectangle, ScrollbarGrab, + Vector, }; const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; -fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { +fn background_bounds(bounds: Rectangle) -> Rectangle { Rectangle { x: bounds.x + bounds.width - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), @@ -16,15 +17,53 @@ fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { } } +fn scroller_bounds( + bounds: Rectangle, + content_bounds: Rectangle, + background_bounds: Rectangle, + offset: u32, +) -> Rectangle { + let ratio = bounds.height / content_bounds.height; + let scrollbar_height = bounds.height * ratio; + let y_offset = offset as f32 * ratio; + + Rectangle { + x: background_bounds.x + f32::from(SCROLLBAR_MARGIN), + y: background_bounds.y + y_offset, + width: background_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), + height: scrollbar_height, + } +} + impl scrollable::Renderer for Renderer { - fn is_mouse_over_scrollbar( + fn scrollbar_grab( &self, bounds: Rectangle, content_bounds: Rectangle, + offset: u32, cursor_position: Point, - ) -> bool { - content_bounds.height > bounds.height - && scrollbar_bounds(bounds).contains(cursor_position) + ) -> Option<(ScrollbarGrab, Rectangle)> { + let background_bounds = background_bounds(bounds); + if content_bounds.height > bounds.height + && background_bounds.contains(cursor_position) + { + let scroller_bounds = scroller_bounds( + bounds, + content_bounds, + background_bounds, + offset, + ); + + let scrollbar_grab = if scroller_bounds.contains(cursor_position) { + ScrollbarGrab::Scroller + } else { + ScrollbarGrab::Background + }; + + Some((scrollbar_grab, scroller_bounds)) + } else { + None + } } fn draw( @@ -38,7 +77,7 @@ impl scrollable::Renderer for Renderer { (content, mouse_cursor): Self::Output, ) -> Self::Output { let is_content_overflowing = content_bounds.height > bounds.height; - let scrollbar_bounds = scrollbar_bounds(bounds); + let background_bounds = background_bounds(bounds); let clip = Primitive::Clip { bounds, @@ -48,31 +87,28 @@ impl scrollable::Renderer for Renderer { ( if is_content_overflowing - && (is_mouse_over || state.is_scrollbar_grabbed()) + && (is_mouse_over || state.currently_grabbed()) { - let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; - + let scroller_bounds = scroller_bounds( + bounds, + content_bounds, + background_bounds, + offset, + ); let scrollbar = Primitive::Quad { - bounds: Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), - y: scrollbar_bounds.y + y_offset, - width: scrollbar_bounds.width - - f32::from(2 * SCROLLBAR_MARGIN), - height: scrollbar_height, - }, + bounds: scroller_bounds, background: Background::Color([0.0, 0.0, 0.0, 0.7].into()), border_radius: 5, }; - if is_mouse_over_scrollbar || state.is_scrollbar_grabbed() { + if is_mouse_over_scrollbar || state.currently_grabbed() { let scrollbar_background = Primitive::Quad { bounds: Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), - width: scrollbar_bounds.width + x: background_bounds.x + + f32::from(SCROLLBAR_MARGIN), + width: background_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - ..scrollbar_bounds + ..background_bounds }, background: Background::Color( [0.0, 0.0, 0.0, 0.3].into(), @@ -91,7 +127,7 @@ impl scrollable::Renderer for Renderer { } else { clip }, - if is_mouse_over_scrollbar || state.is_scrollbar_grabbed() { + if is_mouse_over_scrollbar || state.currently_grabbed() { MouseCursor::Idle } else { mouse_cursor -- cgit From f8fac432c665e57267243a9ee3920208b2724e6e Mon Sep 17 00:00:00 2001 From: Friz64 Date: Sat, 30 Nov 2019 17:30:42 +0100 Subject: Finalize work --- examples/tour.rs | 2 +- native/src/widget/scrollable.rs | 69 ++++++++++++++++++++-------------- wgpu/src/renderer/widget/scrollable.rs | 32 +++++++++------- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/examples/tour.rs b/examples/tour.rs index 6b366957..0121c3bd 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -145,7 +145,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 6, + current: 0, } } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index fed7f54e..17a1363e 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -151,10 +151,13 @@ where let content_bounds = content.bounds(); let offset = self.state.offset(bounds, content_bounds); + let (background_bounds, scroller_bounds) = + renderer.scrollbar_bounds(bounds, content_bounds, offset); let scrollbar_grab = renderer.scrollbar_grab( bounds, content_bounds, - offset, + background_bounds, + scroller_bounds, cursor_position, ); @@ -183,10 +186,8 @@ where state, }) => match state { ButtonState::Pressed => { - let (scrollbar_grab, scroller_bounds) = - scrollbar_grab.unwrap(); - - let scroller_grabbed_at = match scrollbar_grab { + let scroller_grabbed_at = match scrollbar_grab.unwrap() + { ScrollbarGrab::Background => 0.5, ScrollbarGrab::Scroller => { (cursor_position.y - scroller_bounds.y) @@ -194,18 +195,15 @@ where } }; - let scroll_percentage = (cursor_position.y - - (scroller_bounds.height * scroller_grabbed_at)) - / (bounds.height - - (scroller_bounds.height - * scroller_grabbed_at)); + let scroll_percentage = (cursor_position.y + bounds.y + - scroller_bounds.height * scroller_grabbed_at) + / (bounds.height - scroller_bounds.height); - dbg!((scroll_percentage, scroller_grabbed_at)); - /*self.state.scroll_to( + self.state.scroll_to( scroll_percentage, bounds, content_bounds, - );*/ + ); self.state.scroller_grabbed_at = Some(scroller_grabbed_at); @@ -214,24 +212,21 @@ where self.state.scroller_grabbed_at = None; } }, - /* TODO: Implement dragging to scroll Event::Mouse(mouse::Event::CursorMoved { .. }) => { - if let Some(scrollbar_grabbed_at) = - self.state.scrollbar_grabbed_at + if let Some(scroller_grabbed_at) = + self.state.scroller_grabbed_at { - let ratio = content_bounds.height / bounds.height; - let delta = scrollbar_grabbed_at.y - cursor_position.y; + let scroll_percentage = (cursor_position.y + bounds.y + - scroller_bounds.height * scroller_grabbed_at) + / (bounds.height - scroller_bounds.height); - self.state.scroll( - delta * ratio, + self.state.scroll_to( + scroll_percentage, bounds, content_bounds, ); - - self.state.scrollbar_grabbed_at = Some(cursor_position); } } - */ _ => {} } } @@ -273,8 +268,16 @@ where let offset = self.state.offset(bounds, content_bounds); let is_mouse_over = bounds.contains(cursor_position); + let (background_bounds, scroller_bounds) = + renderer.scrollbar_bounds(bounds, content_bounds, offset); let is_mouse_over_scrollbar = renderer - .scrollbar_grab(bounds, content_bounds, offset, cursor_position) + .scrollbar_grab( + bounds, + content_bounds, + background_bounds, + scroller_bounds, + cursor_position, + ) .is_some(); let content = { @@ -399,18 +402,28 @@ pub enum ScrollbarGrab { /// [`Scrollable`]: struct.Scrollable.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// Returns the bounds of the scrollbar + /// - Background + /// - Movable Scroller + fn scrollbar_bounds( + &self, + bounds: Rectangle, + content_bounds: Rectangle, + offset: u32, + ) -> (Rectangle, Rectangle); + /// Returns what part of the scrollbar is being grabbed by the mouse - /// given the bounds of the [`Scrollable`] and its contents together - /// with the current scroller bounds. + /// given the bounds of the [`Scrollable`] and its contents. /// /// [`Scrollable`]: struct.Scrollable.html fn scrollbar_grab( &self, bounds: Rectangle, content_bounds: Rectangle, - offset: u32, + background_bounds: Rectangle, + scroller_bounds: Rectangle, cursor_position: Point, - ) -> Option<(ScrollbarGrab, Rectangle)>; + ) -> Option; /// Draws the [`Scrollable`]. /// diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index d069b799..175fac11 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -36,31 +36,35 @@ fn scroller_bounds( } impl scrollable::Renderer for Renderer { - fn scrollbar_grab( + fn scrollbar_bounds( &self, bounds: Rectangle, content_bounds: Rectangle, offset: u32, - cursor_position: Point, - ) -> Option<(ScrollbarGrab, Rectangle)> { + ) -> (Rectangle, Rectangle) { let background_bounds = background_bounds(bounds); + let scroller_bounds = + scroller_bounds(bounds, content_bounds, background_bounds, offset); + + (background_bounds, scroller_bounds) + } + + fn scrollbar_grab( + &self, + bounds: Rectangle, + content_bounds: Rectangle, + background_bounds: Rectangle, + scroller_bounds: Rectangle, + cursor_position: Point, + ) -> Option { if content_bounds.height > bounds.height && background_bounds.contains(cursor_position) { - let scroller_bounds = scroller_bounds( - bounds, - content_bounds, - background_bounds, - offset, - ); - - let scrollbar_grab = if scroller_bounds.contains(cursor_position) { + Some(if scroller_bounds.contains(cursor_position) { ScrollbarGrab::Scroller } else { ScrollbarGrab::Background - }; - - Some((scrollbar_grab, scroller_bounds)) + }) } else { None } -- cgit From c12ba14ddad40e8f0e656ae0f8a43c87f0b5907f Mon Sep 17 00:00:00 2001 From: Friz64 Date: Sat, 30 Nov 2019 17:49:42 +0100 Subject: Move scrolling percentage logic to separate function --- native/src/widget/scrollable.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 17a1363e..ab1a203b 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -102,6 +102,17 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> { } } +fn scroll_percentage( + bounds: Rectangle, + scroller_bounds: Rectangle, + scroller_grabbed_at: f32, + cursor_position: Point, +) -> f32 { + (cursor_position.y + bounds.y + - scroller_bounds.height * scroller_grabbed_at) + / (bounds.height - scroller_bounds.height) +} + impl<'a, Message, Renderer> Widget for Scrollable<'a, Message, Renderer> where @@ -195,12 +206,13 @@ where } }; - let scroll_percentage = (cursor_position.y + bounds.y - - scroller_bounds.height * scroller_grabbed_at) - / (bounds.height - scroller_bounds.height); - self.state.scroll_to( - scroll_percentage, + scroll_percentage( + bounds, + scroller_bounds, + scroller_grabbed_at, + cursor_position, + ), bounds, content_bounds, ); @@ -216,12 +228,13 @@ where if let Some(scroller_grabbed_at) = self.state.scroller_grabbed_at { - let scroll_percentage = (cursor_position.y + bounds.y - - scroller_bounds.height * scroller_grabbed_at) - / (bounds.height - scroller_bounds.height); - self.state.scroll_to( - scroll_percentage, + scroll_percentage( + bounds, + scroller_bounds, + scroller_grabbed_at, + cursor_position, + ), bounds, content_bounds, ); -- cgit From 9a733bb3c89e15ada05fc90efb71eeae12b8c9c1 Mon Sep 17 00:00:00 2001 From: Friz64 Date: Sat, 30 Nov 2019 18:16:01 +0100 Subject: Support null renderer --- native/src/renderer/null.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index c8852ad1..c06d1dfa 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -61,11 +61,21 @@ impl text::Renderer for Null { } impl scrollable::Renderer for Null { - fn scrollbar_grab( + fn scrollbar_bounds( &self, _bounds: Rectangle, _content_bounds: Rectangle, _offset: u32, + ) -> (Rectangle, Rectangle) { + Default::default() + } + + fn scrollbar_grab( + &self, + _bounds: Rectangle, + _content_bounds: Rectangle, + _background_bounds: Rectangle, + _scroller_bounds: Rectangle, _cursor_position: Point, ) -> Option { None -- cgit From 6943041e0fad65927a741e316509bf07bdaa2f61 Mon Sep 17 00:00:00 2001 From: Friz64 Date: Mon, 2 Dec 2019 18:51:34 +0100 Subject: Address suggestions --- examples/tour.rs | 2 +- native/src/renderer/null.rs | 23 +++---- native/src/widget.rs | 2 +- native/src/widget/scrollable.rs | 108 ++++++++++++++++++++------------- wgpu/src/renderer/widget/scrollable.rs | 102 +++++++++---------------------- 5 files changed, 105 insertions(+), 132 deletions(-) diff --git a/examples/tour.rs b/examples/tour.rs index 0121c3bd..6b366957 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -145,7 +145,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 0, + current: 6, } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index c06d1dfa..2ce150c0 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,7 +1,7 @@ use crate::{ button, checkbox, column, radio, row, scrollable, text, text_input, Background, Color, Element, Font, HorizontalAlignment, Layout, Point, - Rectangle, Renderer, ScrollbarGrab, Size, VerticalAlignment, + Rectangle, Renderer, Size, VerticalAlignment, }; /// A renderer that does nothing. @@ -61,24 +61,17 @@ impl text::Renderer for Null { } impl scrollable::Renderer for Null { - fn scrollbar_bounds( - &self, - _bounds: Rectangle, - _content_bounds: Rectangle, - _offset: u32, - ) -> (Rectangle, Rectangle) { + fn scrollbar_bounds(_bounds: Rectangle) -> Rectangle { Default::default() } - fn scrollbar_grab( - &self, + fn scroller_bounds( _bounds: Rectangle, _content_bounds: Rectangle, - _background_bounds: Rectangle, - _scroller_bounds: Rectangle, - _cursor_position: Point, - ) -> Option { - None + _scrollbar_bounds: Rectangle, + _offset: u32, + ) -> Rectangle { + Default::default() } fn draw( @@ -88,6 +81,8 @@ impl scrollable::Renderer for Null { _content_bounds: Rectangle, _is_mouse_over: bool, _is_mouse_over_scrollbar: bool, + _scrollbar_bounds: Rectangle, + _scroller_bounds: Rectangle, _offset: u32, _content: Self::Output, ) { diff --git a/native/src/widget.rs b/native/src/widget.rs index a2d3aa12..71dcdc0d 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -47,7 +47,7 @@ pub use radio::Radio; #[doc(no_inline)] pub use row::Row; #[doc(no_inline)] -pub use scrollable::{Scrollable, ScrollbarGrab}; +pub use scrollable::Scrollable; #[doc(no_inline)] pub use slider::Slider; #[doc(no_inline)] diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index ab1a203b..b48ae492 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -162,12 +162,17 @@ where let content_bounds = content.bounds(); let offset = self.state.offset(bounds, content_bounds); - let (background_bounds, scroller_bounds) = - renderer.scrollbar_bounds(bounds, content_bounds, offset); - let scrollbar_grab = renderer.scrollbar_grab( + let scrollbar_bounds = Renderer::scrollbar_bounds(bounds); + let scroller_bounds = Renderer::scroller_bounds( bounds, content_bounds, - background_bounds, + scrollbar_bounds, + offset, + ); + let scrollbar_grab = ScrollbarItem::from_cursor_position( + bounds, + content_bounds, + scrollbar_bounds, scroller_bounds, cursor_position, ); @@ -190,7 +195,7 @@ where } } - if self.state.currently_grabbed() || scrollbar_grab.is_some() { + if self.state.is_scroller_grabbed() || scrollbar_grab.is_some() { match event { Event::Mouse(mouse::Event::Input { button: mouse::Button::Left, @@ -199,8 +204,8 @@ where ButtonState::Pressed => { let scroller_grabbed_at = match scrollbar_grab.unwrap() { - ScrollbarGrab::Background => 0.5, - ScrollbarGrab::Scroller => { + ScrollbarItem::Background => 0.5, + ScrollbarItem::Scroller => { (cursor_position.y - scroller_bounds.y) / scroller_bounds.height } @@ -245,7 +250,7 @@ where } let cursor_position = if is_mouse_over - && !(scrollbar_grab.is_some() || self.state.currently_grabbed()) + && !(scrollbar_grab.is_some() || self.state.is_scroller_grabbed()) { Point::new( cursor_position.x, @@ -281,17 +286,21 @@ where let offset = self.state.offset(bounds, content_bounds); let is_mouse_over = bounds.contains(cursor_position); - let (background_bounds, scroller_bounds) = - renderer.scrollbar_bounds(bounds, content_bounds, offset); - let is_mouse_over_scrollbar = renderer - .scrollbar_grab( - bounds, - content_bounds, - background_bounds, - scroller_bounds, - cursor_position, - ) - .is_some(); + let scrollbar_bounds = Renderer::scrollbar_bounds(bounds); + let scroller_bounds = Renderer::scroller_bounds( + bounds, + content_bounds, + scrollbar_bounds, + offset, + ); + let is_mouse_over_scrollbar = ScrollbarItem::from_cursor_position( + bounds, + content_bounds, + scrollbar_bounds, + scroller_bounds, + cursor_position, + ) + .is_some(); let content = { let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { @@ -310,6 +319,8 @@ where content_layout.bounds(), is_mouse_over, is_mouse_over_scrollbar, + scrollbar_bounds, + scroller_bounds, offset, content, ) @@ -392,21 +403,41 @@ impl State { self.offset.min(hidden_content as f32) as u32 } - /// Returns whether the scrollbar is currently grabbed or not. - pub fn currently_grabbed(&self) -> bool { + /// Returns whether the scroller is currently grabbed or not. + pub fn is_scroller_grabbed(&self) -> bool { self.scroller_grabbed_at.is_some() } } #[derive(Debug, Clone, Copy)] -/// What the mouse is grabbing on the scrollbar -pub enum ScrollbarGrab { - /// The mouse is grabbing the background +enum ScrollbarItem { Background, - /// The mouse is grabbing the scroller Scroller, } +impl ScrollbarItem { + /// `None` means the cursor is not over any item + fn from_cursor_position( + bounds: Rectangle, + content_bounds: Rectangle, + scrollbar_bounds: Rectangle, + scroller_bounds: Rectangle, + cursor_position: Point, + ) -> Option { + if content_bounds.height > bounds.height + && scrollbar_bounds.contains(cursor_position) + { + Some(if scroller_bounds.contains(cursor_position) { + ScrollbarItem::Scroller + } else { + ScrollbarItem::Background + }) + } else { + None + } + } +} + /// The renderer of a [`Scrollable`]. /// /// Your [renderer] will need to implement this trait before being @@ -416,27 +447,16 @@ pub enum ScrollbarGrab { /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { /// Returns the bounds of the scrollbar - /// - Background - /// - Movable Scroller - fn scrollbar_bounds( - &self, - bounds: Rectangle, - content_bounds: Rectangle, - offset: u32, - ) -> (Rectangle, Rectangle); + fn scrollbar_bounds(bounds: Rectangle) -> Rectangle; - /// Returns what part of the scrollbar is being grabbed by the mouse - /// given the bounds of the [`Scrollable`] and its contents. - /// - /// [`Scrollable`]: struct.Scrollable.html - fn scrollbar_grab( - &self, + /// Returns the bounds of the scroller + /// "The part that you can drag around with your mouse to scroll" + fn scroller_bounds( bounds: Rectangle, content_bounds: Rectangle, - background_bounds: Rectangle, - scroller_bounds: Rectangle, - cursor_position: Point, - ) -> Option; + scrollbar_bounds: Rectangle, + offset: u32, + ) -> Rectangle; /// Draws the [`Scrollable`]. /// @@ -457,6 +477,8 @@ pub trait Renderer: crate::Renderer + Sized { content_bounds: Rectangle, is_mouse_over: bool, is_mouse_over_scrollbar: bool, + scrollbar_bounds: Rectangle, + scroller_bounds: Rectangle, offset: u32, content: Self::Output, ) -> Self::Output; diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 175fac11..b83cee1b 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -1,72 +1,34 @@ use crate::{Primitive, Renderer}; -use iced_native::{ - scrollable, Background, MouseCursor, Point, Rectangle, ScrollbarGrab, - Vector, -}; +use iced_native::{scrollable, Background, MouseCursor, Rectangle, Vector}; const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; -fn background_bounds(bounds: Rectangle) -> Rectangle { - Rectangle { - x: bounds.x + bounds.width - - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - y: bounds.y, - width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - height: bounds.height, - } -} - -fn scroller_bounds( - bounds: Rectangle, - content_bounds: Rectangle, - background_bounds: Rectangle, - offset: u32, -) -> Rectangle { - let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; - - Rectangle { - x: background_bounds.x + f32::from(SCROLLBAR_MARGIN), - y: background_bounds.y + y_offset, - width: background_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - height: scrollbar_height, - } -} - impl scrollable::Renderer for Renderer { - fn scrollbar_bounds( - &self, - bounds: Rectangle, - content_bounds: Rectangle, - offset: u32, - ) -> (Rectangle, Rectangle) { - let background_bounds = background_bounds(bounds); - let scroller_bounds = - scroller_bounds(bounds, content_bounds, background_bounds, offset); - - (background_bounds, scroller_bounds) + fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { + Rectangle { + x: bounds.x + bounds.width + - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + y: bounds.y, + width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + height: bounds.height, + } } - fn scrollbar_grab( - &self, + fn scroller_bounds( bounds: Rectangle, content_bounds: Rectangle, - background_bounds: Rectangle, - scroller_bounds: Rectangle, - cursor_position: Point, - ) -> Option { - if content_bounds.height > bounds.height - && background_bounds.contains(cursor_position) - { - Some(if scroller_bounds.contains(cursor_position) { - ScrollbarGrab::Scroller - } else { - ScrollbarGrab::Background - }) - } else { - None + scrollbar_bounds: Rectangle, + offset: u32, + ) -> Rectangle { + let ratio = bounds.height / content_bounds.height; + let scrollbar_height = bounds.height * ratio; + let y_offset = offset as f32 * ratio; + Rectangle { + x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + y: scrollbar_bounds.y + y_offset, + width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), + height: scrollbar_height, } } @@ -77,11 +39,12 @@ impl scrollable::Renderer for Renderer { content_bounds: Rectangle, is_mouse_over: bool, is_mouse_over_scrollbar: bool, + scrollbar_bounds: Rectangle, + scroller_bounds: Rectangle, offset: u32, (content, mouse_cursor): Self::Output, ) -> Self::Output { let is_content_overflowing = content_bounds.height > bounds.height; - let background_bounds = background_bounds(bounds); let clip = Primitive::Clip { bounds, @@ -91,28 +54,21 @@ impl scrollable::Renderer for Renderer { ( if is_content_overflowing - && (is_mouse_over || state.currently_grabbed()) + && (is_mouse_over || state.is_scroller_grabbed()) { - let scroller_bounds = scroller_bounds( - bounds, - content_bounds, - background_bounds, - offset, - ); let scrollbar = Primitive::Quad { bounds: scroller_bounds, background: Background::Color([0.0, 0.0, 0.0, 0.7].into()), border_radius: 5, }; - if is_mouse_over_scrollbar || state.currently_grabbed() { + if is_mouse_over_scrollbar || state.is_scroller_grabbed() { let scrollbar_background = Primitive::Quad { bounds: Rectangle { - x: background_bounds.x - + f32::from(SCROLLBAR_MARGIN), - width: background_bounds.width + x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - ..background_bounds + ..scrollbar_bounds }, background: Background::Color( [0.0, 0.0, 0.0, 0.3].into(), @@ -131,7 +87,7 @@ impl scrollable::Renderer for Renderer { } else { clip }, - if is_mouse_over_scrollbar || state.currently_grabbed() { + if is_mouse_over_scrollbar || state.is_scroller_grabbed() { MouseCursor::Idle } else { mouse_cursor -- cgit From 6b25b4df2219460882f3d3c57da03d2e930e9d82 Mon Sep 17 00:00:00 2001 From: Friz64 Date: Mon, 2 Dec 2019 18:58:25 +0100 Subject: Reset tour start to 0 --- examples/tour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tour.rs b/examples/tour.rs index 6b366957..0121c3bd 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -145,7 +145,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 6, + current: 0, } } -- cgit From 31e3b6fbcb06bd5e1e5773a7c2febd0cb0092819 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 3 Dec 2019 06:48:29 +0100 Subject: Unify logic by introducing `scrollable::Scrollbar` --- native/src/renderer/null.rs | 15 +-- native/src/widget/scrollable.rs | 215 ++++++++++++++++----------------- wgpu/src/renderer/widget/scrollable.rs | 110 +++++++++-------- 3 files changed, 169 insertions(+), 171 deletions(-) diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 2ce150c0..da0e5159 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -61,17 +61,13 @@ impl text::Renderer for Null { } impl scrollable::Renderer for Null { - fn scrollbar_bounds(_bounds: Rectangle) -> Rectangle { - Default::default() - } - - fn scroller_bounds( + fn scrollbar( + &self, _bounds: Rectangle, _content_bounds: Rectangle, - _scrollbar_bounds: Rectangle, _offset: u32, - ) -> Rectangle { - Default::default() + ) -> Option { + None } fn draw( @@ -81,8 +77,7 @@ impl scrollable::Renderer for Null { _content_bounds: Rectangle, _is_mouse_over: bool, _is_mouse_over_scrollbar: bool, - _scrollbar_bounds: Rectangle, - _scroller_bounds: Rectangle, + _scrollbar: Option, _offset: u32, _content: Self::Output, ) { diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index b48ae492..7753c2d2 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -102,17 +102,6 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> { } } -fn scroll_percentage( - bounds: Rectangle, - scroller_bounds: Rectangle, - scroller_grabbed_at: f32, - cursor_position: Point, -) -> f32 { - (cursor_position.y + bounds.y - - scroller_bounds.height * scroller_grabbed_at) - / (bounds.height - scroller_bounds.height) -} - impl<'a, Message, Renderer> Widget for Scrollable<'a, Message, Renderer> where @@ -161,22 +150,6 @@ where let content = layout.children().next().unwrap(); let content_bounds = content.bounds(); - let offset = self.state.offset(bounds, content_bounds); - let scrollbar_bounds = Renderer::scrollbar_bounds(bounds); - let scroller_bounds = Renderer::scroller_bounds( - bounds, - content_bounds, - scrollbar_bounds, - offset, - ); - let scrollbar_grab = ScrollbarItem::from_cursor_position( - bounds, - content_bounds, - scrollbar_bounds, - scroller_bounds, - cursor_position, - ); - // TODO: Event capture. Nested scrollables should capture scroll events. if is_mouse_over { match event { @@ -195,48 +168,27 @@ where } } - if self.state.is_scroller_grabbed() || scrollbar_grab.is_some() { + let offset = self.state.offset(bounds, content_bounds); + let scrollbar = renderer.scrollbar(bounds, content_bounds, offset); + let is_mouse_over_scrollbar = scrollbar + .as_ref() + .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) + .unwrap_or(false); + + if self.state.is_scroller_grabbed() { match event { Event::Mouse(mouse::Event::Input { button: mouse::Button::Left, - state, - }) => match state { - ButtonState::Pressed => { - let scroller_grabbed_at = match scrollbar_grab.unwrap() - { - ScrollbarItem::Background => 0.5, - ScrollbarItem::Scroller => { - (cursor_position.y - scroller_bounds.y) - / scroller_bounds.height - } - }; - - self.state.scroll_to( - scroll_percentage( - bounds, - scroller_bounds, - scroller_grabbed_at, - cursor_position, - ), - bounds, - content_bounds, - ); - - self.state.scroller_grabbed_at = - Some(scroller_grabbed_at); - } - ButtonState::Released => { - self.state.scroller_grabbed_at = None; - } - }, + state: ButtonState::Released, + }) => { + self.state.scroller_grabbed_at = None; + } Event::Mouse(mouse::Event::CursorMoved { .. }) => { - if let Some(scroller_grabbed_at) = - self.state.scroller_grabbed_at + if let (Some(scrollbar), Some(scroller_grabbed_at)) = + (scrollbar, self.state.scroller_grabbed_at) { self.state.scroll_to( - scroll_percentage( - bounds, - scroller_bounds, + scrollbar.scroll_percentage( scroller_grabbed_at, cursor_position, ), @@ -247,11 +199,35 @@ where } _ => {} } + } else if is_mouse_over_scrollbar { + match event { + Event::Mouse(mouse::Event::Input { + button: mouse::Button::Left, + state: ButtonState::Pressed, + }) => { + if let Some(scrollbar) = scrollbar { + if let Some(scroller_grabbed_at) = + scrollbar.grab_scroller(cursor_position) + { + self.state.scroll_to( + scrollbar.scroll_percentage( + scroller_grabbed_at, + cursor_position, + ), + bounds, + content_bounds, + ); + + self.state.scroller_grabbed_at = + Some(scroller_grabbed_at); + } + } + } + _ => {} + } } - let cursor_position = if is_mouse_over - && !(scrollbar_grab.is_some() || self.state.is_scroller_grabbed()) - { + let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { Point::new( cursor_position.x, cursor_position.y @@ -284,23 +260,13 @@ where let content_layout = layout.children().next().unwrap(); let content_bounds = content_layout.bounds(); let offset = self.state.offset(bounds, content_bounds); + let scrollbar = renderer.scrollbar(bounds, content_bounds, offset); let is_mouse_over = bounds.contains(cursor_position); - let scrollbar_bounds = Renderer::scrollbar_bounds(bounds); - let scroller_bounds = Renderer::scroller_bounds( - bounds, - content_bounds, - scrollbar_bounds, - offset, - ); - let is_mouse_over_scrollbar = ScrollbarItem::from_cursor_position( - bounds, - content_bounds, - scrollbar_bounds, - scroller_bounds, - cursor_position, - ) - .is_some(); + let is_mouse_over_scrollbar = scrollbar + .as_ref() + .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) + .unwrap_or(false); let content = { let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { @@ -319,8 +285,7 @@ where content_layout.bounds(), is_mouse_over, is_mouse_over_scrollbar, - scrollbar_bounds, - scroller_bounds, + scrollbar, offset, content, ) @@ -409,33 +374,60 @@ impl State { } } -#[derive(Debug, Clone, Copy)] -enum ScrollbarItem { - Background, - Scroller, +/// The scrollbar of a [`Scrollable`]. +/// +/// [`Scrollable`]: struct.Scrollable.html +#[derive(Debug)] +pub struct Scrollbar { + /// The bounds of the [`Scrollbar`]. + /// + /// [`Scrollbar`]: struct.Scrollbar.html + pub bounds: Rectangle, + + /// The bounds of the [`Scroller`]. + /// + /// [`Scroller`]: struct.Scroller.html + pub scroller: Scroller, } -impl ScrollbarItem { - /// `None` means the cursor is not over any item - fn from_cursor_position( - bounds: Rectangle, - content_bounds: Rectangle, - scrollbar_bounds: Rectangle, - scroller_bounds: Rectangle, - cursor_position: Point, - ) -> Option { - if content_bounds.height > bounds.height - && scrollbar_bounds.contains(cursor_position) - { - Some(if scroller_bounds.contains(cursor_position) { - ScrollbarItem::Scroller +impl Scrollbar { + fn is_mouse_over(&self, cursor_position: Point) -> bool { + self.bounds.contains(cursor_position) + } + + fn grab_scroller(&self, cursor_position: Point) -> Option { + if self.bounds.contains(cursor_position) { + Some(if self.scroller.bounds.contains(cursor_position) { + (cursor_position.y - self.scroller.bounds.y) + / self.scroller.bounds.height } else { - ScrollbarItem::Background + 0.5 }) } else { None } } + + fn scroll_percentage( + &self, + grabbed_at: f32, + cursor_position: Point, + ) -> f32 { + (cursor_position.y + self.bounds.y + - self.scroller.bounds.height * grabbed_at) + / (self.bounds.height - self.scroller.bounds.height) + } +} + +/// The handle of a [`Scrollbar`]. +/// +/// [`Scrollbar`]: struct.Scrollbar.html +#[derive(Debug, Clone, Copy)] +pub struct Scroller { + /// The bounds of the [`Scroller`]. + /// + /// [`Scroller`]: struct.Scrollbar.html + pub bounds: Rectangle, } /// The renderer of a [`Scrollable`]. @@ -446,17 +438,17 @@ impl ScrollbarItem { /// [`Scrollable`]: struct.Scrollable.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { - /// Returns the bounds of the scrollbar - fn scrollbar_bounds(bounds: Rectangle) -> Rectangle; - - /// Returns the bounds of the scroller - /// "The part that you can drag around with your mouse to scroll" - fn scroller_bounds( + /// Returns the [`Scrollbar`] given the bounds and content bounds of a + /// [`Scrollable`]. + /// + /// [`Scrollbar`]: struct.Scrollbar.html + /// [`Scrollable`]: struct.Scrollable.html + fn scrollbar( + &self, bounds: Rectangle, content_bounds: Rectangle, - scrollbar_bounds: Rectangle, offset: u32, - ) -> Rectangle; + ) -> Option; /// Draws the [`Scrollable`]. /// @@ -477,8 +469,7 @@ pub trait Renderer: crate::Renderer + Sized { content_bounds: Rectangle, is_mouse_over: bool, is_mouse_over_scrollbar: bool, - scrollbar_bounds: Rectangle, - scroller_bounds: Rectangle, + scrollbar: Option, offset: u32, content: Self::Output, ) -> Self::Output; diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index b83cee1b..6ef57185 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -5,30 +5,40 @@ const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; impl scrollable::Renderer for Renderer { - fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { - Rectangle { - x: bounds.x + bounds.width - - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - y: bounds.y, - width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - height: bounds.height, - } - } - - fn scroller_bounds( + fn scrollbar( + &self, bounds: Rectangle, content_bounds: Rectangle, - scrollbar_bounds: Rectangle, offset: u32, - ) -> Rectangle { - let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; - Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), - y: scrollbar_bounds.y + y_offset, - width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - height: scrollbar_height, + ) -> Option { + if content_bounds.height > bounds.height { + let scrollbar_bounds = Rectangle { + x: bounds.x + bounds.width + - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + y: bounds.y, + width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + height: bounds.height, + }; + + let ratio = bounds.height / content_bounds.height; + let scrollbar_height = bounds.height * ratio; + let y_offset = offset as f32 * ratio; + + let scroller_bounds = Rectangle { + x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + y: scrollbar_bounds.y + y_offset, + width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), + height: scrollbar_height, + }; + + Some(scrollable::Scrollbar { + bounds: scrollbar_bounds, + scroller: scrollable::Scroller { + bounds: scroller_bounds, + }, + }) + } else { + None } } @@ -36,16 +46,13 @@ impl scrollable::Renderer for Renderer { &mut self, state: &scrollable::State, bounds: Rectangle, - content_bounds: Rectangle, + _content_bounds: Rectangle, is_mouse_over: bool, is_mouse_over_scrollbar: bool, - scrollbar_bounds: Rectangle, - scroller_bounds: Rectangle, + scrollbar: Option, offset: u32, (content, mouse_cursor): Self::Output, ) -> Self::Output { - let is_content_overflowing = content_bounds.height > bounds.height; - let clip = Primitive::Clip { bounds, offset: Vector::new(0, offset), @@ -53,36 +60,41 @@ impl scrollable::Renderer for Renderer { }; ( - if is_content_overflowing - && (is_mouse_over || state.is_scroller_grabbed()) - { - let scrollbar = Primitive::Quad { - bounds: scroller_bounds, - background: Background::Color([0.0, 0.0, 0.0, 0.7].into()), - border_radius: 5, - }; - - if is_mouse_over_scrollbar || state.is_scroller_grabbed() { - let scrollbar_background = Primitive::Quad { - bounds: Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), - width: scrollbar_bounds.width - - f32::from(2 * SCROLLBAR_MARGIN), - ..scrollbar_bounds - }, + if let Some(scrollbar) = scrollbar { + if is_mouse_over || state.is_scroller_grabbed() { + let scroller = Primitive::Quad { + bounds: scrollbar.scroller.bounds, background: Background::Color( - [0.0, 0.0, 0.0, 0.3].into(), + [0.0, 0.0, 0.0, 0.7].into(), ), border_radius: 5, }; - Primitive::Group { - primitives: vec![clip, scrollbar_background, scrollbar], + if is_mouse_over_scrollbar || state.is_scroller_grabbed() { + let scrollbar = Primitive::Quad { + bounds: Rectangle { + x: scrollbar.bounds.x + + f32::from(SCROLLBAR_MARGIN), + width: scrollbar.bounds.width + - f32::from(2 * SCROLLBAR_MARGIN), + ..scrollbar.bounds + }, + background: Background::Color( + [0.0, 0.0, 0.0, 0.3].into(), + ), + border_radius: 5, + }; + + Primitive::Group { + primitives: vec![clip, scrollbar, scroller], + } + } else { + Primitive::Group { + primitives: vec![clip, scroller], + } } } else { - Primitive::Group { - primitives: vec![clip, scrollbar], - } + clip } } else { clip -- cgit From 369ed9bc2e3c09666458bd0a0c0834a402239b3f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 3 Dec 2019 07:08:12 +0100 Subject: Add `decorations` to `settings::Window` --- src/settings.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/settings.rs b/src/settings.rs index bd13dcaa..62a1a614 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -19,6 +19,9 @@ pub struct Window { /// Whether the window should be resizable or not. pub resizable: bool, + + /// Whether the window should have a border, a title bar, etc. or not. + pub decorations: bool, } impl Default for Window { @@ -26,6 +29,7 @@ impl Default for Window { Window { size: (1024, 768), resizable: true, + decorations: true, } } } @@ -33,9 +37,13 @@ impl Default for Window { #[cfg(not(target_arch = "wasm32"))] impl From for iced_winit::Settings { fn from(settings: Settings) -> iced_winit::Settings { - let mut iced_winit_settings = iced_winit::settings::Settings::default(); - iced_winit_settings.window.size = settings.window.size; - iced_winit_settings.window.resizable = settings.window.resizable; - iced_winit_settings + iced_winit::Settings { + window: iced_winit::settings::Window { + size: settings.window.size, + resizable: settings.window.resizable, + decorations: settings.window.decorations, + platform_specific: Default::default(), + }, + } } } -- cgit From 7756081fdbc93aee3f5d11fbd14e3d9f2cbefe57 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 3 Dec 2019 07:20:22 +0100 Subject: Refactor window creation in `iced_winit` --- winit/src/application.rs | 30 +++++++++++------------------- winit/src/settings/mod.rs | 12 ++++-------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/winit/src/application.rs b/winit/src/application.rs index a75d57af..00625052 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -96,25 +96,12 @@ pub trait Application: Sized { let mut title = application.title(); - let (width, height) = settings.window.size; - - #[cfg(not(target_os = "windows"))] - let window = WindowBuilder::new() - .with_title(&title) - .with_inner_size(winit::dpi::LogicalSize { - width: f64::from(width), - height: f64::from(height), - }) - .with_resizable(settings.window.resizable) - .with_decorations(settings.window.decorations) - .build(&event_loop) - .expect("Open window"); - - #[cfg(target_os = "windows")] let window = { - use winit::platform::windows::WindowBuilderExtWindows; + let mut window_builder = WindowBuilder::new(); - let mut window_builder = WindowBuilder::new() + let (width, height) = settings.window.size; + + window_builder = window_builder .with_title(&title) .with_inner_size(winit::dpi::LogicalSize { width: f64::from(width), @@ -123,8 +110,13 @@ pub trait Application: Sized { .with_resizable(settings.window.resizable) .with_decorations(settings.window.decorations); - if let Some(parent) = settings.window.platform_specific.parent { - window_builder = window_builder.with_parent_window(parent); + #[cfg(target_os = "windows")] + { + use winit::platform::windows::WindowBuilderExtWindows; + + if let Some(parent) = settings.window.platform_specific.parent { + window_builder = window_builder.with_parent_window(parent); + } } window_builder.build(&event_loop).expect("Open window") diff --git a/winit/src/settings/mod.rs b/winit/src/settings/mod.rs index 61a67bdb..151d73d7 100644 --- a/winit/src/settings/mod.rs +++ b/winit/src/settings/mod.rs @@ -1,12 +1,8 @@ //! Configure your application. -#[cfg(target_os = "windows")] -#[path = "windows.rs"] -pub mod platform; - -#[cfg(not(target_os = "windows"))] -#[path = "not_windows.rs"] -pub mod platform; +#[cfg_attr(target_os = "windows", path = "windows.rs")] +#[cfg_attr(not(target_os = "windows"), path = "not_windows.rs")] +mod platform; pub use platform::PlatformSpecific; @@ -31,7 +27,7 @@ pub struct Window { /// Whether the window should have a border, a title bar, etc. pub decorations: bool, - /// Platform specific Setting. + /// Platform specific settings. pub platform_specific: platform::PlatformSpecific, } -- cgit From 0b5409c53d2563825d5f36bcae9700ec8721c34e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 3 Dec 2019 07:40:56 +0100 Subject: Fix `TextInput` inner border radius in `iced_wgpu` --- wgpu/src/renderer/widget/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index 9ed3b415..d64fca6d 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -45,7 +45,7 @@ impl text_input::Renderer for Renderer { height: bounds.height - 2.0, }, background: Background::Color(Color::WHITE), - border_radius: 5, + border_radius: 4, }; let size = f32::from(size); -- cgit From 2cd517c09998f2dd4c8b15ba3b014d9904f957fd Mon Sep 17 00:00:00 2001 From: Friz64 Date: Tue, 3 Dec 2019 15:19:59 +0100 Subject: Correct documentation oversight --- native/src/widget/scrollable.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 7753c2d2..3c2625b7 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -454,12 +454,15 @@ pub trait Renderer: crate::Renderer + Sized { /// /// It receives: /// - the [`State`] of the [`Scrollable`] - /// - the bounds of the [`Scrollable`] + /// - the bounds of the [`Scrollable`] widget + /// - the bounds of the [`Scrollable`] content /// - whether the mouse is over the [`Scrollable`] or not - /// - whether the mouse is over the scrollbar or not + /// - whether the mouse is over the [`Scrollbar`] or not + /// - a optional [`Scrollbar`] to be rendered /// - the scrolling offset /// - the drawn content /// + /// [`Scrollbar`]: struct.Scrollbar.html /// [`Scrollable`]: struct.Scrollable.html /// [`State`]: struct.State.html fn draw( -- cgit