From 0ff5a02550e5d5de8fb5fd0643ea424d9e508888 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 23 May 2020 01:07:59 +0200 Subject: Rename `Layer` to `overlay::Content` --- graphics/src/layer.rs | 2 - graphics/src/layer/menu.rs | 102 ------------- graphics/src/lib.rs | 1 + graphics/src/overlay.rs | 1 + graphics/src/overlay/menu.rs | 102 +++++++++++++ native/src/layer.rs | 39 ----- native/src/layer/menu.rs | 323 ----------------------------------------- native/src/lib.rs | 4 +- native/src/overlay.rs | 45 +++--- native/src/overlay/content.rs | 34 +++++ native/src/overlay/menu.rs | 323 +++++++++++++++++++++++++++++++++++++++++ native/src/widget/combo_box.rs | 9 +- 12 files changed, 493 insertions(+), 492 deletions(-) delete mode 100644 graphics/src/layer/menu.rs create mode 100644 graphics/src/overlay.rs create mode 100644 graphics/src/overlay/menu.rs delete mode 100644 native/src/layer.rs delete mode 100644 native/src/layer/menu.rs create mode 100644 native/src/overlay/content.rs create mode 100644 native/src/overlay/menu.rs diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index ddf835a4..6aca738e 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -1,6 +1,4 @@ //! Organize rendering primitives into a flattened list of layers. -mod menu; - use crate::image; use crate::svg; use crate::triangle; diff --git a/graphics/src/layer/menu.rs b/graphics/src/layer/menu.rs deleted file mode 100644 index e94ef964..00000000 --- a/graphics/src/layer/menu.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::backend::Backend; -use crate::{Primitive, Renderer}; -use iced_native::{ - layer, mouse, Background, Color, Font, HorizontalAlignment, Point, - Rectangle, VerticalAlignment, -}; - -impl layer::menu::Renderer for Renderer -where - B: Backend, -{ - fn decorate( - &mut self, - bounds: Rectangle, - _cursor_position: Point, - (primitives, mouse_cursor): Self::Output, - ) -> Self::Output { - ( - Primitive::Group { - primitives: vec![ - Primitive::Quad { - bounds, - background: Background::Color( - [0.87, 0.87, 0.87].into(), - ), - border_color: [0.7, 0.7, 0.7].into(), - border_width: 1, - border_radius: 0, - }, - primitives, - ], - }, - mouse_cursor, - ) - } - - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - options: &[T], - hovered_option: Option, - text_size: u16, - padding: u16, - ) -> Self::Output { - use std::f32; - - let is_mouse_over = bounds.contains(cursor_position); - - let mut primitives = Vec::new(); - - for (i, option) in options.iter().enumerate() { - 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, - width: bounds.width, - height: f32::from(text_size + padding * 2), - }; - - if is_selected { - primitives.push(Primitive::Quad { - bounds, - background: Background::Color([0.4, 0.4, 1.0].into()), - border_color: Color::TRANSPARENT, - border_width: 0, - border_radius: 0, - }); - } - - primitives.push(Primitive::Text { - content: option.to_string(), - bounds: Rectangle { - x: bounds.x + f32::from(padding), - y: bounds.center_y(), - width: f32::INFINITY, - ..bounds - }, - size: f32::from(text_size), - font: Font::Default, - color: if is_selected { - Color::WHITE - } else { - Color::BLACK - }, - horizontal_alignment: HorizontalAlignment::Left, - vertical_alignment: VerticalAlignment::Center, - }); - } - - ( - Primitive::Group { primitives }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } -} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index bad35ccf..92e8432e 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,6 +9,7 @@ #![forbid(rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg))] mod antialiasing; +mod overlay; mod primitive; mod renderer; mod transformation; diff --git a/graphics/src/overlay.rs b/graphics/src/overlay.rs new file mode 100644 index 00000000..c57668d4 --- /dev/null +++ b/graphics/src/overlay.rs @@ -0,0 +1 @@ +mod menu; diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs new file mode 100644 index 00000000..f4204f25 --- /dev/null +++ b/graphics/src/overlay/menu.rs @@ -0,0 +1,102 @@ +use crate::backend::Backend; +use crate::{Primitive, Renderer}; +use iced_native::{ + mouse, overlay, Background, Color, Font, HorizontalAlignment, Point, + Rectangle, VerticalAlignment, +}; + +impl overlay::menu::Renderer for Renderer +where + B: Backend, +{ + fn decorate( + &mut self, + bounds: Rectangle, + _cursor_position: Point, + (primitives, mouse_cursor): Self::Output, + ) -> Self::Output { + ( + Primitive::Group { + primitives: vec![ + Primitive::Quad { + bounds, + background: Background::Color( + [0.87, 0.87, 0.87].into(), + ), + border_color: [0.7, 0.7, 0.7].into(), + border_width: 1, + border_radius: 0, + }, + primitives, + ], + }, + mouse_cursor, + ) + } + + fn draw( + &mut self, + bounds: Rectangle, + cursor_position: Point, + options: &[T], + hovered_option: Option, + text_size: u16, + padding: u16, + ) -> Self::Output { + use std::f32; + + let is_mouse_over = bounds.contains(cursor_position); + + let mut primitives = Vec::new(); + + for (i, option) in options.iter().enumerate() { + 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, + width: bounds.width, + height: f32::from(text_size + padding * 2), + }; + + if is_selected { + primitives.push(Primitive::Quad { + bounds, + background: Background::Color([0.4, 0.4, 1.0].into()), + border_color: Color::TRANSPARENT, + border_width: 0, + border_radius: 0, + }); + } + + primitives.push(Primitive::Text { + content: option.to_string(), + bounds: Rectangle { + x: bounds.x + f32::from(padding), + y: bounds.center_y(), + width: f32::INFINITY, + ..bounds + }, + size: f32::from(text_size), + font: Font::Default, + color: if is_selected { + Color::WHITE + } else { + Color::BLACK + }, + horizontal_alignment: HorizontalAlignment::Left, + vertical_alignment: VerticalAlignment::Center, + }); + } + + ( + Primitive::Group { primitives }, + if is_mouse_over { + mouse::Interaction::Pointer + } else { + mouse::Interaction::default() + }, + ) + } +} diff --git a/native/src/layer.rs b/native/src/layer.rs deleted file mode 100644 index eacfe94b..00000000 --- a/native/src/layer.rs +++ /dev/null @@ -1,39 +0,0 @@ -pub mod menu; - -#[doc(no_inline)] -pub use menu::Menu; - -use crate::{layout, Clipboard, Event, Hasher, Layout, Point, Size}; - -pub trait Layer -where - Renderer: crate::Renderer, -{ - fn layout( - &self, - renderer: &Renderer, - bounds: Size, - position: Point, - ) -> layout::Node; - - fn draw( - &self, - renderer: &mut Renderer, - defaults: &Renderer::Defaults, - layout: Layout<'_>, - cursor_position: Point, - ) -> Renderer::Output; - - fn hash_layout(&self, state: &mut Hasher, position: Point); - - fn on_event( - &mut self, - _event: Event, - _layout: Layout<'_>, - _cursor_position: Point, - _messages: &mut Vec, - _renderer: &Renderer, - _clipboard: Option<&dyn Clipboard>, - ) { - } -} diff --git a/native/src/layer/menu.rs b/native/src/layer/menu.rs deleted file mode 100644 index 05026a54..00000000 --- a/native/src/layer/menu.rs +++ /dev/null @@ -1,323 +0,0 @@ -use crate::{ - container, layout, mouse, scrollable, Clipboard, Container, Element, Event, - Hasher, Layer, Layout, Length, Point, Rectangle, Scrollable, Size, Vector, - Widget, -}; -use std::borrow::Cow; - -pub struct Menu<'a, Message, Renderer: self::Renderer> { - container: Container<'a, Message, Renderer>, - is_open: &'a mut bool, - width: u16, - target_height: f32, -} - -#[derive(Default)] -pub struct State { - scrollable: scrollable::State, - hovered_option: Option, - is_open: bool, -} - -impl State { - pub fn is_open(&self) -> bool { - self.is_open - } - - pub fn open(&mut self, hovered_option: Option) { - self.is_open = true; - self.hovered_option = hovered_option; - } -} - -impl<'a, Message, Renderer: self::Renderer> Menu<'a, Message, Renderer> -where - Message: 'static, - Renderer: 'a, -{ - pub fn new( - state: &'a mut State, - options: impl Into>, - on_selected: Box Message>, - width: u16, - target_height: f32, - text_size: u16, - padding: u16, - ) -> Self - where - T: Clone + ToString, - [T]: ToOwned>, - { - let container = Container::new( - Scrollable::new(&mut state.scrollable).push(List::new( - &mut state.hovered_option, - options, - on_selected, - text_size, - padding, - )), - ) - .padding(1); - - Self { - container, - is_open: &mut state.is_open, - width, - target_height, - } - } -} - -impl<'a, Message, Renderer> Layer - for Menu<'a, Message, Renderer> -where - Renderer: self::Renderer, -{ - fn layout( - &self, - renderer: &Renderer, - bounds: Size, - position: Point, - ) -> layout::Node { - let space_below = bounds.height - (position.y + self.target_height); - let space_above = position.y; - - let limits = layout::Limits::new( - Size::ZERO, - Size::new( - bounds.width - position.x, - if space_below > space_above { - space_below - } else { - space_above - }, - ), - ) - .width(Length::Units(self.width)); - - let mut node = self.container.layout(renderer, &limits); - - node.move_to(if space_below > space_above { - position + Vector::new(0.0, self.target_height) - } else { - position - Vector::new(0.0, node.size().height) - }); - - node - } - - fn hash_layout(&self, state: &mut Hasher, position: Point) { - use std::hash::Hash; - - (position.x as u32).hash(state); - (position.y as u32).hash(state); - } - - fn on_event( - &mut self, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - messages: &mut Vec, - renderer: &Renderer, - clipboard: Option<&dyn Clipboard>, - ) { - let bounds = layout.bounds(); - let current_messages = messages.len(); - - self.container.on_event( - event.clone(), - layout, - cursor_position, - messages, - renderer, - clipboard, - ); - - match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - if !bounds.contains(cursor_position) - || current_messages < messages.len() => - { - *self.is_open = false; - } - _ => {} - } - } - - fn draw( - &self, - renderer: &mut Renderer, - defaults: &Renderer::Defaults, - layout: Layout<'_>, - cursor_position: Point, - ) -> Renderer::Output { - let primitives = - self.container - .draw(renderer, defaults, layout, cursor_position); - - renderer.decorate(layout.bounds(), cursor_position, primitives) - } -} - -struct List<'a, T, Message> -where - [T]: ToOwned, -{ - hovered_option: &'a mut Option, - options: Cow<'a, [T]>, - on_selected: Box Message>, - text_size: u16, - padding: u16, -} - -impl<'a, T, Message> List<'a, T, Message> -where - [T]: ToOwned, -{ - pub fn new( - hovered_option: &'a mut Option, - options: impl Into>, - on_selected: Box Message>, - text_size: u16, - padding: u16, - ) -> Self { - List { - hovered_option, - options: options.into(), - on_selected, - text_size, - padding, - } - } -} - -impl<'a, T, Message, Renderer> Widget<'a, Message, Renderer> - for List<'a, T, Message> -where - T: ToString + Clone, - [T]: ToOwned, - Renderer: self::Renderer, -{ - fn width(&self) -> Length { - Length::Fill - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - use std::f32; - - let limits = limits.width(Length::Fill).height(Length::Shrink); - - let size = { - let intrinsic = Size::new( - 0.0, - f32::from(self.text_size + self.padding * 2) - * self.options.len() as f32, - ); - - limits.resolve(intrinsic) - }; - - layout::Node::new(size) - } - - fn hash_layout(&self, state: &mut Hasher) { - use std::hash::Hash as _; - - 0.hash(state); - } - - fn on_event( - &mut self, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - messages: &mut Vec, - _renderer: &Renderer, - _clipboard: Option<&dyn Clipboard>, - ) { - match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { - let bounds = layout.bounds(); - - if bounds.contains(cursor_position) { - if let Some(index) = *self.hovered_option { - if let Some(option) = self.options.get(index) { - messages.push((self.on_selected)(option.clone())); - } - } - } - } - Event::Mouse(mouse::Event::CursorMoved { .. }) => { - let bounds = layout.bounds(); - - if bounds.contains(cursor_position) { - *self.hovered_option = Some( - ((cursor_position.y - bounds.y) - / f32::from(self.text_size + self.padding * 2)) - as usize, - ); - } - } - _ => {} - } - } - - fn draw( - &self, - renderer: &mut Renderer, - _defaults: &Renderer::Defaults, - layout: Layout<'_>, - cursor_position: Point, - ) -> Renderer::Output { - self::Renderer::draw( - renderer, - layout.bounds(), - cursor_position, - &self.options, - *self.hovered_option, - self.text_size, - self.padding, - ) - } -} - -pub trait Renderer: scrollable::Renderer + container::Renderer { - fn decorate( - &mut self, - bounds: Rectangle, - cursor_position: Point, - primitive: Self::Output, - ) -> Self::Output; - - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - options: &[T], - hovered_option: Option, - text_size: u16, - padding: u16, - ) -> Self::Output; -} - -impl<'a, T, Message, Renderer> Into> - for List<'a, T, Message> -where - T: ToString + Clone, - [T]: ToOwned, - Message: 'static, - Renderer: self::Renderer, -{ - fn into(self) -> Element<'a, Message, Renderer> { - Element::new(self) - } -} diff --git a/native/src/lib.rs b/native/src/lib.rs index 99d80126..ea328592 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -36,9 +36,9 @@ #![forbid(unsafe_code)] #![forbid(rust_2018_idioms)] pub mod keyboard; -pub mod layer; pub mod layout; pub mod mouse; +pub mod overlay; pub mod program; pub mod renderer; pub mod subscription; @@ -49,7 +49,6 @@ mod clipboard; mod element; mod event; mod hasher; -mod overlay; mod runtime; mod user_interface; @@ -76,7 +75,6 @@ pub use debug::Debug; pub use element::Element; pub use event::Event; pub use hasher::Hasher; -pub use layer::Layer; pub use layout::Layout; pub use overlay::Overlay; pub use program::Program; diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 57f11fbf..96390348 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -1,12 +1,17 @@ -use crate::{ - layout, Clipboard, Event, Hasher, Layer, Layout, Point, Size, Vector, -}; +mod content; + +pub mod menu; + +pub use content::Content; +pub use menu::Menu; + +use crate::{layout, Clipboard, Event, Hasher, Layout, Point, Size, Vector}; use std::rc::Rc; #[allow(missing_debug_implementations)] pub struct Overlay<'a, Message, Renderer> { position: Point, - layer: Box + 'a>, + content: Box + 'a>, } impl<'a, Message, Renderer> Overlay<'a, Message, Renderer> @@ -15,9 +20,9 @@ where { pub fn new( position: Point, - layer: Box + 'a>, + content: Box + 'a>, ) -> Self { - Self { position, layer } + Self { position, content } } pub fn translate(mut self, translation: Vector) -> Self { @@ -33,12 +38,12 @@ where { Overlay { position: self.position, - layer: Box::new(Map::new(self.layer, f)), + content: Box::new(Map::new(self.content, f)), } } pub fn layout(&self, renderer: &Renderer, bounds: Size) -> layout::Node { - self.layer.layout(renderer, bounds, self.position) + self.content.layout(renderer, bounds, self.position) } pub fn draw( @@ -48,11 +53,12 @@ where layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - self.layer.draw(renderer, defaults, layout, cursor_position) + self.content + .draw(renderer, defaults, layout, cursor_position) } pub fn hash_layout(&self, state: &mut Hasher) { - self.layer.hash_layout(state, self.position); + self.content.hash_layout(state, self.position); } pub fn on_event( @@ -64,7 +70,7 @@ where renderer: &Renderer, clipboard: Option<&dyn Clipboard>, ) { - self.layer.on_event( + self.content.on_event( event, layout, cursor_position, @@ -76,20 +82,20 @@ where } struct Map<'a, A, B, Renderer> { - layer: Box + 'a>, + content: Box + 'a>, mapper: Rc B>, } impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> { pub fn new( - layer: Box + 'a>, + content: Box + 'a>, mapper: Rc B + 'static>, ) -> Map<'a, A, B, Renderer> { - Map { layer, mapper } + Map { content, mapper } } } -impl<'a, A, B, Renderer> Layer for Map<'a, A, B, Renderer> +impl<'a, A, B, Renderer> Content for Map<'a, A, B, Renderer> where Renderer: crate::Renderer, { @@ -99,7 +105,7 @@ where bounds: Size, position: Point, ) -> layout::Node { - self.layer.layout(renderer, bounds, position) + self.content.layout(renderer, bounds, position) } fn on_event( @@ -113,7 +119,7 @@ where ) { let mut original_messages = Vec::new(); - self.layer.on_event( + self.content.on_event( event, layout, cursor_position, @@ -134,10 +140,11 @@ where layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - self.layer.draw(renderer, defaults, layout, cursor_position) + self.content + .draw(renderer, defaults, layout, cursor_position) } fn hash_layout(&self, state: &mut Hasher, position: Point) { - self.layer.hash_layout(state, position); + self.content.hash_layout(state, position); } } diff --git a/native/src/overlay/content.rs b/native/src/overlay/content.rs new file mode 100644 index 00000000..5259c4b8 --- /dev/null +++ b/native/src/overlay/content.rs @@ -0,0 +1,34 @@ +use crate::{layout, Clipboard, Event, Hasher, Layout, Point, Size}; + +pub trait Content +where + Renderer: crate::Renderer, +{ + fn layout( + &self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node; + + fn draw( + &self, + renderer: &mut Renderer, + defaults: &Renderer::Defaults, + layout: Layout<'_>, + cursor_position: Point, + ) -> Renderer::Output; + + fn hash_layout(&self, state: &mut Hasher, position: Point); + + fn on_event( + &mut self, + _event: Event, + _layout: Layout<'_>, + _cursor_position: Point, + _messages: &mut Vec, + _renderer: &Renderer, + _clipboard: Option<&dyn Clipboard>, + ) { + } +} diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs new file mode 100644 index 00000000..05c41181 --- /dev/null +++ b/native/src/overlay/menu.rs @@ -0,0 +1,323 @@ +use crate::{ + container, layout, mouse, overlay, scrollable, Clipboard, Container, + Element, Event, Hasher, Layout, Length, Point, Rectangle, Scrollable, Size, + Vector, Widget, +}; +use std::borrow::Cow; + +pub struct Menu<'a, Message, Renderer: self::Renderer> { + container: Container<'a, Message, Renderer>, + is_open: &'a mut bool, + width: u16, + target_height: f32, +} + +#[derive(Default)] +pub struct State { + scrollable: scrollable::State, + hovered_option: Option, + is_open: bool, +} + +impl State { + pub fn is_open(&self) -> bool { + self.is_open + } + + pub fn open(&mut self, hovered_option: Option) { + self.is_open = true; + self.hovered_option = hovered_option; + } +} + +impl<'a, Message, Renderer: self::Renderer> Menu<'a, Message, Renderer> +where + Message: 'static, + Renderer: 'a, +{ + pub fn new( + state: &'a mut State, + options: impl Into>, + on_selected: Box Message>, + width: u16, + target_height: f32, + text_size: u16, + padding: u16, + ) -> Self + where + T: Clone + ToString, + [T]: ToOwned>, + { + let container = Container::new( + Scrollable::new(&mut state.scrollable).push(List::new( + &mut state.hovered_option, + options, + on_selected, + text_size, + padding, + )), + ) + .padding(1); + + Self { + container, + is_open: &mut state.is_open, + width, + target_height, + } + } +} + +impl<'a, Message, Renderer> overlay::Content + for Menu<'a, Message, Renderer> +where + Renderer: self::Renderer, +{ + fn layout( + &self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node { + let space_below = bounds.height - (position.y + self.target_height); + let space_above = position.y; + + let limits = layout::Limits::new( + Size::ZERO, + Size::new( + bounds.width - position.x, + if space_below > space_above { + space_below + } else { + space_above + }, + ), + ) + .width(Length::Units(self.width)); + + let mut node = self.container.layout(renderer, &limits); + + node.move_to(if space_below > space_above { + position + Vector::new(0.0, self.target_height) + } else { + position - Vector::new(0.0, node.size().height) + }); + + node + } + + fn hash_layout(&self, state: &mut Hasher, position: Point) { + use std::hash::Hash; + + (position.x as u32).hash(state); + (position.y as u32).hash(state); + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + messages: &mut Vec, + renderer: &Renderer, + clipboard: Option<&dyn Clipboard>, + ) { + let bounds = layout.bounds(); + let current_messages = messages.len(); + + self.container.on_event( + event.clone(), + layout, + cursor_position, + messages, + renderer, + clipboard, + ); + + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + if !bounds.contains(cursor_position) + || current_messages < messages.len() => + { + *self.is_open = false; + } + _ => {} + } + } + + fn draw( + &self, + renderer: &mut Renderer, + defaults: &Renderer::Defaults, + layout: Layout<'_>, + cursor_position: Point, + ) -> Renderer::Output { + let primitives = + self.container + .draw(renderer, defaults, layout, cursor_position); + + renderer.decorate(layout.bounds(), cursor_position, primitives) + } +} + +struct List<'a, T, Message> +where + [T]: ToOwned, +{ + hovered_option: &'a mut Option, + options: Cow<'a, [T]>, + on_selected: Box Message>, + text_size: u16, + padding: u16, +} + +impl<'a, T, Message> List<'a, T, Message> +where + [T]: ToOwned, +{ + pub fn new( + hovered_option: &'a mut Option, + options: impl Into>, + on_selected: Box Message>, + text_size: u16, + padding: u16, + ) -> Self { + List { + hovered_option, + options: options.into(), + on_selected, + text_size, + padding, + } + } +} + +impl<'a, T, Message, Renderer> Widget<'a, Message, Renderer> + for List<'a, T, Message> +where + T: ToString + Clone, + [T]: ToOwned, + Renderer: self::Renderer, +{ + fn width(&self) -> Length { + Length::Fill + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + use std::f32; + + let limits = limits.width(Length::Fill).height(Length::Shrink); + + let size = { + let intrinsic = Size::new( + 0.0, + f32::from(self.text_size + self.padding * 2) + * self.options.len() as f32, + ); + + limits.resolve(intrinsic) + }; + + layout::Node::new(size) + } + + fn hash_layout(&self, state: &mut Hasher) { + use std::hash::Hash as _; + + 0.hash(state); + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + messages: &mut Vec, + _renderer: &Renderer, + _clipboard: Option<&dyn Clipboard>, + ) { + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + let bounds = layout.bounds(); + + if bounds.contains(cursor_position) { + if let Some(index) = *self.hovered_option { + if let Some(option) = self.options.get(index) { + messages.push((self.on_selected)(option.clone())); + } + } + } + } + Event::Mouse(mouse::Event::CursorMoved { .. }) => { + let bounds = layout.bounds(); + + if bounds.contains(cursor_position) { + *self.hovered_option = Some( + ((cursor_position.y - bounds.y) + / f32::from(self.text_size + self.padding * 2)) + as usize, + ); + } + } + _ => {} + } + } + + fn draw( + &self, + renderer: &mut Renderer, + _defaults: &Renderer::Defaults, + layout: Layout<'_>, + cursor_position: Point, + ) -> Renderer::Output { + self::Renderer::draw( + renderer, + layout.bounds(), + cursor_position, + &self.options, + *self.hovered_option, + self.text_size, + self.padding, + ) + } +} + +pub trait Renderer: scrollable::Renderer + container::Renderer { + fn decorate( + &mut self, + bounds: Rectangle, + cursor_position: Point, + primitive: Self::Output, + ) -> Self::Output; + + fn draw( + &mut self, + bounds: Rectangle, + cursor_position: Point, + options: &[T], + hovered_option: Option, + text_size: u16, + padding: u16, + ) -> Self::Output; +} + +impl<'a, T, Message, Renderer> Into> + for List<'a, T, Message> +where + T: ToString + Clone, + [T]: ToOwned, + Message: 'static, + Renderer: self::Renderer, +{ + fn into(self) -> Element<'a, Message, Renderer> { + Element::new(self) + } +} diff --git a/native/src/widget/combo_box.rs b/native/src/widget/combo_box.rs index 0f249282..2adee884 100644 --- a/native/src/widget/combo_box.rs +++ b/native/src/widget/combo_box.rs @@ -1,7 +1,8 @@ use crate::{ - layer::{self, menu}, - layout, mouse, scrollable, text, Clipboard, Element, Event, Hasher, Layout, - Length, Overlay, Point, Rectangle, Size, Widget, + layout, mouse, + overlay::menu::{self, Menu}, + scrollable, text, Clipboard, Element, Event, Hasher, Layout, Length, + Overlay, Point, Rectangle, Size, Widget, }; use std::borrow::Cow; @@ -214,7 +215,7 @@ where Some(Overlay::new( layout.position(), - Box::new(layer::Menu::new( + Box::new(Menu::new( menu, self.options.clone(), on_selected, -- cgit