diff options
54 files changed, 712 insertions, 109 deletions
@@ -67,6 +67,7 @@ members = [ "examples/pick_list", "examples/pokedex", "examples/progress_bar", + "examples/scrollable", "examples/solar_system", "examples/stopwatch", "examples/styling", diff --git a/core/src/keyboard/key_code.rs b/core/src/keyboard/key_code.rs index 26020a57..74ead170 100644 --- a/core/src/keyboard/key_code.rs +++ b/core/src/keyboard/key_code.rs @@ -55,7 +55,7 @@ pub enum KeyCode { Y, Z, - /// The Escape key, next to F1 + /// The Escape key, next to F1. Escape, F1, @@ -83,14 +83,14 @@ pub enum KeyCode { F23, F24, - /// Print Screen/SysRq + /// Print Screen/SysRq. Snapshot, - /// Scroll Lock + /// Scroll Lock. Scroll, - /// Pause/Break key, next to Scroll lock + /// Pause/Break key, next to Scroll lock. Pause, - /// `Insert`, next to Backspace + /// `Insert`, next to Backspace. Insert, Home, Delete, @@ -103,11 +103,14 @@ pub enum KeyCode { Right, Down, + /// The Backspace key, right over Enter. Backspace, + /// The Enter key. Enter, + /// The space bar. Space, - /// The "Compose" key on Linux + /// The "Compose" key on Linux. Compose, Caret, @@ -123,12 +126,20 @@ pub enum KeyCode { Numpad7, Numpad8, Numpad9, + NumpadAdd, + NumpadDivide, + NumpadDecimal, + NumpadComma, + NumpadEnter, + NumpadEquals, + NumpadMultiply, + NumpadSubtract, AbntC1, AbntC2, - Add, Apostrophe, Apps, + Asterisk, At, Ax, Backslash, @@ -137,8 +148,6 @@ pub enum KeyCode { Colon, Comma, Convert, - Decimal, - Divide, Equals, Grave, Kana, @@ -152,19 +161,16 @@ pub enum KeyCode { MediaSelect, MediaStop, Minus, - Multiply, Mute, MyComputer, - NavigateForward, // also called "Prior" - NavigateBackward, // also called "Next" + NavigateForward, // also called "Next" + NavigateBackward, // also called "Prior" NextTrack, NoConvert, - NumpadComma, - NumpadEnter, - NumpadEquals, OEM102, Period, PlayPause, + Plus, Power, PrevTrack, RAlt, @@ -176,7 +182,6 @@ pub enum KeyCode { Slash, Sleep, Stop, - Subtract, Sysrq, Tab, Underline, diff --git a/core/src/point.rs b/core/src/point.rs index 3714aa2f..7d93538f 100644 --- a/core/src/point.rs +++ b/core/src/point.rs @@ -46,6 +46,12 @@ impl From<[u16; 2]> for Point { } } +impl From<Point> for [f32; 2] { + fn from(point: Point) -> [f32; 2] { + [point.x, point.y] + } +} + impl std::ops::Add<Vector> for Point { type Output = Self; diff --git a/core/src/size.rs b/core/src/size.rs index aceb5311..7c481935 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -56,3 +56,9 @@ impl From<[u16; 2]> for Size { Size::new(width.into(), height.into()) } } + +impl From<Size> for [f32; 2] { + fn from(size: Size) -> [f32; 2] { + [size.width, size.height] + } +} diff --git a/examples/README.md b/examples/README.md index 34a916a1..32ccf724 100644 --- a/examples/README.md +++ b/examples/README.md @@ -103,6 +103,7 @@ A bunch of simpler examples exist: - [`pick_list`](pick_list), a dropdown list of selectable options. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. - [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider. +- [`scrollable`](scrollable), a showcase of the various scrollbar width options. - [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms. - [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time. - [`svg`](svg), an application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget. diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index fec5f48c..bb2c61cb 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -284,7 +284,7 @@ impl<C: 'static + ColorSpace + Copy> ColorPicker<C> { let [s1, s2, s3] = &mut self.sliders; let [cr1, cr2, cr3] = C::COMPONENT_RANGES; - fn slider<C>( + fn slider<C: Clone>( state: &mut slider::State, range: RangeInclusive<f64>, component: f32, diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index 0eba1cd0..a0003d65 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -12,7 +12,7 @@ mod circle { use iced_graphics::{Backend, Defaults, Primitive, Renderer}; use iced_native::{ layout, mouse, Background, Color, Element, Hasher, Layout, Length, - Point, Size, Widget, + Point, Rectangle, Size, Widget, }; pub struct Circle { @@ -60,6 +60,7 @@ mod circle { _defaults: &Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> (Primitive, mouse::Interaction) { ( Primitive::Quad { diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 1c13c8d5..f650b2c1 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -15,8 +15,8 @@ mod rainbow { Backend, Defaults, Primitive, Renderer, }; use iced_native::{ - layout, mouse, Element, Hasher, Layout, Length, Point, Size, Vector, - Widget, + layout, mouse, Element, Hasher, Layout, Length, Point, Rectangle, Size, + Vector, Widget, }; pub struct Rainbow; @@ -57,6 +57,7 @@ mod rainbow { _defaults: &Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> (Primitive, mouse::Interaction) { let b = layout.bounds(); diff --git a/examples/scrollable/Cargo.toml b/examples/scrollable/Cargo.toml new file mode 100644 index 00000000..12753fb6 --- /dev/null +++ b/examples/scrollable/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "scrollable" +version = "0.1.0" +authors = ["Clark Moody <clark@clarkmoody.com>"] +edition = "2018" +publish = false + +[dependencies] +iced = { path = "../.." } diff --git a/examples/scrollable/README.md b/examples/scrollable/README.md new file mode 100644 index 00000000..ed0e31b5 --- /dev/null +++ b/examples/scrollable/README.md @@ -0,0 +1,15 @@ +# Scrollable +An example showcasing the various size and style options for the Scrollable. + +All the example code is located in the __[`main`](src/main.rs)__ file. + +<div align="center"> + <a href="./screenshot.png"> + <img src="./screenshot.png" height="640px"> + </a> +</div> + +You can run it with `cargo run`: +``` +cargo run --package scrollable +``` diff --git a/examples/scrollable/screenshot.png b/examples/scrollable/screenshot.png Binary files differnew file mode 100644 index 00000000..2d800251 --- /dev/null +++ b/examples/scrollable/screenshot.png diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs new file mode 100644 index 00000000..8dd2e20c --- /dev/null +++ b/examples/scrollable/src/main.rs @@ -0,0 +1,184 @@ +mod style; + +use iced::{ + scrollable, Column, Container, Element, Length, Radio, Row, Rule, Sandbox, + Scrollable, Settings, Space, Text, +}; + +pub fn main() -> iced::Result { + ScrollableDemo::run(Settings::default()) +} + +struct ScrollableDemo { + theme: style::Theme, + variants: Vec<Variant>, +} + +#[derive(Debug, Clone)] +enum Message { + ThemeChanged(style::Theme), +} + +impl Sandbox for ScrollableDemo { + type Message = Message; + + fn new() -> Self { + ScrollableDemo { + theme: Default::default(), + variants: Variant::all(), + } + } + + fn title(&self) -> String { + String::from("Scrollable - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::ThemeChanged(theme) => self.theme = theme, + } + } + + fn view(&mut self) -> Element<Message> { + let ScrollableDemo { + theme, variants, .. + } = self; + + let choose_theme = style::Theme::ALL.iter().fold( + Column::new().spacing(10).push(Text::new("Choose a theme:")), + |column, option| { + column.push( + Radio::new( + *option, + &format!("{:?}", option), + Some(*theme), + Message::ThemeChanged, + ) + .style(*theme), + ) + }, + ); + + let scrollable_row = Row::with_children( + variants + .iter_mut() + .map(|variant| { + let mut scrollable = Scrollable::new(&mut variant.state) + .padding(10) + .width(Length::Fill) + .height(Length::Fill) + .style(*theme) + .push(Text::new(variant.title)); + + if let Some(scrollbar_width) = variant.scrollbar_width { + scrollable = scrollable + .scrollbar_width(scrollbar_width) + .push(Text::new(format!( + "scrollbar_width: {:?}", + scrollbar_width + ))); + } + + if let Some(scrollbar_margin) = variant.scrollbar_margin { + scrollable = scrollable + .scrollbar_margin(scrollbar_margin) + .push(Text::new(format!( + "scrollbar_margin: {:?}", + scrollbar_margin + ))); + } + + if let Some(scroller_width) = variant.scroller_width { + scrollable = scrollable + .scroller_width(scroller_width) + .push(Text::new(format!( + "scroller_width: {:?}", + scroller_width + ))); + } + + scrollable = scrollable + .push(Space::with_height(Length::Units(100))) + .push(Text::new( + "Some content that should wrap within the \ + scrollable. Let's output a lot of short words, so \ + that we'll make sure to see how wrapping works \ + with these scrollbars.", + )) + .push(Space::with_height(Length::Units(1200))) + .push(Text::new("Middle")) + .push(Space::with_height(Length::Units(1200))) + .push(Text::new("The End.")); + + Container::new(scrollable) + .width(Length::Fill) + .height(Length::Fill) + .style(*theme) + .into() + }) + .collect(), + ) + .spacing(20) + .width(Length::Fill) + .height(Length::Fill); + + let content = Column::new() + .spacing(20) + .padding(20) + .push(choose_theme) + .push(Rule::horizontal(20).style(self.theme)) + .push(scrollable_row); + + Container::new(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .style(self.theme) + .into() + } +} + +/// A version of a scrollable +struct Variant { + title: &'static str, + state: scrollable::State, + scrollbar_width: Option<u16>, + scrollbar_margin: Option<u16>, + scroller_width: Option<u16>, +} + +impl Variant { + pub fn all() -> Vec<Self> { + vec![ + Self { + title: "Default Scrollbar", + state: scrollable::State::new(), + scrollbar_width: None, + scrollbar_margin: None, + scroller_width: None, + }, + Self { + title: "Slimmed & Margin", + state: scrollable::State::new(), + scrollbar_width: Some(4), + scrollbar_margin: Some(3), + scroller_width: Some(4), + }, + Self { + title: "Wide Scroller", + state: scrollable::State::new(), + scrollbar_width: Some(4), + scrollbar_margin: None, + scroller_width: Some(10), + }, + Self { + title: "Narrow Scroller", + state: scrollable::State::new(), + scrollbar_width: Some(10), + scrollbar_margin: None, + scroller_width: Some(4), + }, + ] + } +} diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs new file mode 100644 index 00000000..24d711ac --- /dev/null +++ b/examples/scrollable/src/style.rs @@ -0,0 +1,190 @@ +use iced::{container, radio, rule, scrollable}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Theme { + Light, + Dark, +} + +impl Theme { + pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; +} + +impl Default for Theme { + fn default() -> Theme { + Theme::Light + } +} + +impl From<Theme> for Box<dyn container::StyleSheet> { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Container.into(), + } + } +} + +impl From<Theme> for Box<dyn radio::StyleSheet> { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Radio.into(), + } + } +} + +impl From<Theme> for Box<dyn scrollable::StyleSheet> { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Scrollable.into(), + } + } +} + +impl From<Theme> for Box<dyn rule::StyleSheet> { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Rule.into(), + } + } +} + +mod dark { + use iced::{container, radio, rule, scrollable, Color}; + + const BACKGROUND: Color = Color::from_rgb( + 0x36 as f32 / 255.0, + 0x39 as f32 / 255.0, + 0x3F as f32 / 255.0, + ); + + const SURFACE: Color = Color::from_rgb( + 0x40 as f32 / 255.0, + 0x44 as f32 / 255.0, + 0x4B as f32 / 255.0, + ); + + const ACCENT: Color = Color::from_rgb( + 0x6F as f32 / 255.0, + 0xFF as f32 / 255.0, + 0xE9 as f32 / 255.0, + ); + + const ACTIVE: Color = Color::from_rgb( + 0x72 as f32 / 255.0, + 0x89 as f32 / 255.0, + 0xDA as f32 / 255.0, + ); + + const SCROLLBAR: Color = Color::from_rgb( + 0x2E as f32 / 255.0, + 0x33 as f32 / 255.0, + 0x38 as f32 / 255.0, + ); + + const SCROLLER: Color = Color::from_rgb( + 0x20 as f32 / 255.0, + 0x22 as f32 / 255.0, + 0x25 as f32 / 255.0, + ); + + pub struct Container; + + impl container::StyleSheet for Container { + fn style(&self) -> container::Style { + container::Style { + background: Color { + a: 0.99, + ..BACKGROUND + } + .into(), + text_color: Color::WHITE.into(), + ..container::Style::default() + } + } + } + + pub struct Radio; + + impl radio::StyleSheet for Radio { + fn active(&self) -> radio::Style { + radio::Style { + background: SURFACE.into(), + dot_color: ACTIVE, + border_width: 1, + border_color: ACTIVE, + } + } + + fn hovered(&self) -> radio::Style { + radio::Style { + background: Color { a: 0.5, ..SURFACE }.into(), + ..self.active() + } + } + } + + pub struct Scrollable; + + impl scrollable::StyleSheet for Scrollable { + fn active(&self) -> scrollable::Scrollbar { + scrollable::Scrollbar { + background: Color { + a: 0.8, + ..SCROLLBAR + } + .into(), + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + scroller: scrollable::Scroller { + color: Color { a: 0.7, ..SCROLLER }, + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + } + } + + fn hovered(&self) -> scrollable::Scrollbar { + let active = self.active(); + + scrollable::Scrollbar { + background: SCROLLBAR.into(), + scroller: scrollable::Scroller { + color: SCROLLER, + ..active.scroller + }, + ..active + } + } + + fn dragging(&self) -> scrollable::Scrollbar { + let hovered = self.hovered(); + + scrollable::Scrollbar { + scroller: scrollable::Scroller { + color: ACCENT, + ..hovered.scroller + }, + ..hovered + } + } + } + + pub struct Rule; + + impl rule::StyleSheet for Rule { + fn style(&self) -> rule::Style { + rule::Style { + color: SURFACE, + width: 2, + radius: 1, + fill_mode: rule::FillMode::Percent(30.0), + } + } + } +} diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index ec464801..560d67e2 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -689,7 +689,7 @@ fn ferris<'a>(width: u16) -> Container<'a, StepMessage> { .center_x() } -fn button<'a, Message>( +fn button<'a, Message: Clone>( state: &'a mut button::State, label: &str, ) -> Button<'a, Message> { diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml index 4652112c..b298134c 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -14,7 +14,7 @@ categories = ["gui"] debug = ["iced_winit/debug"] [dependencies] -glutin = "0.24" +glutin = "0.25" [dependencies.iced_native] version = "0.2" diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs index a952f065..f42c5e3c 100644 --- a/graphics/src/overlay/menu.rs +++ b/graphics/src/overlay/menu.rs @@ -42,6 +42,7 @@ where &mut self, bounds: Rectangle, cursor_position: Point, + viewport: &Rectangle, options: &[T], hovered_option: Option<usize>, padding: u16, @@ -52,16 +53,24 @@ where use std::f32; let is_mouse_over = bounds.contains(cursor_position); + let option_height = text_size as usize + padding as usize * 2; let mut primitives = Vec::new(); - for (i, option) in options.iter().enumerate() { + let offset = viewport.y - bounds.y; + let start = (offset / option_height as f32) as usize; + let end = + ((offset + viewport.height) / option_height as f32).ceil() as usize; + + let visible_options = &options[start..end.min(options.len())]; + + for (i, option) in visible_options.iter().enumerate() { + let i = start + i; let is_selected = hovered_option == Some(i); let bounds = Rectangle { x: bounds.x, - y: bounds.y - + ((text_size as usize + padding as usize * 2) * i) as f32, + y: bounds.y + (option_height * i) as f32, width: bounds.width, height: f32::from(text_size + padding * 2), }; diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 5d51e6d4..a880e22a 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -96,10 +96,11 @@ where widget: &dyn Widget<Message, Self>, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, color: Color, ) -> Self::Output { let (primitive, cursor) = - widget.draw(self, defaults, layout, cursor_position); + widget.draw(self, defaults, layout, cursor_position, viewport); let mut primitives = Vec::new(); diff --git a/graphics/src/widget/button.rs b/graphics/src/widget/button.rs index ecabc868..a1afc940 100644 --- a/graphics/src/widget/button.rs +++ b/graphics/src/widget/button.rs @@ -62,6 +62,7 @@ where }, content_layout, cursor_position, + &bounds, ); ( diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index bc0802e5..73778d16 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -8,8 +8,8 @@ //! [`Frame`]: struct.Frame.html use crate::{Backend, Defaults, Primitive, Renderer}; use iced_native::{ - layout, mouse, Clipboard, Element, Hasher, Layout, Length, Point, Size, - Vector, Widget, + layout, mouse, Clipboard, Element, Hasher, Layout, Length, Point, + Rectangle, Size, Vector, Widget, }; use std::hash::Hash; use std::marker::PhantomData; @@ -196,6 +196,7 @@ where _defaults: &Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> (Primitive, mouse::Interaction) { let bounds = layout.bounds(); let translation = Vector::new(bounds.x, bounds.y); diff --git a/graphics/src/widget/column.rs b/graphics/src/widget/column.rs index 6c7235c7..0cf56842 100644 --- a/graphics/src/widget/column.rs +++ b/graphics/src/widget/column.rs @@ -1,7 +1,7 @@ use crate::{Backend, Primitive, Renderer}; use iced_native::column; use iced_native::mouse; -use iced_native::{Element, Layout, Point}; +use iced_native::{Element, Layout, Point, Rectangle}; /// A container that distributes its contents vertically. pub type Column<'a, Message, Backend> = @@ -17,6 +17,7 @@ where content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Self::Output { let mut mouse_interaction = mouse::Interaction::default(); @@ -26,8 +27,13 @@ where .iter() .zip(layout.children()) .map(|(child, layout)| { - let (primitive, new_mouse_interaction) = - child.draw(self, defaults, layout, cursor_position); + let (primitive, new_mouse_interaction) = child.draw( + self, + defaults, + layout, + cursor_position, + viewport, + ); if new_mouse_interaction > mouse_interaction { mouse_interaction = new_mouse_interaction; diff --git a/graphics/src/widget/container.rs b/graphics/src/widget/container.rs index 5b3a01d2..4854f589 100644 --- a/graphics/src/widget/container.rs +++ b/graphics/src/widget/container.rs @@ -24,6 +24,7 @@ where defaults: &Defaults, bounds: Rectangle, cursor_position: Point, + viewport: &Rectangle, style_sheet: &Self::Style, content: &Element<'_, Message, Self>, content_layout: Layout<'_>, @@ -36,8 +37,13 @@ where }, }; - let (content, mouse_interaction) = - content.draw(self, &defaults, content_layout, cursor_position); + let (content, mouse_interaction) = content.draw( + self, + &defaults, + content_layout, + cursor_position, + viewport, + ); if let Some(background) = background(bounds, &style) { ( diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index aa8a3f7c..5b0eb391 100644 --- a/graphics/src/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -137,7 +137,7 @@ where let (body, body_layout) = body; let (body_primitive, body_interaction) = - body.draw(self, defaults, body_layout, cursor_position); + body.draw(self, defaults, body_layout, cursor_position, &bounds); let background = crate::widget::container::background(bounds, &style); @@ -224,6 +224,7 @@ where &defaults, controls_layout, cursor_position, + &bounds, ); ( diff --git a/graphics/src/widget/row.rs b/graphics/src/widget/row.rs index 4c1dbadc..397d80bf 100644 --- a/graphics/src/widget/row.rs +++ b/graphics/src/widget/row.rs @@ -1,7 +1,7 @@ use crate::{Backend, Primitive, Renderer}; use iced_native::mouse; use iced_native::row; -use iced_native::{Element, Layout, Point}; +use iced_native::{Element, Layout, Point, Rectangle}; /// A container that distributes its contents horizontally. pub type Row<'a, Message, Backend> = @@ -17,6 +17,7 @@ where content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Self::Output { let mut mouse_interaction = mouse::Interaction::default(); @@ -26,8 +27,13 @@ where .iter() .zip(layout.children()) .map(|(child, layout)| { - let (primitive, new_mouse_interaction) = - child.draw(self, defaults, layout, cursor_position); + let (primitive, new_mouse_interaction) = child.draw( + self, + defaults, + layout, + cursor_position, + viewport, + ); if new_mouse_interaction > mouse_interaction { mouse_interaction = new_mouse_interaction; diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index b149db0a..fed79c18 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -15,9 +15,6 @@ pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; pub type Scrollable<'a, Message, Backend> = iced_native::Scrollable<'a, Message, Renderer<Backend>>; -const SCROLLBAR_WIDTH: u16 = 10; -const SCROLLBAR_MARGIN: u16 = 2; - impl<B> scrollable::Renderer for Renderer<B> where B: Backend, @@ -29,29 +26,45 @@ where bounds: Rectangle, content_bounds: Rectangle, offset: u32, + scrollbar_width: u16, + scrollbar_margin: u16, + scroller_width: u16, ) -> Option<scrollable::Scrollbar> { if content_bounds.height > bounds.height { + let outer_width = + scrollbar_width.max(scroller_width) + 2 * scrollbar_margin; + + let outer_bounds = Rectangle { + x: bounds.x + bounds.width - outer_width as f32, + y: bounds.y, + width: outer_width as f32, + height: bounds.height, + }; + let scrollbar_bounds = Rectangle { x: bounds.x + bounds.width - - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + - f32::from(outer_width / 2 + scrollbar_width / 2), y: bounds.y, - width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + width: scrollbar_width as f32, height: bounds.height, }; let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; + let scroller_height = bounds.height * ratio; let y_offset = offset as f32 * ratio; let scroller_bounds = Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + x: bounds.x + bounds.width + - f32::from(outer_width / 2 + scroller_width / 2), y: scrollbar_bounds.y + y_offset, - width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - height: scrollbar_height, + width: scroller_width as f32, + height: scroller_height, }; Some(scrollable::Scrollbar { + outer_bounds, bounds: scrollbar_bounds, + margin: scrollbar_margin, scroller: scrollable::Scroller { bounds: scroller_bounds, }, @@ -109,12 +122,7 @@ where let scrollbar = if is_scrollbar_visible { Primitive::Quad { - bounds: Rectangle { - x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN), - width: scrollbar.bounds.width - - f32::from(2 * SCROLLBAR_MARGIN), - ..scrollbar.bounds - }, + bounds: scrollbar.bounds, background: style .background .unwrap_or(Background::Color(Color::TRANSPARENT)), diff --git a/native/src/element.rs b/native/src/element.rs index 514a135b..10e1b5fb 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -1,6 +1,7 @@ +use crate::layout; +use crate::overlay; use crate::{ - layout, overlay, Clipboard, Color, Event, Hasher, Layout, Length, Point, - Widget, + Clipboard, Color, Event, Hasher, Layout, Length, Point, Rectangle, Widget, }; /// A generic [`Widget`]. @@ -260,9 +261,10 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { self.widget - .draw(renderer, defaults, layout, cursor_position) + .draw(renderer, defaults, layout, cursor_position, viewport) } /// Computes the _layout_ hash of the [`Element`]. @@ -356,9 +358,10 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { self.widget - .draw(renderer, defaults, layout, cursor_position) + .draw(renderer, defaults, layout, cursor_position, viewport) } fn hash_layout(&self, state: &mut Hasher) { @@ -437,12 +440,14 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { renderer.explain( defaults, self.element.widget.as_ref(), layout, cursor_position, + viewport, self.color, ) } diff --git a/native/src/layout/debugger.rs b/native/src/layout/debugger.rs index e4b21609..4c6dd793 100644 --- a/native/src/layout/debugger.rs +++ b/native/src/layout/debugger.rs @@ -1,4 +1,4 @@ -use crate::{Color, Layout, Point, Renderer, Widget}; +use crate::{Color, Layout, Point, Rectangle, Renderer, Widget}; /// A renderer able to graphically explain a [`Layout`]. /// @@ -21,6 +21,7 @@ pub trait Debugger: Renderer { widget: &dyn Widget<Message, Self>, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, color: Color, ) -> Self::Output; } diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index c2df468e..4b392a8e 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -253,9 +253,13 @@ where layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - let primitives = - self.container - .draw(renderer, defaults, layout, cursor_position); + let primitives = self.container.draw( + renderer, + defaults, + layout, + cursor_position, + &layout.bounds(), + ); renderer.decorate( layout.bounds(), @@ -368,11 +372,13 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { self::Renderer::draw( renderer, layout.bounds(), cursor_position, + viewport, self.options, *self.hovered_option, self.padding, @@ -418,6 +424,7 @@ pub trait Renderer: &mut self, bounds: Rectangle, cursor_position: Point, + viewport: &Rectangle, options: &[T], hovered_option: Option<usize>, padding: u16, diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 2aee0da1..a3c3cf33 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -35,6 +35,7 @@ impl column::Renderer for Null { _content: &[Element<'_, Message, Self>], _layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) { } } @@ -46,6 +47,7 @@ impl row::Renderer for Null { _content: &[Element<'_, Message, Self>], _layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) { } } @@ -89,6 +91,9 @@ impl scrollable::Renderer for Null { _bounds: Rectangle, _content_bounds: Rectangle, _offset: u32, + _scrollbar_width: u16, + _scrollbar_margin: u16, + _scroller_width: u16, ) -> Option<scrollable::Scrollbar> { None } @@ -234,6 +239,7 @@ impl container::Renderer for Null { _defaults: &Self::Defaults, _bounds: Rectangle, _cursor_position: Point, + _viewport: &Rectangle, _style: &Self::Style, _content: &Element<'_, Message, Self>, _content_layout: Layout<'_>, diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 00a290f1..59d91f42 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -1,4 +1,6 @@ -use crate::{layout, overlay, Clipboard, Element, Event, Layout, Point, Size}; +use crate::layout; +use crate::overlay; +use crate::{Clipboard, Element, Event, Layout, Point, Rectangle, Size}; use std::hash::Hasher; @@ -327,6 +329,8 @@ where renderer: &mut Renderer, cursor_position: Point, ) -> Renderer::Output { + let viewport = Rectangle::with_size(self.bounds); + let overlay = if let Some(mut overlay) = self.root.overlay(Layout::new(&self.base.layout)) { @@ -365,6 +369,7 @@ where &Renderer::Defaults::default(), Layout::new(&self.base.layout), base_cursor, + &viewport, ); renderer.overlay( @@ -378,6 +383,7 @@ where &Renderer::Defaults::default(), Layout::new(&self.base.layout), cursor_position, + &viewport, ) } } diff --git a/native/src/widget.rs b/native/src/widget.rs index a10281df..8687ce6f 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -73,7 +73,9 @@ pub use text::Text; #[doc(no_inline)] pub use text_input::TextInput; -use crate::{layout, overlay, Clipboard, Event, Hasher, Layout, Length, Point}; +use crate::{ + layout, overlay, Clipboard, Event, Hasher, Layout, Length, Point, Rectangle, +}; /// A component that displays information and allows interaction. /// @@ -137,6 +139,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output; /// Computes the _layout_ hash of the [`Widget`]. diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index c932da2b..995ba7bc 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -18,6 +18,7 @@ use std::hash::Hash; /// # type Button<'a, Message> = /// # iced_native::Button<'a, Message, iced_native::renderer::Null>; /// # +/// #[derive(Clone)] /// enum Message { /// ButtonPressed, /// } @@ -41,6 +42,7 @@ pub struct Button<'a, Message, Renderer: self::Renderer> { impl<'a, Message, Renderer> Button<'a, Message, Renderer> where + Message: Clone, Renderer: self::Renderer, { /// Creates a new [`Button`] with some local [`State`] and the given @@ -142,8 +144,8 @@ impl State { impl<'a, Message, Renderer> Widget<Message, Renderer> for Button<'a, Message, Renderer> where - Renderer: self::Renderer, Message: Clone, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -215,6 +217,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw( defaults, @@ -272,8 +275,8 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, Message: 'a + Clone, + Renderer: 'a + self::Renderer, { fn from( button: Button<'a, Message, Renderer>, diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 99178aae..e389427e 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -180,6 +180,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { let bounds = layout.bounds(); let mut children = layout.children(); diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 42cfe9b9..e874ad42 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -1,18 +1,16 @@ //! Distribute content vertically. use std::hash::Hash; +use crate::layout; +use crate::overlay; use crate::{ - layout, overlay, Align, Clipboard, Element, Event, Hasher, Layout, Length, - Point, Widget, + Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, + Widget, }; use std::u32; /// A container that distributes its contents vertically. -/// -/// A [`Column`] will try to fill the horizontal space of its container. -/// -/// [`Column`]: struct.Column.html #[allow(missing_debug_implementations)] pub struct Column<'a, Message, Renderer> { spacing: u16, @@ -185,8 +183,15 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { - renderer.draw(defaults, &self.children, layout, cursor_position) + renderer.draw( + defaults, + &self.children, + layout, + cursor_position, + viewport, + ) } fn hash_layout(&self, state: &mut Hasher) { @@ -199,6 +204,7 @@ where self.max_height.hash(state); self.align_items.hash(state); self.spacing.hash(state); + self.padding.hash(state); for child in &self.children { child.widget.hash_layout(state); @@ -240,6 +246,7 @@ pub trait Renderer: crate::Renderer + Sized { content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Self::Output; } diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index b8316e62..5b04d699 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -191,11 +191,13 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { renderer.draw( defaults, layout.bounds(), cursor_position, + viewport, &self.style, &self.content, layout.children().next().unwrap(), @@ -242,6 +244,7 @@ pub trait Renderer: crate::Renderer { defaults: &Self::Defaults, bounds: Rectangle, cursor_position: Point, + viewport: &Rectangle, style: &Self::Style, content: &Element<'_, Message, Self>, content_layout: Layout<'_>, diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 132f249d..9586da0f 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -1,5 +1,6 @@ //! Display images in your user interface. -use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget}; +use crate::layout; +use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; use std::{ hash::{Hash, Hasher as _}, @@ -97,6 +98,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw(self.handle.clone(), layout) } diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 5180fd3b..276bfae3 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -588,6 +588,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { let picked_split = self .state diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 04478225..e086e367 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -52,7 +52,7 @@ impl<T> Default for State<T> { impl<'a, T: 'a, Message, Renderer: self::Renderer> PickList<'a, T, Message, Renderer> where - T: ToString, + T: ToString + Eq, [T]: ToOwned<Owned = Vec<T>>, { /// Creates a new [`PickList`] with the given [`State`], a list of options, @@ -256,6 +256,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { self::Renderer::draw( renderer, diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index 5ab76d47..4f8a7e1b 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -104,6 +104,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw( layout.bounds(), diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 5b8d00e9..06d3f846 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -47,6 +47,8 @@ pub struct Radio<Message, Renderer: self::Renderer + text::Renderer> { impl<Message, Renderer: self::Renderer + text::Renderer> Radio<Message, Renderer> +where + Message: Clone, { /// Creates a new [`Radio`] button. /// @@ -123,8 +125,8 @@ impl<Message, Renderer: self::Renderer + text::Renderer> impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message, Renderer> where - Renderer: self::Renderer + text::Renderer + row::Renderer, Message: Clone, + Renderer: self::Renderer + text::Renderer + row::Renderer, { fn width(&self) -> Length { self.width @@ -181,6 +183,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { let bounds = layout.bounds(); let mut children = layout.children(); @@ -264,8 +267,8 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From<Radio<Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + row::Renderer + text::Renderer, Message: 'a + Clone, + Renderer: 'a + self::Renderer + row::Renderer + text::Renderer, { fn from(radio: Radio<Message, Renderer>) -> Element<'a, Message, Renderer> { Element::new(radio) diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 2b6db224..bc8a3df1 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -1,18 +1,15 @@ //! Distribute content horizontally. -use std::hash::Hash; - +use crate::layout; +use crate::overlay; use crate::{ - layout, overlay, Align, Clipboard, Element, Event, Hasher, Layout, Length, - Point, Widget, + Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, + Widget, }; +use std::hash::Hash; use std::u32; /// A container that distributes its contents horizontally. -/// -/// A [`Row`] will try to fill the horizontal space of its container. -/// -/// [`Row`]: struct.Row.html #[allow(missing_debug_implementations)] pub struct Row<'a, Message, Renderer> { spacing: u16, @@ -186,8 +183,15 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Renderer::Output { - renderer.draw(defaults, &self.children, layout, cursor_position) + renderer.draw( + defaults, + &self.children, + layout, + cursor_position, + viewport, + ) } fn hash_layout(&self, state: &mut Hasher) { @@ -200,7 +204,7 @@ where self.max_height.hash(state); self.align_items.hash(state); self.spacing.hash(state); - self.spacing.hash(state); + self.padding.hash(state); for child in &self.children { child.widget.hash_layout(state); @@ -242,6 +246,7 @@ pub trait Renderer: crate::Renderer + Sized { children: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, + viewport: &Rectangle, ) -> Self::Output; } diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs index 25cec53b..66328c08 100644 --- a/native/src/widget/rule.rs +++ b/native/src/widget/rule.rs @@ -77,6 +77,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw(layout.bounds(), &self.style, self.is_horizontal) } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 75e97027..60ec2d7d 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -13,6 +13,9 @@ pub struct Scrollable<'a, Message, Renderer: self::Renderer> { state: &'a mut State, height: Length, max_height: u32, + scrollbar_width: u16, + scrollbar_margin: u16, + scroller_width: u16, content: Column<'a, Message, Renderer>, style: Renderer::Style, } @@ -27,6 +30,9 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { state, height: Length::Shrink, max_height: u32::MAX, + scrollbar_width: 10, + scrollbar_margin: 0, + scroller_width: 10, content: Column::new(), style: Renderer::Style::default(), } @@ -90,6 +96,32 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { self } + /// Sets the scrollbar width of the [`Scrollable`] . + /// Silently enforces a minimum value of 1. + /// + /// [`Scrollable`]: struct.Scrollable.html + pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self { + self.scrollbar_width = scrollbar_width.max(1); + self + } + + /// Sets the scrollbar margin of the [`Scrollable`] . + /// + /// [`Scrollable`]: struct.Scrollable.html + pub fn scrollbar_margin(mut self, scrollbar_margin: u16) -> Self { + self.scrollbar_margin = scrollbar_margin; + self + } + + /// Sets the scroller width of the [`Scrollable`] . + /// Silently enforces a minimum value of 1. + /// + /// [`Scrollable`]: struct.Scrollable.html + pub fn scroller_width(mut self, scroller_width: u16) -> Self { + self.scroller_width = scroller_width.max(1); + self + } + /// Sets the style of the [`Scrollable`] . /// /// [`Scrollable`]: struct.Scrollable.html @@ -178,7 +210,14 @@ where } let offset = self.state.offset(bounds, content_bounds); - let scrollbar = renderer.scrollbar(bounds, content_bounds, offset); + let scrollbar = renderer.scrollbar( + bounds, + content_bounds, + offset, + self.scrollbar_width, + self.scrollbar_margin, + self.scroller_width, + ); let is_mouse_over_scrollbar = scrollbar .as_ref() .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) @@ -264,12 +303,20 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { let bounds = layout.bounds(); 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 scrollbar = renderer.scrollbar( + bounds, + content_bounds, + offset, + self.scrollbar_width, + self.scrollbar_margin, + self.scroller_width, + ); let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over_scrollbar = scrollbar @@ -289,6 +336,10 @@ where defaults, content_layout, cursor_position, + &Rectangle { + y: bounds.y + offset as f32, + ..bounds + }, ) }; @@ -413,11 +464,23 @@ impl State { /// [`Scrollable`]: struct.Scrollable.html #[derive(Debug)] pub struct Scrollbar { + /// The outer bounds of the scrollable, including the [`Scrollbar`] and + /// [`Scroller`]. + /// + /// [`Scrollbar`]: struct.Scrollbar.html + /// [`Scroller`]: struct.Scroller.html + pub outer_bounds: Rectangle, + /// The bounds of the [`Scrollbar`]. /// /// [`Scrollbar`]: struct.Scrollbar.html pub bounds: Rectangle, + /// The margin within the [`Scrollbar`]. + /// + /// [`Scrollbar`]: struct.Scrollbar.html + pub margin: u16, + /// The bounds of the [`Scroller`]. /// /// [`Scroller`]: struct.Scroller.html @@ -426,11 +489,11 @@ pub struct Scrollbar { impl Scrollbar { fn is_mouse_over(&self, cursor_position: Point) -> bool { - self.bounds.contains(cursor_position) + self.outer_bounds.contains(cursor_position) } fn grab_scroller(&self, cursor_position: Point) -> Option<f32> { - if self.bounds.contains(cursor_position) { + if self.outer_bounds.contains(cursor_position) { Some(if self.scroller.bounds.contains(cursor_position) { (cursor_position.y - self.scroller.bounds.y) / self.scroller.bounds.height @@ -486,6 +549,9 @@ pub trait Renderer: column::Renderer + Sized { bounds: Rectangle, content_bounds: Rectangle, offset: u32, + scrollbar_width: u16, + scrollbar_margin: u16, + scroller_width: u16, ) -> Option<Scrollbar>; /// Draws the [`Scrollable`]. diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index c49053f1..d6e366aa 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -26,6 +26,7 @@ use std::{hash::Hash, ops::RangeInclusive}; /// # use iced_native::{slider, renderer::Null}; /// # /// # pub type Slider<'a, T, Message> = iced_native::Slider<'a, T, Message, Null>; +/// #[derive(Clone)] /// pub enum Message { /// SliderChanged(f32), /// } @@ -53,6 +54,7 @@ pub struct Slider<'a, T, Message, Renderer: self::Renderer> { impl<'a, T, Message, Renderer> Slider<'a, T, Message, Renderer> where T: Copy + From<u8> + std::cmp::PartialOrd, + Message: Clone, Renderer: self::Renderer, { /// Creates a new [`Slider`]. @@ -168,8 +170,8 @@ impl<'a, T, Message, Renderer> Widget<Message, Renderer> for Slider<'a, T, Message, Renderer> where T: Copy + Into<f64> + num_traits::FromPrimitive, - Renderer: self::Renderer, Message: Clone, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -257,6 +259,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { let start = *self.range.start(); let end = *self.range.end(); @@ -322,8 +325,8 @@ impl<'a, T, Message, Renderer> From<Slider<'a, T, Message, Renderer>> for Element<'a, Message, Renderer> where T: 'a + Copy + Into<f64> + num_traits::FromPrimitive, - Renderer: 'a + self::Renderer, Message: 'a + Clone, + Renderer: 'a + self::Renderer, { fn from( slider: Slider<'a, T, Message, Renderer>, diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs index f1576ffb..032f341d 100644 --- a/native/src/widget/space.rs +++ b/native/src/widget/space.rs @@ -71,6 +71,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw(layout.bounds()) } diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 114d5e41..ede1aff8 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -1,5 +1,6 @@ //! Display vector graphics in your application. -use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget}; +use crate::layout; +use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; use std::{ hash::{Hash, Hasher as _}, @@ -103,6 +104,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw(self.handle.clone(), layout) } diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index 48a69e34..c2544b8e 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -149,6 +149,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { renderer.draw( defaults, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 9e15f4be..64182f2d 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -63,7 +63,11 @@ pub struct TextInput<'a, Message, Renderer: self::Renderer> { style: Renderer::Style, } -impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> { +impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> +where + Message: Clone, + Renderer: self::Renderer, +{ /// Creates a new [`TextInput`]. /// /// It expects: @@ -175,8 +179,8 @@ impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget<Message, Renderer> for TextInput<'a, Message, Renderer> where - Renderer: self::Renderer, Message: Clone, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -493,6 +497,7 @@ where _defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, + _viewport: &Rectangle, ) -> Renderer::Output { let bounds = layout.bounds(); let text_bounds = layout.children().next().unwrap().bounds(); @@ -629,8 +634,8 @@ pub trait Renderer: text::Renderer + Sized { impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, Message: 'a + Clone, + Renderer: 'a + self::Renderer, { fn from( text_input: TextInput<'a, Message, Renderer>, diff --git a/src/application.rs b/src/application.rs index d46cd2ac..d9e25ad4 100644 --- a/src/application.rs +++ b/src/application.rs @@ -65,7 +65,7 @@ use crate::{Color, Command, Element, Executor, Settings, Subscription}; /// struct Hello; /// /// impl Application for Hello { -/// type Executor = executor::Null; +/// type Executor = executor::Default; /// type Message = (); /// type Flags = (); /// diff --git a/src/executor.rs b/src/executor.rs index 59d59a5a..ea6ab14a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,5 @@ //! Choose your preferred executor to power your application. -pub use crate::runtime::{executor::Null, Executor}; +pub use crate::runtime::Executor; pub use platform::Default; diff --git a/src/sandbox.rs b/src/sandbox.rs index c72b58d8..5a668e39 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,4 +1,3 @@ -use crate::executor; use crate::{ Application, Color, Command, Element, Error, Settings, Subscription, }; @@ -172,7 +171,7 @@ impl<T> Application for T where T: Sandbox, { - type Executor = executor::Null; + type Executor = crate::runtime::executor::Null; type Flags = (); type Message = T::Message; diff --git a/src/window/settings.rs b/src/window/settings.rs index 7bc49ce1..6b5d2985 100644 --- a/src/window/settings.rs +++ b/src/window/settings.rs @@ -18,9 +18,12 @@ pub struct Settings { /// Whether the window should have a border, a title bar, etc. or not. pub decorations: bool, - /// Whether the window should be transparent + /// Whether the window should be transparent. pub transparent: bool, + /// Whether the window will always be on top of other windows. + pub always_on_top: bool, + /// The icon of the window. pub icon: Option<Icon>, } @@ -34,6 +37,7 @@ impl Default for Settings { resizable: true, decorations: true, transparent: false, + always_on_top: false, icon: None, } } @@ -49,6 +53,7 @@ impl From<Settings> for iced_winit::settings::Window { resizable: settings.resizable, decorations: settings.decorations, transparent: settings.transparent, + always_on_top: settings.always_on_top, icon: settings.icon.map(Icon::into), platform_specific: Default::default(), } diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 06e5df9a..7cf62821 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -14,7 +14,7 @@ categories = ["gui"] debug = ["iced_native/debug"] [dependencies] -winit = "0.22" +winit = "0.23" window_clipboard = "0.1" log = "0.4" thiserror = "1.0" diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 638787ab..de38f246 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -127,7 +127,7 @@ pub fn window_event( /// [`Mode`]: ../enum.Mode.html /// [`winit`]: https://github.com/rust-windowing/winit pub fn fullscreen( - monitor: winit::monitor::MonitorHandle, + monitor: Option<winit::monitor::MonitorHandle>, mode: Mode, ) -> Option<winit::window::Fullscreen> { match mode { @@ -299,7 +299,8 @@ pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode { winit::event::VirtualKeyCode::Numpad9 => KeyCode::Numpad9, winit::event::VirtualKeyCode::AbntC1 => KeyCode::AbntC1, winit::event::VirtualKeyCode::AbntC2 => KeyCode::AbntC2, - winit::event::VirtualKeyCode::Add => KeyCode::Add, + winit::event::VirtualKeyCode::NumpadAdd => KeyCode::NumpadAdd, + winit::event::VirtualKeyCode::Plus => KeyCode::Plus, winit::event::VirtualKeyCode::Apostrophe => KeyCode::Apostrophe, winit::event::VirtualKeyCode::Apps => KeyCode::Apps, winit::event::VirtualKeyCode::At => KeyCode::At, @@ -310,8 +311,8 @@ pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode { winit::event::VirtualKeyCode::Colon => KeyCode::Colon, winit::event::VirtualKeyCode::Comma => KeyCode::Comma, winit::event::VirtualKeyCode::Convert => KeyCode::Convert, - winit::event::VirtualKeyCode::Decimal => KeyCode::Decimal, - winit::event::VirtualKeyCode::Divide => KeyCode::Divide, + winit::event::VirtualKeyCode::NumpadDecimal => KeyCode::NumpadDecimal, + winit::event::VirtualKeyCode::NumpadDivide => KeyCode::NumpadDivide, winit::event::VirtualKeyCode::Equals => KeyCode::Equals, winit::event::VirtualKeyCode::Grave => KeyCode::Grave, winit::event::VirtualKeyCode::Kana => KeyCode::Kana, @@ -325,7 +326,7 @@ pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode { winit::event::VirtualKeyCode::MediaSelect => KeyCode::MediaSelect, winit::event::VirtualKeyCode::MediaStop => KeyCode::MediaStop, winit::event::VirtualKeyCode::Minus => KeyCode::Minus, - winit::event::VirtualKeyCode::Multiply => KeyCode::Multiply, + winit::event::VirtualKeyCode::NumpadMultiply => KeyCode::NumpadMultiply, winit::event::VirtualKeyCode::Mute => KeyCode::Mute, winit::event::VirtualKeyCode::MyComputer => KeyCode::MyComputer, winit::event::VirtualKeyCode::NavigateForward => { @@ -353,7 +354,7 @@ pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode { winit::event::VirtualKeyCode::Slash => KeyCode::Slash, winit::event::VirtualKeyCode::Sleep => KeyCode::Sleep, winit::event::VirtualKeyCode::Stop => KeyCode::Stop, - winit::event::VirtualKeyCode::Subtract => KeyCode::Subtract, + winit::event::VirtualKeyCode::NumpadSubtract => KeyCode::NumpadSubtract, winit::event::VirtualKeyCode::Sysrq => KeyCode::Sysrq, winit::event::VirtualKeyCode::Tab => KeyCode::Tab, winit::event::VirtualKeyCode::Underline => KeyCode::Underline, @@ -372,6 +373,7 @@ pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode { winit::event::VirtualKeyCode::Copy => KeyCode::Copy, winit::event::VirtualKeyCode::Paste => KeyCode::Paste, winit::event::VirtualKeyCode::Cut => KeyCode::Cut, + winit::event::VirtualKeyCode::Asterisk => KeyCode::Asterisk, } } diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 92541e7d..a6b96ec7 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -45,9 +45,12 @@ pub struct Window { /// Whether the window should have a border, a title bar, etc. pub decorations: bool, - /// Whether the window should be transparent + /// Whether the window should be transparent. pub transparent: bool, + /// Whether the window will always be on top of other windows. + pub always_on_top: bool, + /// The window icon, which is also usually used in the taskbar pub icon: Option<winit::window::Icon>, @@ -61,7 +64,7 @@ impl Window { self, title: &str, mode: Mode, - primary_monitor: MonitorHandle, + primary_monitor: Option<MonitorHandle>, ) -> WindowBuilder { let mut window_builder = WindowBuilder::new(); @@ -74,6 +77,7 @@ impl Window { .with_decorations(self.decorations) .with_transparent(self.transparent) .with_window_icon(self.icon) + .with_always_on_top(self.always_on_top) .with_fullscreen(conversion::fullscreen(primary_monitor, mode)); if let Some((width, height)) = self.min_size { @@ -108,6 +112,7 @@ impl Default for Window { resizable: true, decorations: true, transparent: false, + always_on_top: false, icon: None, platform_specific: Default::default(), } |