From 1c12bad866d06b320f16609576d5937413418a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 8 Jul 2020 06:55:22 +0200 Subject: [PATCH] Split `Menu::new` into multiple builder methods --- native/src/overlay.rs | 2 +- native/src/overlay/menu.rs | 124 +++++++++++++++++++++++++-------- native/src/widget/combo_box.rs | 42 +++++------ 3 files changed, 114 insertions(+), 54 deletions(-) diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 96390348..b8c8bb48 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -32,7 +32,7 @@ where pub fn map(self, f: Rc B>) -> Overlay<'a, B, Renderer> where - Message: 'static, + Message: 'a, Renderer: 'a, B: 'static, { diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index ffa3b14c..8475f130 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -3,16 +3,74 @@ use crate::{ Element, Event, Hasher, Layout, Length, Point, Rectangle, Scrollable, Size, Vector, Widget, }; -use std::borrow::Borrow; -pub struct Menu<'a, Message, Renderer: self::Renderer> { - container: Container<'a, Message, Renderer>, - is_open: &'a mut bool, +pub struct Menu<'a, T, Message, Renderer: self::Renderer> { + state: &'a mut State, + options: &'a [T], + on_selected: &'a dyn Fn(T) -> Message, width: u16, - target_height: f32, + padding: u16, + text_size: Option, style: ::Style, } +impl<'a, T, Message, Renderer> Menu<'a, T, Message, Renderer> +where + T: ToString + Clone, + Message: 'a, + Renderer: self::Renderer + 'a, +{ + pub fn new( + state: &'a mut State, + options: &'a [T], + on_selected: &'a dyn Fn(T) -> Message, + ) -> Self { + Menu { + state, + options, + on_selected, + width: 0, + padding: 0, + text_size: None, + style: Default::default(), + } + } + + pub fn width(mut self, width: u16) -> Self { + self.width = width; + self + } + + pub fn padding(mut self, padding: u16) -> Self { + self.padding = padding; + self + } + + pub fn text_size(mut self, text_size: u16) -> Self { + self.text_size = Some(text_size); + self + } + + pub fn style( + mut self, + style: impl Into<::Style>, + ) -> Self { + self.style = style.into(); + self + } + + pub fn overlay( + self, + position: Point, + target_height: f32, + ) -> overlay::Overlay<'a, Message, Renderer> { + overlay::Overlay::new( + position, + Box::new(Overlay::new(self, target_height)), + ) + } +} + #[derive(Default)] pub struct State { scrollable: scrollable::State, @@ -31,29 +89,40 @@ impl State { } } -impl<'a, Message, Renderer: self::Renderer> Menu<'a, Message, Renderer> +struct Overlay<'a, Message, Renderer: self::Renderer> { + container: Container<'a, Message, Renderer>, + is_open: &'a mut bool, + width: u16, + target_height: f32, + style: ::Style, +} + +impl<'a, Message, Renderer: self::Renderer> Overlay<'a, Message, Renderer> where - Message: 'static, + Message: 'a, Renderer: 'a, { pub fn new( - state: &'a mut State, - options: &'a dyn Borrow<[T]>, - on_selected: &'a dyn Fn(T) -> Message, - width: u16, + menu: Menu<'a, T, Message, Renderer>, target_height: f32, - text_size: Option, - padding: u16, - style: ::Style, ) -> Self where T: Clone + ToString, - [T]: ToOwned, { + let Menu { + state, + options, + on_selected, + width, + padding, + text_size, + style, + } = menu; + let container = Container::new( Scrollable::new(&mut state.scrollable).push(List::new( - &mut state.hovered_option, options, + &mut state.hovered_option, on_selected, text_size, padding, @@ -65,15 +134,15 @@ where Self { container, is_open: &mut state.is_open, - width, + width: width, target_height, - style, + style: style, } } } impl<'a, Message, Renderer> overlay::Content - for Menu<'a, Message, Renderer> + for Overlay<'a, Message, Renderer> where Renderer: self::Renderer, { @@ -174,8 +243,8 @@ where } struct List<'a, T, Message, Renderer: self::Renderer> { + options: &'a [T], hovered_option: &'a mut Option, - options: &'a dyn Borrow<[T]>, on_selected: &'a dyn Fn(T) -> Message, text_size: Option, padding: u16, @@ -184,16 +253,16 @@ struct List<'a, T, Message, Renderer: self::Renderer> { impl<'a, T, Message, Renderer: self::Renderer> List<'a, T, Message, Renderer> { pub fn new( + options: &'a [T], hovered_option: &'a mut Option, - options: &'a dyn Borrow<[T]>, on_selected: &'a dyn Fn(T) -> Message, text_size: Option, padding: u16, style: ::Style, ) -> Self { List { - hovered_option, options, + hovered_option, on_selected, text_size, padding, @@ -230,7 +299,7 @@ where let intrinsic = Size::new( 0.0, f32::from(text_size + self.padding * 2) - * self.options.borrow().len() as f32, + * self.options.len() as f32, ); limits.resolve(intrinsic) @@ -245,7 +314,7 @@ where struct Marker; std::any::TypeId::of::().hash(state); - self.options.borrow().len().hash(state); + self.options.len().hash(state); self.text_size.hash(state); self.padding.hash(state); } @@ -265,7 +334,7 @@ where if bounds.contains(cursor_position) { if let Some(index) = *self.hovered_option { - if let Some(option) = self.options.borrow().get(index) { + if let Some(option) = self.options.get(index) { messages.push((self.on_selected)(option.clone())); } } @@ -299,7 +368,7 @@ where renderer, layout.bounds(), cursor_position, - self.options.borrow(), + self.options, *self.hovered_option, self.text_size.unwrap_or(renderer.default_size()), self.padding, @@ -337,8 +406,7 @@ impl<'a, T, Message, Renderer> Into> for List<'a, T, Message, Renderer> where T: ToString + Clone, - [T]: ToOwned, - Message: 'static, + Message: 'a, Renderer: 'a + self::Renderer, { fn into(self) -> Element<'a, Message, Renderer> { diff --git a/native/src/widget/combo_box.rs b/native/src/widget/combo_box.rs index f2dc86e8..f6da076a 100644 --- a/native/src/widget/combo_box.rs +++ b/native/src/widget/combo_box.rs @@ -10,7 +10,8 @@ pub struct ComboBox<'a, T, Message, Renderer: self::Renderer> where [T]: ToOwned>, { - internal: Internal<'a, T, Message>, + menu: &'a mut menu::State, + on_selected: Box Message>, options: Cow<'a, [T]>, selected: Option, width: Length, @@ -24,11 +25,6 @@ pub struct State { menu: menu::State, } -pub struct Internal<'a, T, Message> { - menu: &'a mut menu::State, - on_selected: Box Message>, -} - impl<'a, T: 'a, Message, Renderer: self::Renderer> ComboBox<'a, T, Message, Renderer> where @@ -42,10 +38,8 @@ where on_selected: impl Fn(T) -> Message + 'static, ) -> Self { Self { - internal: Internal { - menu: &mut state.menu, - on_selected: Box::new(on_selected), - }, + menu: &mut state.menu, + on_selected: Box::new(on_selected), options: options.into(), selected, width: Length::Shrink, @@ -183,7 +177,7 @@ where if layout.bounds().contains(cursor_position) { let selected = self.selected.as_ref(); - self.internal.menu.open( + self.menu.open( self.options .iter() .position(|option| Some(option) == selected), @@ -216,22 +210,20 @@ where &mut self, layout: Layout<'_>, ) -> Option> { - if self.internal.menu.is_open() { + if self.menu.is_open() { let bounds = layout.bounds(); - Some(Overlay::new( - layout.position(), - Box::new(Menu::new( - self.internal.menu, - &self.options, - &self.internal.on_selected, - bounds.width.round() as u16, - bounds.height, - self.text_size, - self.padding, - Renderer::menu_style(&self.style), - )), - )) + let mut menu = + Menu::new(&mut self.menu, &self.options, &self.on_selected) + .width(bounds.width.round() as u16) + .padding(self.padding) + .style(Renderer::menu_style(&self.style)); + + if let Some(text_size) = self.text_size { + menu = menu.text_size(text_size); + } + + Some(menu.overlay(layout.position(), bounds.height)) } else { None }