From d99da9380ef9c93959d7799a2bab02477794fcfc Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sun, 28 Jan 2024 17:28:57 +0800 Subject: [PATCH 01/40] temp update --- Cargo.toml | 2 +- src/native/helpers.rs | 16 +-- src/native/menu/flex.rs | 214 +++++++++++++++++++--------------- src/native/menu/menu_bar.rs | 85 ++++++++------ src/native/menu/menu_inner.rs | 102 +++++++++------- src/native/menu/menu_tree.rs | 26 ++--- 6 files changed, 251 insertions(+), 194 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 678e3e4f..3509590a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ members = [ "examples/cupertino/cupertino_switch", "examples/WidgetIDReturn", "examples/segmented_button", - #"examples/menu", + "examples/menu", ] [workspace.dependencies.iced] diff --git a/src/native/helpers.rs b/src/native/helpers.rs index 32c982f7..a4e756cb 100644 --- a/src/native/helpers.rs +++ b/src/native/helpers.rs @@ -97,12 +97,12 @@ macro_rules! menu_bar { /// /// [`MenuBar`]: crate::MenuBar #[must_use] -pub fn menu_bar( - menu_roots: Vec>, -) -> crate::menu::menu_bar::MenuBar +pub fn menu_bar( + menu_roots: Vec>, +) -> crate::menu::menu_bar::MenuBar where Renderer: core::Renderer, - Renderer::Theme: crate::style::menu_bar::StyleSheet, + Theme: crate::style::menu_bar::StyleSheet, { crate::menu::menu_bar::MenuBar::new(menu_roots) } @@ -112,10 +112,10 @@ where /// /// [`MenuTree`]: crate::MenuTree #[must_use] -pub fn menu_tree<'a, Message, Renderer>( - item: impl Into>, - children: Vec>>, -) -> crate::menu::menu_tree::MenuTree<'a, Message, Renderer> +pub fn menu_tree<'a, Message, Theme, Renderer>( + item: impl Into>, + children: Vec>>, +) -> crate::menu::menu_tree::MenuTree<'a, Message, Theme, Renderer> where Renderer: core::Renderer, { diff --git a/src/native/menu/flex.rs b/src/native/menu/flex.rs index bb8070e2..b7baae94 100644 --- a/src/native/menu/flex.rs +++ b/src/native/menu/flex.rs @@ -1,6 +1,23 @@ +//! Distribute elements using a flex-based layout. +// This code is heavily inspired by the [`druid`] codebase. +// +// [`druid`]: https://github.com/xi-editor/druid +// +// Copyright 2018 The xi-editor Authors, Héctor Ramón +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use iced_widget::core::{ - layout::{Limits, Node}, - renderer, Alignment, Element, Padding, Point, Size, + Element, layout::{Limits, Node}, widget, Alignment, Length, Padding, Point, Size }; /// The main axis of a flex layout. @@ -10,32 +27,28 @@ pub enum Axis { Horizontal, /// The vertical axis - #[allow(dead_code)] Vertical, } impl Axis { - /// Gets the main Axis fn main(&self, size: Size) -> f32 { match self { - Self::Horizontal => size.width, - Self::Vertical => size.height, + Axis::Horizontal => size.width, + Axis::Vertical => size.height, } } - /// Gets the cross Axis fn cross(&self, size: Size) -> f32 { match self { - Self::Horizontal => size.height, - Self::Vertical => size.width, + Axis::Horizontal => size.height, + Axis::Vertical => size.width, } } - /// Returns a Packed axis - fn pack(&self, main: f32, cross: f32) -> (f32, f32) { + fn pack(&self, main: T, cross: T) -> (T, T) { match self { - Self::Horizontal => (main, cross), - Self::Vertical => (cross, main), + Axis::Horizontal => (main, cross), + Axis::Vertical => (cross, main), } } } @@ -44,137 +57,140 @@ impl Axis { /// padding and alignment to the items as needed. /// /// It returns a new layout [`Node`]. -pub fn resolve<'a, E, Message, Renderer>( - axis: &Axis, +pub fn resolve<'a, E, Message, Theme, Renderer>( + axis: Axis, renderer: &Renderer, limits: &Limits, + width: Length, + height: Length, padding: Padding, spacing: f32, align_items: Alignment, items: &[E], + trees: &mut [widget::Tree], ) -> Node where - E: std::borrow::Borrow>, - Renderer: renderer::Renderer, + E: std::borrow::Borrow>, + Renderer: iced_widget::core::Renderer, { - let limits = limits.pad(padding); + let limits = limits.width(width).height(height).shrink(padding); let total_spacing = spacing * items.len().saturating_sub(1) as f32; let max_cross = axis.cross(limits.max()); - let mut fill_sum = 0; - let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill())); + let mut fill_main_sum = 0; + let mut cross = match axis { + Axis::Horizontal => match height { + Length::Shrink => 0.0, + _ => max_cross, + }, + Axis::Vertical => match width { + Length::Shrink => 0.0, + _ => max_cross, + }, + }; + let mut available = axis.main(limits.max()) - total_spacing; let mut nodes: Vec = Vec::with_capacity(items.len()); nodes.resize(items.len(), Node::default()); - if align_items == Alignment::Center { - let mut fill_cross = axis.cross(limits.min()); + for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { + let (fill_main_factor, fill_cross_factor) = { + let size = child.borrow().as_widget().size(); - for child in items { - let child = child.borrow(); - let cross_fill_factor = match axis { - Axis::Horizontal => child.as_widget().height(), - Axis::Vertical => child.as_widget().width(), - } - .fill_factor(); + axis.pack(size.width.fill_factor(), size.height.fill_factor()) + }; - if cross_fill_factor == 0 { + if fill_main_factor == 0 { + if fill_cross_factor == 0 { let (max_width, max_height) = axis.pack(available, max_cross); - let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = + Limits::new(Size::ZERO, Size::new(max_width, max_height)); - let layout = child.as_widget().layout(renderer, &child_limits); + let layout = + child.borrow().as_widget().layout(tree, renderer, &child_limits); let size = layout.size(); - fill_cross = fill_cross.max(axis.cross(size)); + available -= axis.main(size); + cross = cross.max(axis.cross(size)); + + nodes[i] = layout; } + } else { + fill_main_sum += fill_main_factor; } - - cross = fill_cross; } - for (i, child) in items.iter().enumerate() { - let child = child.borrow(); - let fill_factor = match axis { - Axis::Horizontal => child.as_widget().width(), - Axis::Vertical => child.as_widget().height(), - } - .fill_factor(); + for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { + let (fill_main_factor, fill_cross_factor) = { + let size = child.borrow().as_widget().size(); - if fill_factor == 0 { - let (min_width, min_height) = if align_items == Alignment::Center { - axis.pack(0.0, cross) - } else { - axis.pack(0.0, 0.0) - }; + axis.pack(size.width.fill_factor(), size.height.fill_factor()) + }; - let (max_width, max_height) = if align_items == Alignment::Center { - axis.pack(available, cross) - } else { - axis.pack(available, max_cross) - }; + if fill_main_factor == 0 && fill_cross_factor != 0 { + let (max_width, max_height) = axis.pack(available, cross); - let child_limits = Limits::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ); + let child_limits = + Limits::new(Size::ZERO, Size::new(max_width, max_height)); - let layout = child.as_widget().layout(renderer, &child_limits); + let layout = + child.borrow().as_widget().layout(tree, renderer, &child_limits); let size = layout.size(); available -= axis.main(size); - - if align_items != Alignment::Center { - cross = cross.max(axis.cross(size)); - } + cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; - } else { - fill_sum += fill_factor; } } - let remaining = available.max(0.0); + let remaining = match axis { + Axis::Horizontal => match width { + Length::Shrink => 0.0, + _ => available.max(0.0), + }, + Axis::Vertical => match height { + Length::Shrink => 0.0, + _ => available.max(0.0), + }, + }; - for (i, child) in items.iter().enumerate() { - let child = child.borrow(); - let fill_factor = match axis { - Axis::Horizontal => child.as_widget().width(), - Axis::Vertical => child.as_widget().height(), - } - .fill_factor(); + for (i, (child, tree)) in items.iter().zip(trees).enumerate() { + let (fill_main_factor, fill_cross_factor) = { + let size = child.borrow().as_widget().size(); + + axis.pack(size.width.fill_factor(), size.height.fill_factor()) + }; + + if fill_main_factor != 0 { + let max_main = + remaining * fill_main_factor as f32 / fill_main_sum as f32; - if fill_factor != 0 { - let max_main = remaining * f32::from(fill_factor) / f32::from(fill_sum); let min_main = if max_main.is_infinite() { 0.0 } else { max_main }; - let (min_width, min_height) = if align_items == Alignment::Center { - axis.pack(min_main, cross) + let max_cross = if fill_cross_factor == 0 { + max_cross } else { - axis.pack(min_main, axis.cross(limits.min())) + cross }; - let (max_width, max_height) = if align_items == Alignment::Center { - axis.pack(max_main, cross) - } else { - axis.pack(max_main, max_cross) - }; + let (min_width, min_height) = axis.pack(min_main, 0.0); + let (max_width, max_height) = axis.pack(max_main, max_cross); let child_limits = Limits::new( Size::new(min_width, min_height), Size::new(max_width, max_height), ); - let layout = child.as_widget().layout(renderer, &child_limits); - - if align_items != Alignment::Center { - cross = cross.max(axis.cross(layout.size())); - } + let layout = + child.borrow().as_widget().layout(tree, renderer, &child_limits); + cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; } @@ -190,14 +206,22 @@ where let (x, y) = axis.pack(main, pad.1); - node.move_to(Point::new(x, y)); + node.move_to_mut(Point::new(x, y)); match axis { Axis::Horizontal => { - node.align(Alignment::Start, align_items, Size::new(0.0, cross)); + node.align_mut( + Alignment::Start, + align_items, + Size::new(0.0, cross), + ); } Axis::Vertical => { - node.align(align_items, Alignment::Start, Size::new(cross, 0.0)); + node.align_mut( + align_items, + Alignment::Start, + Size::new(cross, 0.0), + ); } } @@ -206,8 +230,12 @@ where main += axis.main(size); } - let (width, height) = axis.pack(main - pad.0, cross); - let size = limits.resolve(Size::new(width, height)); + let (intrinsic_width, intrinsic_height) = axis.pack(main - pad.0, cross); + let size = limits.resolve( + width, + height, + Size::new(intrinsic_width, intrinsic_height), + ); - Node::with_children(size.pad(padding), nodes) + Node::with_children(size.expand(padding), nodes) } diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 838d5717..4f2ab0f7 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -9,11 +9,13 @@ use crate::style::menu_bar::StyleSheet; use iced_widget::core::{ event, - layout::{Limits, Node}, + layout::{self, Limits, Node}, mouse::{self, Cursor}, overlay, renderer, touch, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Layout, Length, Padding, Rectangle, Shell, Widget, + Border, Shadow, + Size, }; pub(super) struct MenuBarState { @@ -56,10 +58,10 @@ impl Default for MenuBarState { /// A `MenuBar` collects `MenuTree`s and handles /// all the layout, event processing and drawing #[allow(missing_debug_implementations)] -pub struct MenuBar<'a, Message, Renderer = iced_widget::Renderer> +pub struct MenuBar<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { width: Length, height: Length, @@ -72,18 +74,19 @@ where item_width: ItemWidth, item_height: ItemHeight, path_highlight: Option, - menu_roots: Vec>, - style: ::Style, + menu_roots: Vec>, + // style: ::Style, + style: Theme::Style, } -impl<'a, Message, Renderer> MenuBar<'a, Message, Renderer> +impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { /// Creates a new [`MenuBar`] with the given menu roots #[must_use] - pub fn new(menu_roots: Vec>) -> Self { + pub fn new(menu_roots: Vec>) -> Self { let mut menu_roots = menu_roots; menu_roots.iter_mut().for_each(MenuTree::set_index); @@ -104,7 +107,7 @@ where item_height: ItemHeight::Uniform(30), path_highlight: Some(PathHighlight::MenuActive), menu_roots, - style: ::Style::default(), + style: Theme::Style::default(), } } @@ -184,7 +187,7 @@ where /// Sets the style of the menu bar and its menus #[must_use] - pub fn style(mut self, style: impl Into<::Style>) -> Self { + pub fn style(mut self, style: impl Into) -> Self { self.style = style.into(); self } @@ -196,17 +199,13 @@ where self } } -impl<'a, Message, Renderer> Widget for MenuBar<'a, Message, Renderer> +impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height + fn size(&self) -> Size { + Size::new(self.width, self.height) } fn diff(&self, tree: &mut Tree) { @@ -275,7 +274,8 @@ where .collect() } - fn layout(&self, renderer: &Renderer, limits: &Limits) -> Node { + + fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { use super::flex; let limits = limits.width(self.width).height(self.height); @@ -285,13 +285,16 @@ where .map(|root| &root.item) .collect::>(); flex::resolve( - &flex::Axis::Horizontal, - renderer, - &limits, - self.padding, - self.spacing, - Alignment::Center, - &children, + flex::Axis::Horizontal, + renderer, + &limits, + self.width, + self.height, + self.padding, + self.spacing, + Alignment::Center, + &children, + &mut tree.children ) } @@ -311,7 +314,7 @@ where use touch::Event::{FingerLifted, FingerLost}; let root_status = process_root_events( - &mut self.menu_roots, + self.menu_roots.as_mut_slice(), view_cursor, tree, &event, @@ -340,7 +343,7 @@ where &self, tree: &Tree, renderer: &mut Renderer, - theme: &::Theme, + theme: &Theme, style: &renderer::Style, layout: Layout<'_>, view_cursor: Cursor, @@ -365,9 +368,15 @@ where .bounds(); let path_quad = renderer::Quad { bounds: active_bounds, - border_radius: styling.border_radius.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, + // border_radius: styling.border_radius.into(), + // border_width: 0.0, + // border_color: Color::TRANSPARENT, + border: Border{ + color: Color::TRANSPARENT, + width: 0.0, + radius: styling.border_radius.into() + }, + shadow: Shadow::default(), }; let path_color = styling.path; renderer.fill_quad(path_quad, path_color); @@ -396,7 +405,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, _renderer: &Renderer, - ) -> Option> { + ) -> Option> { let state = tree.state.downcast_ref::(); if !state.open { return None; @@ -420,21 +429,23 @@ where .overlay(), ) } + + } -impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> +impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, Renderer: 'a + renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { - fn from(value: MenuBar<'a, Message, Renderer>) -> Self { + fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { Self::new(value) } } #[allow(unused_results, clippy::too_many_arguments)] -fn process_root_events( - menu_roots: &mut [MenuTree<'_, Message, Renderer>], +fn process_root_events( + menu_roots: &mut [MenuTree<'_, Message, Theme, Renderer>], view_cursor: Cursor, tree: &mut Tree, event: &event::Event, diff --git a/src/native/menu/menu_inner.rs b/src/native/menu/menu_inner.rs index 64f1bd2d..e3aa7caa 100644 --- a/src/native/menu/menu_inner.rs +++ b/src/native/menu/menu_inner.rs @@ -9,6 +9,7 @@ use iced_widget::core::{ overlay, renderer, touch, widget::Tree, Clipboard, Color, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, + Border, Shadow }; /// The condition of when to close a menu @@ -236,8 +237,8 @@ struct MenuBounds { } impl MenuBounds { #[allow(clippy::too_many_arguments)] - fn new( - menu_tree: &MenuTree<'_, Message, Renderer>, + fn new( + menu_tree: &MenuTree<'_, Message, Theme, Renderer>, renderer: &Renderer, item_width: ItemWidth, item_height: ItemHeight, @@ -246,12 +247,13 @@ impl MenuBounds { aod: &Aod, bounds_expand: u16, parent_bounds: Rectangle, + tree: &mut [Tree], ) -> Self where Renderer: renderer::Renderer, { let (children_size, child_positions, child_sizes) = - get_children_layout(menu_tree, renderer, item_width, item_height); + get_children_layout(menu_tree, renderer, item_width, item_height, tree); // viewport space parent bounds let view_parent_bounds = parent_bounds + overlay_offset; @@ -291,12 +293,13 @@ pub(super) struct MenuState { menu_bounds: MenuBounds, } impl MenuState { - fn layout( + fn layout( &self, overlay_offset: Vector, slice: MenuSlice, renderer: &Renderer, - menu_tree: &MenuTree<'_, Message, Renderer>, + menu_tree: &MenuTree<'_, Message, Theme, Renderer>, + tree: &mut [Tree], ) -> Node where Renderer: renderer::Renderer, @@ -334,7 +337,7 @@ impl MenuState { let limits = Limits::new(Size::ZERO, size); - let mut node = mt.item.as_widget().layout(renderer, &limits); + let mut node = mt.item.as_widget().layout(&mut tree[mt.index], renderer, &limits); node.move_to(Point::new(0.0, position + self.scroll_offset)); node }) @@ -345,12 +348,13 @@ impl MenuState { node } - fn layout_single( + fn layout_single( &self, overlay_offset: Vector, index: usize, renderer: &Renderer, - menu_tree: &MenuTree<'_, Message, Renderer>, + menu_tree: &MenuTree<'_, Message, Theme, Renderer>, + tree: &mut Tree, ) -> Node where Renderer: renderer::Renderer, @@ -361,7 +365,7 @@ impl MenuState { let position = self.menu_bounds.child_positions[index]; let limits = Limits::new(Size::ZERO, self.menu_bounds.child_sizes[index]); let parent_offset = children_bounds.position() - Point::ORIGIN; - let mut node = menu_tree.item.as_widget().layout(renderer, &limits); + let mut node = menu_tree.item.as_widget().layout(tree, renderer, &limits); node.move_to(Point::new( parent_offset.x, parent_offset.y + position + self.scroll_offset, @@ -423,13 +427,13 @@ impl MenuState { } } -pub(super) struct Menu<'a, 'b, Message, Renderer> +pub(super) struct Menu<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { pub(super) tree: &'b mut Tree, - pub(super) menu_roots: &'b mut Vec>, + pub(super) menu_roots: &'b mut Vec>, pub(super) bounds_expand: u16, pub(super) close_condition: CloseCondition, pub(super) item_width: ItemWidth, @@ -439,28 +443,34 @@ where pub(super) cross_offset: i32, pub(super) root_bounds_list: Vec, pub(super) path_highlight: Option, - pub(super) style: &'b ::Style, + pub(super) style: &'b Theme::Style, } -impl<'a, 'b, Message, Renderer> Menu<'a, 'b, Message, Renderer> +impl<'a, 'b, Message, Theme, Renderer> Menu<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { - pub(super) fn overlay(self) -> overlay::Element<'b, Message, Renderer> { + pub(super) fn overlay(self) -> overlay::Element<'b, Message, Theme, Renderer> { overlay::Element::new(Point::ORIGIN, Box::new(self)) } } -impl<'a, 'b, Message, Renderer> overlay::Overlay - for Menu<'a, 'b, Message, Renderer> +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay + for Menu<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { - fn layout(&self, _renderer: &Renderer, bounds: Size, position: Point) -> Node { + fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> iced_widget::core::layout::Node { // overlay space viewport rectangle Node::new(bounds).translate(Point::ORIGIN - position) } - + fn on_event( &mut self, event: event::Event, @@ -583,7 +593,7 @@ where fn draw( &self, renderer: &mut Renderer, - theme: &Renderer::Theme, + theme: &Theme, style: &renderer::Style, layout: Layout<'_>, view_cursor: Cursor, @@ -641,9 +651,12 @@ where // println!("color: {:?}\n", styling.background); let menu_quad = renderer::Quad { bounds: pad_rectangle(children_bounds, styling.background_expand.into()), - border_radius: styling.border_radius.into(), - border_width: styling.border_width, - border_color: styling.border_color, + border: Border { + color: styling.border_color, + width: styling.border_width, + radius: styling.border_radius.into() + }, + shadow: Shadow::default(), }; let menu_color = styling.background; r.fill_quad(menu_quad, menu_color); @@ -657,9 +670,12 @@ where .bounds(); let path_quad = renderer::Quad { bounds: active_bounds, - border_radius: styling.border_radius.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, + border: Border{ + color: Color::TRANSPARENT, + width: 0.0, + radius: styling.border_radius.into(), + }, + shadow: Shadow::default(), }; let path_color = styling.path; r.fill_quad(path_quad, path_color); @@ -700,8 +716,8 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { } } -fn init_root_menu( - menu: &mut Menu<'_, '_, Message, Renderer>, +fn init_root_menu( + menu: &mut Menu<'_, '_, Message, Theme, Renderer>, renderer: &Renderer, overlay_cursor: Point, viewport_size: Size, @@ -710,7 +726,7 @@ fn init_root_menu( main_offset: f32, ) where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { let state = menu.tree.state.downcast_mut::(); if !(state.menu_states.is_empty() && bar_bounds.contains(overlay_cursor)) { @@ -773,9 +789,9 @@ fn init_root_menu( } #[allow(clippy::too_many_arguments)] -fn process_menu_events<'b, Message, Renderer>( +fn process_menu_events<'b, Message, Theme, Renderer>( tree: &'b mut Tree, - menu_roots: &'b mut [MenuTree<'_, Message, Renderer>], + menu_roots: &'b mut [MenuTree<'_, Message, Theme, Renderer>], event: event::Event, view_cursor: Cursor, renderer: &Renderer, @@ -831,8 +847,8 @@ where } #[allow(unused_results)] -fn process_overlay_events( - menu: &mut Menu<'_, '_, Message, Renderer>, +fn process_overlay_events( + menu: &mut Menu<'_, '_, Message, Theme, Renderer>, renderer: &Renderer, viewport_size: Size, overlay_offset: Vector, @@ -842,7 +858,7 @@ fn process_overlay_events( ) -> event::Status where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { use event::Status::{Captured, Ignored}; /* @@ -1022,8 +1038,8 @@ where Captured } -fn process_scroll_events( - menu: &mut Menu<'_, '_, Message, Renderer>, +fn process_scroll_events( + menu: &mut Menu<'_, '_, Message, Theme, Renderer>, delta: mouse::ScrollDelta, overlay_cursor: Point, viewport_size: Size, @@ -1031,7 +1047,7 @@ fn process_scroll_events( ) -> event::Status where Renderer: renderer::Renderer, - Renderer::Theme: StyleSheet, + Theme: StyleSheet, { use event::Status::{Captured, Ignored}; use mouse::ScrollDelta; @@ -1101,11 +1117,12 @@ where #[allow(clippy::pedantic)] /// Returns (children_size, child_positions, child_sizes) -fn get_children_layout( - menu_tree: &MenuTree<'_, Message, Renderer>, +fn get_children_layout( + menu_tree: &MenuTree<'_, Message, Theme, Renderer>, renderer: &Renderer, item_width: ItemWidth, item_height: ItemHeight, + tree: &mut [Tree], ) -> (Size, Vec, Vec) where Renderer: renderer::Renderer, @@ -1130,11 +1147,12 @@ where .iter() .map(|mt| { let w = mt.item.as_widget(); - match w.height() { + match w.size().height { Length::Fixed(f) => Size::new(width, f), Length::Shrink => { let l_height = w .layout( + &mut tree[mt.index], renderer, &Limits::new(Size::ZERO, Size::new(width, f32::MAX)), ) diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 49904b02..5ed8d7ad 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -10,26 +10,26 @@ use iced_widget::core::{renderer, Element}; /// but there's no need to explicitly distinguish them here, if a menu tree /// has children, it's a menu, otherwise it's an item #[allow(missing_debug_implementations)] -pub struct MenuTree<'a, Message, Renderer = iced_widget::Renderer> { +pub struct MenuTree<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> { /// The menu tree will be flatten into a vector to build a linear widget tree, /// the `index` field is the index of the item in that vector pub(super) index: usize, /// The item of the menu tree - pub(super) item: Element<'a, Message, Renderer>, + pub(super) item: Element<'a, Message, Theme, Renderer>, /// The children of the menu tree - pub(super) children: Vec>, + pub(super) children: Vec>, /// The width of the menu tree pub(super) width: Option, /// The height of the menu tree pub(super) height: Option, } -impl<'a, Message, Renderer> MenuTree<'a, Message, Renderer> +impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { /// Create a new menu tree from a widget - pub fn new(item: impl Into>) -> Self { + pub fn new(item: impl Into>) -> Self { Self { index: 0, item: item.into(), @@ -41,8 +41,8 @@ where /// Create a menu tree from a widget and a vector of sub trees pub fn with_children( - item: impl Into>, - children: Vec>>, + item: impl Into>, + children: Vec>>, ) -> Self { Self { index: 0, @@ -78,7 +78,7 @@ where /// Set the index of each item pub(super) fn set_index(&mut self) { /// inner counting function. - fn rec(mt: &mut MenuTree<'_, Message, Renderer>, count: &mut usize) { + fn rec(mt: &mut MenuTree<'_, Message, Theme, Renderer>, count: &mut usize) { // keep items under the same menu line up mt.children.iter_mut().for_each(|c| { c.index = *count; @@ -97,9 +97,9 @@ where /// Flatten the menu tree pub(super) fn flattern(&'a self) -> Vec<&Self> { /// Inner flattening function - fn rec<'a, Message, Renderer>( - mt: &'a MenuTree<'a, Message, Renderer>, - flat: &mut Vec<&MenuTree<'a, Message, Renderer>>, + fn rec<'a, Message, Theme, Renderer>( + mt: &'a MenuTree<'a, Message, Theme, Renderer>, + flat: &mut Vec<&MenuTree<'a, Message, Theme, Renderer>>, ) { mt.children.iter().for_each(|c| { flat.push(c); @@ -118,11 +118,11 @@ where } } -impl<'a, Message, Renderer> From> for MenuTree<'a, Message, Renderer> +impl<'a, Message, Theme, Renderer> From> for MenuTree<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - fn from(value: Element<'a, Message, Renderer>) -> Self { + fn from(value: Element<'a, Message, Theme, Renderer>) -> Self { Self::new(value) } } From 638dd673b1d2ae9a61eb082b3ee9a1a19723fa77 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Tue, 30 Jan 2024 10:52:58 +0800 Subject: [PATCH 02/40] nested overlay --- examples/menu/src/main.rs | 176 ++++++++--- src/native/menu.rs | 2 + src/native/menu/flex.rs | 12 +- src/native/menu/menu_bar.rs | 83 ++--- src/native/menu/menu_inner.rs | 78 +++-- src/native/menu/menux.rs | 563 ++++++++++++++++++++++++++++++++++ src/native/quad.rs | 2 +- 7 files changed, 802 insertions(+), 114 deletions(-) create mode 100644 src/native/menu/menux.rs diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 0d07e4f2..f78f1a9c 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -3,20 +3,36 @@ use iced::widget::{ button, checkbox, container, horizontal_space, pick_list, row, slider, svg, text, text_input, toggler, vertical_slider, }; -use iced::{alignment, theme, Application, Color, Element, Length}; +use iced::{alignment, theme, Application, Border, Color, Element, Length, Pixels, Size}; -use iced_aw::menu::{menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight}; +use iced_aw::menu::menu_bar::MenuBar; +use iced_aw::menu::{ + menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight, + Menux, +}; use iced_aw::quad; use iced_aw::{helpers::menu_tree, menu_bar, menu_tree}; pub fn main() -> iced::Result { + // std::env::set_var("RUST_BACKTRACE", "full"); App::run(iced::Settings { - default_text_size: 15.0, - window: iced::window::Settings { - size: (1000, 500), - // position: iced::window::Position::Default, + default_text_size: Pixels(15.0), + window: iced::window::Settings{ + size: Size::new(1000.0, 500.0), ..Default::default() }, + // id: todo!(), + // flags: todo!(), + // fonts: todo!(), + // default_font: todo!(), + // antialiasing: todo!(), + + // default_text_size: 15.0, + // window: iced::window::Settings { + // size: (1000, 500), + // // position: iced::window::Position::Default, + // ..Default::default() + // }, ..Default::default() }) } @@ -81,10 +97,13 @@ impl Application for App { type Flags = (); fn new(_flags: Self::Flags) -> (Self, iced::Command) { - let theme = iced::Theme::custom(theme::Palette { - primary: Color::from([0.45, 0.25, 0.57]), - ..iced::Theme::Light.palette() - }); + let theme = iced::Theme::custom( + "Custom Theme".into(), + theme::Palette { + primary: Color::from([0.45, 0.25, 0.57]), + ..iced::Theme::Light.palette() + } + ); ( Self { @@ -112,6 +131,7 @@ impl Application for App { } fn update(&mut self, message: Self::Message) -> iced::Command { + println!("app update"); match message { Message::Debug(s) => { self.title = s; @@ -129,10 +149,13 @@ impl Application for App { self.title = t.to_string(); } Message::ColorChange(c) => { - self.theme = iced::Theme::custom(theme::Palette { - primary: c, - ..self.theme.palette() - }); + self.theme = iced::Theme::custom( + "Color Change".into(), + theme::Palette { + primary: c, + ..self.theme.palette() + } + ); self.title = format!("[{:.2}, {:.2}, {:.2}]", c.r, c.g, c.b); } Message::FlipHorizontal => self.flip_h = !self.flip_h, @@ -141,15 +164,21 @@ impl Application for App { self.dark_mode = b; let primary = self.theme.palette().primary; if b { - self.theme = iced::Theme::custom(theme::Palette { - primary, - ..iced::Theme::Dark.palette() - }) + self.theme = iced::Theme::custom( + "Dark".into(), + theme::Palette { + primary, + ..iced::Theme::Dark.palette() + } + ) } else { - self.theme = iced::Theme::custom(theme::Palette { - primary, - ..iced::Theme::Light.palette() - }) + self.theme = iced::Theme::custom( + "Light".into(), + theme::Palette { + primary, + ..iced::Theme::Light.palette() + } + ) } } Message::TextChange(s) => { @@ -164,14 +193,51 @@ impl Application for App { iced::Command::none() } - fn view(&self) -> iced::Element<'_, Self::Message, iced::Renderer> { + fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { + println!("app view"); let pick_size_option = pick_list( &SizeOption::ALL[..], Some(self.size_option), Message::SizeOption, ); - let mb = match self.size_option { + let mb = row![ + Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ + button("abc").on_press(Message::Debug("abc".into())).into(), + button("def").on_press(Message::Debug("def".into())).into(), + button("xxx").on_press(Message::Debug("xxx".into())).into(), + ]), + Menux::new(button("aaa").on_press(Message::Debug("aaa".into())).into(), vec![ + button("abc").on_press(Message::Debug("abc".into())).into(), + button("def").on_press(Message::Debug("def".into())).into(), + button("xxx").on_press(Message::Debug("xxx".into())).into(), + ]), + ]; + + // let mb = MenuBar::new( + // [ + // MenuTree::with_children( + // button("content").on_press(Message::Debug("content".into())), + // [ + // MenuTree::new(button("abc")), + // MenuTree::new(button("def")), + // MenuTree::new(button("wagrarga")), + // MenuTree::new(button("jfuykyfuk")), + // ].into() + // ), + // MenuTree::with_children( + // button("xxx").on_press(Message::Debug("xxx".into())), + // [ + // MenuTree::new(button("abc")), + // MenuTree::new(button("def")), + // MenuTree::new(button("wagrarga")), + // MenuTree::new(button("jfuykyfuk")), + // ].into() + // ) + // ].into() + // ); + + /* let mb = match self.size_option { SizeOption::Uniform => { menu_bar!(menu_1(self), menu_2(self), menu_3(self), menu_4(self)) .item_width(ItemWidth::Uniform(180)) @@ -205,12 +271,17 @@ impl Application for App { leave: true, click_outside: false, click_inside: false, - }); + }); */ let r = if self.flip_h { - row!(pick_size_option, horizontal_space(Length::Fill), mb,) + row!(pick_size_option, horizontal_space(Length::Fill), + mb, + ) } else { - row!(mb, horizontal_space(Length::Fill), pick_size_option) + row!( + mb, + horizontal_space(Length::Fill), pick_size_option + ) } .padding([2, 8]) .align_items(alignment::Alignment::Center); @@ -248,9 +319,13 @@ impl button::StyleSheet for ButtonStyle { fn active(&self, style: &Self::Style) -> button::Appearance { button::Appearance { text_color: style.extended_palette().background.base.text, - border_radius: [4.0; 4].into(), background: Some(Color::TRANSPARENT.into()), + border: Border{ + radius: [4.0;4].into(), + ..Default::default() + }, ..Default::default() + } } @@ -265,17 +340,17 @@ impl button::StyleSheet for ButtonStyle { } } -fn base_button<'a>( - content: impl Into>, +/* fn base_button<'a>( + content: impl Into>, msg: Message, -) -> button::Button<'a, Message, iced::Renderer> { +) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { button(content) .padding([4, 8]) .style(iced::theme::Button::Custom(Box::new(ButtonStyle {}))) .on_press(msg) } -fn labeled_button<'a>(label: &str, msg: Message) -> button::Button<'a, Message, iced::Renderer> { +fn labeled_button<'a>(label: &str, msg: Message) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { base_button( text(label) .width(Length::Fill) @@ -285,27 +360,27 @@ fn labeled_button<'a>(label: &str, msg: Message) -> button::Button<'a, Message, ) } -fn debug_button<'a>(label: &str) -> button::Button<'a, Message, iced::Renderer> { +fn debug_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { labeled_button(label, Message::Debug(label.into())) } -fn debug_item<'a>(label: &str) -> MenuTree<'a, Message, iced::Renderer> { +fn debug_item<'a>(label: &str) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { menu_tree!(debug_button(label).width(Length::Fill).height(Length::Fill)) } -fn debug_item2<'a>(label: &str) -> MenuTree<'a, Message, iced::Renderer> { +fn debug_item2<'a>(label: &str) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { menu_tree!(debug_button(label) .width(Length::Fill) .height(Length::Shrink)) } -fn debug_item3<'a>(label: &str, h: f32) -> MenuTree<'a, Message, iced::Renderer> { +fn debug_item3<'a>(label: &str, h: f32) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { menu_tree!(debug_button(label) .width(Length::Fill) .height(Length::Fixed(h))) } -fn color_item<'a>(color: impl Into) -> MenuTree<'a, Message, iced::Renderer> { +fn color_item<'a>(color: impl Into) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let color = color.into(); menu_tree!(base_button(circle(color), Message::ColorChange(color))) } @@ -313,8 +388,8 @@ fn color_item<'a>(color: impl Into) -> MenuTree<'a, Message, iced::Render fn sub_menu<'a>( label: &str, msg: Message, - children: Vec>, -) -> MenuTree<'a, Message, iced::Renderer> { + children: Vec>, +) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let handle = svg::Handle::from_path(format!( "{}/caret-right-fill.svg", env!("CARGO_MANIFEST_DIR") @@ -345,12 +420,12 @@ fn sub_menu<'a>( fn debug_sub_menu<'a>( label: &str, - children: Vec>, -) -> MenuTree<'a, Message, iced::Renderer> { + children: Vec>, +) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { sub_menu(label, Message::Debug(label.into()), children) } -fn separator<'a>() -> MenuTree<'a, Message, iced::Renderer> { +fn separator<'a>() -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { menu_tree!(quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], @@ -359,7 +434,7 @@ fn separator<'a>() -> MenuTree<'a, Message, iced::Renderer> { }) } -fn dot_separator<'a>() -> MenuTree<'a, Message, iced::Renderer> { +fn dot_separator<'a>() -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { menu_tree!(text("·························") .size(30) .width(Length::Fill) @@ -368,7 +443,7 @@ fn dot_separator<'a>() -> MenuTree<'a, Message, iced::Renderer> { .vertical_alignment(alignment::Vertical::Center)) } -fn labeled_separator(label: &'_ str) -> MenuTree<'_, Message, iced::Renderer> { +fn labeled_separator(label: &'_ str) -> MenuTree<'_, Message, iced::Theme, iced::Renderer> { let q_1 = quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], @@ -402,7 +477,7 @@ fn circle(color: Color) -> quad::Quad { } } -fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Renderer> { +fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let sub_5 = debug_sub_menu( "SUB", vec![ @@ -476,7 +551,7 @@ fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Renderer> { root } -fn menu_2<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { +fn menu_2<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let sub_1 = menu_tree( container(toggler( Some("Or as a sub menu item".to_string()), @@ -538,7 +613,7 @@ fn menu_2<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { root } -fn menu_3<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { +fn menu_3<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); let primary = debug_sub_menu( @@ -583,7 +658,7 @@ fn menu_3<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { root } -fn menu_4<'a>(_app: &App) -> MenuTree<'a, Message, iced::Renderer> { +fn menu_4<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let dekjdaud = debug_sub_menu( "dekjdaud", vec![ @@ -723,7 +798,7 @@ fn menu_4<'a>(_app: &App) -> MenuTree<'a, Message, iced::Renderer> { root } -fn menu_5<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { +fn menu_5<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let slider_count = 3; let slider_width = 30; let spacing = 4; @@ -756,7 +831,7 @@ fn menu_5<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { root } -fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { +fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { let slider_count = 3; let slider_width = 30; let spacing = 4; @@ -796,3 +871,4 @@ fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Renderer> { root } + */ \ No newline at end of file diff --git a/src/native/menu.rs b/src/native/menu.rs index cf87e219..63150f2d 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -56,6 +56,7 @@ mod flex; pub mod menu_bar; mod menu_inner; pub mod menu_tree; +pub mod menux; pub use crate::style::menu_bar::{Appearance, StyleSheet}; /// A `MenuBar` collects `MenuTree`s and handles @@ -63,3 +64,4 @@ pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Rendere pub use menu_inner::{CloseCondition, ItemHeight, ItemWidth, PathHighlight}; /// Nested menu is essentially a tree of items, a menu is a collection of items pub type MenuTree<'a, Message, Renderer> = menu_tree::MenuTree<'a, Message, Renderer>; +pub use menux::Menux; \ No newline at end of file diff --git a/src/native/menu/flex.rs b/src/native/menu/flex.rs index b7baae94..20b9dc17 100644 --- a/src/native/menu/flex.rs +++ b/src/native/menu/flex.rs @@ -57,7 +57,7 @@ impl Axis { /// padding and alignment to the items as needed. /// /// It returns a new layout [`Node`]. -pub fn resolve<'a, E, Message, Theme, Renderer>( +pub fn resolve<'a, E, T, Message, Theme, Renderer>( axis: Axis, renderer: &Renderer, limits: &Limits, @@ -67,10 +67,12 @@ pub fn resolve<'a, E, Message, Theme, Renderer>( spacing: f32, align_items: Alignment, items: &[E], - trees: &mut [widget::Tree], + trees: &mut [T], ) -> Node where E: std::borrow::Borrow>, + T: std::borrow::BorrowMut, + Renderer: iced_widget::core::Renderer, { let limits = limits.width(width).height(height).shrink(padding); @@ -109,7 +111,7 @@ where Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.borrow().as_widget().layout(tree, renderer, &child_limits); + child.borrow().as_widget().layout(tree.borrow_mut(), renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -136,7 +138,7 @@ where Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.borrow().as_widget().layout(tree, renderer, &child_limits); + child.borrow().as_widget().layout(tree.borrow_mut(), renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -189,7 +191,7 @@ where ); let layout = - child.borrow().as_widget().layout(tree, renderer, &child_limits); + child.borrow().as_widget().layout(tree.borrow_mut(), renderer, &child_limits); cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 4f2ab0f7..4f878c63 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,7 +1,11 @@ //! A widget that handles menu trees use super::{ menu_inner::{ - CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight, + // Menu, + // Direction, + CloseCondition, + ItemHeight, ItemWidth, + MenuState, PathHighlight, }, menu_tree::MenuTree, }; @@ -23,23 +27,23 @@ pub(super) struct MenuBarState { pub(super) view_cursor: Cursor, pub(super) open: bool, pub(super) active_root: Option, - pub(super) horizontal_direction: Direction, - pub(super) vertical_direction: Direction, + // pub(super) horizontal_direction: Direction, + // pub(super) vertical_direction: Direction, pub(super) menu_states: Vec, } impl MenuBarState { - pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { + /* pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { self.menu_states .iter() .take_while(|ms| ms.index.is_some()) .map(|ms| ms.index.expect("No indices were found in the menu state.")) - } + } */ - pub(super) fn reset(&mut self) { + /* pub(super) fn reset(&mut self) { self.open = false; self.active_root = None; self.menu_states.clear(); - } + } */ } impl Default for MenuBarState { fn default() -> Self { @@ -48,8 +52,8 @@ impl Default for MenuBarState { view_cursor: Cursor::Available([-0.5, -0.5].into()), open: false, active_root: None, - horizontal_direction: Direction::Positive, - vertical_direction: Direction::Positive, + // horizontal_direction: Direction::Positive, + // vertical_direction: Direction::Positive, menu_states: Vec::new(), } } @@ -75,7 +79,6 @@ where item_height: ItemHeight, path_highlight: Option, menu_roots: Vec>, - // style: ::Style, style: Theme::Style, } @@ -90,7 +93,7 @@ where let mut menu_roots = menu_roots; menu_roots.iter_mut().for_each(MenuTree::set_index); - Self { + let mb = Self { width: Length::Shrink, height: Length::Shrink, spacing: 0.0, @@ -108,7 +111,9 @@ where path_highlight: Some(PathHighlight::MenuActive), menu_roots, style: Theme::Style::default(), - } + }; + println!("new mb"); + mb } /// Sets the expand value for each menu's check bounds @@ -284,6 +289,11 @@ where .iter() .map(|root| &root.item) .collect::>(); + let mut tree_children = tree + .children + .iter_mut() + .map(|t| &mut t.children[0]) + .collect::>(); flex::resolve( flex::Axis::Horizontal, renderer, @@ -294,7 +304,7 @@ where self.spacing, Alignment::Center, &children, - &mut tree.children + tree_children.as_mut_slice() ) } @@ -309,6 +319,7 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { + println!("mb on_event"); use event::Event::{Mouse, Touch}; use mouse::{Button::Left, Event::ButtonReleased}; use touch::Event::{FingerLifted, FingerLost}; @@ -349,7 +360,10 @@ where view_cursor: Cursor, viewport: &Rectangle, ) { + println!("mb draw"); let state = tree.state.downcast_ref::(); + println!("mb draw downcast"); + let cursor_pos = view_cursor.position().unwrap_or_default(); let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) { state.view_cursor @@ -368,9 +382,6 @@ where .bounds(); let path_quad = renderer::Quad { bounds: active_bounds, - // border_radius: styling.border_radius.into(), - // border_width: 0.0, - // border_color: Color::TRANSPARENT, border: Border{ color: Color::TRANSPARENT, width: 0.0, @@ -410,33 +421,33 @@ where if !state.open { return None; } - - Some( - Menu { - tree, - menu_roots: &mut self.menu_roots, - bounds_expand: self.bounds_expand, - close_condition: self.close_condition, - item_width: self.item_width, - item_height: self.item_height, - bar_bounds: layout.bounds(), - main_offset: self.main_offset, - cross_offset: self.cross_offset, - root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), - path_highlight: self.path_highlight, - style: &self.style, - } - .overlay(), - ) + None + // Some( + // Menu { + // tree, + // menu_roots: &mut self.menu_roots, + // bounds_expand: self.bounds_expand, + // close_condition: self.close_condition, + // item_width: self.item_width, + // item_height: self.item_height, + // bar_bounds: layout.bounds(), + // main_offset: self.main_offset, + // cross_offset: self.cross_offset, + // root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), + // path_highlight: self.path_highlight, + // style: &self.style, + // } + // .overlay(), + // ) } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where - Message: 'a, + Message: 'a + Clone, Renderer: 'a + renderer::Renderer, - Theme: StyleSheet, + Theme: 'a + StyleSheet, { fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { Self::new(value) diff --git a/src/native/menu/menu_inner.rs b/src/native/menu/menu_inner.rs index e3aa7caa..8c33b26d 100644 --- a/src/native/menu/menu_inner.rs +++ b/src/native/menu/menu_inner.rs @@ -236,7 +236,7 @@ struct MenuBounds { offset_bounds: Rectangle, } impl MenuBounds { - #[allow(clippy::too_many_arguments)] + /* #[allow(clippy::too_many_arguments)] fn new( menu_tree: &MenuTree<'_, Message, Theme, Renderer>, renderer: &Renderer, @@ -284,7 +284,7 @@ impl MenuBounds { check_bounds, offset_bounds, } - } + } */ } pub(super) struct MenuState { @@ -293,7 +293,7 @@ pub(super) struct MenuState { menu_bounds: MenuBounds, } impl MenuState { - fn layout( + /* fn layout( &self, overlay_offset: Vector, slice: MenuSlice, @@ -347,8 +347,8 @@ impl MenuState { node.move_to(children_bounds.position()); node } - - fn layout_single( + */ + /* fn layout_single( &self, overlay_offset: Vector, index: usize, @@ -372,8 +372,8 @@ impl MenuState { )); node } - - fn slice( + */ + /* fn slice( &self, viewport_size: Size, overlay_offset: Vector, @@ -425,6 +425,7 @@ impl MenuState { upper_bound_rel, } } + */ } pub(super) struct Menu<'a, 'b, Message, Theme, Renderer> @@ -472,6 +473,37 @@ where } fn on_event( + &mut self, + _event: event::Event, + _layout: Layout<'_>, + _cursor: mouse::Cursor, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + _shell: &mut Shell<'_, Message>, + ) -> event::Status { + event::Status::Ignored + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + + } + + fn overlay<'c>( + &'c mut self, + _layout: Layout<'_>, + _renderer: &Renderer, + ) -> Option> { + None + } + + /* fn on_event( &mut self, event: event::Event, layout: Layout<'_>, @@ -588,8 +620,8 @@ where _ => menu_status, } } - - #[allow(unused_results)] + */ + /* #[allow(unused_results)] fn draw( &self, renderer: &mut Renderer, @@ -704,10 +736,10 @@ where ms.index .map_or(menu_root, |active| &menu_root.children[active]) }); - } + } */ } -fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { +/* fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { Rectangle { x: rect.x - padding.left, y: rect.y - padding.top, @@ -715,8 +747,8 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { height: rect.height + padding.vertical(), } } - -fn init_root_menu( + */ +/* fn init_root_menu( menu: &mut Menu<'_, '_, Message, Theme, Renderer>, renderer: &Renderer, overlay_cursor: Point, @@ -787,8 +819,8 @@ fn init_root_menu( } } } - -#[allow(clippy::too_many_arguments)] + */ +/* #[allow(clippy::too_many_arguments)] fn process_menu_events<'b, Message, Theme, Renderer>( tree: &'b mut Tree, menu_roots: &'b mut [MenuTree<'_, Message, Theme, Renderer>], @@ -845,8 +877,8 @@ where &Rectangle::default(), ) } - -#[allow(unused_results)] + */ +/* #[allow(unused_results)] fn process_overlay_events( menu: &mut Menu<'_, '_, Message, Theme, Renderer>, renderer: &Renderer, @@ -1037,8 +1069,8 @@ where Captured } - -fn process_scroll_events( + */ +/* fn process_scroll_events( menu: &mut Menu<'_, '_, Message, Theme, Renderer>, delta: mouse::ScrollDelta, overlay_cursor: Point, @@ -1114,8 +1146,8 @@ where } Captured } - -#[allow(clippy::pedantic)] + */ +/* #[allow(clippy::pedantic)] /// Returns (children_size, child_positions, child_sizes) fn get_children_layout( menu_tree: &MenuTree<'_, Message, Theme, Renderer>, @@ -1187,8 +1219,9 @@ where let height = child_sizes.iter().fold(0.0, |acc, x| acc + x.height); (Size::new(width, height), child_positions, child_sizes) -} +} */ +/* fn search_bound( default: usize, default_left: usize, @@ -1217,3 +1250,4 @@ fn search_bound( } index } + */ \ No newline at end of file diff --git a/src/native/menu/menux.rs b/src/native/menu/menux.rs new file mode 100644 index 00000000..0c82ae36 --- /dev/null +++ b/src/native/menu/menux.rs @@ -0,0 +1,563 @@ +//! doc +//! +use iced_widget::core::{ + widget::{self, tree::{self, Tree}, }, + Element, Widget, + renderer, overlay, event, layout, mouse, touch, + Event, Rectangle, Clipboard, Shell, Size, Length, Alignment, Padding, Point +}; +use iced_widget::Column; +use super::menu_inner::Direction; + +struct MenuxState{ + open: bool, +} + +/// doc +#[allow(clippy::missing_docs_in_private_parents)] +pub struct Menux<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + parent: Element<'a, Message, Theme, Renderer>, + children: Vec>, + spacing: f32, + padding: Padding, + width: Length, + height: Length, + direction: Direction, +} +impl<'a, Message, Theme, Renderer> Menux<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + #[allow(missing_docs)] + pub fn new(parent: Element<'a, Message, Theme, Renderer>, children: Vec>) -> Self{ + Self{ + parent, + children, + spacing: 4.0, + padding: [0.0;4].into(), + width: Length::Shrink, + height: Length::Shrink, + direction: Direction::Positive, + } + } + + /// Sets the vertical spacing _between_ elements. + /// + /// Custom margins per element do not exist in iced. You should use this + /// method instead! While less flexible, it helps you keep spacing between + /// elements consistent. + pub fn spacing(mut self, spacing: f32) -> Self { + self.spacing = spacing; + self + } + + /// Sets the [`Padding`] of the [`Menux`]. + pub fn padding>(mut self, padding: P) -> Self { + self.padding = padding.into(); + self + } + + /// Sets the width of the [`Menux`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`Menux`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } +} +impl <'a, Message, Theme, Renderer> Widget for Menux<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + fn state(&self) -> tree::State { + tree::State::new(MenuxState{ open: false}) + } + + fn children(&self) -> Vec { + println!("mx children"); + [ + Tree::new(&self.parent), + Tree{ + children: self.children.iter().map(Tree::new).collect::>(), + ..Tree::empty() + } + ].into() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + println!("mx layout"); + self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: event::Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + println!("mx event"); + let status = self.parent.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); + + let state = tree.state.downcast_mut::(); + let bounds = layout.bounds(); + + use event::Status::*; + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if cursor.is_over(bounds) { + state.open = true; + + Captured + }else{ + Ignored + } + } + // Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + // | Event::Touch(touch::Event::FingerLifted { .. }) => { + // state.open = false; + // Captured + // } + // Event::Touch(touch::Event::FingerLost { .. }) => { + // state.open = false; + // Captured + // } + _ => Ignored + }.merge(status) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + println!("mx draw"); + self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: layout::Layout<'_>, + renderer: &Renderer, + ) -> Option> { + println!("mx overlay"); + let mut og = overlay::Group::new(); + let Tree { tag, state, children } = tree; + + let [parent_tree, children_tree] = children.as_mut_slice() else { panic!("Tree Error") }; + + if let Some(c) = self.parent + .as_widget_mut() + .overlay(parent_tree, layout, renderer) + { + og = og.push(c); + } + + println!("mx overlay downcast_ref"); + let ms = state.downcast_mut::(); + println!("mx overlay downcast_ref done"); + + if !ms.open { + Some(og.overlay()) + }else{ + Some(og.push( + MenuxOverlay{ + state: ms, + tree: children_tree, + children: &mut self.children, + parent_bounds: layout.bounds(), + max_width: 1000.0, + spacing: self.spacing, + padding: self.padding, + width: self.width, + height: self.height, + direction: self.direction, + }.overlay() + ).overlay()) + } + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: renderer::Renderer + 'a, +{ + fn from(m: Menux<'a, Message, Theme, Renderer>) -> Self { + Self::new(m) + } +} + + +struct MenuxOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer +{ + state: &'b mut MenuxState, + tree: &'b mut Tree, + children: &'b mut [Element<'a, Message, Theme, Renderer>], + parent_bounds: Rectangle, + max_width: f32, + spacing: f32, + padding: Padding, + width: Length, + height: Length, + direction: Direction, +} +impl<'a, 'b, Message, Theme, Renderer> MenuxOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer +{ + fn overlay(self) -> overlay::Element<'b, Message, Theme, Renderer>{ + overlay::Element::new(Point::ORIGIN, Box::new(self)) + } +} +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuxOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: iced_widget::core::Point, + translation: iced_widget::core::Vector, + ) -> layout::Node { + println!("mxo layout"); + let limits = layout::Limits::NONE.max_width(self.max_width); + let layout = layout::flex::resolve( + layout::flex::Axis::Vertical, + renderer, + &limits, + self.width, + self.height, + self.padding, + self.spacing, + Alignment::Center, + &self.children, + &mut self.tree.children + ); + + let aod = Aod{ + horizontal: true, + vertical: false, + horizontal_overlap: true, + vertical_overlap: false, + horizontal_direction: Direction::Positive, + vertical_direction: Direction::Positive, + horizontal_offset: 0.0, + vertical_offset: 0.0, + }; + + let children_size = layout.bounds().size(); + let (children_position, offset_position) = aod.resolve( + self.parent_bounds + translation, + children_size, + bounds + ); + + // calc offset bounds + let delta = children_position - offset_position; + let offset_size = if delta.x.abs() > delta.y.abs() { + Size::new(delta.x, children_size.height) + } else { + Size::new(children_size.width, delta.y) + }; + let offset_bounds = Rectangle::new(offset_position, offset_size); + let children_bounds = Rectangle::new(children_position, children_size); + let bounds_expand = 30.0; + let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); + + layout::Node::with_children(Size::INFINITY, [ + layout.move_to(children_position), + layout::Node::new(bounds), + layout::Node::new(offset_bounds.size()).move_to(offset_bounds.position()), + layout::Node::new(check_bounds.size()).move_to(check_bounds.position()), + ].into()) + } + + fn on_event( + &mut self, + event: event::Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + println!("mxo event"); + use event::Status::*; + + let mut lc = layout.children(); + let children_layout = lc.next().unwrap(); + let viewport = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + let status = self.children + .iter_mut() + .zip(&mut self.tree.children) + .zip(children_layout.children()) + .map(|((child, tree), layout)| + child.as_widget_mut().on_event( + tree, + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + &viewport, + ) + ) + .fold(event::Status::Ignored, event::Status::merge); + + match event { + Event::Mouse(mouse::Event::CursorMoved { position }) => { + self.state.open = + self.parent_bounds.contains(position) + || offset_bounds.contains(position) + || check_bounds.contains(position); + Captured + } + _ => Ignored + }.merge(status) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + ) { + println!("mxo draw"); + let mut lc = layout.children(); + let children_layout = lc.next().unwrap(); + let viewport = lc.next().unwrap().bounds(); + + /* renderer.fill_quad( + renderer::Quad{ + bounds: todo!(), + border: todo!(), + shadow: todo!(), + }, + background + ); */ + + if let Some(viewport) = children_layout.bounds().intersection(&viewport) { + for ((child, tree), layout) in self + .children + .iter() + .zip(&self.tree.children) + .zip(children_layout.children()) + { + child.as_widget().draw( + tree, renderer, theme, style, layout, cursor, &viewport, + ); + } + } + } + + fn overlay<'c>( + &'c mut self, + layout: layout::Layout<'_>, + renderer: &Renderer, + ) -> Option> { + println!("mxo overlay"); + overlay::from_children( + self.children, + self.tree, + layout.children().next().unwrap(), + renderer + ) + } +} + + +/// Adaptive open direction +#[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] +struct Aod { + // whether or not to use aod + horizontal: bool, + vertical: bool, + + // whether or not to use overlap + horizontal_overlap: bool, + vertical_overlap: bool, + + // default direction + horizontal_direction: Direction, + vertical_direction: Direction, + + // Offset of the child in the default direction + horizontal_offset: f32, + vertical_offset: f32, +} +impl Aod { + /// Returns child position and offset position + #[allow(clippy::too_many_arguments)] + fn adaptive( + parent_pos: f32, + parent_size: f32, + child_size: f32, + max_size: f32, + offset: f32, + on: bool, + overlap: bool, + direction: Direction, + ) -> (f32, f32) { + /* + Imagine there're two sticks, parent and child + parent: o-----o + child: o----------o + + Now we align the child to the parent in one dimension + There are 4 possibilities: + + 1. to the right + o-----oo----------o + + 2. to the right with overlaping + o-----o + o----------o + + 3. to the left + o----------oo-----o + + 4. to the left with overlaping + o-----o + o----------o + + The child goes to the default direction by default, + if the space on the default direction runs out it goes to the the other, + whether to use overlap is the caller's decision + + This can be applied to any direction + */ + + match direction { + Direction::Positive => { + let space_negative = parent_pos; + let space_positive = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - overshoot) + } else { + (parent_pos, parent_pos) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - offset) + } else { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } + } + } + Direction::Negative => { + let space_positive = parent_pos; + let space_negative = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos, parent_pos) + } else { + (parent_pos - overshoot, parent_pos - overshoot) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } else { + (parent_pos - overshoot, parent_pos - offset) + } + } + } + } + } + + /// Returns child position and offset position + fn resolve( + &self, + parent_bounds: Rectangle, + children_size: Size, + viewport_size: Size, + ) -> (Point, Point) { + let (x, ox) = Self::adaptive( + parent_bounds.x, + parent_bounds.width, + children_size.width, + viewport_size.width, + self.horizontal_offset, + self.horizontal, + self.horizontal_overlap, + self.horizontal_direction, + ); + let (y, oy) = Self::adaptive( + parent_bounds.y, + parent_bounds.height, + children_size.height, + viewport_size.height, + self.vertical_offset, + self.vertical, + self.vertical_overlap, + self.vertical_direction, + ); + + ([x, y].into(), [ox, oy].into()) + } +} + +fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { + Rectangle { + x: rect.x - padding.left, + y: rect.y - padding.top, + width: rect.width + padding.horizontal(), + height: rect.height + padding.vertical(), + } +} + diff --git a/src/native/quad.rs b/src/native/quad.rs index 9ad06794..8fd75cdf 100644 --- a/src/native/quad.rs +++ b/src/native/quad.rs @@ -10,7 +10,7 @@ use iced_widget::core::{ Border, Color, Element, Layout, Length, Rectangle, Shadow, Size, Widget, }; -use crate::native::InnerBounds; +pub use crate::native::InnerBounds; /// A dummy widget that draws a quad #[allow(missing_debug_implementations)] From e8e4920d4fc71d37f97ca8db98b9101f996ef53f Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Tue, 30 Jan 2024 22:57:45 +0800 Subject: [PATCH 03/40] update --- examples/menu/src/main.rs | 118 +++++++++-- src/native/menu.rs | 3 +- src/native/menu/menu_bar.rs | 5 +- src/native/menu/menu_inner.rs | 63 +----- src/native/menu/menux.rs | 388 +++++++++++++++++++++++++++++----- src/native/menu/types.rs | 78 +++++++ 6 files changed, 525 insertions(+), 130 deletions(-) create mode 100644 src/native/menu/types.rs diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index f78f1a9c..37d69f1e 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -3,12 +3,12 @@ use iced::widget::{ button, checkbox, container, horizontal_space, pick_list, row, slider, svg, text, text_input, toggler, vertical_slider, }; -use iced::{alignment, theme, Application, Border, Color, Element, Length, Pixels, Size}; +use iced::{alignment, theme, Application, Border, Color, Element, Event, Length, Pixels, Size}; use iced_aw::menu::menu_bar::MenuBar; use iced_aw::menu::{ menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight, - Menux, + Menux, OpenCondition, Axis }; use iced_aw::quad; use iced_aw::{helpers::menu_tree, menu_bar, menu_tree}; @@ -76,6 +76,7 @@ enum Message { ThemeChange(bool), TextChange(String), SizeOption(SizeOption), + None, } struct App { @@ -130,6 +131,24 @@ impl Application for App { self.title.clone() } + fn subscription(&self) -> iced::Subscription { + use iced::keyboard; + use keyboard::key::Named; + + iced::event::listen().map(|event|{ + match event{ + Event::Keyboard(keyboard::Event::KeyPressed { key, ..}) => { + match key { + keyboard::Key::Named(Named::F1) => Message::FlipHorizontal, + keyboard::Key::Named(Named::F2) => Message::FlipVertical, + _ => Message::None + } + }, + _ => Message::None + } + }) + } + fn update(&mut self, message: Self::Message) -> iced::Command { println!("app update"); match message { @@ -189,6 +208,7 @@ impl Application for App { self.size_option = so; self.title = self.size_option.to_string(); } + Message::None => {} } iced::Command::none() } @@ -203,15 +223,84 @@ impl Application for App { let mb = row![ Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ - button("abc").on_press(Message::Debug("abc".into())).into(), - button("def").on_press(Message::Debug("def".into())).into(), - button("xxx").on_press(Message::Debug("xxx".into())).into(), - ]), - Menux::new(button("aaa").on_press(Message::Debug("aaa".into())).into(), vec![ - button("abc").on_press(Message::Debug("abc".into())).into(), - button("def").on_press(Message::Debug("def".into())).into(), - button("xxx").on_press(Message::Debug("xxx".into())).into(), - ]), + debug_button("abc").width(180.0).into(), + debug_button("def").width(180.0).into(), + debug_button("xxx").width(180.0).into(), + Menux::new(debug_button("htrsth").width(180.0).into(), vec![ + debug_button("ccgh").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("vcsa").width(180.0).into(), + debug_button("kiug").width(180.0).into(), + ]).axis(Axis::Horizontal).into() + ]).open_condition(OpenCondition::Click), + Menux::new(debug_button("aaa").into(), vec![ + debug_button("abc").width(180.0).into(), + debug_button("def").width(180.0).into(), + debug_button("xxx").width(180.0).into(), + Menux::new(debug_button("syjdtyjd").width(180.0).into(), vec![ + debug_button("hghg").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("arga").width(180.0).into(), + debug_button("abcd").width(180.0).into(), + debug_button("vcsa").width(180.0).into(), + Menux::new(debug_button("htrsthfs").width(180.0).into(), vec![ + debug_button("hghg").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("vcsa").width(180.0).into(), + debug_button("kiug").width(180.0).into(), + ]).axis(Axis::Horizontal).into(), + debug_button("kiug").width(180.0).into(), + ]).axis(Axis::Horizontal).into(), + debug_button("abc").width(180.0).into(), + debug_button("def").width(180.0).into(), + debug_button("xxx").width(180.0).into(), + ]).open_condition(OpenCondition::Click), + Menux::new(debug_button("pondjssbah").into(), vec![ + debug_button("abc").width(180.0).into(), + debug_button("def").width(180.0).into(), + debug_button("xxx").width(180.0).into(), + debug_button("htrsrt").width(180.0).into(), + debug_button("htrdf").width(180.0).into(), + debug_button("ngfcgng").width(180.0).into(), + debug_button("hytfy").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("qegvd").width(180.0).into(), + debug_button("iuoiy").width(180.0).into(), + debug_button("rzsajf").width(180.0).into(), + debug_button("pkmehs").width(180.0).into(), + debug_button("ivrye").width(180.0).into(), + debug_button("zhdkr").width(180.0).into(), + debug_button("vjdiwo").width(180.0).into(), + Menux::new(debug_button("syjdtyjd").width(180.0).into(), vec![ + debug_button("hghg").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("arga").width(180.0).into(), + debug_button("abcd").width(180.0).into(), + debug_button("vcsa").width(180.0).into(), + Menux::new(debug_button("htrsthfs").width(180.0).into(), vec![ + debug_button("hghg").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("vcsa").width(180.0).into(), + debug_button("kiug").width(180.0).into(), + ]).axis(Axis::Horizontal).into(), + debug_button("kiug").width(180.0).into(), + ]).axis(Axis::Horizontal).into(), + debug_button("abc").width(180.0).into(), + debug_button("def").width(180.0).into(), + debug_button("xxx").width(180.0).into(), + debug_button("htrsrt").width(180.0).into(), + debug_button("htrdf").width(180.0).into(), + debug_button("ngfcgng").width(180.0).into(), + debug_button("hytfy").width(180.0).into(), + debug_button("kuyg").width(180.0).into(), + debug_button("qegvd").width(180.0).into(), + debug_button("iuoiy").width(180.0).into(), + debug_button("rzsajf").width(180.0).into(), + debug_button("pkmehs").width(180.0).into(), + debug_button("ivrye").width(180.0).into(), + debug_button("zhdkr").width(180.0).into(), + debug_button("vjdiwo").width(180.0).into(), + ]).open_condition(OpenCondition::Click), ]; // let mb = MenuBar::new( @@ -340,7 +429,7 @@ impl button::StyleSheet for ButtonStyle { } } -/* fn base_button<'a>( +fn base_button<'a>( content: impl Into>, msg: Message, ) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { @@ -353,8 +442,8 @@ impl button::StyleSheet for ButtonStyle { fn labeled_button<'a>(label: &str, msg: Message) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { base_button( text(label) - .width(Length::Fill) - .height(Length::Fill) + // .width(Length::Fill) + // .height(Length::Fill) .vertical_alignment(alignment::Vertical::Center), msg, ) @@ -871,4 +960,3 @@ fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { root } - */ \ No newline at end of file diff --git a/src/native/menu.rs b/src/native/menu.rs index 63150f2d..6903ba6e 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -57,11 +57,12 @@ pub mod menu_bar; mod menu_inner; pub mod menu_tree; pub mod menux; +mod types; pub use crate::style::menu_bar::{Appearance, StyleSheet}; /// A `MenuBar` collects `MenuTree`s and handles pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Renderer>; -pub use menu_inner::{CloseCondition, ItemHeight, ItemWidth, PathHighlight}; +pub use types::*; /// Nested menu is essentially a tree of items, a menu is a collection of items pub type MenuTree<'a, Message, Renderer> = menu_tree::MenuTree<'a, Message, Renderer>; pub use menux::Menux; \ No newline at end of file diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 4f878c63..b0c38c10 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,12 +1,13 @@ //! A widget that handles menu trees use super::{ - menu_inner::{ + types::{ // Menu, // Direction, CloseCondition, ItemHeight, ItemWidth, - MenuState, PathHighlight, + PathHighlight, }, + menu_inner::MenuState, menu_tree::MenuTree, }; use crate::style::menu_bar::StyleSheet; diff --git a/src/native/menu/menu_inner.rs b/src/native/menu/menu_inner.rs index 8c33b26d..1ed0d943 100644 --- a/src/native/menu/menu_inner.rs +++ b/src/native/menu/menu_inner.rs @@ -1,6 +1,7 @@ //! Menu tree overlay use super::{menu_bar::MenuBarState, menu_tree::MenuTree}; use crate::style::menu_bar::StyleSheet; +use super::types::*; use iced_widget::core::{ event, @@ -12,68 +13,6 @@ use iced_widget::core::{ Border, Shadow }; -/// The condition of when to close a menu -#[derive(Debug, Clone, Copy)] -pub struct CloseCondition { - /// Close menus when the cursor moves outside the check bounds - pub leave: bool, - - /// Close menus when the cursor clicks outside the check bounds - pub click_outside: bool, - - /// Close menus when the cursor clicks inside the check bounds - pub click_inside: bool, -} - -/// The width of an item -#[derive(Debug, Clone, Copy)] -pub enum ItemWidth { - /// Use uniform width - Uniform(u16), - /// Static tries to use the width value of each menu(menu tree with children), - /// the widths of items(menu tree with empty children) will be the same as the menu they're in, - /// if that value is None, - /// the default value will be used instead, - /// which is the value of the Static variant - Static(u16), -} - -/// The height of an item -#[derive(Debug, Clone, Copy)] -pub enum ItemHeight { - /// Use uniform height. - Uniform(u16), - /// Static tries to use `MenuTree.height` as item height, - /// when it's `None` it'll fallback to the value of the `Static` variant. - Static(u16), - /// Dynamic tries to automatically choose the proper item height for you, - /// but it only works in certain cases: - /// - /// - Fixed height - /// - Shrink height - /// - Menu tree height - /// - /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. - Dynamic(u16), -} - -/// Methods for drawing path highlight -#[derive(Debug, Clone, Copy)] -pub enum PathHighlight { - /// Draw the full path, - Full, - /// Omit the active item(the last item in the path) - OmitActive, - /// Omit the active item if it's not a menu - MenuActive, -} - -/// X+ goes right and Y+ goes down -#[derive(Debug, Clone, Copy)] -pub(super) enum Direction { - Positive, - Negative, -} /// Adaptive open direction #[derive(Debug)] diff --git a/src/native/menu/menux.rs b/src/native/menu/menux.rs index 0c82ae36..61902844 100644 --- a/src/native/menu/menux.rs +++ b/src/native/menu/menux.rs @@ -1,16 +1,20 @@ //! doc //! use iced_widget::core::{ - widget::{self, tree::{self, Tree}, }, - Element, Widget, - renderer, overlay, event, layout, mouse, touch, - Event, Rectangle, Clipboard, Shell, Size, Length, Alignment, Padding, Point + event, layout, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, + Alignment, Border, Clipboard, Color, Element, Event, Length, + Padding, Point, Vector, Rectangle, Shell, Size, Widget, }; -use iced_widget::Column; -use super::menu_inner::Direction; +use super::types::*; struct MenuxState{ open: bool, + scroll_offset: f32, +} +impl Default for MenuxState{ + fn default() -> Self { + Self { open: false, scroll_offset: 0.0 } + } } /// doc @@ -25,7 +29,9 @@ where padding: Padding, width: Length, height: Length, - direction: Direction, + axis: Axis, + offset: f32, + open_condition: OpenCondition, } impl<'a, Message, Theme, Renderer> Menux<'a, Message, Theme, Renderer> where @@ -40,7 +46,9 @@ where padding: [0.0;4].into(), width: Length::Shrink, height: Length::Shrink, - direction: Direction::Positive, + axis: Axis::Vertical, + offset: 0.0, + open_condition: OpenCondition::Hover, } } @@ -71,6 +79,18 @@ where self.height = height.into(); self } + + /// Sets the axis of the [`Menux`]. + pub fn axis(mut self, axis: Axis) -> Self { + self.axis = axis.into(); + self + } + + /// Sets the open condition of the [`Menux`]. + pub fn open_condition(mut self, open_condition: OpenCondition) -> Self { + self.open_condition = open_condition; + self + } } impl <'a, Message, Theme, Renderer> Widget for Menux<'a, Message, Theme, Renderer> where @@ -81,7 +101,7 @@ where } fn state(&self) -> tree::State { - tree::State::new(MenuxState{ open: false}) + tree::State::new(MenuxState::default()) } fn children(&self) -> Vec { @@ -132,28 +152,34 @@ where let bounds = layout.bounds(); use event::Status::*; - match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if cursor.is_over(bounds) { - state.open = true; - - Captured - }else{ - Ignored + match self.open_condition{ + OpenCondition::Click => match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if cursor.is_over(bounds) { + state.open = true; + state.scroll_offset = 0.0; + Captured + }else{ + Ignored + } } + _ => Ignored } - // Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) - // | Event::Touch(touch::Event::FingerLifted { .. }) => { - // state.open = false; - // Captured - // } - // Event::Touch(touch::Event::FingerLost { .. }) => { - // state.open = false; - // Captured - // } - _ => Ignored - }.merge(status) + OpenCondition::Hover => match event { + Event::Mouse(mouse::Event::CursorMoved { position }) => { + if bounds.contains(position) { + state.open = true; + state.scroll_offset = 0.0; + Captured + }else{ + Ignored + } + } + _ => Ignored + } + } + .merge(status) } fn draw( @@ -189,10 +215,7 @@ where og = og.push(c); } - println!("mx overlay downcast_ref"); let ms = state.downcast_mut::(); - println!("mx overlay downcast_ref done"); - if !ms.open { Some(og.overlay()) }else{ @@ -207,7 +230,8 @@ where padding: self.padding, width: self.width, height: self.height, - direction: self.direction, + axis: self.axis, + offset: self.offset, }.overlay() ).overlay()) } @@ -240,7 +264,8 @@ where padding: Padding, width: Length, height: Length, - direction: Direction, + axis: Axis, + offset: f32, } impl<'a, 'b, Message, Theme, Renderer> MenuxOverlay<'a, 'b, Message, Theme, Renderer> where @@ -276,15 +301,36 @@ where &mut self.tree.children ); - let aod = Aod{ - horizontal: true, - vertical: false, - horizontal_overlap: true, - vertical_overlap: false, - horizontal_direction: Direction::Positive, - vertical_direction: Direction::Positive, - horizontal_offset: 0.0, - vertical_offset: 0.0, + let hcenter = bounds.width / 2.0; + let vcenter = bounds.height / 2.0; + + let phcenter = self.parent_bounds.x + self.parent_bounds.width / 2.0; + let pvcenter = self.parent_bounds.y + self.parent_bounds.height / 2.0; + + let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; + let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; + + let aod = match self.axis { + Axis::Horizontal => Aod{ + horizontal: false, + vertical: true, + horizontal_overlap: false, + vertical_overlap: true, + horizontal_direction, + vertical_direction, + horizontal_offset: self.offset, + vertical_offset: 0.0, + }, + Axis::Vertical => Aod{ + horizontal: true, + vertical: false, + horizontal_overlap: true, + vertical_overlap: false, + horizontal_direction, + vertical_direction, + horizontal_offset: 0.0, + vertical_offset: self.offset, + } }; let children_size = layout.bounds().size(); @@ -311,7 +357,7 @@ where layout::Node::new(bounds), layout::Node::new(offset_bounds.size()).move_to(offset_bounds.position()), layout::Node::new(check_bounds.size()).move_to(check_bounds.position()), - ].into()) + ].into()).translate([0.0, self.state.scroll_offset]) } fn on_event( @@ -332,6 +378,8 @@ where let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); + let children_bounds = children_layout.bounds(); + let status = self.children .iter_mut() .zip(&mut self.tree.children) @@ -358,6 +406,15 @@ where || check_bounds.contains(position); Captured } + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + if cursor.is_over(children_bounds){ + self.state.scroll_offset += match delta{ + mouse::ScrollDelta::Lines { x, y } => y, + mouse::ScrollDelta::Pixels { x, y } => y, + }; + } + Ignored + } _ => Ignored }.merge(status) } @@ -375,14 +432,27 @@ where let children_layout = lc.next().unwrap(); let viewport = lc.next().unwrap().bounds(); - /* renderer.fill_quad( + renderer.fill_quad( + renderer::Quad{ + bounds: self.parent_bounds, + ..Default::default() + }, + Color::from([1.0, 0.0, 0.0, 0.5]) + ); + + renderer.fill_quad( renderer::Quad{ - bounds: todo!(), - border: todo!(), - shadow: todo!(), + bounds: pad_rectangle(children_layout.bounds(), 4.0.into()), + border: Border{ + // color: todo!(), + // width: 4.0, + radius: 4.0.into(), + ..Default::default() + }, + ..Default::default() }, - background - ); */ + Color::from([1.0; 3]) + ); if let Some(viewport) = children_layout.bounds().intersection(&viewport) { for ((child, tree), layout) in self @@ -411,8 +481,50 @@ where renderer ) } + + fn is_over( + &self, + layout: layout::Layout<'_>, + _renderer: &Renderer, + cursor_position: Point, + ) -> bool { + let mut lc = layout.children(); + let children_layout = lc.next().unwrap(); + children_layout.bounds().contains(cursor_position) + } } +enum CheckBound{ + In(Rectangle), + Out(Rectangle), +} + +struct CheckBounds{ + bounds: Vec> +} +impl CheckBounds{ + fn check(&self, point:Point, default: bool) -> bool{ + let mut cb = None; + for bounds in self.bounds.iter().rev(){ + for bound in bounds.iter(){ + match bound { + CheckBound::In(rect) | CheckBound::Out(rect) => { + if rect.contains(point){ + cb = Some(bound) + } + } + } + } + } + match cb { + Some(x) => match x { + CheckBound::In(_) => true, + CheckBound::Out(_) => false + } + None => default + } + } +} /// Adaptive open direction #[derive(Debug)] @@ -561,3 +673,179 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { } } + +/// A part of a menu where items are displayed. +/// +/// When the bounds of a menu exceed the viewport, +/// only items inside the viewport will be displayed, +/// when scrolling happens, this should be updated +#[derive(Debug, Clone, Copy)] +struct MenuSlice { + start_index: usize, + end_index: usize, + lower_bound_rel: f32, + upper_bound_rel: f32, +} + +fn slice( + children_bounds: Rectangle, + child_positions: Vec, + child_sizes: Vec, + scroll_offset: f32, + viewport_size: Size, + overlay_offset: Vector, + item_height: ItemHeight, +) -> MenuSlice { + // viewport space children bounds + let children_bounds = children_bounds + overlay_offset; + + let max_index = child_positions.len().saturating_sub(1); + + // viewport space absolute bounds + let lower_bound = children_bounds.y.max(0.0); + let upper_bound = (children_bounds.y + children_bounds.height).min(viewport_size.height); + + // menu space relative bounds + let lower_bound_rel = lower_bound - (children_bounds.y + scroll_offset); + let upper_bound_rel = upper_bound - (children_bounds.y + scroll_offset); + + // index range + let (start_index, end_index) = match item_height { + ItemHeight::Uniform(u) => { + let start_index = (lower_bound_rel / f32::from(u)).floor() as usize; + let end_index = ((upper_bound_rel / f32::from(u)).floor() as usize).min(max_index); + (start_index, end_index) + } + ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { + let positions = &child_positions; + let sizes = &child_sizes; + + let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); + let end_index = search_bound( + max_index, + start_index, + max_index, + upper_bound_rel, + positions, + sizes, + ) + .min(max_index); + + (start_index, end_index) + } + }; + + MenuSlice { + start_index, + end_index, + lower_bound_rel, + upper_bound_rel, + } +} + +fn search_bound( + default: usize, + default_left: usize, + default_right: usize, + bound: f32, + positions: &[f32], + sizes: &[Size], +) -> usize { + // binary search + let mut left = default_left; + let mut right = default_right; + + let mut index = default; + while left != right { + let m = ((left + right) / 2) + 1; + if positions[m] > bound { + right = m - 1; + } else { + left = m; + } + } + let height = sizes[left].height; + if positions[left] + height > bound { + index = left; + } + index +} + +/* fn process_scroll_events( + menu: &mut Menu<'_, '_, Message, Theme, Renderer>, + delta: mouse::ScrollDelta, + overlay_cursor: Point, + viewport_size: Size, + overlay_offset: Vector, +) -> event::Status +where + Renderer: renderer::Renderer, + Theme: StyleSheet, +{ + use event::Status::{Captured, Ignored}; + use mouse::ScrollDelta; + + let state = menu.tree.state.downcast_mut::(); + + let delta_y = match delta { + ScrollDelta::Lines { y, .. } => y * 60.0, + ScrollDelta::Pixels { y, .. } => y, + }; + + let calc_offset_bounds = |menu_state: &MenuState, viewport_size: Size| -> (f32, f32) { + // viewport space children bounds + let children_bounds = menu_state.menu_bounds.children_bounds + overlay_offset; + + let max_offset = (0.0 - children_bounds.y).max(0.0); + let min_offset = + (viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0); + (max_offset, min_offset) + }; + + // update + if state.menu_states.is_empty() { + return Ignored; + } else if state.menu_states.len() == 1 { + let last_ms = &mut state.menu_states[0]; + + if last_ms.index.is_none() { + return Captured; + } + + let (max_offset, min_offset) = calc_offset_bounds(last_ms, viewport_size); + last_ms.scroll_offset = (last_ms.scroll_offset + delta_y).clamp(min_offset, max_offset); + } else { + // >= 2 + let max_index = state.menu_states.len() - 1; + let last_two = &mut state.menu_states[max_index - 1..=max_index]; + + if last_two[1].index.is_some() { + // scroll the last one + let (max_offset, min_offset) = calc_offset_bounds(&last_two[1], viewport_size); + last_two[1].scroll_offset = + (last_two[1].scroll_offset + delta_y).clamp(min_offset, max_offset); + } else { + if !last_two[0] + .menu_bounds + .children_bounds + .contains(overlay_cursor) + { + return Captured; + } + + // scroll the second last one + let (max_offset, min_offset) = calc_offset_bounds(&last_two[0], viewport_size); + let scroll_offset = (last_two[0].scroll_offset + delta_y).clamp(min_offset, max_offset); + let clamped_delta_y = scroll_offset - last_two[0].scroll_offset; + last_two[0].scroll_offset = scroll_offset; + + // update the bounds of the last one + last_two[1].menu_bounds.parent_bounds.y += clamped_delta_y; + last_two[1].menu_bounds.children_bounds.y += clamped_delta_y; + last_two[1].menu_bounds.check_bounds.y += clamped_delta_y; + } + } + Captured +} +*/ + diff --git a/src/native/menu/types.rs b/src/native/menu/types.rs new file mode 100644 index 00000000..ea90ee89 --- /dev/null +++ b/src/native/menu/types.rs @@ -0,0 +1,78 @@ + +/// The condition of when to close a menu +#[derive(Debug, Clone, Copy)] +pub struct CloseCondition { + /// Close menus when the cursor moves outside the check bounds + pub leave: bool, + + /// Close menus when the cursor clicks outside the check bounds + pub click_outside: bool, + + /// Close menus when the cursor clicks inside the check bounds + pub click_inside: bool, +} + +/// The width of an item +#[derive(Debug, Clone, Copy)] +pub enum ItemWidth { + /// Use uniform width + Uniform(u16), + /// Static tries to use the width value of each menu(menu tree with children), + /// the widths of items(menu tree with empty children) will be the same as the menu they're in, + /// if that value is None, + /// the default value will be used instead, + /// which is the value of the Static variant + Static(u16), +} + +/// The height of an item +#[derive(Debug, Clone, Copy)] +pub enum ItemHeight { + /// Use uniform height. + Uniform(u16), + /// Static tries to use `MenuTree.height` as item height, + /// when it's `None` it'll fallback to the value of the `Static` variant. + Static(u16), + /// Dynamic tries to automatically choose the proper item height for you, + /// but it only works in certain cases: + /// + /// - Fixed height + /// - Shrink height + /// - Menu tree height + /// + /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. + Dynamic(u16), +} + +/// Methods for drawing path highlight +#[derive(Debug, Clone, Copy)] +pub enum PathHighlight { + /// Draw the full path, + Full, + /// Omit the active item(the last item in the path) + OmitActive, + /// Omit the active item if it's not a menu + MenuActive, +} + +/// X+ goes right and Y+ goes down +#[derive(Debug, Clone, Copy)] +pub(super) enum Direction { + Positive, + Negative, +} + +/// Axis +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +pub enum Axis{ + Horizontal, + Vertical, +} + +#[allow(missing_docs)] +pub enum OpenCondition{ + Hover, + Click, +} + From a43969bcd7c125675ba0baf7cd71dd9ae3b3fb71 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 31 Jan 2024 21:03:39 +0800 Subject: [PATCH 04/40] scroll --- examples/menu/src/main.rs | 82 ++++++++++++------ src/native/menu/menux.rs | 169 ++++++++++++++++++-------------------- 2 files changed, 138 insertions(+), 113 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 37d69f1e..39fbe9b1 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -1,7 +1,7 @@ -use iced::widget::column as col; +use iced::widget::{column as col, vertical_space}; use iced::widget::{ button, checkbox, container, horizontal_space, pick_list, row, slider, svg, text, text_input, - toggler, vertical_slider, + toggler, vertical_slider, scrollable, }; use iced::{alignment, theme, Application, Border, Color, Element, Event, Length, Pixels, Size}; @@ -150,7 +150,7 @@ impl Application for App { } fn update(&mut self, message: Self::Message) -> iced::Command { - println!("app update"); + // println!("app update"); match message { Message::Debug(s) => { self.title = s; @@ -214,12 +214,12 @@ impl Application for App { } fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { - println!("app view"); - let pick_size_option = pick_list( + // println!("app view"); + /* let pick_size_option = pick_list( &SizeOption::ALL[..], Some(self.size_option), Message::SizeOption, - ); + ); */ let mb = row![ Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ @@ -362,42 +362,74 @@ impl Application for App { click_inside: false, }); */ - let r = if self.flip_h { - row!(pick_size_option, horizontal_space(Length::Fill), + /* let r = if self.flip_h { + row!( + // pick_size_option, + horizontal_space(Length::Fill), mb, ) } else { row!( mb, - horizontal_space(Length::Fill), pick_size_option + horizontal_space(Length::Fill), + // pick_size_option ) } .padding([2, 8]) - .align_items(alignment::Alignment::Center); + .align_items(alignment::Alignment::Center); */ - let top_bar_style: fn(&iced::Theme) -> container::Appearance = - |_theme| container::Appearance { - background: Some(Color::TRANSPARENT.into()), - ..Default::default() - }; - let top_bar = container(r).width(Length::Fill).style(top_bar_style); + let r = row![ + horizontal_space(800), + mb, + horizontal_space(800), + ] + .padding([2, 8]) + .align_items(alignment::Alignment::Center) + ; + + + // let top_bar_style: fn(&iced::Theme) -> container::Appearance = + // |_theme| container::Appearance { + // background: Some(Color::TRANSPARENT.into()), + // ..Default::default() + // }; + // let top_bar = container(r).width(Length::Fill).style(top_bar_style); + + /* + let c = if self.flip_v { + col![back, top_bar,] + } else { + col![top_bar, back,] + }; + c.into() */ + + let c = col![ + vertical_space(600), + r, + vertical_space(600), + ]; + + let sc = scrollable(c) + // .direction(scrollable::Direction::Both{ + // vertical: scrollable::Properties::new(), + // horizontal: scrollable::Properties::new(), + // }); + .direction(scrollable::Direction::Both{ + vertical: scrollable::Properties::new(), + horizontal: scrollable::Properties::new(), + }); + let back_style: fn(&iced::Theme) -> container::Appearance = |theme| container::Appearance { background: Some(theme.extended_palette().primary.base.color.into()), ..Default::default() }; - let back = container(col![]) + let back = container(sc) .width(Length::Fill) .height(Length::Fill) .style(back_style); - - let c = if self.flip_v { - col![back, top_bar,] - } else { - col![top_bar, back,] - }; - - c.into() + + back.into() } } diff --git a/src/native/menu/menux.rs b/src/native/menu/menux.rs index 61902844..2d3e31e0 100644 --- a/src/native/menu/menux.rs +++ b/src/native/menu/menux.rs @@ -1,7 +1,7 @@ //! doc //! use iced_widget::core::{ - event, layout, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, + event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Vector, Rectangle, Shell, Size, Widget, }; @@ -105,7 +105,7 @@ where } fn children(&self) -> Vec { - println!("mx children"); + // println!("mx children"); [ Tree::new(&self.parent), Tree{ @@ -121,7 +121,7 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - println!("mx layout"); + // println!("mx layout"); self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) } @@ -136,7 +136,7 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - println!("mx event"); + // println!("mx event"); let status = self.parent.as_widget_mut().on_event( &mut tree.children[0], event.clone(), @@ -192,7 +192,7 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - println!("mx draw"); + // println!("mx draw"); self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) } @@ -202,7 +202,7 @@ where layout: layout::Layout<'_>, renderer: &Renderer, ) -> Option> { - println!("mx overlay"); + // println!("mx overlay"); let mut og = overlay::Group::new(); let Tree { tag, state, children } = tree; @@ -250,7 +250,6 @@ where } } - struct MenuxOverlay<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer @@ -286,7 +285,13 @@ where position: iced_widget::core::Point, translation: iced_widget::core::Vector, ) -> layout::Node { + println!(); println!("mxo layout"); + println!("bounds: {:?}", bounds); + println!("position: {:?}", position); + println!("translation: {:?}", translation); + println!(); + let limits = layout::Limits::NONE.max_width(self.max_width); let layout = layout::flex::resolve( layout::flex::Axis::Vertical, @@ -301,11 +306,13 @@ where &mut self.tree.children ); + let vpb = self.parent_bounds + translation; // viewport space parent bounds + let hcenter = bounds.width / 2.0; let vcenter = bounds.height / 2.0; - - let phcenter = self.parent_bounds.x + self.parent_bounds.width / 2.0; - let pvcenter = self.parent_bounds.y + self.parent_bounds.height / 2.0; + + let phcenter = vpb.x + vpb.width / 2.0; + let pvcenter = vpb.y + vpb.height / 2.0; let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; @@ -335,7 +342,7 @@ where let children_size = layout.bounds().size(); let (children_position, offset_position) = aod.resolve( - self.parent_bounds + translation, + vpb, children_size, bounds ); @@ -353,11 +360,18 @@ where let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); layout::Node::with_children(Size::INFINITY, [ - layout.move_to(children_position), - layout::Node::new(bounds), - layout::Node::new(offset_bounds.size()).move_to(offset_bounds.position()), - layout::Node::new(check_bounds.size()).move_to(check_bounds.position()), - ].into()).translate([0.0, self.state.scroll_offset]) + layout.move_to(children_position) + .translate([0.0, self.state.scroll_offset]), // children layout + layout::Node::new(children_size) + .move_to(children_position), // prescroll children bounds + layout::Node::new(bounds), // viewport + layout::Node::new(offset_bounds.size()) + .move_to(offset_bounds.position()) + .translate([0.0, self.state.scroll_offset]), // offset bounds + layout::Node::new(check_bounds.size()) + .move_to(check_bounds.position()) + .translate([0.0, self.state.scroll_offset]), // check bounds + ].into()) } fn on_event( @@ -374,6 +388,7 @@ where let mut lc = layout.children(); let children_layout = lc.next().unwrap(); + let prescroll_children_bounds = lc.next().unwrap().bounds(); let viewport = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); @@ -399,6 +414,18 @@ where .fold(event::Status::Ignored, event::Status::merge); match event { + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + if cursor.is_over(children_bounds){ + // self.state.scroll_offset += match delta{ + // mouse::ScrollDelta::Lines { x, y } => y, + // mouse::ScrollDelta::Pixels { x, y } => y, + // }; + // Ignored + process_scroll_event(&mut self.state, prescroll_children_bounds, delta, viewport.size()) + }else{ + Ignored + } + } Event::Mouse(mouse::Event::CursorMoved { position }) => { self.state.open = self.parent_bounds.contains(position) @@ -406,15 +433,6 @@ where || check_bounds.contains(position); Captured } - Event::Mouse(mouse::Event::WheelScrolled { delta }) => { - if cursor.is_over(children_bounds){ - self.state.scroll_offset += match delta{ - mouse::ScrollDelta::Lines { x, y } => y, - mouse::ScrollDelta::Pixels { x, y } => y, - }; - } - Ignored - } _ => Ignored }.merge(status) } @@ -430,7 +448,10 @@ where println!("mxo draw"); let mut lc = layout.children(); let children_layout = lc.next().unwrap(); + let prescroll_children_bounds = lc.next().unwrap().bounds(); let viewport = lc.next().unwrap().bounds(); + // let offset_bounds = lc.next().unwrap().bounds(); + // let check_bounds = lc.next().unwrap().bounds(); renderer.fill_quad( renderer::Quad{ @@ -442,13 +463,13 @@ where renderer.fill_quad( renderer::Quad{ - bounds: pad_rectangle(children_layout.bounds(), 4.0.into()), + bounds: pad_rectangle(prescroll_children_bounds, 4.0.into()), border: Border{ - // color: todo!(), - // width: 4.0, - radius: 4.0.into(), + color: [0.5; 3].into(), + width: 1.5, + radius: 6.0.into(), ..Default::default() - }, + }, ..Default::default() }, Color::from([1.0; 3]) @@ -494,37 +515,6 @@ where } } -enum CheckBound{ - In(Rectangle), - Out(Rectangle), -} - -struct CheckBounds{ - bounds: Vec> -} -impl CheckBounds{ - fn check(&self, point:Point, default: bool) -> bool{ - let mut cb = None; - for bounds in self.bounds.iter().rev(){ - for bound in bounds.iter(){ - match bound { - CheckBound::In(rect) | CheckBound::Out(rect) => { - if rect.contains(point){ - cb = Some(bound) - } - } - } - } - } - match cb { - Some(x) => match x { - CheckBound::In(_) => true, - CheckBound::Out(_) => false - } - None => default - } - } -} /// Adaptive open direction #[derive(Debug)] @@ -688,16 +678,15 @@ struct MenuSlice { } fn slice( - children_bounds: Rectangle, + children_bounds: Rectangle, // offset + unsrolled child_positions: Vec, child_sizes: Vec, scroll_offset: f32, viewport_size: Size, - overlay_offset: Vector, - item_height: ItemHeight, + // overlay_offset: Vector, ) -> MenuSlice { // viewport space children bounds - let children_bounds = children_bounds + overlay_offset; + // let children_bounds = children_bounds + overlay_offset; let max_index = child_positions.len().saturating_sub(1); @@ -710,30 +699,13 @@ fn slice( let upper_bound_rel = upper_bound - (children_bounds.y + scroll_offset); // index range - let (start_index, end_index) = match item_height { - ItemHeight::Uniform(u) => { - let start_index = (lower_bound_rel / f32::from(u)).floor() as usize; - let end_index = ((upper_bound_rel / f32::from(u)).floor() as usize).min(max_index); - (start_index, end_index) - } - ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { - let positions = &child_positions; - let sizes = &child_sizes; - - let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); - let end_index = search_bound( - max_index, - start_index, - max_index, - upper_bound_rel, - positions, - sizes, - ) - .min(max_index); + let positions = &child_positions; + let sizes = &child_sizes; - (start_index, end_index) - } - }; + let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); + let end_index = + search_bound(max_index, start_index, max_index, upper_bound_rel, positions, sizes) + .min(max_index); MenuSlice { start_index, @@ -771,6 +743,27 @@ fn search_bound( index } +fn process_scroll_event( + state: &mut MenuxState, + prescroll_children_bounds: Rectangle, + delta: mouse::ScrollDelta, + viewport_size: Size, +) -> event::Status{ + use mouse::ScrollDelta; + + let pcb = prescroll_children_bounds; + + let delta_y = match delta { + ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, + }; + + let max_offset = (0.0 - pcb.y).max(0.0); + let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); + state.scroll_offset = (state.scroll_offset + delta_y).clamp(min_offset, max_offset); + + event::Status::Captured +} + /* fn process_scroll_events( menu: &mut Menu<'_, '_, Message, Theme, Renderer>, delta: mouse::ScrollDelta, From 016dd413bf3c84ce8d626e576ae5829be104d22b Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 3 Feb 2024 17:26:14 +0800 Subject: [PATCH 05/40] menu bar overlay --- src/native/menu.rs | 18 +- src/native/menu/bk/menu_bar.rs | 491 ++++++++++++++++++++++ src/native/menu/{ => bk}/menu_inner.rs | 0 src/native/menu/bk/menu_tree.rs | 128 ++++++ src/native/menu/{ => bk}/menux.rs | 0 src/native/menu/bk/types.rs | 78 ++++ src/native/menu/menu_bar.rs | 478 ++++------------------ src/native/menu/menu_bar_overlay.rs | 188 +++++++++ src/native/menu/menu_tree.rs | 384 +++++++++++++----- src/native/menu/menu_tree_overlay.rs | 540 +++++++++++++++++++++++++ src/native/menu/types.rs | 111 +++-- 11 files changed, 1871 insertions(+), 545 deletions(-) create mode 100644 src/native/menu/bk/menu_bar.rs rename src/native/menu/{ => bk}/menu_inner.rs (100%) create mode 100644 src/native/menu/bk/menu_tree.rs rename src/native/menu/{ => bk}/menux.rs (100%) create mode 100644 src/native/menu/bk/types.rs create mode 100644 src/native/menu/menu_bar_overlay.rs create mode 100644 src/native/menu/menu_tree_overlay.rs diff --git a/src/native/menu.rs b/src/native/menu.rs index 6903ba6e..bde892c6 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -54,15 +54,17 @@ mod flex; pub mod menu_bar; -mod menu_inner; +mod menu_bar_overlay; +// mod menu_inner; pub mod menu_tree; -pub mod menux; +// pub mod menux; mod types; +mod menu_tree_overlay; pub use crate::style::menu_bar::{Appearance, StyleSheet}; -/// A `MenuBar` collects `MenuTree`s and handles -pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Renderer>; -pub use types::*; -/// Nested menu is essentially a tree of items, a menu is a collection of items -pub type MenuTree<'a, Message, Renderer> = menu_tree::MenuTree<'a, Message, Renderer>; -pub use menux::Menux; \ No newline at end of file +// A `MenuBar` collects `MenuTree`s and handles +// pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Renderer>; +// pub use types::*; +// Nested menu is essentially a tree of items, a menu is a collection of items +// pub type MenuTree<'a, Message, Renderer> = menu_tree::MenuTree<'a, Message, Renderer>; +// pub use menux::Menux; \ No newline at end of file diff --git a/src/native/menu/bk/menu_bar.rs b/src/native/menu/bk/menu_bar.rs new file mode 100644 index 00000000..b0c38c10 --- /dev/null +++ b/src/native/menu/bk/menu_bar.rs @@ -0,0 +1,491 @@ +//! A widget that handles menu trees +use super::{ + types::{ + // Menu, + // Direction, + CloseCondition, + ItemHeight, ItemWidth, + PathHighlight, + }, + menu_inner::MenuState, + menu_tree::MenuTree, +}; +use crate::style::menu_bar::StyleSheet; + +use iced_widget::core::{ + event, + layout::{self, Limits, Node}, + mouse::{self, Cursor}, + overlay, renderer, touch, + widget::{tree, Tree}, + Alignment, Clipboard, Color, Element, Layout, Length, Padding, Rectangle, Shell, Widget, + Border, Shadow, + Size, +}; + +pub(super) struct MenuBarState { + pub(super) pressed: bool, + pub(super) view_cursor: Cursor, + pub(super) open: bool, + pub(super) active_root: Option, + // pub(super) horizontal_direction: Direction, + // pub(super) vertical_direction: Direction, + pub(super) menu_states: Vec, +} +impl MenuBarState { + /* pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { + self.menu_states + .iter() + .take_while(|ms| ms.index.is_some()) + .map(|ms| ms.index.expect("No indices were found in the menu state.")) + } */ + + /* pub(super) fn reset(&mut self) { + self.open = false; + self.active_root = None; + self.menu_states.clear(); + } */ +} +impl Default for MenuBarState { + fn default() -> Self { + Self { + pressed: false, + view_cursor: Cursor::Available([-0.5, -0.5].into()), + open: false, + active_root: None, + // horizontal_direction: Direction::Positive, + // vertical_direction: Direction::Positive, + menu_states: Vec::new(), + } + } +} + +/// A `MenuBar` collects `MenuTree`s and handles +/// all the layout, event processing and drawing +#[allow(missing_debug_implementations)] +pub struct MenuBar<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> +where + Renderer: renderer::Renderer, + Theme: StyleSheet, +{ + width: Length, + height: Length, + spacing: f32, + padding: Padding, + bounds_expand: u16, + main_offset: i32, + cross_offset: i32, + close_condition: CloseCondition, + item_width: ItemWidth, + item_height: ItemHeight, + path_highlight: Option, + menu_roots: Vec>, + style: Theme::Style, +} + +impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, + Theme: StyleSheet, +{ + /// Creates a new [`MenuBar`] with the given menu roots + #[must_use] + pub fn new(menu_roots: Vec>) -> Self { + let mut menu_roots = menu_roots; + menu_roots.iter_mut().for_each(MenuTree::set_index); + + let mb = Self { + width: Length::Shrink, + height: Length::Shrink, + spacing: 0.0, + padding: Padding::ZERO, + bounds_expand: 15, + main_offset: 0, + cross_offset: 0, + close_condition: CloseCondition { + leave: true, + click_outside: true, + click_inside: true, + }, + item_width: ItemWidth::Uniform(150), + item_height: ItemHeight::Uniform(30), + path_highlight: Some(PathHighlight::MenuActive), + menu_roots, + style: Theme::Style::default(), + }; + println!("new mb"); + mb + } + + /// Sets the expand value for each menu's check bounds + /// + /// When the cursor goes outside of a menu's check bounds, + /// the menu will be closed automatically, this value expands + /// the check bounds + #[must_use] + pub fn bounds_expand(mut self, value: u16) -> Self { + self.bounds_expand = value; + self + } + + /// [`CloseCondition`] + #[must_use] + pub fn close_condition(mut self, close_condition: CloseCondition) -> Self { + self.close_condition = close_condition; + self + } + + /// Moves each menu in the horizontal open direction + #[must_use] + pub fn cross_offset(mut self, value: i32) -> Self { + self.cross_offset = value; + self + } + + /// Sets the height of the [`MenuBar`] + #[must_use] + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + + /// [`ItemHeight`] + #[must_use] + pub fn item_height(mut self, item_height: ItemHeight) -> Self { + self.item_height = item_height; + self + } + + /// [`ItemWidth`] + #[must_use] + pub fn item_width(mut self, item_width: ItemWidth) -> Self { + self.item_width = item_width; + self + } + + /// Moves all the menus in the vertical open direction + #[must_use] + pub fn main_offset(mut self, value: i32) -> Self { + self.main_offset = value; + self + } + + /// Sets the [`Padding`] of the [`MenuBar`] + #[must_use] + pub fn padding>(mut self, padding: P) -> Self { + self.padding = padding.into(); + self + } + + /// Sets the method for drawing path highlight + #[must_use] + pub fn path_highlight(mut self, path_highlight: Option) -> Self { + self.path_highlight = path_highlight; + self + } + + /// Sets the spacing between menu roots + #[must_use] + pub fn spacing(mut self, units: f32) -> Self { + self.spacing = units; + self + } + + /// Sets the style of the menu bar and its menus + #[must_use] + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } + + /// Sets the width of the [`MenuBar`] + #[must_use] + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } +} +impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, + Theme: StyleSheet, +{ + fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + fn diff(&self, tree: &mut Tree) { + if tree.children.len() > self.menu_roots.len() { + tree.children.truncate(self.menu_roots.len()); + } + + tree.children + .iter_mut() + .zip(self.menu_roots.iter()) + .for_each(|(t, root)| { + let flat = root + .flattern() + .iter() + .map(|mt| mt.item.as_widget()) + .collect::>(); + + t.diff_children(&flat); + }); + + if tree.children.len() < self.menu_roots.len() { + let extended = self.menu_roots[tree.children.len()..].iter().map(|root| { + let mut tree = Tree::empty(); + let flat = root + .flattern() + .iter() + .map(|mt| Tree::new(mt.item.as_widget())) + .collect(); + tree.children = flat; + tree + }); + tree.children.extend(extended); + } + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(MenuBarState::default()) + } + + fn children(&self) -> Vec { + /* + menu bar + menu root 1 (stateless) + flat tree + menu root 2 (stateless) + flat tree + ... + */ + + self.menu_roots + .iter() + .map(|root| { + let mut tree = Tree::empty(); + let flat = root + .flattern() + .iter() + .map(|mt| Tree::new(mt.item.as_widget())) + .collect(); + tree.children = flat; + tree + }) + .collect() + } + + + fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { + use super::flex; + + let limits = limits.width(self.width).height(self.height); + let children = self + .menu_roots + .iter() + .map(|root| &root.item) + .collect::>(); + let mut tree_children = tree + .children + .iter_mut() + .map(|t| &mut t.children[0]) + .collect::>(); + flex::resolve( + flex::Axis::Horizontal, + renderer, + &limits, + self.width, + self.height, + self.padding, + self.spacing, + Alignment::Center, + &children, + tree_children.as_mut_slice() + ) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: event::Event, + layout: Layout<'_>, + view_cursor: Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + println!("mb on_event"); + use event::Event::{Mouse, Touch}; + use mouse::{Button::Left, Event::ButtonReleased}; + use touch::Event::{FingerLifted, FingerLost}; + + let root_status = process_root_events( + self.menu_roots.as_mut_slice(), + view_cursor, + tree, + &event, + layout, + renderer, + clipboard, + shell, + viewport, + ); + + let state = tree.state.downcast_mut::(); + + match event { + Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { + if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) { + state.view_cursor = view_cursor; + state.open = true; + } + } + _ => (), + } + root_status + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + view_cursor: Cursor, + viewport: &Rectangle, + ) { + println!("mb draw"); + let state = tree.state.downcast_ref::(); + println!("mb draw downcast"); + + let cursor_pos = view_cursor.position().unwrap_or_default(); + let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) { + state.view_cursor + } else { + view_cursor + }; + + // draw path highlight + if self.path_highlight.is_some() { + let styling = theme.appearance(&self.style); + if let Some(active) = state.active_root { + let active_bounds = layout + .children() + .nth(active) + .expect("Active child not found in menu?") + .bounds(); + let path_quad = renderer::Quad { + bounds: active_bounds, + border: Border{ + color: Color::TRANSPARENT, + width: 0.0, + radius: styling.border_radius.into() + }, + shadow: Shadow::default(), + }; + let path_color = styling.path; + renderer.fill_quad(path_quad, path_color); + } + } + + self.menu_roots + .iter() + .zip(&tree.children) + .zip(layout.children()) + .for_each(|((root, t), lo)| { + root.item.as_widget().draw( + &t.children[root.index], + renderer, + theme, + style, + lo, + position, + viewport, + ); + }); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + _renderer: &Renderer, + ) -> Option> { + let state = tree.state.downcast_ref::(); + if !state.open { + return None; + } + None + // Some( + // Menu { + // tree, + // menu_roots: &mut self.menu_roots, + // bounds_expand: self.bounds_expand, + // close_condition: self.close_condition, + // item_width: self.item_width, + // item_height: self.item_height, + // bar_bounds: layout.bounds(), + // main_offset: self.main_offset, + // cross_offset: self.cross_offset, + // root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), + // path_highlight: self.path_highlight, + // style: &self.style, + // } + // .overlay(), + // ) + } + + +} +impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> +where + Message: 'a + Clone, + Renderer: 'a + renderer::Renderer, + Theme: 'a + StyleSheet, +{ + fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { + Self::new(value) + } +} + +#[allow(unused_results, clippy::too_many_arguments)] +fn process_root_events( + menu_roots: &mut [MenuTree<'_, Message, Theme, Renderer>], + view_cursor: Cursor, + tree: &mut Tree, + event: &event::Event, + layout: Layout<'_>, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, +) -> event::Status +where + Renderer: renderer::Renderer, +{ + menu_roots + .iter_mut() + .zip(&mut tree.children) + .zip(layout.children()) + .map(|((root, t), lo)| { + // assert!(t.tag == tree::Tag::stateless()); + root.item.as_widget_mut().on_event( + &mut t.children[root.index], + event.clone(), + lo, + view_cursor, + renderer, + clipboard, + shell, + viewport, + ) + }) + .fold(event::Status::Ignored, event::Status::merge) +} diff --git a/src/native/menu/menu_inner.rs b/src/native/menu/bk/menu_inner.rs similarity index 100% rename from src/native/menu/menu_inner.rs rename to src/native/menu/bk/menu_inner.rs diff --git a/src/native/menu/bk/menu_tree.rs b/src/native/menu/bk/menu_tree.rs new file mode 100644 index 00000000..5ed8d7ad --- /dev/null +++ b/src/native/menu/bk/menu_tree.rs @@ -0,0 +1,128 @@ +//! A tree structure for constructing a hierarchical menu + +use iced_widget::core::{renderer, Element}; +/// Nested menu is essentially a tree of items, a menu is a collection of items +/// a menu itself can also be an item of another menu. +/// +/// A `MenuTree` represents a node in the tree, it holds a widget as a menu item +/// for its parent, and a list of menu tree as child nodes. +/// Conceptually a node is either a menu(inner node) or an item(leaf node), +/// but there's no need to explicitly distinguish them here, if a menu tree +/// has children, it's a menu, otherwise it's an item +#[allow(missing_debug_implementations)] +pub struct MenuTree<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> { + /// The menu tree will be flatten into a vector to build a linear widget tree, + /// the `index` field is the index of the item in that vector + pub(super) index: usize, + + /// The item of the menu tree + pub(super) item: Element<'a, Message, Theme, Renderer>, + /// The children of the menu tree + pub(super) children: Vec>, + /// The width of the menu tree + pub(super) width: Option, + /// The height of the menu tree + pub(super) height: Option, +} +impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + /// Create a new menu tree from a widget + pub fn new(item: impl Into>) -> Self { + Self { + index: 0, + item: item.into(), + children: Vec::new(), + width: None, + height: None, + } + } + + /// Create a menu tree from a widget and a vector of sub trees + pub fn with_children( + item: impl Into>, + children: Vec>>, + ) -> Self { + Self { + index: 0, + item: item.into(), + children: children.into_iter().map(Into::into).collect(), + width: None, + height: None, + } + } + + /// Sets the width of the menu tree. + /// See [`ItemWidth`] + /// + /// [`ItemWidth`]:`super::ItemWidth` + #[must_use] + pub fn width(mut self, width: u16) -> Self { + self.width = Some(width); + self + } + + /// Sets the height of the menu tree. + /// See [`ItemHeight`] + /// + /// [`ItemHeight`]: `super::ItemHeight` + #[must_use] + pub fn height(mut self, height: u16) -> Self { + self.height = Some(height); + self + } + + /* Keep `set_index()` and `flattern()` recurse in the same order */ + + /// Set the index of each item + pub(super) fn set_index(&mut self) { + /// inner counting function. + fn rec(mt: &mut MenuTree<'_, Message, Theme, Renderer>, count: &mut usize) { + // keep items under the same menu line up + mt.children.iter_mut().for_each(|c| { + c.index = *count; + *count += 1; + }); + + mt.children.iter_mut().for_each(|c| rec(c, count)); + } + + let mut count = 0; + self.index = count; + count += 1; + rec(self, &mut count); + } + + /// Flatten the menu tree + pub(super) fn flattern(&'a self) -> Vec<&Self> { + /// Inner flattening function + fn rec<'a, Message, Theme, Renderer>( + mt: &'a MenuTree<'a, Message, Theme, Renderer>, + flat: &mut Vec<&MenuTree<'a, Message, Theme, Renderer>>, + ) { + mt.children.iter().for_each(|c| { + flat.push(c); + }); + + mt.children.iter().for_each(|c| { + rec(c, flat); + }); + } + + let mut flat = Vec::new(); + flat.push(self); + rec(self, &mut flat); + + flat + } +} + +impl<'a, Message, Theme, Renderer> From> for MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn from(value: Element<'a, Message, Theme, Renderer>) -> Self { + Self::new(value) + } +} diff --git a/src/native/menu/menux.rs b/src/native/menu/bk/menux.rs similarity index 100% rename from src/native/menu/menux.rs rename to src/native/menu/bk/menux.rs diff --git a/src/native/menu/bk/types.rs b/src/native/menu/bk/types.rs new file mode 100644 index 00000000..ea90ee89 --- /dev/null +++ b/src/native/menu/bk/types.rs @@ -0,0 +1,78 @@ + +/// The condition of when to close a menu +#[derive(Debug, Clone, Copy)] +pub struct CloseCondition { + /// Close menus when the cursor moves outside the check bounds + pub leave: bool, + + /// Close menus when the cursor clicks outside the check bounds + pub click_outside: bool, + + /// Close menus when the cursor clicks inside the check bounds + pub click_inside: bool, +} + +/// The width of an item +#[derive(Debug, Clone, Copy)] +pub enum ItemWidth { + /// Use uniform width + Uniform(u16), + /// Static tries to use the width value of each menu(menu tree with children), + /// the widths of items(menu tree with empty children) will be the same as the menu they're in, + /// if that value is None, + /// the default value will be used instead, + /// which is the value of the Static variant + Static(u16), +} + +/// The height of an item +#[derive(Debug, Clone, Copy)] +pub enum ItemHeight { + /// Use uniform height. + Uniform(u16), + /// Static tries to use `MenuTree.height` as item height, + /// when it's `None` it'll fallback to the value of the `Static` variant. + Static(u16), + /// Dynamic tries to automatically choose the proper item height for you, + /// but it only works in certain cases: + /// + /// - Fixed height + /// - Shrink height + /// - Menu tree height + /// + /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. + Dynamic(u16), +} + +/// Methods for drawing path highlight +#[derive(Debug, Clone, Copy)] +pub enum PathHighlight { + /// Draw the full path, + Full, + /// Omit the active item(the last item in the path) + OmitActive, + /// Omit the active item if it's not a menu + MenuActive, +} + +/// X+ goes right and Y+ goes down +#[derive(Debug, Clone, Copy)] +pub(super) enum Direction { + Positive, + Negative, +} + +/// Axis +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +pub enum Axis{ + Horizontal, + Vertical, +} + +#[allow(missing_docs)] +pub enum OpenCondition{ + Hover, + Click, +} + diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index b0c38c10..6399d916 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,354 +1,111 @@ -//! A widget that handles menu trees -use super::{ - types::{ - // Menu, - // Direction, - CloseCondition, - ItemHeight, ItemWidth, - PathHighlight, - }, - menu_inner::MenuState, - menu_tree::MenuTree, +use iced_widget::core::{ + alignment, event, layout, mouse, overlay, renderer, touch, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget }; -use crate::style::menu_bar::StyleSheet; -use iced_widget::core::{ - event, - layout::{self, Limits, Node}, - mouse::{self, Cursor}, - overlay, renderer, touch, - widget::{tree, Tree}, - Alignment, Clipboard, Color, Element, Layout, Length, Padding, Rectangle, Shell, Widget, - Border, Shadow, - Size, +use super::{ + flex, menu_bar_overlay::MenuBarOverlay, menu_tree::* }; -pub(super) struct MenuBarState { - pub(super) pressed: bool, - pub(super) view_cursor: Cursor, +pub(super) struct MenuBarState{ + pub(super) active_root: usize, pub(super) open: bool, - pub(super) active_root: Option, - // pub(super) horizontal_direction: Direction, - // pub(super) vertical_direction: Direction, - pub(super) menu_states: Vec, -} -impl MenuBarState { - /* pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { - self.menu_states - .iter() - .take_while(|ms| ms.index.is_some()) - .map(|ms| ms.index.expect("No indices were found in the menu state.")) - } */ - - /* pub(super) fn reset(&mut self) { - self.open = false; - self.active_root = None; - self.menu_states.clear(); - } */ } -impl Default for MenuBarState { +impl Default for MenuBarState{ fn default() -> Self { Self { - pressed: false, - view_cursor: Cursor::Available([-0.5, -0.5].into()), + active_root: 0, open: false, - active_root: None, - // horizontal_direction: Direction::Positive, - // vertical_direction: Direction::Positive, - menu_states: Vec::new(), } } } -/// A `MenuBar` collects `MenuTree`s and handles -/// all the layout, event processing and drawing -#[allow(missing_debug_implementations)] -pub struct MenuBar<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> +pub struct MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Theme: StyleSheet, { - width: Length, - height: Length, + roots: Vec>, spacing: f32, padding: Padding, - bounds_expand: u16, - main_offset: i32, - cross_offset: i32, - close_condition: CloseCondition, - item_width: ItemWidth, - item_height: ItemHeight, - path_highlight: Option, - menu_roots: Vec>, - style: Theme::Style, + width: Length, + height: Length, } - impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Theme: StyleSheet, { - /// Creates a new [`MenuBar`] with the given menu roots - #[must_use] - pub fn new(menu_roots: Vec>) -> Self { - let mut menu_roots = menu_roots; - menu_roots.iter_mut().for_each(MenuTree::set_index); - - let mb = Self { - width: Length::Shrink, - height: Length::Shrink, + pub fn new(roots: Vec>) -> Self { + Self { + roots, spacing: 0.0, padding: Padding::ZERO, - bounds_expand: 15, - main_offset: 0, - cross_offset: 0, - close_condition: CloseCondition { - leave: true, - click_outside: true, - click_inside: true, - }, - item_width: ItemWidth::Uniform(150), - item_height: ItemHeight::Uniform(30), - path_highlight: Some(PathHighlight::MenuActive), - menu_roots, - style: Theme::Style::default(), - }; - println!("new mb"); - mb - } - - /// Sets the expand value for each menu's check bounds - /// - /// When the cursor goes outside of a menu's check bounds, - /// the menu will be closed automatically, this value expands - /// the check bounds - #[must_use] - pub fn bounds_expand(mut self, value: u16) -> Self { - self.bounds_expand = value; - self - } - - /// [`CloseCondition`] - #[must_use] - pub fn close_condition(mut self, close_condition: CloseCondition) -> Self { - self.close_condition = close_condition; - self - } - - /// Moves each menu in the horizontal open direction - #[must_use] - pub fn cross_offset(mut self, value: i32) -> Self { - self.cross_offset = value; - self - } - - /// Sets the height of the [`MenuBar`] - #[must_use] - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// [`ItemHeight`] - #[must_use] - pub fn item_height(mut self, item_height: ItemHeight) -> Self { - self.item_height = item_height; - self - } - - /// [`ItemWidth`] - #[must_use] - pub fn item_width(mut self, item_width: ItemWidth) -> Self { - self.item_width = item_width; - self - } - - /// Moves all the menus in the vertical open direction - #[must_use] - pub fn main_offset(mut self, value: i32) -> Self { - self.main_offset = value; - self - } - - /// Sets the [`Padding`] of the [`MenuBar`] - #[must_use] - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the method for drawing path highlight - #[must_use] - pub fn path_highlight(mut self, path_highlight: Option) -> Self { - self.path_highlight = path_highlight; - self - } - - /// Sets the spacing between menu roots - #[must_use] - pub fn spacing(mut self, units: f32) -> Self { - self.spacing = units; - self - } - - /// Sets the style of the menu bar and its menus - #[must_use] - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); - self - } - - /// Sets the width of the [`MenuBar`] - #[must_use] - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self + width: Length::Shrink, + height: Length::Shrink, + } } + } impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, - Theme: StyleSheet, { + fn size(&self) -> Size { Size::new(self.width, self.height) } - fn diff(&self, tree: &mut Tree) { - if tree.children.len() > self.menu_roots.len() { - tree.children.truncate(self.menu_roots.len()); - } - - tree.children - .iter_mut() - .zip(self.menu_roots.iter()) - .for_each(|(t, root)| { - let flat = root - .flattern() - .iter() - .map(|mt| mt.item.as_widget()) - .collect::>(); - - t.diff_children(&flat); - }); - - if tree.children.len() < self.menu_roots.len() { - let extended = self.menu_roots[tree.children.len()..].iter().map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }); - tree.children.extend(extended); - } - } - fn tag(&self) -> tree::Tag { tree::Tag::of::() } fn state(&self) -> tree::State { - tree::State::new(MenuBarState::default()) + tree::State::Some(Box::new(MenuBarState::default())) } fn children(&self) -> Vec { - /* - menu bar - menu root 1 (stateless) - flat tree - menu root 2 (stateless) - flat tree - ... - */ - - self.menu_roots - .iter() - .map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }) - .collect() + self.roots.iter().map(|mt| mt.tree()).collect::>() } - - fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { - use super::flex; + fn diff(&self, tree: &mut Tree) { + tree.diff_children_custom( + &self.roots, + |tree, mt| diff(tree, mt), + |mt| mt.tree() + ) + } - let limits = limits.width(self.width).height(self.height); - let children = self - .menu_roots - .iter() - .map(|root| &root.item) - .collect::>(); - let mut tree_children = tree - .children - .iter_mut() - .map(|t| &mut t.children[0]) - .collect::>(); + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { flex::resolve( - flex::Axis::Horizontal, - renderer, - &limits, - self.width, - self.height, - self.padding, - self.spacing, - Alignment::Center, - &children, - tree_children.as_mut_slice() + flex::Axis::Horizontal, + renderer, + limits, + self.width, + self.height, + self.padding, + self.spacing, + alignment::Alignment::Center, + &self.roots.iter().map(|mt| &mt.parent).collect::>(), + &mut tree.children.iter().map(|tree| &mut tree.children[0]).collect::>(), ) } fn on_event( &mut self, - tree: &mut Tree, + state: &mut Tree, event: event::Event, layout: Layout<'_>, - view_cursor: Cursor, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - println!("mb on_event"); - use event::Event::{Mouse, Touch}; - use mouse::{Button::Left, Event::ButtonReleased}; - use touch::Event::{FingerLifted, FingerLost}; - - let root_status = process_root_events( - self.menu_roots.as_mut_slice(), - view_cursor, - tree, - &event, - layout, - renderer, - clipboard, - shell, - viewport, - ); - - let state = tree.state.downcast_mut::(); - - match event { - Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { - if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) { - state.view_cursor = view_cursor; - state.open = true; - } - } - _ => (), - } - root_status + + event::Status::Ignored } fn draw( @@ -358,134 +115,51 @@ where theme: &Theme, style: &renderer::Style, layout: Layout<'_>, - view_cursor: Cursor, + cursor: mouse::Cursor, viewport: &Rectangle, ) { - println!("mb draw"); - let state = tree.state.downcast_ref::(); - println!("mb draw downcast"); - - let cursor_pos = view_cursor.position().unwrap_or_default(); - let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) { - state.view_cursor - } else { - view_cursor - }; - - // draw path highlight - if self.path_highlight.is_some() { - let styling = theme.appearance(&self.style); - if let Some(active) = state.active_root { - let active_bounds = layout - .children() - .nth(active) - .expect("Active child not found in menu?") - .bounds(); - let path_quad = renderer::Quad { - bounds: active_bounds, - border: Border{ - color: Color::TRANSPARENT, - width: 0.0, - radius: styling.border_radius.into() - }, - shadow: Shadow::default(), - }; - let path_color = styling.path; - renderer.fill_quad(path_quad, path_color); + if let Some(viewport) = layout.bounds().intersection(viewport) { + for ((root, state), layout) in self + .roots + .iter() + .zip(&tree.children) + .zip(layout.children()) + { + root.parent.as_widget().draw( + state, renderer, theme, style, layout, cursor, &viewport, + ); } } - - self.menu_roots - .iter() - .zip(&tree.children) - .zip(layout.children()) - .for_each(|((root, t), lo)| { - root.item.as_widget().draw( - &t.children[root.index], - renderer, - theme, - style, - lo, - position, - viewport, - ); - }); } - + fn overlay<'b>( &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, - _renderer: &Renderer, + renderer: &Renderer, ) -> Option> { - let state = tree.state.downcast_ref::(); - if !state.open { - return None; + let state = tree.state.downcast_mut::(); + let parent_bounds = layout.children().nth(state.active_root).unwrap().bounds(); + + if state.open{ + Some(MenuBarOverlay{ + tree, + active_tree: &mut tree.children[state.active_root], + active_root: &mut self.roots[state.active_root], + parent_bounds, + }.overlay_element()) + }else { + None } - None - // Some( - // Menu { - // tree, - // menu_roots: &mut self.menu_roots, - // bounds_expand: self.bounds_expand, - // close_condition: self.close_condition, - // item_width: self.item_width, - // item_height: self.item_height, - // bar_bounds: layout.bounds(), - // main_offset: self.main_offset, - // cross_offset: self.cross_offset, - // root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), - // path_highlight: self.path_highlight, - // style: &self.style, - // } - // .overlay(), - // ) } - - } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where - Message: 'a + Clone, + Message: 'a, Renderer: 'a + renderer::Renderer, - Theme: 'a + StyleSheet, + // Renderer::Theme: StyleSheet, { fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { Self::new(value) } } - -#[allow(unused_results, clippy::too_many_arguments)] -fn process_root_events( - menu_roots: &mut [MenuTree<'_, Message, Theme, Renderer>], - view_cursor: Cursor, - tree: &mut Tree, - event: &event::Event, - layout: Layout<'_>, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, -) -> event::Status -where - Renderer: renderer::Renderer, -{ - menu_roots - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|((root, t), lo)| { - // assert!(t.tag == tree::Tag::stateless()); - root.item.as_widget_mut().on_event( - &mut t.children[root.index], - event.clone(), - lo, - view_cursor, - renderer, - clipboard, - shell, - viewport, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) -} diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs new file mode 100644 index 00000000..8d679677 --- /dev/null +++ b/src/native/menu/menu_bar_overlay.rs @@ -0,0 +1,188 @@ +use iced_widget::core::{ + event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget +}; +use crate::menu::menu_tree_overlay::MenuTreeOverlay; + +use super::{ + menu_tree::{MenuTree, MenuTreeState}, + menu_bar::MenuBarState, +}; + +pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) tree: &'b mut Tree, + pub(super) active_tree: &'b mut Tree, + pub(super) active_root: &'b mut MenuTree<'a, Message, Theme, Renderer>, + pub(super) parent_bounds: Rectangle, + +} +impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) fn overlay_element(self) -> overlay::Element<'a, Message, Theme, Renderer>{ + overlay::Element::new(Point::ORIGIN, Box::new(self)) + } +} +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> Node { + /* + Node{ + rect: + children: [ + Node{} // mto layout + Node{} // overlays + Node{} // next mto + ] + } + */ + + fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( + tree: &mut Tree, + menu_tree: &MenuTree<'a, Message, Theme, Renderer>, + parent_bounds: Rectangle, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> Node { + let size = Size::INFINITY; + let state = tree.state.downcast_mut::(); + + let mut mto = MenuTreeOverlay{ + state, + tree, + menu_tree, + parent_bounds, + }; + + let mto_layout = mto.layout(renderer, bounds, position, translation); + + let overlays = menu_tree.children + .iter() + .zip(tree.children[1].children.iter_mut()) + .zip(mto_layout.children()[0].children()) + .filter_map(|((mt, t), n)|{ + mt.parent.as_widget_mut() + .overlay(t, Layout::new(n), renderer) + .and_then(|mut o| Some(o.layout(renderer, bounds, translation))) + }) + .collect::>(); + + let next = menu_tree.children + .iter() + .zip(tree.children[1].children.iter_mut()) + .zip(mto_layout.children()[0].children()) + .find_map(|((mt, t), l)|{ + let state = t.state.downcast_mut::(); + if state.open{ + let parent_bounds = l.bounds(); + // let position = parent_bounds.position(); + let position = Point::ORIGIN; + Some(rec(t, mt, parent_bounds, renderer, bounds, position, position - Point::ORIGIN)) + }else{ + None + } + }); + + Node::with_children( + size, + next.map_or( + [ + mto_layout, + Node::with_children(size, overlays), + ].into(), + |n| [ + mto_layout, + Node::with_children(size, overlays), + n + ].into() + ) + ) + } + + rec( + self.active_tree, + self.active_root, + self.parent_bounds, + renderer, + bounds, + position, + translation + ) + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + todo!() + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( + tree: &mut Tree, + menu_tree: &MenuTree<'a, Message, Theme, Renderer>, + parent_bounds: Rectangle, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ){ + let mut lc = layout.children(); + let mto_layout = lc.next().unwrap(); + let overlay_layout = lc.next().unwrap(); + + let state = tree.state.downcast_mut::(); + let mut mto = MenuTreeOverlay{ + state, + tree, + menu_tree, + parent_bounds, + }; + + mto.draw(renderer, theme, style, mto_layout, cursor); + + menu_tree.children + .iter() + .zip(tree.children[1].children.iter_mut()) + .filter_map(|(mt, t)|{ + mt.parent.as_widget_mut().overlay(t, Layout::new(&mt.layout(t, renderer, &Limits::NONE)), renderer) + }) + .zip(overlay_layout.children()) + .for_each(|(o, l)|{ + o.draw(renderer, theme, style, l, cursor) + }); + + if let Some(next_layout) = lc.next(){ + rec(tree, menu_tree, parent_bounds, renderer, theme, style, next_layout, cursor, viewport); + } + + } + } +} \ No newline at end of file diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 5ed8d7ad..501d157e 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,128 +1,322 @@ -//! A tree structure for constructing a hierarchical menu - -use iced_widget::core::{renderer, Element}; -/// Nested menu is essentially a tree of items, a menu is a collection of items -/// a menu itself can also be an item of another menu. -/// -/// A `MenuTree` represents a node in the tree, it holds a widget as a menu item -/// for its parent, and a list of menu tree as child nodes. -/// Conceptually a node is either a menu(inner node) or an item(leaf node), -/// but there's no need to explicitly distinguish them here, if a menu tree -/// has children, it's a menu, otherwise it's an item -#[allow(missing_debug_implementations)] -pub struct MenuTree<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> { - /// The menu tree will be flatten into a vector to build a linear widget tree, - /// the `index` field is the index of the item in that vector - pub(super) index: usize, - - /// The item of the menu tree - pub(super) item: Element<'a, Message, Theme, Renderer>, - /// The children of the menu tree +//! doc +//! +use iced_widget::core::{ + event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, + Alignment, Border, Clipboard, Color, Element, Event, Length, + Padding, Point, Vector, Rectangle, Shell, Size, Widget, +}; +use super::types::*; +use super::menu_tree_overlay::MenuTreeOverlay; + +pub(super) struct MenuTreeState{ + pub(super) open: bool, + pub(super) scroll_offset: f32, +} +impl Default for MenuTreeState{ + fn default() -> Self { + Self { open: false, scroll_offset: 0.0 } + } +} + +/// doc +#[allow(clippy::missing_docs_in_private_parents)] +pub struct MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) parent: Element<'a, Message, Theme, Renderer>, pub(super) children: Vec>, - /// The width of the menu tree - pub(super) width: Option, - /// The height of the menu tree - pub(super) height: Option, + pub(super) spacing: f32, + pub(super) padding: Padding, + pub(super) max_width: f32, + pub(super) width: Length, + pub(super) height: Length, + pub(super) axis: Axis, + pub(super) offset: f32, + pub(super) open_condition: OpenCondition, } impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - /// Create a new menu tree from a widget - pub fn new(item: impl Into>) -> Self { - Self { - index: 0, - item: item.into(), + #[allow(missing_docs)] + pub fn new( + parent: impl Into> + ) -> Self{ + Self{ + parent: parent.into(), children: Vec::new(), - width: None, - height: None, + spacing: 4.0, + padding: [0.0;4].into(), + max_width: f32::MAX, + width: Length::Shrink, + height: Length::Shrink, + axis: Axis::Vertical, + offset: 0.0, + open_condition: OpenCondition::Hover, } } - /// Create a menu tree from a widget and a vector of sub trees + #[allow(missing_docs)] pub fn with_children( - item: impl Into>, - children: Vec>>, - ) -> Self { - Self { - index: 0, - item: item.into(), + parent: impl Into>, + children: Vec>> + ) -> Self{ + Self{ + parent: parent.into(), children: children.into_iter().map(Into::into).collect(), - width: None, - height: None, + spacing: 4.0, + padding: [0.0;4].into(), + max_width: f32::MAX, + width: Length::Shrink, + height: Length::Shrink, + axis: Axis::Vertical, + offset: 0.0, + open_condition: OpenCondition::Hover, + } + } + + pub fn tree(&self) -> Tree{ + Tree{ + tag: self.tag(), + state: self.state(), + children: self.children() } } - /// Sets the width of the menu tree. - /// See [`ItemWidth`] + /// Sets the vertical spacing _between_ elements. /// - /// [`ItemWidth`]:`super::ItemWidth` - #[must_use] - pub fn width(mut self, width: u16) -> Self { - self.width = Some(width); + /// Custom margins per element do not exist in iced. You should use this + /// method instead! While less flexible, it helps you keep spacing between + /// elements consistent. + pub fn spacing(mut self, spacing: f32) -> Self { + self.spacing = spacing; self } - /// Sets the height of the menu tree. - /// See [`ItemHeight`] - /// - /// [`ItemHeight`]: `super::ItemHeight` - #[must_use] - pub fn height(mut self, height: u16) -> Self { - self.height = Some(height); + /// Sets the [`Padding`] of the [`MenuTree`]. + pub fn padding>(mut self, padding: P) -> Self { + self.padding = padding.into(); self } - /* Keep `set_index()` and `flattern()` recurse in the same order */ + /// Sets the width of the [`MenuTree`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } - /// Set the index of each item - pub(super) fn set_index(&mut self) { - /// inner counting function. - fn rec(mt: &mut MenuTree<'_, Message, Theme, Renderer>, count: &mut usize) { - // keep items under the same menu line up - mt.children.iter_mut().for_each(|c| { - c.index = *count; - *count += 1; - }); + /// Sets the height of the [`MenuTree`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } - mt.children.iter_mut().for_each(|c| rec(c, count)); - } + /// Sets the axis of the [`MenuTree`]. + pub fn axis(mut self, axis: Axis) -> Self { + self.axis = axis.into(); + self + } - let mut count = 0; - self.index = count; - count += 1; - rec(self, &mut count); - } - - /// Flatten the menu tree - pub(super) fn flattern(&'a self) -> Vec<&Self> { - /// Inner flattening function - fn rec<'a, Message, Theme, Renderer>( - mt: &'a MenuTree<'a, Message, Theme, Renderer>, - flat: &mut Vec<&MenuTree<'a, Message, Theme, Renderer>>, - ) { - mt.children.iter().for_each(|c| { - flat.push(c); - }); - - mt.children.iter().for_each(|c| { - rec(c, flat); - }); - } + /// Sets the open condition of the [`MenuTree`]. + pub fn open_condition(mut self, open_condition: OpenCondition) -> Self { + self.open_condition = open_condition; + self + } +} +impl <'a, Message, Theme, Renderer> + // Widget for + MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + pub(super) fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + pub(super) fn state(&self) -> tree::State { + tree::State::new(MenuTreeState::default()) + } + + pub(super) fn children(&self) -> Vec { + [ + Tree::new(&self.parent), + Tree{ + children: self.children.iter().map(|mt| mt.tree()).collect::>(), + ..Tree::empty() + } + ].into() + } - let mut flat = Vec::new(); - flat.push(self); - rec(self, &mut flat); + /* + Tree{ + tag: MTS + state: MTS + children: [ + Tree{parent}, + Tree{tag:none, state:none, children:[ + Tree{tag:MTS, state:MTS, children:[..]}, + Tree{tag:MTS, state:MTS, children:[..]}, + Tree{tag:MTS, state:MTS, children:[..]}, + ... + ]} + ] + } + */ + pub(super) fn diff(&self, tree: &mut Tree) { + let parent = &mut tree.children[0]; + let children = &mut tree.children[1]; + + parent.diff(&self.parent); + children.diff_children_custom( + &self.children, + |tree, mt| diff(tree, mt), + |mt| mt.tree() + ) + } - flat + pub(super) fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + // println!("mt layout"); + self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) } + + pub(super) fn on_event( + &mut self, + tree: &mut Tree, + event: event::Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + // println!("mt event"); + let status = self.parent.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); + + let state = tree.state.downcast_mut::(); + let bounds = layout.bounds(); + + use event::Status::*; + match self.open_condition{ + OpenCondition::Click => match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if cursor.is_over(bounds) { + state.open = true; + state.scroll_offset = 0.0; + Captured + }else{ + Ignored + } + } + _ => Ignored + } + OpenCondition::Hover => match event { + Event::Mouse(mouse::Event::CursorMoved { position }) => { + if bounds.contains(position) { + state.open = true; + state.scroll_offset = 0.0; + Captured + }else{ + Ignored + } + } + _ => Ignored + } + } + .merge(status) + } + + pub(super) fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + // println!("mt draw"); + self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) + } + + // pub(super) fn overlay<'b>( + // &'b mut self, + // extra_input:f32, + // tree: &'b mut Tree, + // layout: layout::Layout<'_>, + // renderer: &Renderer, + // ) -> Option> { + // // ) -> Option> { + // // ) -> Option> { + // // println!("mt overlay"); + // let mut og = overlay::Group::new(); + // let Tree { tag, state, children } = tree; + + // // let [parent_tree, children_tree] = children.as_mut_slice() else { panic!("Tree Error") }; + // let parent_tree = &mut children[0]; + // let children_tree = &mut children[1]; + + // if let Some(c) = self.parent + // .as_widget_mut() + // .overlay(parent_tree, layout, renderer) + // { + // og = og.push(c); + // } + + // let ms = state.downcast_mut::(); + // if !ms.open { + // Some(og.overlay().into()) + // }else{ + // // Some(og.push( + // // MenuTreeOverlay{ + // // state: ms, + // // tree: children_tree, + // // children: &mut self.children, + // // parent_bounds: layout.bounds(), + // // max_width: 1000.0, + // // spacing: self.spacing, + // // padding: self.padding, + // // width: self.width, + // // height: self.height, + // // axis: self.axis, + // // offset: self.offset, + // // }.overlay() + // // ).overlay()) + // None + // } + // } } -impl<'a, Message, Theme, Renderer> From> for MenuTree<'a, Message, Theme, Renderer> -where +pub fn diff<'a, Message, Theme, Renderer>( + tree: &mut Tree, + new: &MenuTree<'a, Message, Theme, Renderer> +) where Renderer: renderer::Renderer, { - fn from(value: Element<'a, Message, Theme, Renderer>) -> Self { - Self::new(value) + if tree.tag == new.tag() { + new.diff(tree); + } else { + *tree = new.tree() } -} +} \ No newline at end of file diff --git a/src/native/menu/menu_tree_overlay.rs b/src/native/menu/menu_tree_overlay.rs new file mode 100644 index 00000000..071904a1 --- /dev/null +++ b/src/native/menu/menu_tree_overlay.rs @@ -0,0 +1,540 @@ +use iced_widget::core::{ + event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, + Alignment, Border, Clipboard, Color, Element, Event, Length, + Padding, Point, Vector, Rectangle, Shell, Size, Widget, +}; +use super::types::*; +use super::menu_tree::{MenuTree, MenuTreeState}; + +pub(super) struct MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer +{ + pub(super) state: &'b mut MenuTreeState, + pub(super) tree: &'b mut Tree, + // pub(super) children: &'b mut [MenuTree<'a, Message, Theme, Renderer>], + pub(super) parent_bounds: Rectangle, + pub(super) menu_tree: &'b MenuTree<'a, Message, Theme, Renderer>, + // pub(super) max_width: f32, + // pub(super) spacing: f32, + // pub(super) padding: Padding, + // pub(super) width: Length, + // pub(super) height: Length, + // pub(super) axis: Axis, + // pub(super) offset: f32, +} +impl<'a, 'b, Message, Theme, Renderer> MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer +{ + // pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer>{ + // overlay::Element::new(Point::ORIGIN, Box::new(self)) + // } +} +impl<'a, 'b, Message, Theme, Renderer> + // overlay::Overlay for + MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> layout::Node { + println!(); + println!("mxo layout"); + println!("bounds: {:?}", bounds); + println!("position: {:?}", position); + println!("translation: {:?}", translation); + println!(); + + let limits = layout::Limits::NONE.max_width(self.menu_tree.max_width); + let children = &self.menu_tree.children.iter().map(|mt| mt.parent).collect::>(); + let layout = layout::flex::resolve( + layout::flex::Axis::Vertical, + renderer, + &limits, + self.menu_tree.width, + self.menu_tree.height, + self.menu_tree.padding, + self.menu_tree.spacing, + Alignment::Center, + children, + &mut self.tree.children + ); + + let vpb = self.parent_bounds + translation; // viewport space parent bounds + + let hcenter = bounds.width / 2.0; + let vcenter = bounds.height / 2.0; + + let phcenter = vpb.x + vpb.width / 2.0; + let pvcenter = vpb.y + vpb.height / 2.0; + + let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; + let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; + + let aod = match self.menu_tree.axis { + Axis::Horizontal => Aod{ + horizontal: false, + vertical: true, + horizontal_overlap: false, + vertical_overlap: true, + horizontal_direction, + vertical_direction, + horizontal_offset: self.menu_tree.offset, + vertical_offset: 0.0, + }, + Axis::Vertical => Aod{ + horizontal: true, + vertical: false, + horizontal_overlap: true, + vertical_overlap: false, + horizontal_direction, + vertical_direction, + horizontal_offset: 0.0, + vertical_offset: self.menu_tree.offset, + } + }; + + let children_size = layout.bounds().size(); + let (children_position, offset_position) = aod.resolve( + vpb, + children_size, + bounds + ); + + // calc offset bounds + let delta = children_position - offset_position; + let offset_size = if delta.x.abs() > delta.y.abs() { + Size::new(delta.x, children_size.height) + } else { + Size::new(children_size.width, delta.y) + }; + let offset_bounds = Rectangle::new(offset_position, offset_size); + let children_bounds = Rectangle::new(children_position, children_size); + let bounds_expand = 30.0; + let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); + + layout::Node::with_children(Size::INFINITY, [ + layout.move_to(children_position) + .translate([0.0, self.state.scroll_offset]), // children layout + layout::Node::new(children_size) + .move_to(children_position), // prescroll children bounds + layout::Node::new(bounds), // viewport + layout::Node::new(offset_bounds.size()) + .move_to(offset_bounds.position()) + .translate([0.0, self.state.scroll_offset]), // offset bounds + layout::Node::new(check_bounds.size()) + .move_to(check_bounds.position()) + .translate([0.0, self.state.scroll_offset]), // check bounds + ].into()) + } + + pub(super) fn on_event( + &mut self, + event: event::Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + println!("mxo event"); + use event::Status::*; + + let mut lc = layout.children(); + let children_layout = lc.next().unwrap(); + let prescroll_children_bounds = lc.next().unwrap().bounds(); + let viewport = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + let children_bounds = children_layout.bounds(); + + let status = self.menu_tree.children + .iter_mut() + .zip(&mut self.tree.children) + .zip(children_layout.children()) + .map(|((child, tree), layout)| + child.parent.as_widget_mut().on_event( + tree, + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + &viewport, + ) + ) + .fold(event::Status::Ignored, event::Status::merge); + + match event { + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + if cursor.is_over(children_bounds){ + // self.state.scroll_offset += match delta{ + // mouse::ScrollDelta::Lines { x, y } => y, + // mouse::ScrollDelta::Pixels { x, y } => y, + // }; + // Ignored + process_scroll_event(&mut self.state, prescroll_children_bounds, delta, viewport.size()) + }else{ + Ignored + } + } + Event::Mouse(mouse::Event::CursorMoved { position }) => { + self.state.open = + self.parent_bounds.contains(position) + || offset_bounds.contains(position) + || check_bounds.contains(position); + Captured + } + _ => Ignored + }.merge(status) + } + + pub(super) fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + ) { + println!("mxo draw"); + let mut lc = layout.children(); + let children_layout = lc.next().unwrap(); + let prescroll_children_bounds = lc.next().unwrap().bounds(); + let viewport = lc.next().unwrap().bounds(); + // let offset_bounds = lc.next().unwrap().bounds(); + // let check_bounds = lc.next().unwrap().bounds(); + + renderer.fill_quad( + renderer::Quad{ + bounds: self.parent_bounds, + ..Default::default() + }, + Color::from([1.0, 0.0, 0.0, 0.5]) + ); + + renderer.fill_quad( + renderer::Quad{ + bounds: pad_rectangle(prescroll_children_bounds, 4.0.into()), + border: Border{ + color: [0.5; 3].into(), + width: 1.5, + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + Color::from([1.0; 3]) + ); + + if let Some(viewport) = children_layout.bounds().intersection(&viewport) { + for ((child, tree), layout) in self + .menu_tree.children + .iter() + .zip(&self.tree.children) + .zip(children_layout.children()) + { + child.parent.as_widget().draw( + tree, renderer, theme, style, layout, cursor, &viewport, + ); + } + } + } + + // fn overlay<'c>( + // &'c mut self, + // extra_input:f32, + // layout: layout::Layout<'_>, + // renderer: &Renderer, + // ) -> Option> { + // // ) -> Option> { + // // ) -> Option> { + // println!("mxo overlay"); + // // overlay::from_children( + // // self.children, + // // self.tree, + // // layout.children().next().unwrap(), + // // renderer + // // ) + // let layout = layout.children().next().unwrap(); + // let children = self.children + // .iter_mut() + // .zip(&mut self.tree.children) + // .zip(layout.children()) + // .filter_map(|((child, state), layout)| { + // child.overlay(0.0, state, layout, renderer) + // }) + // .collect::>(); + + // (!children.is_empty()).then(|| overlay::Group::with_children(children).overlay()) + // // None + // } + + pub(super) fn is_over( + &self, + layout: layout::Layout<'_>, + _renderer: &Renderer, + cursor_position: Point, + ) -> bool { + let mut lc = layout.children(); + let children_layout = lc.next().unwrap(); + children_layout.bounds().contains(cursor_position) + } +} + + +/// Adaptive open direction +#[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] +struct Aod { + // whether or not to use aod + horizontal: bool, + vertical: bool, + + // whether or not to use overlap + horizontal_overlap: bool, + vertical_overlap: bool, + + // default direction + horizontal_direction: Direction, + vertical_direction: Direction, + + // Offset of the child in the default direction + horizontal_offset: f32, + vertical_offset: f32, +} +impl Aod { + /// Returns child position and offset position + #[allow(clippy::too_many_arguments)] + fn adaptive( + parent_pos: f32, + parent_size: f32, + child_size: f32, + max_size: f32, + offset: f32, + on: bool, + overlap: bool, + direction: Direction, + ) -> (f32, f32) { + /* + Imagine there're two sticks, parent and child + parent: o-----o + child: o----------o + + Now we align the child to the parent in one dimension + There are 4 possibilities: + + 1. to the right + o-----oo----------o + + 2. to the right with overlaping + o-----o + o----------o + + 3. to the left + o----------oo-----o + + 4. to the left with overlaping + o-----o + o----------o + + The child goes to the default direction by default, + if the space on the default direction runs out it goes to the the other, + whether to use overlap is the caller's decision + + This can be applied to any direction + */ + + match direction { + Direction::Positive => { + let space_negative = parent_pos; + let space_positive = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - overshoot) + } else { + (parent_pos, parent_pos) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - offset) + } else { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } + } + } + Direction::Negative => { + let space_positive = parent_pos; + let space_negative = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos, parent_pos) + } else { + (parent_pos - overshoot, parent_pos - overshoot) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } else { + (parent_pos - overshoot, parent_pos - offset) + } + } + } + } + } + + /// Returns child position and offset position + fn resolve( + &self, + parent_bounds: Rectangle, + children_size: Size, + viewport_size: Size, + ) -> (Point, Point) { + let (x, ox) = Self::adaptive( + parent_bounds.x, + parent_bounds.width, + children_size.width, + viewport_size.width, + self.horizontal_offset, + self.horizontal, + self.horizontal_overlap, + self.horizontal_direction, + ); + let (y, oy) = Self::adaptive( + parent_bounds.y, + parent_bounds.height, + children_size.height, + viewport_size.height, + self.vertical_offset, + self.vertical, + self.vertical_overlap, + self.vertical_direction, + ); + + ([x, y].into(), [ox, oy].into()) + } +} + +fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { + Rectangle { + x: rect.x - padding.left, + y: rect.y - padding.top, + width: rect.width + padding.horizontal(), + height: rect.height + padding.vertical(), + } +} + + +/// A part of a menu where items are displayed. +/// +/// When the bounds of a menu exceed the viewport, +/// only items inside the viewport will be displayed, +/// when scrolling happens, this should be updated +#[derive(Debug, Clone, Copy)] +struct MenuSlice { + start_index: usize, + end_index: usize, + lower_bound_rel: f32, + upper_bound_rel: f32, +} + +fn slice( + children_bounds: Rectangle, // offset + unsrolled + child_positions: Vec, + child_sizes: Vec, + scroll_offset: f32, + viewport_size: Size, + // overlay_offset: Vector, +) -> MenuSlice { + // viewport space children bounds + // let children_bounds = children_bounds + overlay_offset; + + let max_index = child_positions.len().saturating_sub(1); + + // viewport space absolute bounds + let lower_bound = children_bounds.y.max(0.0); + let upper_bound = (children_bounds.y + children_bounds.height).min(viewport_size.height); + + // menu space relative bounds + let lower_bound_rel = lower_bound - (children_bounds.y + scroll_offset); + let upper_bound_rel = upper_bound - (children_bounds.y + scroll_offset); + + // index range + let positions = &child_positions; + let sizes = &child_sizes; + + let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); + let end_index = + search_bound(max_index, start_index, max_index, upper_bound_rel, positions, sizes) + .min(max_index); + + MenuSlice { + start_index, + end_index, + lower_bound_rel, + upper_bound_rel, + } +} + +fn search_bound( + default: usize, + default_left: usize, + default_right: usize, + bound: f32, + positions: &[f32], + sizes: &[Size], +) -> usize { + // binary search + let mut left = default_left; + let mut right = default_right; + + let mut index = default; + while left != right { + let m = ((left + right) / 2) + 1; + if positions[m] > bound { + right = m - 1; + } else { + left = m; + } + } + let height = sizes[left].height; + if positions[left] + height > bound { + index = left; + } + index +} + +fn process_scroll_event( + state: &mut MenuTreeState, + prescroll_children_bounds: Rectangle, + delta: mouse::ScrollDelta, + viewport_size: Size, +) -> event::Status{ + use mouse::ScrollDelta; + + let pcb = prescroll_children_bounds; + + let delta_y = match delta { + ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, + }; + + let max_offset = (0.0 - pcb.y).max(0.0); + let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); + state.scroll_offset = (state.scroll_offset + delta_y).clamp(min_offset, max_offset); + + event::Status::Captured +} diff --git a/src/native/menu/types.rs b/src/native/menu/types.rs index ea90ee89..d965f7a1 100644 --- a/src/native/menu/types.rs +++ b/src/native/menu/types.rs @@ -1,3 +1,7 @@ +use iced_widget::core::{overlay, renderer}; + +use super::menu_tree_overlay::MenuTreeOverlay; + /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] @@ -12,48 +16,48 @@ pub struct CloseCondition { pub click_inside: bool, } -/// The width of an item -#[derive(Debug, Clone, Copy)] -pub enum ItemWidth { - /// Use uniform width - Uniform(u16), - /// Static tries to use the width value of each menu(menu tree with children), - /// the widths of items(menu tree with empty children) will be the same as the menu they're in, - /// if that value is None, - /// the default value will be used instead, - /// which is the value of the Static variant - Static(u16), -} +// /// The width of an item +// #[derive(Debug, Clone, Copy)] +// pub enum ItemWidth { +// /// Use uniform width +// Uniform(u16), +// /// Static tries to use the width value of each menu(menu tree with children), +// /// the widths of items(menu tree with empty children) will be the same as the menu they're in, +// /// if that value is None, +// /// the default value will be used instead, +// /// which is the value of the Static variant +// Static(u16), +// } -/// The height of an item -#[derive(Debug, Clone, Copy)] -pub enum ItemHeight { - /// Use uniform height. - Uniform(u16), - /// Static tries to use `MenuTree.height` as item height, - /// when it's `None` it'll fallback to the value of the `Static` variant. - Static(u16), - /// Dynamic tries to automatically choose the proper item height for you, - /// but it only works in certain cases: - /// - /// - Fixed height - /// - Shrink height - /// - Menu tree height - /// - /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. - Dynamic(u16), -} +// /// The height of an item +// #[derive(Debug, Clone, Copy)] +// pub enum ItemHeight { +// /// Use uniform height. +// Uniform(u16), +// /// Static tries to use `MenuTree.height` as item height, +// /// when it's `None` it'll fallback to the value of the `Static` variant. +// Static(u16), +// /// Dynamic tries to automatically choose the proper item height for you, +// /// but it only works in certain cases: +// /// +// /// - Fixed height +// /// - Shrink height +// /// - Menu tree height +// /// +// /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. +// Dynamic(u16), +// } -/// Methods for drawing path highlight -#[derive(Debug, Clone, Copy)] -pub enum PathHighlight { - /// Draw the full path, - Full, - /// Omit the active item(the last item in the path) - OmitActive, - /// Omit the active item if it's not a menu - MenuActive, -} +// /// Methods for drawing path highlight +// #[derive(Debug, Clone, Copy)] +// pub enum PathHighlight { +// /// Draw the full path, +// Full, +// /// Omit the active item(the last item in the path) +// OmitActive, +// /// Omit the active item if it's not a menu +// MenuActive, +// } /// X+ goes right and Y+ goes down #[derive(Debug, Clone, Copy)] @@ -76,3 +80,30 @@ pub enum OpenCondition{ Click, } +pub(super) enum MenuOverlayElement<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + Overlay(overlay::Element<'b, Message, Theme, Renderer>), + MenuTree(MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) +} +impl<'a, 'b, Message, Theme, Renderer> + From> for + MenuOverlayElement<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn from(value: overlay::Element<'b, Message, Theme, Renderer>) -> Self { + Self::Overlay(value) + } +} +impl<'a, 'b, Message, Theme, Renderer> + From> for + MenuOverlayElement<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn from(value: MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) -> Self { + Self::MenuTree(value) + } +} From d4958783086d5ea961e723a9ee9954a4637142d9 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 3 Feb 2024 17:33:10 +0800 Subject: [PATCH 06/40] init --- examples/menu/src/main.rs | 91 +- src/lib.rs | 6 +- src/native/helpers.rs | 4 +- src/native/menu.rs | 11 +- src/native/menu/bk2/menu_bar.rs | 165 ++++ src/native/menu/bk2/menu_bar_overlay.rs | 188 ++++ src/native/menu/bk2/menu_tree.rs | 322 +++++++ .../menu/{ => bk2}/menu_tree_overlay.rs | 0 src/native/menu/bk2/types.rs | 109 +++ src/native/menu/bk3/menu_bar.rs | 239 +++++ src/native/menu/bk3/menu_bar_overlay.rs | 228 +++++ src/native/menu/bk3/menu_tree.rs | 804 ++++++++++++++++ src/native/menu/menu_bar.rs | 83 +- src/native/menu/menu_bar_overlay.rs | 147 +-- src/native/menu/menu_tree.rs | 872 ++++++++++++++---- src/native/menu/types.rs | 29 +- 16 files changed, 2812 insertions(+), 486 deletions(-) create mode 100644 src/native/menu/bk2/menu_bar.rs create mode 100644 src/native/menu/bk2/menu_bar_overlay.rs create mode 100644 src/native/menu/bk2/menu_tree.rs rename src/native/menu/{ => bk2}/menu_tree_overlay.rs (100%) create mode 100644 src/native/menu/bk2/types.rs create mode 100644 src/native/menu/bk3/menu_bar.rs create mode 100644 src/native/menu/bk3/menu_bar_overlay.rs create mode 100644 src/native/menu/bk3/menu_tree.rs diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 39fbe9b1..b3e9a4a8 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -5,16 +5,17 @@ use iced::widget::{ }; use iced::{alignment, theme, Application, Border, Color, Element, Event, Length, Pixels, Size}; -use iced_aw::menu::menu_bar::MenuBar; use iced_aw::menu::{ - menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight, - Menux, OpenCondition, Axis + // menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight, + // Menux, OpenCondition, Axis + Menu, Item, MenuBar, + }; use iced_aw::quad; -use iced_aw::{helpers::menu_tree, menu_bar, menu_tree}; +// use iced_aw::{helpers::menu_tree, menu_bar, menu_tree}; pub fn main() -> iced::Result { - // std::env::set_var("RUST_BACKTRACE", "full"); + std::env::set_var("RUST_BACKTRACE", "1"); App::run(iced::Settings { default_text_size: Pixels(15.0), window: iced::window::Settings{ @@ -215,13 +216,17 @@ impl Application for App { fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { // println!("app view"); - /* let pick_size_option = pick_list( - &SizeOption::ALL[..], - Some(self.size_option), - Message::SizeOption, - ); */ - let mb = row![ + let mb = MenuBar::new([ + Item::new(debug_button("aaa")) + .menu(Menu::new([ + Item::new(debug_button("abc").width(180.0)), + Item::new(debug_button("def").width(180.0)), + Item::new(debug_button("xxx").width(180.0)), + ].into())), + ].into()); + + /* let mb = row![ Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ debug_button("abc").width(180.0).into(), debug_button("def").width(180.0).into(), @@ -301,66 +306,7 @@ impl Application for App { debug_button("zhdkr").width(180.0).into(), debug_button("vjdiwo").width(180.0).into(), ]).open_condition(OpenCondition::Click), - ]; - - // let mb = MenuBar::new( - // [ - // MenuTree::with_children( - // button("content").on_press(Message::Debug("content".into())), - // [ - // MenuTree::new(button("abc")), - // MenuTree::new(button("def")), - // MenuTree::new(button("wagrarga")), - // MenuTree::new(button("jfuykyfuk")), - // ].into() - // ), - // MenuTree::with_children( - // button("xxx").on_press(Message::Debug("xxx".into())), - // [ - // MenuTree::new(button("abc")), - // MenuTree::new(button("def")), - // MenuTree::new(button("wagrarga")), - // MenuTree::new(button("jfuykyfuk")), - // ].into() - // ) - // ].into() - // ); - - /* let mb = match self.size_option { - SizeOption::Uniform => { - menu_bar!(menu_1(self), menu_2(self), menu_3(self), menu_4(self)) - .item_width(ItemWidth::Uniform(180)) - .item_height(ItemHeight::Uniform(27)) - } - SizeOption::Static => menu_bar!( - menu_1(self), - menu_2(self), - menu_3(self), - menu_4(self), - menu_5(self), - ) - .item_width(ItemWidth::Static(180)) - .item_height(ItemHeight::Static(35)), - SizeOption::DynamicHeight => menu_bar!( - menu_1(self), - menu_2(self), - menu_3(self), - menu_4(self), - menu_6(self), - ) - .item_width(ItemWidth::Static(180)) - .item_height(ItemHeight::Dynamic(35)), - } - .spacing(4.0) - .bounds_expand(30) - .main_offset(13) - .cross_offset(16) - .path_highlight(Some(PathHighlight::MenuActive)) - .close_condition(CloseCondition { - leave: true, - click_outside: false, - click_inside: false, - }); */ + ]; */ /* let r = if self.flip_h { row!( @@ -485,7 +431,7 @@ fn debug_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, ice labeled_button(label, Message::Debug(label.into())) } -fn debug_item<'a>(label: &str) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +/* fn debug_item<'a>(label: &str) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { menu_tree!(debug_button(label).width(Length::Fill).height(Length::Fill)) } @@ -992,3 +938,4 @@ fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { root } + */ \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ed450c6c..0c9cb7a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,9 +136,9 @@ mod platform { #[cfg(feature = "menu")] pub use { crate::native::menu, - crate::native::menu::{ - CloseCondition, ItemHeight, ItemWidth, MenuBar, MenuTree, PathHighlight, - }, + // crate::native::menu::{ + // CloseCondition, ItemHeight, ItemWidth, MenuBar, MenuTree, PathHighlight, + // }, }; #[doc(no_inline)] diff --git a/src/native/helpers.rs b/src/native/helpers.rs index a4e756cb..4fe02d4a 100644 --- a/src/native/helpers.rs +++ b/src/native/helpers.rs @@ -64,7 +64,7 @@ macro_rules! wrap_vertical { ); } -/// Creates a [`MenuTree`] with the given children. +/* /// Creates a [`MenuTree`] with the given children. /// /// [`MenuTree`]: crate::MenuTree #[cfg(feature = "menu")] @@ -120,7 +120,7 @@ where Renderer: core::Renderer, { crate::menu::menu_tree::MenuTree::with_children(item, children) -} +} */ #[cfg(feature = "badge")] /// Shortcut helper to create a [`Badge`] Widget. diff --git a/src/native/menu.rs b/src/native/menu.rs index bde892c6..621986a9 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -53,15 +53,16 @@ //! mod flex; -pub mod menu_bar; +mod menu_bar; mod menu_bar_overlay; -// mod menu_inner; -pub mod menu_tree; -// pub mod menux; +mod menu_tree; mod types; -mod menu_tree_overlay; pub use crate::style::menu_bar::{Appearance, StyleSheet}; +pub use menu_tree::{Item, Menu}; +pub use menu_bar::MenuBar; +pub use types::*; + // A `MenuBar` collects `MenuTree`s and handles // pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Renderer>; // pub use types::*; diff --git a/src/native/menu/bk2/menu_bar.rs b/src/native/menu/bk2/menu_bar.rs new file mode 100644 index 00000000..6399d916 --- /dev/null +++ b/src/native/menu/bk2/menu_bar.rs @@ -0,0 +1,165 @@ +use iced_widget::core::{ + alignment, event, layout, mouse, overlay, renderer, touch, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget +}; + +use super::{ + flex, menu_bar_overlay::MenuBarOverlay, menu_tree::* +}; + +pub(super) struct MenuBarState{ + pub(super) active_root: usize, + pub(super) open: bool, +} +impl Default for MenuBarState{ + fn default() -> Self { + Self { + active_root: 0, + open: false, + } + } +} + +pub struct MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + roots: Vec>, + spacing: f32, + padding: Padding, + width: Length, + height: Length, +} +impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub fn new(roots: Vec>) -> Self { + Self { + roots, + spacing: 0.0, + padding: Padding::ZERO, + width: Length::Shrink, + height: Length::Shrink, + } + } + +} +impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + + fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::Some(Box::new(MenuBarState::default())) + } + + fn children(&self) -> Vec { + self.roots.iter().map(|mt| mt.tree()).collect::>() + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children_custom( + &self.roots, + |tree, mt| diff(tree, mt), + |mt| mt.tree() + ) + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + flex::resolve( + flex::Axis::Horizontal, + renderer, + limits, + self.width, + self.height, + self.padding, + self.spacing, + alignment::Alignment::Center, + &self.roots.iter().map(|mt| &mt.parent).collect::>(), + &mut tree.children.iter().map(|tree| &mut tree.children[0]).collect::>(), + ) + } + + fn on_event( + &mut self, + state: &mut Tree, + event: event::Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + + event::Status::Ignored + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + if let Some(viewport) = layout.bounds().intersection(viewport) { + for ((root, state), layout) in self + .roots + .iter() + .zip(&tree.children) + .zip(layout.children()) + { + root.parent.as_widget().draw( + state, renderer, theme, style, layout, cursor, &viewport, + ); + } + } + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + let state = tree.state.downcast_mut::(); + let parent_bounds = layout.children().nth(state.active_root).unwrap().bounds(); + + if state.open{ + Some(MenuBarOverlay{ + tree, + active_tree: &mut tree.children[state.active_root], + active_root: &mut self.roots[state.active_root], + parent_bounds, + }.overlay_element()) + }else { + None + } + } +} +impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Renderer: 'a + renderer::Renderer, + // Renderer::Theme: StyleSheet, +{ + fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { + Self::new(value) + } +} diff --git a/src/native/menu/bk2/menu_bar_overlay.rs b/src/native/menu/bk2/menu_bar_overlay.rs new file mode 100644 index 00000000..8d679677 --- /dev/null +++ b/src/native/menu/bk2/menu_bar_overlay.rs @@ -0,0 +1,188 @@ +use iced_widget::core::{ + event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget +}; +use crate::menu::menu_tree_overlay::MenuTreeOverlay; + +use super::{ + menu_tree::{MenuTree, MenuTreeState}, + menu_bar::MenuBarState, +}; + +pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) tree: &'b mut Tree, + pub(super) active_tree: &'b mut Tree, + pub(super) active_root: &'b mut MenuTree<'a, Message, Theme, Renderer>, + pub(super) parent_bounds: Rectangle, + +} +impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) fn overlay_element(self) -> overlay::Element<'a, Message, Theme, Renderer>{ + overlay::Element::new(Point::ORIGIN, Box::new(self)) + } +} +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> Node { + /* + Node{ + rect: + children: [ + Node{} // mto layout + Node{} // overlays + Node{} // next mto + ] + } + */ + + fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( + tree: &mut Tree, + menu_tree: &MenuTree<'a, Message, Theme, Renderer>, + parent_bounds: Rectangle, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> Node { + let size = Size::INFINITY; + let state = tree.state.downcast_mut::(); + + let mut mto = MenuTreeOverlay{ + state, + tree, + menu_tree, + parent_bounds, + }; + + let mto_layout = mto.layout(renderer, bounds, position, translation); + + let overlays = menu_tree.children + .iter() + .zip(tree.children[1].children.iter_mut()) + .zip(mto_layout.children()[0].children()) + .filter_map(|((mt, t), n)|{ + mt.parent.as_widget_mut() + .overlay(t, Layout::new(n), renderer) + .and_then(|mut o| Some(o.layout(renderer, bounds, translation))) + }) + .collect::>(); + + let next = menu_tree.children + .iter() + .zip(tree.children[1].children.iter_mut()) + .zip(mto_layout.children()[0].children()) + .find_map(|((mt, t), l)|{ + let state = t.state.downcast_mut::(); + if state.open{ + let parent_bounds = l.bounds(); + // let position = parent_bounds.position(); + let position = Point::ORIGIN; + Some(rec(t, mt, parent_bounds, renderer, bounds, position, position - Point::ORIGIN)) + }else{ + None + } + }); + + Node::with_children( + size, + next.map_or( + [ + mto_layout, + Node::with_children(size, overlays), + ].into(), + |n| [ + mto_layout, + Node::with_children(size, overlays), + n + ].into() + ) + ) + } + + rec( + self.active_tree, + self.active_root, + self.parent_bounds, + renderer, + bounds, + position, + translation + ) + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + todo!() + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( + tree: &mut Tree, + menu_tree: &MenuTree<'a, Message, Theme, Renderer>, + parent_bounds: Rectangle, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ){ + let mut lc = layout.children(); + let mto_layout = lc.next().unwrap(); + let overlay_layout = lc.next().unwrap(); + + let state = tree.state.downcast_mut::(); + let mut mto = MenuTreeOverlay{ + state, + tree, + menu_tree, + parent_bounds, + }; + + mto.draw(renderer, theme, style, mto_layout, cursor); + + menu_tree.children + .iter() + .zip(tree.children[1].children.iter_mut()) + .filter_map(|(mt, t)|{ + mt.parent.as_widget_mut().overlay(t, Layout::new(&mt.layout(t, renderer, &Limits::NONE)), renderer) + }) + .zip(overlay_layout.children()) + .for_each(|(o, l)|{ + o.draw(renderer, theme, style, l, cursor) + }); + + if let Some(next_layout) = lc.next(){ + rec(tree, menu_tree, parent_bounds, renderer, theme, style, next_layout, cursor, viewport); + } + + } + } +} \ No newline at end of file diff --git a/src/native/menu/bk2/menu_tree.rs b/src/native/menu/bk2/menu_tree.rs new file mode 100644 index 00000000..501d157e --- /dev/null +++ b/src/native/menu/bk2/menu_tree.rs @@ -0,0 +1,322 @@ +//! doc +//! +use iced_widget::core::{ + event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, + Alignment, Border, Clipboard, Color, Element, Event, Length, + Padding, Point, Vector, Rectangle, Shell, Size, Widget, +}; +use super::types::*; +use super::menu_tree_overlay::MenuTreeOverlay; + +pub(super) struct MenuTreeState{ + pub(super) open: bool, + pub(super) scroll_offset: f32, +} +impl Default for MenuTreeState{ + fn default() -> Self { + Self { open: false, scroll_offset: 0.0 } + } +} + +/// doc +#[allow(clippy::missing_docs_in_private_parents)] +pub struct MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) parent: Element<'a, Message, Theme, Renderer>, + pub(super) children: Vec>, + pub(super) spacing: f32, + pub(super) padding: Padding, + pub(super) max_width: f32, + pub(super) width: Length, + pub(super) height: Length, + pub(super) axis: Axis, + pub(super) offset: f32, + pub(super) open_condition: OpenCondition, +} +impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + #[allow(missing_docs)] + pub fn new( + parent: impl Into> + ) -> Self{ + Self{ + parent: parent.into(), + children: Vec::new(), + spacing: 4.0, + padding: [0.0;4].into(), + max_width: f32::MAX, + width: Length::Shrink, + height: Length::Shrink, + axis: Axis::Vertical, + offset: 0.0, + open_condition: OpenCondition::Hover, + } + } + + #[allow(missing_docs)] + pub fn with_children( + parent: impl Into>, + children: Vec>> + ) -> Self{ + Self{ + parent: parent.into(), + children: children.into_iter().map(Into::into).collect(), + spacing: 4.0, + padding: [0.0;4].into(), + max_width: f32::MAX, + width: Length::Shrink, + height: Length::Shrink, + axis: Axis::Vertical, + offset: 0.0, + open_condition: OpenCondition::Hover, + } + } + + pub fn tree(&self) -> Tree{ + Tree{ + tag: self.tag(), + state: self.state(), + children: self.children() + } + } + + /// Sets the vertical spacing _between_ elements. + /// + /// Custom margins per element do not exist in iced. You should use this + /// method instead! While less flexible, it helps you keep spacing between + /// elements consistent. + pub fn spacing(mut self, spacing: f32) -> Self { + self.spacing = spacing; + self + } + + /// Sets the [`Padding`] of the [`MenuTree`]. + pub fn padding>(mut self, padding: P) -> Self { + self.padding = padding.into(); + self + } + + /// Sets the width of the [`MenuTree`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`MenuTree`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } + + /// Sets the axis of the [`MenuTree`]. + pub fn axis(mut self, axis: Axis) -> Self { + self.axis = axis.into(); + self + } + + /// Sets the open condition of the [`MenuTree`]. + pub fn open_condition(mut self, open_condition: OpenCondition) -> Self { + self.open_condition = open_condition; + self + } +} +impl <'a, Message, Theme, Renderer> + // Widget for + MenuTree<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + pub(super) fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + pub(super) fn state(&self) -> tree::State { + tree::State::new(MenuTreeState::default()) + } + + pub(super) fn children(&self) -> Vec { + [ + Tree::new(&self.parent), + Tree{ + children: self.children.iter().map(|mt| mt.tree()).collect::>(), + ..Tree::empty() + } + ].into() + } + + /* + Tree{ + tag: MTS + state: MTS + children: [ + Tree{parent}, + Tree{tag:none, state:none, children:[ + Tree{tag:MTS, state:MTS, children:[..]}, + Tree{tag:MTS, state:MTS, children:[..]}, + Tree{tag:MTS, state:MTS, children:[..]}, + ... + ]} + ] + } + */ + pub(super) fn diff(&self, tree: &mut Tree) { + let parent = &mut tree.children[0]; + let children = &mut tree.children[1]; + + parent.diff(&self.parent); + children.diff_children_custom( + &self.children, + |tree, mt| diff(tree, mt), + |mt| mt.tree() + ) + } + + pub(super) fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + // println!("mt layout"); + self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) + } + + pub(super) fn on_event( + &mut self, + tree: &mut Tree, + event: event::Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + // println!("mt event"); + let status = self.parent.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); + + let state = tree.state.downcast_mut::(); + let bounds = layout.bounds(); + + use event::Status::*; + match self.open_condition{ + OpenCondition::Click => match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if cursor.is_over(bounds) { + state.open = true; + state.scroll_offset = 0.0; + Captured + }else{ + Ignored + } + } + _ => Ignored + } + OpenCondition::Hover => match event { + Event::Mouse(mouse::Event::CursorMoved { position }) => { + if bounds.contains(position) { + state.open = true; + state.scroll_offset = 0.0; + Captured + }else{ + Ignored + } + } + _ => Ignored + } + } + .merge(status) + } + + pub(super) fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + // println!("mt draw"); + self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) + } + + // pub(super) fn overlay<'b>( + // &'b mut self, + // extra_input:f32, + // tree: &'b mut Tree, + // layout: layout::Layout<'_>, + // renderer: &Renderer, + // ) -> Option> { + // // ) -> Option> { + // // ) -> Option> { + // // println!("mt overlay"); + // let mut og = overlay::Group::new(); + // let Tree { tag, state, children } = tree; + + // // let [parent_tree, children_tree] = children.as_mut_slice() else { panic!("Tree Error") }; + // let parent_tree = &mut children[0]; + // let children_tree = &mut children[1]; + + // if let Some(c) = self.parent + // .as_widget_mut() + // .overlay(parent_tree, layout, renderer) + // { + // og = og.push(c); + // } + + // let ms = state.downcast_mut::(); + // if !ms.open { + // Some(og.overlay().into()) + // }else{ + // // Some(og.push( + // // MenuTreeOverlay{ + // // state: ms, + // // tree: children_tree, + // // children: &mut self.children, + // // parent_bounds: layout.bounds(), + // // max_width: 1000.0, + // // spacing: self.spacing, + // // padding: self.padding, + // // width: self.width, + // // height: self.height, + // // axis: self.axis, + // // offset: self.offset, + // // }.overlay() + // // ).overlay()) + // None + // } + // } +} + +pub fn diff<'a, Message, Theme, Renderer>( + tree: &mut Tree, + new: &MenuTree<'a, Message, Theme, Renderer> +) where + Renderer: renderer::Renderer, +{ + if tree.tag == new.tag() { + new.diff(tree); + } else { + *tree = new.tree() + } +} \ No newline at end of file diff --git a/src/native/menu/menu_tree_overlay.rs b/src/native/menu/bk2/menu_tree_overlay.rs similarity index 100% rename from src/native/menu/menu_tree_overlay.rs rename to src/native/menu/bk2/menu_tree_overlay.rs diff --git a/src/native/menu/bk2/types.rs b/src/native/menu/bk2/types.rs new file mode 100644 index 00000000..d965f7a1 --- /dev/null +++ b/src/native/menu/bk2/types.rs @@ -0,0 +1,109 @@ +use iced_widget::core::{overlay, renderer}; + +use super::menu_tree_overlay::MenuTreeOverlay; + + +/// The condition of when to close a menu +#[derive(Debug, Clone, Copy)] +pub struct CloseCondition { + /// Close menus when the cursor moves outside the check bounds + pub leave: bool, + + /// Close menus when the cursor clicks outside the check bounds + pub click_outside: bool, + + /// Close menus when the cursor clicks inside the check bounds + pub click_inside: bool, +} + +// /// The width of an item +// #[derive(Debug, Clone, Copy)] +// pub enum ItemWidth { +// /// Use uniform width +// Uniform(u16), +// /// Static tries to use the width value of each menu(menu tree with children), +// /// the widths of items(menu tree with empty children) will be the same as the menu they're in, +// /// if that value is None, +// /// the default value will be used instead, +// /// which is the value of the Static variant +// Static(u16), +// } + +// /// The height of an item +// #[derive(Debug, Clone, Copy)] +// pub enum ItemHeight { +// /// Use uniform height. +// Uniform(u16), +// /// Static tries to use `MenuTree.height` as item height, +// /// when it's `None` it'll fallback to the value of the `Static` variant. +// Static(u16), +// /// Dynamic tries to automatically choose the proper item height for you, +// /// but it only works in certain cases: +// /// +// /// - Fixed height +// /// - Shrink height +// /// - Menu tree height +// /// +// /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. +// Dynamic(u16), +// } + +// /// Methods for drawing path highlight +// #[derive(Debug, Clone, Copy)] +// pub enum PathHighlight { +// /// Draw the full path, +// Full, +// /// Omit the active item(the last item in the path) +// OmitActive, +// /// Omit the active item if it's not a menu +// MenuActive, +// } + +/// X+ goes right and Y+ goes down +#[derive(Debug, Clone, Copy)] +pub(super) enum Direction { + Positive, + Negative, +} + +/// Axis +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy)] +pub enum Axis{ + Horizontal, + Vertical, +} + +#[allow(missing_docs)] +pub enum OpenCondition{ + Hover, + Click, +} + +pub(super) enum MenuOverlayElement<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + Overlay(overlay::Element<'b, Message, Theme, Renderer>), + MenuTree(MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) +} +impl<'a, 'b, Message, Theme, Renderer> + From> for + MenuOverlayElement<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn from(value: overlay::Element<'b, Message, Theme, Renderer>) -> Self { + Self::Overlay(value) + } +} +impl<'a, 'b, Message, Theme, Renderer> + From> for + MenuOverlayElement<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn from(value: MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) -> Self { + Self::MenuTree(value) + } +} diff --git a/src/native/menu/bk3/menu_bar.rs b/src/native/menu/bk3/menu_bar.rs new file mode 100644 index 00000000..79557b78 --- /dev/null +++ b/src/native/menu/bk3/menu_bar.rs @@ -0,0 +1,239 @@ +//! menu bar + +use iced_widget::core::{ + alignment, event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::{tree, Tree}, + Event, + Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget +}; + +use super::{ + flex, menu_bar_overlay::MenuBarOverlay, menu_tree::* +}; + +pub(super) struct MenuBarState{ + pub(super) active_root: usize, + pub(super) open: bool, + pub(super) viewport: Rectangle, + pub(super) indices: Vec, +} +impl Default for MenuBarState{ + fn default() -> Self { + Self { + active_root: 0, + open: false, + viewport: Rectangle::default(), + indices: Vec::new(), + } + } +} + +/// menu bar +pub struct MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + roots: Vec>, + spacing: f32, + padding: Padding, + width: Length, + height: Length, +} +#[allow(missing_docs)] +impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub fn new(roots: Vec>) -> Self { + Self { + roots, + spacing: 0.0, + padding: Padding::ZERO, + width: Length::Shrink, + height: Length::Shrink, + } + } + +} +impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + + fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::Some(Box::new(MenuBarState::default())) + } + + /// \[Tree{item_state, \[widget_state, menu_state]}...] + fn children(&self) -> Vec { + println!("bar children"); + self.roots.iter().map(|item| item.tree()).collect::>() + } + + /// tree: Tree{bar_state, \[item_tree...]} + fn diff(&self, tree: &mut Tree) { + println!("bar diff"); + tree.diff_children_custom( + &self.roots, + |tree, item| item.diff(tree), + |item| item.tree() + ) + } + + /// tree: Tree{bar_state, \[item_tree...]} + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + println!("bar layout"); + flex::resolve( + flex::Axis::Horizontal, + renderer, + limits, + self.width, + self.height, + self.padding, + self.spacing, + alignment::Alignment::Center, + &self.roots.iter().map(|item| &item.item ).collect::>(), + &mut tree.children.iter_mut().map(|tree| &mut tree.children[0]).collect::>() + ) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: event::Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + println!("bar event"); + use event::Status::*; + + let status = self.roots // [Item...] + .iter_mut() + .zip(tree.children.iter_mut()) // [item_tree...] + .zip(layout.children()) // [widget_layout...] + .map(|((item, tree), layout)|{ + item.item.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport + ) + }) + .fold(event::Status::Ignored, |acc, x| acc.merge(x) ); + + // make sure there's only one item that is open + let mut use_open = false; + for (i, t) in tree.children.iter_mut().enumerate(){ + let item_state = t.state.downcast_mut::(); + if use_open == true{ + item_state.open = false; + }else if item_state.open{ + use_open = true; + let bar = tree.state.downcast_mut::(); + // store the active item index + bar.active_root = i; + } + } + + status + + // let bar_bounds = layout.bounds(); + + // match event { + // Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + // | Event::Touch(touch::Event::FingerPressed { .. }) => { + + // Ignored + // } + // Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + // | Event::Touch(touch::Event::FingerLifted { .. }) + // | Event::Touch(touch::Event::FingerLost { .. }) => { + + // Ignored + // } + // Event::Mouse(mouse::Event::CursorMoved { position:_ }) => { + // Ignored + // } + // _ => Ignored + // }.merge(status) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + println!("bar draw"); + if let Some(viewport) = layout.bounds().intersection(viewport) { + for ((root, tree), layout) in self + .roots + .iter() + .zip(&tree.children) + .zip(layout.children()) + { + root.item.as_widget().draw( + &tree.children[0], renderer, theme, style, layout, cursor, &viewport, + ); + } + } + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + println!("bar overlay"); + let state = tree.state.downcast_mut::(); + let bar_bounds = layout.bounds(); + let parent_bounds = layout.children().nth(state.active_root).unwrap().bounds(); + + if state.open{ + Some(MenuBarOverlay{ + tree, + roots: &mut self.roots, + parent_bounds, + bar_bounds, + }.overlay_element()) + }else { + None + } + } +} +impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: 'a + renderer::Renderer, + // Renderer::Theme: StyleSheet, +{ + fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { + Self::new(value) + } +} diff --git a/src/native/menu/bk3/menu_bar_overlay.rs b/src/native/menu/bk3/menu_bar_overlay.rs new file mode 100644 index 00000000..61563964 --- /dev/null +++ b/src/native/menu/bk3/menu_bar_overlay.rs @@ -0,0 +1,228 @@ +use iced_widget::core::{ + event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget +}; + +use super::{ + menu_tree::*, + menu_bar::MenuBarState, +}; + +pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + /// Tree{ bar_state, [item_tree...] } + pub(super) tree: &'b mut Tree, + /// Tree{ item_state, [widget_tree, menu_tree] } + // pub(super) bar: &'b mut MenuBarState, + // pub(super) active_tree: &'b mut Tree, + // pub(super) active_root: &'b mut Item<'a, Message, Theme, Renderer>, + pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], + pub(super) parent_bounds: Rectangle, + pub(super) bar_bounds: Rectangle, +} +impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer>{ + overlay::Element::new(Point::ORIGIN, Box::new(self)) + } +} +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + /// out: Node{size:0, \[widget_node, menu_node]} + fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> Node { + println!("mbo layout"); + let viewport = Rectangle::new(Point::ORIGIN, bounds); + let limits = Limits::NONE; + + let bar = self.tree.state.downcast_mut::(); + let active_root = &self.roots[bar.active_root]; + let active_tree = &mut self.tree.children[bar.active_root]; + + active_root.layout(active_tree, renderer, &limits, &viewport, + // translation + ) + } + + /// layout: Node{size:0, \[widget_node, menu_node]} + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + println!("mbo event"); + fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( + bar: &mut MenuBarState, + current_item: &mut Item<'a, Message, Theme, Renderer>, + current_tree: &mut Tree, + current_layout: Layout<'_>, + event: Event, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + cursor: mouse::Cursor, + parent_bounds: Rectangle, + items_bounds_list: &mut Vec, + ) -> event::Status{ + let menu = current_item.menu.as_mut().expect("No menu defined in this item"); + let mut lc = current_layout.children(); + let _ = lc.next().unwrap(); // widget_node + let menu_layout = lc.next().unwrap(); // menu_node: Node{size:inf, [items_layout, prescroll, offset_bounds, check_bounds]} + let items_layout = menu_layout.children().next().unwrap(); // items_layout: Node{size:items_bounds, [item_layout...]} + + let menu_tree = &mut current_tree.children[1]; // menu_tree: Tree{ menu_state, [item_tree...] } + + let item_state = current_tree.state.downcast_mut::(); + let viewport = bar.viewport; + + // on_event + let status = menu.on_event( + item_state, menu_tree, event.clone(), menu_layout, cursor, renderer, clipboard, shell, &viewport, &parent_bounds, &items_bounds_list + ); + + // push items_bounds + items_bounds_list.push(items_layout.bounds()); + + if let Some((next_item, next_tree, next_layout)) = menu.items // [item...] + .iter_mut() + .zip(menu_tree.children.iter_mut()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + .find_map(|((item, tree), layout)|{ + let item_state = tree.state.downcast_ref::(); + item_state.open.then(|| (item, tree, layout)) + }) + { + rec(bar, next_item, next_tree, next_layout, event, renderer, clipboard, shell, cursor, parent_bounds, items_bounds_list) + .merge(status) + }else{ + status + } + } + + let bar = self.tree.state.downcast_mut::(); + let active_root = &mut self.roots[bar.active_root]; + let active_tree = &mut self.tree.children[bar.active_root]; + let mut items_bounds_list = vec![self.bar_bounds]; + + rec( + bar, + active_root, + active_tree, + layout, + event, + renderer, + clipboard, + shell, + cursor, + self.parent_bounds, + &mut items_bounds_list, + ) + } + + /// layout: Node{size:0, \[widget_node, menu_node]} + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + /* let bar = self.tree.state.downcast_ref::(); + let mut current_item = &self.active_root; + let mut current_tree = &self.active_tree; // item_tree: Tree{item_state, [widget_tree, menu_tree]} + let mut current_layout = layout; // item_layout: Node{size:0, [widget_node, menu_node]} + + loop { + let cm = current_item.menu.as_mut(); + let menu = cm.expect("No menu defined in this item"); + let mut lc = current_layout.children(); + let widget_layout = lc.next().unwrap(); + let menu_layout = lc.next().unwrap(); // menu_node: Node{size:inf, [items_layout, prescroll, offset_bounds, check_bounds]} + let items_layout = menu_layout.children().next().unwrap(); // items_layout: Node{size:items_bounds, [item_layout...]} + + // let widget_tree = &mut current_tree.children[0]; + let menu_tree = &mut current_tree.children[1]; // menu_tree: Tree{ menu_state, [item_tree...] } + + menu.draw(&menu_tree, renderer, theme, style, menu_layout, cursor, &bar.viewport); + + if let Some((next_item, next_tree, next_layout)) = menu.items // [item...] + .iter_mut() + .zip(menu_tree.children.iter_mut()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + .find_map(|((item, tree), layout)|{ + let item_state = tree.state.downcast_ref::(); + item_state.open.then(|| (item, tree, layout)) + }) + { + current_item = next_item; + current_tree = next_tree; + current_layout = next_layout; + }else{ + break; + } + } */ + + fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( + bar: &MenuBarState, + current_item: &Item<'a, Message, Theme, Renderer>, + current_tree: &Tree, + current_layout: &Layout<'_>, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + cursor: mouse::Cursor, + ){ + let menu = current_item.menu.as_ref().expect("No menu defined in this item"); + let mut lc = current_layout.children(); + let _ = lc.next().unwrap(); // widget_node + let menu_layout = lc.next().unwrap(); // menu_node: Node{size:inf, [items_layout, prescroll, offset_bounds, check_bounds]} + let items_layout = menu_layout.children().next().unwrap(); // items_layout: Node{size:items_bounds, [item_layout...]} + + let menu_tree = ¤t_tree.children[1]; // menu_tree: Tree{ menu_state, [item_tree...] } + let viewport = bar.viewport; + + menu.draw(&menu_tree, renderer, theme, style, menu_layout, cursor, &viewport); + + if let Some((next_item, next_tree, next_layout)) = menu.items // [item...] + .iter() + .zip(menu_tree.children.iter()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + .find_map(|((item, tree), layout)|{ + let item_state = tree.state.downcast_ref::(); + item_state.open.then(|| (item, tree, layout)) + }) + { + rec(bar, next_item, &next_tree, &next_layout, renderer, theme, style, cursor) + } + } + + let bar = self.tree.state.downcast_ref::(); + let active_root = &self.roots[bar.active_root]; + let active_tree = &self.tree.children[bar.active_root]; + rec( + bar, + active_root, + active_tree, + &layout, + renderer, + theme, + style, + cursor + ) + } +} \ No newline at end of file diff --git a/src/native/menu/bk3/menu_tree.rs b/src/native/menu/bk3/menu_tree.rs new file mode 100644 index 00000000..31cdbb47 --- /dev/null +++ b/src/native/menu/bk3/menu_tree.rs @@ -0,0 +1,804 @@ +//! doc +//! +use std::borrow::BorrowMut; + +use iced_widget::core::{ + alignment, event, layout::{self, Limits, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget +}; +use super::types::*; +use super::{flex, menu_bar::MenuBarState}; + +/* + +menu tree: +Item{ + widget + Menu [ + Item{...} + Item{...} + Item{...} + ... + ] +} + +state tree: +Tree{ + item state + [ + Tree{widget state} + Tree{ + menu state + [ + Tree{item state [...]} + Tree{item state [...]} + Tree{item state [...]} + ... + ] + } + ] +} + +layout tree +Node{ + item node + [ + Node{widget node} + Node{ + menu node + [ + Node{item node [...]} + Node{item node [...]} + Node{item node [...]} + ... + ] + } + ] +} + +*/ + +#[derive(Debug, Default)] +pub(super) struct MenuState{ + scroll_offset:f32, +} + +/// menu +pub struct Menu<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub(super) items: Vec>, + pub(super) spacing: f32, + pub(super) padding: Padding, + pub(super) max_width: f32, + pub(super) width: Length, + pub(super) height: Length, + pub(super) axis: Axis, + pub(super) offset: f32, + pub(super) open_condition: OpenCondition, +} +#[allow(missing_docs)] +impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub fn new(items: Vec>) -> Self{ + Self{ + items, + spacing: 0.0, + padding: Padding::ZERO, + max_width: f32::MAX, + width: Length::Shrink, + height: Length::Shrink, + axis: Axis::Vertical, + offset: 0.0, + open_condition: OpenCondition::Click, + } + } + + pub fn tree(&self) -> Tree{ + Tree{ + tag: self.tag(), + state: self.state(), + children: self.children(), + } + } +} +impl<'a, Message, Theme, Renderer> + // Widget for + Menu<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub(super) fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + pub(super) fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + pub(super) fn state(&self) -> tree::State { + tree::State::Some(Box::new(MenuState::default())) + } + + /// out: \[item_tree...] + pub(super) fn children(&self) -> Vec { + self.items.iter().map(|i| i.tree() ).collect() + } + + /// tree: Tree{menu_state, \[item_tree...]} + pub(super) fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.items.iter().map(|i| &i.item ).collect::>()); + } + + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// out: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + pub(super) fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + parent_bounds: Rectangle, + // translation: Vector, + viewport: &Rectangle, + ) -> layout::Node { + let limits = limits.max_width(self.max_width); + + let items_node = flex::resolve( + flex::Axis::Vertical, + renderer, + &limits, + self.width, + self.height, + self.padding, + self.spacing, + alignment::Alignment::Center, + &self.items.iter().map(|i| &i.item ).collect::>(), + &mut tree.children.iter_mut().map(|t| &mut t.children[0] ).collect::>(), + ); + + let items_node = Node::with_children( + items_node.bounds().size(), + self.items + .iter() + .zip(tree.children.iter_mut()) + .zip(items_node.children().into_iter()) + .map(|((item, tree), node)|{ + Node::with_children( + Size::ZERO, + [ + node.clone(), + item.layout(tree, renderer, &Limits::NONE, viewport, + // translation + ) + ].into() + ) + }) + .collect() + ); + + + // for ((item, tree), node) in self.items + // .iter() + // .zip(tree.children.iter_mut()) + // .zip(items_node.children().iter_mut()) + // { + // *node = Node::with_children( + // Size::ZERO, + // [ + // node.clone(), + // item.layout(tree, renderer, &Limits::NONE, viewport, + // // translation + // ) + // .children()[1] + // ].into() + // ) + // } + + let bounds = viewport.size(); + // let vpb = parent_bounds + translation; // viewport space parent bounds + let vpb = parent_bounds; + let aod = { + let hcenter = bounds.width / 2.0; + let vcenter = bounds.height / 2.0; + + let phcenter = vpb.x + vpb.width / 2.0; + let pvcenter = vpb.y + vpb.height / 2.0; + + let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; + let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; + + match self.axis { + Axis::Horizontal => Aod{ + horizontal: false, + vertical: true, + horizontal_overlap: false, + vertical_overlap: true, + horizontal_direction, + vertical_direction, + horizontal_offset: self.offset, + vertical_offset: 0.0, + }, + Axis::Vertical => Aod{ + horizontal: true, + vertical: false, + horizontal_overlap: true, + vertical_overlap: false, + horizontal_direction, + vertical_direction, + horizontal_offset: 0.0, + vertical_offset: self.offset, + } + } + }; + + let children_size = items_node.bounds().size(); + let (children_position, offset_position) = aod.resolve( + vpb, + children_size, + bounds + ); + + // calc offset bounds + let delta = children_position - offset_position; + let offset_size = if delta.x.abs() > delta.y.abs() { + Size::new(delta.x, children_size.height) + } else { + Size::new(children_size.width, delta.y) + }; + let offset_bounds = Rectangle::new(offset_position, offset_size); + let children_bounds = Rectangle::new(children_position, children_size); + let bounds_expand = 30.0; + let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); + + let menu_state = tree.state.downcast_ref::(); + + layout::Node::with_children(Size::INFINITY, [ + items_node + .move_to(children_position) + .translate([0.0, menu_state.scroll_offset]), // items layout + layout::Node::new(children_size) + .move_to(children_position), // prescroll menu bounds + + layout::Node::new(offset_bounds.size()) + .move_to(offset_bounds.position()) + .translate([0.0, menu_state.scroll_offset]), // offset bounds + layout::Node::new(check_bounds.size()) + .move_to(check_bounds.position()) + .translate([0.0, menu_state.scroll_offset]), // check bounds + ].into()) + } + + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + pub(super) fn on_event( + &mut self, + // bar: &mut MenuBarState, + item_state: &mut ItemState, + tree: &mut Tree, + event: Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + parent_bounds: &Rectangle, + items_bounds_list: &[Rectangle], + ) -> event::Status { + let mut lc = layout.children(); + let items_layout = lc.next().unwrap(); + + let prescroll = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + let status = self.items // [item...] + .iter_mut() + .zip(tree.children.iter_mut()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + .map(|((item, tree), layout)| { + item.on_event( + tree, + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) + }) + .fold(event::Status::Ignored, event::Status::merge); + + let menu_state = tree.state.downcast_mut::(); + + use event::Status::*; + match event { + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + if cursor.is_over(items_layout.bounds()){ + process_scroll_event(menu_state, prescroll, delta, viewport.size()) + }else{ + Ignored + } + } + Event::Mouse(mouse::Event::CursorMoved { position }) => { + let open = { + if prescroll.contains(position){ + true + }else if items_bounds_list + .iter() + .any(|r| r.contains(position)) + { + false + }else if parent_bounds.contains(position) + || offset_bounds.contains(position) + || check_bounds.contains(position) + { + true + }else{ + false + } + }; + + if item_state.open == true && open == false { + item_state.open = false; + // menu_state.scroll_offset = 0.0; + } + Captured + } + _ => Ignored + }.merge(status) + } + + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + pub(super) fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + let mut lc = layout.children(); + let items_layout = lc.next().unwrap(); + + for ((item, tree), layout) in self.items // [item...] + .iter() + .zip(tree.children.iter()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + { + item.draw( + tree, renderer, theme, style, layout, cursor, &viewport, + ); + } + } +} + + +#[derive(Debug, Default)] +pub(super) struct ItemState{ + pub(super) open: bool +} + +/// menu item +pub struct Item<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub(super) item: Element<'a, Message, Theme, Renderer>, + pub(super) menu: Option>>, +} +#[allow(missing_docs)] +impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub fn new(item: impl Into>) -> Self{ + Self{ + item: item.into(), + menu: None, + } + } + + pub fn with_menu(item: impl Into>, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ + Self{ + item: item.into(), + menu: Some(Box::new(menu)), + } + } + + pub fn menu(mut self, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ + self.menu = Some(Box::new(menu)); + self + } + + pub(super) fn tree(&self) -> Tree{ + Tree{ + tag: self.tag(), + state: self.state(), + children: self.children(), + } + } +} +impl<'a, Message, Theme, Renderer> + // Widget for + Item<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub(super) fn size(&self) -> Size { + self.item.as_widget().size() + } + + pub(super) fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + pub(super) fn state(&self) -> tree::State { + tree::State::Some(Box::new(ItemState::default())) + } + + /// out: \[widget_tree, menu_tree] + pub(super) fn children(&self) -> Vec { + self.menu.as_ref().map_or( + [ + Tree::new(&self.item), + ].into(), + |m|[ + Tree::new(&self.item), + m.tree() + ].into(), + ) + } + + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + pub(super) fn diff(&self, tree: &mut Tree) { + tree.children[0].diff(&self.item); + self.menu.as_ref().map_or( + {}, + |m| { + m.diff(&mut tree.children[1]) + } + ) + } + + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// + /// out: Node{size:0, \[widget_node, menu_node]} + pub(super) fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + viewport: &Rectangle, + // translation: Vector, + ) -> layout::Node { + let state = tree.state.downcast_ref::(); + let widget_node = self.item.as_widget().layout(&mut tree.children[0], renderer, limits); + let parent_bounds = widget_node.bounds(); + Node::with_children( + Size::ZERO, + [ + widget_node, + if state.open{ + self.menu.as_ref().unwrap().layout( + &mut tree.children[1], + renderer, + &Limits::NONE, + parent_bounds, + // translation, + viewport, + ) + }else{ + Node::default() + } + ].into() + ) + } + + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// + /// layout: Node{size:0, \[widget_node, menu_node]} + pub(super) fn on_event( + &mut self, + // index: usize, // within parent menu + // bar: &mut MenuBarState, + tree: &mut Tree, + event: Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + let mut lc = layout.children(); + let widget_layout = lc.next().unwrap(); + let status = self.item.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + widget_layout, + cursor, + renderer, + clipboard, + shell, + viewport + ); + + let item_state = tree.state.downcast_mut::(); + + let Some(menu) = self.menu.as_ref() else { + item_state.open = false; + return status + }; + let menu_state = tree.children[1].state.downcast_mut::(); + let widget_bounds = widget_layout.bounds(); + + use event::Status::*; + match menu.open_condition{ + OpenCondition::Click => match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) => { + if cursor.is_over(widget_bounds) { + if item_state.open == false { + item_state.open = true; + menu_state.scroll_offset = 0.0; + // bar.indices.push(index); + } + Captured + }else{ + Ignored + } + } + _ => Ignored + } + OpenCondition::Hover => match event { + Event::Mouse(mouse::Event::CursorMoved { position }) => { + if widget_bounds.contains(position) { + if item_state.open == false { + item_state.open = true; + menu_state.scroll_offset = 0.0; + // bar.indices.push(index); + } + Captured + }else{ + Ignored + } + } + _ => Ignored + } + } + .merge(status) + } + + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// + /// layout: Node{size:0, \[widget_node, menu_node]} + pub(super) fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + let item_layout = layout.children().next().unwrap(); + self.item.as_widget().draw( + tree, + renderer, + theme, + style, + item_layout, + cursor, + viewport + ) + } +} + + +/// Adaptive open direction +#[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] +struct Aod { + // whether or not to use aod + horizontal: bool, + vertical: bool, + + // whether or not to use overlap + horizontal_overlap: bool, + vertical_overlap: bool, + + // default direction + horizontal_direction: Direction, + vertical_direction: Direction, + + // Offset of the child in the default direction + horizontal_offset: f32, + vertical_offset: f32, +} +impl Aod { + /// Returns child position and offset position + #[allow(clippy::too_many_arguments)] + fn adaptive( + parent_pos: f32, + parent_size: f32, + child_size: f32, + max_size: f32, + offset: f32, + on: bool, + overlap: bool, + direction: Direction, + ) -> (f32, f32) { + /* + Imagine there're two sticks, parent and child + parent: o-----o + child: o----------o + + Now we align the child to the parent in one dimension + There are 4 possibilities: + + 1. to the right + o-----oo----------o + + 2. to the right with overlaping + o-----o + o----------o + + 3. to the left + o----------oo-----o + + 4. to the left with overlaping + o-----o + o----------o + + The child goes to the default direction by default, + if the space on the default direction runs out it goes to the the other, + whether to use overlap is the caller's decision + + This can be applied to any direction + */ + + match direction { + Direction::Positive => { + let space_negative = parent_pos; + let space_positive = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - overshoot) + } else { + (parent_pos, parent_pos) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - offset) + } else { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } + } + } + Direction::Negative => { + let space_positive = parent_pos; + let space_negative = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos, parent_pos) + } else { + (parent_pos - overshoot, parent_pos - overshoot) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } else { + (parent_pos - overshoot, parent_pos - offset) + } + } + } + } + } + + /// Returns child position and offset position + fn resolve( + &self, + parent_bounds: Rectangle, + children_size: Size, + viewport_size: Size, + ) -> (Point, Point) { + let (x, ox) = Self::adaptive( + parent_bounds.x, + parent_bounds.width, + children_size.width, + viewport_size.width, + self.horizontal_offset, + self.horizontal, + self.horizontal_overlap, + self.horizontal_direction, + ); + let (y, oy) = Self::adaptive( + parent_bounds.y, + parent_bounds.height, + children_size.height, + viewport_size.height, + self.vertical_offset, + self.vertical, + self.vertical_overlap, + self.vertical_direction, + ); + + ([x, y].into(), [ox, oy].into()) + } +} + +fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { + Rectangle { + x: rect.x - padding.left, + y: rect.y - padding.top, + width: rect.width + padding.horizontal(), + height: rect.height + padding.vertical(), + } +} + + +fn search_bound( + default: usize, + default_left: usize, + default_right: usize, + bound: f32, + positions: &[f32], + sizes: &[Size], +) -> usize { + // binary search + let mut left = default_left; + let mut right = default_right; + + let mut index = default; + while left != right { + let m = ((left + right) / 2) + 1; + if positions[m] > bound { + right = m - 1; + } else { + left = m; + } + } + let height = sizes[left].height; + if positions[left] + height > bound { + index = left; + } + index +} + +fn process_scroll_event( + menu_state: &mut MenuState, + prescroll_children_bounds: Rectangle, + delta: mouse::ScrollDelta, + viewport_size: Size, +) -> event::Status{ + use mouse::ScrollDelta; + + let pcb = prescroll_children_bounds; + + let delta_y = match delta { + ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, + }; + + let max_offset = (0.0 - pcb.y).max(0.0); + let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); + menu_state.scroll_offset = (menu_state.scroll_offset + delta_y).clamp(min_offset, max_offset); + + event::Status::Captured +} diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 6399d916..32b4ec41 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,5 +1,9 @@ +//! menu bar + use iced_widget::core::{ - alignment, event, layout, mouse, overlay, renderer, touch, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget + alignment, event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::{tree, Tree}, + Event, + Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget }; use super::{ @@ -9,31 +13,37 @@ use super::{ pub(super) struct MenuBarState{ pub(super) active_root: usize, pub(super) open: bool, + pub(super) viewport: Rectangle, + pub(super) indices: Vec, } impl Default for MenuBarState{ fn default() -> Self { Self { active_root: 0, open: false, + viewport: Rectangle::default(), + indices: Vec::new(), } } } +/// menu bar pub struct MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - roots: Vec>, + roots: Vec>, spacing: f32, padding: Padding, width: Length, height: Length, } +#[allow(missing_docs)] impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - pub fn new(roots: Vec>) -> Self { + pub fn new(roots: Vec>) -> Self { Self { roots, spacing: 0.0, @@ -61,41 +71,32 @@ where tree::State::Some(Box::new(MenuBarState::default())) } + /// \[Tree{item_state, \[widget_state, menu_state]}...] fn children(&self) -> Vec { - self.roots.iter().map(|mt| mt.tree()).collect::>() + println!("bar children"); + todo!() } + /// tree: Tree{bar_state, \[item_tree...]} fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.roots, - |tree, mt| diff(tree, mt), - |mt| mt.tree() - ) + println!("bar diff"); + todo!() } - + + /// tree: Tree{bar_state, \[item_tree...]} fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - flex::resolve( - flex::Axis::Horizontal, - renderer, - limits, - self.width, - self.height, - self.padding, - self.spacing, - alignment::Alignment::Center, - &self.roots.iter().map(|mt| &mt.parent).collect::>(), - &mut tree.children.iter().map(|tree| &mut tree.children[0]).collect::>(), - ) + println!("bar layout"); + todo!() } fn on_event( &mut self, - state: &mut Tree, + tree: &mut Tree, event: event::Event, layout: Layout<'_>, cursor: mouse::Cursor, @@ -104,8 +105,10 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - - event::Status::Ignored + println!("bar event"); + use event::Status::*; + + todo!() } fn draw( @@ -118,18 +121,8 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - if let Some(viewport) = layout.bounds().intersection(viewport) { - for ((root, state), layout) in self - .roots - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - root.parent.as_widget().draw( - state, renderer, theme, style, layout, cursor, &viewport, - ); - } - } + println!("bar draw"); + todo!() } fn overlay<'b>( @@ -138,24 +131,14 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - let state = tree.state.downcast_mut::(); - let parent_bounds = layout.children().nth(state.active_root).unwrap().bounds(); - - if state.open{ - Some(MenuBarOverlay{ - tree, - active_tree: &mut tree.children[state.active_root], - active_root: &mut self.roots[state.active_root], - parent_bounds, - }.overlay_element()) - }else { - None - } + println!("bar overlay"); + todo!() } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, + Theme: 'a, Renderer: 'a + renderer::Renderer, // Renderer::Theme: StyleSheet, { diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 8d679677..0b97f225 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -1,10 +1,9 @@ use iced_widget::core::{ event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget }; -use crate::menu::menu_tree_overlay::MenuTreeOverlay; use super::{ - menu_tree::{MenuTree, MenuTreeState}, + menu_tree::*, menu_bar::MenuBarState, }; @@ -12,17 +11,21 @@ pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, { + /// Tree{ bar_state, [item_tree...] } pub(super) tree: &'b mut Tree, - pub(super) active_tree: &'b mut Tree, - pub(super) active_root: &'b mut MenuTree<'a, Message, Theme, Renderer>, + /// Tree{ item_state, [widget_tree, menu_tree] } + // pub(super) bar: &'b mut MenuBarState, + // pub(super) active_tree: &'b mut Tree, + // pub(super) active_root: &'b mut Item<'a, Message, Theme, Renderer>, + pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], pub(super) parent_bounds: Rectangle, - + pub(super) bar_bounds: Rectangle, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - pub(super) fn overlay_element(self) -> overlay::Element<'a, Message, Theme, Renderer>{ + pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer>{ overlay::Element::new(Point::ORIGIN, Box::new(self)) } } @@ -30,6 +33,7 @@ impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay Node { - /* - Node{ - rect: - children: [ - Node{} // mto layout - Node{} // overlays - Node{} // next mto - ] - } - */ - - fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( - tree: &mut Tree, - menu_tree: &MenuTree<'a, Message, Theme, Renderer>, - parent_bounds: Rectangle, - renderer: &Renderer, - bounds: Size, - position: Point, - translation: Vector, - ) -> Node { - let size = Size::INFINITY; - let state = tree.state.downcast_mut::(); - - let mut mto = MenuTreeOverlay{ - state, - tree, - menu_tree, - parent_bounds, - }; - - let mto_layout = mto.layout(renderer, bounds, position, translation); - - let overlays = menu_tree.children - .iter() - .zip(tree.children[1].children.iter_mut()) - .zip(mto_layout.children()[0].children()) - .filter_map(|((mt, t), n)|{ - mt.parent.as_widget_mut() - .overlay(t, Layout::new(n), renderer) - .and_then(|mut o| Some(o.layout(renderer, bounds, translation))) - }) - .collect::>(); - - let next = menu_tree.children - .iter() - .zip(tree.children[1].children.iter_mut()) - .zip(mto_layout.children()[0].children()) - .find_map(|((mt, t), l)|{ - let state = t.state.downcast_mut::(); - if state.open{ - let parent_bounds = l.bounds(); - // let position = parent_bounds.position(); - let position = Point::ORIGIN; - Some(rec(t, mt, parent_bounds, renderer, bounds, position, position - Point::ORIGIN)) - }else{ - None - } - }); - - Node::with_children( - size, - next.map_or( - [ - mto_layout, - Node::with_children(size, overlays), - ].into(), - |n| [ - mto_layout, - Node::with_children(size, overlays), - n - ].into() - ) - ) - } - - rec( - self.active_tree, - self.active_root, - self.parent_bounds, - renderer, - bounds, - position, - translation - ) + println!("mbo layout"); + todo!() } + /// layout: Node{size:0, \[widget_node, menu_node]} fn on_event( &mut self, event: Event, @@ -132,9 +55,11 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { + println!("mbo event"); todo!() } + /// layout: Node{size:0, \[widget_node, menu_node]} fn draw( &self, renderer: &mut Renderer, @@ -143,46 +68,6 @@ where layout: Layout<'_>, cursor: mouse::Cursor, ) { - fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( - tree: &mut Tree, - menu_tree: &MenuTree<'a, Message, Theme, Renderer>, - parent_bounds: Rectangle, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ){ - let mut lc = layout.children(); - let mto_layout = lc.next().unwrap(); - let overlay_layout = lc.next().unwrap(); - - let state = tree.state.downcast_mut::(); - let mut mto = MenuTreeOverlay{ - state, - tree, - menu_tree, - parent_bounds, - }; - - mto.draw(renderer, theme, style, mto_layout, cursor); - - menu_tree.children - .iter() - .zip(tree.children[1].children.iter_mut()) - .filter_map(|(mt, t)|{ - mt.parent.as_widget_mut().overlay(t, Layout::new(&mt.layout(t, renderer, &Limits::NONE)), renderer) - }) - .zip(overlay_layout.children()) - .for_each(|(o, l)|{ - o.draw(renderer, theme, style, l, cursor) - }); - - if let Some(next_layout) = lc.next(){ - rec(tree, menu_tree, parent_bounds, renderer, theme, style, next_layout, cursor, viewport); - } - - } + todo!() } } \ No newline at end of file diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 501d157e..31cdbb47 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,31 +1,73 @@ //! doc //! +use std::borrow::BorrowMut; + use iced_widget::core::{ - event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, - Alignment, Border, Clipboard, Color, Element, Event, Length, - Padding, Point, Vector, Rectangle, Shell, Size, Widget, + alignment, event, layout::{self, Limits, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget }; use super::types::*; -use super::menu_tree_overlay::MenuTreeOverlay; +use super::{flex, menu_bar::MenuBarState}; + +/* -pub(super) struct MenuTreeState{ - pub(super) open: bool, - pub(super) scroll_offset: f32, +menu tree: +Item{ + widget + Menu [ + Item{...} + Item{...} + Item{...} + ... + ] } -impl Default for MenuTreeState{ - fn default() -> Self { - Self { open: false, scroll_offset: 0.0 } - } + +state tree: +Tree{ + item state + [ + Tree{widget state} + Tree{ + menu state + [ + Tree{item state [...]} + Tree{item state [...]} + Tree{item state [...]} + ... + ] + } + ] +} + +layout tree +Node{ + item node + [ + Node{widget node} + Node{ + menu node + [ + Node{item node [...]} + Node{item node [...]} + Node{item node [...]} + ... + ] + } + ] +} + +*/ + +#[derive(Debug, Default)] +pub(super) struct MenuState{ + scroll_offset:f32, } -/// doc -#[allow(clippy::missing_docs_in_private_parents)] -pub struct MenuTree<'a, Message, Theme, Renderer> +/// menu +pub struct Menu<'a, Message, Theme, Renderer> where - Renderer: renderer::Renderer, + Renderer:renderer::Renderer { - pub(super) parent: Element<'a, Message, Theme, Renderer>, - pub(super) children: Vec>, + pub(super) items: Vec>, pub(super) spacing: f32, pub(super) padding: Padding, pub(super) max_width: f32, @@ -35,44 +77,22 @@ where pub(super) offset: f32, pub(super) open_condition: OpenCondition, } -impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> +#[allow(missing_docs)] +impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where - Renderer: renderer::Renderer, + Renderer:renderer::Renderer { - #[allow(missing_docs)] - pub fn new( - parent: impl Into> - ) -> Self{ - Self{ - parent: parent.into(), - children: Vec::new(), - spacing: 4.0, - padding: [0.0;4].into(), - max_width: f32::MAX, - width: Length::Shrink, - height: Length::Shrink, - axis: Axis::Vertical, - offset: 0.0, - open_condition: OpenCondition::Hover, - } - } - - #[allow(missing_docs)] - pub fn with_children( - parent: impl Into>, - children: Vec>> - ) -> Self{ + pub fn new(items: Vec>) -> Self{ Self{ - parent: parent.into(), - children: children.into_iter().map(Into::into).collect(), - spacing: 4.0, - padding: [0.0;4].into(), + items, + spacing: 0.0, + padding: Padding::ZERO, max_width: f32::MAX, width: Length::Shrink, height: Length::Shrink, axis: Axis::Vertical, offset: 0.0, - open_condition: OpenCondition::Hover, + open_condition: OpenCondition::Click, } } @@ -80,119 +100,419 @@ where Tree{ tag: self.tag(), state: self.state(), - children: self.children() + children: self.children(), } } +} +impl<'a, Message, Theme, Renderer> + // Widget for + Menu<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub(super) fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + pub(super) fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } - /// Sets the vertical spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, spacing: f32) -> Self { - self.spacing = spacing; - self + pub(super) fn state(&self) -> tree::State { + tree::State::Some(Box::new(MenuState::default())) } - /// Sets the [`Padding`] of the [`MenuTree`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self + /// out: \[item_tree...] + pub(super) fn children(&self) -> Vec { + self.items.iter().map(|i| i.tree() ).collect() } - /// Sets the width of the [`MenuTree`]. - pub fn width(mut self, width: impl Into) -> Self { - self.width = width.into(); - self + /// tree: Tree{menu_state, \[item_tree...]} + pub(super) fn diff(&self, tree: &mut Tree) { + tree.diff_children(&self.items.iter().map(|i| &i.item ).collect::>()); } - /// Sets the height of the [`MenuTree`]. - pub fn height(mut self, height: impl Into) -> Self { - self.height = height.into(); - self + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// out: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + pub(super) fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + parent_bounds: Rectangle, + // translation: Vector, + viewport: &Rectangle, + ) -> layout::Node { + let limits = limits.max_width(self.max_width); + + let items_node = flex::resolve( + flex::Axis::Vertical, + renderer, + &limits, + self.width, + self.height, + self.padding, + self.spacing, + alignment::Alignment::Center, + &self.items.iter().map(|i| &i.item ).collect::>(), + &mut tree.children.iter_mut().map(|t| &mut t.children[0] ).collect::>(), + ); + + let items_node = Node::with_children( + items_node.bounds().size(), + self.items + .iter() + .zip(tree.children.iter_mut()) + .zip(items_node.children().into_iter()) + .map(|((item, tree), node)|{ + Node::with_children( + Size::ZERO, + [ + node.clone(), + item.layout(tree, renderer, &Limits::NONE, viewport, + // translation + ) + ].into() + ) + }) + .collect() + ); + + + // for ((item, tree), node) in self.items + // .iter() + // .zip(tree.children.iter_mut()) + // .zip(items_node.children().iter_mut()) + // { + // *node = Node::with_children( + // Size::ZERO, + // [ + // node.clone(), + // item.layout(tree, renderer, &Limits::NONE, viewport, + // // translation + // ) + // .children()[1] + // ].into() + // ) + // } + + let bounds = viewport.size(); + // let vpb = parent_bounds + translation; // viewport space parent bounds + let vpb = parent_bounds; + let aod = { + let hcenter = bounds.width / 2.0; + let vcenter = bounds.height / 2.0; + + let phcenter = vpb.x + vpb.width / 2.0; + let pvcenter = vpb.y + vpb.height / 2.0; + + let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; + let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; + + match self.axis { + Axis::Horizontal => Aod{ + horizontal: false, + vertical: true, + horizontal_overlap: false, + vertical_overlap: true, + horizontal_direction, + vertical_direction, + horizontal_offset: self.offset, + vertical_offset: 0.0, + }, + Axis::Vertical => Aod{ + horizontal: true, + vertical: false, + horizontal_overlap: true, + vertical_overlap: false, + horizontal_direction, + vertical_direction, + horizontal_offset: 0.0, + vertical_offset: self.offset, + } + } + }; + + let children_size = items_node.bounds().size(); + let (children_position, offset_position) = aod.resolve( + vpb, + children_size, + bounds + ); + + // calc offset bounds + let delta = children_position - offset_position; + let offset_size = if delta.x.abs() > delta.y.abs() { + Size::new(delta.x, children_size.height) + } else { + Size::new(children_size.width, delta.y) + }; + let offset_bounds = Rectangle::new(offset_position, offset_size); + let children_bounds = Rectangle::new(children_position, children_size); + let bounds_expand = 30.0; + let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); + + let menu_state = tree.state.downcast_ref::(); + + layout::Node::with_children(Size::INFINITY, [ + items_node + .move_to(children_position) + .translate([0.0, menu_state.scroll_offset]), // items layout + layout::Node::new(children_size) + .move_to(children_position), // prescroll menu bounds + + layout::Node::new(offset_bounds.size()) + .move_to(offset_bounds.position()) + .translate([0.0, menu_state.scroll_offset]), // offset bounds + layout::Node::new(check_bounds.size()) + .move_to(check_bounds.position()) + .translate([0.0, menu_state.scroll_offset]), // check bounds + ].into()) } - /// Sets the axis of the [`MenuTree`]. - pub fn axis(mut self, axis: Axis) -> Self { - self.axis = axis.into(); - self + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + pub(super) fn on_event( + &mut self, + // bar: &mut MenuBarState, + item_state: &mut ItemState, + tree: &mut Tree, + event: Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + parent_bounds: &Rectangle, + items_bounds_list: &[Rectangle], + ) -> event::Status { + let mut lc = layout.children(); + let items_layout = lc.next().unwrap(); + + let prescroll = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + let status = self.items // [item...] + .iter_mut() + .zip(tree.children.iter_mut()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + .map(|((item, tree), layout)| { + item.on_event( + tree, + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) + }) + .fold(event::Status::Ignored, event::Status::merge); + + let menu_state = tree.state.downcast_mut::(); + + use event::Status::*; + match event { + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + if cursor.is_over(items_layout.bounds()){ + process_scroll_event(menu_state, prescroll, delta, viewport.size()) + }else{ + Ignored + } + } + Event::Mouse(mouse::Event::CursorMoved { position }) => { + let open = { + if prescroll.contains(position){ + true + }else if items_bounds_list + .iter() + .any(|r| r.contains(position)) + { + false + }else if parent_bounds.contains(position) + || offset_bounds.contains(position) + || check_bounds.contains(position) + { + true + }else{ + false + } + }; + + if item_state.open == true && open == false { + item_state.open = false; + // menu_state.scroll_offset = 0.0; + } + Captured + } + _ => Ignored + }.merge(status) + } + + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + pub(super) fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + let mut lc = layout.children(); + let items_layout = lc.next().unwrap(); + + for ((item, tree), layout) in self.items // [item...] + .iter() + .zip(tree.children.iter()) // [item_tree...] + .zip(items_layout.children()) // [item_layout...] + { + item.draw( + tree, renderer, theme, style, layout, cursor, &viewport, + ); + } + } +} + + +#[derive(Debug, Default)] +pub(super) struct ItemState{ + pub(super) open: bool +} + +/// menu item +pub struct Item<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub(super) item: Element<'a, Message, Theme, Renderer>, + pub(super) menu: Option>>, +} +#[allow(missing_docs)] +impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> +where + Renderer:renderer::Renderer +{ + pub fn new(item: impl Into>) -> Self{ + Self{ + item: item.into(), + menu: None, + } + } + + pub fn with_menu(item: impl Into>, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ + Self{ + item: item.into(), + menu: Some(Box::new(menu)), + } } - /// Sets the open condition of the [`MenuTree`]. - pub fn open_condition(mut self, open_condition: OpenCondition) -> Self { - self.open_condition = open_condition; + pub fn menu(mut self, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ + self.menu = Some(Box::new(menu)); self } + + pub(super) fn tree(&self) -> Tree{ + Tree{ + tag: self.tag(), + state: self.state(), + children: self.children(), + } + } } -impl <'a, Message, Theme, Renderer> - // Widget for - MenuTree<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> + // Widget for + Item<'a, Message, Theme, Renderer> where - Renderer: renderer::Renderer, + Renderer:renderer::Renderer { pub(super) fn size(&self) -> Size { - Size::new(self.width, self.height) + self.item.as_widget().size() } - + pub(super) fn tag(&self) -> tree::Tag { - tree::Tag::of::() + tree::Tag::of::() } - + pub(super) fn state(&self) -> tree::State { - tree::State::new(MenuTreeState::default()) + tree::State::Some(Box::new(ItemState::default())) } + /// out: \[widget_tree, menu_tree] pub(super) fn children(&self) -> Vec { - [ - Tree::new(&self.parent), - Tree{ - children: self.children.iter().map(|mt| mt.tree()).collect::>(), - ..Tree::empty() - } - ].into() + self.menu.as_ref().map_or( + [ + Tree::new(&self.item), + ].into(), + |m|[ + Tree::new(&self.item), + m.tree() + ].into(), + ) } - /* - Tree{ - tag: MTS - state: MTS - children: [ - Tree{parent}, - Tree{tag:none, state:none, children:[ - Tree{tag:MTS, state:MTS, children:[..]}, - Tree{tag:MTS, state:MTS, children:[..]}, - Tree{tag:MTS, state:MTS, children:[..]}, - ... - ]} - ] - } - */ + /// tree: Tree{item_state, \[widget_tree, menu_tree]} pub(super) fn diff(&self, tree: &mut Tree) { - let parent = &mut tree.children[0]; - let children = &mut tree.children[1]; - - parent.diff(&self.parent); - children.diff_children_custom( - &self.children, - |tree, mt| diff(tree, mt), - |mt| mt.tree() + tree.children[0].diff(&self.item); + self.menu.as_ref().map_or( + {}, + |m| { + m.diff(&mut tree.children[1]) + } ) } + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// + /// out: Node{size:0, \[widget_node, menu_node]} pub(super) fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, + viewport: &Rectangle, + // translation: Vector, ) -> layout::Node { - // println!("mt layout"); - self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) + let state = tree.state.downcast_ref::(); + let widget_node = self.item.as_widget().layout(&mut tree.children[0], renderer, limits); + let parent_bounds = widget_node.bounds(); + Node::with_children( + Size::ZERO, + [ + widget_node, + if state.open{ + self.menu.as_ref().unwrap().layout( + &mut tree.children[1], + renderer, + &Limits::NONE, + parent_bounds, + // translation, + viewport, + ) + }else{ + Node::default() + } + ].into() + ) } + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// + /// layout: Node{size:0, \[widget_node, menu_node]} pub(super) fn on_event( &mut self, + // index: usize, // within parent menu + // bar: &mut MenuBarState, tree: &mut Tree, - event: event::Event, + event: Event, layout: layout::Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, @@ -200,29 +520,39 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - // println!("mt event"); - let status = self.parent.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - viewport, + let mut lc = layout.children(); + let widget_layout = lc.next().unwrap(); + let status = self.item.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + widget_layout, + cursor, + renderer, + clipboard, + shell, + viewport ); - let state = tree.state.downcast_mut::(); - let bounds = layout.bounds(); + let item_state = tree.state.downcast_mut::(); + + let Some(menu) = self.menu.as_ref() else { + item_state.open = false; + return status + }; + let menu_state = tree.children[1].state.downcast_mut::(); + let widget_bounds = widget_layout.bounds(); use event::Status::*; - match self.open_condition{ + match menu.open_condition{ OpenCondition::Click => match event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { - if cursor.is_over(bounds) { - state.open = true; - state.scroll_offset = 0.0; + if cursor.is_over(widget_bounds) { + if item_state.open == false { + item_state.open = true; + menu_state.scroll_offset = 0.0; + // bar.indices.push(index); + } Captured }else{ Ignored @@ -232,9 +562,12 @@ where } OpenCondition::Hover => match event { Event::Mouse(mouse::Event::CursorMoved { position }) => { - if bounds.contains(position) { - state.open = true; - state.scroll_offset = 0.0; + if widget_bounds.contains(position) { + if item_state.open == false { + item_state.open = true; + menu_state.scroll_offset = 0.0; + // bar.indices.push(index); + } Captured }else{ Ignored @@ -246,6 +579,9 @@ where .merge(status) } + /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// + /// layout: Node{size:0, \[widget_node, menu_node]} pub(super) fn draw( &self, tree: &Tree, @@ -256,67 +592,213 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - // println!("mt draw"); - self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) + let item_layout = layout.children().next().unwrap(); + self.item.as_widget().draw( + tree, + renderer, + theme, + style, + item_layout, + cursor, + viewport + ) + } +} + + +/// Adaptive open direction +#[derive(Debug)] +#[allow(clippy::struct_excessive_bools)] +struct Aod { + // whether or not to use aod + horizontal: bool, + vertical: bool, + + // whether or not to use overlap + horizontal_overlap: bool, + vertical_overlap: bool, + + // default direction + horizontal_direction: Direction, + vertical_direction: Direction, + + // Offset of the child in the default direction + horizontal_offset: f32, + vertical_offset: f32, +} +impl Aod { + /// Returns child position and offset position + #[allow(clippy::too_many_arguments)] + fn adaptive( + parent_pos: f32, + parent_size: f32, + child_size: f32, + max_size: f32, + offset: f32, + on: bool, + overlap: bool, + direction: Direction, + ) -> (f32, f32) { + /* + Imagine there're two sticks, parent and child + parent: o-----o + child: o----------o + + Now we align the child to the parent in one dimension + There are 4 possibilities: + + 1. to the right + o-----oo----------o + + 2. to the right with overlaping + o-----o + o----------o + + 3. to the left + o----------oo-----o + + 4. to the left with overlaping + o-----o + o----------o + + The child goes to the default direction by default, + if the space on the default direction runs out it goes to the the other, + whether to use overlap is the caller's decision + + This can be applied to any direction + */ + + match direction { + Direction::Positive => { + let space_negative = parent_pos; + let space_positive = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - overshoot) + } else { + (parent_pos, parent_pos) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos - overshoot, parent_pos - offset) + } else { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } + } + } + Direction::Negative => { + let space_positive = parent_pos; + let space_negative = max_size - parent_pos - parent_size; + + if overlap { + let overshoot = child_size - parent_size; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos, parent_pos) + } else { + (parent_pos - overshoot, parent_pos - overshoot) + } + } else { + let overshoot = child_size + offset; + if on && space_negative > space_positive && overshoot > space_positive { + (parent_pos + parent_size + offset, parent_pos + parent_size) + } else { + (parent_pos - overshoot, parent_pos - offset) + } + } + } + } } - // pub(super) fn overlay<'b>( - // &'b mut self, - // extra_input:f32, - // tree: &'b mut Tree, - // layout: layout::Layout<'_>, - // renderer: &Renderer, - // ) -> Option> { - // // ) -> Option> { - // // ) -> Option> { - // // println!("mt overlay"); - // let mut og = overlay::Group::new(); - // let Tree { tag, state, children } = tree; - - // // let [parent_tree, children_tree] = children.as_mut_slice() else { panic!("Tree Error") }; - // let parent_tree = &mut children[0]; - // let children_tree = &mut children[1]; - - // if let Some(c) = self.parent - // .as_widget_mut() - // .overlay(parent_tree, layout, renderer) - // { - // og = og.push(c); - // } - - // let ms = state.downcast_mut::(); - // if !ms.open { - // Some(og.overlay().into()) - // }else{ - // // Some(og.push( - // // MenuTreeOverlay{ - // // state: ms, - // // tree: children_tree, - // // children: &mut self.children, - // // parent_bounds: layout.bounds(), - // // max_width: 1000.0, - // // spacing: self.spacing, - // // padding: self.padding, - // // width: self.width, - // // height: self.height, - // // axis: self.axis, - // // offset: self.offset, - // // }.overlay() - // // ).overlay()) - // None - // } - // } + /// Returns child position and offset position + fn resolve( + &self, + parent_bounds: Rectangle, + children_size: Size, + viewport_size: Size, + ) -> (Point, Point) { + let (x, ox) = Self::adaptive( + parent_bounds.x, + parent_bounds.width, + children_size.width, + viewport_size.width, + self.horizontal_offset, + self.horizontal, + self.horizontal_overlap, + self.horizontal_direction, + ); + let (y, oy) = Self::adaptive( + parent_bounds.y, + parent_bounds.height, + children_size.height, + viewport_size.height, + self.vertical_offset, + self.vertical, + self.vertical_overlap, + self.vertical_direction, + ); + + ([x, y].into(), [ox, oy].into()) + } } -pub fn diff<'a, Message, Theme, Renderer>( - tree: &mut Tree, - new: &MenuTree<'a, Message, Theme, Renderer> -) where - Renderer: renderer::Renderer, -{ - if tree.tag == new.tag() { - new.diff(tree); - } else { - *tree = new.tree() +fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { + Rectangle { + x: rect.x - padding.left, + y: rect.y - padding.top, + width: rect.width + padding.horizontal(), + height: rect.height + padding.vertical(), } -} \ No newline at end of file +} + + +fn search_bound( + default: usize, + default_left: usize, + default_right: usize, + bound: f32, + positions: &[f32], + sizes: &[Size], +) -> usize { + // binary search + let mut left = default_left; + let mut right = default_right; + + let mut index = default; + while left != right { + let m = ((left + right) / 2) + 1; + if positions[m] > bound { + right = m - 1; + } else { + left = m; + } + } + let height = sizes[left].height; + if positions[left] + height > bound { + index = left; + } + index +} + +fn process_scroll_event( + menu_state: &mut MenuState, + prescroll_children_bounds: Rectangle, + delta: mouse::ScrollDelta, + viewport_size: Size, +) -> event::Status{ + use mouse::ScrollDelta; + + let pcb = prescroll_children_bounds; + + let delta_y = match delta { + ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, + }; + + let max_offset = (0.0 - pcb.y).max(0.0); + let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); + menu_state.scroll_offset = (menu_state.scroll_offset + delta_y).clamp(min_offset, max_offset); + + event::Status::Captured +} diff --git a/src/native/menu/types.rs b/src/native/menu/types.rs index d965f7a1..12de4789 100644 --- a/src/native/menu/types.rs +++ b/src/native/menu/types.rs @@ -1,6 +1,6 @@ use iced_widget::core::{overlay, renderer}; -use super::menu_tree_overlay::MenuTreeOverlay; +// use super::menu_tree_overlay::MenuTreeOverlay; /// The condition of when to close a menu @@ -80,30 +80,3 @@ pub enum OpenCondition{ Click, } -pub(super) enum MenuOverlayElement<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - Overlay(overlay::Element<'b, Message, Theme, Renderer>), - MenuTree(MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) -} -impl<'a, 'b, Message, Theme, Renderer> - From> for - MenuOverlayElement<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn from(value: overlay::Element<'b, Message, Theme, Renderer>) -> Self { - Self::Overlay(value) - } -} -impl<'a, 'b, Message, Theme, Renderer> - From> for - MenuOverlayElement<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn from(value: MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) -> Self { - Self::MenuTree(value) - } -} From 2d5be3b204b97913a235a4ec590a947b07bcccd2 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sun, 11 Feb 2024 20:44:09 +0800 Subject: [PATCH 07/40] nested menu event --- examples/menu/src/main.rs | 242 ++++++--- src/lib.rs | 7 +- src/native/menu.rs | 4 +- src/native/menu/flex.rs | 45 +- src/native/menu/menu_bar.rs | 192 +++++-- src/native/menu/menu_bar_overlay.rs | 423 ++++++++++++++- src/native/menu/menu_tree.rs | 763 +++++++++++++++------------- src/native/menu/types.rs | 23 +- 8 files changed, 1202 insertions(+), 497 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index b3e9a4a8..3b043135 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -1,24 +1,25 @@ -use iced::widget::{column as col, vertical_space}; use iced::widget::{ - button, checkbox, container, horizontal_space, pick_list, row, slider, svg, text, text_input, - toggler, vertical_slider, scrollable, + button, checkbox, container, horizontal_space, pick_list, row, scrollable, slider, svg, text, + text_input, toggler, vertical_slider, }; +use iced::widget::{column as col, vertical_space}; use iced::{alignment, theme, Application, Border, Color, Element, Event, Length, Pixels, Size}; use iced_aw::menu::{ + Item, // menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight, // Menux, OpenCondition, Axis - Menu, Item, MenuBar, - + Menu, + MenuBar, }; use iced_aw::quad; // use iced_aw::{helpers::menu_tree, menu_bar, menu_tree}; pub fn main() -> iced::Result { - std::env::set_var("RUST_BACKTRACE", "1"); + // std::env::set_var("RUST_BACKTRACE", "full"); App::run(iced::Settings { default_text_size: Pixels(15.0), - window: iced::window::Settings{ + window: iced::window::Settings { size: Size::new(1000.0, 500.0), ..Default::default() }, @@ -27,7 +28,7 @@ pub fn main() -> iced::Result { // fonts: todo!(), // default_font: todo!(), // antialiasing: todo!(), - + // default_text_size: 15.0, // window: iced::window::Settings { // size: (1000, 500), @@ -104,7 +105,7 @@ impl Application for App { theme::Palette { primary: Color::from([0.45, 0.25, 0.57]), ..iced::Theme::Light.palette() - } + }, ); ( @@ -136,17 +137,13 @@ impl Application for App { use iced::keyboard; use keyboard::key::Named; - iced::event::listen().map(|event|{ - match event{ - Event::Keyboard(keyboard::Event::KeyPressed { key, ..}) => { - match key { - keyboard::Key::Named(Named::F1) => Message::FlipHorizontal, - keyboard::Key::Named(Named::F2) => Message::FlipVertical, - _ => Message::None - } - }, - _ => Message::None - } + iced::event::listen().map(|event| match event { + Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => match key { + keyboard::Key::Named(Named::F1) => Message::FlipHorizontal, + keyboard::Key::Named(Named::F2) => Message::FlipVertical, + _ => Message::None, + }, + _ => Message::None, }) } @@ -174,7 +171,7 @@ impl Application for App { theme::Palette { primary: c, ..self.theme.palette() - } + }, ); self.title = format!("[{:.2}, {:.2}, {:.2}]", c.r, c.g, c.b); } @@ -189,7 +186,7 @@ impl Application for App { theme::Palette { primary, ..iced::Theme::Dark.palette() - } + }, ) } else { self.theme = iced::Theme::custom( @@ -197,7 +194,7 @@ impl Application for App { theme::Palette { primary, ..iced::Theme::Light.palette() - } + }, ) } } @@ -217,14 +214,145 @@ impl Application for App { fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { // println!("app view"); - let mb = MenuBar::new([ - Item::new(debug_button("aaa")) - .menu(Menu::new([ - Item::new(debug_button("abc").width(180.0)), - Item::new(debug_button("def").width(180.0)), - Item::new(debug_button("xxx").width(180.0)), - ].into())), - ].into()); + let mb = MenuBar::new( + [ + Item::with_menu( + debug_button("content"), + Menu::new( + [ + Item::new(debug_button("abc").width(180.0)), + Item::new(debug_button("def").width(180.0)), + Item::new(debug_button("xxx").width(180.0)), + Item::with_menu( + debug_button("htrsth").width(180.0), + Menu::new( + vec![ + Item::new(debug_button("ccgh").width(180.0)), + Item::new(debug_button("kuyg").width(180.0)), + Item::new(debug_button("vcsa").width(180.0)), + Item::new(debug_button("kiug").width(180.0)), + ] + ).max_width(180.0) + ) + ] + .into(), + ).max_width(180.0), + ), + Item::with_menu( + debug_button("aaa"), + Menu::new( + [ + Item::new(debug_button("abc").width(Length::Fill)), + Item::new(debug_button("def").width(Length::Fill)), + Item::new(debug_button("xxx").width(Length::Fill)), + Item::with_menu( + debug_button("syjdtyjd").width(Length::Fill), + Menu::new( + vec![ + Item::new(debug_button("hghg").width(Length::Fill)), + Item::new(debug_button("kuyg").width(Length::Fill)), + Item::new(debug_button("arga").width(Length::Fill)), + Item::new(debug_button("abcd").width(Length::Fill)), + Item::new(debug_button("vcsa").width(Length::Fill)), + Item::with_menu( + debug_button("htrsthfs").width(Length::Fill), + Menu::new( + vec![ + Item::new(debug_button("hghg").width(Length::Fill)), + Item::new(debug_button("kuyg").width(Length::Fill)), + Item::new(debug_button("vcsa").width(Length::Fill)), + Item::new(debug_button("kiug").width(Length::Fill)), + ] + ).max_width(220.0) + ), + Item::new(debug_button("kiug").width(Length::Fill)), + ] + ).max_width(200.0) + ), + Item::new(debug_button("abc").width(Length::Fill)), + Item::new(debug_button("def").width(Length::Fill)), + Item::new(debug_button("xxx").width(Length::Fill)), + ] + .into(), + ).max_width(180.0), + ), + Item::with_menu( + debug_button("pondjssbah"), + Menu::new( + [ + Item::new(debug_button("abc").width(180.0)), + Item::new(debug_button("def").width(180.0)), + Item::new(debug_button("xxx").width(180.0)), + Item::new(debug_button("htrsrt").width(180.0)), + Item::new(debug_button("htrdf").width(180.0)), + Item::new(debug_button("ngfcgng").width(180.0)), + Item::new(debug_button("hytfy").width(180.0)), + Item::new(debug_button("kuyg").width(180.0)), + Item::new(debug_button("qegvd").width(180.0)), + Item::with_menu( + debug_button("iuoiy").width(180.0), + Menu::new( + [ + Item::new(debug_button("abc").width(Length::Fill)), + Item::new(debug_button("def").width(Length::Fill)), + Item::new(debug_button("xxx").width(Length::Fill)), + Item::new(debug_button("htrsrt").width(Length::Fill)), + Item::new(debug_button("htrdf").width(Length::Fill)), + Item::new(debug_button("ngfcgng").width(Length::Fill)), + Item::new(debug_button("hytfy").width(Length::Fill)), + Item::new(debug_button("kuyg").width(Length::Fill)), + Item::new(debug_button("qegvd").width(Length::Fill)), + Item::new(debug_button("iuoiy").width(Length::Fill)), + Item::new(debug_button("rzsajf").width(Length::Fill)), + Item::new(debug_button("pkmehs").width(Length::Fill)), + Item::new(debug_button("ivrye").width(Length::Fill)), + Item::new(debug_button("zhdkr").width(Length::Fill)), + Item::new(debug_button("vjdiwo").width(Length::Fill)), + Item::new(debug_button("abc").width(Length::Fill)), + Item::new(debug_button("def").width(Length::Fill)), + Item::new(debug_button("xxx").width(Length::Fill)), + Item::new(debug_button("htrsrt").width(Length::Fill)), + Item::new(debug_button("htrdf").width(Length::Fill)), + Item::new(debug_button("ngfcgng").width(Length::Fill)), + Item::new(debug_button("hytfy").width(Length::Fill)), + Item::new(debug_button("kuyg").width(Length::Fill)), + Item::new(debug_button("qegvd").width(Length::Fill)), + Item::new(debug_button("iuoiy").width(Length::Fill)), + Item::new(debug_button("rzsajf").width(Length::Fill)), + Item::new(debug_button("pkmehs").width(Length::Fill)), + Item::new(debug_button("ivrye").width(Length::Fill)), + Item::new(debug_button("zhdkr").width(Length::Fill)), + Item::new(debug_button("vjdiwo").width(Length::Fill)), + ].into() + ).max_width(180.0) + ), + Item::new(debug_button("rzsajf").width(180.0)), + Item::new(debug_button("pkmehs").width(180.0)), + Item::new(debug_button("ivrye").width(180.0)), + Item::new(debug_button("zhdkr").width(180.0)), + Item::new(debug_button("vjdiwo").width(180.0)), + Item::new(debug_button("abc").width(180.0)), + Item::new(debug_button("def").width(180.0)), + Item::new(debug_button("xxx").width(180.0)), + Item::new(debug_button("htrsrt").width(180.0)), + Item::new(debug_button("htrdf").width(180.0)), + Item::new(debug_button("ngfcgng").width(180.0)), + Item::new(debug_button("hytfy").width(180.0)), + Item::new(debug_button("kuyg").width(180.0)), + Item::new(debug_button("qegvd").width(180.0)), + Item::new(debug_button("iuoiy").width(180.0)), + Item::new(debug_button("rzsajf").width(180.0)), + Item::new(debug_button("pkmehs").width(180.0)), + Item::new(debug_button("ivrye").width(180.0)), + Item::new(debug_button("zhdkr").width(180.0)), + Item::new(debug_button("vjdiwo").width(180.0)), + ] + .into(), + ).max_width(180.0), + ), + ] + .into(), + ); /* let mb = row![ Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ @@ -310,38 +438,32 @@ impl Application for App { /* let r = if self.flip_h { row!( - // pick_size_option, - horizontal_space(Length::Fill), + // pick_size_option, + horizontal_space(Length::Fill), mb, ) } else { row!( - mb, - horizontal_space(Length::Fill), + mb, + horizontal_space(Length::Fill), // pick_size_option ) } .padding([2, 8]) .align_items(alignment::Alignment::Center); */ - let r = row![ - horizontal_space(800), - mb, - horizontal_space(800), - ] - .padding([2, 8]) - .align_items(alignment::Alignment::Center) - ; - - + let r = row![horizontal_space(800), mb, horizontal_space(800),] + .padding([2, 8]) + .align_items(alignment::Alignment::Center); + // let top_bar_style: fn(&iced::Theme) -> container::Appearance = // |_theme| container::Appearance { // background: Some(Color::TRANSPARENT.into()), // ..Default::default() // }; // let top_bar = container(r).width(Length::Fill).style(top_bar_style); - - /* + + /* let c = if self.flip_v { col![back, top_bar,] } else { @@ -349,23 +471,19 @@ impl Application for App { }; c.into() */ - - let c = col![ - vertical_space(600), - r, - vertical_space(600), - ]; + + let c = col![vertical_space(600), r, vertical_space(600),]; let sc = scrollable(c) // .direction(scrollable::Direction::Both{ // vertical: scrollable::Properties::new(), // horizontal: scrollable::Properties::new(), // }); - .direction(scrollable::Direction::Both{ + .direction(scrollable::Direction::Both { vertical: scrollable::Properties::new(), horizontal: scrollable::Properties::new(), }); - + let back_style: fn(&iced::Theme) -> container::Appearance = |theme| container::Appearance { background: Some(theme.extended_palette().primary.base.color.into()), ..Default::default() @@ -374,7 +492,7 @@ impl Application for App { .width(Length::Fill) .height(Length::Fill) .style(back_style); - + back.into() } } @@ -387,12 +505,11 @@ impl button::StyleSheet for ButtonStyle { button::Appearance { text_color: style.extended_palette().background.base.text, background: Some(Color::TRANSPARENT.into()), - border: Border{ - radius: [4.0;4].into(), + border: Border { + radius: [4.0; 4].into(), ..Default::default() }, ..Default::default() - } } @@ -417,7 +534,10 @@ fn base_button<'a>( .on_press(msg) } -fn labeled_button<'a>(label: &str, msg: Message) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { +fn labeled_button<'a>( + label: &str, + msg: Message, +) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { base_button( text(label) // .width(Length::Fill) @@ -938,4 +1058,4 @@ fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { root } - */ \ No newline at end of file + */ diff --git a/src/lib.rs b/src/lib.rs index 0c9cb7a7..05320552 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,12 +134,7 @@ mod platform { #[doc(no_inline)] #[cfg(feature = "menu")] - pub use { - crate::native::menu, - // crate::native::menu::{ - // CloseCondition, ItemHeight, ItemWidth, MenuBar, MenuTree, PathHighlight, - // }, - }; + pub use crate::native::menu; #[doc(no_inline)] #[cfg(feature = "quad")] diff --git a/src/native/menu.rs b/src/native/menu.rs index 621986a9..d5367f64 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -59,8 +59,8 @@ mod menu_tree; mod types; pub use crate::style::menu_bar::{Appearance, StyleSheet}; -pub use menu_tree::{Item, Menu}; pub use menu_bar::MenuBar; +pub use menu_tree::{Item, Menu}; pub use types::*; // A `MenuBar` collects `MenuTree`s and handles @@ -68,4 +68,4 @@ pub use types::*; // pub use types::*; // Nested menu is essentially a tree of items, a menu is a collection of items // pub type MenuTree<'a, Message, Renderer> = menu_tree::MenuTree<'a, Message, Renderer>; -// pub use menux::Menux; \ No newline at end of file +// pub use menux::Menux; diff --git a/src/native/menu/flex.rs b/src/native/menu/flex.rs index 20b9dc17..6a6a565e 100644 --- a/src/native/menu/flex.rs +++ b/src/native/menu/flex.rs @@ -17,7 +17,8 @@ // See the License for the specific language governing permissions and // limitations under the License. use iced_widget::core::{ - Element, layout::{Limits, Node}, widget, Alignment, Length, Padding, Point, Size + layout::{Limits, Node}, + widget, Alignment, Element, Length, Padding, Point, Size, }; /// The main axis of a flex layout. @@ -107,11 +108,13 @@ where if fill_cross_factor == 0 { let (max_width, max_height) = axis.pack(available, max_cross); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.borrow().as_widget().layout(tree.borrow_mut(), renderer, &child_limits); + child + .borrow() + .as_widget() + .layout(tree.borrow_mut(), renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -134,11 +137,13 @@ where if fill_main_factor == 0 && fill_cross_factor != 0 { let (max_width, max_height) = axis.pack(available, cross); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.borrow().as_widget().layout(tree.borrow_mut(), renderer, &child_limits); + child + .borrow() + .as_widget() + .layout(tree.borrow_mut(), renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -167,8 +172,7 @@ where }; if fill_main_factor != 0 { - let max_main = - remaining * fill_main_factor as f32 / fill_main_sum as f32; + let max_main = remaining * fill_main_factor as f32 / fill_main_sum as f32; let min_main = if max_main.is_infinite() { 0.0 @@ -191,7 +195,10 @@ where ); let layout = - child.borrow().as_widget().layout(tree.borrow_mut(), renderer, &child_limits); + child + .borrow() + .as_widget() + .layout(tree.borrow_mut(), renderer, &child_limits); cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; @@ -212,18 +219,10 @@ where match axis { Axis::Horizontal => { - node.align_mut( - Alignment::Start, - align_items, - Size::new(0.0, cross), - ); + node.align_mut(Alignment::Start, align_items, Size::new(0.0, cross)); } Axis::Vertical => { - node.align_mut( - align_items, - Alignment::Start, - Size::new(cross, 0.0), - ); + node.align_mut(align_items, Alignment::Start, Size::new(cross, 0.0)); } } @@ -233,11 +232,7 @@ where } let (intrinsic_width, intrinsic_height) = axis.pack(main - pad.0, cross); - let size = limits.resolve( - width, - height, - Size::new(intrinsic_width, intrinsic_height), - ); + let size = limits.resolve(width, height, Size::new(intrinsic_width, intrinsic_height)); Node::with_children(size.expand(padding), nodes) } diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 32b4ec41..c4776d2e 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,28 +1,31 @@ //! menu bar use iced_widget::core::{ - alignment, event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::{tree, Tree}, - Event, - Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget + alignment, event, + layout::{self, Limits, Node}, + mouse, overlay, renderer, touch, + widget::{tree, Tree}, + Alignment, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Point, + Rectangle, Shell, Size, Widget, }; -use super::{ - flex, menu_bar_overlay::MenuBarOverlay, menu_tree::* -}; +use super::{flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*, types::*}; -pub(super) struct MenuBarState{ - pub(super) active_root: usize, +pub(super) struct MenuBarState { + pub(super) active_root: Index, pub(super) open: bool, - pub(super) viewport: Rectangle, - pub(super) indices: Vec, + pub(super) is_pressed: bool, + // pub(super) viewport: Rectangle, + // pub(super) indices: Vec, } -impl Default for MenuBarState{ +impl Default for MenuBarState { fn default() -> Self { Self { - active_root: 0, + active_root: None, open: false, - viewport: Rectangle::default(), - indices: Vec::new(), + is_pressed: false, + // viewport: Rectangle::default(), + // indices: Vec::new(), } } } @@ -43,7 +46,12 @@ impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - pub fn new(roots: Vec>) -> Self { + pub fn new(mut roots: Vec>) -> Self { + roots.iter_mut().for_each(|i|{ + if let Some(m) = i.menu.as_mut(){ + m.axis = Axis::Vertical; + } + }); Self { roots, spacing: 0.0, @@ -52,13 +60,12 @@ where height: Length::Shrink, } } - } -impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> Widget + for MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - fn size(&self) -> Size { Size::new(self.width, self.height) } @@ -71,18 +78,25 @@ where tree::State::Some(Box::new(MenuBarState::default())) } - /// \[Tree{item_state, \[widget_state, menu_state]}...] + /// \[Tree{stateless, \[widget_state, menu_state]}...] fn children(&self) -> Vec { - println!("bar children"); - todo!() + // println!("bar children"); + self.roots + .iter() + .map(|item| item.tree()) + .collect::>() } /// tree: Tree{bar_state, \[item_tree...]} fn diff(&self, tree: &mut Tree) { - println!("bar diff"); - todo!() + // println!("bar diff"); + tree.diff_children_custom( + &self.roots, + |tree, item| item.diff(tree), + |item| item.tree(), + ) } - + /// tree: Tree{bar_state, \[item_tree...]} fn layout( &self, @@ -90,8 +104,23 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - println!("bar layout"); - todo!() + // println!("bar layout"); + flex::resolve( + flex::Axis::Horizontal, + renderer, + limits, + self.width, + self.height, + self.padding, + self.spacing, + alignment::Alignment::Center, + &self.roots.iter().map(|item| &item.item).collect::>(), + &mut tree + .children + .iter_mut() + .map(|tree| &mut tree.children[0]) + .collect::>(), + ) } fn on_event( @@ -105,10 +134,76 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - println!("bar event"); + // println!("bar event"); use event::Status::*; - todo!() + let status = self + .roots + .iter_mut() // [Item...] + .zip(tree.children.iter_mut()) // [item_tree...] + .zip(layout.children()) // [widget_node...] + .map(|((item, tree), layout)| { + item.on_event( + tree, + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) + }) + .fold(Ignored, |acc, x| acc.merge(x)); + + let bar = tree.state.downcast_mut::(); + let bar_bounds = layout.bounds(); + + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + if cursor.is_over(bar_bounds) { + bar.is_pressed = true; + Captured + } else { + Ignored + } + } + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => { + if cursor.is_over(bar_bounds) && bar.is_pressed { + bar.open = true; + bar.is_pressed = false; + for (i, l) in layout.children().enumerate() { + if cursor.is_over(l.bounds()) { + bar.active_root = Some(i); + break; + } + } + Captured + } else { + Ignored + } + } + Event::Mouse(mouse::Event::CursorMoved { .. }) => { + if bar.open{ + if cursor.is_over(bar_bounds) { + for (i, l) in layout.children().enumerate() { + if cursor.is_over(l.bounds()) { + bar.active_root = Some(i); + break; + } + } + // println!("mbo bar.active: {:?}", bar.active_root) + }else{ + bar.open = false + } + Captured + }else{ + Ignored + } + } + _ => Ignored, + } + .merge(status) } fn draw( @@ -121,21 +216,50 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - println!("bar draw"); - todo!() + // println!("bar draw"); + self.roots + .iter() // [Item...] + .zip(tree.children.iter()) // [item_tree...] + .zip(layout.children()) // [widget_node...] + .for_each(|((item, tree), layout)| { + item.draw(tree, renderer, theme, style, layout, cursor, viewport) + }); } - + fn overlay<'b>( &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - println!("bar overlay"); - todo!() + // println!("bar overlay"); + let state = tree.state.downcast_mut::(); + + let init_bar_bounds = layout.bounds(); + // let init_parent_bounds = state.active_root + // .map(|i| layout.children().nth(i).unwrap().bounds()); + let init_root_bounds = layout.children().map(|l| l.bounds() ).collect(); + + if state.open { + // println!("bar open"); + Some( + MenuBarOverlay { + tree, + roots: &mut self.roots, + init_bar_bounds, + // init_parent_bounds, + init_root_bounds, + } + .overlay_element(), + ) + } else { + // println!("None"); + None + } } } -impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> where Message: 'a, Theme: 'a, diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 0b97f225..c9284241 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -1,39 +1,44 @@ -use iced_widget::core::{ - event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget -}; +use iced_widget::{core::{ + event, + layout::{self, Limits, Node}, + mouse, overlay, renderer, touch, + widget::tree::{self, Tree}, + Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, + Shell, Size, Vector, Widget, +}, shader::wgpu::naga::Barrier}; -use super::{ - menu_tree::*, - menu_bar::MenuBarState, -}; +use super::{menu_bar::MenuBarState, menu_tree::*, types::*}; -pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where +pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +where Renderer: renderer::Renderer, { + // pub(super) active: usize, /// Tree{ bar_state, [item_tree...] } pub(super) tree: &'b mut Tree, - /// Tree{ item_state, [widget_tree, menu_tree] } // pub(super) bar: &'b mut MenuBarState, // pub(super) active_tree: &'b mut Tree, // pub(super) active_root: &'b mut Item<'a, Message, Theme, Renderer>, pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], - pub(super) parent_bounds: Rectangle, - pub(super) bar_bounds: Rectangle, + + // pub(super) bar_layout: Layout<'b>, + pub(super) init_bar_bounds: Rectangle, + pub(super) init_root_bounds: Vec, + // pub(super) init_parent_bounds: Option, } -impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer>{ + pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer> { overlay::Element::new(Point::ORIGIN, Box::new(self)) } } -impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay + for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - /// out: Node{size:0, \[widget_node, menu_node]} fn layout( &mut self, renderer: &Renderer, @@ -41,11 +46,121 @@ where position: Point, translation: Vector, ) -> Node { + println!(); println!("mbo layout"); - todo!() - } + // println!("translation: {:?}", translation); + + let bar = self.tree.state.downcast_ref::(); + let bar_bounds = self.init_bar_bounds; + // println!("bar bounds: {:?}", bar_bounds); + + let bar_node = Node::with_children( + bar_bounds.size(), + [].into() + ).move_to(bar_bounds.position() + translation); + + let roots_node = Node::with_children( + Size::ZERO, + self.init_root_bounds.iter().map(|r| + Node::new(r.size()).move_to(r.position()) + ).collect() + ).translate(translation); + + let Some(active) = bar.active_root else { + println!("no active"); + return Node::with_children( + bounds, + [ + bar_node, + roots_node, + ].into() + ) + }; - /// layout: Node{size:0, \[widget_node, menu_node]} + let active_root = &mut self.roots[active]; + let active_tree = &mut self.tree.children[active]; // item_tree: Tree{ stateless, [ widget_tree, menu_tree ] } + let parent_bounds = self.init_root_bounds[active] + translation; + // println!("parent bounds: {:?}", parent_bounds); + + fn rec<'a, Message, Theme, Renderer: renderer::Renderer>( + renderer: &Renderer, + item: &Item<'a, Message, Theme, Renderer>, + tree: &mut Tree, + menu_nodes: &mut Vec, + parent_bounds: Rectangle, + parent_direction: (Direction, Direction), + translation: Vector, + // parent_offset: Vector, + viewport: &Rectangle, + ) { + let menu = item.menu.as_ref().unwrap(); + let menu_tree = &mut tree.children[1]; + + let (menu_node, direction) = menu.layout(menu_tree, renderer, &Limits::NONE, parent_bounds, parent_direction, translation, viewport); + // Node{inf, [ items_node, prescroll, offset_bounds, check_bounds ]} + menu_nodes.push(menu_node); + + let menu_state = menu_tree.state.downcast_ref::(); + + if let Some(active) = menu_state.active{ + let next_item = &menu.items[active]; + let next_tree = &mut menu_tree.children[active]; + let items_node = &menu_nodes.last().unwrap().children()[0]; + let next_parent_bounds = items_node.children()[active].bounds() + + (items_node.bounds().position() - Point::ORIGIN); + // let next_parent_direction = + // println!("items bounds: {:?}", items_node.bounds()); + // println!("next parent bounds: {:?}", next_parent_bounds); + + rec(renderer, next_item, next_tree, menu_nodes, next_parent_bounds, direction, translation, viewport); + } + } + + let mut menu_nodes = vec![]; + + let parent_direction = { + let hcenter = bounds.width / 2.0; + let vcenter = bounds.height / 2.0; + + let phcenter = parent_bounds.x + parent_bounds.width / 2.0; + let pvcenter = parent_bounds.y + parent_bounds.height / 2.0; + + ( + if phcenter < hcenter { + Direction::Positive + } else { + Direction::Negative + }, + if pvcenter < vcenter { + Direction::Positive + } else { + Direction::Negative + } + ) + }; + + rec( + renderer, + active_root, + active_tree, + &mut menu_nodes, + parent_bounds, + parent_direction, + translation, + &Rectangle::new(position, bounds), + ); + + Node::with_children( + bounds, + [ + bar_node, + roots_node, + Node::with_children(Size::ZERO, menu_nodes), + ].into() + ) + } + + #[allow(unused_results)] fn on_event( &mut self, event: Event, @@ -55,11 +170,142 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { + use event::Status::*; + + println!(); println!("mbo event"); - todo!() + + let viewport = layout.bounds(); + let mut lc = layout.children(); + let bar_bounds = lc.next().unwrap().bounds(); + let roots_layout = lc.next().unwrap(); + + let bar = self.tree.state.downcast_mut::(); + + // if cursor.is_over(bar_bounds){ + // return Ignored; + // } + + let Some(active) = bar.active_root else { + return Ignored; + }; + + let parent_bounds = roots_layout.children().nth(active).unwrap().bounds(); + let Some(menu_layouts_layout) = lc.next() else { return Ignored; }; // Node{0, [menu_node...]} + let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] + + let active_root = &mut self.roots[active]; + let active_tree = &mut self.tree.children[active]; + let mut prev_bounds_list = vec![bar_bounds]; + + fn rec<'a, 'b, Message, Theme, Renderer: renderer::Renderer>( + tree: &mut Tree, + item: &mut Item<'a, Message, Theme, Renderer>, + event: Event, + layout_iter: &mut impl Iterator< Item = Layout<'b>>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + parent_bounds: Rectangle, + viewport: &Rectangle, + prev_bounds_list: &mut Vec, + prev: &mut Index, + ) -> RecEvent { + let menu = item.menu.as_mut().expect("No menu defined in this item"); + let menu_tree = &mut tree.children[1]; + + let Some(menu_layout) = layout_iter.next() else { return RecEvent::None; }; // menu_node: Node{inf, [ items_node, prescroll, offset_bounds, check_bounds ]} + + let mut mc = menu_layout.children(); + let items_layout = mc.next().unwrap(); // items_node + let prescroll = mc.next().unwrap().bounds(); + prev_bounds_list.push(prescroll); + + let menu_state = menu_tree.state.downcast_mut::(); + + if let Some(active) = menu_state.active{ + let next_tree = &mut menu_tree.children[active]; + let next_item = &mut menu.items[active]; + let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); + + let re = rec( + next_tree, + next_item, + event.clone(), + layout_iter, + cursor, + renderer, + clipboard, + shell, + next_parent_bounds, + viewport, + prev_bounds_list, + &mut menu_state.active + ); + + prev_bounds_list.pop(); + + match re { + RecEvent::Event => RecEvent::Event, + RecEvent::Close => { + menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); + if prev.is_some(){ + RecEvent::None + }else{ + RecEvent::Close + } + } + RecEvent::None => { + if cursor.is_over(prescroll){ + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.open_event(menu_tree, menu_layout, cursor); + RecEvent::Event + }else{ + RecEvent::None + } + } + } + }else{ + prev_bounds_list.pop(); + + if cursor.is_over(prescroll){ + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.open_event(menu_tree, menu_layout, cursor); + RecEvent::Event + }else{ + menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); + if prev.is_some(){ + RecEvent::None + }else{ + RecEvent::Close + } + } + } + } + + rec( + active_tree, + active_root, + event, + &mut menu_layouts, + cursor, + renderer, + clipboard, + shell, + parent_bounds, + &viewport, + &mut prev_bounds_list, + &mut bar.active_root, + ); + + if cursor.is_over(bar_bounds){ + Ignored + }else{ + Captured + } } - - /// layout: Node{size:0, \[widget_node, menu_node]} + fn draw( &self, renderer: &mut Renderer, @@ -68,6 +314,135 @@ where layout: Layout<'_>, cursor: mouse::Cursor, ) { - todo!() + let viewport = layout.bounds(); + let mut lc = layout.children(); + let _bar_bounds = lc.next().unwrap().bounds(); + let roots_layout = lc.next().unwrap(); + + let bar = self.tree.state.downcast_ref::(); + + let Some(active) = bar.active_root else { + return; + }; + + let parent_bounds = roots_layout.children().nth(active).unwrap().bounds(); + let menu_layouts_layout = lc.next().unwrap(); // Node{0, [menu_node...]} + let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] + + let active_root = &self.roots[active]; + let active_tree = &self.tree.children[active]; + + /* println!("bar bounds: {:?}", bar_bounds); + renderer.fill_quad( + renderer::Quad{ + bounds: bar_bounds, + border: Border{ + // width: 6.0, + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + Color::from([0.0, 1.0, 1.0, 0.3]) + ); + + roots_layout.children().for_each(|l|{ + println!("root bounds: {:?}", l.bounds()); + renderer.fill_quad( + renderer::Quad{ + bounds: l.bounds(), + ..Default::default() + }, + Color::from([1.0, 0.0, 0.0, 0.6]) + ); + }); */ + + fn rec<'a, 'b, Message, Theme, Renderer: renderer::Renderer>( + tree: &Tree, + item: &Item<'a, Message, Theme, Renderer>, + layout_iter: &mut impl Iterator< Item = Layout<'b>>, + cursor: mouse::Cursor, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + parent_bounds: Rectangle, + viewport: &Rectangle, + ) { + let menu = item.menu.as_ref().expect("No menu defined in this item"); + let menu_tree = &tree.children[1]; + + let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ items_node, prescroll, offset_bounds, check_bounds ]} + + menu.draw( + menu_tree, + renderer, + theme, + style, + menu_layout, + cursor, + viewport, + parent_bounds + ); + + let items_layout = menu_layout.children().next().unwrap(); // items_node + + let menu_state = menu_tree.state.downcast_ref::(); + + if let Some(active) = menu_state.active{ + let next_tree = &menu_tree.children[active]; + let next_item = &menu.items[active]; + let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); + renderer.with_layer( + *viewport, + |r| rec( + next_tree, + next_item, + layout_iter, + cursor, + r, + theme, + style, + next_parent_bounds, + viewport + ) + ); + } + } + + rec( + active_tree, + active_root, + &mut menu_layouts, + cursor, + renderer, + theme, + style, + parent_bounds, + &viewport + ) + + } + + fn is_over( + &self, + layout: Layout<'_>, + _renderer: &Renderer, + cursor_position: Point + ) -> bool { + // let viewport = layout.bounds(); + // let mut lc = layout.children(); + // let menu_layouts_layout = lc.next().unwrap(); // Node{0, [menu_node...]} + // let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] + // // let parent_bounds = lc.next().unwrap().bounds(); + + // let isover = menu_layouts.any(|l|{ + // let items_layout = l.children().next().unwrap(); + // items_layout.bounds().contains(cursor_position) + // }); + // // if isover{ + // // println!("is_over"); + // // } + // isover + false } -} \ No newline at end of file +} diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 31cdbb47..e94cada5 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,12 +1,17 @@ //! doc //! -use std::borrow::BorrowMut; +use std::{any::Any, borrow::BorrowMut}; -use iced_widget::core::{ - alignment, event, layout::{self, Limits, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget -}; use super::types::*; use super::{flex, menu_bar::MenuBarState}; +use iced_widget::core::{ + alignment, event, + layout::{self, Limits, Node}, + mouse, overlay, renderer, touch, + widget::tree::{self, Tree}, + Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, + Size, Vector, Widget, +}; /* @@ -38,34 +43,26 @@ Tree{ ] } -layout tree -Node{ - item node - [ - Node{widget node} - Node{ - menu node - [ - Node{item node [...]} - Node{item node [...]} - Node{item node [...]} - ... - ] - } - ] -} - */ -#[derive(Debug, Default)] -pub(super) struct MenuState{ - scroll_offset:f32, +#[derive(Debug)] +pub(super) struct MenuState { + scroll_offset: f32, + pub(super) active: Index, +} +impl Default for MenuState{ + fn default() -> Self { + Self { + scroll_offset: 0.0, + active: None, + } + } } /// menu pub struct Menu<'a, Message, Theme, Renderer> where - Renderer:renderer::Renderer + Renderer: renderer::Renderer, { pub(super) items: Vec>, pub(super) spacing: f32, @@ -80,40 +77,43 @@ where #[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where - Renderer:renderer::Renderer + Renderer: renderer::Renderer, { - pub fn new(items: Vec>) -> Self{ - Self{ + pub fn new(items: Vec>) -> Self { + Self { items, spacing: 0.0, padding: Padding::ZERO, max_width: f32::MAX, - width: Length::Shrink, + width: Length::Fill, height: Length::Shrink, - axis: Axis::Vertical, - offset: 0.0, + axis: Axis::Horizontal, + offset: 20.0, open_condition: OpenCondition::Click, } } - pub fn tree(&self) -> Tree{ - Tree{ + pub fn tree(&self) -> Tree { + Tree { tag: self.tag(), state: self.state(), children: self.children(), } } + + pub fn max_width(mut self, max_width: f32) -> Self{ + self.max_width = max_width; + self + } } -impl<'a, Message, Theme, Renderer> - // Widget for - Menu<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where - Renderer:renderer::Renderer + Renderer: renderer::Renderer, { pub(super) fn size(&self) -> Size { Size::new(self.width, self.height) } - + pub(super) fn tag(&self) -> tree::Tag { tree::Tag::of::() } @@ -124,26 +124,32 @@ where /// out: \[item_tree...] pub(super) fn children(&self) -> Vec { - self.items.iter().map(|i| i.tree() ).collect() + self.items.iter().map(|i| i.tree()).collect() } /// tree: Tree{menu_state, \[item_tree...]} pub(super) fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.items.iter().map(|i| &i.item ).collect::>()); + // tree.diff_children(&self.items.iter().map(|i| &i.item ).collect::>()); + tree.diff_children_custom( + &self.items, + |tree, item| item.diff(tree), + |item| item.tree(), + ) } /// tree: Tree{menu_state, \[item_tree...]} - /// - /// out: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + /// + /// out: Node{inf, \[ items_node, prescroll, offset_boundss, check_bounds ]} pub(super) fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, parent_bounds: Rectangle, - // translation: Vector, + parent_direction: (Direction, Direction), + translation: Vector, viewport: &Rectangle, - ) -> layout::Node { + ) -> (layout::Node, (Direction, Direction)) { let limits = limits.max_width(self.max_width); let items_node = flex::resolve( @@ -155,129 +161,59 @@ where self.padding, self.spacing, alignment::Alignment::Center, - &self.items.iter().map(|i| &i.item ).collect::>(), - &mut tree.children.iter_mut().map(|t| &mut t.children[0] ).collect::>(), - ); - - let items_node = Node::with_children( - items_node.bounds().size(), - self.items - .iter() - .zip(tree.children.iter_mut()) - .zip(items_node.children().into_iter()) - .map(|((item, tree), node)|{ - Node::with_children( - Size::ZERO, - [ - node.clone(), - item.layout(tree, renderer, &Limits::NONE, viewport, - // translation - ) - ].into() - ) - }) - .collect() + &self.items.iter().map(|i| &i.item).collect::>(), + &mut tree + .children + .iter_mut() + .map(|t| &mut t.children[0]) + .collect::>(), ); - - // for ((item, tree), node) in self.items - // .iter() - // .zip(tree.children.iter_mut()) - // .zip(items_node.children().iter_mut()) - // { - // *node = Node::with_children( - // Size::ZERO, - // [ - // node.clone(), - // item.layout(tree, renderer, &Limits::NONE, viewport, - // // translation - // ) - // .children()[1] - // ].into() - // ) - // } - - let bounds = viewport.size(); - // let vpb = parent_bounds + translation; // viewport space parent bounds - let vpb = parent_bounds; - let aod = { - let hcenter = bounds.width / 2.0; - let vcenter = bounds.height / 2.0; - - let phcenter = vpb.x + vpb.width / 2.0; - let pvcenter = vpb.y + vpb.height / 2.0; - - let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; - let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; - - match self.axis { - Axis::Horizontal => Aod{ - horizontal: false, - vertical: true, - horizontal_overlap: false, - vertical_overlap: true, - horizontal_direction, - vertical_direction, - horizontal_offset: self.offset, - vertical_offset: 0.0, - }, - Axis::Vertical => Aod{ - horizontal: true, - vertical: false, - horizontal_overlap: true, - vertical_overlap: false, - horizontal_direction, - vertical_direction, - horizontal_offset: 0.0, - vertical_offset: self.offset, - } - } - }; + let aod = Aod::new(self.axis, viewport.size(), parent_bounds, parent_direction, self.offset); let children_size = items_node.bounds().size(); - let (children_position, offset_position) = aod.resolve( - vpb, - children_size, - bounds - ); - + let (children_position, offset_position, child_direction) = aod.resolve(parent_bounds, children_size, viewport.size()); + // calc offset bounds let delta = children_position - offset_position; let offset_size = if delta.x.abs() > delta.y.abs() { - Size::new(delta.x, children_size.height) + Size::new(self.offset, children_size.height) } else { - Size::new(children_size.width, delta.y) + Size::new(children_size.width, self.offset) }; + let offset_bounds = Rectangle::new(offset_position, offset_size); let children_bounds = Rectangle::new(children_position, children_size); let bounds_expand = 30.0; let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); - + let menu_state = tree.state.downcast_ref::(); - layout::Node::with_children(Size::INFINITY, [ - items_node - .move_to(children_position) - .translate([0.0, menu_state.scroll_offset]), // items layout - layout::Node::new(children_size) - .move_to(children_position), // prescroll menu bounds - - layout::Node::new(offset_bounds.size()) - .move_to(offset_bounds.position()) - .translate([0.0, menu_state.scroll_offset]), // offset bounds - layout::Node::new(check_bounds.size()) - .move_to(check_bounds.position()) - .translate([0.0, menu_state.scroll_offset]), // check bounds - ].into()) + ( + layout::Node::with_children( + Size::INFINITY, + [ + items_node + .move_to(children_position) + .translate([0.0, menu_state.scroll_offset]), // items layout + layout::Node::new(children_size) + .move_to(children_position), // prescroll bounds + layout::Node::new(offset_bounds.size()) + .move_to(offset_bounds.position()), // offset boundss + layout::Node::new(check_bounds.size()) + .move_to(check_bounds.position()), // check bounds + ] + .into(), + ), + child_direction + ) } /// tree: Tree{menu_state, \[item_tree...]} - /// - /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} + /// + /// layout: Node{inf, \[ items_node, prescroll, offset_boundss, check_bounds ]} pub(super) fn on_event( &mut self, - // bar: &mut MenuBarState, - item_state: &mut ItemState, tree: &mut Tree, event: Event, layout: layout::Layout<'_>, @@ -286,17 +222,17 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, - parent_bounds: &Rectangle, - items_bounds_list: &[Rectangle], ) -> event::Status { + // println!("menu event"); let mut lc = layout.children(); let items_layout = lc.next().unwrap(); - + let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); - let status = self.items // [item...] + let status = self + .items // [item...] .iter_mut() .zip(tree.children.iter_mut()) // [item_tree...] .zip(items_layout.children()) // [item_layout...] @@ -313,85 +249,253 @@ where ) }) .fold(event::Status::Ignored, event::Status::merge); - + let menu_state = tree.state.downcast_mut::(); use event::Status::*; match event { Event::Mouse(mouse::Event::WheelScrolled { delta }) => { - if cursor.is_over(items_layout.bounds()){ - process_scroll_event(menu_state, prescroll, delta, viewport.size()) - }else{ + if cursor.is_over(items_layout.bounds()) { + process_scroll_event(menu_state, prescroll, delta, viewport.size()); + Captured + } else if cursor.is_over(offset_bounds) || cursor.is_over(check_bounds) { + Captured + } else { Ignored } } - Event::Mouse(mouse::Event::CursorMoved { position }) => { + _ => Ignored, + } + .merge(status) + } + + /// tree: Tree{menu_state, \[item_tree...]} + /// + /// layout: Node{inf, \[ items_node, prescroll, offset_bounds, check_bounds ]} + pub(super) fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + parent_bounds: Rectangle, + ) { + let mut lc = layout.children(); + let items_layout = lc.next().unwrap(); + let prescroll = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + // println!("prescroll: {:?}", prescroll); + // println!("parent_bounds: {:?}", parent_bounds); + + renderer.fill_quad( + renderer::Quad{ + bounds: check_bounds, + border: Border{ + // color: todo!(), + // width: todo!(), + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + Color::from([1.0, 0.0, 0.0, 0.1]) + ); + renderer.fill_quad( + renderer::Quad{ + bounds: prescroll, + border: Border{ + // color: todo!(), + // width: todo!(), + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + Color::from([1.0, 1.0, 1.0, 1.0]) + ); + renderer.fill_quad( + renderer::Quad{ + bounds: offset_bounds, + border: Border{ + // color: todo!(), + // width: todo!(), + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + Color::from([0.0, 0.0, 1.0, 0.3]) + ); + + renderer.fill_quad( + renderer::Quad{ + bounds: parent_bounds, + border: Border{ + // color: todo!(), + // width: todo!(), + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + Color::from([1.0, 1.0, 0.0, 0.5]) + ); + + for ((item, tree), layout) in self + .items // [item...] + .iter() + .zip(tree.children.iter()) // [item_tree...] + .zip(items_layout.children()) + // [item_layout...] + { + item.draw(tree, renderer, theme, style, layout, cursor, &viewport); + } + } + + /// layout: Node{inf, \[ items_node, prescroll, offset_boundss, check_bounds ]} + pub(super) fn open_or_close_event( + &mut self, + // process_oce: &mut bool, + prev: &mut Index, + tree: &mut Tree, + event: Event, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + parent_bounds: Rectangle, + prev_bounds_list: &[Rectangle], + ){ + let mut lc = layout.children(); + let items_layout = lc.next().unwrap(); + let prescroll = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + let menu_state = tree.state.downcast_mut::(); + + match event { + Event::Mouse(mouse::Event::CursorMoved { .. }) => { let open = { - if prescroll.contains(position){ + if cursor.is_over(prescroll) + || cursor.is_over(parent_bounds) + || cursor.is_over(offset_bounds) { true - }else if items_bounds_list - .iter() - .any(|r| r.contains(position)) - { + } else if prev_bounds_list.iter().any(|r| cursor.is_over(*r)) { false - }else if parent_bounds.contains(position) - || offset_bounds.contains(position) - || check_bounds.contains(position) - { + } else if cursor.is_over(check_bounds) { true - }else{ + } else { false } }; - if item_state.open == true && open == false { - item_state.open = false; - // menu_state.scroll_offset = 0.0; + if open { + if !cursor.is_over(prescroll) { + menu_state.active = None + } else { + self.items + .iter() + .zip(items_layout.children()) + .enumerate() + .for_each(|(i, (item, layout))|{ + if item.menu.is_some() && cursor.is_over(layout.bounds()) { + println!("new active: {}", i); + menu_state.active = Some(i) + } + }) + } + } else { + // *process_oce = true; + + println!("set prev none"); + *prev = None; + menu_state.scroll_offset = 0.0 } - Captured } - _ => Ignored - }.merge(status) + _ => () + } } - /// tree: Tree{menu_state, \[item_tree...]} - /// - /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} - pub(super) fn draw( + pub(super) fn open_event( &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, + tree: &mut Tree, layout: layout::Layout<'_>, cursor: mouse::Cursor, - viewport: &Rectangle, - ) { + ) -> event::Status { let mut lc = layout.children(); let items_layout = lc.next().unwrap(); + // let prescroll = lc.next().unwrap().bounds(); + // let offset_bounds = lc.next().unwrap().bounds(); + // let check_bounds = lc.next().unwrap().bounds(); + + let menu_state = tree.state.downcast_mut::(); + menu_state.active = None; - for ((item, tree), layout) in self.items // [item...] + for (i, (item, layout)) in self.items .iter() - .zip(tree.children.iter()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] + .zip(items_layout.children()) + .enumerate() { - item.draw( - tree, renderer, theme, style, layout, cursor, &viewport, - ); + if item.menu.is_some() && cursor.is_over(layout.bounds()) { + println!("new active: {}", i); + menu_state.active = Some(i); + return event::Status::Captured; + } } + event::Status::Ignored } -} + pub(super) fn close_event( + &self, + tree: &mut Tree, + layout: layout::Layout<'_>, + cursor: mouse::Cursor, + parent_bounds: Rectangle, + prev_bounds_list: &[Rectangle], + prev: &mut Index + ) { + let mut lc = layout.children(); + let _items_layout = lc.next().unwrap(); + let prescroll = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); + + let open = { + if cursor.is_over(prescroll) + || cursor.is_over(parent_bounds) + || cursor.is_over(offset_bounds) { + true + } else if prev_bounds_list.iter().any(|r| cursor.is_over(*r)) { + false + } else if cursor.is_over(check_bounds) { + true + } else { + false + } + }; -#[derive(Debug, Default)] -pub(super) struct ItemState{ - pub(super) open: bool + if !open { + *prev = None; + let menu_state = tree.state.downcast_mut::(); + menu_state.scroll_offset = 0.0 + } + } + } +// #[derive(Debug, Default)] +// pub(super) struct ItemState{ +// pub(super) open: bool +// } + /// menu item pub struct Item<'a, Message, Theme, Renderer> where - Renderer:renderer::Renderer + Renderer: renderer::Renderer, { pub(super) item: Element<'a, Message, Theme, Renderer>, pub(super) menu: Option>>, @@ -399,118 +503,91 @@ where #[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> where - Renderer:renderer::Renderer + Renderer: renderer::Renderer, { - pub fn new(item: impl Into>) -> Self{ - Self{ + pub fn new(item: impl Into>) -> Self { + Self { item: item.into(), menu: None, } } - pub fn with_menu(item: impl Into>, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ - Self{ + pub fn with_menu( + item: impl Into>, + menu: Menu<'a, Message, Theme, Renderer>, + ) -> Self { + Self { item: item.into(), menu: Some(Box::new(menu)), } } - pub fn menu(mut self, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ + pub fn menu(mut self, menu: Menu<'a, Message, Theme, Renderer>) -> Self { self.menu = Some(Box::new(menu)); self } - pub(super) fn tree(&self) -> Tree{ - Tree{ + pub(super) fn tree(&self) -> Tree { + Tree { tag: self.tag(), state: self.state(), children: self.children(), } } } -impl<'a, Message, Theme, Renderer> - // Widget for - Item<'a, Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> where - Renderer:renderer::Renderer + Renderer: renderer::Renderer, { pub(super) fn size(&self) -> Size { self.item.as_widget().size() } - + pub(super) fn tag(&self) -> tree::Tag { - tree::Tag::of::() + tree::Tag::stateless() } pub(super) fn state(&self) -> tree::State { - tree::State::Some(Box::new(ItemState::default())) + // tree::State::Some(Box::new(ItemState::default())) + tree::State::None } /// out: \[widget_tree, menu_tree] pub(super) fn children(&self) -> Vec { - self.menu.as_ref().map_or( - [ - Tree::new(&self.item), - ].into(), - |m|[ - Tree::new(&self.item), - m.tree() - ].into(), - ) + self.menu + .as_ref() + .map_or([Tree::new(&self.item)].into(), |m| { + [Tree::new(&self.item), m.tree()].into() + }) } - /// tree: Tree{item_state, \[widget_tree, menu_tree]} + /// tree: Tree{stateless, \[widget_tree, menu_tree]} pub(super) fn diff(&self, tree: &mut Tree) { tree.children[0].diff(&self.item); - self.menu.as_ref().map_or( - {}, - |m| { - m.diff(&mut tree.children[1]) - } - ) + self.menu + .as_ref() + .map_or({}, |m| m.diff(&mut tree.children[1])) } - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - /// - /// out: Node{size:0, \[widget_node, menu_node]} + /// tree: Tree{stateless, \[widget_tree, menu_tree]} + /// pub(super) fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, - viewport: &Rectangle, // translation: Vector, ) -> layout::Node { - let state = tree.state.downcast_ref::(); - let widget_node = self.item.as_widget().layout(&mut tree.children[0], renderer, limits); - let parent_bounds = widget_node.bounds(); - Node::with_children( - Size::ZERO, - [ - widget_node, - if state.open{ - self.menu.as_ref().unwrap().layout( - &mut tree.children[1], - renderer, - &Limits::NONE, - parent_bounds, - // translation, - viewport, - ) - }else{ - Node::default() - } - ].into() - ) + // println!("Item layout"); + self.item + .as_widget() + .layout(&mut tree.children[0], renderer, limits) } - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - /// - /// layout: Node{size:0, \[widget_node, menu_node]} + /// tree: Tree{stateless, \[widget_tree, menu_tree]} + /// pub(super) fn on_event( &mut self, - // index: usize, // within parent menu - // bar: &mut MenuBarState, tree: &mut Tree, event: Event, layout: layout::Layout<'_>, @@ -520,68 +597,21 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - let mut lc = layout.children(); - let widget_layout = lc.next().unwrap(); - let status = self.item.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - widget_layout, - cursor, - renderer, - clipboard, - shell, - viewport - ); - - let item_state = tree.state.downcast_mut::(); - - let Some(menu) = self.menu.as_ref() else { - item_state.open = false; - return status - }; - let menu_state = tree.children[1].state.downcast_mut::(); - let widget_bounds = widget_layout.bounds(); - - use event::Status::*; - match menu.open_condition{ - OpenCondition::Click => match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if cursor.is_over(widget_bounds) { - if item_state.open == false { - item_state.open = true; - menu_state.scroll_offset = 0.0; - // bar.indices.push(index); - } - Captured - }else{ - Ignored - } - } - _ => Ignored - } - OpenCondition::Hover => match event { - Event::Mouse(mouse::Event::CursorMoved { position }) => { - if widget_bounds.contains(position) { - if item_state.open == false { - item_state.open = true; - menu_state.scroll_offset = 0.0; - // bar.indices.push(index); - } - Captured - }else{ - Ignored - } - } - _ => Ignored - } - } - .merge(status) + // println!("item event"); + self.item.as_widget_mut().on_event( + &mut tree.children[0], + event, + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) } - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - /// - /// layout: Node{size:0, \[widget_node, menu_node]} + /// tree: Tree{stateless, \[widget_tree, menu_tree]} + /// pub(super) fn draw( &self, tree: &Tree, @@ -592,20 +622,18 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - let item_layout = layout.children().next().unwrap(); self.item.as_widget().draw( - tree, - renderer, - theme, - style, - item_layout, - cursor, - viewport + &tree.children[0], + renderer, + theme, + style, + layout, + cursor, + viewport, ) } } - /// Adaptive open direction #[derive(Debug)] #[allow(clippy::struct_excessive_bools)] @@ -627,7 +655,7 @@ struct Aod { vertical_offset: f32, } impl Aod { - /// Returns child position and offset position + /// Returns child position. offset position, child direction #[allow(clippy::too_many_arguments)] fn adaptive( parent_pos: f32, @@ -638,7 +666,7 @@ impl Aod { on: bool, overlap: bool, direction: Direction, - ) -> (f32, f32) { + ) -> (f32, f32, Direction) { /* Imagine there're two sticks, parent and child parent: o-----o @@ -676,16 +704,16 @@ impl Aod { if overlap { let overshoot = child_size - parent_size; if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - overshoot) + (parent_pos - overshoot, parent_pos - overshoot, direction.flip()) } else { - (parent_pos, parent_pos) + (parent_pos, parent_pos, direction) } } else { let overshoot = child_size + offset; if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - offset) + (parent_pos - overshoot, parent_pos - offset, direction.flip()) } else { - (parent_pos + parent_size + offset, parent_pos + parent_size) + (parent_pos + parent_size + offset, parent_pos + parent_size, direction) } } } @@ -696,16 +724,16 @@ impl Aod { if overlap { let overshoot = child_size - parent_size; if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos, parent_pos) + (parent_pos, parent_pos, direction.flip()) } else { - (parent_pos - overshoot, parent_pos - overshoot) + (parent_pos - overshoot, parent_pos - overshoot, direction) } } else { let overshoot = child_size + offset; if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos + parent_size + offset, parent_pos + parent_size) + (parent_pos + parent_size + offset, parent_pos + parent_size, direction.flip()) } else { - (parent_pos - overshoot, parent_pos - offset) + (parent_pos - overshoot, parent_pos - offset, direction) } } } @@ -718,8 +746,8 @@ impl Aod { parent_bounds: Rectangle, children_size: Size, viewport_size: Size, - ) -> (Point, Point) { - let (x, ox) = Self::adaptive( + ) -> (Point, Point, (Direction, Direction)) { + let (x, ox, dx) = Self::adaptive( parent_bounds.x, parent_bounds.width, children_size.width, @@ -729,7 +757,7 @@ impl Aod { self.horizontal_overlap, self.horizontal_direction, ); - let (y, oy) = Self::adaptive( + let (y, oy, dy) = Self::adaptive( parent_bounds.y, parent_bounds.height, children_size.height, @@ -740,7 +768,61 @@ impl Aod { self.vertical_direction, ); - ([x, y].into(), [ox, oy].into()) + ([x, y].into(), [ox, oy].into(), (dx, dy)) + } + + fn new( + axis: Axis, + viewport: Size, + parent_bounds: Rectangle, + parent_direction: (Direction, Direction), + offset: f32, + ) -> Self{ + let hcenter = viewport.width / 2.0; + let vcenter = viewport.height / 2.0; + + let phcenter = parent_bounds.x + parent_bounds.width / 2.0; + let pvcenter = parent_bounds.y + parent_bounds.height / 2.0; + + let (pdx, pdy) = parent_direction; + match axis { + Axis::Horizontal =>{ + let horizontal_direction = pdx; + let vertical_direction = if pvcenter < vcenter { + Direction::Positive + } else { + Direction::Negative + }; + Aod { + horizontal: true, + vertical: true, + horizontal_overlap: false, + vertical_overlap: true, + horizontal_direction, + vertical_direction, + horizontal_offset: offset, + vertical_offset: 0.0, + } + }, + Axis::Vertical => { + let horizontal_direction = if phcenter < hcenter { + Direction::Positive + } else { + Direction::Negative + }; + let vertical_direction = pdy; + Aod { + horizontal: true, + vertical: true, + horizontal_overlap: true, + vertical_overlap: false, + horizontal_direction, + vertical_direction, + horizontal_offset: 0.0, + vertical_offset: offset, + } + }, + } } } @@ -753,7 +835,6 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { } } - fn search_bound( default: usize, default_left: usize, @@ -787,7 +868,7 @@ fn process_scroll_event( prescroll_children_bounds: Rectangle, delta: mouse::ScrollDelta, viewport_size: Size, -) -> event::Status{ +){ use mouse::ScrollDelta; let pcb = prescroll_children_bounds; @@ -795,10 +876,8 @@ fn process_scroll_event( let delta_y = match delta { ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, }; - + let max_offset = (0.0 - pcb.y).max(0.0); let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); menu_state.scroll_offset = (menu_state.scroll_offset + delta_y).clamp(min_offset, max_offset); - - event::Status::Captured } diff --git a/src/native/menu/types.rs b/src/native/menu/types.rs index 12de4789..f0614675 100644 --- a/src/native/menu/types.rs +++ b/src/native/menu/types.rs @@ -2,7 +2,6 @@ use iced_widget::core::{overlay, renderer}; // use super::menu_tree_overlay::MenuTreeOverlay; - /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] pub struct CloseCondition { @@ -65,18 +64,36 @@ pub(super) enum Direction { Positive, Negative, } +impl Direction{ + pub(super) fn flip(&self) -> Direction{ + match self { + Direction::Positive => Direction::Negative, + Direction::Negative => Direction::Positive, + } + } +} /// Axis #[allow(missing_docs)] #[derive(Debug, Clone, Copy)] -pub enum Axis{ +pub enum Axis { Horizontal, Vertical, } #[allow(missing_docs)] -pub enum OpenCondition{ +pub enum OpenCondition { Hover, Click, } +pub(super) type Index = Option; + + +/// Should be returned from the recursive event processing function, +/// tells the caller which type of event has been processed +pub(super) enum RecEvent{ + Event, + Close, + None +} \ No newline at end of file From f0a55e4e697de434db4fa1afad9f904a01888309 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Mon, 12 Feb 2024 17:32:45 +0800 Subject: [PATCH 08/40] menu slice --- examples/menu/src/main.rs | 11 + src/native/menu/menu_bar_overlay.rs | 63 ++--- src/native/menu/menu_tree.rs | 380 +++++++++++++++++++--------- 3 files changed, 303 insertions(+), 151 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 3b043135..bb3aeb44 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -350,6 +350,17 @@ impl Application for App { .into(), ).max_width(180.0), ), + Item::with_menu( + debug_button("kjdfnh"), + Menu::new( + [ + Item::new(debug_button("dfths").width(Length::Fill).height(30.0)), + Item::new(debug_button("iodfns").width(Length::Fill).height(50.0)), + Item::new(debug_button("dfkmjk").width(Length::Fill).height(40.0)), + Item::new(debug_button("uvbw").width(Length::Fill).height(60.0)), + ].into() + ).max_width(180.0) + ) ] .into(), ); diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index c9284241..4ea09177 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -89,30 +89,31 @@ where menu_nodes: &mut Vec, parent_bounds: Rectangle, parent_direction: (Direction, Direction), - translation: Vector, + // translation: Vector, // parent_offset: Vector, viewport: &Rectangle, ) { let menu = item.menu.as_ref().unwrap(); let menu_tree = &mut tree.children[1]; - let (menu_node, direction) = menu.layout(menu_tree, renderer, &Limits::NONE, parent_bounds, parent_direction, translation, viewport); - // Node{inf, [ items_node, prescroll, offset_bounds, check_bounds ]} + let (menu_node, direction) = menu.layout(menu_tree, renderer, &Limits::NONE, parent_bounds, parent_direction, viewport); + // Node{inf, [ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} menu_nodes.push(menu_node); let menu_state = menu_tree.state.downcast_ref::(); + let slice = &menu_state.slice; if let Some(active) = menu_state.active{ let next_item = &menu.items[active]; let next_tree = &mut menu_tree.children[active]; - let items_node = &menu_nodes.last().unwrap().children()[0]; - let next_parent_bounds = items_node.children()[active].bounds() + - (items_node.bounds().position() - Point::ORIGIN); - // let next_parent_direction = - // println!("items bounds: {:?}", items_node.bounds()); - // println!("next parent bounds: {:?}", next_parent_bounds); - - rec(renderer, next_item, next_tree, menu_nodes, next_parent_bounds, direction, translation, viewport); + // let items_node = &menu_nodes.last().unwrap().children()[0]; + // let next_parent_bounds = items_node.children()[active].bounds() + + // (items_node.bounds().position() - Point::ORIGIN); + let slice_node = &menu_nodes.last().unwrap().children()[1]; + let next_parent_bounds = slice_node.children()[active - slice.start_index].bounds() + + (slice_node.bounds().position() - Point::ORIGIN); + + rec(renderer, next_item, next_tree, menu_nodes, next_parent_bounds, direction, viewport); } } @@ -146,7 +147,7 @@ where &mut menu_nodes, parent_bounds, parent_direction, - translation, + // translation, &Rectangle::new(position, bounds), ); @@ -182,10 +183,6 @@ where let bar = self.tree.state.downcast_mut::(); - // if cursor.is_over(bar_bounds){ - // return Ignored; - // } - let Some(active) = bar.active_root else { return Ignored; }; @@ -215,10 +212,11 @@ where let menu = item.menu.as_mut().expect("No menu defined in this item"); let menu_tree = &mut tree.children[1]; - let Some(menu_layout) = layout_iter.next() else { return RecEvent::None; }; // menu_node: Node{inf, [ items_node, prescroll, offset_bounds, check_bounds ]} + let Some(menu_layout) = layout_iter.next() else { return RecEvent::None; }; // menu_node: Node{inf, [ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} let mut mc = menu_layout.children(); let items_layout = mc.next().unwrap(); // items_node + let slice_layout = mc.next().unwrap(); // slice_node let prescroll = mc.next().unwrap().bounds(); prev_bounds_list.push(prescroll); @@ -227,7 +225,8 @@ where if let Some(active) = menu_state.active{ let next_tree = &mut menu_tree.children[active]; let next_item = &mut menu.items[active]; - let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); + // let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); + let next_parent_bounds = slice_layout.children().nth(active - menu_state.slice.start_index).unwrap().bounds(); let re = rec( next_tree, @@ -371,7 +370,7 @@ where let menu = item.menu.as_ref().expect("No menu defined in this item"); let menu_tree = &tree.children[1]; - let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ items_node, prescroll, offset_bounds, check_bounds ]} + let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} menu.draw( menu_tree, @@ -384,14 +383,18 @@ where parent_bounds ); - let items_layout = menu_layout.children().next().unwrap(); // items_node + let mut mc = menu_layout.children(); + let items_layout = mc.next().unwrap(); // items_node + let slice_layout = mc.next().unwrap(); // slice_node let menu_state = menu_tree.state.downcast_ref::(); if let Some(active) = menu_state.active{ let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; - let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); + // let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); + let next_parent_bounds = slice_layout.children().nth(active - menu_state.slice.start_index).unwrap().bounds(); + renderer.with_layer( *viewport, |r| rec( @@ -425,24 +428,10 @@ where fn is_over( &self, - layout: Layout<'_>, + _layout: Layout<'_>, _renderer: &Renderer, - cursor_position: Point + _cursor_position: Point ) -> bool { - // let viewport = layout.bounds(); - // let mut lc = layout.children(); - // let menu_layouts_layout = lc.next().unwrap(); // Node{0, [menu_node...]} - // let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] - // // let parent_bounds = lc.next().unwrap().bounds(); - - // let isover = menu_layouts.any(|l|{ - // let items_layout = l.children().next().unwrap(); - // items_layout.bounds().contains(cursor_position) - // }); - // // if isover{ - // // println!("is_over"); - // // } - // isover false } } diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index e94cada5..9d483f1f 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,6 +1,7 @@ //! doc //! -use std::{any::Any, borrow::BorrowMut}; +use core::slice; +use std::{any::Any, borrow::BorrowMut, iter::once}; use super::types::*; use super::{flex, menu_bar::MenuBarState}; @@ -49,12 +50,19 @@ Tree{ pub(super) struct MenuState { scroll_offset: f32, pub(super) active: Index, + pub(super) slice: MenuSlice, } impl Default for MenuState{ fn default() -> Self { Self { scroll_offset: 0.0, active: None, + slice: MenuSlice{ + start_index: 0, + end_index: usize::MAX, + lower_bound_rel: 0.0, + upper_bound_rel: f32::MAX, + } } } } @@ -147,7 +155,6 @@ where limits: &layout::Limits, parent_bounds: Rectangle, parent_direction: (Direction, Direction), - translation: Vector, viewport: &Rectangle, ) -> (layout::Node, (Direction, Direction)) { let limits = limits.max_width(self.max_width); @@ -187,7 +194,60 @@ where let bounds_expand = 30.0; let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); - let menu_state = tree.state.downcast_ref::(); + let menu_state = tree.state.downcast_mut::(); + + let slice = MenuSlice::new( + &items_node, + children_position-Point::ORIGIN, + viewport.size(), + menu_state.scroll_offset + ); + menu_state.slice = slice; + + let slice_node = { + let start_node = { + let node = &items_node.children()[slice.start_index]; + let bounds = node.bounds(); + let start_offset = slice.lower_bound_rel - bounds.y; + Node::with_children( + Size::new( + bounds.width, + bounds.height - start_offset + ), + node.children().iter().map(Clone::clone).collect() + ).move_to(bounds.position()) + .translate([0.0, start_offset]) + }; + + let end_node = { + let node = &items_node.children()[slice.end_index]; + let bounds = node.bounds(); + Node::with_children( + Size::new( + bounds.width, + slice.upper_bound_rel - bounds.y + ), + node.children().iter().map(Clone::clone).collect() + ).move_to(bounds.position()) + }; + + Node::with_children( + Size::new( + items_node.bounds().width, + slice.upper_bound_rel - slice.lower_bound_rel + ), + once(start_node) + .chain( + items_node.children()[ + slice.start_index + 1 .. slice.end_index + ] + .iter() + .map(Clone::clone) + ) + .chain(once(end_node)) + .collect() + ) + }; ( layout::Node::with_children( @@ -196,14 +256,16 @@ where items_node .move_to(children_position) .translate([0.0, menu_state.scroll_offset]), // items layout + slice_node + .move_to(children_position) + .translate([0.0, menu_state.scroll_offset]), // slice layout layout::Node::new(children_size) .move_to(children_position), // prescroll bounds layout::Node::new(offset_bounds.size()) .move_to(offset_bounds.position()), // offset boundss layout::Node::new(check_bounds.size()) .move_to(check_bounds.position()), // check bounds - ] - .into(), + ].into(), ), child_direction ) @@ -211,7 +273,7 @@ where /// tree: Tree{menu_state, \[item_tree...]} /// - /// layout: Node{inf, \[ items_node, prescroll, offset_boundss, check_bounds ]} + /// layout: Node{inf, \[ items_node, slice_node, prescroll, offset_boundss, check_bounds ]} pub(super) fn on_event( &mut self, tree: &mut Tree, @@ -225,17 +287,20 @@ where ) -> event::Status { // println!("menu event"); let mut lc = layout.children(); - let items_layout = lc.next().unwrap(); - + let _items_layout = lc.next().unwrap(); + let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); + let menu_state = tree.state.downcast_mut::(); + let slice = &menu_state.slice; + let status = self - .items // [item...] + .items[ slice.start_index .. slice.end_index+1 ] // [item...] .iter_mut() - .zip(tree.children.iter_mut()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] + .zip(tree.children[ slice.start_index .. slice.end_index+1 ].iter_mut()) // [item_tree...] + .zip(slice_layout.children()) // [item_layout...] .map(|((item, tree), layout)| { item.on_event( tree, @@ -250,12 +315,10 @@ where }) .fold(event::Status::Ignored, event::Status::merge); - let menu_state = tree.state.downcast_mut::(); - use event::Status::*; match event { Event::Mouse(mouse::Event::WheelScrolled { delta }) => { - if cursor.is_over(items_layout.bounds()) { + if cursor.is_over(prescroll) { process_scroll_event(menu_state, prescroll, delta, viewport.size()); Captured } else if cursor.is_over(offset_bounds) || cursor.is_over(check_bounds) { @@ -271,7 +334,7 @@ where /// tree: Tree{menu_state, \[item_tree...]} /// - /// layout: Node{inf, \[ items_node, prescroll, offset_bounds, check_bounds ]} + /// layout: Node{inf, \[ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} pub(super) fn draw( &self, tree: &Tree, @@ -284,7 +347,8 @@ where parent_bounds: Rectangle, ) { let mut lc = layout.children(); - let items_layout = lc.next().unwrap(); + let _items_layout = lc.next().unwrap(); + let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); @@ -292,6 +356,10 @@ where // println!("prescroll: {:?}", prescroll); // println!("parent_bounds: {:?}", parent_bounds); + let menu_state = tree.state.downcast_ref::(); + let slice = &menu_state.slice; + println!("slice: {:?}", slice); + renderer.fill_quad( renderer::Quad{ bounds: check_bounds, @@ -305,6 +373,22 @@ where }, Color::from([1.0, 0.0, 0.0, 0.1]) ); + + // slice_layout.children().for_each(|l|{ + // renderer.fill_quad( + // renderer::Quad{ + // bounds: l.bounds(), + // border: Border{ + // // color: todo!(), + // // width: todo!(), + // radius: 6.0.into(), + // ..Default::default() + // }, + // ..Default::default() + // }, + // Color::from([1.0, 1.0, 1.0, 1.0]) + // ); + // }); renderer.fill_quad( renderer::Quad{ bounds: prescroll, @@ -318,6 +402,7 @@ where }, Color::from([1.0, 1.0, 1.0, 1.0]) ); + renderer.fill_quad( renderer::Quad{ bounds: offset_bounds, @@ -346,78 +431,22 @@ where Color::from([1.0, 1.0, 0.0, 0.5]) ); + for ((item, tree), layout) in self - .items // [item...] - .iter() - .zip(tree.children.iter()) // [item_tree...] - .zip(items_layout.children()) - // [item_layout...] + .items[ slice.start_index .. slice.end_index+1 ].iter() // [item...].iter() + .zip(tree.children[ slice.start_index .. slice.end_index+1 ].iter()) // [item_tree...] + .zip(slice_layout.children()) // [item_layout...] { item.draw(tree, renderer, theme, style, layout, cursor, &viewport); } - } - - /// layout: Node{inf, \[ items_node, prescroll, offset_boundss, check_bounds ]} - pub(super) fn open_or_close_event( - &mut self, - // process_oce: &mut bool, - prev: &mut Index, - tree: &mut Tree, - event: Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - parent_bounds: Rectangle, - prev_bounds_list: &[Rectangle], - ){ - let mut lc = layout.children(); - let items_layout = lc.next().unwrap(); - let prescroll = lc.next().unwrap().bounds(); - let offset_bounds = lc.next().unwrap().bounds(); - let check_bounds = lc.next().unwrap().bounds(); - - let menu_state = tree.state.downcast_mut::(); - - match event { - Event::Mouse(mouse::Event::CursorMoved { .. }) => { - let open = { - if cursor.is_over(prescroll) - || cursor.is_over(parent_bounds) - || cursor.is_over(offset_bounds) { - true - } else if prev_bounds_list.iter().any(|r| cursor.is_over(*r)) { - false - } else if cursor.is_over(check_bounds) { - true - } else { - false - } - }; - - if open { - if !cursor.is_over(prescroll) { - menu_state.active = None - } else { - self.items - .iter() - .zip(items_layout.children()) - .enumerate() - .for_each(|(i, (item, layout))|{ - if item.menu.is_some() && cursor.is_over(layout.bounds()) { - println!("new active: {}", i); - menu_state.active = Some(i) - } - }) - } - } else { - // *process_oce = true; - - println!("set prev none"); - *prev = None; - menu_state.scroll_offset = 0.0 - } - } - _ => () - } + // for ((item, tree), layout) in self + // .items // [item...] + // .iter() + // .zip(tree.children.iter()) // [item_tree...] + // .zip(items_layout.children())// [item_layout...] + // { + // item.draw(tree, renderer, theme, style, layout, cursor, &viewport); + // } } pub(super) fn open_event( @@ -427,22 +456,24 @@ where cursor: mouse::Cursor, ) -> event::Status { let mut lc = layout.children(); - let items_layout = lc.next().unwrap(); + let _items_layout = lc.next().unwrap(); + let slice_layout = lc.next().unwrap(); // let prescroll = lc.next().unwrap().bounds(); // let offset_bounds = lc.next().unwrap().bounds(); // let check_bounds = lc.next().unwrap().bounds(); let menu_state = tree.state.downcast_mut::(); + let slice = &menu_state.slice; menu_state.active = None; - for (i, (item, layout)) in self.items + for (i, (item, layout)) in self.items[slice.start_index .. slice.end_index + 1] .iter() - .zip(items_layout.children()) + .zip(slice_layout.children()) .enumerate() { if item.menu.is_some() && cursor.is_over(layout.bounds()) { println!("new active: {}", i); - menu_state.active = Some(i); + menu_state.active = Some(i + slice.start_index); return event::Status::Captured; } } @@ -460,6 +491,7 @@ where ) { let mut lc = layout.children(); let _items_layout = lc.next().unwrap(); + let _slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); @@ -487,11 +519,6 @@ where } -// #[derive(Debug, Default)] -// pub(super) struct ItemState{ -// pub(super) open: bool -// } - /// menu item pub struct Item<'a, Message, Theme, Renderer> where @@ -576,7 +603,6 @@ where tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, - // translation: Vector, ) -> layout::Node { // println!("Item layout"); self.item @@ -835,7 +861,151 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { } } -fn search_bound( +fn process_scroll_event( + menu_state: &mut MenuState, + prescroll_children_bounds: Rectangle, + delta: mouse::ScrollDelta, + viewport_size: Size, +){ + use mouse::ScrollDelta; + + let pcb = prescroll_children_bounds; + + let delta_y = match delta { + ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, + }; + + let max_offset = (0.0 - pcb.y).max(0.0); + let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); + menu_state.scroll_offset = (menu_state.scroll_offset + delta_y).clamp(min_offset, max_offset); +} + +#[derive(Debug, Clone, Copy)] +pub(super) struct MenuSlice { + pub(super) start_index: usize, + pub(super) end_index: usize, + pub(super) lower_bound_rel: f32, + pub(super) upper_bound_rel: f32, +} +impl MenuSlice{ + fn new( + items_node: &Node, + translation: Vector, + viewport: Size, + scroll_offset: f32, + ) -> Self { + let items_bounds = items_node.bounds() + translation; + let max_index = items_node.children().len().saturating_sub(1); + + // viewport space absolute bounds + let lower_bound = items_bounds.y.max(0.0); + let upper_bound = (items_bounds.y + items_bounds.height).min(viewport.height); + + // menu space relative bounds + let lower_bound_rel = lower_bound - (items_bounds.y + scroll_offset); + let upper_bound_rel = upper_bound - (items_bounds.y + scroll_offset); + + let start_index = items_node.children().iter().enumerate().find_map(|(i, n)|{ + let bounds = n.bounds(); + (bounds.y <= lower_bound_rel && bounds.y + bounds.height >= lower_bound_rel).then_some(i) + }).unwrap_or(0); + + let end_index = items_node.children().iter().enumerate().find_map(|(i, n)|{ + let bounds = n.bounds(); + (bounds.y <= upper_bound_rel && bounds.y + bounds.height >= upper_bound_rel).then_some(i) + }).unwrap_or(max_index); + + // let start_index = search_bound( + // 0, + // 0, + // max_index, + // lower_bound_rel, + // items_node.children(), + // |n| n.bounds().y, + // |n| n.bounds().height + // ); + // let end_index = search_bound( + // max_index, + // start_index, + // max_index, + // upper_bound_rel, + // items_node.children(), + // |n| n.bounds().y, + // |n| n.bounds().height + // ); + + Self { + start_index, + end_index, + lower_bound_rel, + upper_bound_rel, + } + } +} + +fn search_bound( + default: usize, + default_left: usize, + default_right: usize, + bound: f32, + list: &[T], + get_position: impl Fn(&T) -> f32, + get_size: impl Fn(&T) -> f32, +) -> usize { + // binary search + let mut left = default_left; + let mut right = default_right; + + let mut index = default; + while left != right { + let m = ((left + right) / 2) + 1; + if get_position(&list[m]) > bound { + right = m - 1; + } else { + left = m; + } + } + let height = get_size(&list[left]); + if get_position(&list[left]) + height > bound { + index = left; + } + index +} + +/* fn slice( + // children_bounds: Rectangle, + items_node: &Node, + scroll_offset: f32, + viewport: Size, + translation: Vector, +) -> MenuSlice { + let items_bounds = items_node.bounds() + translation; + let max_index = items_node.children().len().saturating_sub(1); + + // viewport space absolute bounds + let lower_bound = items_bounds.y.max(0.0); + let upper_bound = (items_bounds.y + items_bounds.height).min(viewport.height); + + // menu space relative bounds + let lower_bound_rel = lower_bound - (items_bounds.y + scroll_offset); + let upper_bound_rel = upper_bound - (items_bounds.y + scroll_offset); + + // index range + // let positions = &child_positions; + // let sizes = &child_sizes; + + // let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); + // let end_index = search_bound(max_index, start_index, max_index, upper_bound_rel, positions, sizes).min(max_index); + + MenuSlice { + start_index, + end_index, + lower_bound_rel, + upper_bound_rel, + } +} */ + +/* fn search_bound( default: usize, default_left: usize, default_right: usize, @@ -861,23 +1031,5 @@ fn search_bound( index = left; } index -} - -fn process_scroll_event( - menu_state: &mut MenuState, - prescroll_children_bounds: Rectangle, - delta: mouse::ScrollDelta, - viewport_size: Size, -){ - use mouse::ScrollDelta; - - let pcb = prescroll_children_bounds; +} */ - let delta_y = match delta { - ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, - }; - - let max_offset = (0.0 - pcb.y).max(0.0); - let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); - menu_state.scroll_offset = (menu_state.scroll_offset + delta_y).clamp(min_offset, max_offset); -} From 9f5cb369bdf488d9d8d0b210ca5c0bf7402dc1fb Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Mon, 12 Feb 2024 18:18:36 +0800 Subject: [PATCH 09/40] remove items_layout --- src/native/menu/bk/menu_bar.rs | 85 ++++++++++-------------- src/native/menu/bk2/menu_tree_overlay.rs | 10 +-- src/native/menu/menu_bar_overlay.rs | 45 ++++++++----- src/native/menu/menu_tree.rs | 20 +----- 4 files changed, 67 insertions(+), 93 deletions(-) diff --git a/src/native/menu/bk/menu_bar.rs b/src/native/menu/bk/menu_bar.rs index b0c38c10..2f41d005 100644 --- a/src/native/menu/bk/menu_bar.rs +++ b/src/native/menu/bk/menu_bar.rs @@ -1,13 +1,8 @@ //! A widget that handles menu trees use super::{ - types::{ - // Menu, - // Direction, - CloseCondition, - ItemHeight, ItemWidth, - PathHighlight, + menu_inner::{ + CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight, }, - menu_inner::MenuState, menu_tree::MenuTree, }; use crate::style::menu_bar::StyleSheet; @@ -28,23 +23,23 @@ pub(super) struct MenuBarState { pub(super) view_cursor: Cursor, pub(super) open: bool, pub(super) active_root: Option, - // pub(super) horizontal_direction: Direction, - // pub(super) vertical_direction: Direction, + pub(super) horizontal_direction: Direction, + pub(super) vertical_direction: Direction, pub(super) menu_states: Vec, } impl MenuBarState { - /* pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { + pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { self.menu_states .iter() .take_while(|ms| ms.index.is_some()) .map(|ms| ms.index.expect("No indices were found in the menu state.")) - } */ + } - /* pub(super) fn reset(&mut self) { + pub(super) fn reset(&mut self) { self.open = false; self.active_root = None; self.menu_states.clear(); - } */ + } } impl Default for MenuBarState { fn default() -> Self { @@ -53,8 +48,8 @@ impl Default for MenuBarState { view_cursor: Cursor::Available([-0.5, -0.5].into()), open: false, active_root: None, - // horizontal_direction: Direction::Positive, - // vertical_direction: Direction::Positive, + horizontal_direction: Direction::Positive, + vertical_direction: Direction::Positive, menu_states: Vec::new(), } } @@ -94,7 +89,7 @@ where let mut menu_roots = menu_roots; menu_roots.iter_mut().for_each(MenuTree::set_index); - let mb = Self { + Self { width: Length::Shrink, height: Length::Shrink, spacing: 0.0, @@ -112,9 +107,7 @@ where path_highlight: Some(PathHighlight::MenuActive), menu_roots, style: Theme::Style::default(), - }; - println!("new mb"); - mb + } } /// Sets the expand value for each menu's check bounds @@ -290,11 +283,6 @@ where .iter() .map(|root| &root.item) .collect::>(); - let mut tree_children = tree - .children - .iter_mut() - .map(|t| &mut t.children[0]) - .collect::>(); flex::resolve( flex::Axis::Horizontal, renderer, @@ -305,7 +293,7 @@ where self.spacing, Alignment::Center, &children, - tree_children.as_mut_slice() + &mut tree.children ) } @@ -320,7 +308,6 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - println!("mb on_event"); use event::Event::{Mouse, Touch}; use mouse::{Button::Left, Event::ButtonReleased}; use touch::Event::{FingerLifted, FingerLost}; @@ -361,10 +348,7 @@ where view_cursor: Cursor, viewport: &Rectangle, ) { - println!("mb draw"); let state = tree.state.downcast_ref::(); - println!("mb draw downcast"); - let cursor_pos = view_cursor.position().unwrap_or_default(); let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) { state.view_cursor @@ -383,6 +367,9 @@ where .bounds(); let path_quad = renderer::Quad { bounds: active_bounds, + // border_radius: styling.border_radius.into(), + // border_width: 0.0, + // border_color: Color::TRANSPARENT, border: Border{ color: Color::TRANSPARENT, width: 0.0, @@ -422,33 +409,33 @@ where if !state.open { return None; } - None - // Some( - // Menu { - // tree, - // menu_roots: &mut self.menu_roots, - // bounds_expand: self.bounds_expand, - // close_condition: self.close_condition, - // item_width: self.item_width, - // item_height: self.item_height, - // bar_bounds: layout.bounds(), - // main_offset: self.main_offset, - // cross_offset: self.cross_offset, - // root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), - // path_highlight: self.path_highlight, - // style: &self.style, - // } - // .overlay(), - // ) + + Some( + Menu { + tree, + menu_roots: &mut self.menu_roots, + bounds_expand: self.bounds_expand, + close_condition: self.close_condition, + item_width: self.item_width, + item_height: self.item_height, + bar_bounds: layout.bounds(), + main_offset: self.main_offset, + cross_offset: self.cross_offset, + root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), + path_highlight: self.path_highlight, + style: &self.style, + } + .overlay(), + ) } } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where - Message: 'a + Clone, + Message: 'a, Renderer: 'a + renderer::Renderer, - Theme: 'a + StyleSheet, + Theme: StyleSheet, { fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { Self::new(value) diff --git a/src/native/menu/bk2/menu_tree_overlay.rs b/src/native/menu/bk2/menu_tree_overlay.rs index 071904a1..88f21277 100644 --- a/src/native/menu/bk2/menu_tree_overlay.rs +++ b/src/native/menu/bk2/menu_tree_overlay.rs @@ -12,16 +12,8 @@ where { pub(super) state: &'b mut MenuTreeState, pub(super) tree: &'b mut Tree, - // pub(super) children: &'b mut [MenuTree<'a, Message, Theme, Renderer>], - pub(super) parent_bounds: Rectangle, pub(super) menu_tree: &'b MenuTree<'a, Message, Theme, Renderer>, - // pub(super) max_width: f32, - // pub(super) spacing: f32, - // pub(super) padding: Padding, - // pub(super) width: Length, - // pub(super) height: Length, - // pub(super) axis: Axis, - // pub(super) offset: f32, + pub(super) parent_bounds: Rectangle, } impl<'a, 'b, Message, Theme, Renderer> MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> where diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 4ea09177..e2031312 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -97,7 +97,7 @@ where let menu_tree = &mut tree.children[1]; let (menu_node, direction) = menu.layout(menu_tree, renderer, &Limits::NONE, parent_bounds, parent_direction, viewport); - // Node{inf, [ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} + // Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} menu_nodes.push(menu_node); let menu_state = menu_tree.state.downcast_ref::(); @@ -106,10 +106,7 @@ where if let Some(active) = menu_state.active{ let next_item = &menu.items[active]; let next_tree = &mut menu_tree.children[active]; - // let items_node = &menu_nodes.last().unwrap().children()[0]; - // let next_parent_bounds = items_node.children()[active].bounds() + - // (items_node.bounds().position() - Point::ORIGIN); - let slice_node = &menu_nodes.last().unwrap().children()[1]; + let slice_node = &menu_nodes.last().unwrap().children()[0]; let next_parent_bounds = slice_node.children()[active - slice.start_index].bounds() + (slice_node.bounds().position() - Point::ORIGIN); @@ -212,12 +209,12 @@ where let menu = item.menu.as_mut().expect("No menu defined in this item"); let menu_tree = &mut tree.children[1]; - let Some(menu_layout) = layout_iter.next() else { return RecEvent::None; }; // menu_node: Node{inf, [ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} + let Some(menu_layout) = layout_iter.next() else { return RecEvent::None; }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} let mut mc = menu_layout.children(); - let items_layout = mc.next().unwrap(); // items_node let slice_layout = mc.next().unwrap(); // slice_node let prescroll = mc.next().unwrap().bounds(); + let offset_bounds = mc.next().unwrap().bounds(); prev_bounds_list.push(prescroll); let menu_state = menu_tree.state.downcast_mut::(); @@ -225,7 +222,6 @@ where if let Some(active) = menu_state.active{ let next_tree = &mut menu_tree.children[active]; let next_item = &mut menu.items[active]; - // let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); let next_parent_bounds = slice_layout.children().nth(active - menu_state.slice.start_index).unwrap().bounds(); let re = rec( @@ -250,7 +246,11 @@ where RecEvent::Close => { menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); if prev.is_some(){ - RecEvent::None + if cursor.is_over(offset_bounds){ + RecEvent::Event + }else{ + RecEvent::None + } }else{ RecEvent::Close } @@ -275,7 +275,11 @@ where }else{ menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); if prev.is_some(){ - RecEvent::None + if cursor.is_over(offset_bounds){ + RecEvent::Event + }else{ + RecEvent::None + } }else{ RecEvent::Close } @@ -283,7 +287,7 @@ where } } - rec( + let re = rec( active_tree, active_root, event, @@ -298,10 +302,17 @@ where &mut bar.active_root, ); - if cursor.is_over(bar_bounds){ - Ignored - }else{ - Captured + match re { + RecEvent::Event => { + Captured + }, + RecEvent::Close | RecEvent::None => { + if cursor.is_over(bar_bounds){ + Ignored + }else{ + Captured + } + }, } } @@ -370,7 +381,7 @@ where let menu = item.menu.as_ref().expect("No menu defined in this item"); let menu_tree = &tree.children[1]; - let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} + let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} menu.draw( menu_tree, @@ -384,7 +395,6 @@ where ); let mut mc = menu_layout.children(); - let items_layout = mc.next().unwrap(); // items_node let slice_layout = mc.next().unwrap(); // slice_node let menu_state = menu_tree.state.downcast_ref::(); @@ -392,7 +402,6 @@ where if let Some(active) = menu_state.active{ let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; - // let next_parent_bounds = items_layout.children().nth(active).unwrap().bounds(); let next_parent_bounds = slice_layout.children().nth(active - menu_state.slice.start_index).unwrap().bounds(); renderer.with_layer( diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 9d483f1f..0b292d63 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -253,9 +253,6 @@ where layout::Node::with_children( Size::INFINITY, [ - items_node - .move_to(children_position) - .translate([0.0, menu_state.scroll_offset]), // items layout slice_node .move_to(children_position) .translate([0.0, menu_state.scroll_offset]), // slice layout @@ -273,7 +270,7 @@ where /// tree: Tree{menu_state, \[item_tree...]} /// - /// layout: Node{inf, \[ items_node, slice_node, prescroll, offset_boundss, check_bounds ]} + /// layout: Node{inf, \[ slice_node, prescroll, offset_boundss, check_bounds ]} pub(super) fn on_event( &mut self, tree: &mut Tree, @@ -287,7 +284,6 @@ where ) -> event::Status { // println!("menu event"); let mut lc = layout.children(); - let _items_layout = lc.next().unwrap(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); @@ -347,7 +343,6 @@ where parent_bounds: Rectangle, ) { let mut lc = layout.children(); - let _items_layout = lc.next().unwrap(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); @@ -439,14 +434,6 @@ where { item.draw(tree, renderer, theme, style, layout, cursor, &viewport); } - // for ((item, tree), layout) in self - // .items // [item...] - // .iter() - // .zip(tree.children.iter()) // [item_tree...] - // .zip(items_layout.children())// [item_layout...] - // { - // item.draw(tree, renderer, theme, style, layout, cursor, &viewport); - // } } pub(super) fn open_event( @@ -456,7 +443,6 @@ where cursor: mouse::Cursor, ) -> event::Status { let mut lc = layout.children(); - let _items_layout = lc.next().unwrap(); let slice_layout = lc.next().unwrap(); // let prescroll = lc.next().unwrap().bounds(); // let offset_bounds = lc.next().unwrap().bounds(); @@ -490,7 +476,6 @@ where prev: &mut Index ) { let mut lc = layout.children(); - let _items_layout = lc.next().unwrap(); let _slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); let offset_bounds = lc.next().unwrap().bounds(); @@ -513,7 +498,8 @@ where if !open { *prev = None; let menu_state = tree.state.downcast_mut::(); - menu_state.scroll_offset = 0.0 + menu_state.scroll_offset = 0.0; + menu_state.active = None; } } From fce5b5f6a2e1609cacb96468e9ec247c5e99810d Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 16 Feb 2024 10:28:35 +0800 Subject: [PATCH 10/40] public methods and docs --- src/native/menu/menu_bar.rs | 42 +++++-- src/native/menu/menu_bar_overlay.rs | 39 +++--- src/native/menu/menu_tree.rs | 179 ++++++++++------------------ src/native/menu/types.rs | 51 +------- 4 files changed, 111 insertions(+), 200 deletions(-) diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index c4776d2e..c0eca226 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,9 +1,9 @@ -//! menu bar +//! [`MenuBar`] use iced_widget::core::{ alignment, event, - layout::{self, Limits, Node}, - mouse, overlay, renderer, touch, + layout::{Limits, Node}, + mouse, overlay, renderer, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Point, Rectangle, Shell, Size, Widget, @@ -15,8 +15,6 @@ pub(super) struct MenuBarState { pub(super) active_root: Index, pub(super) open: bool, pub(super) is_pressed: bool, - // pub(super) viewport: Rectangle, - // pub(super) indices: Vec, } impl Default for MenuBarState { fn default() -> Self { @@ -24,8 +22,6 @@ impl Default for MenuBarState { active_root: None, open: false, is_pressed: false, - // viewport: Rectangle::default(), - // indices: Vec::new(), } } } @@ -41,11 +37,11 @@ where width: Length, height: Length, } -#[allow(missing_docs)] impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { + /// Creates a [`MenuBar`] with the given root items. pub fn new(mut roots: Vec>) -> Self { roots.iter_mut().for_each(|i|{ if let Some(m) = i.menu.as_mut(){ @@ -60,6 +56,31 @@ where height: Length::Shrink, } } + + /// Sets the width of the [`MenuBar`]. + pub fn width(mut self, width: impl Into) -> Self{ + self.width = width.into(); + self + } + + /// Sets the height of the [`MenuBar`]. + pub fn height(mut self, height: impl Into) -> Self{ + self.height = height.into(); + self + } + + /// Sets the spacing of the [`MenuBar`]. + pub fn spacing(mut self, spacing: f32) -> Self{ + self.spacing = spacing; + self + } + + /// Sets the padding of the [`MenuBar`]. + pub fn padding(mut self, padding: impl Into) -> Self{ + self.padding = padding.into(); + self + } + } impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> @@ -102,8 +123,8 @@ where &self, tree: &mut Tree, renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { + limits: &Limits, + ) -> Node { // println!("bar layout"); flex::resolve( flex::Axis::Horizontal, @@ -192,7 +213,6 @@ where break; } } - // println!("mbo bar.active: {:?}", bar.active_root) }else{ bar.open = false } diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index e2031312..99d0a937 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -1,30 +1,32 @@ -use iced_widget::{core::{ +use iced_widget::core::{ event, - layout::{self, Limits, Node}, - mouse, overlay, renderer, touch, - widget::tree::{self, Tree}, + layout::{Limits, Node}, + mouse, overlay, renderer, + widget::tree::Tree, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget, -}, shader::wgpu::naga::Barrier}; +}; use super::{menu_bar::MenuBarState, menu_tree::*, types::*}; +/// Should be returned from the recursive event processing function, +/// tells the caller which type of event has been processed +enum RecEvent{ + Event, + Close, + None +} + pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where Renderer: renderer::Renderer, { - // pub(super) active: usize, /// Tree{ bar_state, [item_tree...] } pub(super) tree: &'b mut Tree, - // pub(super) bar: &'b mut MenuBarState, - // pub(super) active_tree: &'b mut Tree, - // pub(super) active_root: &'b mut Item<'a, Message, Theme, Renderer>, - pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], - // pub(super) bar_layout: Layout<'b>, + pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], pub(super) init_bar_bounds: Rectangle, pub(super) init_root_bounds: Vec, - // pub(super) init_parent_bounds: Option, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where @@ -46,13 +48,11 @@ where position: Point, translation: Vector, ) -> Node { - println!(); - println!("mbo layout"); - // println!("translation: {:?}", translation); + // println!(); + // println!("mbo layout"); let bar = self.tree.state.downcast_ref::(); let bar_bounds = self.init_bar_bounds; - // println!("bar bounds: {:?}", bar_bounds); let bar_node = Node::with_children( bar_bounds.size(), @@ -67,7 +67,7 @@ where ).translate(translation); let Some(active) = bar.active_root else { - println!("no active"); + // println!("no active"); return Node::with_children( bounds, [ @@ -80,7 +80,6 @@ where let active_root = &mut self.roots[active]; let active_tree = &mut self.tree.children[active]; // item_tree: Tree{ stateless, [ widget_tree, menu_tree ] } let parent_bounds = self.init_root_bounds[active] + translation; - // println!("parent bounds: {:?}", parent_bounds); fn rec<'a, Message, Theme, Renderer: renderer::Renderer>( renderer: &Renderer, @@ -170,8 +169,8 @@ where ) -> event::Status { use event::Status::*; - println!(); - println!("mbo event"); + // println!(); + // println!("mbo event"); let viewport = layout.bounds(); let mut lc = layout.children(); diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 0b292d63..50d1c6e8 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,21 +1,18 @@ -//! doc +//! [`Item`] and [`Menu`] //! -use core::slice; -use std::{any::Any, borrow::BorrowMut, iter::once}; - +use std::iter::once; use super::types::*; -use super::{flex, menu_bar::MenuBarState}; +use super::flex; use iced_widget::core::{ alignment, event, - layout::{self, Limits, Node}, - mouse, overlay, renderer, touch, + layout::{Limits, Node, Layout}, + mouse, overlay, renderer, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget, }; /* - menu tree: Item{ widget @@ -67,7 +64,7 @@ impl Default for MenuState{ } } -/// menu +/// Menu pub struct Menu<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, @@ -80,13 +77,13 @@ where pub(super) height: Length, pub(super) axis: Axis, pub(super) offset: f32, - pub(super) open_condition: OpenCondition, } #[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { + /// Creates a [`Menu`] with the given items. pub fn new(items: Vec>) -> Self { Self { items, @@ -96,23 +93,47 @@ where width: Length::Fill, height: Length::Shrink, axis: Axis::Horizontal, - offset: 20.0, - open_condition: OpenCondition::Click, + offset: 0.0, } } - pub fn tree(&self) -> Tree { + /// Sets the maximum width of the [`Menu`]. + pub fn max_width(mut self, max_width: f32) -> Self{ + self.max_width = max_width; + self + } + + /// Sets the width of the [`Menu`]. + pub fn width(mut self, width: impl Into) -> Self{ + self.width = width.into(); + self + } + + /// Sets the spacing of the [`Menu`]. + pub fn spacing(mut self, spacing: f32) -> Self{ + self.spacing = spacing; + self + } + + /// Sets the padding of the [`Menu`]. + pub fn padding(mut self, padding: impl Into) -> Self{ + self.padding = padding.into(); + self + } + + /// The offset from the bounds of the menu's parent item. + pub fn offset(mut self, offset: f32) -> Self{ + self.offset = offset; + self + } + + pub(super) fn tree(&self) -> Tree { Tree { tag: self.tag(), state: self.state(), children: self.children(), } } - - pub fn max_width(mut self, max_width: f32) -> Self{ - self.max_width = max_width; - self - } } impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where @@ -152,11 +173,11 @@ where &self, tree: &mut Tree, renderer: &Renderer, - limits: &layout::Limits, + limits: &Limits, parent_bounds: Rectangle, parent_direction: (Direction, Direction), viewport: &Rectangle, - ) -> (layout::Node, (Direction, Direction)) { + ) -> (Node, (Direction, Direction)) { let limits = limits.max_width(self.max_width); let items_node = flex::resolve( @@ -250,24 +271,23 @@ where }; ( - layout::Node::with_children( + Node::with_children( Size::INFINITY, [ slice_node .move_to(children_position) .translate([0.0, menu_state.scroll_offset]), // slice layout - layout::Node::new(children_size) + Node::new(children_size) .move_to(children_position), // prescroll bounds - layout::Node::new(offset_bounds.size()) + Node::new(offset_bounds.size()) .move_to(offset_bounds.position()), // offset boundss - layout::Node::new(check_bounds.size()) + Node::new(check_bounds.size()) .move_to(check_bounds.position()), // check bounds ].into(), ), child_direction ) } - /// tree: Tree{menu_state, \[item_tree...]} /// /// layout: Node{inf, \[ slice_node, prescroll, offset_boundss, check_bounds ]} @@ -275,7 +295,7 @@ where &mut self, tree: &mut Tree, event: Event, - layout: layout::Layout<'_>, + layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, @@ -337,7 +357,7 @@ where renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, - layout: layout::Layout<'_>, + layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, parent_bounds: Rectangle, @@ -353,7 +373,7 @@ where let menu_state = tree.state.downcast_ref::(); let slice = &menu_state.slice; - println!("slice: {:?}", slice); + // println!("slice: {:?}", slice); renderer.fill_quad( renderer::Quad{ @@ -369,21 +389,6 @@ where Color::from([1.0, 0.0, 0.0, 0.1]) ); - // slice_layout.children().for_each(|l|{ - // renderer.fill_quad( - // renderer::Quad{ - // bounds: l.bounds(), - // border: Border{ - // // color: todo!(), - // // width: todo!(), - // radius: 6.0.into(), - // ..Default::default() - // }, - // ..Default::default() - // }, - // Color::from([1.0, 1.0, 1.0, 1.0]) - // ); - // }); renderer.fill_quad( renderer::Quad{ bounds: prescroll, @@ -439,7 +444,7 @@ where pub(super) fn open_event( &self, tree: &mut Tree, - layout: layout::Layout<'_>, + layout: Layout<'_>, cursor: mouse::Cursor, ) -> event::Status { let mut lc = layout.children(); @@ -458,7 +463,7 @@ where .enumerate() { if item.menu.is_some() && cursor.is_over(layout.bounds()) { - println!("new active: {}", i); + // println!("new active: {}", i); menu_state.active = Some(i + slice.start_index); return event::Status::Captured; } @@ -469,7 +474,7 @@ where pub(super) fn close_event( &self, tree: &mut Tree, - layout: layout::Layout<'_>, + layout: Layout<'_>, cursor: mouse::Cursor, parent_bounds: Rectangle, prev_bounds_list: &[Rectangle], @@ -505,7 +510,7 @@ where } -/// menu item +/// Item inside a [`Menu`] pub struct Item<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, @@ -518,6 +523,7 @@ impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> where Renderer: renderer::Renderer, { + /// Creates an [`Item`] with the given element. pub fn new(item: impl Into>) -> Self { Self { item: item.into(), @@ -525,6 +531,7 @@ where } } + /// Creates an [`Item`] with the given element and menu. pub fn with_menu( item: impl Into>, menu: Menu<'a, Message, Theme, Renderer>, @@ -535,11 +542,6 @@ where } } - pub fn menu(mut self, menu: Menu<'a, Message, Theme, Renderer>) -> Self { - self.menu = Some(Box::new(menu)); - self - } - pub(super) fn tree(&self) -> Tree { Tree { tag: self.tag(), @@ -588,8 +590,8 @@ where &self, tree: &mut Tree, renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { + limits: &Limits, + ) -> Node { // println!("Item layout"); self.item .as_widget() @@ -602,7 +604,7 @@ where &mut self, tree: &mut Tree, event: Event, - layout: layout::Layout<'_>, + layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, @@ -630,7 +632,7 @@ where renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, - layout: layout::Layout<'_>, + layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { @@ -667,7 +669,7 @@ struct Aod { vertical_offset: f32, } impl Aod { - /// Returns child position. offset position, child direction + /// Returns (child position, offset position, child direction) #[allow(clippy::too_many_arguments)] fn adaptive( parent_pos: f32, @@ -752,7 +754,7 @@ impl Aod { } } - /// Returns child position and offset position + /// Returns (child position, offset position, child direction) fn resolve( &self, parent_bounds: Rectangle, @@ -958,64 +960,3 @@ fn search_bound( index } -/* fn slice( - // children_bounds: Rectangle, - items_node: &Node, - scroll_offset: f32, - viewport: Size, - translation: Vector, -) -> MenuSlice { - let items_bounds = items_node.bounds() + translation; - let max_index = items_node.children().len().saturating_sub(1); - - // viewport space absolute bounds - let lower_bound = items_bounds.y.max(0.0); - let upper_bound = (items_bounds.y + items_bounds.height).min(viewport.height); - - // menu space relative bounds - let lower_bound_rel = lower_bound - (items_bounds.y + scroll_offset); - let upper_bound_rel = upper_bound - (items_bounds.y + scroll_offset); - - // index range - // let positions = &child_positions; - // let sizes = &child_sizes; - - // let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); - // let end_index = search_bound(max_index, start_index, max_index, upper_bound_rel, positions, sizes).min(max_index); - - MenuSlice { - start_index, - end_index, - lower_bound_rel, - upper_bound_rel, - } -} */ - -/* fn search_bound( - default: usize, - default_left: usize, - default_right: usize, - bound: f32, - positions: &[f32], - sizes: &[Size], -) -> usize { - // binary search - let mut left = default_left; - let mut right = default_right; - - let mut index = default; - while left != right { - let m = ((left + right) / 2) + 1; - if positions[m] > bound { - right = m - 1; - } else { - left = m; - } - } - let height = sizes[left].height; - if positions[left] + height > bound { - index = left; - } - index -} */ - diff --git a/src/native/menu/types.rs b/src/native/menu/types.rs index f0614675..e48235b0 100644 --- a/src/native/menu/types.rs +++ b/src/native/menu/types.rs @@ -1,6 +1,3 @@ -use iced_widget::core::{overlay, renderer}; - -// use super::menu_tree_overlay::MenuTreeOverlay; /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] @@ -15,38 +12,6 @@ pub struct CloseCondition { pub click_inside: bool, } -// /// The width of an item -// #[derive(Debug, Clone, Copy)] -// pub enum ItemWidth { -// /// Use uniform width -// Uniform(u16), -// /// Static tries to use the width value of each menu(menu tree with children), -// /// the widths of items(menu tree with empty children) will be the same as the menu they're in, -// /// if that value is None, -// /// the default value will be used instead, -// /// which is the value of the Static variant -// Static(u16), -// } - -// /// The height of an item -// #[derive(Debug, Clone, Copy)] -// pub enum ItemHeight { -// /// Use uniform height. -// Uniform(u16), -// /// Static tries to use `MenuTree.height` as item height, -// /// when it's `None` it'll fallback to the value of the `Static` variant. -// Static(u16), -// /// Dynamic tries to automatically choose the proper item height for you, -// /// but it only works in certain cases: -// /// -// /// - Fixed height -// /// - Shrink height -// /// - Menu tree height -// /// -// /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. -// Dynamic(u16), -// } - // /// Methods for drawing path highlight // #[derive(Debug, Clone, Copy)] // pub enum PathHighlight { @@ -76,24 +41,10 @@ impl Direction{ /// Axis #[allow(missing_docs)] #[derive(Debug, Clone, Copy)] -pub enum Axis { +pub(super) enum Axis { Horizontal, Vertical, } -#[allow(missing_docs)] -pub enum OpenCondition { - Hover, - Click, -} - pub(super) type Index = Option; - -/// Should be returned from the recursive event processing function, -/// tells the caller which type of event has been processed -pub(super) enum RecEvent{ - Event, - Close, - None -} \ No newline at end of file From 62bc9d0c2060be25f5b8fb29e1802d389ff9cba5 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 16 Feb 2024 10:39:49 +0800 Subject: [PATCH 11/40] remove horizontal and vertical from Aod --- src/native/menu/menu_tree.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 50d1c6e8..b5aecc2f 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -652,10 +652,6 @@ where #[derive(Debug)] #[allow(clippy::struct_excessive_bools)] struct Aod { - // whether or not to use aod - horizontal: bool, - vertical: bool, - // whether or not to use overlap horizontal_overlap: bool, vertical_overlap: bool, @@ -677,7 +673,6 @@ impl Aod { child_size: f32, max_size: f32, offset: f32, - on: bool, overlap: bool, direction: Direction, ) -> (f32, f32, Direction) { @@ -717,14 +712,14 @@ impl Aod { if overlap { let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { + if space_negative > space_positive && overshoot > space_positive { (parent_pos - overshoot, parent_pos - overshoot, direction.flip()) } else { (parent_pos, parent_pos, direction) } } else { let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { + if space_negative > space_positive && overshoot > space_positive { (parent_pos - overshoot, parent_pos - offset, direction.flip()) } else { (parent_pos + parent_size + offset, parent_pos + parent_size, direction) @@ -737,14 +732,14 @@ impl Aod { if overlap { let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { + if space_negative > space_positive && overshoot > space_positive { (parent_pos, parent_pos, direction.flip()) } else { (parent_pos - overshoot, parent_pos - overshoot, direction) } } else { let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { + if space_negative > space_positive && overshoot > space_positive { (parent_pos + parent_size + offset, parent_pos + parent_size, direction.flip()) } else { (parent_pos - overshoot, parent_pos - offset, direction) @@ -767,7 +762,6 @@ impl Aod { children_size.width, viewport_size.width, self.horizontal_offset, - self.horizontal, self.horizontal_overlap, self.horizontal_direction, ); @@ -777,7 +771,6 @@ impl Aod { children_size.height, viewport_size.height, self.vertical_offset, - self.vertical, self.vertical_overlap, self.vertical_direction, ); @@ -808,8 +801,6 @@ impl Aod { Direction::Negative }; Aod { - horizontal: true, - vertical: true, horizontal_overlap: false, vertical_overlap: true, horizontal_direction, @@ -826,8 +817,6 @@ impl Aod { }; let vertical_direction = pdy; Aod { - horizontal: true, - vertical: true, horizontal_overlap: true, vertical_overlap: false, horizontal_direction, From 1c9def0ad7427ef7a3305700070c9a11fd2ebf5c Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 17 Feb 2024 10:28:48 +0800 Subject: [PATCH 12/40] styling --- src/native/menu/menu_bar.rs | 19 ++++- src/native/menu/menu_bar_overlay.rs | 121 ++++++++++++++-------------- src/native/menu/menu_tree.rs | 107 ++++++++++++------------ src/style/menu_bar.rs | 55 +++++++------ 4 files changed, 155 insertions(+), 147 deletions(-) diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index c0eca226..cd94427f 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,15 +1,16 @@ //! [`MenuBar`] -use iced_widget::core::{ +use iced_widget::{core::{ alignment, event, layout::{Limits, Node}, mouse, overlay, renderer, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Point, Rectangle, Shell, Size, Widget, -}; +}, Theme}; use super::{flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*, types::*}; +use crate::style::menu_bar::*; pub(super) struct MenuBarState { pub(super) active_root: Index, @@ -29,6 +30,7 @@ impl Default for MenuBarState { /// menu bar pub struct MenuBar<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { roots: Vec>, @@ -36,9 +38,11 @@ where padding: Padding, width: Length, height: Length, + style: Theme::Style, } impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { /// Creates a [`MenuBar`] with the given root items. @@ -54,6 +58,7 @@ where padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, + style: Theme::Style::default() } } @@ -81,10 +86,16 @@ where self } + /// Sets the style variant of this [`MenuBar`]. + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } } impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { fn size(&self) -> Size { @@ -269,6 +280,7 @@ where init_bar_bounds, // init_parent_bounds, init_root_bounds, + style: &self.style } .overlay_element(), ) @@ -282,9 +294,8 @@ impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where Message: 'a, - Theme: 'a, + Theme: 'a + StyleSheet, Renderer: 'a + renderer::Renderer, - // Renderer::Theme: StyleSheet, { fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { Self::new(value) diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 99d0a937..96dd6495 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -8,6 +8,7 @@ use iced_widget::core::{ }; use super::{menu_bar::MenuBarState, menu_tree::*, types::*}; +use crate::style::menu_bar::*; /// Should be returned from the recursive event processing function, /// tells the caller which type of event has been processed @@ -19,17 +20,20 @@ enum RecEvent{ pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { /// Tree{ bar_state, [item_tree...] } pub(super) tree: &'b mut Tree, - + pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], pub(super) init_bar_bounds: Rectangle, pub(super) init_root_bounds: Vec, + pub(super) style: &'b Theme::Style, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer> { @@ -39,6 +43,7 @@ where impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { fn layout( @@ -81,7 +86,7 @@ where let active_tree = &mut self.tree.children[active]; // item_tree: Tree{ stateless, [ widget_tree, menu_tree ] } let parent_bounds = self.init_root_bounds[active] + translation; - fn rec<'a, Message, Theme, Renderer: renderer::Renderer>( + fn rec<'a, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( renderer: &Renderer, item: &Item<'a, Message, Theme, Renderer>, tree: &mut Tree, @@ -100,15 +105,17 @@ where menu_nodes.push(menu_node); let menu_state = menu_tree.state.downcast_ref::(); - let slice = &menu_state.slice; if let Some(active) = menu_state.active{ let next_item = &menu.items[active]; let next_tree = &mut menu_tree.children[active]; - let slice_node = &menu_nodes.last().unwrap().children()[0]; - let next_parent_bounds = slice_node.children()[active - slice.start_index].bounds() + - (slice_node.bounds().position() - Point::ORIGIN); - + let next_parent_bounds = { + let slice_node = &menu_nodes.last().unwrap().children()[0]; + let Some(node) = slice_node.children().get(active - menu_state.slice.start_index) + else { return; }; + + node.bounds() + (slice_node.bounds().position() - Point::ORIGIN) + }; rec(renderer, next_item, next_tree, menu_nodes, next_parent_bounds, direction, viewport); } } @@ -191,7 +198,7 @@ where let active_tree = &mut self.tree.children[active]; let mut prev_bounds_list = vec![bar_bounds]; - fn rec<'a, 'b, Message, Theme, Renderer: renderer::Renderer>( + fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( tree: &mut Tree, item: &mut Item<'a, Message, Theme, Renderer>, event: Event, @@ -221,7 +228,12 @@ where if let Some(active) = menu_state.active{ let next_tree = &mut menu_tree.children[active]; let next_item = &mut menu.items[active]; - let next_parent_bounds = slice_layout.children().nth(active - menu_state.slice.start_index).unwrap().bounds(); + let next_parent_bounds = { + let Some(layout) = slice_layout.children().nth(active - menu_state.slice.start_index) + else { return RecEvent::Event }; + + layout.bounds() + }; let re = rec( next_tree, @@ -323,50 +335,22 @@ where layout: Layout<'_>, cursor: mouse::Cursor, ) { + let bar = self.tree.state.downcast_ref::(); + let Some(active) = bar.active_root else { return; }; + let viewport = layout.bounds(); let mut lc = layout.children(); let _bar_bounds = lc.next().unwrap().bounds(); - let roots_layout = lc.next().unwrap(); - - let bar = self.tree.state.downcast_ref::(); - - let Some(active) = bar.active_root else { - return; - }; + let _roots_layout = lc.next().unwrap(); - let parent_bounds = roots_layout.children().nth(active).unwrap().bounds(); + // let parent_bounds = roots_layout.children().nth(active).unwrap().bounds(); let menu_layouts_layout = lc.next().unwrap(); // Node{0, [menu_node...]} let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] let active_root = &self.roots[active]; let active_tree = &self.tree.children[active]; - /* println!("bar bounds: {:?}", bar_bounds); - renderer.fill_quad( - renderer::Quad{ - bounds: bar_bounds, - border: Border{ - // width: 6.0, - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() - }, - Color::from([0.0, 1.0, 1.0, 0.3]) - ); - - roots_layout.children().for_each(|l|{ - println!("root bounds: {:?}", l.bounds()); - renderer.fill_quad( - renderer::Quad{ - bounds: l.bounds(), - ..Default::default() - }, - Color::from([1.0, 0.0, 0.0, 0.6]) - ); - }); */ - - fn rec<'a, 'b, Message, Theme, Renderer: renderer::Renderer>( + fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( tree: &Tree, item: &Item<'a, Message, Theme, Renderer>, layout_iter: &mut impl Iterator< Item = Layout<'b>>, @@ -374,7 +358,7 @@ where renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, - parent_bounds: Rectangle, + theme_style: &Theme::Style, viewport: &Rectangle, ) { let menu = item.menu.as_ref().expect("No menu defined in this item"); @@ -382,26 +366,39 @@ where let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} - menu.draw( - menu_tree, - renderer, - theme, - style, - menu_layout, - cursor, - viewport, - parent_bounds - ); - - let mut mc = menu_layout.children(); - let slice_layout = mc.next().unwrap(); // slice_node + let mut draw_menu = |cursor| + menu.draw( + menu_tree, + renderer, + theme, + style, + theme_style, + menu_layout, + cursor, + viewport, + ); let menu_state = menu_tree.state.downcast_ref::(); - + if let Some(active) = menu_state.active{ let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; - let next_parent_bounds = slice_layout.children().nth(active - menu_state.slice.start_index).unwrap().bounds(); + + let mut mc = menu_layout.children(); + let slice_layout = mc.next().unwrap(); // slice_node + let active_bounds = { + let Some(layout) = slice_layout.children().nth(active - menu_state.slice.start_index) + else { return; }; + layout.bounds() + }; + + draw_menu( + if cursor.is_over(active_bounds){ + cursor + }else{ + mouse::Cursor::Available(active_bounds.center()) + } + ); renderer.with_layer( *viewport, @@ -413,10 +410,12 @@ where r, theme, style, - next_parent_bounds, + theme_style, viewport ) ); + }else{ + draw_menu(cursor) } } @@ -428,7 +427,7 @@ where renderer, theme, style, - parent_bounds, + self.style, &viewport ) diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index b5aecc2f..52074bad 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -12,6 +12,8 @@ use iced_widget::core::{ Size, Vector, Widget, }; +use crate::style::menu_bar::*; + /* menu tree: Item{ @@ -67,6 +69,7 @@ impl Default for MenuState{ /// Menu pub struct Menu<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { pub(super) items: Vec>, @@ -81,6 +84,7 @@ where #[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { /// Creates a [`Menu`] with the given items. @@ -137,6 +141,7 @@ where } impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { pub(super) fn size(&self) -> Size { @@ -357,80 +362,33 @@ where renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, + theme_style: &Theme::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, - parent_bounds: Rectangle, + // parent_bounds: Rectangle, ) { let mut lc = layout.children(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); - let offset_bounds = lc.next().unwrap().bounds(); - let check_bounds = lc.next().unwrap().bounds(); - - // println!("prescroll: {:?}", prescroll); - // println!("parent_bounds: {:?}", parent_bounds); + // let offset_bounds = lc.next().unwrap().bounds(); + // let check_bounds = lc.next().unwrap().bounds(); let menu_state = tree.state.downcast_ref::(); let slice = &menu_state.slice; - // println!("slice: {:?}", slice); - renderer.fill_quad( - renderer::Quad{ - bounds: check_bounds, - border: Border{ - // color: todo!(), - // width: todo!(), - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() - }, - Color::from([1.0, 0.0, 0.0, 0.1]) - ); - - renderer.fill_quad( - renderer::Quad{ - bounds: prescroll, - border: Border{ - // color: todo!(), - // width: todo!(), - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() - }, - Color::from([1.0, 1.0, 1.0, 1.0]) - ); + let styling = theme.appearance(theme_style); - renderer.fill_quad( - renderer::Quad{ - bounds: offset_bounds, - border: Border{ - // color: todo!(), - // width: todo!(), - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() - }, - Color::from([0.0, 0.0, 1.0, 0.3]) - ); + // debug_draw(renderer, check_bounds, offset_bounds, parent_bounds); renderer.fill_quad( renderer::Quad{ - bounds: parent_bounds, - border: Border{ - // color: todo!(), - // width: todo!(), - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() + bounds: pad_rectangle(prescroll, styling.background_expand), + border: styling.border, + shadow: styling.shadow, }, - Color::from([1.0, 1.0, 0.0, 0.5]) + styling.background ); - for ((item, tree), layout) in self .items[ slice.start_index .. slice.end_index+1 ].iter() // [item...].iter() @@ -510,9 +468,42 @@ where } +/* fn debug_draw( + renderer: &mut Renderer, + check_bounds: Rectangle, + offset_bounds: Rectangle, + parent_bounds: Rectangle, +){ + [ + check_bounds, + offset_bounds, + parent_bounds + ].iter() + .zip([ + Color::from([1.0, 0.0, 0.0, 0.1]), + Color::from([0.0, 0.0, 1.0, 0.3]), + Color::from([1.0, 1.0, 0.0, 0.5]) + ]) + .for_each(|(b, c)|{ + renderer.fill_quad( + renderer::Quad{ + bounds: *b, + border: Border{ + radius: 6.0.into(), + ..Default::default() + }, + ..Default::default() + }, + c + ); + }); +} */ + + /// Item inside a [`Menu`] pub struct Item<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { pub(super) item: Element<'a, Message, Theme, Renderer>, @@ -521,6 +512,7 @@ where #[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { /// Creates an [`Item`] with the given element. @@ -552,6 +544,7 @@ where } impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> where + Theme: StyleSheet, Renderer: renderer::Renderer, { pub(super) fn size(&self) -> Size { diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 278374db..bffea66d 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -1,31 +1,34 @@ //! Change the appearance of menu bars and their menus. -use iced_widget::{core::Color, style::Theme}; +use iced_widget::{ + core::{ + Color, Border, Background, Shadow, Padding + }, + style::Theme +}; /// The appearance of a menu bar and its menus. #[derive(Debug, Clone, Copy)] pub struct Appearance { - /// The background color of the menu bar and its menus. - pub background: Color, - /// The border width of the menu bar and its menus. - pub border_width: f32, - /// The border radius of the menu bar and its menus. - pub border_radius: [f32; 4], - /// The border [`Color`] of the menu bar and its menus. - pub border_color: Color, - /// The expand value of the menus' background - pub background_expand: [u16; 4], - /// The highlighted path [`Color`] of the the menu bar and its menus. - pub path: Color, + /// The background of the menus. + pub background: Background, + /// The border of the menus. + pub border: Border, + /// The shadow of the menus + pub shadow: Shadow, + /// Expand the background + pub background_expand: Padding, } impl std::default::Default for Appearance { fn default() -> Self { Self { - background: Color::from([0.85; 3]), - border_width: 1.0, - border_radius: [6.0; 4], - border_color: Color::from([0.5; 3]), - background_expand: [6; 4], - path: Color::from([0.3; 3]), + background: Color::from([0.85; 3]).into(), + border: Border{ + color: Color::from([0.5; 3]), + width: 1.0, + radius: [6.0; 4].into(), + }, + shadow: Shadow::default(), + background_expand: [6; 4].into(), } } } @@ -72,12 +75,14 @@ impl StyleSheet for Theme { match style { MenuBarStyle::Default => Appearance { - background: palette.background.base.color, - border_width: 1.0, - border_radius: [6.0; 4], - border_color: palette.background.weak.color, - background_expand: [6; 4], - path: palette.primary.weak.color, + background: palette.background.base.color.into(), + border: Border{ + color: palette.background.weak.color.into(), + width: 1.0, + radius: [6.0; 4].into(), + }, + shadow: Shadow::default(), + background_expand: [6; 4].into() }, MenuBarStyle::Custom(c) => c.appearance(self), } From ccd9b4b979391d1e4bd512b181c4cd1636fcd1d1 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Tue, 20 Feb 2024 09:51:34 +0800 Subject: [PATCH 13/40] closing logic, macros, scrolling clipping and examples --- examples/menu/src/main.rs | 1550 ++++++++++++++++----------- src/native/helpers.rs | 76 +- src/native/menu.rs | 1 - src/native/menu/menu_bar.rs | 23 +- src/native/menu/menu_bar_overlay.rs | 52 +- src/native/menu/menu_tree.rs | 221 +++- src/style/menu_bar.rs | 24 +- 7 files changed, 1225 insertions(+), 722 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index bb3aeb44..57224e10 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -3,17 +3,15 @@ use iced::widget::{ text_input, toggler, vertical_slider, }; use iced::widget::{column as col, vertical_space}; -use iced::{alignment, theme, Application, Border, Color, Element, Event, Length, Pixels, Size}; - -use iced_aw::menu::{ - Item, - // menu_tree::MenuTree, CloseCondition, ItemHeight, ItemWidth, PathHighlight, - // Menux, OpenCondition, Axis - Menu, - MenuBar, -}; +use iced::{alignment, theme, Alignment, Application, Border, Color, Element, Event, Length, Pixels, Size, Theme}; + +use iced_aw::menu::{Item, Menu, MenuBar}; use iced_aw::quad; -// use iced_aw::{helpers::menu_tree, menu_bar, menu_tree}; +use iced_aw::{ + menu_bar, + menu, + menu_items +}; pub fn main() -> iced::Result { // std::env::set_var("RUST_BACKTRACE", "full"); @@ -23,18 +21,6 @@ pub fn main() -> iced::Result { size: Size::new(1000.0, 500.0), ..Default::default() }, - // id: todo!(), - // flags: todo!(), - // fonts: todo!(), - // default_font: todo!(), - // antialiasing: todo!(), - - // default_text_size: 15.0, - // window: iced::window::Settings { - // size: (1000, 500), - // // position: iced::window::Position::Default, - // ..Default::default() - // }, ..Default::default() }) } @@ -214,156 +200,433 @@ impl Application for App { fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { // println!("app view"); - let mb = MenuBar::new( - [ - Item::with_menu( - debug_button("content"), - Menu::new( - [ - Item::new(debug_button("abc").width(180.0)), - Item::new(debug_button("def").width(180.0)), - Item::new(debug_button("xxx").width(180.0)), - Item::with_menu( - debug_button("htrsth").width(180.0), - Menu::new( - vec![ - Item::new(debug_button("ccgh").width(180.0)), - Item::new(debug_button("kuyg").width(180.0)), - Item::new(debug_button("vcsa").width(180.0)), - Item::new(debug_button("kiug").width(180.0)), - ] - ).max_width(180.0) - ) - ] - .into(), - ).max_width(180.0), + let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(12.0); + let menu_temp_2 = |items| Menu::new(items).max_width(180.0); + + let mb = menu_bar!( + (debug_button_s("Nested Menus"), + { + let sub5 = menu_temp_2(menu_items!( + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + )); + + let sub4 = menu_temp_2(menu_items!( + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + )).width(180.0); + + let sub3 = menu_temp_2(menu_items!( + (debug_button("You can")), + (debug_button("nest menus")), + (submenu_button("SUB"), + sub4 + ), + (debug_button("how ever")), + (debug_button("You like")), + (submenu_button("SUB"), + sub5 + ), + )).width(160.0); + + let sub2 = menu_temp_2(menu_items!( + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + (submenu_button("More sub menus"), + sub3 + ), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + )).width(140.0); + + let sub1 = menu_temp_2(menu_items!( + (debug_button("Item")), + (debug_button("Item")), + (submenu_button("Another sub menu"), + sub2 + ), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + )).width(220.0); + + menu_temp_1(menu_items!( + (debug_button("Item")), + (debug_button("Item")), + (submenu_button("A sub menu"), + sub1 + ), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + )).width(110.0) + } + ), + (debug_button_s("Widgets"), menu_temp_1(menu_items!( + (debug_button("You can use any widget")), + (debug_button("as a menu item")), + (button( + text("Button") + .width(Length::Fill) + .vertical_alignment(alignment::Vertical::Center), + ) + .width(Length::Fill) + .on_press(Message::Debug("Button".into())) ), - Item::with_menu( - debug_button("aaa"), - Menu::new( - [ - Item::new(debug_button("abc").width(Length::Fill)), - Item::new(debug_button("def").width(Length::Fill)), - Item::new(debug_button("xxx").width(Length::Fill)), - Item::with_menu( - debug_button("syjdtyjd").width(Length::Fill), - Menu::new( - vec![ - Item::new(debug_button("hghg").width(Length::Fill)), - Item::new(debug_button("kuyg").width(Length::Fill)), - Item::new(debug_button("arga").width(Length::Fill)), - Item::new(debug_button("abcd").width(Length::Fill)), - Item::new(debug_button("vcsa").width(Length::Fill)), - Item::with_menu( - debug_button("htrsthfs").width(Length::Fill), - Menu::new( - vec![ - Item::new(debug_button("hghg").width(Length::Fill)), - Item::new(debug_button("kuyg").width(Length::Fill)), - Item::new(debug_button("vcsa").width(Length::Fill)), - Item::new(debug_button("kiug").width(Length::Fill)), - ] - ).max_width(220.0) - ), - Item::new(debug_button("kiug").width(Length::Fill)), - ] - ).max_width(200.0) - ), - Item::new(debug_button("abc").width(Length::Fill)), - Item::new(debug_button("def").width(Length::Fill)), - Item::new(debug_button("xxx").width(Length::Fill)), - ] - .into(), - ).max_width(180.0), + (checkbox("Checkbox", self.check, Message::CheckChange) + .width(Length::Fill) ), - Item::with_menu( - debug_button("pondjssbah"), - Menu::new( - [ - Item::new(debug_button("abc").width(180.0)), - Item::new(debug_button("def").width(180.0)), - Item::new(debug_button("xxx").width(180.0)), - Item::new(debug_button("htrsrt").width(180.0)), - Item::new(debug_button("htrdf").width(180.0)), - Item::new(debug_button("ngfcgng").width(180.0)), - Item::new(debug_button("hytfy").width(180.0)), - Item::new(debug_button("kuyg").width(180.0)), - Item::new(debug_button("qegvd").width(180.0)), - Item::with_menu( - debug_button("iuoiy").width(180.0), - Menu::new( - [ - Item::new(debug_button("abc").width(Length::Fill)), - Item::new(debug_button("def").width(Length::Fill)), - Item::new(debug_button("xxx").width(Length::Fill)), - Item::new(debug_button("htrsrt").width(Length::Fill)), - Item::new(debug_button("htrdf").width(Length::Fill)), - Item::new(debug_button("ngfcgng").width(Length::Fill)), - Item::new(debug_button("hytfy").width(Length::Fill)), - Item::new(debug_button("kuyg").width(Length::Fill)), - Item::new(debug_button("qegvd").width(Length::Fill)), - Item::new(debug_button("iuoiy").width(Length::Fill)), - Item::new(debug_button("rzsajf").width(Length::Fill)), - Item::new(debug_button("pkmehs").width(Length::Fill)), - Item::new(debug_button("ivrye").width(Length::Fill)), - Item::new(debug_button("zhdkr").width(Length::Fill)), - Item::new(debug_button("vjdiwo").width(Length::Fill)), - Item::new(debug_button("abc").width(Length::Fill)), - Item::new(debug_button("def").width(Length::Fill)), - Item::new(debug_button("xxx").width(Length::Fill)), - Item::new(debug_button("htrsrt").width(Length::Fill)), - Item::new(debug_button("htrdf").width(Length::Fill)), - Item::new(debug_button("ngfcgng").width(Length::Fill)), - Item::new(debug_button("hytfy").width(Length::Fill)), - Item::new(debug_button("kuyg").width(Length::Fill)), - Item::new(debug_button("qegvd").width(Length::Fill)), - Item::new(debug_button("iuoiy").width(Length::Fill)), - Item::new(debug_button("rzsajf").width(Length::Fill)), - Item::new(debug_button("pkmehs").width(Length::Fill)), - Item::new(debug_button("ivrye").width(Length::Fill)), - Item::new(debug_button("zhdkr").width(Length::Fill)), - Item::new(debug_button("vjdiwo").width(Length::Fill)), - ].into() - ).max_width(180.0) - ), - Item::new(debug_button("rzsajf").width(180.0)), - Item::new(debug_button("pkmehs").width(180.0)), - Item::new(debug_button("ivrye").width(180.0)), - Item::new(debug_button("zhdkr").width(180.0)), - Item::new(debug_button("vjdiwo").width(180.0)), - Item::new(debug_button("abc").width(180.0)), - Item::new(debug_button("def").width(180.0)), - Item::new(debug_button("xxx").width(180.0)), - Item::new(debug_button("htrsrt").width(180.0)), - Item::new(debug_button("htrdf").width(180.0)), - Item::new(debug_button("ngfcgng").width(180.0)), - Item::new(debug_button("hytfy").width(180.0)), - Item::new(debug_button("kuyg").width(180.0)), - Item::new(debug_button("qegvd").width(180.0)), - Item::new(debug_button("iuoiy").width(180.0)), - Item::new(debug_button("rzsajf").width(180.0)), - Item::new(debug_button("pkmehs").width(180.0)), - Item::new(debug_button("ivrye").width(180.0)), - Item::new(debug_button("zhdkr").width(180.0)), - Item::new(debug_button("vjdiwo").width(180.0)), - ] - .into(), - ).max_width(180.0), + ( + row![ + "Slider", + horizontal_space(Length::Fixed(8.0)), + slider(0..=255, self.value, Message::ValueChange) + ] ), - Item::with_menu( - debug_button("kjdfnh"), - Menu::new( - [ - Item::new(debug_button("dfths").width(Length::Fill).height(30.0)), - Item::new(debug_button("iodfns").width(Length::Fill).height(50.0)), - Item::new(debug_button("dfkmjk").width(Length::Fill).height(40.0)), - Item::new(debug_button("uvbw").width(Length::Fill).height(60.0)), - ].into() - ).max_width(180.0) + (text_input("", &self.text).on_input(Message::TextChange)), + (container(toggler( + Some("Or as a sub menu item".to_string()), + self.toggle, + Message::ToggleChange, + )) + .padding([0, 8]) + .height(Length::Fill) + .align_y(alignment::Vertical::Center), + menu_temp_2(menu_items!( + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + (debug_button("Item")), + )) + ), + (separator()), + (debug_button("Seperators are also widgets")), + (labeled_separator("Separator")), + (debug_button("Item")), + (debug_button("Item")), + (dot_separator()), + (debug_button("Item")), + (debug_button("Item")), + )).width(240.0)), + (debug_button_s("Controls"), menu_temp_1(menu_items!( + (row![toggler( + Some("Dark Mode".into()), + self.dark_mode, + Message::ThemeChange + )].padding([0, 8]) + ), + (color_button([0.45, 0.25, 0.57])), + (color_button([0.15, 0.59, 0.64])), + (color_button([0.76, 0.82, 0.20])), + (color_button([0.17, 0.27, 0.33])), + (labeled_button("Primary", Message::None) + .width(Length::Fill), + { + let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); + + menu_temp_2(menu_items!( + (slider(0..=255, r, move |x| { + Message::ColorChange(Color::from_rgb8(x, g, b)) + })), + (slider(0..=255, g, move |x| { + Message::ColorChange(Color::from_rgb8(r, x, b)) + })), + (slider(0..=255, b, move |x| { + Message::ColorChange(Color::from_rgb8(r, g, x)) + })), + )) + } ) - ] - .into(), - ); + ))), + (debug_button_s("Scroll"), menu_temp_1(menu_items!( + (debug_button("ajrs")), + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (submenu_button("jcsu"), menu_temp_2(menu_items!( + (debug_button("ajrs")), + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (submenu_button("luyortp"), menu_temp_2(menu_items!( + (debug_button("ajrs")), + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), + ))), + (debug_button("mmdyrc")), + (debug_button("nquc")), + ))), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), // 13 + (debug_button("ajrs")), // 14 + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), // 27 + (debug_button("ajrs")), // 28 + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (submenu_button("dekjdaud"), menu_temp_2(menu_items!( + (debug_button("ajrs")), + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), + (debug_button("ajrs")), + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), + ))), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), + (debug_button("ajrs")), + (debug_button("bsdfho")), + (debug_button("clkjhbf")), + (debug_button("dekjdaud")), + (debug_button("ecsh")), + (debug_button("fweiu")), + (debug_button("giwe")), + (debug_button("heruyv")), + (debug_button("isabe")), + (debug_button("jcsu")), + (debug_button("kaljkahd")), + (debug_button("luyortp")), + (debug_button("mmdyrc")), + (debug_button("nquc")), + ))), + (debug_button_s("Dynamic height"), { + let slider_count = 3; + let slider_width = 30; + let spacing = 4; + let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); + + menu_temp_1(menu_items!( + (labeled_separator("Primary")), + ( + row![ + vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( + x, g, b + ))) + .width(slider_width) + // .height(100.0) + , + vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( + r, x, b + ))) + .width(slider_width) + // .height(100.0) + , + vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( + r, g, x + ))) + .width(slider_width) + // .height(100.0) + , + ].spacing(spacing) + .height(100.0) + ), + (debug_button("AABB").height(40)), + (debug_button("CCDD").height(140)), + (debug_button("EEFF").height(30)), + (debug_button("GGHH").height(100)), + (debug_button("IIJJ").height(60)), + )).width(slider_width * slider_count + (slider_count - 1) * spacing) + }), + /* (debug_button_s("content"), + menu_temp_1(menu_items!( + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + (submenu_button("htrsth"), + menu_temp_2(menu_items!( + (debug_button("ccgh")), + (debug_button("kuyg")), + (debug_button("vcsa")), + (debug_button("kiug")), + )) + ), + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + )) + ), + (debug_button_s("aaa"), + menu_temp_1(menu_items!( + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + (submenu_button("syjdtyjd"), + menu_temp_2(menu_items!( + (debug_button("hghg")), + (debug_button("kuyg")), + (debug_button("arga")), + (debug_button("abcd")), + (debug_button("vcsa")), + (submenu_button("htrsthfs"), + menu_temp_2(menu_items!( + (debug_button("hghg")), + (debug_button("kuyg")), + (debug_button("vcsa")), + (debug_button("kiug")), + )).max_width(220.0) + ), + (debug_button("kiug")), + )).max_width(200.0) + ), + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + )).max_width(180.0) + ), + (debug_button_s("pondjssbah"), + menu_temp_1(menu_items!( + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + (debug_button("htrsrt")), + (debug_button("htrdf")), + (debug_button("ngfcgng")), + (debug_button("hytfy")), + (debug_button("kuyg")), + (debug_button("qegvd")), + (submenu_button("iuoiy"), + menu_temp_2(menu_items!( + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + (debug_button("htrsrt")), + (debug_button("htrdf")), + (debug_button("ngfcgng")), + (debug_button("hytfy")), + (debug_button("kuyg")), + (debug_button("qegvd")), + (debug_button("iuoiy")), + (debug_button("rzsajf")), + (debug_button("pkmehs")), + (debug_button("ivrye")), + (debug_button("zhdkr")), + (debug_button("vjdiwo")), + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + (debug_button("htrsrt")), + (debug_button("htrdf")), + (debug_button("ngfcgng")), + (debug_button("hytfy")), + (debug_button("kuyg")), + (debug_button("qegvd")), + (debug_button("iuoiy")), + (debug_button("rzsajf")), + (debug_button("pkmehs")), + (debug_button("ivrye")), + (debug_button("zhdkr")), + (debug_button("vjdiwo")), + )) + ), + (debug_button("rzsajf")), + (debug_button("pkmehs")), + (debug_button("ivrye")), + (debug_button("zhdkr")), + (debug_button("vjdiwo")), + (debug_button("abc")), + (debug_button("def")), + (debug_button("xxx")), + (debug_button("htrsrt")), + (debug_button("htrdf")), + (debug_button("ngfcgng")), + (debug_button("hytfy")), + (debug_button("kuyg")), + (debug_button("qegvd")), + (debug_button("iuoiy")), + (debug_button("rzsajf")), + (debug_button("pkmehs")), + (debug_button("ivrye")), + (debug_button("zhdkr")), + (debug_button("vjdiwo")), + )) + ), + (debug_button_s("kjdfnh"), + menu_temp_1(menu_items!( + (debug_button("dfths").height(30.0)), + (debug_button("iodfns").height(80.0)), + (debug_button("dfkmjk").height(50.0)), + (debug_button("uvbw").height(200.0)), + (debug_button("ngxg").height(100.0)), + (debug_button("udrth").height(80.0)), + (debug_button("aervx").height(120.0)), + (debug_button("iusdz").height(70.0)), + )) + ) + */ + ).check_bounds_width(80.0); /* let mb = row![ Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ @@ -463,7 +726,7 @@ impl Application for App { .padding([2, 8]) .align_items(alignment::Alignment::Center); */ - let r = row![horizontal_space(800), mb, horizontal_space(800),] + let r = row![horizontal_space(400), mb, horizontal_space(400),] .padding([2, 8]) .align_items(alignment::Alignment::Center); @@ -483,13 +746,9 @@ impl Application for App { c.into() */ - let c = col![vertical_space(600), r, vertical_space(600),]; + let c = col![vertical_space(400), r, vertical_space(400),]; let sc = scrollable(c) - // .direction(scrollable::Direction::Both{ - // vertical: scrollable::Properties::new(), - // horizontal: scrollable::Properties::new(), - // }); .direction(scrollable::Direction::Both { vertical: scrollable::Properties::new(), horizontal: scrollable::Properties::new(), @@ -516,6 +775,7 @@ impl button::StyleSheet for ButtonStyle { button::Appearance { text_color: style.extended_palette().background.base.text, background: Some(Color::TRANSPARENT.into()), + // background: Some(Color::from([1.0; 3]).into()), border: Border { radius: [4.0; 4].into(), ..Default::default() @@ -551,8 +811,6 @@ fn labeled_button<'a>( ) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { base_button( text(label) - // .width(Length::Fill) - // .height(Length::Fill) .vertical_alignment(alignment::Vertical::Center), msg, ) @@ -560,34 +818,15 @@ fn labeled_button<'a>( fn debug_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { labeled_button(label, Message::Debug(label.into())) -} - -/* fn debug_item<'a>(label: &str) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - menu_tree!(debug_button(label).width(Length::Fill).height(Length::Fill)) -} - -fn debug_item2<'a>(label: &str) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - menu_tree!(debug_button(label) .width(Length::Fill) - .height(Length::Shrink)) } -fn debug_item3<'a>(label: &str, h: f32) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - menu_tree!(debug_button(label) - .width(Length::Fill) - .height(Length::Fixed(h))) -} - -fn color_item<'a>(color: impl Into) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let color = color.into(); - menu_tree!(base_button(circle(color), Message::ColorChange(color))) +fn debug_button_s<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { + labeled_button(label, Message::Debug(label.into())) + .width(Length::Shrink) } -fn sub_menu<'a>( - label: &str, - msg: Message, - children: Vec>, -) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +fn submenu_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { let handle = svg::Handle::from_path(format!( "{}/caret-right-fill.svg", env!("CARGO_MANIFEST_DIR") @@ -598,70 +837,134 @@ fn sub_menu<'a>( color: Some(theme.extended_palette().background.base.text), })); - menu_tree( - base_button( - row![ - text(label) - .width(Length::Fill) - .height(Length::Fill) - .vertical_alignment(alignment::Vertical::Center), - arrow - ] - .align_items(iced::Alignment::Center), - msg, - ) - .width(Length::Fill) - .height(Length::Fill), - children, - ) + base_button( + row![ + text(label) + .width(Length::Fill) + // .height(Length::Fill) + .vertical_alignment(alignment::Vertical::Center), + arrow + ] + .align_items(iced::Alignment::Center), + Message::Debug(label.into()) + ).width(Length::Fill) } -fn debug_sub_menu<'a>( - label: &str, - children: Vec>, -) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - sub_menu(label, Message::Debug(label.into()), children) +fn color_button<'a>(color: impl Into) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { + let color = color.into(); + base_button( + circle(color), Message::ColorChange(color) + ) } -fn separator<'a>() -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - menu_tree!(quad::Quad { +// fn debug_item<'a>(label: &str) -> Item<'a, Message, iced::Theme, iced::Renderer> { +// menu_item!(debug_button(label).width(Length::Fill).height(Length::Fill)) +// } + +// fn debug_item2<'a>(label: &str) -> Item<'a, Message, iced::Theme, iced::Renderer> { +// menu_item!(debug_button(label) +// .width(Length::Fill) +// .height(Length::Shrink)) +// } + +// fn debug_item3<'a>(label: &str, h: f32) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// menu_tree!(debug_button(label) +// .width(Length::Fill) +// .height(Length::Fixed(h))) +// } + +// fn color_item<'a>(color: impl Into) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let color = color.into(); +// menu_tree!(base_button(circle(color), Message::ColorChange(color))) +// } + +// fn sub_menu<'a>( +// label: &str, +// msg: Message, +// children: Vec>, +// ) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let handle = svg::Handle::from_path(format!( +// "{}/caret-right-fill.svg", +// env!("CARGO_MANIFEST_DIR") +// )); +// let arrow = svg(handle) +// .width(Length::Shrink) +// .style(theme::Svg::custom_fn(|theme| svg::Appearance { +// color: Some(theme.extended_palette().background.base.text), +// })); +// menu_tree( +// base_button( +// row![ +// text(label) +// .width(Length::Fill) +// .height(Length::Fill) +// .vertical_alignment(alignment::Vertical::Center), +// arrow +// ] +// .align_items(iced::Alignment::Center), +// msg, +// ) +// .width(Length::Fill) +// .height(Length::Fill), +// children, +// ) +// } + +// fn debug_sub_menu<'a>( +// label: &str, +// children: Vec>, +// ) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// sub_menu(label, Message::Debug(label.into()), children) +// } + +fn separator<'a>() -> quad::Quad { + quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), + height: Length::Fixed(30.0), ..Default::default() - }) + } } -fn dot_separator<'a>() -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - menu_tree!(text("·························") - .size(30) +fn dot_separator<'a>() -> text::Text<'a, iced::Theme, iced::Renderer> { + text("···································") + .size(22) + // .shaping(text::Shaping::Advanced) .width(Length::Fill) .height(Length::Fill) .horizontal_alignment(alignment::Horizontal::Center) - .vertical_alignment(alignment::Vertical::Center)) + .vertical_alignment(alignment::Vertical::Center) + .height(30.0) } -fn labeled_separator(label: &'_ str) -> MenuTree<'_, Message, iced::Theme, iced::Renderer> { +fn labeled_separator(label: &'_ str) -> Element<'_, Message, iced::Theme, iced::Renderer> { let q_1 = quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), + // background: Some(Color::from([0.8;3]).into()), + // height: Length::Shrink, ..Default::default() }; let q_2 = quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), + // background: Some(Color::from([0.8;3]).into()), + // height: Length::Shrink, ..Default::default() }; - menu_tree!(row![ + row![ q_1, text(label) .height(Length::Fill) .vertical_alignment(alignment::Vertical::Center), q_2, - ]) + ] + .height(30.0) + .into() } fn circle(color: Color) -> quad::Quad { @@ -671,402 +974,401 @@ fn circle(color: Color) -> quad::Quad { color, inner_bounds: quad::InnerBounds::Square(radius * 2.0), border_radius: [radius; 4], + height: 20.0.into(), ..Default::default() } } -fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let sub_5 = debug_sub_menu( - "SUB", - vec![ - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - ], - ); - let sub_4 = debug_sub_menu( - "SUB", - vec![ - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - ], - ) - .width(180); - let sub_3 = debug_sub_menu( - "More sub menus", - vec![ - debug_item("You can"), - debug_item("nest menus"), - sub_4, - debug_item("how ever"), - debug_item("You like"), - sub_5, - ], - ); - let sub_2 = debug_sub_menu( - "Another sub menu", - vec![ - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - sub_3, - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - ], - ) - .width(140); - let sub_1 = debug_sub_menu( - "A sub menu", - vec![ - debug_item("Item"), - debug_item("Item"), - sub_2, - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - ], - ) - .width(220); - - let root = menu_tree( - debug_button("Nested Menus"), - vec![ - debug_item("Item"), - debug_item("Item"), - sub_1, - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - ], - ) - .width(110); - - root -} +// fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let sub_5 = debug_sub_menu( +// "SUB", +// vec![ +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ); +// let sub_4 = debug_sub_menu( +// "SUB", +// vec![ +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ) +// .width(180); +// let sub_3 = debug_sub_menu( +// "More sub menus", +// vec![ +// debug_item("You can"), +// debug_item("nest menus"), +// sub_4, +// debug_item("how ever"), +// debug_item("You like"), +// sub_5, +// ], +// ); +// let sub_2 = debug_sub_menu( +// "Another sub menu", +// vec![ +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// sub_3, +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ) +// .width(140); +// let sub_1 = debug_sub_menu( +// "A sub menu", +// vec![ +// debug_item("Item"), +// debug_item("Item"), +// sub_2, +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ) +// .width(220); +// let root = menu_tree( +// debug_button("Nested Menus"), +// vec![ +// debug_item("Item"), +// debug_item("Item"), +// sub_1, +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ) +// .width(110); +// root +// } + +// fn menu_2<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let sub_1 = menu_tree( +// container(toggler( +// Some("Or as a sub menu item".to_string()), +// app.toggle, +// Message::ToggleChange, +// )) +// .padding([0, 8]) +// .height(Length::Fill) +// .align_y(alignment::Vertical::Center), +// vec![ +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ); + +// let bt = menu_tree!(button( +// text("Button") +// .width(Length::Fill) +// .height(Length::Fill) +// .vertical_alignment(alignment::Vertical::Center), +// ) +// .width(Length::Fill) +// .height(Length::Fill) +// .on_press(Message::Debug("Button".into()))); + +// let cb = menu_tree!(checkbox("Checkbox", app.check, Message::CheckChange).width(Length::Fill)); + +// let sld = menu_tree!(row![ +// "Slider", +// horizontal_space(Length::Fixed(8.0)), +// slider(0..=255, app.value, Message::ValueChange) +// ]); + +// let txn = menu_tree!(text_input("", &app.text).on_input(Message::TextChange)); + +// let root = menu_tree( +// debug_button("Widgets"), +// vec![ +// debug_item("You can use any widget"), +// debug_item("as a menu item"), +// bt, +// cb, +// sld, +// txn, +// sub_1, +// separator(), +// debug_item("Seperators are also widgets"), +// labeled_separator("Separator"), +// debug_item("Item"), +// debug_item("Item"), +// dot_separator(), +// debug_item("Item"), +// debug_item("Item"), +// ], +// ); + +// root +// } + +// fn menu_3<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); + +// let primary = debug_sub_menu( +// "Primary", +// vec![ +// menu_tree!(slider(0..=255, r, move |x| { +// Message::ColorChange(Color::from_rgb8(x, g, b)) +// })), +// menu_tree!(slider(0..=255, g, move |x| { +// Message::ColorChange(Color::from_rgb8(r, x, b)) +// })), +// menu_tree!(slider(0..=255, b, move |x| { +// Message::ColorChange(Color::from_rgb8(r, g, x)) +// })), +// ], +// ); + +// let root = menu_tree( +// debug_button("Controls"), +// vec![ +// menu_tree!(labeled_button("Flip Horizontal", Message::FlipHorizontal) +// .width(Length::Fill) +// .height(Length::Fill)), +// menu_tree!(labeled_button("Flip Vertical", Message::FlipVertical) +// .width(Length::Fill) +// .height(Length::Fill)), +// separator(), +// menu_tree!(row![toggler( +// Some("Dark Mode".into()), +// app.dark_mode, +// Message::ThemeChange +// )] +// .padding([0, 8])), +// color_item([0.45, 0.25, 0.57]), +// color_item([0.15, 0.59, 0.64]), +// color_item([0.76, 0.82, 0.20]), +// color_item([0.17, 0.27, 0.33]), +// primary, +// ], +// ); + +// root +// } + +// fn menu_4<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let dekjdaud = debug_sub_menu( +// "dekjdaud", +// vec![ +// debug_item("ajrs"), +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), +// debug_item("ajrs"), +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), +// ], +// ); + +// let luyortp = debug_sub_menu( +// "luyortp", +// vec![ +// debug_item("ajrs"), // 0 +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), // 13 +// ], +// ); + +// let jcsu = debug_sub_menu( +// "jcsu", +// vec![ +// debug_item("ajrs"), // 0 +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), +// luyortp, // 11 +// debug_item("mmdyrc"), +// debug_item("nquc"), // 13 +// ], +// ); + +// let root = menu_tree( +// debug_button("Scroll"), +// vec![ +// debug_item("ajrs"), // 0 +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// jcsu, // 9 +// debug_item("kaljkahd"), +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), // 13 +// debug_item("ajrs"), // 14 +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), // 27 +// debug_item("ajrs"), // 28 +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// dekjdaud, +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), // 41 +// debug_item("ajrs"), // 42 +// debug_item("bsdfho"), +// debug_item("clkjhbf"), +// debug_item("dekjdaud"), +// debug_item("ecsh"), +// debug_item("fweiu"), +// debug_item("giwe"), +// debug_item("heruyv"), +// debug_item("isabe"), +// debug_item("jcsu"), +// debug_item("kaljkahd"), // 52 +// debug_item("luyortp"), +// debug_item("mmdyrc"), +// debug_item("nquc"), // 55 +// ], +// ); + +// root +// } + +// fn menu_5<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let slider_count = 3; +// let slider_width = 30; +// let spacing = 4; + +// let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); + +// let sliders = menu_tree!(row![ +// vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( +// x, g, b +// ))) +// .width(30), +// vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( +// r, x, b +// ))) +// .width(30), +// vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( +// r, g, x +// ))) +// .width(30), +// ] +// .spacing(4)) +// .height(100); + +// let root = menu_tree( +// debug_button("Static"), +// vec![labeled_separator("Primary"), sliders], +// ) +// .width(slider_width * slider_count + (slider_count - 1) * spacing); + +// root +// } + +// fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { +// let slider_count = 3; +// let slider_width = 30; +// let spacing = 4; + +// let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); + +// let sliders = menu_tree!(row![ +// vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( +// x, g, b +// ))) +// .width(30), +// vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( +// r, x, b +// ))) +// .width(30), +// vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( +// r, g, x +// ))) +// .width(30), +// ] +// .spacing(4) +// .height(100)); + +// let root = menu_tree( +// debug_button("Dynamic Height"), +// vec![ +// labeled_separator("Primary"), +// sliders, +// debug_item2("AABB"), +// debug_item3("CCDD", 50.0), +// debug_item2("EEFF"), +// debug_item("GGHH").height(100), +// debug_item2("IIJJ"), +// ], +// ) +// .width(slider_width * slider_count + (slider_count - 1) * spacing); + +// root +// } -fn menu_2<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let sub_1 = menu_tree( - container(toggler( - Some("Or as a sub menu item".to_string()), - app.toggle, - Message::ToggleChange, - )) - .padding([0, 8]) - .height(Length::Fill) - .align_y(alignment::Vertical::Center), - vec![ - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - debug_item("Item"), - ], - ); - - let bt = menu_tree!(button( - text("Button") - .width(Length::Fill) - .height(Length::Fill) - .vertical_alignment(alignment::Vertical::Center), - ) - .width(Length::Fill) - .height(Length::Fill) - .on_press(Message::Debug("Button".into()))); - - let cb = menu_tree!(checkbox("Checkbox", app.check, Message::CheckChange).width(Length::Fill)); - - let sld = menu_tree!(row![ - "Slider", - horizontal_space(Length::Fixed(8.0)), - slider(0..=255, app.value, Message::ValueChange) - ]); - - let txn = menu_tree!(text_input("", &app.text).on_input(Message::TextChange)); - - let root = menu_tree( - debug_button("Widgets"), - vec![ - debug_item("You can use any widget"), - debug_item("as a menu item"), - bt, - cb, - sld, - txn, - sub_1, - separator(), - debug_item("Seperators are also widgets"), - labeled_separator("Separator"), - debug_item("Item"), - debug_item("Item"), - dot_separator(), - debug_item("Item"), - debug_item("Item"), - ], - ); - - root -} - -fn menu_3<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); - - let primary = debug_sub_menu( - "Primary", - vec![ - menu_tree!(slider(0..=255, r, move |x| { - Message::ColorChange(Color::from_rgb8(x, g, b)) - })), - menu_tree!(slider(0..=255, g, move |x| { - Message::ColorChange(Color::from_rgb8(r, x, b)) - })), - menu_tree!(slider(0..=255, b, move |x| { - Message::ColorChange(Color::from_rgb8(r, g, x)) - })), - ], - ); - - let root = menu_tree( - debug_button("Controls"), - vec![ - menu_tree!(labeled_button("Flip Horizontal", Message::FlipHorizontal) - .width(Length::Fill) - .height(Length::Fill)), - menu_tree!(labeled_button("Flip Vertical", Message::FlipVertical) - .width(Length::Fill) - .height(Length::Fill)), - separator(), - menu_tree!(row![toggler( - Some("Dark Mode".into()), - app.dark_mode, - Message::ThemeChange - )] - .padding([0, 8])), - color_item([0.45, 0.25, 0.57]), - color_item([0.15, 0.59, 0.64]), - color_item([0.76, 0.82, 0.20]), - color_item([0.17, 0.27, 0.33]), - primary, - ], - ); - - root -} - -fn menu_4<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let dekjdaud = debug_sub_menu( - "dekjdaud", - vec![ - debug_item("ajrs"), - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), - debug_item("ajrs"), - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), - ], - ); - - let luyortp = debug_sub_menu( - "luyortp", - vec![ - debug_item("ajrs"), // 0 - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), // 13 - ], - ); - - let jcsu = debug_sub_menu( - "jcsu", - vec![ - debug_item("ajrs"), // 0 - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), - luyortp, // 11 - debug_item("mmdyrc"), - debug_item("nquc"), // 13 - ], - ); - - let root = menu_tree( - debug_button("Scroll"), - vec![ - debug_item("ajrs"), // 0 - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - jcsu, // 9 - debug_item("kaljkahd"), - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), // 13 - debug_item("ajrs"), // 14 - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), // 27 - debug_item("ajrs"), // 28 - debug_item("bsdfho"), - debug_item("clkjhbf"), - dekjdaud, - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), // 41 - debug_item("ajrs"), // 42 - debug_item("bsdfho"), - debug_item("clkjhbf"), - debug_item("dekjdaud"), - debug_item("ecsh"), - debug_item("fweiu"), - debug_item("giwe"), - debug_item("heruyv"), - debug_item("isabe"), - debug_item("jcsu"), - debug_item("kaljkahd"), // 52 - debug_item("luyortp"), - debug_item("mmdyrc"), - debug_item("nquc"), // 55 - ], - ); - - root -} - -fn menu_5<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let slider_count = 3; - let slider_width = 30; - let spacing = 4; - - let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); - - let sliders = menu_tree!(row![ - vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( - x, g, b - ))) - .width(30), - vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( - r, x, b - ))) - .width(30), - vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( - r, g, x - ))) - .width(30), - ] - .spacing(4)) - .height(100); - - let root = menu_tree( - debug_button("Static"), - vec![labeled_separator("Primary"), sliders], - ) - .width(slider_width * slider_count + (slider_count - 1) * spacing); - - root -} - -fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { - let slider_count = 3; - let slider_width = 30; - let spacing = 4; - - let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); - - let sliders = menu_tree!(row![ - vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( - x, g, b - ))) - .width(30), - vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( - r, x, b - ))) - .width(30), - vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( - r, g, x - ))) - .width(30), - ] - .spacing(4) - .height(100)); - - let root = menu_tree( - debug_button("Dynamic Height"), - vec![ - labeled_separator("Primary"), - sliders, - debug_item2("AABB"), - debug_item3("CCDD", 50.0), - debug_item2("EEFF"), - debug_item("GGHH").height(100), - debug_item2("IIJJ"), - ], - ) - .width(slider_width * slider_count + (slider_count - 1) * spacing); - - root -} - */ diff --git a/src/native/helpers.rs b/src/native/helpers.rs index 4fe02d4a..46737005 100644 --- a/src/native/helpers.rs +++ b/src/native/helpers.rs @@ -64,63 +64,49 @@ macro_rules! wrap_vertical { ); } -/* /// Creates a [`MenuTree`] with the given children. +/// Creates a vec of menu items /// -/// [`MenuTree`]: crate::MenuTree +/// [`Item`]: crate::menu::Item #[cfg(feature = "menu")] #[macro_export] -macro_rules! menu_tree { - ($x:expr) => ( - $crate::menu::menu_tree::MenuTree::new($x) - ); - ($x:expr, $($y:expr),+ $(,)?) => ( - $crate::menu::menu_tree::MenuTree::with_children($x, vec![$($y),+]) - ); +macro_rules! menu_items { + ($($x:tt),+ $(,)?) => { + { + macro_rules! wrap_item { + (($i:expr, $m:expr)) => ( + $crate::menu::Item::with_menu($i, $m) + ); + (($i:expr)) => ( + $crate::menu::Item::new($i) + ); + } + + vec![ $( wrap_item!($x) ),+ ] + } + } } -/// Creates a [`MenuBar`] with the given children. +/// Creates a [`Menu`] with the given items. /// -/// [`MenuBar`]: crate::MenuBar +/// [`Menu`]: crate::menu::Menu #[cfg(feature = "menu")] #[macro_export] -macro_rules! menu_bar { - () => ( - $crate::menu::menu_bar::MenuBar::new(vec![]) - ); - ($($x:expr),+ $(,)?) => ( - $crate::menu::menu_bar::MenuBar::new(vec![$($x),+]) - ); +macro_rules! menu { + ($($x:tt),+ $(,)?) => { + $crate::menu::Menu::new( $crate::menu_items!( $($x),+ ) ) + } } -#[cfg(feature = "menu")] -/// Shortcut helper to create a [`MenuBar`] Widget. +/// Creates a [`MenuBar`] with the given children. /// -/// [`MenuBar`]: crate::MenuBar -#[must_use] -pub fn menu_bar( - menu_roots: Vec>, -) -> crate::menu::menu_bar::MenuBar -where - Renderer: core::Renderer, - Theme: crate::style::menu_bar::StyleSheet, -{ - crate::menu::menu_bar::MenuBar::new(menu_roots) -} - +/// [`MenuBar`]: crate::menu::MenuBar #[cfg(feature = "menu")] -/// Shortcut helper to create a [`MenuTree`] Widget. -/// -/// [`MenuTree`]: crate::MenuTree -#[must_use] -pub fn menu_tree<'a, Message, Theme, Renderer>( - item: impl Into>, - children: Vec>>, -) -> crate::menu::menu_tree::MenuTree<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - crate::menu::menu_tree::MenuTree::with_children(item, children) -} */ +#[macro_export] +macro_rules! menu_bar { + ($(($x:expr, $m:expr)),+ $(,)?) => ( + $crate::menu::MenuBar::new(vec![ $( Item::with_menu($x, $m) ),+ ]) + ); +} #[cfg(feature = "badge")] /// Shortcut helper to create a [`Badge`] Widget. diff --git a/src/native/menu.rs b/src/native/menu.rs index d5367f64..90643ccd 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -61,7 +61,6 @@ mod types; pub use crate::style::menu_bar::{Appearance, StyleSheet}; pub use menu_bar::MenuBar; pub use menu_tree::{Item, Menu}; -pub use types::*; // A `MenuBar` collects `MenuTree`s and handles // pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Renderer>; diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index cd94427f..0560543b 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -38,6 +38,7 @@ where padding: Padding, width: Length, height: Length, + check_bounds_width: f32, style: Theme::Style, } impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> @@ -58,6 +59,7 @@ where padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, + check_bounds_width: 50.0, style: Theme::Style::default() } } @@ -80,6 +82,12 @@ where self } + /// Sets the width of the check bounds of the [`Menu`]s in the [`MenuBar`]. + pub fn check_bounds_width(mut self, check_bounds_width: f32) -> Self{ + self.check_bounds_width = check_bounds_width; + self + } + /// Sets the padding of the [`MenuBar`]. pub fn padding(mut self, padding: impl Into) -> Self{ self.padding = padding.into(); @@ -247,6 +255,15 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { + let state = tree.state.downcast_ref::(); + let cursor = if state.open { + state.active_root.and_then(|active|{ + layout.children().nth(active).and_then(|l| + Some(mouse::Cursor::Available(l.bounds().center())) + ) + }).unwrap_or(cursor) + }else{ cursor }; + // println!("bar draw"); self.roots .iter() // [Item...] @@ -261,14 +278,12 @@ where &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, - renderer: &Renderer, + _renderer: &Renderer, ) -> Option> { // println!("bar overlay"); let state = tree.state.downcast_mut::(); let init_bar_bounds = layout.bounds(); - // let init_parent_bounds = state.active_root - // .map(|i| layout.children().nth(i).unwrap().bounds()); let init_root_bounds = layout.children().map(|l| l.bounds() ).collect(); if state.open { @@ -278,8 +293,8 @@ where tree, roots: &mut self.roots, init_bar_bounds, - // init_parent_bounds, init_root_bounds, + check_bounds_width: self.check_bounds_width, style: &self.style } .overlay_element(), diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 96dd6495..bfa8044c 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -29,6 +29,7 @@ where pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], pub(super) init_bar_bounds: Rectangle, pub(super) init_root_bounds: Vec, + pub(super) check_bounds_width: f32, pub(super) style: &'b Theme::Style, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> @@ -91,6 +92,7 @@ where item: &Item<'a, Message, Theme, Renderer>, tree: &mut Tree, menu_nodes: &mut Vec, + check_bounds_width: f32, parent_bounds: Rectangle, parent_direction: (Direction, Direction), // translation: Vector, @@ -100,7 +102,15 @@ where let menu = item.menu.as_ref().unwrap(); let menu_tree = &mut tree.children[1]; - let (menu_node, direction) = menu.layout(menu_tree, renderer, &Limits::NONE, parent_bounds, parent_direction, viewport); + let (menu_node, direction) = menu.layout( + menu_tree, + renderer, + &Limits::NONE, + check_bounds_width, + parent_bounds, + parent_direction, + viewport + ); // Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} menu_nodes.push(menu_node); @@ -116,7 +126,16 @@ where node.bounds() + (slice_node.bounds().position() - Point::ORIGIN) }; - rec(renderer, next_item, next_tree, menu_nodes, next_parent_bounds, direction, viewport); + rec( + renderer, + next_item, + next_tree, + menu_nodes, + check_bounds_width, + next_parent_bounds, + direction, + viewport + ); } } @@ -148,6 +167,7 @@ where active_root, active_tree, &mut menu_nodes, + self.check_bounds_width, parent_bounds, parent_direction, // translation, @@ -255,15 +275,19 @@ where match re { RecEvent::Event => RecEvent::Event, RecEvent::Close => { - menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); - if prev.is_some(){ - if cursor.is_over(offset_bounds){ - RecEvent::Event - }else{ + if cursor.is_over(prescroll){ + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.open_event(menu_tree, menu_layout, cursor); + RecEvent::Event + }else if cursor.is_over(offset_bounds){ + RecEvent::Event + }else{ + menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); + if prev.is_some(){ RecEvent::None + }else{ + RecEvent::Close } - }else{ - RecEvent::Close } } RecEvent::None => { @@ -271,6 +295,8 @@ where menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event + }else if cursor.is_over(offset_bounds){ + RecEvent::Event }else{ RecEvent::None } @@ -283,14 +309,12 @@ where menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event + }else if cursor.is_over(offset_bounds){ + RecEvent::Event }else{ menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); if prev.is_some(){ - if cursor.is_over(offset_bounds){ - RecEvent::Event - }else{ - RecEvent::None - } + RecEvent::None }else{ RecEvent::Close } diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 52074bad..0334d3ca 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -179,6 +179,7 @@ where tree: &mut Tree, renderer: &Renderer, limits: &Limits, + check_bounds_width: f32, parent_bounds: Rectangle, parent_direction: (Direction, Direction), viewport: &Rectangle, @@ -217,8 +218,7 @@ where let offset_bounds = Rectangle::new(offset_position, offset_size); let children_bounds = Rectangle::new(children_position, children_size); - let bounds_expand = 30.0; - let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); + let check_bounds = pad_rectangle(children_bounds, [check_bounds_width; 4].into()); let menu_state = tree.state.downcast_mut::(); @@ -230,21 +230,78 @@ where ); menu_state.slice = slice; - let slice_node = { + let slice_node = if slice.start_index == slice.end_index{ + let node = &items_node.children()[slice.start_index]; + let bounds = node.bounds(); + let start_offset = slice.lower_bound_rel - bounds.y; + + Node::with_children( + Size::new( + items_node.bounds().width, + slice.upper_bound_rel - slice.lower_bound_rel + ), + once( + Node::with_children( + Size::new( + bounds.width, + slice.upper_bound_rel - slice.lower_bound_rel + ), + node.children().iter().map(Clone::clone).collect() + ).move_to(bounds.position()) + .translate([0.0, start_offset]) + ).collect() + ) + }else{ + // let start_node = { + // let node = &items_node.children()[slice.start_index]; + // let bounds = node.bounds(); + // let start_offset = slice.lower_bound_rel - bounds.y; + // Node::with_children( + // Size::new( + // bounds.width, + // bounds.height - start_offset + // ), + // node.children().iter().map(Clone::clone).collect() + // ).move_to(bounds.position()) + // .translate([0.0, start_offset]) + // }; + + // let start_node = { + // let node = &items_node.children()[slice.start_index]; + // let bounds = node.bounds(); + // clip_node( + // node, + // Rectangle{ + // x: bounds.x, + // y: slice.lower_bound_rel, + // width: bounds.width, + // height: bounds.height - (slice.lower_bound_rel - bounds.y), + // } + // ) + // }; + + // let start_node = { + // let node = &items_node.children()[slice.start_index]; + // let bounds = node.bounds(); + // let start_offset = slice.lower_bound_rel - bounds.y; + // Node::with_children( + // bounds.size(), + // node.children().iter().map(Clone::clone).collect() + // ).move_to(bounds.position()) + // .translate([0.0, start_offset]) + // }; + let start_node = { let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; - Node::with_children( - Size::new( - bounds.width, - bounds.height - start_offset - ), - node.children().iter().map(Clone::clone).collect() - ).move_to(bounds.position()) + scale_node_y( + node, + (bounds.height - start_offset) / bounds.height + ) .translate([0.0, start_offset]) }; - + let end_node = { let node = &items_node.children()[slice.end_index]; let bounds = node.bounds(); @@ -275,6 +332,7 @@ where ) }; + ( Node::with_children( Size::INFINITY, @@ -371,16 +429,17 @@ where let mut lc = layout.children(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); - // let offset_bounds = lc.next().unwrap().bounds(); - // let check_bounds = lc.next().unwrap().bounds(); + let offset_bounds = lc.next().unwrap().bounds(); + let check_bounds = lc.next().unwrap().bounds(); let menu_state = tree.state.downcast_ref::(); let slice = &menu_state.slice; let styling = theme.appearance(theme_style); - // debug_draw(renderer, check_bounds, offset_bounds, parent_bounds); + // debug_draw(renderer, prescroll, check_bounds, offset_bounds); + // draw background renderer.fill_quad( renderer::Quad{ bounds: pad_rectangle(prescroll, styling.background_expand), @@ -390,13 +449,69 @@ where styling.background ); - for ((item, tree), layout) in self - .items[ slice.start_index .. slice.end_index+1 ].iter() // [item...].iter() - .zip(tree.children[ slice.start_index .. slice.end_index+1 ].iter()) // [item_tree...] - .zip(slice_layout.children()) // [item_layout...] + let mut slc = slice_layout.children(); + + // draw start + let Some(start) = self.items.get(slice.start_index) + else{ return }; + let Some(start_tree) = tree.children.get(slice.start_index) + else { return }; + let Some(start_layout) = slc.next() + else { return }; + + let start_bounds = start_layout.bounds(); + // if let Some(sec_layout) = slc.next(){ + // let sec_bounds = sec_layout.bounds(); + // renderer.with_layer( + // Rectangle{ + // x: start_bounds.x, + // y: start_bounds.y, + // width: f32::MAX, + // height: sec_bounds.y - start_bounds.y, + // }, + // |r| start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) + // ); + // }else{ + // start.draw(start_tree, renderer, theme, style, start_layout, cursor, viewport) + // } + + renderer.with_layer( + start_bounds, + |r| start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) + ); + + + if slice.end_index == slice.start_index { + return + } + + // draw the rest + let Some(items) = self.items.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) + else { return; }; + + let Some(trees) = tree.children.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) + else { return; }; + + for ((item, tree), layout) in items.iter() // [item...].iter() + .zip(trees.iter()) // [item_tree...] + .zip(slice_layout.children().skip(1)) // [item_layout...] { item.draw(tree, renderer, theme, style, layout, cursor, &viewport); } + + + // let Some(items) = self.items.get(slice.start_index .. slice.end_index.saturating_add(1)) + // else { return }; + + // let Some(trees) = tree.children.get(slice.start_index .. slice.end_index.saturating_add(1)) + // else { return }; + + // for ((item, tree), layout) in items.iter() // [item...].iter() + // .zip(trees.iter()) // [item_tree...] + // .zip(slice_layout.children()) // [item_layout...] + // { + // item.draw(tree, renderer, theme, style, layout, cursor, &viewport); + // } } pub(super) fn open_event( @@ -468,21 +583,25 @@ where } -/* fn debug_draw( +fn debug_draw( renderer: &mut Renderer, + + prescroll: Rectangle, check_bounds: Rectangle, offset_bounds: Rectangle, - parent_bounds: Rectangle, + // parent_bounds: Rectangle, ){ [ + prescroll, check_bounds, offset_bounds, - parent_bounds + // parent_bounds ].iter() .zip([ + Color::from([1.0, 1.0, 1.0, 0.8]), Color::from([1.0, 0.0, 0.0, 0.1]), Color::from([0.0, 0.0, 1.0, 0.3]), - Color::from([1.0, 1.0, 0.0, 0.5]) + // Color::from([1.0, 1.0, 0.0, 0.5]) ]) .for_each(|(b, c)|{ renderer.fill_quad( @@ -497,7 +616,7 @@ where c ); }); -} */ +} /// Item inside a [`Menu`] @@ -942,3 +1061,59 @@ fn search_bound( index } +fn clip_node( + node: &Node, + bounds: Rectangle, +) -> Node{ + fn clip( + bounds: &Rectangle, + parent_offset: Vector, + node: &Node, + ) -> Node{ + let node_bounds = node.bounds() + parent_offset; + bounds.intersection(&node_bounds) + .and_then(|new_bounds|{Some( + Node::with_children( + new_bounds.size(), + node.children().iter().map(|n|{ + clip( + bounds, + node_bounds.position() - Point::ORIGIN, + n + ).translate(node_bounds.position() - new_bounds.position()) + }).collect() + ).move_to(new_bounds.position()) + .translate(parent_offset * -1.0) + )}) + .unwrap_or( + Node::with_children( + Size::ZERO, + node.children().iter().map(Clone::clone).collect() + ).move_to(node.bounds().position()) + ) + } + + clip(&bounds, Vector::ZERO, node) +} + + +fn scale_node_y( + node: &Node, + factor: f32, +) -> Node{ + let node_bounds = node.bounds(); + Node::with_children( + Size::new( + node_bounds.width, + node_bounds.height * factor + ), + node.children().iter().map(|n|{ + let n_bounds = n.bounds(); + scale_node_y(n, factor) + .move_to(Point::new( + n_bounds.x, + n_bounds.y * factor + )) + }).collect() + ).move_to(node_bounds.position()) +} diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index bffea66d..857213f7 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -1,7 +1,7 @@ //! Change the appearance of menu bars and their menus. use iced_widget::{ core::{ - Color, Border, Background, Shadow, Padding + Color, Border, Background, Shadow, Padding, Vector }, style::Theme }; @@ -23,11 +23,14 @@ impl std::default::Default for Appearance { Self { background: Color::from([0.85; 3]).into(), border: Border{ - color: Color::from([0.5; 3]), - width: 1.0, radius: [6.0; 4].into(), + ..Default::default() + }, + shadow: Shadow{ + color: Color::from([0.0, 0.0, 0.0, 0.5]), + offset: Vector::ZERO, + blur_radius: 10.0, }, - shadow: Shadow::default(), background_expand: [6; 4].into(), } } @@ -76,13 +79,12 @@ impl StyleSheet for Theme { match style { MenuBarStyle::Default => Appearance { background: palette.background.base.color.into(), - border: Border{ - color: palette.background.weak.color.into(), - width: 1.0, - radius: [6.0; 4].into(), - }, - shadow: Shadow::default(), - background_expand: [6; 4].into() + // border: Border{ + // color: palette.background.weak.color.into(), + // width: 1.0, + // radius: [6.0; 4].into(), + // }, + ..Default::default() }, MenuBarStyle::Custom(c) => c.appearance(self), } From 1fd3212b6d28555015be41dcbdecd72aae101f77 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Tue, 20 Feb 2024 10:29:15 +0800 Subject: [PATCH 14/40] menu bar styling --- examples/menu/src/main.rs | 59 ++++++++++++++----------- src/native/menu.rs | 2 +- src/native/menu/{types.rs => common.rs} | 11 +++++ src/native/menu/menu_bar.rs | 12 ++++- src/native/menu/menu_bar_overlay.rs | 2 +- src/native/menu/menu_tree.rs | 22 +++------ src/style/menu_bar.rs | 38 +++++++++++----- 7 files changed, 92 insertions(+), 54 deletions(-) rename src/native/menu/{types.rs => common.rs} (79%) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 57224e10..7c512506 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -199,7 +199,7 @@ impl Application for App { fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { // println!("app view"); - + let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(12.0); let menu_temp_2 = |items| Menu::new(items).max_width(180.0); @@ -297,7 +297,7 @@ impl Application for App { Message::ToggleChange, )) .padding([0, 8]) - .height(Length::Fill) + .height(30.0) .align_y(alignment::Vertical::Center), menu_temp_2(menu_items!( (debug_button("Item")), @@ -306,13 +306,12 @@ impl Application for App { (debug_button("Item")), )) ), + (debug_button("Separator")), (separator()), - (debug_button("Seperators are also widgets")), + (debug_button("Labeled Separator")), (labeled_separator("Separator")), - (debug_button("Item")), - (debug_button("Item")), - (dot_separator()), - (debug_button("Item")), + (debug_button("Dot Separator")), + (dot_separator(&self.theme)), (debug_button("Item")), )).width(240.0)), (debug_button_s("Controls"), menu_temp_1(menu_items!( @@ -493,11 +492,14 @@ impl Application for App { ].spacing(spacing) .height(100.0) ), + (separator()), (debug_button("AABB").height(40)), (debug_button("CCDD").height(140)), (debug_button("EEFF").height(30)), (debug_button("GGHH").height(100)), (debug_button("IIJJ").height(60)), + (debug_button("KKLL").height(120)), + (debug_button("MMNN").height(50)), )).width(slider_width * slider_count + (slider_count - 1) * spacing) }), /* (debug_button_s("content"), @@ -626,7 +628,7 @@ impl Application for App { )) ) */ - ).check_bounds_width(80.0); + ); /* let mb = row![ Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ @@ -726,8 +728,8 @@ impl Application for App { .padding([2, 8]) .align_items(alignment::Alignment::Center); */ - let r = row![horizontal_space(400), mb, horizontal_space(400),] - .padding([2, 8]) + let r = row![horizontal_space(295), mb, horizontal_space(295),] + // .padding([2, 8]) .align_items(alignment::Alignment::Center); // let top_bar_style: fn(&iced::Theme) -> container::Appearance = @@ -746,11 +748,16 @@ impl Application for App { c.into() */ - let c = col![vertical_space(400), r, vertical_space(400),]; - + let c = col![vertical_space(450), r, vertical_space(450),]; + let sc = scrollable(c) + // .direction(scrollable::Direction::Vertical( + // scrollable::Properties::new() + // .alignment(scrollable::Alignment::End) + // )); .direction(scrollable::Direction::Both { - vertical: scrollable::Properties::new(), + vertical: scrollable::Properties::new() + .alignment(scrollable::Alignment::End), horizontal: scrollable::Properties::new(), }); @@ -927,15 +934,19 @@ fn separator<'a>() -> quad::Quad { } } -fn dot_separator<'a>() -> text::Text<'a, iced::Theme, iced::Renderer> { - text("···································") - .size(22) - // .shaping(text::Shaping::Advanced) - .width(Length::Fill) - .height(Length::Fill) - .horizontal_alignment(alignment::Horizontal::Center) - .vertical_alignment(alignment::Vertical::Center) - .height(30.0) +fn dot_separator<'a>(theme: &iced::Theme) -> Element<'a, Message, iced::Theme, iced::Renderer> { + row( + (0..20).map(|_|{ + quad::Quad { + color: theme.extended_palette().background.base.text, + border_radius: [4.0; 4], + inner_bounds: quad::InnerBounds::Square(4.0), + ..Default::default() + }.into() + }) + ) + .height(30.0) + .into() } fn labeled_separator(label: &'_ str) -> Element<'_, Message, iced::Theme, iced::Renderer> { @@ -943,16 +954,12 @@ fn labeled_separator(label: &'_ str) -> Element<'_, Message, iced::Theme, iced:: color: [0.5; 3].into(), border_radius: [4.0; 4], inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), - // background: Some(Color::from([0.8;3]).into()), - // height: Length::Shrink, ..Default::default() }; let q_2 = quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), - // background: Some(Color::from([0.8;3]).into()), - // height: Length::Shrink, ..Default::default() }; diff --git a/src/native/menu.rs b/src/native/menu.rs index 90643ccd..5db3d33e 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -56,7 +56,7 @@ mod flex; mod menu_bar; mod menu_bar_overlay; mod menu_tree; -mod types; +mod common; pub use crate::style::menu_bar::{Appearance, StyleSheet}; pub use menu_bar::MenuBar; diff --git a/src/native/menu/types.rs b/src/native/menu/common.rs similarity index 79% rename from src/native/menu/types.rs rename to src/native/menu/common.rs index e48235b0..5cb44516 100644 --- a/src/native/menu/types.rs +++ b/src/native/menu/common.rs @@ -1,3 +1,6 @@ +use iced_widget::core::{ + Padding, Rectangle +}; /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] @@ -48,3 +51,11 @@ pub(super) enum Axis { pub(super) type Index = Option; +pub fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { + Rectangle { + x: rect.x - padding.left, + y: rect.y - padding.top, + width: rect.width + padding.horizontal(), + height: rect.height + padding.vertical(), + } +} diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 0560543b..fe7d90f7 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -9,7 +9,7 @@ use iced_widget::{core::{ Rectangle, Shell, Size, Widget, }, Theme}; -use super::{flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*, types::*}; +use super::{flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*, common::*}; use crate::style::menu_bar::*; pub(super) struct MenuBarState { @@ -264,6 +264,16 @@ where }).unwrap_or(cursor) }else{ cursor }; + let styling = theme.appearance(&self.style); + renderer.fill_quad( + renderer::Quad{ + bounds: pad_rectangle(layout.bounds(), styling.bar_background_expand), + border: styling.bar_border, + shadow: styling.bar_shadow, + }, + styling.bar_background + ); + // println!("bar draw"); self.roots .iter() // [Item...] diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index bfa8044c..20339699 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -7,7 +7,7 @@ use iced_widget::core::{ Shell, Size, Vector, Widget, }; -use super::{menu_bar::MenuBarState, menu_tree::*, types::*}; +use super::{menu_bar::MenuBarState, menu_tree::*, common::*}; use crate::style::menu_bar::*; /// Should be returned from the recursive event processing function, diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 0334d3ca..4d2918c2 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,7 +1,7 @@ //! [`Item`] and [`Menu`] //! use std::iter::once; -use super::types::*; +use super::common::*; use super::flex; use iced_widget::core::{ alignment, event, @@ -173,7 +173,7 @@ where /// tree: Tree{menu_state, \[item_tree...]} /// - /// out: Node{inf, \[ items_node, prescroll, offset_boundss, check_bounds ]} + /// out: Node{inf, \[ items_node, prescroll, offset_bounds, check_bounds ]} pub(super) fn layout( &self, tree: &mut Tree, @@ -353,7 +353,7 @@ where } /// tree: Tree{menu_state, \[item_tree...]} /// - /// layout: Node{inf, \[ slice_node, prescroll, offset_boundss, check_bounds ]} + /// layout: Node{inf, \[ slice_node, prescroll, offset_bounds, check_bounds ]} pub(super) fn on_event( &mut self, tree: &mut Tree, @@ -442,11 +442,11 @@ where // draw background renderer.fill_quad( renderer::Quad{ - bounds: pad_rectangle(prescroll, styling.background_expand), - border: styling.border, - shadow: styling.shadow, + bounds: pad_rectangle(prescroll, styling.menu_background_expand), + border: styling.menu_border, + shadow: styling.menu_shadow, }, - styling.background + styling.menu_background ); let mut slc = slice_layout.children(); @@ -941,14 +941,6 @@ impl Aod { } } -fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { - Rectangle { - x: rect.x - padding.left, - y: rect.y - padding.top, - width: rect.width + padding.horizontal(), - height: rect.height + padding.vertical(), - } -} fn process_scroll_event( menu_state: &mut MenuState, diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 857213f7..0fd3963d 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -9,29 +9,46 @@ use iced_widget::{ /// The appearance of a menu bar and its menus. #[derive(Debug, Clone, Copy)] pub struct Appearance { + /// The background of the menu bar. + pub bar_background: Background, + /// The border of the menu bar. + pub bar_border: Border, + /// The shadow of the menu bar. + pub bar_shadow: Shadow, + /// Expand the menu bar background + pub bar_background_expand: Padding, + /// The background of the menus. - pub background: Background, + pub menu_background: Background, /// The border of the menus. - pub border: Border, + pub menu_border: Border, /// The shadow of the menus - pub shadow: Shadow, - /// Expand the background - pub background_expand: Padding, + pub menu_shadow: Shadow, + /// Expand the menu background + pub menu_background_expand: Padding, } impl std::default::Default for Appearance { fn default() -> Self { Self { - background: Color::from([0.85; 3]).into(), - border: Border{ + bar_background: Color::from([0.85; 3]).into(), + bar_border: Border{ + radius: [6.0; 4].into(), + ..Default::default() + }, + bar_shadow: Shadow::default(), + bar_background_expand: [3; 4].into(), + + menu_background: Color::from([0.85; 3]).into(), + menu_border: Border{ radius: [6.0; 4].into(), ..Default::default() }, - shadow: Shadow{ + menu_shadow: Shadow{ color: Color::from([0.0, 0.0, 0.0, 0.5]), offset: Vector::ZERO, blur_radius: 10.0, }, - background_expand: [6; 4].into(), + menu_background_expand: [6; 4].into(), } } } @@ -78,7 +95,8 @@ impl StyleSheet for Theme { match style { MenuBarStyle::Default => Appearance { - background: palette.background.base.color.into(), + bar_background: palette.background.base.color.into(), + menu_background: palette.background.base.color.into(), // border: Border{ // color: palette.background.weak.color.into(), // width: 1.0, From 9b6aa0e02ec61e44d747858dad198a49178cd368 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 10:30:53 +0800 Subject: [PATCH 15/40] helper macros remove end comma --- examples/menu/src/main.rs | 637 ++++++++++++-------------------------- src/native/helpers.rs | 46 ++- 2 files changed, 235 insertions(+), 448 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 7c512506..bf3a4feb 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -207,71 +207,61 @@ impl Application for App { (debug_button_s("Nested Menus"), { let sub5 = menu_temp_2(menu_items!( - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) )); let sub4 = menu_temp_2(menu_items!( - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) )).width(180.0); let sub3 = menu_temp_2(menu_items!( - (debug_button("You can")), - (debug_button("nest menus")), - (submenu_button("SUB"), - sub4 - ), - (debug_button("how ever")), - (debug_button("You like")), - (submenu_button("SUB"), - sub5 - ), + (debug_button("You can")) + (debug_button("nest menus")) + (submenu_button("SUB"), sub4) + (debug_button("how ever")) + (debug_button("You like")) + (submenu_button("SUB"), sub5) )).width(160.0); let sub2 = menu_temp_2(menu_items!( - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), - (submenu_button("More sub menus"), - sub3 - ), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (submenu_button("More sub menus"), sub3) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) )).width(140.0); let sub1 = menu_temp_2(menu_items!( - (debug_button("Item")), - (debug_button("Item")), - (submenu_button("Another sub menu"), - sub2 - ), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), + (debug_button("Item")) + (debug_button("Item")) + (submenu_button("Another sub menu"), sub2) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) )).width(220.0); menu_temp_1(menu_items!( - (debug_button("Item")), - (debug_button("Item")), - (submenu_button("A sub menu"), - sub1 - ), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), + (debug_button("Item")) + (debug_button("Item")) + (submenu_button("A sub menu"), sub1) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) )).width(110.0) } - ), + ) (debug_button_s("Widgets"), menu_temp_1(menu_items!( - (debug_button("You can use any widget")), - (debug_button("as a menu item")), + (debug_button("You can use any widget")) + (debug_button("as a menu item")) (button( text("Button") .width(Length::Fill) @@ -279,18 +269,18 @@ impl Application for App { ) .width(Length::Fill) .on_press(Message::Debug("Button".into())) - ), + ) (checkbox("Checkbox", self.check, Message::CheckChange) .width(Length::Fill) - ), + ) ( row![ "Slider", horizontal_space(Length::Fixed(8.0)), slider(0..=255, self.value, Message::ValueChange) ] - ), - (text_input("", &self.text).on_input(Message::TextChange)), + ) + (text_input("", &self.text).on_input(Message::TextChange)) (container(toggler( Some("Or as a sub menu item".to_string()), self.toggle, @@ -300,31 +290,31 @@ impl Application for App { .height(30.0) .align_y(alignment::Vertical::Center), menu_temp_2(menu_items!( - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), - (debug_button("Item")), + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) )) - ), - (debug_button("Separator")), - (separator()), - (debug_button("Labeled Separator")), - (labeled_separator("Separator")), - (debug_button("Dot Separator")), - (dot_separator(&self.theme)), - (debug_button("Item")), - )).width(240.0)), + ) + (debug_button("Separator")) + (separator()) + (debug_button("Labeled Separator")) + (labeled_separator("Separator")) + (debug_button("Dot Separator")) + (dot_separator(&self.theme)) + (debug_button("Item")) + )).width(240.0)) (debug_button_s("Controls"), menu_temp_1(menu_items!( (row![toggler( Some("Dark Mode".into()), self.dark_mode, Message::ThemeChange )].padding([0, 8]) - ), - (color_button([0.45, 0.25, 0.57])), - (color_button([0.15, 0.59, 0.64])), - (color_button([0.76, 0.82, 0.20])), - (color_button([0.17, 0.27, 0.33])), + ) + (color_button([0.45, 0.25, 0.57])) + (color_button([0.15, 0.59, 0.64])) + (color_button([0.76, 0.82, 0.20])) + (color_button([0.17, 0.27, 0.33])) (labeled_button("Primary", Message::None) .width(Length::Fill), { @@ -333,134 +323,134 @@ impl Application for App { menu_temp_2(menu_items!( (slider(0..=255, r, move |x| { Message::ColorChange(Color::from_rgb8(x, g, b)) - })), + })) (slider(0..=255, g, move |x| { Message::ColorChange(Color::from_rgb8(r, x, b)) - })), + })) (slider(0..=255, b, move |x| { Message::ColorChange(Color::from_rgb8(r, g, x)) - })), + })) )) } ) - ))), + ))) (debug_button_s("Scroll"), menu_temp_1(menu_items!( - (debug_button("ajrs")), - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) (submenu_button("jcsu"), menu_temp_2(menu_items!( - (debug_button("ajrs")), - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) (submenu_button("luyortp"), menu_temp_2(menu_items!( - (debug_button("ajrs")), - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), - ))), - (debug_button("mmdyrc")), - (debug_button("nquc")), - ))), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), // 13 - (debug_button("ajrs")), // 14 - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), // 27 - (debug_button("ajrs")), // 28 - (debug_button("bsdfho")), - (debug_button("clkjhbf")), + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + ))) + (debug_button("mmdyrc")) + (debug_button("nquc")) + ))) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) // 13 + (debug_button("ajrs")) // 14 + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) // 27 + (debug_button("ajrs")) // 28 + (debug_button("bsdfho")) + (debug_button("clkjhbf")) (submenu_button("dekjdaud"), menu_temp_2(menu_items!( - (debug_button("ajrs")), - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), - (debug_button("ajrs")), - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), - ))), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), - (debug_button("ajrs")), - (debug_button("bsdfho")), - (debug_button("clkjhbf")), - (debug_button("dekjdaud")), - (debug_button("ecsh")), - (debug_button("fweiu")), - (debug_button("giwe")), - (debug_button("heruyv")), - (debug_button("isabe")), - (debug_button("jcsu")), - (debug_button("kaljkahd")), - (debug_button("luyortp")), - (debug_button("mmdyrc")), - (debug_button("nquc")), - ))), + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + ))) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + ))) (debug_button_s("Dynamic height"), { let slider_count = 3; let slider_width = 30; @@ -468,7 +458,7 @@ impl Application for App { let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); menu_temp_1(menu_items!( - (labeled_separator("Primary")), + (labeled_separator("Primary")) ( row![ vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( @@ -491,263 +481,22 @@ impl Application for App { , ].spacing(spacing) .height(100.0) - ), - (separator()), - (debug_button("AABB").height(40)), - (debug_button("CCDD").height(140)), - (debug_button("EEFF").height(30)), - (debug_button("GGHH").height(100)), - (debug_button("IIJJ").height(60)), - (debug_button("KKLL").height(120)), - (debug_button("MMNN").height(50)), + ) + (separator()) + (debug_button("AABB").height(40)) + (debug_button("CCDD").height(140)) + (debug_button("EEFF").height(30)) + (debug_button("GGHH").height(100)) + (debug_button("IIJJ").height(60)) + (debug_button("KKLL").height(120)) + (debug_button("MMNN").height(50)) )).width(slider_width * slider_count + (slider_count - 1) * spacing) - }), - /* (debug_button_s("content"), - menu_temp_1(menu_items!( - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - (submenu_button("htrsth"), - menu_temp_2(menu_items!( - (debug_button("ccgh")), - (debug_button("kuyg")), - (debug_button("vcsa")), - (debug_button("kiug")), - )) - ), - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - )) - ), - (debug_button_s("aaa"), - menu_temp_1(menu_items!( - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - (submenu_button("syjdtyjd"), - menu_temp_2(menu_items!( - (debug_button("hghg")), - (debug_button("kuyg")), - (debug_button("arga")), - (debug_button("abcd")), - (debug_button("vcsa")), - (submenu_button("htrsthfs"), - menu_temp_2(menu_items!( - (debug_button("hghg")), - (debug_button("kuyg")), - (debug_button("vcsa")), - (debug_button("kiug")), - )).max_width(220.0) - ), - (debug_button("kiug")), - )).max_width(200.0) - ), - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - )).max_width(180.0) - ), - (debug_button_s("pondjssbah"), - menu_temp_1(menu_items!( - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - (debug_button("htrsrt")), - (debug_button("htrdf")), - (debug_button("ngfcgng")), - (debug_button("hytfy")), - (debug_button("kuyg")), - (debug_button("qegvd")), - (submenu_button("iuoiy"), - menu_temp_2(menu_items!( - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - (debug_button("htrsrt")), - (debug_button("htrdf")), - (debug_button("ngfcgng")), - (debug_button("hytfy")), - (debug_button("kuyg")), - (debug_button("qegvd")), - (debug_button("iuoiy")), - (debug_button("rzsajf")), - (debug_button("pkmehs")), - (debug_button("ivrye")), - (debug_button("zhdkr")), - (debug_button("vjdiwo")), - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - (debug_button("htrsrt")), - (debug_button("htrdf")), - (debug_button("ngfcgng")), - (debug_button("hytfy")), - (debug_button("kuyg")), - (debug_button("qegvd")), - (debug_button("iuoiy")), - (debug_button("rzsajf")), - (debug_button("pkmehs")), - (debug_button("ivrye")), - (debug_button("zhdkr")), - (debug_button("vjdiwo")), - )) - ), - (debug_button("rzsajf")), - (debug_button("pkmehs")), - (debug_button("ivrye")), - (debug_button("zhdkr")), - (debug_button("vjdiwo")), - (debug_button("abc")), - (debug_button("def")), - (debug_button("xxx")), - (debug_button("htrsrt")), - (debug_button("htrdf")), - (debug_button("ngfcgng")), - (debug_button("hytfy")), - (debug_button("kuyg")), - (debug_button("qegvd")), - (debug_button("iuoiy")), - (debug_button("rzsajf")), - (debug_button("pkmehs")), - (debug_button("ivrye")), - (debug_button("zhdkr")), - (debug_button("vjdiwo")), - )) - ), - (debug_button_s("kjdfnh"), - menu_temp_1(menu_items!( - (debug_button("dfths").height(30.0)), - (debug_button("iodfns").height(80.0)), - (debug_button("dfkmjk").height(50.0)), - (debug_button("uvbw").height(200.0)), - (debug_button("ngxg").height(100.0)), - (debug_button("udrth").height(80.0)), - (debug_button("aervx").height(120.0)), - (debug_button("iusdz").height(70.0)), - )) - ) - */ + }) ); - /* let mb = row![ - Menux::new(button("content").on_press(Message::Debug("content".into())).into(), vec![ - debug_button("abc").width(180.0).into(), - debug_button("def").width(180.0).into(), - debug_button("xxx").width(180.0).into(), - Menux::new(debug_button("htrsth").width(180.0).into(), vec![ - debug_button("ccgh").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("vcsa").width(180.0).into(), - debug_button("kiug").width(180.0).into(), - ]).axis(Axis::Horizontal).into() - ]).open_condition(OpenCondition::Click), - Menux::new(debug_button("aaa").into(), vec![ - debug_button("abc").width(180.0).into(), - debug_button("def").width(180.0).into(), - debug_button("xxx").width(180.0).into(), - Menux::new(debug_button("syjdtyjd").width(180.0).into(), vec![ - debug_button("hghg").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("arga").width(180.0).into(), - debug_button("abcd").width(180.0).into(), - debug_button("vcsa").width(180.0).into(), - Menux::new(debug_button("htrsthfs").width(180.0).into(), vec![ - debug_button("hghg").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("vcsa").width(180.0).into(), - debug_button("kiug").width(180.0).into(), - ]).axis(Axis::Horizontal).into(), - debug_button("kiug").width(180.0).into(), - ]).axis(Axis::Horizontal).into(), - debug_button("abc").width(180.0).into(), - debug_button("def").width(180.0).into(), - debug_button("xxx").width(180.0).into(), - ]).open_condition(OpenCondition::Click), - Menux::new(debug_button("pondjssbah").into(), vec![ - debug_button("abc").width(180.0).into(), - debug_button("def").width(180.0).into(), - debug_button("xxx").width(180.0).into(), - debug_button("htrsrt").width(180.0).into(), - debug_button("htrdf").width(180.0).into(), - debug_button("ngfcgng").width(180.0).into(), - debug_button("hytfy").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("qegvd").width(180.0).into(), - debug_button("iuoiy").width(180.0).into(), - debug_button("rzsajf").width(180.0).into(), - debug_button("pkmehs").width(180.0).into(), - debug_button("ivrye").width(180.0).into(), - debug_button("zhdkr").width(180.0).into(), - debug_button("vjdiwo").width(180.0).into(), - Menux::new(debug_button("syjdtyjd").width(180.0).into(), vec![ - debug_button("hghg").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("arga").width(180.0).into(), - debug_button("abcd").width(180.0).into(), - debug_button("vcsa").width(180.0).into(), - Menux::new(debug_button("htrsthfs").width(180.0).into(), vec![ - debug_button("hghg").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("vcsa").width(180.0).into(), - debug_button("kiug").width(180.0).into(), - ]).axis(Axis::Horizontal).into(), - debug_button("kiug").width(180.0).into(), - ]).axis(Axis::Horizontal).into(), - debug_button("abc").width(180.0).into(), - debug_button("def").width(180.0).into(), - debug_button("xxx").width(180.0).into(), - debug_button("htrsrt").width(180.0).into(), - debug_button("htrdf").width(180.0).into(), - debug_button("ngfcgng").width(180.0).into(), - debug_button("hytfy").width(180.0).into(), - debug_button("kuyg").width(180.0).into(), - debug_button("qegvd").width(180.0).into(), - debug_button("iuoiy").width(180.0).into(), - debug_button("rzsajf").width(180.0).into(), - debug_button("pkmehs").width(180.0).into(), - debug_button("ivrye").width(180.0).into(), - debug_button("zhdkr").width(180.0).into(), - debug_button("vjdiwo").width(180.0).into(), - ]).open_condition(OpenCondition::Click), - ]; */ - - /* let r = if self.flip_h { - row!( - // pick_size_option, - horizontal_space(Length::Fill), - mb, - ) - } else { - row!( - mb, - horizontal_space(Length::Fill), - // pick_size_option - ) - } - .padding([2, 8]) - .align_items(alignment::Alignment::Center); */ - let r = row![horizontal_space(295), mb, horizontal_space(295),] - // .padding([2, 8]) .align_items(alignment::Alignment::Center); - // let top_bar_style: fn(&iced::Theme) -> container::Appearance = - // |_theme| container::Appearance { - // background: Some(Color::TRANSPARENT.into()), - // ..Default::default() - // }; - // let top_bar = container(r).width(Length::Fill).style(top_bar_style); - - /* - let c = if self.flip_v { - col![back, top_bar,] - } else { - col![top_bar, back,] - }; - - c.into() */ - let c = col![vertical_space(450), r, vertical_space(450),]; let sc = scrollable(c) @@ -760,11 +509,13 @@ impl Application for App { .alignment(scrollable::Alignment::End), horizontal: scrollable::Properties::new(), }); - - let back_style: fn(&iced::Theme) -> container::Appearance = |theme| container::Appearance { - background: Some(theme.extended_palette().primary.base.color.into()), - ..Default::default() - }; + + fn back_style(theme: &iced::Theme) -> container::Appearance{ + container::Appearance { + background: Some(theme.extended_palette().primary.base.color.into()), + ..Default::default() + } + } let back = container(sc) .width(Length::Fill) .height(Length::Fill) diff --git a/src/native/helpers.rs b/src/native/helpers.rs index 46737005..0874bf4b 100644 --- a/src/native/helpers.rs +++ b/src/native/helpers.rs @@ -67,13 +67,26 @@ macro_rules! wrap_vertical { /// Creates a vec of menu items /// /// [`Item`]: crate::menu::Item +/// +/// Syntax: +/// ``` +/// menu_items!( +/// (widget) +/// (widget) +/// (widget, menu) +/// (widget) +/// (widget, menu) +/// (widget) +/// ... +/// ) +/// ``` #[cfg(feature = "menu")] #[macro_export] macro_rules! menu_items { - ($($x:tt),+ $(,)?) => { + ($($x:tt)+) => { { macro_rules! wrap_item { - (($i:expr, $m:expr)) => ( + (($i:expr , $m:expr)) => ( $crate::menu::Item::with_menu($i, $m) ); (($i:expr)) => ( @@ -89,21 +102,44 @@ macro_rules! menu_items { /// Creates a [`Menu`] with the given items. /// /// [`Menu`]: crate::menu::Menu +/// +/// Syntax: +/// ``` +/// menu!( +/// (widget) +/// (widget) +/// (widget, menu) +/// (widget) +/// (widget, menu) +/// (widget) +/// ... +/// ) +/// ``` #[cfg(feature = "menu")] #[macro_export] macro_rules! menu { - ($($x:tt),+ $(,)?) => { - $crate::menu::Menu::new( $crate::menu_items!( $($x),+ ) ) + ($($x:tt)+) => { + $crate::menu::Menu::new( $crate::menu_items!( $($x)+ ) ) } } /// Creates a [`MenuBar`] with the given children. /// /// [`MenuBar`]: crate::menu::MenuBar +/// +/// Syntax: +/// ``` +/// menu_bar!( +/// (widget, menu) +/// (widget, menu) +/// (widget, menu) +/// ... +/// ) +/// ``` #[cfg(feature = "menu")] #[macro_export] macro_rules! menu_bar { - ($(($x:expr, $m:expr)),+ $(,)?) => ( + ($(($x:expr, $m:expr))+) => ( $crate::menu::MenuBar::new(vec![ $( Item::with_menu($x, $m) ),+ ]) ); } From 944b8c213cecd2095c66ccd91cc72123e0e234a8 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 11:51:30 +0800 Subject: [PATCH 16/40] docs --- examples/menu/src/main.rs | 16 +--- src/native/menu.rs | 149 +++++++++++++++++++++++++++++--------- src/native/menu/common.rs | 16 +--- 3 files changed, 118 insertions(+), 63 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index bf3a4feb..af05e478 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -119,20 +119,6 @@ impl Application for App { self.title.clone() } - fn subscription(&self) -> iced::Subscription { - use iced::keyboard; - use keyboard::key::Named; - - iced::event::listen().map(|event| match event { - Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => match key { - keyboard::Key::Named(Named::F1) => Message::FlipHorizontal, - keyboard::Key::Named(Named::F2) => Message::FlipVertical, - _ => Message::None, - }, - _ => Message::None, - }) - } - fn update(&mut self, message: Self::Message) -> iced::Command { // println!("app update"); match message { @@ -737,6 +723,8 @@ fn circle(color: Color) -> quad::Quad { } } + + // fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { // let sub_5 = debug_sub_menu( // "SUB", diff --git a/src/native/menu.rs b/src/native/menu.rs index 5db3d33e..00770603 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -1,56 +1,140 @@ -//! A [`MenuBar`] widget for displaying [`MenuTree`]s +//! A [`MenuBar`] widget for displaying menu trees created with [`Item`]s and [`Menu`]s //! //! *This API requires the following crate features to be activated: `menu`* //! -//! # Example +//! ## Example 1 //! //! ```ignore //! use iced::widget::button; -//! use iced_aw::menu::{MenuTree, MenuBar}; +//! use iced_aw::menu::{Item, Menu, MenuBar}; //! -//! let sub_2 = MenuTree::with_children( +//! let sub_2 = Item::with_menu( //! button("Sub Menu 2"), -//! vec![ -//! MenuTree::new(button("item_1")), -//! MenuTree::new(button("item_2")), -//! MenuTree::new(button("item_3")), -//! ] +//! Menu::new([ +//! Item::new(button("item_1")), +//! Item::new(button("item_2")), +//! Item::new(button("item_3")), +//! ].into()) //! ); //! -//! let sub_1 = MenuTree::with_children( +//! let sub_1 = Item::with_menu( //! button("Sub Menu 1"), -//! vec![ -//! MenuTree::new(button("item_1")), +//! Menu::new([ +//! Item::new(button("item_1")), //! sub_2, -//! MenuTree::new(button("item_2")), -//! MenuTree::new(button("item_3")), -//! ] +//! Item::new(button("item_2")), +//! Item::new(button("item_3")), +//! ].into()) //! ); //! -//! -//! let root_1 = MenuTree::with_children( +//! let root_1 = Item::with_menu( //! button("Menu 1"), -//! vec![ -//! MenuTree::new(button("item_1")), -//! MenuTree::new(button("item_2")), +//! Menu::new([ +//! Item::new(button("item_1")), +//! Item::new(button("item_2")), //! sub_1, -//! MenuTree::new(button("item_3")), -//! ] +//! Item::new(button("item_3")), +//! ].into()) //! ); //! -//! let root_2 = MenuTree::with_children( +//! let root_2 = Item::with_menu( //! button("Menu 2"), -//! vec![ -//! MenuTree::new(button("item_1")), -//! MenuTree::new(button("item_2")), -//! MenuTree::new(button("item_3")), -//! ] +//! Menu::new([ +//! Item::new(button("item_1")), +//! Item::new(button("item_2")), +//! Item::new(button("item_3")), +//! ].into()) //! ); //! //! let menu_bar = MenuBar::new(vec![root_1, root_2]); //! //! ``` -//! +//! +//! Alternatively you can use the helper macros +//! +//! ## Example 2 +//! ``` +//! use iced::widget::button; +//! use iced_aw::menu::{Menu} +//! use iced_aw::{menu_bar, menu_items} +//! +//! let menu_template = |items| Menu::new(items).max_width(180.0).offset(6.0); +//! +//! let menu_bar = menu_bar!( +//! (button("Menu 1"),menu_template(menu_items!( +//! (button("item_1")) +//! (button("item_2")) +//! (button("Sub Menu 1"), menu_template(menu_items!( +//! (button("item_1")) +//! (button("Sub Menu 2"), menu_template(menu_items!( +//! (button("item_1")) +//! (button("item_2")) +//! (button("item_3")) +//! ))) +//! (button("item_2")) +//! (button("item_3")) +//! ))) +//! (button("item_3")) +//! ))) +//! (button("Menu 2"), menu_template(menu_items!( +//! (button("item_1")) +//! (button("item_2")) +//! (button("item_3")) +//! ))) +//! ) +//! ``` +//! +//! Notice a menu_template function/closure is used in example 2. Usually some properties are sync across all menus while others are not, +//! template function is one way to do that. If you find writing menu_template(menu_items!()) cumbersome, +//! there is a menu! macro you can use to create template macros +//! +//! # Example 3 +//! +//! ``` +//! use iced_aw::{menu}; +//! +//! macro_rules! menu_template { +//! ($($x:tt)+) => { +//! menu!($($x)+).max_width(180.0).offset(6.0) +//! }; +//! } +//! +//! // then you can just write +//! let m = menu_template!( +//! (button("item_1")) +//! (button("item_2")) +//! (button("sub menu"), menu_template!( +//! (button("item_1")) +//! (button("item_2")) +//! )) +//! (button("item_3")) +//! ); +//! ``` +//! +//! Technically You can create menu template functions with the menu! macro, +//! but turns out closures can't infer the generic types, +//! and creating a function for it involves writing a ton of generic annotations +//! ``` +//! fn menu_template<'a, Message, Theme, Renderer>( +//! menu: Menu<'a, Message, Theme, Renderer> +//! ) -> Menu<'a, Message, Theme, Renderer> +//! where +//! Theme: iced_aw::menu::StyleSheet, +//! Renderer: iced::advanced::Renderer, +//! { +//! menu.max_width(180.0).offset(6.0) +//! } +//! +//! let m = menu_template(menu!( +//! (button("item_1")) +//! (button("item_2")) +//! (button("item_3")) +//! )); +//! ``` +//! +//! Checkout the menu example in the iced_aw repo for more detail +//! + mod flex; mod menu_bar; @@ -61,10 +145,3 @@ mod common; pub use crate::style::menu_bar::{Appearance, StyleSheet}; pub use menu_bar::MenuBar; pub use menu_tree::{Item, Menu}; - -// A `MenuBar` collects `MenuTree`s and handles -// pub type MenuBar<'a, Message, Renderer> = menu_bar::MenuBar<'a, Message, Renderer>; -// pub use types::*; -// Nested menu is essentially a tree of items, a menu is a collection of items -// pub type MenuTree<'a, Message, Renderer> = menu_tree::MenuTree<'a, Message, Renderer>; -// pub use menux::Menux; diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index 5cb44516..e6d8802f 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -2,7 +2,7 @@ use iced_widget::core::{ Padding, Rectangle }; -/// The condition of when to close a menu +/* /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] pub struct CloseCondition { /// Close menus when the cursor moves outside the check bounds @@ -14,19 +14,9 @@ pub struct CloseCondition { /// Close menus when the cursor clicks inside the check bounds pub click_inside: bool, } + */ -// /// Methods for drawing path highlight -// #[derive(Debug, Clone, Copy)] -// pub enum PathHighlight { -// /// Draw the full path, -// Full, -// /// Omit the active item(the last item in the path) -// OmitActive, -// /// Omit the active item if it's not a menu -// MenuActive, -// } - -/// X+ goes right and Y+ goes down + /// X+ goes right and Y+ goes down #[derive(Debug, Clone, Copy)] pub(super) enum Direction { Positive, From 206ad2e1a94dfe0a6c1778a2aadec9491f727e28 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 17:50:35 +0800 Subject: [PATCH 17/40] improve scrolling clipping --- examples/menu/src/main.rs | 8 +-- src/native/menu/menu_tree.rs | 129 ++++++++++++----------------------- 2 files changed, 46 insertions(+), 91 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index af05e478..d58e8bf1 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -18,7 +18,7 @@ pub fn main() -> iced::Result { App::run(iced::Settings { default_text_size: Pixels(15.0), window: iced::window::Settings { - size: Size::new(1000.0, 500.0), + size: Size::new(1000.0, 600.0), ..Default::default() }, ..Default::default() @@ -186,8 +186,8 @@ impl Application for App { fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { // println!("app view"); - let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(12.0); - let menu_temp_2 = |items| Menu::new(items).max_width(180.0); + let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(12.0).spacing(6.0); + let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(6.0); let mb = menu_bar!( (debug_button_s("Nested Menus"), @@ -483,7 +483,7 @@ impl Application for App { let r = row![horizontal_space(295), mb, horizontal_space(295),] .align_items(alignment::Alignment::Center); - let c = col![vertical_space(450), r, vertical_space(450),]; + let c = col![vertical_space(500), r, vertical_space(500),]; let sc = scrollable(c) // .direction(scrollable::Direction::Vertical( diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 4d2918c2..2b069f95 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -234,6 +234,7 @@ where let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; + let factor = ((bounds.height - start_offset) / bounds.height).max(0.0); Node::with_children( Size::new( @@ -241,14 +242,10 @@ where slice.upper_bound_rel - slice.lower_bound_rel ), once( - Node::with_children( - Size::new( - bounds.width, - slice.upper_bound_rel - slice.lower_bound_rel - ), - node.children().iter().map(Clone::clone).collect() - ).move_to(bounds.position()) - .translate([0.0, start_offset]) + scale_node_y( + node, + factor + ).translate([0.0, start_offset]) ).collect() ) }else{ @@ -295,11 +292,11 @@ where let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; + let factor = ((bounds.height - start_offset) / bounds.height).max(0.0); scale_node_y( node, - (bounds.height - start_offset) / bounds.height - ) - .translate([0.0, start_offset]) + factor + ).translate([0.0, start_offset]) }; let end_node = { @@ -986,34 +983,21 @@ impl MenuSlice{ let lower_bound_rel = lower_bound - (items_bounds.y + scroll_offset); let upper_bound_rel = upper_bound - (items_bounds.y + scroll_offset); - let start_index = items_node.children().iter().enumerate().find_map(|(i, n)|{ - let bounds = n.bounds(); - (bounds.y <= lower_bound_rel && bounds.y + bounds.height >= lower_bound_rel).then_some(i) - }).unwrap_or(0); - - let end_index = items_node.children().iter().enumerate().find_map(|(i, n)|{ - let bounds = n.bounds(); - (bounds.y <= upper_bound_rel && bounds.y + bounds.height >= upper_bound_rel).then_some(i) - }).unwrap_or(max_index); - - // let start_index = search_bound( - // 0, - // 0, - // max_index, - // lower_bound_rel, - // items_node.children(), - // |n| n.bounds().y, - // |n| n.bounds().height - // ); - // let end_index = search_bound( - // max_index, - // start_index, - // max_index, - // upper_bound_rel, - // items_node.children(), - // |n| n.bounds().y, - // |n| n.bounds().height - // ); + // let start_index = search_bound_lin(lower_bound_rel, items_node.children(), 0); + // let end_index = search_bound_lin(upper_bound_rel, items_node.children(), start_index); + + let start_index = search_bound( + 0, + max_index, + lower_bound_rel, + items_node.children(), + ); + let end_index = search_bound( + start_index, + max_index, + upper_bound_rel, + items_node.children(), + ); Self { start_index, @@ -1024,71 +1008,42 @@ impl MenuSlice{ } } -fn search_bound( - default: usize, +/* fn search_bound_lin( + bound: f32, + nodes: &[Node], + mut start_index: usize, // should be less than nodes.len()-1 +) -> usize{ + for (i, n) in nodes.iter().enumerate().skip(start_index){ + let b = n.bounds(); + if !(bound > b.y + b.height){ + start_index = i; + break; + } + } + start_index +} */ + +fn search_bound( default_left: usize, default_right: usize, bound: f32, - list: &[T], - get_position: impl Fn(&T) -> f32, - get_size: impl Fn(&T) -> f32, + list: &[Node], ) -> usize { // binary search let mut left = default_left; let mut right = default_right; - let mut index = default; while left != right { let m = ((left + right) / 2) + 1; - if get_position(&list[m]) > bound { + if list[m].bounds().y > bound { right = m - 1; } else { left = m; } } - let height = get_size(&list[left]); - if get_position(&list[left]) + height > bound { - index = left; - } - index + left } -fn clip_node( - node: &Node, - bounds: Rectangle, -) -> Node{ - fn clip( - bounds: &Rectangle, - parent_offset: Vector, - node: &Node, - ) -> Node{ - let node_bounds = node.bounds() + parent_offset; - bounds.intersection(&node_bounds) - .and_then(|new_bounds|{Some( - Node::with_children( - new_bounds.size(), - node.children().iter().map(|n|{ - clip( - bounds, - node_bounds.position() - Point::ORIGIN, - n - ).translate(node_bounds.position() - new_bounds.position()) - }).collect() - ).move_to(new_bounds.position()) - .translate(parent_offset * -1.0) - )}) - .unwrap_or( - Node::with_children( - Size::ZERO, - node.children().iter().map(Clone::clone).collect() - ).move_to(node.bounds().position()) - ) - } - - clip(&bounds, Vector::ZERO, node) -} - - fn scale_node_y( node: &Node, factor: f32, From 6a052042cb458d0abfee21c4eb49eb96c1609bd4 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 18:39:44 +0800 Subject: [PATCH 18/40] fix Item::diff --- examples/menu/src/main.rs | 47 ++----------------------- src/native/menu/menu_tree.rs | 68 +++++++++++------------------------- 2 files changed, 23 insertions(+), 92 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index d58e8bf1..454df59b 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -25,33 +25,6 @@ pub fn main() -> iced::Result { }) } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum SizeOption { - Uniform, - Static, - DynamicHeight, -} -impl SizeOption { - const ALL: [SizeOption; 3] = [ - SizeOption::Uniform, - SizeOption::Static, - SizeOption::DynamicHeight, - ]; -} -impl std::fmt::Display for SizeOption { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Uniform => "Uniform", - Self::Static => "Static", - Self::DynamicHeight => "DynamicHeight", - } - ) - } -} - #[derive(Debug, Clone)] enum Message { Debug(String), @@ -63,7 +36,6 @@ enum Message { FlipVertical, ThemeChange(bool), TextChange(String), - SizeOption(SizeOption), None, } @@ -77,7 +49,6 @@ struct App { flip_v: bool, dark_mode: bool, text: String, - size_option: SizeOption, } impl Application for App { type Executor = iced::executor::Default; @@ -105,7 +76,6 @@ impl Application for App { flip_v: false, dark_mode: false, text: "Text Input".into(), - size_option: SizeOption::DynamicHeight, }, iced::Command::none(), ) @@ -174,24 +144,17 @@ impl Application for App { self.text = s.clone(); self.title = s; } - Message::SizeOption(so) => { - self.size_option = so; - self.title = self.size_option.to_string(); - } Message::None => {} } iced::Command::none() } fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { - // println!("app view"); - let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(12.0).spacing(6.0); let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(6.0); let mb = menu_bar!( - (debug_button_s("Nested Menus"), - { + (debug_button_s("Nested Menus"), { let sub5 = menu_temp_2(menu_items!( (debug_button("Item")) (debug_button("Item")) @@ -243,8 +206,7 @@ impl Application for App { (debug_button("Item")) (debug_button("Item")) )).width(110.0) - } - ) + }) (debug_button_s("Widgets"), menu_temp_1(menu_items!( (debug_button("You can use any widget")) (debug_button("as a menu item")) @@ -291,6 +253,7 @@ impl Application for App { (debug_button("Item")) )).width(240.0)) (debug_button_s("Controls"), menu_temp_1(menu_items!( + (labeled_button("Flip", Message::FlipHorizontal).width(Length::Fill)) (row![toggler( Some("Dark Mode".into()), self.dark_mode, @@ -486,10 +449,6 @@ impl Application for App { let c = col![vertical_space(500), r, vertical_space(500),]; let sc = scrollable(c) - // .direction(scrollable::Direction::Vertical( - // scrollable::Properties::new() - // .alignment(scrollable::Alignment::End) - // )); .direction(scrollable::Direction::Both { vertical: scrollable::Properties::new() .alignment(scrollable::Alignment::End), diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 2b069f95..3f92c104 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -163,7 +163,6 @@ where /// tree: Tree{menu_state, \[item_tree...]} pub(super) fn diff(&self, tree: &mut Tree) { - // tree.diff_children(&self.items.iter().map(|i| &i.item ).collect::>()); tree.diff_children_custom( &self.items, |tree, item| item.diff(tree), @@ -208,7 +207,7 @@ where let children_size = items_node.bounds().size(); let (children_position, offset_position, child_direction) = aod.resolve(parent_bounds, children_size, viewport.size()); - // calc offset bounds + // calc auxiliary bounds let delta = children_position - offset_position; let offset_size = if delta.x.abs() > delta.y.abs() { Size::new(self.offset, children_size.height) @@ -222,6 +221,7 @@ where let menu_state = tree.state.downcast_mut::(); + // calc slice let slice = MenuSlice::new( &items_node, children_position-Point::ORIGIN, @@ -249,45 +249,6 @@ where ).collect() ) }else{ - // let start_node = { - // let node = &items_node.children()[slice.start_index]; - // let bounds = node.bounds(); - // let start_offset = slice.lower_bound_rel - bounds.y; - // Node::with_children( - // Size::new( - // bounds.width, - // bounds.height - start_offset - // ), - // node.children().iter().map(Clone::clone).collect() - // ).move_to(bounds.position()) - // .translate([0.0, start_offset]) - // }; - - // let start_node = { - // let node = &items_node.children()[slice.start_index]; - // let bounds = node.bounds(); - // clip_node( - // node, - // Rectangle{ - // x: bounds.x, - // y: slice.lower_bound_rel, - // width: bounds.width, - // height: bounds.height - (slice.lower_bound_rel - bounds.y), - // } - // ) - // }; - - // let start_node = { - // let node = &items_node.children()[slice.start_index]; - // let bounds = node.bounds(); - // let start_offset = slice.lower_bound_rel - bounds.y; - // Node::with_children( - // bounds.size(), - // node.children().iter().map(Clone::clone).collect() - // ).move_to(bounds.position()) - // .translate([0.0, start_offset]) - // }; - let start_node = { let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); @@ -680,17 +641,28 @@ where pub(super) fn children(&self) -> Vec { self.menu .as_ref() - .map_or([Tree::new(&self.item)].into(), |m| { - [Tree::new(&self.item), m.tree()].into() - }) + .map_or([ + Tree::new(&self.item)].into(), + |m| [ + Tree::new(&self.item), m.tree() + ].into() + ) } /// tree: Tree{stateless, \[widget_tree, menu_tree]} pub(super) fn diff(&self, tree: &mut Tree) { - tree.children[0].diff(&self.item); - self.menu - .as_ref() - .map_or({}, |m| m.diff(&mut tree.children[1])) + if let Some(t) = tree.children.get_mut(0) { + t.diff(&self.item); + if let Some(t) = tree.children.get_mut(1) { + self.menu + .as_ref() + .map_or({}, |m| m.diff(t) ) + }else{ + *tree = self.tree(); + } + }else{ + *tree = self.tree(); + } } /// tree: Tree{stateless, \[widget_tree, menu_tree]} From 17f4e3a1f0ba6c40c3b7bb03ee9150620c824374 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 20:50:32 +0800 Subject: [PATCH 19/40] clean up --- examples/menu/src/main.rs | 499 +-------- src/native/menu.rs | 3 +- src/native/menu/bk/menu_bar.rs | 478 --------- src/native/menu/bk/menu_inner.rs | 1192 ---------------------- src/native/menu/bk/menu_tree.rs | 128 --- src/native/menu/bk/menux.rs | 844 --------------- src/native/menu/bk/types.rs | 78 -- src/native/menu/bk2/menu_bar.rs | 165 --- src/native/menu/bk2/menu_bar_overlay.rs | 188 ---- src/native/menu/bk2/menu_tree.rs | 322 ------ src/native/menu/bk2/menu_tree_overlay.rs | 532 ---------- src/native/menu/bk2/types.rs | 109 -- src/native/menu/bk3/menu_bar.rs | 239 ----- src/native/menu/bk3/menu_bar_overlay.rs | 228 ----- src/native/menu/bk3/menu_tree.rs | 804 --------------- src/native/menu/menu_bar.rs | 8 - src/native/menu/menu_bar_overlay.rs | 10 - src/native/menu/menu_tree.rs | 99 +- src/style/menu_bar.rs | 8 +- 19 files changed, 47 insertions(+), 5887 deletions(-) delete mode 100644 src/native/menu/bk/menu_bar.rs delete mode 100644 src/native/menu/bk/menu_inner.rs delete mode 100644 src/native/menu/bk/menu_tree.rs delete mode 100644 src/native/menu/bk/menux.rs delete mode 100644 src/native/menu/bk/types.rs delete mode 100644 src/native/menu/bk2/menu_bar.rs delete mode 100644 src/native/menu/bk2/menu_bar_overlay.rs delete mode 100644 src/native/menu/bk2/menu_tree.rs delete mode 100644 src/native/menu/bk2/menu_tree_overlay.rs delete mode 100644 src/native/menu/bk2/types.rs delete mode 100644 src/native/menu/bk3/menu_bar.rs delete mode 100644 src/native/menu/bk3/menu_bar_overlay.rs delete mode 100644 src/native/menu/bk3/menu_tree.rs diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 454df59b..3151dad9 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -1,17 +1,13 @@ use iced::widget::{ - button, checkbox, container, horizontal_space, pick_list, row, scrollable, slider, svg, text, + button, checkbox, container, horizontal_space, row, scrollable, slider, svg, text, text_input, toggler, vertical_slider, }; use iced::widget::{column as col, vertical_space}; -use iced::{alignment, theme, Alignment, Application, Border, Color, Element, Event, Length, Pixels, Size, Theme}; +use iced::{alignment, theme, Alignment, Application, Border, Color, Element, Length, Pixels, Size}; -use iced_aw::menu::{Item, Menu, MenuBar}; +use iced_aw::menu::{Item, Menu}; use iced_aw::quad; -use iced_aw::{ - menu_bar, - menu, - menu_items -}; +use iced_aw::{menu_bar, menu_items}; pub fn main() -> iced::Result { // std::env::set_var("RUST_BACKTRACE", "full"); @@ -32,8 +28,6 @@ enum Message { CheckChange(bool), ToggleChange(bool), ColorChange(Color), - FlipHorizontal, - FlipVertical, ThemeChange(bool), TextChange(String), None, @@ -45,8 +39,6 @@ struct App { check: bool, toggle: bool, theme: iced::Theme, - flip_h: bool, - flip_v: bool, dark_mode: bool, text: String, } @@ -72,8 +64,6 @@ impl Application for App { check: false, toggle: false, theme, - flip_h: false, - flip_v: false, dark_mode: false, text: "Text Input".into(), }, @@ -90,7 +80,6 @@ impl Application for App { } fn update(&mut self, message: Self::Message) -> iced::Command { - // println!("app update"); match message { Message::Debug(s) => { self.title = s; @@ -117,8 +106,6 @@ impl Application for App { ); self.title = format!("[{:.2}, {:.2}, {:.2}]", c.r, c.g, c.b); } - Message::FlipHorizontal => self.flip_h = !self.flip_h, - Message::FlipVertical => self.flip_v = !self.flip_v, Message::ThemeChange(b) => { self.dark_mode = b; let primary = self.theme.palette().primary; @@ -150,8 +137,8 @@ impl Application for App { } fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { - let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(12.0).spacing(6.0); - let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(6.0); + let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); + let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); let mb = menu_bar!( (debug_button_s("Nested Menus"), { @@ -253,7 +240,6 @@ impl Application for App { (debug_button("Item")) )).width(240.0)) (debug_button_s("Controls"), menu_temp_1(menu_items!( - (labeled_button("Flip", Message::FlipHorizontal).width(Length::Fill)) (row![toggler( Some("Dark Mode".into()), self.dark_mode, @@ -327,8 +313,8 @@ impl Application for App { (debug_button("kaljkahd")) (debug_button("luyortp")) (debug_button("mmdyrc")) - (debug_button("nquc")) // 13 - (debug_button("ajrs")) // 14 + (debug_button("nquc")) + (debug_button("ajrs")) (debug_button("bsdfho")) (debug_button("clkjhbf")) (debug_button("dekjdaud")) @@ -341,8 +327,8 @@ impl Application for App { (debug_button("kaljkahd")) (debug_button("luyortp")) (debug_button("mmdyrc")) - (debug_button("nquc")) // 27 - (debug_button("ajrs")) // 28 + (debug_button("nquc")) + (debug_button("ajrs")) (debug_button("bsdfho")) (debug_button("clkjhbf")) (submenu_button("dekjdaud"), menu_temp_2(menu_items!( @@ -403,7 +389,8 @@ impl Application for App { (debug_button_s("Dynamic height"), { let slider_count = 3; let slider_width = 30; - let spacing = 4; + let spacing = 5; + // let pad = 20; let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); menu_temp_1(menu_items!( @@ -414,19 +401,16 @@ impl Application for App { x, g, b ))) .width(slider_width) - // .height(100.0) , vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( r, x, b ))) .width(slider_width) - // .height(100.0) , vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( r, g, x ))) .width(slider_width) - // .height(100.0) , ].spacing(spacing) .height(100.0) @@ -480,7 +464,7 @@ impl button::StyleSheet for ButtonStyle { background: Some(Color::TRANSPARENT.into()), // background: Some(Color::from([1.0; 3]).into()), border: Border { - radius: [4.0; 4].into(), + radius: [6.0; 4].into(), ..Default::default() }, ..Default::default() @@ -544,7 +528,6 @@ fn submenu_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, i row![ text(label) .width(Length::Fill) - // .height(Length::Fill) .vertical_alignment(alignment::Vertical::Center), arrow ] @@ -560,66 +543,6 @@ fn color_button<'a>(color: impl Into) -> button::Button<'a, Message, iced ) } -// fn debug_item<'a>(label: &str) -> Item<'a, Message, iced::Theme, iced::Renderer> { -// menu_item!(debug_button(label).width(Length::Fill).height(Length::Fill)) -// } - -// fn debug_item2<'a>(label: &str) -> Item<'a, Message, iced::Theme, iced::Renderer> { -// menu_item!(debug_button(label) -// .width(Length::Fill) -// .height(Length::Shrink)) -// } - -// fn debug_item3<'a>(label: &str, h: f32) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// menu_tree!(debug_button(label) -// .width(Length::Fill) -// .height(Length::Fixed(h))) -// } - -// fn color_item<'a>(color: impl Into) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let color = color.into(); -// menu_tree!(base_button(circle(color), Message::ColorChange(color))) -// } - -// fn sub_menu<'a>( -// label: &str, -// msg: Message, -// children: Vec>, -// ) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let handle = svg::Handle::from_path(format!( -// "{}/caret-right-fill.svg", -// env!("CARGO_MANIFEST_DIR") -// )); -// let arrow = svg(handle) -// .width(Length::Shrink) -// .style(theme::Svg::custom_fn(|theme| svg::Appearance { -// color: Some(theme.extended_palette().background.base.text), -// })); -// menu_tree( -// base_button( -// row![ -// text(label) -// .width(Length::Fill) -// .height(Length::Fill) -// .vertical_alignment(alignment::Vertical::Center), -// arrow -// ] -// .align_items(iced::Alignment::Center), -// msg, -// ) -// .width(Length::Fill) -// .height(Length::Fill), -// children, -// ) -// } - -// fn debug_sub_menu<'a>( -// label: &str, -// children: Vec>, -// ) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// sub_menu(label, Message::Debug(label.into()), children) -// } - fn separator<'a>() -> quad::Quad { quad::Quad { color: [0.5; 3].into(), @@ -681,399 +604,3 @@ fn circle(color: Color) -> quad::Quad { ..Default::default() } } - - - -// fn menu_1<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let sub_5 = debug_sub_menu( -// "SUB", -// vec![ -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ); -// let sub_4 = debug_sub_menu( -// "SUB", -// vec![ -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ) -// .width(180); -// let sub_3 = debug_sub_menu( -// "More sub menus", -// vec![ -// debug_item("You can"), -// debug_item("nest menus"), -// sub_4, -// debug_item("how ever"), -// debug_item("You like"), -// sub_5, -// ], -// ); -// let sub_2 = debug_sub_menu( -// "Another sub menu", -// vec![ -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// sub_3, -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ) -// .width(140); -// let sub_1 = debug_sub_menu( -// "A sub menu", -// vec![ -// debug_item("Item"), -// debug_item("Item"), -// sub_2, -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ) -// .width(220); -// let root = menu_tree( -// debug_button("Nested Menus"), -// vec![ -// debug_item("Item"), -// debug_item("Item"), -// sub_1, -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ) -// .width(110); -// root -// } - -// fn menu_2<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let sub_1 = menu_tree( -// container(toggler( -// Some("Or as a sub menu item".to_string()), -// app.toggle, -// Message::ToggleChange, -// )) -// .padding([0, 8]) -// .height(Length::Fill) -// .align_y(alignment::Vertical::Center), -// vec![ -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ); - -// let bt = menu_tree!(button( -// text("Button") -// .width(Length::Fill) -// .height(Length::Fill) -// .vertical_alignment(alignment::Vertical::Center), -// ) -// .width(Length::Fill) -// .height(Length::Fill) -// .on_press(Message::Debug("Button".into()))); - -// let cb = menu_tree!(checkbox("Checkbox", app.check, Message::CheckChange).width(Length::Fill)); - -// let sld = menu_tree!(row![ -// "Slider", -// horizontal_space(Length::Fixed(8.0)), -// slider(0..=255, app.value, Message::ValueChange) -// ]); - -// let txn = menu_tree!(text_input("", &app.text).on_input(Message::TextChange)); - -// let root = menu_tree( -// debug_button("Widgets"), -// vec![ -// debug_item("You can use any widget"), -// debug_item("as a menu item"), -// bt, -// cb, -// sld, -// txn, -// sub_1, -// separator(), -// debug_item("Seperators are also widgets"), -// labeled_separator("Separator"), -// debug_item("Item"), -// debug_item("Item"), -// dot_separator(), -// debug_item("Item"), -// debug_item("Item"), -// ], -// ); - -// root -// } - -// fn menu_3<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); - -// let primary = debug_sub_menu( -// "Primary", -// vec![ -// menu_tree!(slider(0..=255, r, move |x| { -// Message::ColorChange(Color::from_rgb8(x, g, b)) -// })), -// menu_tree!(slider(0..=255, g, move |x| { -// Message::ColorChange(Color::from_rgb8(r, x, b)) -// })), -// menu_tree!(slider(0..=255, b, move |x| { -// Message::ColorChange(Color::from_rgb8(r, g, x)) -// })), -// ], -// ); - -// let root = menu_tree( -// debug_button("Controls"), -// vec![ -// menu_tree!(labeled_button("Flip Horizontal", Message::FlipHorizontal) -// .width(Length::Fill) -// .height(Length::Fill)), -// menu_tree!(labeled_button("Flip Vertical", Message::FlipVertical) -// .width(Length::Fill) -// .height(Length::Fill)), -// separator(), -// menu_tree!(row![toggler( -// Some("Dark Mode".into()), -// app.dark_mode, -// Message::ThemeChange -// )] -// .padding([0, 8])), -// color_item([0.45, 0.25, 0.57]), -// color_item([0.15, 0.59, 0.64]), -// color_item([0.76, 0.82, 0.20]), -// color_item([0.17, 0.27, 0.33]), -// primary, -// ], -// ); - -// root -// } - -// fn menu_4<'a>(_app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let dekjdaud = debug_sub_menu( -// "dekjdaud", -// vec![ -// debug_item("ajrs"), -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), -// debug_item("ajrs"), -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), -// ], -// ); - -// let luyortp = debug_sub_menu( -// "luyortp", -// vec![ -// debug_item("ajrs"), // 0 -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), // 13 -// ], -// ); - -// let jcsu = debug_sub_menu( -// "jcsu", -// vec![ -// debug_item("ajrs"), // 0 -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), -// luyortp, // 11 -// debug_item("mmdyrc"), -// debug_item("nquc"), // 13 -// ], -// ); - -// let root = menu_tree( -// debug_button("Scroll"), -// vec![ -// debug_item("ajrs"), // 0 -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// jcsu, // 9 -// debug_item("kaljkahd"), -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), // 13 -// debug_item("ajrs"), // 14 -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), // 27 -// debug_item("ajrs"), // 28 -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// dekjdaud, -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), // 41 -// debug_item("ajrs"), // 42 -// debug_item("bsdfho"), -// debug_item("clkjhbf"), -// debug_item("dekjdaud"), -// debug_item("ecsh"), -// debug_item("fweiu"), -// debug_item("giwe"), -// debug_item("heruyv"), -// debug_item("isabe"), -// debug_item("jcsu"), -// debug_item("kaljkahd"), // 52 -// debug_item("luyortp"), -// debug_item("mmdyrc"), -// debug_item("nquc"), // 55 -// ], -// ); - -// root -// } - -// fn menu_5<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let slider_count = 3; -// let slider_width = 30; -// let spacing = 4; - -// let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); - -// let sliders = menu_tree!(row![ -// vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( -// x, g, b -// ))) -// .width(30), -// vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( -// r, x, b -// ))) -// .width(30), -// vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( -// r, g, x -// ))) -// .width(30), -// ] -// .spacing(4)) -// .height(100); - -// let root = menu_tree( -// debug_button("Static"), -// vec![labeled_separator("Primary"), sliders], -// ) -// .width(slider_width * slider_count + (slider_count - 1) * spacing); - -// root -// } - -// fn menu_6<'a>(app: &App) -> MenuTree<'a, Message, iced::Theme, iced::Renderer> { -// let slider_count = 3; -// let slider_width = 30; -// let spacing = 4; - -// let [r, g, b, _] = app.theme.palette().primary.into_rgba8(); - -// let sliders = menu_tree!(row![ -// vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( -// x, g, b -// ))) -// .width(30), -// vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( -// r, x, b -// ))) -// .width(30), -// vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( -// r, g, x -// ))) -// .width(30), -// ] -// .spacing(4) -// .height(100)); - -// let root = menu_tree( -// debug_button("Dynamic Height"), -// vec![ -// labeled_separator("Primary"), -// sliders, -// debug_item2("AABB"), -// debug_item3("CCDD", 50.0), -// debug_item2("EEFF"), -// debug_item("GGHH").height(100), -// debug_item2("IIJJ"), -// ], -// ) -// .width(slider_width * slider_count + (slider_count - 1) * spacing); - -// root -// } - diff --git a/src/native/menu.rs b/src/native/menu.rs index 00770603..1ed01797 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -132,7 +132,8 @@ //! )); //! ``` //! -//! Checkout the menu example in the iced_aw repo for more detail +//! For a more detailed example please +//! take a look at the menu example in the iced_aw repo //! diff --git a/src/native/menu/bk/menu_bar.rs b/src/native/menu/bk/menu_bar.rs deleted file mode 100644 index 2f41d005..00000000 --- a/src/native/menu/bk/menu_bar.rs +++ /dev/null @@ -1,478 +0,0 @@ -//! A widget that handles menu trees -use super::{ - menu_inner::{ - CloseCondition, Direction, ItemHeight, ItemWidth, Menu, MenuState, PathHighlight, - }, - menu_tree::MenuTree, -}; -use crate::style::menu_bar::StyleSheet; - -use iced_widget::core::{ - event, - layout::{self, Limits, Node}, - mouse::{self, Cursor}, - overlay, renderer, touch, - widget::{tree, Tree}, - Alignment, Clipboard, Color, Element, Layout, Length, Padding, Rectangle, Shell, Widget, - Border, Shadow, - Size, -}; - -pub(super) struct MenuBarState { - pub(super) pressed: bool, - pub(super) view_cursor: Cursor, - pub(super) open: bool, - pub(super) active_root: Option, - pub(super) horizontal_direction: Direction, - pub(super) vertical_direction: Direction, - pub(super) menu_states: Vec, -} -impl MenuBarState { - pub(super) fn get_trimmed_indices(&self) -> impl Iterator + '_ { - self.menu_states - .iter() - .take_while(|ms| ms.index.is_some()) - .map(|ms| ms.index.expect("No indices were found in the menu state.")) - } - - pub(super) fn reset(&mut self) { - self.open = false; - self.active_root = None; - self.menu_states.clear(); - } -} -impl Default for MenuBarState { - fn default() -> Self { - Self { - pressed: false, - view_cursor: Cursor::Available([-0.5, -0.5].into()), - open: false, - active_root: None, - horizontal_direction: Direction::Positive, - vertical_direction: Direction::Positive, - menu_states: Vec::new(), - } - } -} - -/// A `MenuBar` collects `MenuTree`s and handles -/// all the layout, event processing and drawing -#[allow(missing_debug_implementations)] -pub struct MenuBar<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - width: Length, - height: Length, - spacing: f32, - padding: Padding, - bounds_expand: u16, - main_offset: i32, - cross_offset: i32, - close_condition: CloseCondition, - item_width: ItemWidth, - item_height: ItemHeight, - path_highlight: Option, - menu_roots: Vec>, - style: Theme::Style, -} - -impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - /// Creates a new [`MenuBar`] with the given menu roots - #[must_use] - pub fn new(menu_roots: Vec>) -> Self { - let mut menu_roots = menu_roots; - menu_roots.iter_mut().for_each(MenuTree::set_index); - - Self { - width: Length::Shrink, - height: Length::Shrink, - spacing: 0.0, - padding: Padding::ZERO, - bounds_expand: 15, - main_offset: 0, - cross_offset: 0, - close_condition: CloseCondition { - leave: true, - click_outside: true, - click_inside: true, - }, - item_width: ItemWidth::Uniform(150), - item_height: ItemHeight::Uniform(30), - path_highlight: Some(PathHighlight::MenuActive), - menu_roots, - style: Theme::Style::default(), - } - } - - /// Sets the expand value for each menu's check bounds - /// - /// When the cursor goes outside of a menu's check bounds, - /// the menu will be closed automatically, this value expands - /// the check bounds - #[must_use] - pub fn bounds_expand(mut self, value: u16) -> Self { - self.bounds_expand = value; - self - } - - /// [`CloseCondition`] - #[must_use] - pub fn close_condition(mut self, close_condition: CloseCondition) -> Self { - self.close_condition = close_condition; - self - } - - /// Moves each menu in the horizontal open direction - #[must_use] - pub fn cross_offset(mut self, value: i32) -> Self { - self.cross_offset = value; - self - } - - /// Sets the height of the [`MenuBar`] - #[must_use] - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// [`ItemHeight`] - #[must_use] - pub fn item_height(mut self, item_height: ItemHeight) -> Self { - self.item_height = item_height; - self - } - - /// [`ItemWidth`] - #[must_use] - pub fn item_width(mut self, item_width: ItemWidth) -> Self { - self.item_width = item_width; - self - } - - /// Moves all the menus in the vertical open direction - #[must_use] - pub fn main_offset(mut self, value: i32) -> Self { - self.main_offset = value; - self - } - - /// Sets the [`Padding`] of the [`MenuBar`] - #[must_use] - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the method for drawing path highlight - #[must_use] - pub fn path_highlight(mut self, path_highlight: Option) -> Self { - self.path_highlight = path_highlight; - self - } - - /// Sets the spacing between menu roots - #[must_use] - pub fn spacing(mut self, units: f32) -> Self { - self.spacing = units; - self - } - - /// Sets the style of the menu bar and its menus - #[must_use] - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); - self - } - - /// Sets the width of the [`MenuBar`] - #[must_use] - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } -} -impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - fn size(&self) -> Size { - Size::new(self.width, self.height) - } - - fn diff(&self, tree: &mut Tree) { - if tree.children.len() > self.menu_roots.len() { - tree.children.truncate(self.menu_roots.len()); - } - - tree.children - .iter_mut() - .zip(self.menu_roots.iter()) - .for_each(|(t, root)| { - let flat = root - .flattern() - .iter() - .map(|mt| mt.item.as_widget()) - .collect::>(); - - t.diff_children(&flat); - }); - - if tree.children.len() < self.menu_roots.len() { - let extended = self.menu_roots[tree.children.len()..].iter().map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }); - tree.children.extend(extended); - } - } - - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(MenuBarState::default()) - } - - fn children(&self) -> Vec { - /* - menu bar - menu root 1 (stateless) - flat tree - menu root 2 (stateless) - flat tree - ... - */ - - self.menu_roots - .iter() - .map(|root| { - let mut tree = Tree::empty(); - let flat = root - .flattern() - .iter() - .map(|mt| Tree::new(mt.item.as_widget())) - .collect(); - tree.children = flat; - tree - }) - .collect() - } - - - fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { - use super::flex; - - let limits = limits.width(self.width).height(self.height); - let children = self - .menu_roots - .iter() - .map(|root| &root.item) - .collect::>(); - flex::resolve( - flex::Axis::Horizontal, - renderer, - &limits, - self.width, - self.height, - self.padding, - self.spacing, - Alignment::Center, - &children, - &mut tree.children - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: event::Event, - layout: Layout<'_>, - view_cursor: Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - use event::Event::{Mouse, Touch}; - use mouse::{Button::Left, Event::ButtonReleased}; - use touch::Event::{FingerLifted, FingerLost}; - - let root_status = process_root_events( - self.menu_roots.as_mut_slice(), - view_cursor, - tree, - &event, - layout, - renderer, - clipboard, - shell, - viewport, - ); - - let state = tree.state.downcast_mut::(); - - match event { - Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. } | FingerLost { .. }) => { - if state.menu_states.is_empty() && view_cursor.is_over(layout.bounds()) { - state.view_cursor = view_cursor; - state.open = true; - } - } - _ => (), - } - root_status - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - view_cursor: Cursor, - viewport: &Rectangle, - ) { - let state = tree.state.downcast_ref::(); - let cursor_pos = view_cursor.position().unwrap_or_default(); - let position = if state.open && (cursor_pos.x < 0.0 || cursor_pos.y < 0.0) { - state.view_cursor - } else { - view_cursor - }; - - // draw path highlight - if self.path_highlight.is_some() { - let styling = theme.appearance(&self.style); - if let Some(active) = state.active_root { - let active_bounds = layout - .children() - .nth(active) - .expect("Active child not found in menu?") - .bounds(); - let path_quad = renderer::Quad { - bounds: active_bounds, - // border_radius: styling.border_radius.into(), - // border_width: 0.0, - // border_color: Color::TRANSPARENT, - border: Border{ - color: Color::TRANSPARENT, - width: 0.0, - radius: styling.border_radius.into() - }, - shadow: Shadow::default(), - }; - let path_color = styling.path; - renderer.fill_quad(path_quad, path_color); - } - } - - self.menu_roots - .iter() - .zip(&tree.children) - .zip(layout.children()) - .for_each(|((root, t), lo)| { - root.item.as_widget().draw( - &t.children[root.index], - renderer, - theme, - style, - lo, - position, - viewport, - ); - }); - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - _renderer: &Renderer, - ) -> Option> { - let state = tree.state.downcast_ref::(); - if !state.open { - return None; - } - - Some( - Menu { - tree, - menu_roots: &mut self.menu_roots, - bounds_expand: self.bounds_expand, - close_condition: self.close_condition, - item_width: self.item_width, - item_height: self.item_height, - bar_bounds: layout.bounds(), - main_offset: self.main_offset, - cross_offset: self.cross_offset, - root_bounds_list: layout.children().map(|lo| lo.bounds()).collect(), - path_highlight: self.path_highlight, - style: &self.style, - } - .overlay(), - ) - } - - -} -impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> -where - Message: 'a, - Renderer: 'a + renderer::Renderer, - Theme: StyleSheet, -{ - fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { - Self::new(value) - } -} - -#[allow(unused_results, clippy::too_many_arguments)] -fn process_root_events( - menu_roots: &mut [MenuTree<'_, Message, Theme, Renderer>], - view_cursor: Cursor, - tree: &mut Tree, - event: &event::Event, - layout: Layout<'_>, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, -) -> event::Status -where - Renderer: renderer::Renderer, -{ - menu_roots - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|((root, t), lo)| { - // assert!(t.tag == tree::Tag::stateless()); - root.item.as_widget_mut().on_event( - &mut t.children[root.index], - event.clone(), - lo, - view_cursor, - renderer, - clipboard, - shell, - viewport, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) -} diff --git a/src/native/menu/bk/menu_inner.rs b/src/native/menu/bk/menu_inner.rs deleted file mode 100644 index 1ed0d943..00000000 --- a/src/native/menu/bk/menu_inner.rs +++ /dev/null @@ -1,1192 +0,0 @@ -//! Menu tree overlay -use super::{menu_bar::MenuBarState, menu_tree::MenuTree}; -use crate::style::menu_bar::StyleSheet; -use super::types::*; - -use iced_widget::core::{ - event, - layout::{Limits, Node}, - mouse::{self, Cursor}, - overlay, renderer, touch, - widget::Tree, - Clipboard, Color, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, - Border, Shadow -}; - - -/// Adaptive open direction -#[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] -struct Aod { - // whether or not to use aod - horizontal: bool, - vertical: bool, - - // whether or not to use overlap - horizontal_overlap: bool, - vertical_overlap: bool, - - // default direction - horizontal_direction: Direction, - vertical_direction: Direction, - - // Offset of the child in the default direction - horizontal_offset: f32, - vertical_offset: f32, -} -impl Aod { - /// Returns child position and offset position - #[allow(clippy::too_many_arguments)] - fn adaptive( - parent_pos: f32, - parent_size: f32, - child_size: f32, - max_size: f32, - offset: f32, - on: bool, - overlap: bool, - direction: Direction, - ) -> (f32, f32) { - /* - Imagine there're two sticks, parent and child - parent: o-----o - child: o----------o - - Now we align the child to the parent in one dimension - There are 4 possibilities: - - 1. to the right - o-----oo----------o - - 2. to the right but allow overlaping - o-----o - o----------o - - 3. to the left - o----------oo-----o - - 4. to the left but allow overlaping - o-----o - o----------o - - The child goes to the default direction by default, - if the space on the default direction runs out it goes to the the other, - whether to use overlap is the caller's decision - - This can be applied to any direction - */ - - match direction { - Direction::Positive => { - let space_negative = parent_pos; - let space_positive = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - overshoot) - } else { - (parent_pos, parent_pos) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - offset) - } else { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } - } - } - Direction::Negative => { - let space_positive = parent_pos; - let space_negative = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos, parent_pos) - } else { - (parent_pos - overshoot, parent_pos - overshoot) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } else { - (parent_pos - overshoot, parent_pos - offset) - } - } - } - } - } - - /// Returns child position and offset position - fn resolve( - &self, - parent_bounds: Rectangle, - children_size: Size, - viewport_size: Size, - ) -> (Point, Point) { - let (x, ox) = Self::adaptive( - parent_bounds.x, - parent_bounds.width, - children_size.width, - viewport_size.width, - self.horizontal_offset, - self.horizontal, - self.horizontal_overlap, - self.horizontal_direction, - ); - let (y, oy) = Self::adaptive( - parent_bounds.y, - parent_bounds.height, - children_size.height, - viewport_size.height, - self.vertical_offset, - self.vertical, - self.vertical_overlap, - self.vertical_direction, - ); - - ([x, y].into(), [ox, oy].into()) - } -} - -/// A part of a menu where items are displayed. -/// -/// When the bounds of a menu exceed the viewport, -/// only items inside the viewport will be displayed, -/// when scrolling happens, this should be updated -#[derive(Debug, Clone, Copy)] -struct MenuSlice { - start_index: usize, - end_index: usize, - lower_bound_rel: f32, - upper_bound_rel: f32, -} - -/// Menu bounds in overlay space -struct MenuBounds { - child_positions: Vec, - child_sizes: Vec, - children_bounds: Rectangle, - parent_bounds: Rectangle, - check_bounds: Rectangle, - offset_bounds: Rectangle, -} -impl MenuBounds { - /* #[allow(clippy::too_many_arguments)] - fn new( - menu_tree: &MenuTree<'_, Message, Theme, Renderer>, - renderer: &Renderer, - item_width: ItemWidth, - item_height: ItemHeight, - viewport_size: Size, - overlay_offset: Vector, - aod: &Aod, - bounds_expand: u16, - parent_bounds: Rectangle, - tree: &mut [Tree], - ) -> Self - where - Renderer: renderer::Renderer, - { - let (children_size, child_positions, child_sizes) = - get_children_layout(menu_tree, renderer, item_width, item_height, tree); - - // viewport space parent bounds - let view_parent_bounds = parent_bounds + overlay_offset; - - // overlay space children position - let (children_position, offset_position) = { - let (cp, op) = aod.resolve(view_parent_bounds, children_size, viewport_size); - (cp - overlay_offset, op - overlay_offset) - }; - - // calc offset bounds - let delta = children_position - offset_position; - let offset_size = if delta.x.abs() > delta.y.abs() { - Size::new(delta.x, children_size.height) - } else { - Size::new(children_size.width, delta.y) - }; - let offset_bounds = Rectangle::new(offset_position, offset_size); - - let children_bounds = Rectangle::new(children_position, children_size); - let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); - - Self { - child_positions, - child_sizes, - children_bounds, - parent_bounds, - check_bounds, - offset_bounds, - } - } */ -} - -pub(super) struct MenuState { - pub(super) index: Option, - scroll_offset: f32, - menu_bounds: MenuBounds, -} -impl MenuState { - /* fn layout( - &self, - overlay_offset: Vector, - slice: MenuSlice, - renderer: &Renderer, - menu_tree: &MenuTree<'_, Message, Theme, Renderer>, - tree: &mut [Tree], - ) -> Node - where - Renderer: renderer::Renderer, - { - let MenuSlice { - start_index, - end_index, - lower_bound_rel, - upper_bound_rel, - } = slice; - - assert_eq!( - menu_tree.children.len(), - self.menu_bounds.child_positions.len() - ); - - // viewport space children bounds - let children_bounds = self.menu_bounds.children_bounds + overlay_offset; - - let child_nodes = self.menu_bounds.child_positions[start_index..=end_index] - .iter() - .zip(self.menu_bounds.child_sizes[start_index..=end_index].iter()) - .zip(menu_tree.children[start_index..=end_index].iter()) - .map(|((cp, size), mt)| { - let mut position = *cp; - let mut size = *size; - - if position < lower_bound_rel && (position + size.height) > lower_bound_rel { - size.height = position + size.height - lower_bound_rel; - position = lower_bound_rel; - } else if position <= upper_bound_rel && (position + size.height) > upper_bound_rel - { - size.height = upper_bound_rel - position; - } - - let limits = Limits::new(Size::ZERO, size); - - let mut node = mt.item.as_widget().layout(&mut tree[mt.index], renderer, &limits); - node.move_to(Point::new(0.0, position + self.scroll_offset)); - node - }) - .collect::>(); - - let mut node = Node::with_children(children_bounds.size(), child_nodes); - node.move_to(children_bounds.position()); - node - } - */ - /* fn layout_single( - &self, - overlay_offset: Vector, - index: usize, - renderer: &Renderer, - menu_tree: &MenuTree<'_, Message, Theme, Renderer>, - tree: &mut Tree, - ) -> Node - where - Renderer: renderer::Renderer, - { - // viewport space children bounds - let children_bounds = self.menu_bounds.children_bounds + overlay_offset; - - let position = self.menu_bounds.child_positions[index]; - let limits = Limits::new(Size::ZERO, self.menu_bounds.child_sizes[index]); - let parent_offset = children_bounds.position() - Point::ORIGIN; - let mut node = menu_tree.item.as_widget().layout(tree, renderer, &limits); - node.move_to(Point::new( - parent_offset.x, - parent_offset.y + position + self.scroll_offset, - )); - node - } - */ - /* fn slice( - &self, - viewport_size: Size, - overlay_offset: Vector, - item_height: ItemHeight, - ) -> MenuSlice { - // viewport space children bounds - let children_bounds = self.menu_bounds.children_bounds + overlay_offset; - - let max_index = self.menu_bounds.child_positions.len().saturating_sub(1); - - // viewport space absolute bounds - let lower_bound = children_bounds.y.max(0.0); - let upper_bound = (children_bounds.y + children_bounds.height).min(viewport_size.height); - - // menu space relative bounds - let lower_bound_rel = lower_bound - (children_bounds.y + self.scroll_offset); - let upper_bound_rel = upper_bound - (children_bounds.y + self.scroll_offset); - - // index range - let (start_index, end_index) = match item_height { - ItemHeight::Uniform(u) => { - let start_index = (lower_bound_rel / f32::from(u)).floor() as usize; - let end_index = ((upper_bound_rel / f32::from(u)).floor() as usize).min(max_index); - (start_index, end_index) - } - ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { - let positions = &self.menu_bounds.child_positions; - let sizes = &self.menu_bounds.child_sizes; - - let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); - let end_index = search_bound( - max_index, - start_index, - max_index, - upper_bound_rel, - positions, - sizes, - ) - .min(max_index); - - (start_index, end_index) - } - }; - - MenuSlice { - start_index, - end_index, - lower_bound_rel, - upper_bound_rel, - } - } - */ -} - -pub(super) struct Menu<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - pub(super) tree: &'b mut Tree, - pub(super) menu_roots: &'b mut Vec>, - pub(super) bounds_expand: u16, - pub(super) close_condition: CloseCondition, - pub(super) item_width: ItemWidth, - pub(super) item_height: ItemHeight, - pub(super) bar_bounds: Rectangle, - pub(super) main_offset: i32, - pub(super) cross_offset: i32, - pub(super) root_bounds_list: Vec, - pub(super) path_highlight: Option, - pub(super) style: &'b Theme::Style, -} -impl<'a, 'b, Message, Theme, Renderer> Menu<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - pub(super) fn overlay(self) -> overlay::Element<'b, Message, Theme, Renderer> { - overlay::Element::new(Point::ORIGIN, Box::new(self)) - } -} -impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay - for Menu<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: Point, - translation: Vector, - ) -> iced_widget::core::layout::Node { - // overlay space viewport rectangle - Node::new(bounds).translate(Point::ORIGIN - position) - } - - fn on_event( - &mut self, - _event: event::Event, - _layout: Layout<'_>, - _cursor: mouse::Cursor, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - _shell: &mut Shell<'_, Message>, - ) -> event::Status { - event::Status::Ignored - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - - } - - fn overlay<'c>( - &'c mut self, - _layout: Layout<'_>, - _renderer: &Renderer, - ) -> Option> { - None - } - - /* fn on_event( - &mut self, - event: event::Event, - layout: Layout<'_>, - view_cursor: Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - use event::{ - Event::{Mouse, Touch}, - Status::{Captured, Ignored}, - }; - use mouse::{ - Button::Left, - Event::{ButtonPressed, ButtonReleased, CursorMoved, WheelScrolled}, - }; - use touch::Event::{FingerLifted, FingerMoved, FingerPressed}; - - if !self.tree.state.downcast_ref::().open { - return Ignored; - }; - - let viewport = layout.bounds(); - let viewport_size = viewport.size(); - let overlay_offset = Point::ORIGIN - viewport.position(); - let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset; - - let menu_status = process_menu_events( - self.tree, - self.menu_roots, - event.clone(), - view_cursor, - renderer, - clipboard, - shell, - overlay_offset, - ); - - init_root_menu( - self, - renderer, - overlay_cursor, - viewport_size, - overlay_offset, - self.bar_bounds, - self.main_offset as f32, - ); - - match event { - Mouse(WheelScrolled { delta }) => { - process_scroll_events(self, delta, overlay_cursor, viewport_size, overlay_offset) - .merge(menu_status) - } - - Mouse(ButtonPressed(Left)) | Touch(FingerPressed { .. }) => { - let state = self.tree.state.downcast_mut::(); - state.pressed = true; - state.view_cursor = view_cursor; - Captured - } - - Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => { - let view_cursor = Cursor::Available(position); - let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset; - process_overlay_events( - self, - renderer, - viewport_size, - overlay_offset, - view_cursor, - overlay_cursor, - self.cross_offset as f32, - ) - .merge(menu_status) - } - - Mouse(ButtonReleased(Left)) | Touch(FingerLifted { .. }) => { - let state = self.tree.state.downcast_mut::(); - state.pressed = false; - - // process close condition - if state - .view_cursor - .position() - .unwrap_or_default() - .distance(view_cursor.position().unwrap_or_default()) - < 2.0 - { - let is_inside = state - .menu_states - .iter() - .any(|ms| ms.menu_bounds.check_bounds.contains(overlay_cursor)); - - if self.close_condition.click_inside && is_inside { - state.reset(); - return Captured; - } - - if self.close_condition.click_outside && !is_inside { - state.reset(); - return Captured; - } - } - - // close all menus when clicking inside the menu bar - if self.bar_bounds.contains(overlay_cursor) { - state.reset(); - Captured - } else { - menu_status - } - } - - _ => menu_status, - } - } - */ - /* #[allow(unused_results)] - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - view_cursor: Cursor, - ) { - let state = self.tree.state.downcast_ref::(); - let Some(active_root) = state.active_root else { - return; - }; - - let viewport = layout.bounds(); - let viewport_size = viewport.size(); - let overlay_offset = Point::ORIGIN - viewport.position(); - let render_bounds = Rectangle::new(Point::ORIGIN, viewport.size()); - - let styling = theme.appearance(self.style); - - let tree = &self.tree.children[active_root].children; - let root = &self.menu_roots[active_root]; - - let indices = state.get_trimmed_indices().collect::>(); - - state - .menu_states - .iter() - .enumerate() - .fold(root, |menu_root, (i, ms)| { - let draw_path = self.path_highlight.as_ref().map_or(false, |ph| match ph { - PathHighlight::Full => true, - PathHighlight::OmitActive => !indices.is_empty() && i < indices.len() - 1, - PathHighlight::MenuActive => i < state.menu_states.len() - 1, - }); - - // react only to the last menu - let view_cursor = if i == state.menu_states.len() - 1 { - view_cursor - } else { - Cursor::Available([-1.0; 2].into()) - }; - - let draw_menu = |r: &mut Renderer| { - // calc slice - let slice = ms.slice(viewport_size, overlay_offset, self.item_height); - let start_index = slice.start_index; - let end_index = slice.end_index; - - // calc layout - let children_node = ms.layout(overlay_offset, slice, r, menu_root); - let children_layout = Layout::new(&children_node); - let children_bounds = children_layout.bounds(); - - // draw menu background - // let bounds = pad_rectangle(children_bounds, styling.background_expand.into()); - // println!("cursor: {:?}", view_cursor); - // println!("bg_bounds: {:?}", bounds); - // println!("color: {:?}\n", styling.background); - let menu_quad = renderer::Quad { - bounds: pad_rectangle(children_bounds, styling.background_expand.into()), - border: Border { - color: styling.border_color, - width: styling.border_width, - radius: styling.border_radius.into() - }, - shadow: Shadow::default(), - }; - let menu_color = styling.background; - r.fill_quad(menu_quad, menu_color); - - // draw path hightlight - if let (true, Some(active)) = (draw_path, ms.index) { - let active_bounds = children_layout - .children() - .nth(active.saturating_sub(start_index)) - .expect("No active children were found in menu?") - .bounds(); - let path_quad = renderer::Quad { - bounds: active_bounds, - border: Border{ - color: Color::TRANSPARENT, - width: 0.0, - radius: styling.border_radius.into(), - }, - shadow: Shadow::default(), - }; - let path_color = styling.path; - r.fill_quad(path_quad, path_color); - } - - // draw item - menu_root.children[start_index..=end_index] - .iter() - .zip(children_layout.children()) - .for_each(|(mt, clo)| { - mt.item.as_widget().draw( - &tree[mt.index], - r, - theme, - style, - clo, - view_cursor, - &children_layout.bounds(), - ); - }); - }; - - renderer.with_layer(render_bounds, draw_menu); - - // only the last menu can have a None active index - ms.index - .map_or(menu_root, |active| &menu_root.children[active]) - }); - } */ -} - -/* fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { - Rectangle { - x: rect.x - padding.left, - y: rect.y - padding.top, - width: rect.width + padding.horizontal(), - height: rect.height + padding.vertical(), - } -} - */ -/* fn init_root_menu( - menu: &mut Menu<'_, '_, Message, Theme, Renderer>, - renderer: &Renderer, - overlay_cursor: Point, - viewport_size: Size, - overlay_offset: Vector, - bar_bounds: Rectangle, - main_offset: f32, -) where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - let state = menu.tree.state.downcast_mut::(); - if !(state.menu_states.is_empty() && bar_bounds.contains(overlay_cursor)) { - return; - } - - for (i, (&root_bounds, mt)) in menu - .root_bounds_list - .iter() - .zip(menu.menu_roots.iter()) - .enumerate() - { - if mt.children.is_empty() { - continue; - } - - if root_bounds.contains(overlay_cursor) { - let view_center = viewport_size.width * 0.5; - let rb_center = root_bounds.center_x(); - - state.horizontal_direction = if rb_center > view_center { - Direction::Negative - } else { - Direction::Positive - }; - - let aod = Aod { - horizontal: true, - vertical: true, - horizontal_overlap: true, - vertical_overlap: false, - horizontal_direction: state.horizontal_direction, - vertical_direction: state.vertical_direction, - horizontal_offset: 0.0, - vertical_offset: main_offset, - }; - - let menu_bounds = MenuBounds::new( - mt, - renderer, - menu.item_width, - menu.item_height, - viewport_size, - overlay_offset, - &aod, - menu.bounds_expand, - root_bounds, - ); - - state.active_root = Some(i); - state.menu_states.push(MenuState { - index: None, - scroll_offset: 0.0, - menu_bounds, - }); - - break; - } - } -} - */ -/* #[allow(clippy::too_many_arguments)] -fn process_menu_events<'b, Message, Theme, Renderer>( - tree: &'b mut Tree, - menu_roots: &'b mut [MenuTree<'_, Message, Theme, Renderer>], - event: event::Event, - view_cursor: Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - overlay_offset: Vector, -) -> event::Status -where - Renderer: renderer::Renderer, -{ - use event::Status; - - let state = tree.state.downcast_mut::(); - let Some(active_root) = state.active_root else { - return Status::Ignored; - }; - - let indices = state.get_trimmed_indices().collect::>(); - - if indices.is_empty() { - return Status::Ignored; - } - - // get active item - let mt = indices - .iter() - .fold(&mut menu_roots[active_root], |mt, &i| &mut mt.children[i]); - - // get layout - let last_ms = &state.menu_states[indices.len() - 1]; - let child_node = last_ms.layout_single( - overlay_offset, - last_ms.index.expect("missing index within menu state."), - renderer, - mt, - ); - let child_layout = Layout::new(&child_node); - - // widget tree - let tree = &mut tree.children[active_root].children[mt.index]; - - // process only the last widget - mt.item.as_widget_mut().on_event( - tree, - event, - child_layout, - view_cursor, - renderer, - clipboard, - shell, - &Rectangle::default(), - ) -} - */ -/* #[allow(unused_results)] -fn process_overlay_events( - menu: &mut Menu<'_, '_, Message, Theme, Renderer>, - renderer: &Renderer, - viewport_size: Size, - overlay_offset: Vector, - view_cursor: Cursor, - overlay_cursor: Point, - cross_offset: f32, -) -> event::Status -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - use event::Status::{Captured, Ignored}; - /* - if no active root || pressed: - return - else: - remove invalid menus // overlay space - update active item - if active item is a menu: - add menu // viewport space - */ - - let state = menu.tree.state.downcast_mut::(); - - let Some(active_root) = state.active_root else { - if !menu.bar_bounds.contains(overlay_cursor) { - state.reset(); - } - return Ignored; - }; - - if state.pressed { - return Ignored; - } - - /* When overlay is running, cursor_position in any widget method will go negative - but I still want Widget::draw() to react to cursor movement */ - state.view_cursor = view_cursor; - - // * remove invalid menus - let mut prev_bounds = std::iter::once(menu.bar_bounds) - .chain( - state.menu_states[..state.menu_states.len().saturating_sub(1)] - .iter() - .map(|ms| ms.menu_bounds.children_bounds), - ) - .collect::>(); - - if menu.close_condition.leave { - for i in (0..state.menu_states.len()).rev() { - let mb = &state.menu_states[i].menu_bounds; - - if mb.parent_bounds.contains(overlay_cursor) - || mb.children_bounds.contains(overlay_cursor) - || mb.offset_bounds.contains(overlay_cursor) - || (mb.check_bounds.contains(overlay_cursor) - && prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor))) - { - break; - } - prev_bounds.pop(); - state.menu_states.pop(); - } - } else { - for i in (0..state.menu_states.len()).rev() { - let mb = &state.menu_states[i].menu_bounds; - - if mb.parent_bounds.contains(overlay_cursor) - || mb.children_bounds.contains(overlay_cursor) - || prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor)) - { - break; - } - prev_bounds.pop(); - state.menu_states.pop(); - } - } - - // get indices - let indices = state - .menu_states - .iter() - .map(|ms| ms.index) - .collect::>(); - - // * update active item - let Some(last_menu_state) = state.menu_states.last_mut() else { - // no menus left - state.active_root = None; - - // keep state.open when the cursor is still inside the menu bar - // this allows the overlay to keep drawing when the cursor is - // moving aroung the menu bar - if !menu.bar_bounds.contains(overlay_cursor) { - state.open = false; - } - return Captured; - }; - - let last_menu_bounds = &last_menu_state.menu_bounds; - let last_parent_bounds = last_menu_bounds.parent_bounds; - let last_children_bounds = last_menu_bounds.children_bounds; - - if last_parent_bounds.contains(overlay_cursor) - // cursor is in the parent part - || !last_children_bounds.contains(overlay_cursor) - // cursor is outside - { - last_menu_state.index = None; - return Captured; - } - // cursor is in the children part - - // calc new index - let height_diff = (overlay_cursor.y - (last_children_bounds.y + last_menu_state.scroll_offset)) - .clamp(0.0, last_children_bounds.height - 0.001); - - let active_menu_root = &menu.menu_roots[active_root]; - - let active_menu = indices[0..indices.len().saturating_sub(1)] - .iter() - .fold(active_menu_root, |mt, i| { - &mt.children[i.expect("missing active child index in menu")] - }); - - let new_index = match menu.item_height { - ItemHeight::Uniform(u) => (height_diff / f32::from(u)).floor() as usize, - ItemHeight::Static(_) | ItemHeight::Dynamic(_) => { - let max_index = active_menu.children.len() - 1; - search_bound( - 0, - 0, - max_index, - height_diff, - &last_menu_bounds.child_positions, - &last_menu_bounds.child_sizes, - ) - } - }; - - // set new index - last_menu_state.index = Some(new_index); - - // get new active item - let item = &active_menu.children[new_index]; - - // * add new menu if the new item is a menu - if !item.children.is_empty() { - let item_position = Point::new( - 0.0, - last_menu_bounds.child_positions[new_index] + last_menu_state.scroll_offset, - ); - let item_size = last_menu_bounds.child_sizes[new_index]; - - // overlay space item bounds - let item_bounds = Rectangle::new(item_position, item_size) - + (last_menu_bounds.children_bounds.position() - Point::ORIGIN); - - let aod = Aod { - horizontal: true, - vertical: true, - horizontal_overlap: false, - vertical_overlap: true, - horizontal_direction: state.horizontal_direction, - vertical_direction: state.vertical_direction, - horizontal_offset: cross_offset, - vertical_offset: 0.0, - }; - - state.menu_states.push(MenuState { - index: None, - scroll_offset: 0.0, - menu_bounds: MenuBounds::new( - item, - renderer, - menu.item_width, - menu.item_height, - viewport_size, - overlay_offset, - &aod, - menu.bounds_expand, - item_bounds, - ), - }); - } - - Captured -} - */ -/* fn process_scroll_events( - menu: &mut Menu<'_, '_, Message, Theme, Renderer>, - delta: mouse::ScrollDelta, - overlay_cursor: Point, - viewport_size: Size, - overlay_offset: Vector, -) -> event::Status -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - use event::Status::{Captured, Ignored}; - use mouse::ScrollDelta; - - let state = menu.tree.state.downcast_mut::(); - - let delta_y = match delta { - ScrollDelta::Lines { y, .. } => y * 60.0, - ScrollDelta::Pixels { y, .. } => y, - }; - - let calc_offset_bounds = |menu_state: &MenuState, viewport_size: Size| -> (f32, f32) { - // viewport space children bounds - let children_bounds = menu_state.menu_bounds.children_bounds + overlay_offset; - - let max_offset = (0.0 - children_bounds.y).max(0.0); - let min_offset = - (viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0); - (max_offset, min_offset) - }; - - // update - if state.menu_states.is_empty() { - return Ignored; - } else if state.menu_states.len() == 1 { - let last_ms = &mut state.menu_states[0]; - - if last_ms.index.is_none() { - return Captured; - } - - let (max_offset, min_offset) = calc_offset_bounds(last_ms, viewport_size); - last_ms.scroll_offset = (last_ms.scroll_offset + delta_y).clamp(min_offset, max_offset); - } else { - // >= 2 - let max_index = state.menu_states.len() - 1; - let last_two = &mut state.menu_states[max_index - 1..=max_index]; - - if last_two[1].index.is_some() { - // scroll the last one - let (max_offset, min_offset) = calc_offset_bounds(&last_two[1], viewport_size); - last_two[1].scroll_offset = - (last_two[1].scroll_offset + delta_y).clamp(min_offset, max_offset); - } else { - if !last_two[0] - .menu_bounds - .children_bounds - .contains(overlay_cursor) - { - return Captured; - } - - // scroll the second last one - let (max_offset, min_offset) = calc_offset_bounds(&last_two[0], viewport_size); - let scroll_offset = (last_two[0].scroll_offset + delta_y).clamp(min_offset, max_offset); - let clamped_delta_y = scroll_offset - last_two[0].scroll_offset; - last_two[0].scroll_offset = scroll_offset; - - // update the bounds of the last one - last_two[1].menu_bounds.parent_bounds.y += clamped_delta_y; - last_two[1].menu_bounds.children_bounds.y += clamped_delta_y; - last_two[1].menu_bounds.check_bounds.y += clamped_delta_y; - } - } - Captured -} - */ -/* #[allow(clippy::pedantic)] -/// Returns (children_size, child_positions, child_sizes) -fn get_children_layout( - menu_tree: &MenuTree<'_, Message, Theme, Renderer>, - renderer: &Renderer, - item_width: ItemWidth, - item_height: ItemHeight, - tree: &mut [Tree], -) -> (Size, Vec, Vec) -where - Renderer: renderer::Renderer, -{ - let width = match item_width { - ItemWidth::Uniform(u) => f32::from(u), - ItemWidth::Static(s) => f32::from(menu_tree.width.unwrap_or(s)), - }; - - let child_sizes: Vec = match item_height { - ItemHeight::Uniform(u) => { - let count = menu_tree.children.len(); - (0..count).map(|_| Size::new(width, f32::from(u))).collect() - } - ItemHeight::Static(s) => menu_tree - .children - .iter() - .map(|mt| Size::new(width, f32::from(mt.height.unwrap_or(s)))) - .collect(), - ItemHeight::Dynamic(d) => menu_tree - .children - .iter() - .map(|mt| { - let w = mt.item.as_widget(); - match w.size().height { - Length::Fixed(f) => Size::new(width, f), - Length::Shrink => { - let l_height = w - .layout( - &mut tree[mt.index], - renderer, - &Limits::new(Size::ZERO, Size::new(width, f32::MAX)), - ) - .size() - .height; - - let height = if (f32::MAX - l_height) < 0.001 { - f32::from(d) - } else { - l_height - }; - - Size::new(width, height) - } - _ => mt.height.map_or_else( - || Size::new(width, f32::from(d)), - |h| Size::new(width, f32::from(h)), - ), - } - }) - .collect(), - }; - - let max_index = menu_tree.children.len() - 1; - let child_positions: Vec = std::iter::once(0.0) - .chain(child_sizes[0..max_index].iter().scan(0.0, |acc, x| { - *acc += x.height; - Some(*acc) - })) - .collect(); - - let height = child_sizes.iter().fold(0.0, |acc, x| acc + x.height); - - (Size::new(width, height), child_positions, child_sizes) -} */ - -/* -fn search_bound( - default: usize, - default_left: usize, - default_right: usize, - bound: f32, - positions: &[f32], - sizes: &[Size], -) -> usize { - // binary search - let mut left = default_left; - let mut right = default_right; - - let mut index = default; - while left != right { - let m = ((left + right) / 2) + 1; - if positions[m] > bound { - right = m - 1; - } else { - left = m; - } - } - // let height = f32::from(menu_tree.children[left].height.unwrap_or(default_height)); - let height = sizes[left].height; - if positions[left] + height > bound { - index = left; - } - index -} - */ \ No newline at end of file diff --git a/src/native/menu/bk/menu_tree.rs b/src/native/menu/bk/menu_tree.rs deleted file mode 100644 index 5ed8d7ad..00000000 --- a/src/native/menu/bk/menu_tree.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! A tree structure for constructing a hierarchical menu - -use iced_widget::core::{renderer, Element}; -/// Nested menu is essentially a tree of items, a menu is a collection of items -/// a menu itself can also be an item of another menu. -/// -/// A `MenuTree` represents a node in the tree, it holds a widget as a menu item -/// for its parent, and a list of menu tree as child nodes. -/// Conceptually a node is either a menu(inner node) or an item(leaf node), -/// but there's no need to explicitly distinguish them here, if a menu tree -/// has children, it's a menu, otherwise it's an item -#[allow(missing_debug_implementations)] -pub struct MenuTree<'a, Message, Theme = iced_widget::Theme, Renderer = iced_widget::Renderer> { - /// The menu tree will be flatten into a vector to build a linear widget tree, - /// the `index` field is the index of the item in that vector - pub(super) index: usize, - - /// The item of the menu tree - pub(super) item: Element<'a, Message, Theme, Renderer>, - /// The children of the menu tree - pub(super) children: Vec>, - /// The width of the menu tree - pub(super) width: Option, - /// The height of the menu tree - pub(super) height: Option, -} -impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - /// Create a new menu tree from a widget - pub fn new(item: impl Into>) -> Self { - Self { - index: 0, - item: item.into(), - children: Vec::new(), - width: None, - height: None, - } - } - - /// Create a menu tree from a widget and a vector of sub trees - pub fn with_children( - item: impl Into>, - children: Vec>>, - ) -> Self { - Self { - index: 0, - item: item.into(), - children: children.into_iter().map(Into::into).collect(), - width: None, - height: None, - } - } - - /// Sets the width of the menu tree. - /// See [`ItemWidth`] - /// - /// [`ItemWidth`]:`super::ItemWidth` - #[must_use] - pub fn width(mut self, width: u16) -> Self { - self.width = Some(width); - self - } - - /// Sets the height of the menu tree. - /// See [`ItemHeight`] - /// - /// [`ItemHeight`]: `super::ItemHeight` - #[must_use] - pub fn height(mut self, height: u16) -> Self { - self.height = Some(height); - self - } - - /* Keep `set_index()` and `flattern()` recurse in the same order */ - - /// Set the index of each item - pub(super) fn set_index(&mut self) { - /// inner counting function. - fn rec(mt: &mut MenuTree<'_, Message, Theme, Renderer>, count: &mut usize) { - // keep items under the same menu line up - mt.children.iter_mut().for_each(|c| { - c.index = *count; - *count += 1; - }); - - mt.children.iter_mut().for_each(|c| rec(c, count)); - } - - let mut count = 0; - self.index = count; - count += 1; - rec(self, &mut count); - } - - /// Flatten the menu tree - pub(super) fn flattern(&'a self) -> Vec<&Self> { - /// Inner flattening function - fn rec<'a, Message, Theme, Renderer>( - mt: &'a MenuTree<'a, Message, Theme, Renderer>, - flat: &mut Vec<&MenuTree<'a, Message, Theme, Renderer>>, - ) { - mt.children.iter().for_each(|c| { - flat.push(c); - }); - - mt.children.iter().for_each(|c| { - rec(c, flat); - }); - } - - let mut flat = Vec::new(); - flat.push(self); - rec(self, &mut flat); - - flat - } -} - -impl<'a, Message, Theme, Renderer> From> for MenuTree<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn from(value: Element<'a, Message, Theme, Renderer>) -> Self { - Self::new(value) - } -} diff --git a/src/native/menu/bk/menux.rs b/src/native/menu/bk/menux.rs deleted file mode 100644 index 2d3e31e0..00000000 --- a/src/native/menu/bk/menux.rs +++ /dev/null @@ -1,844 +0,0 @@ -//! doc -//! -use iced_widget::core::{ - event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, - Alignment, Border, Clipboard, Color, Element, Event, Length, - Padding, Point, Vector, Rectangle, Shell, Size, Widget, -}; -use super::types::*; - -struct MenuxState{ - open: bool, - scroll_offset: f32, -} -impl Default for MenuxState{ - fn default() -> Self { - Self { open: false, scroll_offset: 0.0 } - } -} - -/// doc -#[allow(clippy::missing_docs_in_private_parents)] -pub struct Menux<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - parent: Element<'a, Message, Theme, Renderer>, - children: Vec>, - spacing: f32, - padding: Padding, - width: Length, - height: Length, - axis: Axis, - offset: f32, - open_condition: OpenCondition, -} -impl<'a, Message, Theme, Renderer> Menux<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - #[allow(missing_docs)] - pub fn new(parent: Element<'a, Message, Theme, Renderer>, children: Vec>) -> Self{ - Self{ - parent, - children, - spacing: 4.0, - padding: [0.0;4].into(), - width: Length::Shrink, - height: Length::Shrink, - axis: Axis::Vertical, - offset: 0.0, - open_condition: OpenCondition::Hover, - } - } - - /// Sets the vertical spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, spacing: f32) -> Self { - self.spacing = spacing; - self - } - - /// Sets the [`Padding`] of the [`Menux`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Menux`]. - pub fn width(mut self, width: impl Into) -> Self { - self.width = width.into(); - self - } - - /// Sets the height of the [`Menux`]. - pub fn height(mut self, height: impl Into) -> Self { - self.height = height.into(); - self - } - - /// Sets the axis of the [`Menux`]. - pub fn axis(mut self, axis: Axis) -> Self { - self.axis = axis.into(); - self - } - - /// Sets the open condition of the [`Menux`]. - pub fn open_condition(mut self, open_condition: OpenCondition) -> Self { - self.open_condition = open_condition; - self - } -} -impl <'a, Message, Theme, Renderer> Widget for Menux<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn size(&self) -> Size { - Size::new(self.width, self.height) - } - - fn state(&self) -> tree::State { - tree::State::new(MenuxState::default()) - } - - fn children(&self) -> Vec { - // println!("mx children"); - [ - Tree::new(&self.parent), - Tree{ - children: self.children.iter().map(Tree::new).collect::>(), - ..Tree::empty() - } - ].into() - } - - fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - // println!("mx layout"); - self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: event::Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - // println!("mx event"); - let status = self.parent.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - viewport, - ); - - let state = tree.state.downcast_mut::(); - let bounds = layout.bounds(); - - use event::Status::*; - match self.open_condition{ - OpenCondition::Click => match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if cursor.is_over(bounds) { - state.open = true; - state.scroll_offset = 0.0; - Captured - }else{ - Ignored - } - } - _ => Ignored - } - OpenCondition::Hover => match event { - Event::Mouse(mouse::Event::CursorMoved { position }) => { - if bounds.contains(position) { - state.open = true; - state.scroll_offset = 0.0; - Captured - }else{ - Ignored - } - } - _ => Ignored - } - } - .merge(status) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - // println!("mx draw"); - self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: layout::Layout<'_>, - renderer: &Renderer, - ) -> Option> { - // println!("mx overlay"); - let mut og = overlay::Group::new(); - let Tree { tag, state, children } = tree; - - let [parent_tree, children_tree] = children.as_mut_slice() else { panic!("Tree Error") }; - - if let Some(c) = self.parent - .as_widget_mut() - .overlay(parent_tree, layout, renderer) - { - og = og.push(c); - } - - let ms = state.downcast_mut::(); - if !ms.open { - Some(og.overlay()) - }else{ - Some(og.push( - MenuxOverlay{ - state: ms, - tree: children_tree, - children: &mut self.children, - parent_bounds: layout.bounds(), - max_width: 1000.0, - spacing: self.spacing, - padding: self.padding, - width: self.width, - height: self.height, - axis: self.axis, - offset: self.offset, - }.overlay() - ).overlay()) - } - } -} - -impl<'a, Message, Theme, Renderer> From> - for Element<'a, Message, Theme, Renderer> -where - Message: 'a, - Theme: 'a, - Renderer: renderer::Renderer + 'a, -{ - fn from(m: Menux<'a, Message, Theme, Renderer>) -> Self { - Self::new(m) - } -} - -struct MenuxOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer -{ - state: &'b mut MenuxState, - tree: &'b mut Tree, - children: &'b mut [Element<'a, Message, Theme, Renderer>], - parent_bounds: Rectangle, - max_width: f32, - spacing: f32, - padding: Padding, - width: Length, - height: Length, - axis: Axis, - offset: f32, -} -impl<'a, 'b, Message, Theme, Renderer> MenuxOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer -{ - fn overlay(self) -> overlay::Element<'b, Message, Theme, Renderer>{ - overlay::Element::new(Point::ORIGIN, Box::new(self)) - } -} -impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuxOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: iced_widget::core::Point, - translation: iced_widget::core::Vector, - ) -> layout::Node { - println!(); - println!("mxo layout"); - println!("bounds: {:?}", bounds); - println!("position: {:?}", position); - println!("translation: {:?}", translation); - println!(); - - let limits = layout::Limits::NONE.max_width(self.max_width); - let layout = layout::flex::resolve( - layout::flex::Axis::Vertical, - renderer, - &limits, - self.width, - self.height, - self.padding, - self.spacing, - Alignment::Center, - &self.children, - &mut self.tree.children - ); - - let vpb = self.parent_bounds + translation; // viewport space parent bounds - - let hcenter = bounds.width / 2.0; - let vcenter = bounds.height / 2.0; - - let phcenter = vpb.x + vpb.width / 2.0; - let pvcenter = vpb.y + vpb.height / 2.0; - - let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; - let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; - - let aod = match self.axis { - Axis::Horizontal => Aod{ - horizontal: false, - vertical: true, - horizontal_overlap: false, - vertical_overlap: true, - horizontal_direction, - vertical_direction, - horizontal_offset: self.offset, - vertical_offset: 0.0, - }, - Axis::Vertical => Aod{ - horizontal: true, - vertical: false, - horizontal_overlap: true, - vertical_overlap: false, - horizontal_direction, - vertical_direction, - horizontal_offset: 0.0, - vertical_offset: self.offset, - } - }; - - let children_size = layout.bounds().size(); - let (children_position, offset_position) = aod.resolve( - vpb, - children_size, - bounds - ); - - // calc offset bounds - let delta = children_position - offset_position; - let offset_size = if delta.x.abs() > delta.y.abs() { - Size::new(delta.x, children_size.height) - } else { - Size::new(children_size.width, delta.y) - }; - let offset_bounds = Rectangle::new(offset_position, offset_size); - let children_bounds = Rectangle::new(children_position, children_size); - let bounds_expand = 30.0; - let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); - - layout::Node::with_children(Size::INFINITY, [ - layout.move_to(children_position) - .translate([0.0, self.state.scroll_offset]), // children layout - layout::Node::new(children_size) - .move_to(children_position), // prescroll children bounds - layout::Node::new(bounds), // viewport - layout::Node::new(offset_bounds.size()) - .move_to(offset_bounds.position()) - .translate([0.0, self.state.scroll_offset]), // offset bounds - layout::Node::new(check_bounds.size()) - .move_to(check_bounds.position()) - .translate([0.0, self.state.scroll_offset]), // check bounds - ].into()) - } - - fn on_event( - &mut self, - event: event::Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - println!("mxo event"); - use event::Status::*; - - let mut lc = layout.children(); - let children_layout = lc.next().unwrap(); - let prescroll_children_bounds = lc.next().unwrap().bounds(); - let viewport = lc.next().unwrap().bounds(); - let offset_bounds = lc.next().unwrap().bounds(); - let check_bounds = lc.next().unwrap().bounds(); - - let children_bounds = children_layout.bounds(); - - let status = self.children - .iter_mut() - .zip(&mut self.tree.children) - .zip(children_layout.children()) - .map(|((child, tree), layout)| - child.as_widget_mut().on_event( - tree, - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - &viewport, - ) - ) - .fold(event::Status::Ignored, event::Status::merge); - - match event { - Event::Mouse(mouse::Event::WheelScrolled { delta }) => { - if cursor.is_over(children_bounds){ - // self.state.scroll_offset += match delta{ - // mouse::ScrollDelta::Lines { x, y } => y, - // mouse::ScrollDelta::Pixels { x, y } => y, - // }; - // Ignored - process_scroll_event(&mut self.state, prescroll_children_bounds, delta, viewport.size()) - }else{ - Ignored - } - } - Event::Mouse(mouse::Event::CursorMoved { position }) => { - self.state.open = - self.parent_bounds.contains(position) - || offset_bounds.contains(position) - || check_bounds.contains(position); - Captured - } - _ => Ignored - }.merge(status) - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - ) { - println!("mxo draw"); - let mut lc = layout.children(); - let children_layout = lc.next().unwrap(); - let prescroll_children_bounds = lc.next().unwrap().bounds(); - let viewport = lc.next().unwrap().bounds(); - // let offset_bounds = lc.next().unwrap().bounds(); - // let check_bounds = lc.next().unwrap().bounds(); - - renderer.fill_quad( - renderer::Quad{ - bounds: self.parent_bounds, - ..Default::default() - }, - Color::from([1.0, 0.0, 0.0, 0.5]) - ); - - renderer.fill_quad( - renderer::Quad{ - bounds: pad_rectangle(prescroll_children_bounds, 4.0.into()), - border: Border{ - color: [0.5; 3].into(), - width: 1.5, - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() - }, - Color::from([1.0; 3]) - ); - - if let Some(viewport) = children_layout.bounds().intersection(&viewport) { - for ((child, tree), layout) in self - .children - .iter() - .zip(&self.tree.children) - .zip(children_layout.children()) - { - child.as_widget().draw( - tree, renderer, theme, style, layout, cursor, &viewport, - ); - } - } - } - - fn overlay<'c>( - &'c mut self, - layout: layout::Layout<'_>, - renderer: &Renderer, - ) -> Option> { - println!("mxo overlay"); - overlay::from_children( - self.children, - self.tree, - layout.children().next().unwrap(), - renderer - ) - } - - fn is_over( - &self, - layout: layout::Layout<'_>, - _renderer: &Renderer, - cursor_position: Point, - ) -> bool { - let mut lc = layout.children(); - let children_layout = lc.next().unwrap(); - children_layout.bounds().contains(cursor_position) - } -} - - -/// Adaptive open direction -#[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] -struct Aod { - // whether or not to use aod - horizontal: bool, - vertical: bool, - - // whether or not to use overlap - horizontal_overlap: bool, - vertical_overlap: bool, - - // default direction - horizontal_direction: Direction, - vertical_direction: Direction, - - // Offset of the child in the default direction - horizontal_offset: f32, - vertical_offset: f32, -} -impl Aod { - /// Returns child position and offset position - #[allow(clippy::too_many_arguments)] - fn adaptive( - parent_pos: f32, - parent_size: f32, - child_size: f32, - max_size: f32, - offset: f32, - on: bool, - overlap: bool, - direction: Direction, - ) -> (f32, f32) { - /* - Imagine there're two sticks, parent and child - parent: o-----o - child: o----------o - - Now we align the child to the parent in one dimension - There are 4 possibilities: - - 1. to the right - o-----oo----------o - - 2. to the right with overlaping - o-----o - o----------o - - 3. to the left - o----------oo-----o - - 4. to the left with overlaping - o-----o - o----------o - - The child goes to the default direction by default, - if the space on the default direction runs out it goes to the the other, - whether to use overlap is the caller's decision - - This can be applied to any direction - */ - - match direction { - Direction::Positive => { - let space_negative = parent_pos; - let space_positive = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - overshoot) - } else { - (parent_pos, parent_pos) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - offset) - } else { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } - } - } - Direction::Negative => { - let space_positive = parent_pos; - let space_negative = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos, parent_pos) - } else { - (parent_pos - overshoot, parent_pos - overshoot) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } else { - (parent_pos - overshoot, parent_pos - offset) - } - } - } - } - } - - /// Returns child position and offset position - fn resolve( - &self, - parent_bounds: Rectangle, - children_size: Size, - viewport_size: Size, - ) -> (Point, Point) { - let (x, ox) = Self::adaptive( - parent_bounds.x, - parent_bounds.width, - children_size.width, - viewport_size.width, - self.horizontal_offset, - self.horizontal, - self.horizontal_overlap, - self.horizontal_direction, - ); - let (y, oy) = Self::adaptive( - parent_bounds.y, - parent_bounds.height, - children_size.height, - viewport_size.height, - self.vertical_offset, - self.vertical, - self.vertical_overlap, - self.vertical_direction, - ); - - ([x, y].into(), [ox, oy].into()) - } -} - -fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { - Rectangle { - x: rect.x - padding.left, - y: rect.y - padding.top, - width: rect.width + padding.horizontal(), - height: rect.height + padding.vertical(), - } -} - - -/// A part of a menu where items are displayed. -/// -/// When the bounds of a menu exceed the viewport, -/// only items inside the viewport will be displayed, -/// when scrolling happens, this should be updated -#[derive(Debug, Clone, Copy)] -struct MenuSlice { - start_index: usize, - end_index: usize, - lower_bound_rel: f32, - upper_bound_rel: f32, -} - -fn slice( - children_bounds: Rectangle, // offset + unsrolled - child_positions: Vec, - child_sizes: Vec, - scroll_offset: f32, - viewport_size: Size, - // overlay_offset: Vector, -) -> MenuSlice { - // viewport space children bounds - // let children_bounds = children_bounds + overlay_offset; - - let max_index = child_positions.len().saturating_sub(1); - - // viewport space absolute bounds - let lower_bound = children_bounds.y.max(0.0); - let upper_bound = (children_bounds.y + children_bounds.height).min(viewport_size.height); - - // menu space relative bounds - let lower_bound_rel = lower_bound - (children_bounds.y + scroll_offset); - let upper_bound_rel = upper_bound - (children_bounds.y + scroll_offset); - - // index range - let positions = &child_positions; - let sizes = &child_sizes; - - let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); - let end_index = - search_bound(max_index, start_index, max_index, upper_bound_rel, positions, sizes) - .min(max_index); - - MenuSlice { - start_index, - end_index, - lower_bound_rel, - upper_bound_rel, - } -} - -fn search_bound( - default: usize, - default_left: usize, - default_right: usize, - bound: f32, - positions: &[f32], - sizes: &[Size], -) -> usize { - // binary search - let mut left = default_left; - let mut right = default_right; - - let mut index = default; - while left != right { - let m = ((left + right) / 2) + 1; - if positions[m] > bound { - right = m - 1; - } else { - left = m; - } - } - let height = sizes[left].height; - if positions[left] + height > bound { - index = left; - } - index -} - -fn process_scroll_event( - state: &mut MenuxState, - prescroll_children_bounds: Rectangle, - delta: mouse::ScrollDelta, - viewport_size: Size, -) -> event::Status{ - use mouse::ScrollDelta; - - let pcb = prescroll_children_bounds; - - let delta_y = match delta { - ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, - }; - - let max_offset = (0.0 - pcb.y).max(0.0); - let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); - state.scroll_offset = (state.scroll_offset + delta_y).clamp(min_offset, max_offset); - - event::Status::Captured -} - -/* fn process_scroll_events( - menu: &mut Menu<'_, '_, Message, Theme, Renderer>, - delta: mouse::ScrollDelta, - overlay_cursor: Point, - viewport_size: Size, - overlay_offset: Vector, -) -> event::Status -where - Renderer: renderer::Renderer, - Theme: StyleSheet, -{ - use event::Status::{Captured, Ignored}; - use mouse::ScrollDelta; - - let state = menu.tree.state.downcast_mut::(); - - let delta_y = match delta { - ScrollDelta::Lines { y, .. } => y * 60.0, - ScrollDelta::Pixels { y, .. } => y, - }; - - let calc_offset_bounds = |menu_state: &MenuState, viewport_size: Size| -> (f32, f32) { - // viewport space children bounds - let children_bounds = menu_state.menu_bounds.children_bounds + overlay_offset; - - let max_offset = (0.0 - children_bounds.y).max(0.0); - let min_offset = - (viewport_size.height - (children_bounds.y + children_bounds.height)).min(0.0); - (max_offset, min_offset) - }; - - // update - if state.menu_states.is_empty() { - return Ignored; - } else if state.menu_states.len() == 1 { - let last_ms = &mut state.menu_states[0]; - - if last_ms.index.is_none() { - return Captured; - } - - let (max_offset, min_offset) = calc_offset_bounds(last_ms, viewport_size); - last_ms.scroll_offset = (last_ms.scroll_offset + delta_y).clamp(min_offset, max_offset); - } else { - // >= 2 - let max_index = state.menu_states.len() - 1; - let last_two = &mut state.menu_states[max_index - 1..=max_index]; - - if last_two[1].index.is_some() { - // scroll the last one - let (max_offset, min_offset) = calc_offset_bounds(&last_two[1], viewport_size); - last_two[1].scroll_offset = - (last_two[1].scroll_offset + delta_y).clamp(min_offset, max_offset); - } else { - if !last_two[0] - .menu_bounds - .children_bounds - .contains(overlay_cursor) - { - return Captured; - } - - // scroll the second last one - let (max_offset, min_offset) = calc_offset_bounds(&last_two[0], viewport_size); - let scroll_offset = (last_two[0].scroll_offset + delta_y).clamp(min_offset, max_offset); - let clamped_delta_y = scroll_offset - last_two[0].scroll_offset; - last_two[0].scroll_offset = scroll_offset; - - // update the bounds of the last one - last_two[1].menu_bounds.parent_bounds.y += clamped_delta_y; - last_two[1].menu_bounds.children_bounds.y += clamped_delta_y; - last_two[1].menu_bounds.check_bounds.y += clamped_delta_y; - } - } - Captured -} -*/ - diff --git a/src/native/menu/bk/types.rs b/src/native/menu/bk/types.rs deleted file mode 100644 index ea90ee89..00000000 --- a/src/native/menu/bk/types.rs +++ /dev/null @@ -1,78 +0,0 @@ - -/// The condition of when to close a menu -#[derive(Debug, Clone, Copy)] -pub struct CloseCondition { - /// Close menus when the cursor moves outside the check bounds - pub leave: bool, - - /// Close menus when the cursor clicks outside the check bounds - pub click_outside: bool, - - /// Close menus when the cursor clicks inside the check bounds - pub click_inside: bool, -} - -/// The width of an item -#[derive(Debug, Clone, Copy)] -pub enum ItemWidth { - /// Use uniform width - Uniform(u16), - /// Static tries to use the width value of each menu(menu tree with children), - /// the widths of items(menu tree with empty children) will be the same as the menu they're in, - /// if that value is None, - /// the default value will be used instead, - /// which is the value of the Static variant - Static(u16), -} - -/// The height of an item -#[derive(Debug, Clone, Copy)] -pub enum ItemHeight { - /// Use uniform height. - Uniform(u16), - /// Static tries to use `MenuTree.height` as item height, - /// when it's `None` it'll fallback to the value of the `Static` variant. - Static(u16), - /// Dynamic tries to automatically choose the proper item height for you, - /// but it only works in certain cases: - /// - /// - Fixed height - /// - Shrink height - /// - Menu tree height - /// - /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. - Dynamic(u16), -} - -/// Methods for drawing path highlight -#[derive(Debug, Clone, Copy)] -pub enum PathHighlight { - /// Draw the full path, - Full, - /// Omit the active item(the last item in the path) - OmitActive, - /// Omit the active item if it's not a menu - MenuActive, -} - -/// X+ goes right and Y+ goes down -#[derive(Debug, Clone, Copy)] -pub(super) enum Direction { - Positive, - Negative, -} - -/// Axis -#[allow(missing_docs)] -#[derive(Debug, Clone, Copy)] -pub enum Axis{ - Horizontal, - Vertical, -} - -#[allow(missing_docs)] -pub enum OpenCondition{ - Hover, - Click, -} - diff --git a/src/native/menu/bk2/menu_bar.rs b/src/native/menu/bk2/menu_bar.rs deleted file mode 100644 index 6399d916..00000000 --- a/src/native/menu/bk2/menu_bar.rs +++ /dev/null @@ -1,165 +0,0 @@ -use iced_widget::core::{ - alignment, event, layout, mouse, overlay, renderer, touch, widget::{tree, Tree}, Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget -}; - -use super::{ - flex, menu_bar_overlay::MenuBarOverlay, menu_tree::* -}; - -pub(super) struct MenuBarState{ - pub(super) active_root: usize, - pub(super) open: bool, -} -impl Default for MenuBarState{ - fn default() -> Self { - Self { - active_root: 0, - open: false, - } - } -} - -pub struct MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - roots: Vec>, - spacing: f32, - padding: Padding, - width: Length, - height: Length, -} -impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub fn new(roots: Vec>) -> Self { - Self { - roots, - spacing: 0.0, - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - } - } - -} -impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - - fn size(&self) -> Size { - Size::new(self.width, self.height) - } - - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::Some(Box::new(MenuBarState::default())) - } - - fn children(&self) -> Vec { - self.roots.iter().map(|mt| mt.tree()).collect::>() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.roots, - |tree, mt| diff(tree, mt), - |mt| mt.tree() - ) - } - - fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - flex::resolve( - flex::Axis::Horizontal, - renderer, - limits, - self.width, - self.height, - self.padding, - self.spacing, - alignment::Alignment::Center, - &self.roots.iter().map(|mt| &mt.parent).collect::>(), - &mut tree.children.iter().map(|tree| &mut tree.children[0]).collect::>(), - ) - } - - fn on_event( - &mut self, - state: &mut Tree, - event: event::Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - - event::Status::Ignored - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - if let Some(viewport) = layout.bounds().intersection(viewport) { - for ((root, state), layout) in self - .roots - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - root.parent.as_widget().draw( - state, renderer, theme, style, layout, cursor, &viewport, - ); - } - } - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - let state = tree.state.downcast_mut::(); - let parent_bounds = layout.children().nth(state.active_root).unwrap().bounds(); - - if state.open{ - Some(MenuBarOverlay{ - tree, - active_tree: &mut tree.children[state.active_root], - active_root: &mut self.roots[state.active_root], - parent_bounds, - }.overlay_element()) - }else { - None - } - } -} -impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> -where - Message: 'a, - Renderer: 'a + renderer::Renderer, - // Renderer::Theme: StyleSheet, -{ - fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { - Self::new(value) - } -} diff --git a/src/native/menu/bk2/menu_bar_overlay.rs b/src/native/menu/bk2/menu_bar_overlay.rs deleted file mode 100644 index 8d679677..00000000 --- a/src/native/menu/bk2/menu_bar_overlay.rs +++ /dev/null @@ -1,188 +0,0 @@ -use iced_widget::core::{ - event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget -}; -use crate::menu::menu_tree_overlay::MenuTreeOverlay; - -use super::{ - menu_tree::{MenuTree, MenuTreeState}, - menu_bar::MenuBarState, -}; - -pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub(super) tree: &'b mut Tree, - pub(super) active_tree: &'b mut Tree, - pub(super) active_root: &'b mut MenuTree<'a, Message, Theme, Renderer>, - pub(super) parent_bounds: Rectangle, - -} -impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub(super) fn overlay_element(self) -> overlay::Element<'a, Message, Theme, Renderer>{ - overlay::Element::new(Point::ORIGIN, Box::new(self)) - } -} -impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: Point, - translation: Vector, - ) -> Node { - /* - Node{ - rect: - children: [ - Node{} // mto layout - Node{} // overlays - Node{} // next mto - ] - } - */ - - fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( - tree: &mut Tree, - menu_tree: &MenuTree<'a, Message, Theme, Renderer>, - parent_bounds: Rectangle, - renderer: &Renderer, - bounds: Size, - position: Point, - translation: Vector, - ) -> Node { - let size = Size::INFINITY; - let state = tree.state.downcast_mut::(); - - let mut mto = MenuTreeOverlay{ - state, - tree, - menu_tree, - parent_bounds, - }; - - let mto_layout = mto.layout(renderer, bounds, position, translation); - - let overlays = menu_tree.children - .iter() - .zip(tree.children[1].children.iter_mut()) - .zip(mto_layout.children()[0].children()) - .filter_map(|((mt, t), n)|{ - mt.parent.as_widget_mut() - .overlay(t, Layout::new(n), renderer) - .and_then(|mut o| Some(o.layout(renderer, bounds, translation))) - }) - .collect::>(); - - let next = menu_tree.children - .iter() - .zip(tree.children[1].children.iter_mut()) - .zip(mto_layout.children()[0].children()) - .find_map(|((mt, t), l)|{ - let state = t.state.downcast_mut::(); - if state.open{ - let parent_bounds = l.bounds(); - // let position = parent_bounds.position(); - let position = Point::ORIGIN; - Some(rec(t, mt, parent_bounds, renderer, bounds, position, position - Point::ORIGIN)) - }else{ - None - } - }); - - Node::with_children( - size, - next.map_or( - [ - mto_layout, - Node::with_children(size, overlays), - ].into(), - |n| [ - mto_layout, - Node::with_children(size, overlays), - n - ].into() - ) - ) - } - - rec( - self.active_tree, - self.active_root, - self.parent_bounds, - renderer, - bounds, - position, - translation - ) - } - - fn on_event( - &mut self, - event: Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - todo!() - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( - tree: &mut Tree, - menu_tree: &MenuTree<'a, Message, Theme, Renderer>, - parent_bounds: Rectangle, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ){ - let mut lc = layout.children(); - let mto_layout = lc.next().unwrap(); - let overlay_layout = lc.next().unwrap(); - - let state = tree.state.downcast_mut::(); - let mut mto = MenuTreeOverlay{ - state, - tree, - menu_tree, - parent_bounds, - }; - - mto.draw(renderer, theme, style, mto_layout, cursor); - - menu_tree.children - .iter() - .zip(tree.children[1].children.iter_mut()) - .filter_map(|(mt, t)|{ - mt.parent.as_widget_mut().overlay(t, Layout::new(&mt.layout(t, renderer, &Limits::NONE)), renderer) - }) - .zip(overlay_layout.children()) - .for_each(|(o, l)|{ - o.draw(renderer, theme, style, l, cursor) - }); - - if let Some(next_layout) = lc.next(){ - rec(tree, menu_tree, parent_bounds, renderer, theme, style, next_layout, cursor, viewport); - } - - } - } -} \ No newline at end of file diff --git a/src/native/menu/bk2/menu_tree.rs b/src/native/menu/bk2/menu_tree.rs deleted file mode 100644 index 501d157e..00000000 --- a/src/native/menu/bk2/menu_tree.rs +++ /dev/null @@ -1,322 +0,0 @@ -//! doc -//! -use iced_widget::core::{ - event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, - Alignment, Border, Clipboard, Color, Element, Event, Length, - Padding, Point, Vector, Rectangle, Shell, Size, Widget, -}; -use super::types::*; -use super::menu_tree_overlay::MenuTreeOverlay; - -pub(super) struct MenuTreeState{ - pub(super) open: bool, - pub(super) scroll_offset: f32, -} -impl Default for MenuTreeState{ - fn default() -> Self { - Self { open: false, scroll_offset: 0.0 } - } -} - -/// doc -#[allow(clippy::missing_docs_in_private_parents)] -pub struct MenuTree<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub(super) parent: Element<'a, Message, Theme, Renderer>, - pub(super) children: Vec>, - pub(super) spacing: f32, - pub(super) padding: Padding, - pub(super) max_width: f32, - pub(super) width: Length, - pub(super) height: Length, - pub(super) axis: Axis, - pub(super) offset: f32, - pub(super) open_condition: OpenCondition, -} -impl<'a, Message, Theme, Renderer> MenuTree<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - #[allow(missing_docs)] - pub fn new( - parent: impl Into> - ) -> Self{ - Self{ - parent: parent.into(), - children: Vec::new(), - spacing: 4.0, - padding: [0.0;4].into(), - max_width: f32::MAX, - width: Length::Shrink, - height: Length::Shrink, - axis: Axis::Vertical, - offset: 0.0, - open_condition: OpenCondition::Hover, - } - } - - #[allow(missing_docs)] - pub fn with_children( - parent: impl Into>, - children: Vec>> - ) -> Self{ - Self{ - parent: parent.into(), - children: children.into_iter().map(Into::into).collect(), - spacing: 4.0, - padding: [0.0;4].into(), - max_width: f32::MAX, - width: Length::Shrink, - height: Length::Shrink, - axis: Axis::Vertical, - offset: 0.0, - open_condition: OpenCondition::Hover, - } - } - - pub fn tree(&self) -> Tree{ - Tree{ - tag: self.tag(), - state: self.state(), - children: self.children() - } - } - - /// Sets the vertical spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, spacing: f32) -> Self { - self.spacing = spacing; - self - } - - /// Sets the [`Padding`] of the [`MenuTree`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`MenuTree`]. - pub fn width(mut self, width: impl Into) -> Self { - self.width = width.into(); - self - } - - /// Sets the height of the [`MenuTree`]. - pub fn height(mut self, height: impl Into) -> Self { - self.height = height.into(); - self - } - - /// Sets the axis of the [`MenuTree`]. - pub fn axis(mut self, axis: Axis) -> Self { - self.axis = axis.into(); - self - } - - /// Sets the open condition of the [`MenuTree`]. - pub fn open_condition(mut self, open_condition: OpenCondition) -> Self { - self.open_condition = open_condition; - self - } -} -impl <'a, Message, Theme, Renderer> - // Widget for - MenuTree<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub(super) fn size(&self) -> Size { - Size::new(self.width, self.height) - } - - pub(super) fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - pub(super) fn state(&self) -> tree::State { - tree::State::new(MenuTreeState::default()) - } - - pub(super) fn children(&self) -> Vec { - [ - Tree::new(&self.parent), - Tree{ - children: self.children.iter().map(|mt| mt.tree()).collect::>(), - ..Tree::empty() - } - ].into() - } - - /* - Tree{ - tag: MTS - state: MTS - children: [ - Tree{parent}, - Tree{tag:none, state:none, children:[ - Tree{tag:MTS, state:MTS, children:[..]}, - Tree{tag:MTS, state:MTS, children:[..]}, - Tree{tag:MTS, state:MTS, children:[..]}, - ... - ]} - ] - } - */ - pub(super) fn diff(&self, tree: &mut Tree) { - let parent = &mut tree.children[0]; - let children = &mut tree.children[1]; - - parent.diff(&self.parent); - children.diff_children_custom( - &self.children, - |tree, mt| diff(tree, mt), - |mt| mt.tree() - ) - } - - pub(super) fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - // println!("mt layout"); - self.parent.as_widget().layout(&mut tree.children[0], renderer, limits) - } - - pub(super) fn on_event( - &mut self, - tree: &mut Tree, - event: event::Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - // println!("mt event"); - let status = self.parent.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - viewport, - ); - - let state = tree.state.downcast_mut::(); - let bounds = layout.bounds(); - - use event::Status::*; - match self.open_condition{ - OpenCondition::Click => match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if cursor.is_over(bounds) { - state.open = true; - state.scroll_offset = 0.0; - Captured - }else{ - Ignored - } - } - _ => Ignored - } - OpenCondition::Hover => match event { - Event::Mouse(mouse::Event::CursorMoved { position }) => { - if bounds.contains(position) { - state.open = true; - state.scroll_offset = 0.0; - Captured - }else{ - Ignored - } - } - _ => Ignored - } - } - .merge(status) - } - - pub(super) fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - // println!("mt draw"); - self.parent.as_widget().draw(&tree.children[0], renderer, theme, style, layout, cursor, viewport) - } - - // pub(super) fn overlay<'b>( - // &'b mut self, - // extra_input:f32, - // tree: &'b mut Tree, - // layout: layout::Layout<'_>, - // renderer: &Renderer, - // ) -> Option> { - // // ) -> Option> { - // // ) -> Option> { - // // println!("mt overlay"); - // let mut og = overlay::Group::new(); - // let Tree { tag, state, children } = tree; - - // // let [parent_tree, children_tree] = children.as_mut_slice() else { panic!("Tree Error") }; - // let parent_tree = &mut children[0]; - // let children_tree = &mut children[1]; - - // if let Some(c) = self.parent - // .as_widget_mut() - // .overlay(parent_tree, layout, renderer) - // { - // og = og.push(c); - // } - - // let ms = state.downcast_mut::(); - // if !ms.open { - // Some(og.overlay().into()) - // }else{ - // // Some(og.push( - // // MenuTreeOverlay{ - // // state: ms, - // // tree: children_tree, - // // children: &mut self.children, - // // parent_bounds: layout.bounds(), - // // max_width: 1000.0, - // // spacing: self.spacing, - // // padding: self.padding, - // // width: self.width, - // // height: self.height, - // // axis: self.axis, - // // offset: self.offset, - // // }.overlay() - // // ).overlay()) - // None - // } - // } -} - -pub fn diff<'a, Message, Theme, Renderer>( - tree: &mut Tree, - new: &MenuTree<'a, Message, Theme, Renderer> -) where - Renderer: renderer::Renderer, -{ - if tree.tag == new.tag() { - new.diff(tree); - } else { - *tree = new.tree() - } -} \ No newline at end of file diff --git a/src/native/menu/bk2/menu_tree_overlay.rs b/src/native/menu/bk2/menu_tree_overlay.rs deleted file mode 100644 index 88f21277..00000000 --- a/src/native/menu/bk2/menu_tree_overlay.rs +++ /dev/null @@ -1,532 +0,0 @@ -use iced_widget::core::{ - event, layout::{self, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, - Alignment, Border, Clipboard, Color, Element, Event, Length, - Padding, Point, Vector, Rectangle, Shell, Size, Widget, -}; -use super::types::*; -use super::menu_tree::{MenuTree, MenuTreeState}; - -pub(super) struct MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer -{ - pub(super) state: &'b mut MenuTreeState, - pub(super) tree: &'b mut Tree, - pub(super) menu_tree: &'b MenuTree<'a, Message, Theme, Renderer>, - pub(super) parent_bounds: Rectangle, -} -impl<'a, 'b, Message, Theme, Renderer> MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer -{ - // pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer>{ - // overlay::Element::new(Point::ORIGIN, Box::new(self)) - // } -} -impl<'a, 'b, Message, Theme, Renderer> - // overlay::Overlay for - MenuTreeOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub(super) fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: Point, - translation: Vector, - ) -> layout::Node { - println!(); - println!("mxo layout"); - println!("bounds: {:?}", bounds); - println!("position: {:?}", position); - println!("translation: {:?}", translation); - println!(); - - let limits = layout::Limits::NONE.max_width(self.menu_tree.max_width); - let children = &self.menu_tree.children.iter().map(|mt| mt.parent).collect::>(); - let layout = layout::flex::resolve( - layout::flex::Axis::Vertical, - renderer, - &limits, - self.menu_tree.width, - self.menu_tree.height, - self.menu_tree.padding, - self.menu_tree.spacing, - Alignment::Center, - children, - &mut self.tree.children - ); - - let vpb = self.parent_bounds + translation; // viewport space parent bounds - - let hcenter = bounds.width / 2.0; - let vcenter = bounds.height / 2.0; - - let phcenter = vpb.x + vpb.width / 2.0; - let pvcenter = vpb.y + vpb.height / 2.0; - - let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; - let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; - - let aod = match self.menu_tree.axis { - Axis::Horizontal => Aod{ - horizontal: false, - vertical: true, - horizontal_overlap: false, - vertical_overlap: true, - horizontal_direction, - vertical_direction, - horizontal_offset: self.menu_tree.offset, - vertical_offset: 0.0, - }, - Axis::Vertical => Aod{ - horizontal: true, - vertical: false, - horizontal_overlap: true, - vertical_overlap: false, - horizontal_direction, - vertical_direction, - horizontal_offset: 0.0, - vertical_offset: self.menu_tree.offset, - } - }; - - let children_size = layout.bounds().size(); - let (children_position, offset_position) = aod.resolve( - vpb, - children_size, - bounds - ); - - // calc offset bounds - let delta = children_position - offset_position; - let offset_size = if delta.x.abs() > delta.y.abs() { - Size::new(delta.x, children_size.height) - } else { - Size::new(children_size.width, delta.y) - }; - let offset_bounds = Rectangle::new(offset_position, offset_size); - let children_bounds = Rectangle::new(children_position, children_size); - let bounds_expand = 30.0; - let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); - - layout::Node::with_children(Size::INFINITY, [ - layout.move_to(children_position) - .translate([0.0, self.state.scroll_offset]), // children layout - layout::Node::new(children_size) - .move_to(children_position), // prescroll children bounds - layout::Node::new(bounds), // viewport - layout::Node::new(offset_bounds.size()) - .move_to(offset_bounds.position()) - .translate([0.0, self.state.scroll_offset]), // offset bounds - layout::Node::new(check_bounds.size()) - .move_to(check_bounds.position()) - .translate([0.0, self.state.scroll_offset]), // check bounds - ].into()) - } - - pub(super) fn on_event( - &mut self, - event: event::Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - println!("mxo event"); - use event::Status::*; - - let mut lc = layout.children(); - let children_layout = lc.next().unwrap(); - let prescroll_children_bounds = lc.next().unwrap().bounds(); - let viewport = lc.next().unwrap().bounds(); - let offset_bounds = lc.next().unwrap().bounds(); - let check_bounds = lc.next().unwrap().bounds(); - - let children_bounds = children_layout.bounds(); - - let status = self.menu_tree.children - .iter_mut() - .zip(&mut self.tree.children) - .zip(children_layout.children()) - .map(|((child, tree), layout)| - child.parent.as_widget_mut().on_event( - tree, - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - &viewport, - ) - ) - .fold(event::Status::Ignored, event::Status::merge); - - match event { - Event::Mouse(mouse::Event::WheelScrolled { delta }) => { - if cursor.is_over(children_bounds){ - // self.state.scroll_offset += match delta{ - // mouse::ScrollDelta::Lines { x, y } => y, - // mouse::ScrollDelta::Pixels { x, y } => y, - // }; - // Ignored - process_scroll_event(&mut self.state, prescroll_children_bounds, delta, viewport.size()) - }else{ - Ignored - } - } - Event::Mouse(mouse::Event::CursorMoved { position }) => { - self.state.open = - self.parent_bounds.contains(position) - || offset_bounds.contains(position) - || check_bounds.contains(position); - Captured - } - _ => Ignored - }.merge(status) - } - - pub(super) fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - ) { - println!("mxo draw"); - let mut lc = layout.children(); - let children_layout = lc.next().unwrap(); - let prescroll_children_bounds = lc.next().unwrap().bounds(); - let viewport = lc.next().unwrap().bounds(); - // let offset_bounds = lc.next().unwrap().bounds(); - // let check_bounds = lc.next().unwrap().bounds(); - - renderer.fill_quad( - renderer::Quad{ - bounds: self.parent_bounds, - ..Default::default() - }, - Color::from([1.0, 0.0, 0.0, 0.5]) - ); - - renderer.fill_quad( - renderer::Quad{ - bounds: pad_rectangle(prescroll_children_bounds, 4.0.into()), - border: Border{ - color: [0.5; 3].into(), - width: 1.5, - radius: 6.0.into(), - ..Default::default() - }, - ..Default::default() - }, - Color::from([1.0; 3]) - ); - - if let Some(viewport) = children_layout.bounds().intersection(&viewport) { - for ((child, tree), layout) in self - .menu_tree.children - .iter() - .zip(&self.tree.children) - .zip(children_layout.children()) - { - child.parent.as_widget().draw( - tree, renderer, theme, style, layout, cursor, &viewport, - ); - } - } - } - - // fn overlay<'c>( - // &'c mut self, - // extra_input:f32, - // layout: layout::Layout<'_>, - // renderer: &Renderer, - // ) -> Option> { - // // ) -> Option> { - // // ) -> Option> { - // println!("mxo overlay"); - // // overlay::from_children( - // // self.children, - // // self.tree, - // // layout.children().next().unwrap(), - // // renderer - // // ) - // let layout = layout.children().next().unwrap(); - // let children = self.children - // .iter_mut() - // .zip(&mut self.tree.children) - // .zip(layout.children()) - // .filter_map(|((child, state), layout)| { - // child.overlay(0.0, state, layout, renderer) - // }) - // .collect::>(); - - // (!children.is_empty()).then(|| overlay::Group::with_children(children).overlay()) - // // None - // } - - pub(super) fn is_over( - &self, - layout: layout::Layout<'_>, - _renderer: &Renderer, - cursor_position: Point, - ) -> bool { - let mut lc = layout.children(); - let children_layout = lc.next().unwrap(); - children_layout.bounds().contains(cursor_position) - } -} - - -/// Adaptive open direction -#[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] -struct Aod { - // whether or not to use aod - horizontal: bool, - vertical: bool, - - // whether or not to use overlap - horizontal_overlap: bool, - vertical_overlap: bool, - - // default direction - horizontal_direction: Direction, - vertical_direction: Direction, - - // Offset of the child in the default direction - horizontal_offset: f32, - vertical_offset: f32, -} -impl Aod { - /// Returns child position and offset position - #[allow(clippy::too_many_arguments)] - fn adaptive( - parent_pos: f32, - parent_size: f32, - child_size: f32, - max_size: f32, - offset: f32, - on: bool, - overlap: bool, - direction: Direction, - ) -> (f32, f32) { - /* - Imagine there're two sticks, parent and child - parent: o-----o - child: o----------o - - Now we align the child to the parent in one dimension - There are 4 possibilities: - - 1. to the right - o-----oo----------o - - 2. to the right with overlaping - o-----o - o----------o - - 3. to the left - o----------oo-----o - - 4. to the left with overlaping - o-----o - o----------o - - The child goes to the default direction by default, - if the space on the default direction runs out it goes to the the other, - whether to use overlap is the caller's decision - - This can be applied to any direction - */ - - match direction { - Direction::Positive => { - let space_negative = parent_pos; - let space_positive = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - overshoot) - } else { - (parent_pos, parent_pos) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - offset) - } else { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } - } - } - Direction::Negative => { - let space_positive = parent_pos; - let space_negative = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos, parent_pos) - } else { - (parent_pos - overshoot, parent_pos - overshoot) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } else { - (parent_pos - overshoot, parent_pos - offset) - } - } - } - } - } - - /// Returns child position and offset position - fn resolve( - &self, - parent_bounds: Rectangle, - children_size: Size, - viewport_size: Size, - ) -> (Point, Point) { - let (x, ox) = Self::adaptive( - parent_bounds.x, - parent_bounds.width, - children_size.width, - viewport_size.width, - self.horizontal_offset, - self.horizontal, - self.horizontal_overlap, - self.horizontal_direction, - ); - let (y, oy) = Self::adaptive( - parent_bounds.y, - parent_bounds.height, - children_size.height, - viewport_size.height, - self.vertical_offset, - self.vertical, - self.vertical_overlap, - self.vertical_direction, - ); - - ([x, y].into(), [ox, oy].into()) - } -} - -fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { - Rectangle { - x: rect.x - padding.left, - y: rect.y - padding.top, - width: rect.width + padding.horizontal(), - height: rect.height + padding.vertical(), - } -} - - -/// A part of a menu where items are displayed. -/// -/// When the bounds of a menu exceed the viewport, -/// only items inside the viewport will be displayed, -/// when scrolling happens, this should be updated -#[derive(Debug, Clone, Copy)] -struct MenuSlice { - start_index: usize, - end_index: usize, - lower_bound_rel: f32, - upper_bound_rel: f32, -} - -fn slice( - children_bounds: Rectangle, // offset + unsrolled - child_positions: Vec, - child_sizes: Vec, - scroll_offset: f32, - viewport_size: Size, - // overlay_offset: Vector, -) -> MenuSlice { - // viewport space children bounds - // let children_bounds = children_bounds + overlay_offset; - - let max_index = child_positions.len().saturating_sub(1); - - // viewport space absolute bounds - let lower_bound = children_bounds.y.max(0.0); - let upper_bound = (children_bounds.y + children_bounds.height).min(viewport_size.height); - - // menu space relative bounds - let lower_bound_rel = lower_bound - (children_bounds.y + scroll_offset); - let upper_bound_rel = upper_bound - (children_bounds.y + scroll_offset); - - // index range - let positions = &child_positions; - let sizes = &child_sizes; - - let start_index = search_bound(0, 0, max_index, lower_bound_rel, positions, sizes); - let end_index = - search_bound(max_index, start_index, max_index, upper_bound_rel, positions, sizes) - .min(max_index); - - MenuSlice { - start_index, - end_index, - lower_bound_rel, - upper_bound_rel, - } -} - -fn search_bound( - default: usize, - default_left: usize, - default_right: usize, - bound: f32, - positions: &[f32], - sizes: &[Size], -) -> usize { - // binary search - let mut left = default_left; - let mut right = default_right; - - let mut index = default; - while left != right { - let m = ((left + right) / 2) + 1; - if positions[m] > bound { - right = m - 1; - } else { - left = m; - } - } - let height = sizes[left].height; - if positions[left] + height > bound { - index = left; - } - index -} - -fn process_scroll_event( - state: &mut MenuTreeState, - prescroll_children_bounds: Rectangle, - delta: mouse::ScrollDelta, - viewport_size: Size, -) -> event::Status{ - use mouse::ScrollDelta; - - let pcb = prescroll_children_bounds; - - let delta_y = match delta { - ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, - }; - - let max_offset = (0.0 - pcb.y).max(0.0); - let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); - state.scroll_offset = (state.scroll_offset + delta_y).clamp(min_offset, max_offset); - - event::Status::Captured -} diff --git a/src/native/menu/bk2/types.rs b/src/native/menu/bk2/types.rs deleted file mode 100644 index d965f7a1..00000000 --- a/src/native/menu/bk2/types.rs +++ /dev/null @@ -1,109 +0,0 @@ -use iced_widget::core::{overlay, renderer}; - -use super::menu_tree_overlay::MenuTreeOverlay; - - -/// The condition of when to close a menu -#[derive(Debug, Clone, Copy)] -pub struct CloseCondition { - /// Close menus when the cursor moves outside the check bounds - pub leave: bool, - - /// Close menus when the cursor clicks outside the check bounds - pub click_outside: bool, - - /// Close menus when the cursor clicks inside the check bounds - pub click_inside: bool, -} - -// /// The width of an item -// #[derive(Debug, Clone, Copy)] -// pub enum ItemWidth { -// /// Use uniform width -// Uniform(u16), -// /// Static tries to use the width value of each menu(menu tree with children), -// /// the widths of items(menu tree with empty children) will be the same as the menu they're in, -// /// if that value is None, -// /// the default value will be used instead, -// /// which is the value of the Static variant -// Static(u16), -// } - -// /// The height of an item -// #[derive(Debug, Clone, Copy)] -// pub enum ItemHeight { -// /// Use uniform height. -// Uniform(u16), -// /// Static tries to use `MenuTree.height` as item height, -// /// when it's `None` it'll fallback to the value of the `Static` variant. -// Static(u16), -// /// Dynamic tries to automatically choose the proper item height for you, -// /// but it only works in certain cases: -// /// -// /// - Fixed height -// /// - Shrink height -// /// - Menu tree height -// /// -// /// If none of these is the case, it'll fallback to the value of the `Dynamic` variant. -// Dynamic(u16), -// } - -// /// Methods for drawing path highlight -// #[derive(Debug, Clone, Copy)] -// pub enum PathHighlight { -// /// Draw the full path, -// Full, -// /// Omit the active item(the last item in the path) -// OmitActive, -// /// Omit the active item if it's not a menu -// MenuActive, -// } - -/// X+ goes right and Y+ goes down -#[derive(Debug, Clone, Copy)] -pub(super) enum Direction { - Positive, - Negative, -} - -/// Axis -#[allow(missing_docs)] -#[derive(Debug, Clone, Copy)] -pub enum Axis{ - Horizontal, - Vertical, -} - -#[allow(missing_docs)] -pub enum OpenCondition{ - Hover, - Click, -} - -pub(super) enum MenuOverlayElement<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - Overlay(overlay::Element<'b, Message, Theme, Renderer>), - MenuTree(MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) -} -impl<'a, 'b, Message, Theme, Renderer> - From> for - MenuOverlayElement<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn from(value: overlay::Element<'b, Message, Theme, Renderer>) -> Self { - Self::Overlay(value) - } -} -impl<'a, 'b, Message, Theme, Renderer> - From> for - MenuOverlayElement<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn from(value: MenuTreeOverlay<'a, 'b, Message, Theme, Renderer>) -> Self { - Self::MenuTree(value) - } -} diff --git a/src/native/menu/bk3/menu_bar.rs b/src/native/menu/bk3/menu_bar.rs deleted file mode 100644 index 79557b78..00000000 --- a/src/native/menu/bk3/menu_bar.rs +++ /dev/null @@ -1,239 +0,0 @@ -//! menu bar - -use iced_widget::core::{ - alignment, event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::{tree, Tree}, - Event, - Alignment, Clipboard, Color, Element, Layout, Length, Overlay, Padding, Rectangle, Shell, Size, Widget -}; - -use super::{ - flex, menu_bar_overlay::MenuBarOverlay, menu_tree::* -}; - -pub(super) struct MenuBarState{ - pub(super) active_root: usize, - pub(super) open: bool, - pub(super) viewport: Rectangle, - pub(super) indices: Vec, -} -impl Default for MenuBarState{ - fn default() -> Self { - Self { - active_root: 0, - open: false, - viewport: Rectangle::default(), - indices: Vec::new(), - } - } -} - -/// menu bar -pub struct MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - roots: Vec>, - spacing: f32, - padding: Padding, - width: Length, - height: Length, -} -#[allow(missing_docs)] -impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub fn new(roots: Vec>) -> Self { - Self { - roots, - spacing: 0.0, - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - } - } - -} -impl<'a, Message, Theme, Renderer> Widget for MenuBar<'a, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - - fn size(&self) -> Size { - Size::new(self.width, self.height) - } - - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::Some(Box::new(MenuBarState::default())) - } - - /// \[Tree{item_state, \[widget_state, menu_state]}...] - fn children(&self) -> Vec { - println!("bar children"); - self.roots.iter().map(|item| item.tree()).collect::>() - } - - /// tree: Tree{bar_state, \[item_tree...]} - fn diff(&self, tree: &mut Tree) { - println!("bar diff"); - tree.diff_children_custom( - &self.roots, - |tree, item| item.diff(tree), - |item| item.tree() - ) - } - - /// tree: Tree{bar_state, \[item_tree...]} - fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - println!("bar layout"); - flex::resolve( - flex::Axis::Horizontal, - renderer, - limits, - self.width, - self.height, - self.padding, - self.spacing, - alignment::Alignment::Center, - &self.roots.iter().map(|item| &item.item ).collect::>(), - &mut tree.children.iter_mut().map(|tree| &mut tree.children[0]).collect::>() - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: event::Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - println!("bar event"); - use event::Status::*; - - let status = self.roots // [Item...] - .iter_mut() - .zip(tree.children.iter_mut()) // [item_tree...] - .zip(layout.children()) // [widget_layout...] - .map(|((item, tree), layout)|{ - item.item.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - viewport - ) - }) - .fold(event::Status::Ignored, |acc, x| acc.merge(x) ); - - // make sure there's only one item that is open - let mut use_open = false; - for (i, t) in tree.children.iter_mut().enumerate(){ - let item_state = t.state.downcast_mut::(); - if use_open == true{ - item_state.open = false; - }else if item_state.open{ - use_open = true; - let bar = tree.state.downcast_mut::(); - // store the active item index - bar.active_root = i; - } - } - - status - - // let bar_bounds = layout.bounds(); - - // match event { - // Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - // | Event::Touch(touch::Event::FingerPressed { .. }) => { - - // Ignored - // } - // Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) - // | Event::Touch(touch::Event::FingerLifted { .. }) - // | Event::Touch(touch::Event::FingerLost { .. }) => { - - // Ignored - // } - // Event::Mouse(mouse::Event::CursorMoved { position:_ }) => { - // Ignored - // } - // _ => Ignored - // }.merge(status) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - println!("bar draw"); - if let Some(viewport) = layout.bounds().intersection(viewport) { - for ((root, tree), layout) in self - .roots - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - root.item.as_widget().draw( - &tree.children[0], renderer, theme, style, layout, cursor, &viewport, - ); - } - } - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - println!("bar overlay"); - let state = tree.state.downcast_mut::(); - let bar_bounds = layout.bounds(); - let parent_bounds = layout.children().nth(state.active_root).unwrap().bounds(); - - if state.open{ - Some(MenuBarOverlay{ - tree, - roots: &mut self.roots, - parent_bounds, - bar_bounds, - }.overlay_element()) - }else { - None - } - } -} -impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> -where - Message: 'a, - Theme: 'a, - Renderer: 'a + renderer::Renderer, - // Renderer::Theme: StyleSheet, -{ - fn from(value: MenuBar<'a, Message, Theme, Renderer>) -> Self { - Self::new(value) - } -} diff --git a/src/native/menu/bk3/menu_bar_overlay.rs b/src/native/menu/bk3/menu_bar_overlay.rs deleted file mode 100644 index 61563964..00000000 --- a/src/native/menu/bk3/menu_bar_overlay.rs +++ /dev/null @@ -1,228 +0,0 @@ -use iced_widget::core::{ - event, layout::{self, Node, Limits}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget -}; - -use super::{ - menu_tree::*, - menu_bar::MenuBarState, -}; - -pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - /// Tree{ bar_state, [item_tree...] } - pub(super) tree: &'b mut Tree, - /// Tree{ item_state, [widget_tree, menu_tree] } - // pub(super) bar: &'b mut MenuBarState, - // pub(super) active_tree: &'b mut Tree, - // pub(super) active_root: &'b mut Item<'a, Message, Theme, Renderer>, - pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], - pub(super) parent_bounds: Rectangle, - pub(super) bar_bounds: Rectangle, -} -impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer>{ - overlay::Element::new(Point::ORIGIN, Box::new(self)) - } -} -impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay for MenuBarOverlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - /// out: Node{size:0, \[widget_node, menu_node]} - fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - position: Point, - translation: Vector, - ) -> Node { - println!("mbo layout"); - let viewport = Rectangle::new(Point::ORIGIN, bounds); - let limits = Limits::NONE; - - let bar = self.tree.state.downcast_mut::(); - let active_root = &self.roots[bar.active_root]; - let active_tree = &mut self.tree.children[bar.active_root]; - - active_root.layout(active_tree, renderer, &limits, &viewport, - // translation - ) - } - - /// layout: Node{size:0, \[widget_node, menu_node]} - fn on_event( - &mut self, - event: Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - println!("mbo event"); - fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( - bar: &mut MenuBarState, - current_item: &mut Item<'a, Message, Theme, Renderer>, - current_tree: &mut Tree, - current_layout: Layout<'_>, - event: Event, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - cursor: mouse::Cursor, - parent_bounds: Rectangle, - items_bounds_list: &mut Vec, - ) -> event::Status{ - let menu = current_item.menu.as_mut().expect("No menu defined in this item"); - let mut lc = current_layout.children(); - let _ = lc.next().unwrap(); // widget_node - let menu_layout = lc.next().unwrap(); // menu_node: Node{size:inf, [items_layout, prescroll, offset_bounds, check_bounds]} - let items_layout = menu_layout.children().next().unwrap(); // items_layout: Node{size:items_bounds, [item_layout...]} - - let menu_tree = &mut current_tree.children[1]; // menu_tree: Tree{ menu_state, [item_tree...] } - - let item_state = current_tree.state.downcast_mut::(); - let viewport = bar.viewport; - - // on_event - let status = menu.on_event( - item_state, menu_tree, event.clone(), menu_layout, cursor, renderer, clipboard, shell, &viewport, &parent_bounds, &items_bounds_list - ); - - // push items_bounds - items_bounds_list.push(items_layout.bounds()); - - if let Some((next_item, next_tree, next_layout)) = menu.items // [item...] - .iter_mut() - .zip(menu_tree.children.iter_mut()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] - .find_map(|((item, tree), layout)|{ - let item_state = tree.state.downcast_ref::(); - item_state.open.then(|| (item, tree, layout)) - }) - { - rec(bar, next_item, next_tree, next_layout, event, renderer, clipboard, shell, cursor, parent_bounds, items_bounds_list) - .merge(status) - }else{ - status - } - } - - let bar = self.tree.state.downcast_mut::(); - let active_root = &mut self.roots[bar.active_root]; - let active_tree = &mut self.tree.children[bar.active_root]; - let mut items_bounds_list = vec![self.bar_bounds]; - - rec( - bar, - active_root, - active_tree, - layout, - event, - renderer, - clipboard, - shell, - cursor, - self.parent_bounds, - &mut items_bounds_list, - ) - } - - /// layout: Node{size:0, \[widget_node, menu_node]} - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - /* let bar = self.tree.state.downcast_ref::(); - let mut current_item = &self.active_root; - let mut current_tree = &self.active_tree; // item_tree: Tree{item_state, [widget_tree, menu_tree]} - let mut current_layout = layout; // item_layout: Node{size:0, [widget_node, menu_node]} - - loop { - let cm = current_item.menu.as_mut(); - let menu = cm.expect("No menu defined in this item"); - let mut lc = current_layout.children(); - let widget_layout = lc.next().unwrap(); - let menu_layout = lc.next().unwrap(); // menu_node: Node{size:inf, [items_layout, prescroll, offset_bounds, check_bounds]} - let items_layout = menu_layout.children().next().unwrap(); // items_layout: Node{size:items_bounds, [item_layout...]} - - // let widget_tree = &mut current_tree.children[0]; - let menu_tree = &mut current_tree.children[1]; // menu_tree: Tree{ menu_state, [item_tree...] } - - menu.draw(&menu_tree, renderer, theme, style, menu_layout, cursor, &bar.viewport); - - if let Some((next_item, next_tree, next_layout)) = menu.items // [item...] - .iter_mut() - .zip(menu_tree.children.iter_mut()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] - .find_map(|((item, tree), layout)|{ - let item_state = tree.state.downcast_ref::(); - item_state.open.then(|| (item, tree, layout)) - }) - { - current_item = next_item; - current_tree = next_tree; - current_layout = next_layout; - }else{ - break; - } - } */ - - fn rec<'a, Message, Theme, Renderer:renderer::Renderer>( - bar: &MenuBarState, - current_item: &Item<'a, Message, Theme, Renderer>, - current_tree: &Tree, - current_layout: &Layout<'_>, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - cursor: mouse::Cursor, - ){ - let menu = current_item.menu.as_ref().expect("No menu defined in this item"); - let mut lc = current_layout.children(); - let _ = lc.next().unwrap(); // widget_node - let menu_layout = lc.next().unwrap(); // menu_node: Node{size:inf, [items_layout, prescroll, offset_bounds, check_bounds]} - let items_layout = menu_layout.children().next().unwrap(); // items_layout: Node{size:items_bounds, [item_layout...]} - - let menu_tree = ¤t_tree.children[1]; // menu_tree: Tree{ menu_state, [item_tree...] } - let viewport = bar.viewport; - - menu.draw(&menu_tree, renderer, theme, style, menu_layout, cursor, &viewport); - - if let Some((next_item, next_tree, next_layout)) = menu.items // [item...] - .iter() - .zip(menu_tree.children.iter()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] - .find_map(|((item, tree), layout)|{ - let item_state = tree.state.downcast_ref::(); - item_state.open.then(|| (item, tree, layout)) - }) - { - rec(bar, next_item, &next_tree, &next_layout, renderer, theme, style, cursor) - } - } - - let bar = self.tree.state.downcast_ref::(); - let active_root = &self.roots[bar.active_root]; - let active_tree = &self.tree.children[bar.active_root]; - rec( - bar, - active_root, - active_tree, - &layout, - renderer, - theme, - style, - cursor - ) - } -} \ No newline at end of file diff --git a/src/native/menu/bk3/menu_tree.rs b/src/native/menu/bk3/menu_tree.rs deleted file mode 100644 index 31cdbb47..00000000 --- a/src/native/menu/bk3/menu_tree.rs +++ /dev/null @@ -1,804 +0,0 @@ -//! doc -//! -use std::borrow::BorrowMut; - -use iced_widget::core::{ - alignment, event, layout::{self, Limits, Node}, mouse, overlay, renderer, touch, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget -}; -use super::types::*; -use super::{flex, menu_bar::MenuBarState}; - -/* - -menu tree: -Item{ - widget - Menu [ - Item{...} - Item{...} - Item{...} - ... - ] -} - -state tree: -Tree{ - item state - [ - Tree{widget state} - Tree{ - menu state - [ - Tree{item state [...]} - Tree{item state [...]} - Tree{item state [...]} - ... - ] - } - ] -} - -layout tree -Node{ - item node - [ - Node{widget node} - Node{ - menu node - [ - Node{item node [...]} - Node{item node [...]} - Node{item node [...]} - ... - ] - } - ] -} - -*/ - -#[derive(Debug, Default)] -pub(super) struct MenuState{ - scroll_offset:f32, -} - -/// menu -pub struct Menu<'a, Message, Theme, Renderer> -where - Renderer:renderer::Renderer -{ - pub(super) items: Vec>, - pub(super) spacing: f32, - pub(super) padding: Padding, - pub(super) max_width: f32, - pub(super) width: Length, - pub(super) height: Length, - pub(super) axis: Axis, - pub(super) offset: f32, - pub(super) open_condition: OpenCondition, -} -#[allow(missing_docs)] -impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> -where - Renderer:renderer::Renderer -{ - pub fn new(items: Vec>) -> Self{ - Self{ - items, - spacing: 0.0, - padding: Padding::ZERO, - max_width: f32::MAX, - width: Length::Shrink, - height: Length::Shrink, - axis: Axis::Vertical, - offset: 0.0, - open_condition: OpenCondition::Click, - } - } - - pub fn tree(&self) -> Tree{ - Tree{ - tag: self.tag(), - state: self.state(), - children: self.children(), - } - } -} -impl<'a, Message, Theme, Renderer> - // Widget for - Menu<'a, Message, Theme, Renderer> -where - Renderer:renderer::Renderer -{ - pub(super) fn size(&self) -> Size { - Size::new(self.width, self.height) - } - - pub(super) fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - pub(super) fn state(&self) -> tree::State { - tree::State::Some(Box::new(MenuState::default())) - } - - /// out: \[item_tree...] - pub(super) fn children(&self) -> Vec { - self.items.iter().map(|i| i.tree() ).collect() - } - - /// tree: Tree{menu_state, \[item_tree...]} - pub(super) fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.items.iter().map(|i| &i.item ).collect::>()); - } - - /// tree: Tree{menu_state, \[item_tree...]} - /// - /// out: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} - pub(super) fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - parent_bounds: Rectangle, - // translation: Vector, - viewport: &Rectangle, - ) -> layout::Node { - let limits = limits.max_width(self.max_width); - - let items_node = flex::resolve( - flex::Axis::Vertical, - renderer, - &limits, - self.width, - self.height, - self.padding, - self.spacing, - alignment::Alignment::Center, - &self.items.iter().map(|i| &i.item ).collect::>(), - &mut tree.children.iter_mut().map(|t| &mut t.children[0] ).collect::>(), - ); - - let items_node = Node::with_children( - items_node.bounds().size(), - self.items - .iter() - .zip(tree.children.iter_mut()) - .zip(items_node.children().into_iter()) - .map(|((item, tree), node)|{ - Node::with_children( - Size::ZERO, - [ - node.clone(), - item.layout(tree, renderer, &Limits::NONE, viewport, - // translation - ) - ].into() - ) - }) - .collect() - ); - - - // for ((item, tree), node) in self.items - // .iter() - // .zip(tree.children.iter_mut()) - // .zip(items_node.children().iter_mut()) - // { - // *node = Node::with_children( - // Size::ZERO, - // [ - // node.clone(), - // item.layout(tree, renderer, &Limits::NONE, viewport, - // // translation - // ) - // .children()[1] - // ].into() - // ) - // } - - let bounds = viewport.size(); - // let vpb = parent_bounds + translation; // viewport space parent bounds - let vpb = parent_bounds; - let aod = { - let hcenter = bounds.width / 2.0; - let vcenter = bounds.height / 2.0; - - let phcenter = vpb.x + vpb.width / 2.0; - let pvcenter = vpb.y + vpb.height / 2.0; - - let horizontal_direction = if phcenter < hcenter { Direction::Positive } else { Direction::Negative }; - let vertical_direction = if pvcenter < vcenter { Direction::Positive } else { Direction::Negative }; - - match self.axis { - Axis::Horizontal => Aod{ - horizontal: false, - vertical: true, - horizontal_overlap: false, - vertical_overlap: true, - horizontal_direction, - vertical_direction, - horizontal_offset: self.offset, - vertical_offset: 0.0, - }, - Axis::Vertical => Aod{ - horizontal: true, - vertical: false, - horizontal_overlap: true, - vertical_overlap: false, - horizontal_direction, - vertical_direction, - horizontal_offset: 0.0, - vertical_offset: self.offset, - } - } - }; - - let children_size = items_node.bounds().size(); - let (children_position, offset_position) = aod.resolve( - vpb, - children_size, - bounds - ); - - // calc offset bounds - let delta = children_position - offset_position; - let offset_size = if delta.x.abs() > delta.y.abs() { - Size::new(delta.x, children_size.height) - } else { - Size::new(children_size.width, delta.y) - }; - let offset_bounds = Rectangle::new(offset_position, offset_size); - let children_bounds = Rectangle::new(children_position, children_size); - let bounds_expand = 30.0; - let check_bounds = pad_rectangle(children_bounds, [bounds_expand; 4].into()); - - let menu_state = tree.state.downcast_ref::(); - - layout::Node::with_children(Size::INFINITY, [ - items_node - .move_to(children_position) - .translate([0.0, menu_state.scroll_offset]), // items layout - layout::Node::new(children_size) - .move_to(children_position), // prescroll menu bounds - - layout::Node::new(offset_bounds.size()) - .move_to(offset_bounds.position()) - .translate([0.0, menu_state.scroll_offset]), // offset bounds - layout::Node::new(check_bounds.size()) - .move_to(check_bounds.position()) - .translate([0.0, menu_state.scroll_offset]), // check bounds - ].into()) - } - - /// tree: Tree{menu_state, \[item_tree...]} - /// - /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} - pub(super) fn on_event( - &mut self, - // bar: &mut MenuBarState, - item_state: &mut ItemState, - tree: &mut Tree, - event: Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - parent_bounds: &Rectangle, - items_bounds_list: &[Rectangle], - ) -> event::Status { - let mut lc = layout.children(); - let items_layout = lc.next().unwrap(); - - let prescroll = lc.next().unwrap().bounds(); - let offset_bounds = lc.next().unwrap().bounds(); - let check_bounds = lc.next().unwrap().bounds(); - - let status = self.items // [item...] - .iter_mut() - .zip(tree.children.iter_mut()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] - .map(|((item, tree), layout)| { - item.on_event( - tree, - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - viewport, - ) - }) - .fold(event::Status::Ignored, event::Status::merge); - - let menu_state = tree.state.downcast_mut::(); - - use event::Status::*; - match event { - Event::Mouse(mouse::Event::WheelScrolled { delta }) => { - if cursor.is_over(items_layout.bounds()){ - process_scroll_event(menu_state, prescroll, delta, viewport.size()) - }else{ - Ignored - } - } - Event::Mouse(mouse::Event::CursorMoved { position }) => { - let open = { - if prescroll.contains(position){ - true - }else if items_bounds_list - .iter() - .any(|r| r.contains(position)) - { - false - }else if parent_bounds.contains(position) - || offset_bounds.contains(position) - || check_bounds.contains(position) - { - true - }else{ - false - } - }; - - if item_state.open == true && open == false { - item_state.open = false; - // menu_state.scroll_offset = 0.0; - } - Captured - } - _ => Ignored - }.merge(status) - } - - /// tree: Tree{menu_state, \[item_tree...]} - /// - /// layout: Node{size:inf, \[items_layout, prescroll, offset_bounds, check_bounds]} - pub(super) fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - let mut lc = layout.children(); - let items_layout = lc.next().unwrap(); - - for ((item, tree), layout) in self.items // [item...] - .iter() - .zip(tree.children.iter()) // [item_tree...] - .zip(items_layout.children()) // [item_layout...] - { - item.draw( - tree, renderer, theme, style, layout, cursor, &viewport, - ); - } - } -} - - -#[derive(Debug, Default)] -pub(super) struct ItemState{ - pub(super) open: bool -} - -/// menu item -pub struct Item<'a, Message, Theme, Renderer> -where - Renderer:renderer::Renderer -{ - pub(super) item: Element<'a, Message, Theme, Renderer>, - pub(super) menu: Option>>, -} -#[allow(missing_docs)] -impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> -where - Renderer:renderer::Renderer -{ - pub fn new(item: impl Into>) -> Self{ - Self{ - item: item.into(), - menu: None, - } - } - - pub fn with_menu(item: impl Into>, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ - Self{ - item: item.into(), - menu: Some(Box::new(menu)), - } - } - - pub fn menu(mut self, menu: Menu<'a, Message, Theme, Renderer>) -> Self{ - self.menu = Some(Box::new(menu)); - self - } - - pub(super) fn tree(&self) -> Tree{ - Tree{ - tag: self.tag(), - state: self.state(), - children: self.children(), - } - } -} -impl<'a, Message, Theme, Renderer> - // Widget for - Item<'a, Message, Theme, Renderer> -where - Renderer:renderer::Renderer -{ - pub(super) fn size(&self) -> Size { - self.item.as_widget().size() - } - - pub(super) fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - pub(super) fn state(&self) -> tree::State { - tree::State::Some(Box::new(ItemState::default())) - } - - /// out: \[widget_tree, menu_tree] - pub(super) fn children(&self) -> Vec { - self.menu.as_ref().map_or( - [ - Tree::new(&self.item), - ].into(), - |m|[ - Tree::new(&self.item), - m.tree() - ].into(), - ) - } - - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - pub(super) fn diff(&self, tree: &mut Tree) { - tree.children[0].diff(&self.item); - self.menu.as_ref().map_or( - {}, - |m| { - m.diff(&mut tree.children[1]) - } - ) - } - - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - /// - /// out: Node{size:0, \[widget_node, menu_node]} - pub(super) fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - viewport: &Rectangle, - // translation: Vector, - ) -> layout::Node { - let state = tree.state.downcast_ref::(); - let widget_node = self.item.as_widget().layout(&mut tree.children[0], renderer, limits); - let parent_bounds = widget_node.bounds(); - Node::with_children( - Size::ZERO, - [ - widget_node, - if state.open{ - self.menu.as_ref().unwrap().layout( - &mut tree.children[1], - renderer, - &Limits::NONE, - parent_bounds, - // translation, - viewport, - ) - }else{ - Node::default() - } - ].into() - ) - } - - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - /// - /// layout: Node{size:0, \[widget_node, menu_node]} - pub(super) fn on_event( - &mut self, - // index: usize, // within parent menu - // bar: &mut MenuBarState, - tree: &mut Tree, - event: Event, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - let mut lc = layout.children(); - let widget_layout = lc.next().unwrap(); - let status = self.item.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - widget_layout, - cursor, - renderer, - clipboard, - shell, - viewport - ); - - let item_state = tree.state.downcast_mut::(); - - let Some(menu) = self.menu.as_ref() else { - item_state.open = false; - return status - }; - let menu_state = tree.children[1].state.downcast_mut::(); - let widget_bounds = widget_layout.bounds(); - - use event::Status::*; - match menu.open_condition{ - OpenCondition::Click => match event { - Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerPressed { .. }) => { - if cursor.is_over(widget_bounds) { - if item_state.open == false { - item_state.open = true; - menu_state.scroll_offset = 0.0; - // bar.indices.push(index); - } - Captured - }else{ - Ignored - } - } - _ => Ignored - } - OpenCondition::Hover => match event { - Event::Mouse(mouse::Event::CursorMoved { position }) => { - if widget_bounds.contains(position) { - if item_state.open == false { - item_state.open = true; - menu_state.scroll_offset = 0.0; - // bar.indices.push(index); - } - Captured - }else{ - Ignored - } - } - _ => Ignored - } - } - .merge(status) - } - - /// tree: Tree{item_state, \[widget_tree, menu_tree]} - /// - /// layout: Node{size:0, \[widget_node, menu_node]} - pub(super) fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: layout::Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - let item_layout = layout.children().next().unwrap(); - self.item.as_widget().draw( - tree, - renderer, - theme, - style, - item_layout, - cursor, - viewport - ) - } -} - - -/// Adaptive open direction -#[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] -struct Aod { - // whether or not to use aod - horizontal: bool, - vertical: bool, - - // whether or not to use overlap - horizontal_overlap: bool, - vertical_overlap: bool, - - // default direction - horizontal_direction: Direction, - vertical_direction: Direction, - - // Offset of the child in the default direction - horizontal_offset: f32, - vertical_offset: f32, -} -impl Aod { - /// Returns child position and offset position - #[allow(clippy::too_many_arguments)] - fn adaptive( - parent_pos: f32, - parent_size: f32, - child_size: f32, - max_size: f32, - offset: f32, - on: bool, - overlap: bool, - direction: Direction, - ) -> (f32, f32) { - /* - Imagine there're two sticks, parent and child - parent: o-----o - child: o----------o - - Now we align the child to the parent in one dimension - There are 4 possibilities: - - 1. to the right - o-----oo----------o - - 2. to the right with overlaping - o-----o - o----------o - - 3. to the left - o----------oo-----o - - 4. to the left with overlaping - o-----o - o----------o - - The child goes to the default direction by default, - if the space on the default direction runs out it goes to the the other, - whether to use overlap is the caller's decision - - This can be applied to any direction - */ - - match direction { - Direction::Positive => { - let space_negative = parent_pos; - let space_positive = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - overshoot) - } else { - (parent_pos, parent_pos) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - offset) - } else { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } - } - } - Direction::Negative => { - let space_positive = parent_pos; - let space_negative = max_size - parent_pos - parent_size; - - if overlap { - let overshoot = child_size - parent_size; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos, parent_pos) - } else { - (parent_pos - overshoot, parent_pos - overshoot) - } - } else { - let overshoot = child_size + offset; - if on && space_negative > space_positive && overshoot > space_positive { - (parent_pos + parent_size + offset, parent_pos + parent_size) - } else { - (parent_pos - overshoot, parent_pos - offset) - } - } - } - } - } - - /// Returns child position and offset position - fn resolve( - &self, - parent_bounds: Rectangle, - children_size: Size, - viewport_size: Size, - ) -> (Point, Point) { - let (x, ox) = Self::adaptive( - parent_bounds.x, - parent_bounds.width, - children_size.width, - viewport_size.width, - self.horizontal_offset, - self.horizontal, - self.horizontal_overlap, - self.horizontal_direction, - ); - let (y, oy) = Self::adaptive( - parent_bounds.y, - parent_bounds.height, - children_size.height, - viewport_size.height, - self.vertical_offset, - self.vertical, - self.vertical_overlap, - self.vertical_direction, - ); - - ([x, y].into(), [ox, oy].into()) - } -} - -fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { - Rectangle { - x: rect.x - padding.left, - y: rect.y - padding.top, - width: rect.width + padding.horizontal(), - height: rect.height + padding.vertical(), - } -} - - -fn search_bound( - default: usize, - default_left: usize, - default_right: usize, - bound: f32, - positions: &[f32], - sizes: &[Size], -) -> usize { - // binary search - let mut left = default_left; - let mut right = default_right; - - let mut index = default; - while left != right { - let m = ((left + right) / 2) + 1; - if positions[m] > bound { - right = m - 1; - } else { - left = m; - } - } - let height = sizes[left].height; - if positions[left] + height > bound { - index = left; - } - index -} - -fn process_scroll_event( - menu_state: &mut MenuState, - prescroll_children_bounds: Rectangle, - delta: mouse::ScrollDelta, - viewport_size: Size, -) -> event::Status{ - use mouse::ScrollDelta; - - let pcb = prescroll_children_bounds; - - let delta_y = match delta { - ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, - }; - - let max_offset = (0.0 - pcb.y).max(0.0); - let min_offset = (viewport_size.height - (pcb.y + pcb.height)).min(0.0); - menu_state.scroll_offset = (menu_state.scroll_offset + delta_y).clamp(min_offset, max_offset); - - event::Status::Captured -} diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index fe7d90f7..8bdf888f 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -120,7 +120,6 @@ where /// \[Tree{stateless, \[widget_state, menu_state]}...] fn children(&self) -> Vec { - // println!("bar children"); self.roots .iter() .map(|item| item.tree()) @@ -129,7 +128,6 @@ where /// tree: Tree{bar_state, \[item_tree...]} fn diff(&self, tree: &mut Tree) { - // println!("bar diff"); tree.diff_children_custom( &self.roots, |tree, item| item.diff(tree), @@ -144,7 +142,6 @@ where renderer: &Renderer, limits: &Limits, ) -> Node { - // println!("bar layout"); flex::resolve( flex::Axis::Horizontal, renderer, @@ -174,7 +171,6 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - // println!("bar event"); use event::Status::*; let status = self @@ -274,7 +270,6 @@ where styling.bar_background ); - // println!("bar draw"); self.roots .iter() // [Item...] .zip(tree.children.iter()) // [item_tree...] @@ -290,14 +285,12 @@ where layout: Layout<'_>, _renderer: &Renderer, ) -> Option> { - // println!("bar overlay"); let state = tree.state.downcast_mut::(); let init_bar_bounds = layout.bounds(); let init_root_bounds = layout.children().map(|l| l.bounds() ).collect(); if state.open { - // println!("bar open"); Some( MenuBarOverlay { tree, @@ -310,7 +303,6 @@ where .overlay_element(), ) } else { - // println!("None"); None } } diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 20339699..6ed9fd29 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -54,9 +54,6 @@ where position: Point, translation: Vector, ) -> Node { - // println!(); - // println!("mbo layout"); - let bar = self.tree.state.downcast_ref::(); let bar_bounds = self.init_bar_bounds; @@ -73,7 +70,6 @@ where ).translate(translation); let Some(active) = bar.active_root else { - // println!("no active"); return Node::with_children( bounds, [ @@ -95,8 +91,6 @@ where check_bounds_width: f32, parent_bounds: Rectangle, parent_direction: (Direction, Direction), - // translation: Vector, - // parent_offset: Vector, viewport: &Rectangle, ) { let menu = item.menu.as_ref().unwrap(); @@ -170,7 +164,6 @@ where self.check_bounds_width, parent_bounds, parent_direction, - // translation, &Rectangle::new(position, bounds), ); @@ -196,9 +189,6 @@ where ) -> event::Status { use event::Status::*; - // println!(); - // println!("mbo event"); - let viewport = layout.bounds(); let mut lc = layout.children(); let bar_bounds = lc.next().unwrap().bounds(); diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 3f92c104..90be4072 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -323,7 +323,6 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - // println!("menu event"); let mut lc = layout.children(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); @@ -382,13 +381,12 @@ where layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, - // parent_bounds: Rectangle, ) { let mut lc = layout.children(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); - let offset_bounds = lc.next().unwrap().bounds(); - let check_bounds = lc.next().unwrap().bounds(); + // let offset_bounds = lc.next().unwrap().bounds(); + // let check_bounds = lc.next().unwrap().bounds(); let menu_state = tree.state.downcast_ref::(); let slice = &menu_state.slice; @@ -417,59 +415,29 @@ where let Some(start_layout) = slc.next() else { return }; - let start_bounds = start_layout.bounds(); - // if let Some(sec_layout) = slc.next(){ - // let sec_bounds = sec_layout.bounds(); - // renderer.with_layer( - // Rectangle{ - // x: start_bounds.x, - // y: start_bounds.y, - // width: f32::MAX, - // height: sec_bounds.y - start_bounds.y, - // }, - // |r| start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) - // ); - // }else{ - // start.draw(start_tree, renderer, theme, style, start_layout, cursor, viewport) - // } - - renderer.with_layer( - start_bounds, - |r| start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) - ); - - if slice.end_index == slice.start_index { - return - } - - // draw the rest - let Some(items) = self.items.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) - else { return; }; - - let Some(trees) = tree.children.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) - else { return; }; - - for ((item, tree), layout) in items.iter() // [item...].iter() - .zip(trees.iter()) // [item_tree...] - .zip(slice_layout.children().skip(1)) // [item_layout...] - { - item.draw(tree, renderer, theme, style, layout, cursor, &viewport); + start.draw(start_tree, renderer, theme, style, start_layout, cursor, viewport); + }else{ + let start_bounds = start_layout.bounds(); + renderer.with_layer( + start_bounds, + |r| start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) + ); + + // draw the rest + let Some(items) = self.items.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) + else { return; }; + + let Some(trees) = tree.children.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) + else { return; }; + + for ((item, tree), layout) in items.iter() // [item...].iter() + .zip(trees.iter()) // [item_tree...] + .zip(slice_layout.children().skip(1)) // [item_layout...] + { + item.draw(tree, renderer, theme, style, layout, cursor, &viewport); + } } - - - // let Some(items) = self.items.get(slice.start_index .. slice.end_index.saturating_add(1)) - // else { return }; - - // let Some(trees) = tree.children.get(slice.start_index .. slice.end_index.saturating_add(1)) - // else { return }; - - // for ((item, tree), layout) in items.iter() // [item...].iter() - // .zip(trees.iter()) // [item_tree...] - // .zip(slice_layout.children()) // [item_layout...] - // { - // item.draw(tree, renderer, theme, style, layout, cursor, &viewport); - // } } pub(super) fn open_event( @@ -494,7 +462,6 @@ where .enumerate() { if item.menu.is_some() && cursor.is_over(layout.bounds()) { - // println!("new active: {}", i); menu_state.active = Some(i + slice.start_index); return event::Status::Captured; } @@ -541,25 +508,21 @@ where } -fn debug_draw( +/* fn debug_draw( renderer: &mut Renderer, - prescroll: Rectangle, check_bounds: Rectangle, offset_bounds: Rectangle, - // parent_bounds: Rectangle, ){ [ prescroll, check_bounds, offset_bounds, - // parent_bounds ].iter() .zip([ Color::from([1.0, 1.0, 1.0, 0.8]), Color::from([1.0, 0.0, 0.0, 0.1]), Color::from([0.0, 0.0, 1.0, 0.3]), - // Color::from([1.0, 1.0, 0.0, 0.5]) ]) .for_each(|(b, c)|{ renderer.fill_quad( @@ -574,8 +537,7 @@ fn debug_draw( c ); }); -} - +} */ /// Item inside a [`Menu`] pub struct Item<'a, Message, Theme, Renderer> @@ -633,7 +595,6 @@ where } pub(super) fn state(&self) -> tree::State { - // tree::State::Some(Box::new(ItemState::default())) tree::State::None } @@ -654,9 +615,7 @@ where if let Some(t) = tree.children.get_mut(0) { t.diff(&self.item); if let Some(t) = tree.children.get_mut(1) { - self.menu - .as_ref() - .map_or({}, |m| m.diff(t) ) + self.menu.as_ref().map_or((), |m| m.diff(t) ) }else{ *tree = self.tree(); } @@ -667,17 +626,16 @@ where /// tree: Tree{stateless, \[widget_tree, menu_tree]} /// - pub(super) fn layout( + /* pub(super) fn layout( &self, tree: &mut Tree, renderer: &Renderer, limits: &Limits, ) -> Node { - // println!("Item layout"); self.item .as_widget() .layout(&mut tree.children[0], renderer, limits) - } + } */ /// tree: Tree{stateless, \[widget_tree, menu_tree]} /// @@ -692,7 +650,6 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { - // println!("item event"); self.item.as_widget_mut().on_event( &mut tree.children[0], event, diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 0fd3963d..4fd6680e 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -32,15 +32,15 @@ impl std::default::Default for Appearance { Self { bar_background: Color::from([0.85; 3]).into(), bar_border: Border{ - radius: [6.0; 4].into(), + radius: [8.0; 4].into(), ..Default::default() }, bar_shadow: Shadow::default(), - bar_background_expand: [3; 4].into(), + bar_background_expand: [5; 4].into(), menu_background: Color::from([0.85; 3]).into(), menu_border: Border{ - radius: [6.0; 4].into(), + radius: [8.0; 4].into(), ..Default::default() }, menu_shadow: Shadow{ @@ -48,7 +48,7 @@ impl std::default::Default for Appearance { offset: Vector::ZERO, blur_radius: 10.0, }, - menu_background_expand: [6; 4].into(), + menu_background_expand: [5; 4].into(), } } } From b116ac539b07394659b6bb1e89980a1341db5f32 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 20:56:51 +0800 Subject: [PATCH 20/40] fix Item::diff --- src/native/menu/menu_tree.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 90be4072..579855b4 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -612,12 +612,12 @@ where /// tree: Tree{stateless, \[widget_tree, menu_tree]} pub(super) fn diff(&self, tree: &mut Tree) { - if let Some(t) = tree.children.get_mut(0) { - t.diff(&self.item); - if let Some(t) = tree.children.get_mut(1) { - self.menu.as_ref().map_or((), |m| m.diff(t) ) - }else{ - *tree = self.tree(); + if let Some(t0) = tree.children.get_mut(0) { + t0.diff(&self.item); + if let Some(t1) = tree.children.get_mut(1) { + self.menu + .as_ref() + .map_or((), |m| m.diff(t1) ) } }else{ *tree = self.tree(); From 29fe2dc20efcce242aeebc4df20146a69cbec28f Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 21:39:05 +0800 Subject: [PATCH 21/40] example --- examples/menu/caret-right-fill.svg | 3 --- examples/menu/src/main.rs | 31 +++++++++++++----------------- 2 files changed, 13 insertions(+), 21 deletions(-) delete mode 100644 examples/menu/caret-right-fill.svg diff --git a/examples/menu/caret-right-fill.svg b/examples/menu/caret-right-fill.svg deleted file mode 100644 index b4455518..00000000 --- a/examples/menu/caret-right-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 3151dad9..d6c5f1f0 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -8,6 +8,8 @@ use iced::{alignment, theme, Alignment, Application, Border, Color, Element, Len use iced_aw::menu::{Item, Menu}; use iced_aw::quad; use iced_aw::{menu_bar, menu_items}; +use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; + pub fn main() -> iced::Result { // std::env::set_var("RUST_BACKTRACE", "full"); @@ -67,7 +69,7 @@ impl Application for App { dark_mode: false, text: "Text Input".into(), }, - iced::Command::none(), + iced::font::load(BOOTSTRAP_FONT_BYTES).map(|_| Message::None) ) } @@ -155,7 +157,7 @@ impl Application for App { (debug_button("Item")) (debug_button("Item")) (debug_button("Item")) - )).width(180.0); + )).width(200.0); let sub3 = menu_temp_2(menu_items!( (debug_button("You can")) @@ -164,7 +166,7 @@ impl Application for App { (debug_button("how ever")) (debug_button("You like")) (submenu_button("SUB"), sub5) - )).width(160.0); + )).width(180.0); let sub2 = menu_temp_2(menu_items!( (debug_button("Item")) @@ -174,7 +176,7 @@ impl Application for App { (debug_button("Item")) (debug_button("Item")) (debug_button("Item")) - )).width(140.0); + )).width(160.0); let sub1 = menu_temp_2(menu_items!( (debug_button("Item")) @@ -192,7 +194,7 @@ impl Application for App { (debug_button("Item")) (debug_button("Item")) (debug_button("Item")) - )).width(110.0) + )).width(140.0) }) (debug_button_s("Widgets"), menu_temp_1(menu_items!( (debug_button("You can use any widget")) @@ -390,7 +392,7 @@ impl Application for App { let slider_count = 3; let slider_width = 30; let spacing = 5; - // let pad = 20; + let pad = 20; let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); menu_temp_1(menu_items!( @@ -423,7 +425,7 @@ impl Application for App { (debug_button("IIJJ").height(60)) (debug_button("KKLL").height(120)) (debug_button("MMNN").height(50)) - )).width(slider_width * slider_count + (slider_count - 1) * spacing) + )).width(slider_width * slider_count + (slider_count - 1) * spacing + pad) }) ); @@ -514,22 +516,15 @@ fn debug_button_s<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, i } fn submenu_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { - let handle = svg::Handle::from_path(format!( - "{}/caret-right-fill.svg", - env!("CARGO_MANIFEST_DIR") - )); - let arrow = svg(handle) - .width(Length::Shrink) - .style(theme::Svg::custom_fn(|theme| svg::Appearance { - color: Some(theme.extended_palette().background.base.text), - })); - base_button( row![ text(label) .width(Length::Fill) .vertical_alignment(alignment::Vertical::Center), - arrow + text(BootstrapIcon::CaretRightFill) + .font(BOOTSTRAP_FONT) + .width(Length::Shrink) + .vertical_alignment(alignment::Vertical::Center), ] .align_items(iced::Alignment::Center), Message::Debug(label.into()) From 0b483833251878ffaf18648f03004f11b448f0da Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 21:51:55 +0800 Subject: [PATCH 22/40] fix Item::diff --- src/native/menu/menu_tree.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 579855b4..9f3ea005 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -614,10 +614,12 @@ where pub(super) fn diff(&self, tree: &mut Tree) { if let Some(t0) = tree.children.get_mut(0) { t0.diff(&self.item); - if let Some(t1) = tree.children.get_mut(1) { - self.menu - .as_ref() - .map_or((), |m| m.diff(t1) ) + if let Some(m) = self.menu.as_ref(){ + if let Some(t1) = tree.children.get_mut(1) { + m.diff(t1); + }else{ + *tree = self.tree(); + } } }else{ *tree = self.tree(); From 077ee0932b46cacc830406ca6030eecf3d768f6a Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Wed, 21 Feb 2024 22:01:58 +0800 Subject: [PATCH 23/40] fmt --- examples/menu/src/main.rs | 613 ++++++++++++++-------------- src/native/helpers.rs | 6 +- src/native/menu.rs | 39 +- src/native/menu/common.rs | 10 +- src/native/menu/menu_bar.rs | 78 ++-- src/native/menu/menu_bar_overlay.rs | 368 +++++++++-------- src/native/menu/menu_tree.rs | 329 ++++++++------- src/style/menu_bar.rs | 12 +- 8 files changed, 738 insertions(+), 717 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index d6c5f1f0..30ef1ab8 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -1,15 +1,16 @@ use iced::widget::{ - button, checkbox, container, horizontal_space, row, scrollable, slider, svg, text, - text_input, toggler, vertical_slider, + button, checkbox, container, horizontal_space, row, scrollable, slider, svg, text, text_input, + toggler, vertical_slider, }; use iced::widget::{column as col, vertical_space}; -use iced::{alignment, theme, Alignment, Application, Border, Color, Element, Length, Pixels, Size}; +use iced::{ + alignment, theme, Alignment, Application, Border, Color, Element, Length, Pixels, Size, +}; +use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; use iced_aw::menu::{Item, Menu}; use iced_aw::quad; use iced_aw::{menu_bar, menu_items}; -use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; - pub fn main() -> iced::Result { // std::env::set_var("RUST_BACKTRACE", "full"); @@ -69,7 +70,7 @@ impl Application for App { dark_mode: false, text: "Text Input".into(), }, - iced::font::load(BOOTSTRAP_FONT_BYTES).map(|_| Message::None) + iced::font::load(BOOTSTRAP_FONT_BYTES).map(|_| Message::None), ) } @@ -142,306 +143,287 @@ impl Application for App { let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); - let mb = menu_bar!( - (debug_button_s("Nested Menus"), { - let sub5 = menu_temp_2(menu_items!( - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - )); - - let sub4 = menu_temp_2(menu_items!( - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - )).width(200.0); - - let sub3 = menu_temp_2(menu_items!( - (debug_button("You can")) - (debug_button("nest menus")) - (submenu_button("SUB"), sub4) - (debug_button("how ever")) - (debug_button("You like")) - (submenu_button("SUB"), sub5) - )).width(180.0); - - let sub2 = menu_temp_2(menu_items!( - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - (submenu_button("More sub menus"), sub3) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - )).width(160.0); - - let sub1 = menu_temp_2(menu_items!( - (debug_button("Item")) - (debug_button("Item")) - (submenu_button("Another sub menu"), sub2) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - )).width(220.0); - - menu_temp_1(menu_items!( - (debug_button("Item")) - (debug_button("Item")) - (submenu_button("A sub menu"), sub1) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - )).width(140.0) - }) - (debug_button_s("Widgets"), menu_temp_1(menu_items!( - (debug_button("You can use any widget")) - (debug_button("as a menu item")) - (button( + let mb = menu_bar!((debug_button_s("Nested Menus"), { + let sub5 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + debug_button("Item") + )(debug_button("Item"))( + debug_button("Item") + ))); + + let sub4 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + debug_button("Item") + )(debug_button("Item")))) + .width(200.0); + + let sub3 = menu_temp_2(menu_items!((debug_button("You can"))(debug_button( + "nest menus" + ))(submenu_button("SUB"), sub4)( + debug_button("how ever") + )(debug_button("You like"))( + submenu_button("SUB"), sub5 + ))) + .width(180.0); + + let sub2 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + debug_button("Item") + )( + submenu_button("More sub menus"), sub3 + )(debug_button("Item"))( + debug_button("Item") + )(debug_button("Item")))) + .width(160.0); + + let sub1 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + submenu_button("Another sub menu"), + sub2 + )(debug_button("Item"))( + debug_button("Item") + )(debug_button("Item")))) + .width(220.0); + + menu_temp_1(menu_items!((debug_button("Item"))(debug_button("Item"))( + submenu_button("A sub menu"), + sub1 + )(debug_button("Item"))( + debug_button("Item") + )(debug_button("Item")))) + .width(140.0) + })( + debug_button_s("Widgets"), + menu_temp_1(menu_items!((debug_button("You can use any widget"))( + debug_button("as a menu item") + )( + button( text("Button") .width(Length::Fill) .vertical_alignment(alignment::Vertical::Center), - ) - .width(Length::Fill) - .on_press(Message::Debug("Button".into())) - ) - (checkbox("Checkbox", self.check, Message::CheckChange) - .width(Length::Fill) - ) - ( - row![ - "Slider", - horizontal_space(Length::Fixed(8.0)), - slider(0..=255, self.value, Message::ValueChange) - ] - ) - (text_input("", &self.text).on_input(Message::TextChange)) - (container(toggler( - Some("Or as a sub menu item".to_string()), - self.toggle, - Message::ToggleChange, - )) - .padding([0, 8]) - .height(30.0) - .align_y(alignment::Vertical::Center), - menu_temp_2(menu_items!( - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - (debug_button("Item")) - )) - ) - (debug_button("Separator")) - (separator()) - (debug_button("Labeled Separator")) - (labeled_separator("Separator")) - (debug_button("Dot Separator")) - (dot_separator(&self.theme)) - (debug_button("Item")) - )).width(240.0)) - (debug_button_s("Controls"), menu_temp_1(menu_items!( - (row![toggler( - Some("Dark Mode".into()), - self.dark_mode, - Message::ThemeChange - )].padding([0, 8]) - ) - (color_button([0.45, 0.25, 0.57])) - (color_button([0.15, 0.59, 0.64])) - (color_button([0.76, 0.82, 0.20])) - (color_button([0.17, 0.27, 0.33])) - (labeled_button("Primary", Message::None) - .width(Length::Fill), - { - let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); - - menu_temp_2(menu_items!( - (slider(0..=255, r, move |x| { - Message::ColorChange(Color::from_rgb8(x, g, b)) - })) - (slider(0..=255, g, move |x| { - Message::ColorChange(Color::from_rgb8(r, x, b)) - })) - (slider(0..=255, b, move |x| { - Message::ColorChange(Color::from_rgb8(r, g, x)) - })) - )) - } ) + .width(Length::Fill) + .on_press(Message::Debug("Button".into())) + )( + checkbox("Checkbox", self.check, Message::CheckChange).width(Length::Fill) + )(row![ + "Slider", + horizontal_space(Length::Fixed(8.0)), + slider(0..=255, self.value, Message::ValueChange) + ])( + text_input("", &self.text).on_input(Message::TextChange) + )( + container(toggler( + Some("Or as a sub menu item".to_string()), + self.toggle, + Message::ToggleChange, + )) + .padding([0, 8]) + .height(30.0) + .align_y(alignment::Vertical::Center), + menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + debug_button("Item") + )(debug_button("Item")))) + )(debug_button("Separator"))( + separator() + )(debug_button( + "Labeled Separator" + ))(labeled_separator("Separator"))( + debug_button("Dot Separator") + )(dot_separator(&self.theme))( + debug_button("Item") ))) - (debug_button_s("Scroll"), menu_temp_1(menu_items!( - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (submenu_button("jcsu"), menu_temp_2(menu_items!( - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (submenu_button("luyortp"), menu_temp_2(menu_items!( - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) - ))) - (debug_button("mmdyrc")) - (debug_button("nquc")) - ))) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (submenu_button("dekjdaud"), menu_temp_2(menu_items!( - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) + .width(240.0) + )( + debug_button_s("Controls"), + menu_temp_1(menu_items!((row![toggler( + Some("Dark Mode".into()), + self.dark_mode, + Message::ThemeChange + )] + .padding([0, 8]))(color_button([ + 0.45, 0.25, 0.57 + ]))(color_button([ + 0.15, 0.59, 0.64 + ]))(color_button([ + 0.76, 0.82, 0.20 + ]))(color_button([ + 0.17, 0.27, 0.33 + ]))( + labeled_button("Primary", Message::None).width(Length::Fill), + { + let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); + + menu_temp_2(menu_items!((slider(0..=255, r, move |x| { + Message::ColorChange(Color::from_rgb8(x, g, b)) + }))(slider( + 0..=255, + g, + move |x| { Message::ColorChange(Color::from_rgb8(r, x, b)) } + ))(slider( + 0..=255, + b, + move |x| { Message::ColorChange(Color::from_rgb8(r, g, x)) } + )))) + } + ))) + )( + debug_button_s("Scroll"), + menu_temp_1(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + debug_button("clkjhbf") + )(debug_button("dekjdaud"))( + debug_button("ecsh") + )(debug_button("fweiu"))( + debug_button("giwe") + )(debug_button("heruyv"))( + debug_button("isabe") + )( + submenu_button("jcsu"), + menu_temp_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + debug_button("clkjhbf") + )(debug_button("dekjdaud"))( + debug_button("ecsh") + )(debug_button("fweiu"))( + debug_button("giwe") + )(debug_button("heruyv"))( + debug_button("isabe") + )(debug_button("jcsu"))( + debug_button("kaljkahd") + )( + submenu_button("luyortp"), + menu_temp_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + debug_button("clkjhbf") + )(debug_button("dekjdaud"))( + debug_button("ecsh") + )(debug_button("fweiu"))( + debug_button("giwe") + )(debug_button("heruyv"))( + debug_button("isabe") + )(debug_button("jcsu"))( + debug_button("kaljkahd") + )(debug_button("luyortp"))( + debug_button("mmdyrc") + )(debug_button("nquc")))) + )(debug_button("mmdyrc"))( + debug_button("nquc") ))) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) - (debug_button("ajrs")) - (debug_button("bsdfho")) - (debug_button("clkjhbf")) - (debug_button("dekjdaud")) - (debug_button("ecsh")) - (debug_button("fweiu")) - (debug_button("giwe")) - (debug_button("heruyv")) - (debug_button("isabe")) - (debug_button("jcsu")) - (debug_button("kaljkahd")) - (debug_button("luyortp")) - (debug_button("mmdyrc")) - (debug_button("nquc")) + )(debug_button("kaljkahd"))( + debug_button("luyortp") + )(debug_button("mmdyrc"))( + debug_button("nquc") + )(debug_button("ajrs"))( + debug_button("bsdfho") + )(debug_button("clkjhbf"))( + debug_button("dekjdaud") + )(debug_button("ecsh"))( + debug_button("fweiu") + )(debug_button("giwe"))( + debug_button("heruyv") + )(debug_button("isabe"))( + debug_button("jcsu") + )(debug_button("kaljkahd"))( + debug_button("luyortp") + )(debug_button("mmdyrc"))( + debug_button("nquc") + )(debug_button("ajrs"))( + debug_button("bsdfho") + )(debug_button("clkjhbf"))( + submenu_button("dekjdaud"), + menu_temp_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + debug_button("clkjhbf") + )(debug_button("dekjdaud"))( + debug_button("ecsh") + )(debug_button("fweiu"))( + debug_button("giwe") + )(debug_button("heruyv"))( + debug_button("isabe") + )(debug_button("jcsu"))( + debug_button("kaljkahd") + )(debug_button("luyortp"))( + debug_button("mmdyrc") + )(debug_button("nquc"))( + debug_button("ajrs") + )(debug_button("bsdfho"))( + debug_button("clkjhbf") + )(debug_button("dekjdaud"))( + debug_button("ecsh") + )(debug_button("fweiu"))( + debug_button("giwe") + )(debug_button("heruyv"))( + debug_button("isabe") + )(debug_button("jcsu"))( + debug_button("kaljkahd") + )(debug_button("luyortp"))( + debug_button("mmdyrc") + )(debug_button("nquc")))) + )(debug_button("ecsh"))( + debug_button("fweiu") + )(debug_button("giwe"))( + debug_button("heruyv") + )(debug_button("isabe"))( + debug_button("jcsu") + )(debug_button("kaljkahd"))( + debug_button("luyortp") + )(debug_button("mmdyrc"))( + debug_button("nquc") + )(debug_button("ajrs"))( + debug_button("bsdfho") + )(debug_button("clkjhbf"))( + debug_button("dekjdaud") + )(debug_button("ecsh"))( + debug_button("fweiu") + )(debug_button("giwe"))( + debug_button("heruyv") + )(debug_button("isabe"))( + debug_button("jcsu") + )(debug_button("kaljkahd"))( + debug_button("luyortp") + )(debug_button("mmdyrc"))( + debug_button("nquc") ))) - (debug_button_s("Dynamic height"), { - let slider_count = 3; - let slider_width = 30; - let spacing = 5; - let pad = 20; - let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); - - menu_temp_1(menu_items!( - (labeled_separator("Primary")) - ( - row![ - vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( - x, g, b - ))) - .width(slider_width) - , - vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( - r, x, b - ))) - .width(slider_width) - , - vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( - r, g, x - ))) - .width(slider_width) - , - ].spacing(spacing) - .height(100.0) - ) - (separator()) - (debug_button("AABB").height(40)) - (debug_button("CCDD").height(140)) - (debug_button("EEFF").height(30)) - (debug_button("GGHH").height(100)) - (debug_button("IIJJ").height(60)) - (debug_button("KKLL").height(120)) - (debug_button("MMNN").height(50)) - )).width(slider_width * slider_count + (slider_count - 1) * spacing + pad) - }) - ); + )(debug_button_s("Dynamic height"), { + let slider_count = 3; + let slider_width = 30; + let spacing = 5; + let pad = 20; + let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); + + menu_temp_1(menu_items!((labeled_separator("Primary"))( + row![ + vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( + x, g, b + ))) + .width(slider_width), + vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( + r, x, b + ))) + .width(slider_width), + vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( + r, g, x + ))) + .width(slider_width), + ] + .spacing(spacing) + .height(100.0) + )(separator())( + debug_button("AABB").height(40) + )(debug_button("CCDD").height(140))( + debug_button("EEFF").height(30) + )(debug_button("GGHH").height(100))( + debug_button("IIJJ").height(60) + )(debug_button("KKLL").height(120))( + debug_button("MMNN").height(50) + ))) + .width(slider_width * slider_count + (slider_count - 1) * spacing + pad) + })); let r = row![horizontal_space(295), mb, horizontal_space(295),] .align_items(alignment::Alignment::Center); let c = col![vertical_space(500), r, vertical_space(500),]; - - let sc = scrollable(c) - .direction(scrollable::Direction::Both { - vertical: scrollable::Properties::new() - .alignment(scrollable::Alignment::End), - horizontal: scrollable::Properties::new(), - }); - - fn back_style(theme: &iced::Theme) -> container::Appearance{ + + let sc = scrollable(c).direction(scrollable::Direction::Both { + vertical: scrollable::Properties::new().alignment(scrollable::Alignment::End), + horizontal: scrollable::Properties::new(), + }); + + fn back_style(theme: &iced::Theme) -> container::Appearance { container::Appearance { background: Some(theme.extended_palette().primary.base.color.into()), ..Default::default() @@ -499,20 +481,17 @@ fn labeled_button<'a>( msg: Message, ) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { base_button( - text(label) - .vertical_alignment(alignment::Vertical::Center), + text(label).vertical_alignment(alignment::Vertical::Center), msg, ) } fn debug_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { - labeled_button(label, Message::Debug(label.into())) - .width(Length::Fill) + labeled_button(label, Message::Debug(label.into())).width(Length::Fill) } fn debug_button_s<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { - labeled_button(label, Message::Debug(label.into())) - .width(Length::Shrink) + labeled_button(label, Message::Debug(label.into())).width(Length::Shrink) } fn submenu_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { @@ -526,16 +505,17 @@ fn submenu_button<'a>(label: &str) -> button::Button<'a, Message, iced::Theme, i .width(Length::Shrink) .vertical_alignment(alignment::Vertical::Center), ] - .align_items(iced::Alignment::Center), - Message::Debug(label.into()) - ).width(Length::Fill) + .align_items(iced::Alignment::Center), + Message::Debug(label.into()), + ) + .width(Length::Fill) } -fn color_button<'a>(color: impl Into) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { +fn color_button<'a>( + color: impl Into, +) -> button::Button<'a, Message, iced::Theme, iced::Renderer> { let color = color.into(); - base_button( - circle(color), Message::ColorChange(color) - ) + base_button(circle(color), Message::ColorChange(color)) } fn separator<'a>() -> quad::Quad { @@ -549,16 +529,15 @@ fn separator<'a>() -> quad::Quad { } fn dot_separator<'a>(theme: &iced::Theme) -> Element<'a, Message, iced::Theme, iced::Renderer> { - row( - (0..20).map(|_|{ - quad::Quad { - color: theme.extended_palette().background.base.text, - border_radius: [4.0; 4], - inner_bounds: quad::InnerBounds::Square(4.0), - ..Default::default() - }.into() - }) - ) + row((0..20).map(|_| { + quad::Quad { + color: theme.extended_palette().background.base.text, + border_radius: [4.0; 4], + inner_bounds: quad::InnerBounds::Square(4.0), + ..Default::default() + } + .into() + })) .height(30.0) .into() } diff --git a/src/native/helpers.rs b/src/native/helpers.rs index 0874bf4b..3c4d558c 100644 --- a/src/native/helpers.rs +++ b/src/native/helpers.rs @@ -67,7 +67,7 @@ macro_rules! wrap_vertical { /// Creates a vec of menu items /// /// [`Item`]: crate::menu::Item -/// +/// /// Syntax: /// ``` /// menu_items!( @@ -102,7 +102,7 @@ macro_rules! menu_items { /// Creates a [`Menu`] with the given items. /// /// [`Menu`]: crate::menu::Menu -/// +/// /// Syntax: /// ``` /// menu!( @@ -126,7 +126,7 @@ macro_rules! menu { /// Creates a [`MenuBar`] with the given children. /// /// [`MenuBar`]: crate::menu::MenuBar -/// +/// /// Syntax: /// ``` /// menu_bar!( diff --git a/src/native/menu.rs b/src/native/menu.rs index 1ed01797..04adc8b2 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -49,17 +49,17 @@ //! let menu_bar = MenuBar::new(vec![root_1, root_2]); //! //! ``` -//! +//! //! Alternatively you can use the helper macros -//! +//! //! ## Example 2 //! ``` //! use iced::widget::button; //! use iced_aw::menu::{Menu} //! use iced_aw::{menu_bar, menu_items} -//! +//! //! let menu_template = |items| Menu::new(items).max_width(180.0).offset(6.0); -//! +//! //! let menu_bar = menu_bar!( //! (button("Menu 1"),menu_template(menu_items!( //! (button("item_1")) @@ -83,22 +83,22 @@ //! ))) //! ) //! ``` -//! -//! Notice a menu_template function/closure is used in example 2. Usually some properties are sync across all menus while others are not, -//! template function is one way to do that. If you find writing menu_template(menu_items!()) cumbersome, +//! +//! Notice a menu_template function/closure is used in example 2. Usually some properties are sync across all menus while others are not, +//! template function is one way to do that. If you find writing menu_template(menu_items!()) cumbersome, //! there is a menu! macro you can use to create template macros -//! +//! //! # Example 3 -//! +//! //! ``` //! use iced_aw::{menu}; -//! +//! //! macro_rules! menu_template { //! ($($x:tt)+) => { //! menu!($($x)+).max_width(180.0).offset(6.0) //! }; //! } -//! +//! //! // then you can just write //! let m = menu_template!( //! (button("item_1")) @@ -110,9 +110,9 @@ //! (button("item_3")) //! ); //! ``` -//! -//! Technically You can create menu template functions with the menu! macro, -//! but turns out closures can't infer the generic types, +//! +//! Technically You can create menu template functions with the menu! macro, +//! but turns out closures can't infer the generic types, //! and creating a function for it involves writing a ton of generic annotations //! ``` //! fn menu_template<'a, Message, Theme, Renderer>( @@ -124,24 +124,23 @@ //! { //! menu.max_width(180.0).offset(6.0) //! } -//! +//! //! let m = menu_template(menu!( //! (button("item_1")) //! (button("item_2")) //! (button("item_3")) //! )); //! ``` -//! -//! For a more detailed example please +//! +//! For a more detailed example please //! take a look at the menu example in the iced_aw repo -//! - +//! +mod common; mod flex; mod menu_bar; mod menu_bar_overlay; mod menu_tree; -mod common; pub use crate::style::menu_bar::{Appearance, StyleSheet}; pub use menu_bar::MenuBar; diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index e6d8802f..7fd29e57 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -1,6 +1,4 @@ -use iced_widget::core::{ - Padding, Rectangle -}; +use iced_widget::core::{Padding, Rectangle}; /* /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] @@ -16,14 +14,14 @@ pub struct CloseCondition { } */ - /// X+ goes right and Y+ goes down +/// X+ goes right and Y+ goes down #[derive(Debug, Clone, Copy)] pub(super) enum Direction { Positive, Negative, } -impl Direction{ - pub(super) fn flip(&self) -> Direction{ +impl Direction { + pub(super) fn flip(&self) -> Direction { match self { Direction::Positive => Direction::Negative, Direction::Negative => Direction::Positive, diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 8bdf888f..b7f269a2 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,15 +1,18 @@ //! [`MenuBar`] -use iced_widget::{core::{ - alignment, event, - layout::{Limits, Node}, - mouse, overlay, renderer, - widget::{tree, Tree}, - Alignment, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Point, - Rectangle, Shell, Size, Widget, -}, Theme}; +use iced_widget::{ + core::{ + alignment, event, + layout::{Limits, Node}, + mouse, overlay, renderer, + widget::{tree, Tree}, + Alignment, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Point, + Rectangle, Shell, Size, Widget, + }, + Theme, +}; -use super::{flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*, common::*}; +use super::{common::*, flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*}; use crate::style::menu_bar::*; pub(super) struct MenuBarState { @@ -48,8 +51,8 @@ where { /// Creates a [`MenuBar`] with the given root items. pub fn new(mut roots: Vec>) -> Self { - roots.iter_mut().for_each(|i|{ - if let Some(m) = i.menu.as_mut(){ + roots.iter_mut().for_each(|i| { + if let Some(m) = i.menu.as_mut() { m.axis = Axis::Vertical; } }); @@ -60,36 +63,36 @@ where width: Length::Shrink, height: Length::Shrink, check_bounds_width: 50.0, - style: Theme::Style::default() + style: Theme::Style::default(), } } /// Sets the width of the [`MenuBar`]. - pub fn width(mut self, width: impl Into) -> Self{ + pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the height of the [`MenuBar`]. - pub fn height(mut self, height: impl Into) -> Self{ + pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); self } /// Sets the spacing of the [`MenuBar`]. - pub fn spacing(mut self, spacing: f32) -> Self{ + pub fn spacing(mut self, spacing: f32) -> Self { self.spacing = spacing; self } /// Sets the width of the check bounds of the [`Menu`]s in the [`MenuBar`]. - pub fn check_bounds_width(mut self, check_bounds_width: f32) -> Self{ + pub fn check_bounds_width(mut self, check_bounds_width: f32) -> Self { self.check_bounds_width = check_bounds_width; self } /// Sets the padding of the [`MenuBar`]. - pub fn padding(mut self, padding: impl Into) -> Self{ + pub fn padding(mut self, padding: impl Into) -> Self { self.padding = padding.into(); self } @@ -136,12 +139,7 @@ where } /// tree: Tree{bar_state, \[item_tree...]} - fn layout( - &self, - tree: &mut Tree, - renderer: &Renderer, - limits: &Limits, - ) -> Node { + fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { flex::resolve( flex::Axis::Horizontal, renderer, @@ -220,7 +218,7 @@ where } } Event::Mouse(mouse::Event::CursorMoved { .. }) => { - if bar.open{ + if bar.open { if cursor.is_over(bar_bounds) { for (i, l) in layout.children().enumerate() { if cursor.is_over(l.bounds()) { @@ -228,11 +226,11 @@ where break; } } - }else{ + } else { bar.open = false } Captured - }else{ + } else { Ignored } } @@ -253,21 +251,27 @@ where ) { let state = tree.state.downcast_ref::(); let cursor = if state.open { - state.active_root.and_then(|active|{ - layout.children().nth(active).and_then(|l| - Some(mouse::Cursor::Available(l.bounds().center())) - ) - }).unwrap_or(cursor) - }else{ cursor }; + state + .active_root + .and_then(|active| { + layout + .children() + .nth(active) + .and_then(|l| Some(mouse::Cursor::Available(l.bounds().center()))) + }) + .unwrap_or(cursor) + } else { + cursor + }; let styling = theme.appearance(&self.style); renderer.fill_quad( - renderer::Quad{ + renderer::Quad { bounds: pad_rectangle(layout.bounds(), styling.bar_background_expand), border: styling.bar_border, shadow: styling.bar_shadow, - }, - styling.bar_background + }, + styling.bar_background, ); self.roots @@ -288,7 +292,7 @@ where let state = tree.state.downcast_mut::(); let init_bar_bounds = layout.bounds(); - let init_root_bounds = layout.children().map(|l| l.bounds() ).collect(); + let init_root_bounds = layout.children().map(|l| l.bounds()).collect(); if state.open { Some( @@ -298,7 +302,7 @@ where init_bar_bounds, init_root_bounds, check_bounds_width: self.check_bounds_width, - style: &self.style + style: &self.style, } .overlay_element(), ) diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 6ed9fd29..5bbbb9f5 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -7,15 +7,15 @@ use iced_widget::core::{ Shell, Size, Vector, Widget, }; -use super::{menu_bar::MenuBarState, menu_tree::*, common::*}; +use super::{common::*, menu_bar::MenuBarState, menu_tree::*}; use crate::style::menu_bar::*; -/// Should be returned from the recursive event processing function, +/// Should be returned from the recursive event processing function, /// tells the caller which type of event has been processed -enum RecEvent{ - Event, +enum RecEvent { + Event, Close, - None + None, } pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> @@ -57,32 +57,26 @@ where let bar = self.tree.state.downcast_ref::(); let bar_bounds = self.init_bar_bounds; - let bar_node = Node::with_children( - bar_bounds.size(), - [].into() - ).move_to(bar_bounds.position() + translation); + let bar_node = Node::with_children(bar_bounds.size(), [].into()) + .move_to(bar_bounds.position() + translation); let roots_node = Node::with_children( - Size::ZERO, - self.init_root_bounds.iter().map(|r| - Node::new(r.size()).move_to(r.position()) - ).collect() - ).translate(translation); + Size::ZERO, + self.init_root_bounds + .iter() + .map(|r| Node::new(r.size()).move_to(r.position())) + .collect(), + ) + .translate(translation); let Some(active) = bar.active_root else { - return Node::with_children( - bounds, - [ - bar_node, - roots_node, - ].into() - ) + return Node::with_children(bounds, [bar_node, roots_node].into()); }; let active_root = &mut self.roots[active]; let active_tree = &mut self.tree.children[active]; // item_tree: Tree{ stateless, [ widget_tree, menu_tree ] } let parent_bounds = self.init_root_bounds[active] + translation; - + fn rec<'a, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( renderer: &Renderer, item: &Item<'a, Message, Theme, Renderer>, @@ -97,38 +91,42 @@ where let menu_tree = &mut tree.children[1]; let (menu_node, direction) = menu.layout( - menu_tree, - renderer, - &Limits::NONE, - check_bounds_width, - parent_bounds, - parent_direction, - viewport - ); - // Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} + menu_tree, + renderer, + &Limits::NONE, + check_bounds_width, + parent_bounds, + parent_direction, + viewport, + ); + // Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} menu_nodes.push(menu_node); - + let menu_state = menu_tree.state.downcast_ref::(); - if let Some(active) = menu_state.active{ + if let Some(active) = menu_state.active { let next_item = &menu.items[active]; let next_tree = &mut menu_tree.children[active]; let next_parent_bounds = { let slice_node = &menu_nodes.last().unwrap().children()[0]; - let Some(node) = slice_node.children().get(active - menu_state.slice.start_index) - else { return; }; + let Some(node) = slice_node + .children() + .get(active - menu_state.slice.start_index) + else { + return; + }; node.bounds() + (slice_node.bounds().position() - Point::ORIGIN) }; rec( - renderer, - next_item, - next_tree, - menu_nodes, - check_bounds_width, - next_parent_bounds, - direction, - viewport + renderer, + next_item, + next_tree, + menu_nodes, + check_bounds_width, + next_parent_bounds, + direction, + viewport, ); } } @@ -138,7 +136,7 @@ where let parent_direction = { let hcenter = bounds.width / 2.0; let vcenter = bounds.height / 2.0; - + let phcenter = parent_bounds.x + parent_bounds.width / 2.0; let pvcenter = parent_bounds.y + parent_bounds.height / 2.0; @@ -152,7 +150,7 @@ where Direction::Positive } else { Direction::Negative - } + }, ) }; @@ -168,15 +166,16 @@ where ); Node::with_children( - bounds, + bounds, [ bar_node, roots_node, Node::with_children(Size::ZERO, menu_nodes), - ].into() + ] + .into(), ) } - + #[allow(unused_results)] fn on_event( &mut self, @@ -193,7 +192,7 @@ where let mut lc = layout.children(); let bar_bounds = lc.next().unwrap().bounds(); let roots_layout = lc.next().unwrap(); - + let bar = self.tree.state.downcast_mut::(); let Some(active) = bar.active_root else { @@ -201,18 +200,20 @@ where }; let parent_bounds = roots_layout.children().nth(active).unwrap().bounds(); - let Some(menu_layouts_layout) = lc.next() else { return Ignored; }; // Node{0, [menu_node...]} + let Some(menu_layouts_layout) = lc.next() else { + return Ignored; + }; // Node{0, [menu_node...]} let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] let active_root = &mut self.roots[active]; let active_tree = &mut self.tree.children[active]; let mut prev_bounds_list = vec![bar_bounds]; - + fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( tree: &mut Tree, item: &mut Item<'a, Message, Theme, Renderer>, event: Event, - layout_iter: &mut impl Iterator< Item = Layout<'b>>, + layout_iter: &mut impl Iterator>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, @@ -225,119 +226,164 @@ where let menu = item.menu.as_mut().expect("No menu defined in this item"); let menu_tree = &mut tree.children[1]; - let Some(menu_layout) = layout_iter.next() else { return RecEvent::None; }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} + let Some(menu_layout) = layout_iter.next() else { + return RecEvent::None; + }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} let mut mc = menu_layout.children(); let slice_layout = mc.next().unwrap(); // slice_node let prescroll = mc.next().unwrap().bounds(); let offset_bounds = mc.next().unwrap().bounds(); prev_bounds_list.push(prescroll); - + let menu_state = menu_tree.state.downcast_mut::(); - if let Some(active) = menu_state.active{ + if let Some(active) = menu_state.active { let next_tree = &mut menu_tree.children[active]; let next_item = &mut menu.items[active]; let next_parent_bounds = { - let Some(layout) = slice_layout.children().nth(active - menu_state.slice.start_index) - else { return RecEvent::Event }; - + let Some(layout) = slice_layout + .children() + .nth(active - menu_state.slice.start_index) + else { + return RecEvent::Event; + }; + layout.bounds() }; let re = rec( - next_tree, - next_item, - event.clone(), - layout_iter, - cursor, - renderer, - clipboard, - shell, - next_parent_bounds, - viewport, - prev_bounds_list, - &mut menu_state.active + next_tree, + next_item, + event.clone(), + layout_iter, + cursor, + renderer, + clipboard, + shell, + next_parent_bounds, + viewport, + prev_bounds_list, + &mut menu_state.active, ); - + prev_bounds_list.pop(); match re { RecEvent::Event => RecEvent::Event, RecEvent::Close => { - if cursor.is_over(prescroll){ - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + if cursor.is_over(prescroll) { + menu.on_event( + menu_tree, + event, + menu_layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event - }else if cursor.is_over(offset_bounds){ + } else if cursor.is_over(offset_bounds) { RecEvent::Event - }else{ - menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); - if prev.is_some(){ + } else { + menu.close_event( + menu_tree, + menu_layout, + cursor, + parent_bounds, + prev_bounds_list, + prev, + ); + if prev.is_some() { RecEvent::None - }else{ + } else { RecEvent::Close } } } RecEvent::None => { - if cursor.is_over(prescroll){ - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + if cursor.is_over(prescroll) { + menu.on_event( + menu_tree, + event, + menu_layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event - }else if cursor.is_over(offset_bounds){ + } else if cursor.is_over(offset_bounds) { RecEvent::Event - }else{ + } else { RecEvent::None } } } - }else{ + } else { prev_bounds_list.pop(); - if cursor.is_over(prescroll){ - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + if cursor.is_over(prescroll) { + menu.on_event( + menu_tree, + event, + menu_layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event - }else if cursor.is_over(offset_bounds){ + } else if cursor.is_over(offset_bounds) { RecEvent::Event - }else{ - menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); - if prev.is_some(){ + } else { + menu.close_event( + menu_tree, + menu_layout, + cursor, + parent_bounds, + prev_bounds_list, + prev, + ); + if prev.is_some() { RecEvent::None - }else{ + } else { RecEvent::Close } } } } - + let re = rec( - active_tree, - active_root, - event, + active_tree, + active_root, + event, &mut menu_layouts, - cursor, - renderer, - clipboard, - shell, - parent_bounds, - &viewport, + cursor, + renderer, + clipboard, + shell, + parent_bounds, + &viewport, &mut prev_bounds_list, &mut bar.active_root, ); match re { - RecEvent::Event => { - Captured - }, + RecEvent::Event => Captured, RecEvent::Close | RecEvent::None => { - if cursor.is_over(bar_bounds){ + if cursor.is_over(bar_bounds) { Ignored - }else{ + } else { Captured } - }, + } } } @@ -350,7 +396,9 @@ where cursor: mouse::Cursor, ) { let bar = self.tree.state.downcast_ref::(); - let Some(active) = bar.active_root else { return; }; + let Some(active) = bar.active_root else { + return; + }; let viewport = layout.bounds(); let mut lc = layout.children(); @@ -367,7 +415,7 @@ where fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( tree: &Tree, item: &Item<'a, Message, Theme, Renderer>, - layout_iter: &mut impl Iterator< Item = Layout<'b>>, + layout_iter: &mut impl Iterator>, cursor: mouse::Cursor, renderer: &mut Renderer, theme: &Theme, @@ -378,81 +426,79 @@ where let menu = item.menu.as_ref().expect("No menu defined in this item"); let menu_tree = &tree.children[1]; - let Some(menu_layout) = layout_iter.next() else { return }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} - - let mut draw_menu = |cursor| + let Some(menu_layout) = layout_iter.next() else { + return; + }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} + + let mut draw_menu = |cursor| { menu.draw( - menu_tree, - renderer, - theme, - style, + menu_tree, + renderer, + theme, + style, theme_style, - menu_layout, - cursor, - viewport, - ); - + menu_layout, + cursor, + viewport, + ) + }; + let menu_state = menu_tree.state.downcast_ref::(); - - if let Some(active) = menu_state.active{ + + if let Some(active) = menu_state.active { let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; let mut mc = menu_layout.children(); let slice_layout = mc.next().unwrap(); // slice_node let active_bounds = { - let Some(layout) = slice_layout.children().nth(active - menu_state.slice.start_index) - else { return; }; + let Some(layout) = slice_layout + .children() + .nth(active - menu_state.slice.start_index) + else { + return; + }; layout.bounds() }; - draw_menu( - if cursor.is_over(active_bounds){ - cursor - }else{ - mouse::Cursor::Available(active_bounds.center()) - } - ); - - renderer.with_layer( - *viewport, - |r| rec( - next_tree, - next_item, - layout_iter, - cursor, - r, - theme, - style, + draw_menu(if cursor.is_over(active_bounds) { + cursor + } else { + mouse::Cursor::Available(active_bounds.center()) + }); + + renderer.with_layer(*viewport, |r| { + rec( + next_tree, + next_item, + layout_iter, + cursor, + r, + theme, + style, theme_style, - viewport + viewport, ) - ); - }else{ + }); + } else { draw_menu(cursor) } } - + rec( - active_tree, - active_root, - &mut menu_layouts, - cursor, - renderer, - theme, - style, + active_tree, + active_root, + &mut menu_layouts, + cursor, + renderer, + theme, + style, self.style, - &viewport + &viewport, ) - } - fn is_over( - &self, - _layout: Layout<'_>, - _renderer: &Renderer, - _cursor_position: Point - ) -> bool { + fn is_over(&self, _layout: Layout<'_>, _renderer: &Renderer, _cursor_position: Point) -> bool { false } } diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 9f3ea005..66e67f04 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,16 +1,16 @@ //! [`Item`] and [`Menu`] //! -use std::iter::once; use super::common::*; use super::flex; use iced_widget::core::{ alignment, event, - layout::{Limits, Node, Layout}, + layout::{Layout, Limits, Node}, mouse, overlay, renderer, widget::tree::{self, Tree}, Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, Size, Vector, Widget, }; +use std::iter::once; use crate::style::menu_bar::*; @@ -51,17 +51,17 @@ pub(super) struct MenuState { pub(super) active: Index, pub(super) slice: MenuSlice, } -impl Default for MenuState{ +impl Default for MenuState { fn default() -> Self { Self { scroll_offset: 0.0, active: None, - slice: MenuSlice{ + slice: MenuSlice { start_index: 0, end_index: usize::MAX, lower_bound_rel: 0.0, upper_bound_rel: f32::MAX, - } + }, } } } @@ -102,31 +102,31 @@ where } /// Sets the maximum width of the [`Menu`]. - pub fn max_width(mut self, max_width: f32) -> Self{ + pub fn max_width(mut self, max_width: f32) -> Self { self.max_width = max_width; self } /// Sets the width of the [`Menu`]. - pub fn width(mut self, width: impl Into) -> Self{ + pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the spacing of the [`Menu`]. - pub fn spacing(mut self, spacing: f32) -> Self{ + pub fn spacing(mut self, spacing: f32) -> Self { self.spacing = spacing; self } /// Sets the padding of the [`Menu`]. - pub fn padding(mut self, padding: impl Into) -> Self{ + pub fn padding(mut self, padding: impl Into) -> Self { self.padding = padding.into(); self } /// The offset from the bounds of the menu's parent item. - pub fn offset(mut self, offset: f32) -> Self{ + pub fn offset(mut self, offset: f32) -> Self { self.offset = offset; self } @@ -202,11 +202,18 @@ where .collect::>(), ); - let aod = Aod::new(self.axis, viewport.size(), parent_bounds, parent_direction, self.offset); + let aod = Aod::new( + self.axis, + viewport.size(), + parent_bounds, + parent_direction, + self.offset, + ); let children_size = items_node.bounds().size(); - let (children_position, offset_position, child_direction) = aod.resolve(parent_bounds, children_size, viewport.size()); - + let (children_position, offset_position, child_direction) = + aod.resolve(parent_bounds, children_size, viewport.size()); + // calc auxiliary bounds let delta = children_position - offset_position; let offset_size = if delta.x.abs() > delta.y.abs() { @@ -214,7 +221,7 @@ where } else { Size::new(children_size.width, self.offset) }; - + let offset_bounds = Rectangle::new(offset_position, offset_size); let children_bounds = Rectangle::new(children_position, children_size); let check_bounds = pad_rectangle(children_bounds, [check_bounds_width; 4].into()); @@ -223,74 +230,61 @@ where // calc slice let slice = MenuSlice::new( - &items_node, - children_position-Point::ORIGIN, - viewport.size(), - menu_state.scroll_offset + &items_node, + children_position - Point::ORIGIN, + viewport.size(), + menu_state.scroll_offset, ); menu_state.slice = slice; - - let slice_node = if slice.start_index == slice.end_index{ + + let slice_node = if slice.start_index == slice.end_index { let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; let factor = ((bounds.height - start_offset) / bounds.height).max(0.0); - + Node::with_children( Size::new( - items_node.bounds().width, - slice.upper_bound_rel - slice.lower_bound_rel - ), - once( - scale_node_y( - node, - factor - ).translate([0.0, start_offset]) - ).collect() + items_node.bounds().width, + slice.upper_bound_rel - slice.lower_bound_rel, + ), + once(scale_node_y(node, factor).translate([0.0, start_offset])).collect(), ) - }else{ + } else { let start_node = { let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; let factor = ((bounds.height - start_offset) / bounds.height).max(0.0); - scale_node_y( - node, - factor - ).translate([0.0, start_offset]) + scale_node_y(node, factor).translate([0.0, start_offset]) }; let end_node = { let node = &items_node.children()[slice.end_index]; let bounds = node.bounds(); Node::with_children( - Size::new( - bounds.width, - slice.upper_bound_rel - bounds.y - ), - node.children().iter().map(Clone::clone).collect() - ).move_to(bounds.position()) + Size::new(bounds.width, slice.upper_bound_rel - bounds.y), + node.children().iter().map(Clone::clone).collect(), + ) + .move_to(bounds.position()) }; - + Node::with_children( Size::new( - items_node.bounds().width, - slice.upper_bound_rel - slice.lower_bound_rel - ), + items_node.bounds().width, + slice.upper_bound_rel - slice.lower_bound_rel, + ), once(start_node) - .chain( - items_node.children()[ - slice.start_index + 1 .. slice.end_index - ] - .iter() - .map(Clone::clone) - ) - .chain(once(end_node)) - .collect() + .chain( + items_node.children()[slice.start_index + 1..slice.end_index] + .iter() + .map(Clone::clone), + ) + .chain(once(end_node)) + .collect(), ) }; - ( Node::with_children( Size::INFINITY, @@ -298,15 +292,13 @@ where slice_node .move_to(children_position) .translate([0.0, menu_state.scroll_offset]), // slice layout - Node::new(children_size) - .move_to(children_position), // prescroll bounds - Node::new(offset_bounds.size()) - .move_to(offset_bounds.position()), // offset boundss - Node::new(check_bounds.size()) - .move_to(check_bounds.position()), // check bounds - ].into(), + Node::new(children_size).move_to(children_position), // prescroll bounds + Node::new(offset_bounds.size()).move_to(offset_bounds.position()), // offset boundss + Node::new(check_bounds.size()).move_to(check_bounds.position()), // check bounds + ] + .into(), ), - child_direction + child_direction, ) } /// tree: Tree{menu_state, \[item_tree...]} @@ -331,11 +323,10 @@ where let menu_state = tree.state.downcast_mut::(); let slice = &menu_state.slice; - - let status = self - .items[ slice.start_index .. slice.end_index+1 ] // [item...] + + let status = self.items[slice.start_index..slice.end_index + 1] // [item...] .iter_mut() - .zip(tree.children[ slice.start_index .. slice.end_index+1 ].iter_mut()) // [item_tree...] + .zip(tree.children[slice.start_index..slice.end_index + 1].iter_mut()) // [item_tree...] .zip(slice_layout.children()) // [item_layout...] .map(|((item, tree), layout)| { item.on_event( @@ -392,48 +383,67 @@ where let slice = &menu_state.slice; let styling = theme.appearance(theme_style); - + // debug_draw(renderer, prescroll, check_bounds, offset_bounds); // draw background renderer.fill_quad( - renderer::Quad{ + renderer::Quad { bounds: pad_rectangle(prescroll, styling.menu_background_expand), border: styling.menu_border, shadow: styling.menu_shadow, - }, - styling.menu_background + }, + styling.menu_background, ); - + let mut slc = slice_layout.children(); // draw start - let Some(start) = self.items.get(slice.start_index) - else{ return }; - let Some(start_tree) = tree.children.get(slice.start_index) - else { return }; - let Some(start_layout) = slc.next() - else { return }; - + let Some(start) = self.items.get(slice.start_index) else { + return; + }; + let Some(start_tree) = tree.children.get(slice.start_index) else { + return; + }; + let Some(start_layout) = slc.next() else { + return; + }; + if slice.end_index == slice.start_index { - start.draw(start_tree, renderer, theme, style, start_layout, cursor, viewport); - }else{ - let start_bounds = start_layout.bounds(); - renderer.with_layer( - start_bounds, - |r| start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) + start.draw( + start_tree, + renderer, + theme, + style, + start_layout, + cursor, + viewport, ); - + } else { + let start_bounds = start_layout.bounds(); + renderer.with_layer(start_bounds, |r| { + start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) + }); + // draw the rest - let Some(items) = self.items.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) - else { return; }; - - let Some(trees) = tree.children.get(slice.start_index+1 .. slice.end_index.saturating_add(1)) - else { return; }; - - for ((item, tree), layout) in items.iter() // [item...].iter() - .zip(trees.iter()) // [item_tree...] - .zip(slice_layout.children().skip(1)) // [item_layout...] + let Some(items) = self + .items + .get(slice.start_index + 1..slice.end_index.saturating_add(1)) + else { + return; + }; + + let Some(trees) = tree + .children + .get(slice.start_index + 1..slice.end_index.saturating_add(1)) + else { + return; + }; + + for ((item, tree), layout) in items + .iter() + .zip(trees.iter()) + .zip(slice_layout.children().skip(1)) { item.draw(tree, renderer, theme, style, layout, cursor, &viewport); } @@ -456,7 +466,7 @@ where let slice = &menu_state.slice; menu_state.active = None; - for (i, (item, layout)) in self.items[slice.start_index .. slice.end_index + 1] + for (i, (item, layout)) in self.items[slice.start_index..slice.end_index + 1] .iter() .zip(slice_layout.children()) .enumerate() @@ -476,7 +486,7 @@ where cursor: mouse::Cursor, parent_bounds: Rectangle, prev_bounds_list: &[Rectangle], - prev: &mut Index + prev: &mut Index, ) { let mut lc = layout.children(); let _slice_layout = lc.next().unwrap(); @@ -486,8 +496,9 @@ where let open = { if cursor.is_over(prescroll) - || cursor.is_over(parent_bounds) - || cursor.is_over(offset_bounds) { + || cursor.is_over(parent_bounds) + || cursor.is_over(offset_bounds) + { true } else if prev_bounds_list.iter().any(|r| cursor.is_over(*r)) { false @@ -505,7 +516,6 @@ where menu_state.active = None; } } - } /* fn debug_draw( @@ -533,7 +543,7 @@ where ..Default::default() }, ..Default::default() - }, + }, c ); }); @@ -602,26 +612,23 @@ where pub(super) fn children(&self) -> Vec { self.menu .as_ref() - .map_or([ - Tree::new(&self.item)].into(), - |m| [ - Tree::new(&self.item), m.tree() - ].into() - ) + .map_or([Tree::new(&self.item)].into(), |m| { + [Tree::new(&self.item), m.tree()].into() + }) } /// tree: Tree{stateless, \[widget_tree, menu_tree]} pub(super) fn diff(&self, tree: &mut Tree) { if let Some(t0) = tree.children.get_mut(0) { t0.diff(&self.item); - if let Some(m) = self.menu.as_ref(){ + if let Some(m) = self.menu.as_ref() { if let Some(t1) = tree.children.get_mut(1) { m.diff(t1); - }else{ + } else { *tree = self.tree(); } } - }else{ + } else { *tree = self.tree(); } } @@ -753,16 +760,28 @@ impl Aod { if overlap { let overshoot = child_size - parent_size; if space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - overshoot, direction.flip()) + ( + parent_pos - overshoot, + parent_pos - overshoot, + direction.flip(), + ) } else { (parent_pos, parent_pos, direction) } } else { let overshoot = child_size + offset; if space_negative > space_positive && overshoot > space_positive { - (parent_pos - overshoot, parent_pos - offset, direction.flip()) + ( + parent_pos - overshoot, + parent_pos - offset, + direction.flip(), + ) } else { - (parent_pos + parent_size + offset, parent_pos + parent_size, direction) + ( + parent_pos + parent_size + offset, + parent_pos + parent_size, + direction, + ) } } } @@ -780,7 +799,11 @@ impl Aod { } else { let overshoot = child_size + offset; if space_negative > space_positive && overshoot > space_positive { - (parent_pos + parent_size + offset, parent_pos + parent_size, direction.flip()) + ( + parent_pos + parent_size + offset, + parent_pos + parent_size, + direction.flip(), + ) } else { (parent_pos - overshoot, parent_pos - offset, direction) } @@ -824,7 +847,7 @@ impl Aod { parent_bounds: Rectangle, parent_direction: (Direction, Direction), offset: f32, - ) -> Self{ + ) -> Self { let hcenter = viewport.width / 2.0; let vcenter = viewport.height / 2.0; @@ -833,7 +856,7 @@ impl Aod { let (pdx, pdy) = parent_direction; match axis { - Axis::Horizontal =>{ + Axis::Horizontal => { let horizontal_direction = pdx; let vertical_direction = if pvcenter < vcenter { Direction::Positive @@ -848,7 +871,7 @@ impl Aod { horizontal_offset: offset, vertical_offset: 0.0, } - }, + } Axis::Vertical => { let horizontal_direction = if phcenter < hcenter { Direction::Positive @@ -864,18 +887,17 @@ impl Aod { horizontal_offset: 0.0, vertical_offset: offset, } - }, + } } } } - fn process_scroll_event( menu_state: &mut MenuState, prescroll_children_bounds: Rectangle, delta: mouse::ScrollDelta, viewport_size: Size, -){ +) { use mouse::ScrollDelta; let pcb = prescroll_children_bounds; @@ -896,40 +918,26 @@ pub(super) struct MenuSlice { pub(super) lower_bound_rel: f32, pub(super) upper_bound_rel: f32, } -impl MenuSlice{ - fn new( - items_node: &Node, - translation: Vector, - viewport: Size, - scroll_offset: f32, - ) -> Self { +impl MenuSlice { + fn new(items_node: &Node, translation: Vector, viewport: Size, scroll_offset: f32) -> Self { let items_bounds = items_node.bounds() + translation; let max_index = items_node.children().len().saturating_sub(1); - + // viewport space absolute bounds let lower_bound = items_bounds.y.max(0.0); let upper_bound = (items_bounds.y + items_bounds.height).min(viewport.height); - + // menu space relative bounds let lower_bound_rel = lower_bound - (items_bounds.y + scroll_offset); let upper_bound_rel = upper_bound - (items_bounds.y + scroll_offset); - + // let start_index = search_bound_lin(lower_bound_rel, items_node.children(), 0); // let end_index = search_bound_lin(upper_bound_rel, items_node.children(), start_index); - let start_index = search_bound( - 0, - max_index, - lower_bound_rel, - items_node.children(), - ); - let end_index = search_bound( - start_index, - max_index, - upper_bound_rel, - items_node.children(), - ); - + let nodes = items_node.children(); + let start_index = search_bound(0, max_index, lower_bound_rel, nodes); + let end_index = search_bound(start_index, max_index, upper_bound_rel, nodes); + Self { start_index, end_index, @@ -954,12 +962,7 @@ impl MenuSlice{ start_index } */ -fn search_bound( - default_left: usize, - default_right: usize, - bound: f32, - list: &[Node], -) -> usize { +fn search_bound(default_left: usize, default_right: usize, bound: f32, list: &[Node]) -> usize { // binary search let mut left = default_left; let mut right = default_right; @@ -975,23 +978,17 @@ fn search_bound( left } -fn scale_node_y( - node: &Node, - factor: f32, -) -> Node{ +fn scale_node_y(node: &Node, factor: f32) -> Node { let node_bounds = node.bounds(); Node::with_children( - Size::new( - node_bounds.width, - node_bounds.height * factor - ), - node.children().iter().map(|n|{ - let n_bounds = n.bounds(); - scale_node_y(n, factor) - .move_to(Point::new( - n_bounds.x, - n_bounds.y * factor - )) - }).collect() - ).move_to(node_bounds.position()) + Size::new(node_bounds.width, node_bounds.height * factor), + node.children() + .iter() + .map(|n| { + let n_bounds = n.bounds(); + scale_node_y(n, factor).move_to(Point::new(n_bounds.x, n_bounds.y * factor)) + }) + .collect(), + ) + .move_to(node_bounds.position()) } diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 4fd6680e..711422c7 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -1,9 +1,7 @@ //! Change the appearance of menu bars and their menus. use iced_widget::{ - core::{ - Color, Border, Background, Shadow, Padding, Vector - }, - style::Theme + core::{Background, Border, Color, Padding, Shadow, Vector}, + style::Theme, }; /// The appearance of a menu bar and its menus. @@ -31,7 +29,7 @@ impl std::default::Default for Appearance { fn default() -> Self { Self { bar_background: Color::from([0.85; 3]).into(), - bar_border: Border{ + bar_border: Border { radius: [8.0; 4].into(), ..Default::default() }, @@ -39,11 +37,11 @@ impl std::default::Default for Appearance { bar_background_expand: [5; 4].into(), menu_background: Color::from([0.85; 3]).into(), - menu_border: Border{ + menu_border: Border { radius: [8.0; 4].into(), ..Default::default() }, - menu_shadow: Shadow{ + menu_shadow: Shadow { color: Color::from([0.0, 0.0, 0.0, 0.5]), offset: Vector::ZERO, blur_radius: 10.0, From 1a288691559508c9263be86ddbf678c8396c29f9 Mon Sep 17 00:00:00 2001 From: genusistimelord Date: Wed, 21 Feb 2024 09:58:50 -0500 Subject: [PATCH 24/40] Fix #77 --- .vscode/launch.json | 18 +++++++++++ Cargo.toml | 1 + src/native/floating_element.rs | 56 ++++++++++++++++++++-------------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 49979230..90068c35 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -202,6 +202,24 @@ "args": [], "cwd": "${workspaceFolder}" }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'floating_element_overlay'", + "cargo": { + "args": [ + "build", + "--bin=floating_element_overlay", + "--package=floating_element_overlay" + ], + "filter": { + "name": "floating_element_overlay", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, { "type": "lldb", "request": "launch", diff --git a/Cargo.toml b/Cargo.toml index 4719b325..7e14a24a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ members = [ "examples/date_picker", "examples/color_picker", "examples/floating_element", + "examples/floating_element multioverlay", "examples/floating_element_anchors", "examples/grid", "examples/modal", diff --git a/src/native/floating_element.rs b/src/native/floating_element.rs index a8883269..2bc6fe96 100644 --- a/src/native/floating_element.rs +++ b/src/native/floating_element.rs @@ -7,7 +7,8 @@ use super::overlay::floating_element::FloatingElementOverlay; use iced::{ advanced::{ layout::{Limits, Node}, - overlay, renderer, + overlay::{self, Group}, + renderer, widget::{Operation, Tree}, Clipboard, Layout, Shell, Widget, }, @@ -212,31 +213,40 @@ where renderer: &Renderer, translation: Vector, ) -> Option> { - if self.hidden { - return self.underlay.as_widget_mut().overlay( - &mut state.children[0], - layout, - renderer, - translation, - ); + let mut group = Group::new(); + let mut children = state.children.iter_mut(); + + if let Some(underlay) = self.underlay + .as_widget_mut() + .overlay( + children + .next() + .expect("missing underlay in floating element"), + layout, + renderer, + translation, + ) { + group = group.push(underlay); } - if state.children.len() == 2 { - let bounds = layout.bounds(); - - Some(overlay::Element::new(Box::new( - FloatingElementOverlay::new( - layout.position() + translation, - &mut state.children[1], - &mut self.element, - &self.anchor, - &self.offset, - bounds, - ), - ))) - } else { - None + if !self.hidden { + if let Some(el) = children.next() { + let bounds = layout.bounds(); + + group = group.push(overlay::Element::new(Box::new( + FloatingElementOverlay::new( + layout.position() + translation, + el, + &mut self.element, + &self.anchor, + &self.offset, + bounds, + ), + ))); + } } + + Some(group.overlay()) } } From ca9044bf82d884538f4461fb22f2c0750a608979 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Thu, 22 Feb 2024 00:29:49 +0800 Subject: [PATCH 25/40] fix --- Cargo.toml | 2 +- examples/menu/src/main.rs | 42 +++++++++++++++++------------ src/native/menu.rs | 5 +++- src/native/menu/common.rs | 2 +- src/native/menu/flex.rs | 12 ++++++--- src/native/menu/menu_bar.rs | 16 +++++++---- src/native/menu/menu_bar_overlay.rs | 35 +++++++++++++++++------- src/native/menu/menu_tree.rs | 17 +++++++----- src/style/menu_bar.rs | 6 ++--- 9 files changed, 89 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7e14a24a..9b4c1248 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ members = [ "examples/date_picker", "examples/color_picker", "examples/floating_element", - "examples/floating_element multioverlay", + # "examples/floating_element multioverlay", "examples/floating_element_anchors", "examples/grid", "examples/modal", diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 30ef1ab8..6afda936 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -1,5 +1,5 @@ use iced::widget::{ - button, checkbox, container, horizontal_space, row, scrollable, slider, svg, text, text_input, + button, checkbox, container, horizontal_space, row, scrollable, slider, text, text_input, toggler, vertical_slider, }; use iced::widget::{column as col, vertical_space}; @@ -9,7 +9,7 @@ use iced::{ use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; use iced_aw::menu::{Item, Menu}; -use iced_aw::quad; +use iced_aw::{quad, native::InnerBounds}; use iced_aw::{menu_bar, menu_items}; pub fn main() -> iced::Result { @@ -140,8 +140,8 @@ impl Application for App { } fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { - let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); - let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); + let menu_temp_1 = |items| {Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0)}; + let menu_temp_2 = |items| {Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0)}; let mb = menu_bar!((debug_button_s("Nested Menus"), { let sub5 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( @@ -201,10 +201,11 @@ impl Application for App { .width(Length::Fill) .on_press(Message::Debug("Button".into())) )( - checkbox("Checkbox", self.check, Message::CheckChange).width(Length::Fill) + // checkbox("Checkbox", self.check, Message::CheckChange).width(Length::Fill) + checkbox("Checkbox", self.check).on_toggle(|x| Message::CheckChange(x)).width(Length::Fill) )(row![ "Slider", - horizontal_space(Length::Fixed(8.0)), + horizontal_space().width(Length::Fixed(8.0)), slider(0..=255, self.value, Message::ValueChange) ])( text_input("", &self.text).on_input(Message::TextChange) @@ -412,14 +413,21 @@ impl Application for App { ))) .width(slider_width * slider_count + (slider_count - 1) * spacing + pad) })); - - let r = row![horizontal_space(295), mb, horizontal_space(295),] - .align_items(alignment::Alignment::Center); - - let c = col![vertical_space(500), r, vertical_space(500),]; + + let r = row![ + horizontal_space().width(295), + mb, + horizontal_space().width(295), + ].align_items(alignment::Alignment::Center); + + let c = col![ + vertical_space().height(500), + r, + vertical_space().height(500), + ]; let sc = scrollable(c).direction(scrollable::Direction::Both { - vertical: scrollable::Properties::new().alignment(scrollable::Alignment::End), + vertical: scrollable::Properties::new(), horizontal: scrollable::Properties::new(), }); @@ -522,7 +530,7 @@ fn separator<'a>() -> quad::Quad { quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], - inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), + inner_bounds: InnerBounds::Ratio(0.98, 0.1), height: Length::Fixed(30.0), ..Default::default() } @@ -533,7 +541,7 @@ fn dot_separator<'a>(theme: &iced::Theme) -> Element<'a, Message, iced::Theme, i quad::Quad { color: theme.extended_palette().background.base.text, border_radius: [4.0; 4], - inner_bounds: quad::InnerBounds::Square(4.0), + inner_bounds: InnerBounds::Square(4.0), ..Default::default() } .into() @@ -546,13 +554,13 @@ fn labeled_separator(label: &'_ str) -> Element<'_, Message, iced::Theme, iced:: let q_1 = quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], - inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), + inner_bounds: InnerBounds::Ratio(0.98, 0.1), ..Default::default() }; let q_2 = quad::Quad { color: [0.5; 3].into(), border_radius: [4.0; 4], - inner_bounds: quad::InnerBounds::Ratio(0.98, 0.1), + inner_bounds: InnerBounds::Ratio(0.98, 0.1), ..Default::default() }; @@ -572,7 +580,7 @@ fn circle(color: Color) -> quad::Quad { quad::Quad { color, - inner_bounds: quad::InnerBounds::Square(radius * 2.0), + inner_bounds: InnerBounds::Square(radius * 2.0), border_radius: [radius; 4], height: 20.0.into(), ..Default::default() diff --git a/src/native/menu.rs b/src/native/menu.rs index 04adc8b2..ebe80ac8 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -88,7 +88,7 @@ //! template function is one way to do that. If you find writing menu_template(menu_items!()) cumbersome, //! there is a menu! macro you can use to create template macros //! -//! # Example 3 +//! ## Example 3 //! //! ``` //! use iced_aw::{menu}; @@ -114,6 +114,9 @@ //! Technically You can create menu template functions with the menu! macro, //! but turns out closures can't infer the generic types, //! and creating a function for it involves writing a ton of generic annotations +//! +//! ## Example 3 +//! //! ``` //! fn menu_template<'a, Message, Theme, Renderer>( //! menu: Menu<'a, Message, Theme, Renderer> diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index 7fd29e57..8170e6ab 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -1,4 +1,4 @@ -use iced_widget::core::{Padding, Rectangle}; +use iced::{Padding, Rectangle}; /* /// The condition of when to close a menu #[derive(Debug, Clone, Copy)] diff --git a/src/native/menu/flex.rs b/src/native/menu/flex.rs index 6a6a565e..68fb7ced 100644 --- a/src/native/menu/flex.rs +++ b/src/native/menu/flex.rs @@ -16,9 +16,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use iced_widget::core::{ - layout::{Limits, Node}, - widget, Alignment, Element, Length, Padding, Point, Size, +use iced::{ + advanced::{ + layout::{Limits, Node}, + renderer, + widget, + }, + Alignment, Element, Length, Padding, Point, Size, }; /// The main axis of a flex layout. @@ -74,7 +78,7 @@ where E: std::borrow::Borrow>, T: std::borrow::BorrowMut, - Renderer: iced_widget::core::Renderer, + Renderer: renderer::Renderer, { let limits = limits.width(width).height(height).shrink(padding); let total_spacing = spacing * items.len().saturating_sub(1) as f32; diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index b7f269a2..87ee9bd5 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,14 +1,17 @@ //! [`MenuBar`] -use iced_widget::{ - core::{ - alignment, event, +use iced::{ + advanced::{ layout::{Limits, Node}, mouse, overlay, renderer, widget::{tree, Tree}, - Alignment, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Point, - Rectangle, Shell, Size, Widget, + Clipboard, Shell, Layout, + Overlay, Widget, }, + alignment, event, + + Alignment, Color, Element, Event, Length, Padding, Point, + Rectangle, Size, Theme, }; @@ -288,6 +291,7 @@ where tree: &'b mut Tree, layout: Layout<'_>, _renderer: &Renderer, + translation: iced::Vector, ) -> Option> { let state = tree.state.downcast_mut::(); @@ -297,6 +301,7 @@ where if state.open { Some( MenuBarOverlay { + translation, tree, roots: &mut self.roots, init_bar_bounds, @@ -310,6 +315,7 @@ where None } } + } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 5bbbb9f5..b909296a 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -1,10 +1,18 @@ -use iced_widget::core::{ +use iced::{ + advanced::{ + layout::{Limits, Node}, + renderer, + mouse, + overlay, + widget::Tree, + Clipboard, + Shell, + Layout, + Widget, + }, event, - layout::{Limits, Node}, - mouse, overlay, renderer, - widget::tree::Tree, - Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Point, Rectangle, - Shell, Size, Vector, Widget, + Alignment, Border, Color, Element, Event, Length, Padding, Point, Rectangle, + Size, Vector, }; use super::{common::*, menu_bar::MenuBarState, menu_tree::*}; @@ -24,6 +32,7 @@ where Renderer: renderer::Renderer, { /// Tree{ bar_state, [item_tree...] } + pub(super) translation: Vector, pub(super) tree: &'b mut Tree, pub(super) roots: &'b mut [Item<'a, Message, Theme, Renderer>], @@ -38,7 +47,7 @@ where Renderer: renderer::Renderer, { pub(super) fn overlay_element(self) -> overlay::Element<'b, Message, Theme, Renderer> { - overlay::Element::new(Point::ORIGIN, Box::new(self)) + overlay::Element::new(Box::new(self)) } } impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay @@ -51,9 +60,11 @@ where &mut self, renderer: &Renderer, bounds: Size, - position: Point, - translation: Vector, + // position: Point, + // translation: Vector, ) -> Node { + let translation = self.translation; + let bar = self.tree.state.downcast_ref::(); let bar_bounds = self.init_bar_bounds; @@ -162,7 +173,11 @@ where self.check_bounds_width, parent_bounds, parent_direction, - &Rectangle::new(position, bounds), + &Rectangle::new( + // Point::new(translation.x, translation.y), + Point::ORIGIN, + bounds + ), ); Node::with_children( diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 66e67f04..8d7d0427 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -2,13 +2,18 @@ //! use super::common::*; use super::flex; -use iced_widget::core::{ +use iced::{ + advanced::{ + layout::{Layout, Limits, Node}, + mouse, overlay, renderer, + widget::tree::{self, Tree}, + Clipboard, + Shell, + Widget, + }, alignment, event, - layout::{Layout, Limits, Node}, - mouse, overlay, renderer, - widget::tree::{self, Tree}, - Alignment, Border, Clipboard, Color, Element, Event, Length, Padding, Point, Rectangle, Shell, - Size, Vector, Widget, + Alignment, Border, Color, Element, Event, Length, Padding, Point, Rectangle, + Size, Vector, }; use std::iter::once; diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 711422c7..a54048db 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -1,9 +1,9 @@ //! Change the appearance of menu bars and their menus. -use iced_widget::{ - core::{Background, Border, Color, Padding, Shadow, Vector}, - style::Theme, +use iced::{ + Background, Border, Color, Padding, Shadow, Vector, Theme, }; + /// The appearance of a menu bar and its menus. #[derive(Debug, Clone, Copy)] pub struct Appearance { From dfe5910ee286b442e6c882feb21fa2b894c51d99 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Thu, 22 Feb 2024 11:51:50 +0800 Subject: [PATCH 26/40] fmt and clippy --- examples/menu/src/main.rs | 27 +++++---- src/native/menu.rs | 18 +++--- src/native/menu/common.rs | 6 +- src/native/menu/flex.rs | 8 ++- src/native/menu/menu_bar.rs | 49 +++++++---------- src/native/menu/menu_bar_overlay.rs | 45 +++++++-------- src/native/menu/menu_tree.rs | 85 ++++++++++++++++------------- src/style/menu_bar.rs | 5 +- 8 files changed, 126 insertions(+), 117 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 6afda936..1fd0701b 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -4,13 +4,13 @@ use iced::widget::{ }; use iced::widget::{column as col, vertical_space}; use iced::{ - alignment, theme, Alignment, Application, Border, Color, Element, Length, Pixels, Size, + alignment, theme, Application, Border, Color, Element, Length, Pixels, Size, }; use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; use iced_aw::menu::{Item, Menu}; -use iced_aw::{quad, native::InnerBounds}; use iced_aw::{menu_bar, menu_items}; +use iced_aw::{native::InnerBounds, quad}; pub fn main() -> iced::Result { // std::env::set_var("RUST_BACKTRACE", "full"); @@ -140,8 +140,8 @@ impl Application for App { } fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { - let menu_temp_1 = |items| {Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0)}; - let menu_temp_2 = |items| {Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0)}; + let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); + let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); let mb = menu_bar!((debug_button_s("Nested Menus"), { let sub5 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( @@ -202,7 +202,9 @@ impl Application for App { .on_press(Message::Debug("Button".into())) )( // checkbox("Checkbox", self.check, Message::CheckChange).width(Length::Fill) - checkbox("Checkbox", self.check).on_toggle(|x| Message::CheckChange(x)).width(Length::Fill) + checkbox("Checkbox", self.check) + .on_toggle(|x| Message::CheckChange(x)) + .width(Length::Fill) )(row![ "Slider", horizontal_space().width(Length::Fixed(8.0)), @@ -413,21 +415,22 @@ impl Application for App { ))) .width(slider_width * slider_count + (slider_count - 1) * spacing + pad) })); - + let r = row![ - horizontal_space().width(295), - mb, horizontal_space().width(295), - ].align_items(alignment::Alignment::Center); - + mb, + horizontal_space().width(295), + ] + .align_items(alignment::Alignment::Center); + let c = col![ vertical_space().height(500), - r, + r, vertical_space().height(500), ]; let sc = scrollable(c).direction(scrollable::Direction::Both { - vertical: scrollable::Properties::new(), + vertical: scrollable::Properties::new().alignment(scrollable::Alignment::End), horizontal: scrollable::Properties::new(), }); diff --git a/src/native/menu.rs b/src/native/menu.rs index ebe80ac8..dd3d914d 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -1,3 +1,5 @@ +#![allow(clippy::doc_markdown)] + //! A [`MenuBar`] widget for displaying menu trees created with [`Item`]s and [`Menu`]s //! //! *This API requires the following crate features to be activated: `menu`* @@ -84,9 +86,11 @@ //! ) //! ``` //! -//! Notice a menu_template function/closure is used in example 2. Usually some properties are sync across all menus while others are not, -//! template function is one way to do that. If you find writing menu_template(menu_items!()) cumbersome, -//! there is a menu! macro you can use to create template macros +//! Here a menu_template function/closure is used in example 2, +//! usually some properties are sync across all menus while others are not, +//! using template functions can reduce the repeated code. +//! If you find writing menu_template(menu_items!()) too cumbersome, +//! there's a menu! macro you can use to create template macros //! //! ## Example 3 //! @@ -111,11 +115,11 @@ //! ); //! ``` //! -//! Technically You can create menu template functions with the menu! macro, -//! but turns out closures can't infer the generic types, -//! and creating a function for it involves writing a ton of generic annotations +//! Technically you can create menu template functions with the menu! macro, +//! but turns out closures can't infer the proper generic types in this case, +//! and creating a function for it you have to write a bunch of generic annotations //! -//! ## Example 3 +//! ## Example 4 //! //! ``` //! fn menu_template<'a, Message, Theme, Renderer>( diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index 8170e6ab..a24ff67d 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -21,10 +21,10 @@ pub(super) enum Direction { Negative, } impl Direction { - pub(super) fn flip(&self) -> Direction { + pub(super) fn flip(self) -> Self { match self { - Direction::Positive => Direction::Negative, - Direction::Negative => Direction::Positive, + Self::Positive => Self::Negative, + Self::Negative => Self::Positive, } } } diff --git a/src/native/menu/flex.rs b/src/native/menu/flex.rs index 68fb7ced..2b5a79c7 100644 --- a/src/native/menu/flex.rs +++ b/src/native/menu/flex.rs @@ -16,11 +16,15 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +#![allow(clippy::all)] +#![allow(clippy::pedantic)] +#![allow(clippy::use_self)] + use iced::{ advanced::{ layout::{Limits, Node}, - renderer, - widget, + renderer, widget, }, Alignment, Element, Length, Padding, Point, Size, }; diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 87ee9bd5..fd3a7619 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -1,39 +1,32 @@ //! [`MenuBar`] +#![allow(clippy::unwrap_used)] +#![allow(clippy::doc_markdown)] +#![allow(clippy::wildcard_imports)] +#![allow(clippy::enum_glob_use)] + use iced::{ advanced::{ layout::{Limits, Node}, mouse, overlay, renderer, widget::{tree, Tree}, - Clipboard, Shell, Layout, - Overlay, Widget, + Clipboard, Layout, Shell, Widget, }, - alignment, event, - - Alignment, Color, Element, Event, Length, Padding, Point, - Rectangle, Size, - Theme, -}; + alignment, event, Element, Event, Length, Padding, Rectangle, Size, + }; use super::{common::*, flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*}; use crate::style::menu_bar::*; +#[derive(Default)] pub(super) struct MenuBarState { pub(super) active_root: Index, pub(super) open: bool, pub(super) is_pressed: bool, } -impl Default for MenuBarState { - fn default() -> Self { - Self { - active_root: None, - open: false, - is_pressed: false, - } - } -} /// menu bar +#[must_use] pub struct MenuBar<'a, Message, Theme, Renderer> where Theme: StyleSheet, @@ -54,11 +47,12 @@ where { /// Creates a [`MenuBar`] with the given root items. pub fn new(mut roots: Vec>) -> Self { - roots.iter_mut().for_each(|i| { + for i in &mut roots { if let Some(m) = i.menu.as_mut() { m.axis = Axis::Vertical; } - }); + } + Self { roots, spacing: 0.0, @@ -121,14 +115,14 @@ where } fn state(&self) -> tree::State { - tree::State::Some(Box::new(MenuBarState::default())) + tree::State::Some(Box::::default()) } /// \[Tree{stateless, \[widget_state, menu_state]}...] fn children(&self) -> Vec { self.roots .iter() - .map(|item| item.tree()) + .map(Item::tree) .collect::>() } @@ -137,8 +131,8 @@ where tree.diff_children_custom( &self.roots, |tree, item| item.diff(tree), - |item| item.tree(), - ) + Item::tree, + ); } /// tree: Tree{bar_state, \[item_tree...]} @@ -191,7 +185,7 @@ where viewport, ) }) - .fold(Ignored, |acc, x| acc.merge(x)); + .fold(Ignored, event::Status::merge); let bar = tree.state.downcast_mut::(); let bar_bounds = layout.bounds(); @@ -230,7 +224,7 @@ where } } } else { - bar.open = false + bar.open = false; } Captured } else { @@ -260,7 +254,7 @@ where layout .children() .nth(active) - .and_then(|l| Some(mouse::Cursor::Available(l.bounds().center()))) + .map(|l| mouse::Cursor::Available(l.bounds().center())) }) .unwrap_or(cursor) } else { @@ -282,7 +276,7 @@ where .zip(tree.children.iter()) // [item_tree...] .zip(layout.children()) // [widget_node...] .for_each(|((item, tree), layout)| { - item.draw(tree, renderer, theme, style, layout, cursor, viewport) + item.draw(tree, renderer, theme, style, layout, cursor, viewport); }); } @@ -315,7 +309,6 @@ where None } } - } impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index b909296a..5fee0397 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -1,18 +1,19 @@ +#![allow(clippy::unwrap_used)] +#![allow(clippy::doc_markdown)] +#![allow(clippy::wildcard_imports)] +#![allow(clippy::enum_glob_use)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::items_after_statements)] +#![allow(clippy::similar_names)] + use iced::{ advanced::{ layout::{Limits, Node}, - renderer, - mouse, - overlay, + mouse, overlay, renderer, widget::Tree, - Clipboard, - Shell, - Layout, - Widget, - }, - event, - Alignment, Border, Color, Element, Event, Length, Padding, Point, Rectangle, - Size, Vector, + Clipboard, Layout, Shell, }, + event, Event, Point, Rectangle, Size, + Vector, }; use super::{common::*, menu_bar::MenuBarState, menu_tree::*}; @@ -88,9 +89,9 @@ where let active_tree = &mut self.tree.children[active]; // item_tree: Tree{ stateless, [ widget_tree, menu_tree ] } let parent_bounds = self.init_root_bounds[active] + translation; - fn rec<'a, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( + fn rec( renderer: &Renderer, - item: &Item<'a, Message, Theme, Renderer>, + item: &Item<'_, Message, Theme, Renderer>, tree: &mut Tree, menu_nodes: &mut Vec, check_bounds_width: f32, @@ -175,8 +176,8 @@ where parent_direction, &Rectangle::new( // Point::new(translation.x, translation.y), - Point::ORIGIN, - bounds + Point::ORIGIN, + bounds, ), ); @@ -227,7 +228,7 @@ where fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( tree: &mut Tree, item: &mut Item<'a, Message, Theme, Renderer>, - event: Event, + event: &Event, layout_iter: &mut impl Iterator>, cursor: mouse::Cursor, renderer: &Renderer, @@ -270,7 +271,7 @@ where let re = rec( next_tree, next_item, - event.clone(), + event, layout_iter, cursor, renderer, @@ -378,7 +379,7 @@ where let re = rec( active_tree, active_root, - event, + &event, &mut menu_layouts, cursor, renderer, @@ -455,7 +456,7 @@ where menu_layout, cursor, viewport, - ) + ); }; let menu_state = menu_tree.state.downcast_ref::(); @@ -493,10 +494,10 @@ where style, theme_style, viewport, - ) + ); }); } else { - draw_menu(cursor) + draw_menu(cursor); } } @@ -510,7 +511,7 @@ where style, self.style, &viewport, - ) + ); } fn is_over(&self, _layout: Layout<'_>, _renderer: &Renderer, _cursor_position: Point) -> bool { diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 8d7d0427..43074299 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -1,19 +1,25 @@ //! [`Item`] and [`Menu`] //! +#![allow(clippy::unwrap_used)] +#![allow(clippy::doc_markdown)] +#![allow(clippy::wildcard_imports)] +#![allow(clippy::enum_glob_use)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::unused_self)] +#![allow(clippy::return_self_not_must_use)] +#![allow(clippy::pedantic)] +#![allow(clippy::similar_names)] + use super::common::*; use super::flex; use iced::{ advanced::{ layout::{Layout, Limits, Node}, - mouse, overlay, renderer, + mouse, renderer, widget::tree::{self, Tree}, - Clipboard, - Shell, - Widget, - }, - alignment, event, - Alignment, Border, Color, Element, Event, Length, Padding, Point, Rectangle, - Size, Vector, + Clipboard, Shell, }, + alignment, event, Element, Event, Length, Padding, Point, Rectangle, + Size, Vector, }; use std::iter::once; @@ -72,6 +78,7 @@ impl Default for MenuState { } /// Menu +#[must_use] pub struct Menu<'a, Message, Theme, Renderer> where Theme: StyleSheet, @@ -86,7 +93,6 @@ where pub(super) axis: Axis, pub(super) offset: f32, } -#[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Menu<'a, Message, Theme, Renderer> where Theme: StyleSheet, @@ -136,6 +142,7 @@ where self } + /// rebuild state tree pub(super) fn tree(&self) -> Tree { Tree { tag: self.tag(), @@ -149,21 +156,21 @@ where Theme: StyleSheet, Renderer: renderer::Renderer, { - pub(super) fn size(&self) -> Size { - Size::new(self.width, self.height) - } + // pub(super) fn size(&self) -> Size { + // Size::new(self.width, self.height) + // } pub(super) fn tag(&self) -> tree::Tag { tree::Tag::of::() } pub(super) fn state(&self) -> tree::State { - tree::State::Some(Box::new(MenuState::default())) + tree::State::Some(Box::::default()) } /// out: \[item_tree...] pub(super) fn children(&self) -> Vec { - self.items.iter().map(|i| i.tree()).collect() + self.items.iter().map(Item::tree).collect() } /// tree: Tree{menu_state, \[item_tree...]} @@ -171,11 +178,11 @@ where tree.diff_children_custom( &self.items, |tree, item| item.diff(tree), - |item| item.tree(), - ) + Item::tree, + ); } - /// tree: Tree{menu_state, \[item_tree...]} + /// tree: Tree{ menu_state, \[item_tree...] } /// /// out: Node{inf, \[ items_node, prescroll, offset_bounds, check_bounds ]} pub(super) fn layout( @@ -306,13 +313,13 @@ where child_direction, ) } - /// tree: Tree{menu_state, \[item_tree...]} + /// tree: Tree{ menu_state, \[item_tree...] } /// /// layout: Node{inf, \[ slice_node, prescroll, offset_bounds, check_bounds ]} pub(super) fn on_event( &mut self, tree: &mut Tree, - event: Event, + event: &Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, @@ -320,6 +327,8 @@ where shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) -> event::Status { + use event::Status::*; + let mut lc = layout.children(); let slice_layout = lc.next().unwrap(); let prescroll = lc.next().unwrap().bounds(); @@ -329,9 +338,9 @@ where let menu_state = tree.state.downcast_mut::(); let slice = &menu_state.slice; - let status = self.items[slice.start_index..slice.end_index + 1] // [item...] + let status = self.items[slice.start_index..=slice.end_index] // [item...] .iter_mut() - .zip(tree.children[slice.start_index..slice.end_index + 1].iter_mut()) // [item_tree...] + .zip(tree.children[slice.start_index..=slice.end_index].iter_mut()) // [item_tree...] .zip(slice_layout.children()) // [item_layout...] .map(|((item, tree), layout)| { item.on_event( @@ -345,13 +354,12 @@ where viewport, ) }) - .fold(event::Status::Ignored, event::Status::merge); - - use event::Status::*; + .fold(Ignored, event::Status::merge); + match event { Event::Mouse(mouse::Event::WheelScrolled { delta }) => { if cursor.is_over(prescroll) { - process_scroll_event(menu_state, prescroll, delta, viewport.size()); + process_scroll_event(menu_state, prescroll, *delta, viewport.size()); Captured } else if cursor.is_over(offset_bounds) || cursor.is_over(check_bounds) { Captured @@ -427,7 +435,7 @@ where } else { let start_bounds = start_layout.bounds(); renderer.with_layer(start_bounds, |r| { - start.draw(start_tree, r, theme, style, start_layout, cursor, viewport) + start.draw(start_tree, r, theme, style, start_layout, cursor, viewport); }); // draw the rest @@ -450,7 +458,7 @@ where .zip(trees.iter()) .zip(slice_layout.children().skip(1)) { - item.draw(tree, renderer, theme, style, layout, cursor, &viewport); + item.draw(tree, renderer, theme, style, layout, cursor, viewport); } } } @@ -471,7 +479,7 @@ where let slice = &menu_state.slice; menu_state.active = None; - for (i, (item, layout)) in self.items[slice.start_index..slice.end_index + 1] + for (i, (item, layout)) in self.items[slice.start_index..=slice.end_index] .iter() .zip(slice_layout.children()) .enumerate() @@ -507,10 +515,8 @@ where true } else if prev_bounds_list.iter().any(|r| cursor.is_over(*r)) { false - } else if cursor.is_over(check_bounds) { - true } else { - false + cursor.is_over(check_bounds) } }; @@ -555,6 +561,7 @@ where } */ /// Item inside a [`Menu`] +#[must_use] pub struct Item<'a, Message, Theme, Renderer> where Theme: StyleSheet, @@ -563,7 +570,6 @@ where pub(super) item: Element<'a, Message, Theme, Renderer>, pub(super) menu: Option>>, } -#[allow(missing_docs)] impl<'a, Message, Theme, Renderer> Item<'a, Message, Theme, Renderer> where Theme: StyleSheet, @@ -588,6 +594,7 @@ where } } + /// rebuild state tree pub(super) fn tree(&self) -> Tree { Tree { tag: self.tag(), @@ -601,9 +608,9 @@ where Theme: StyleSheet, Renderer: renderer::Renderer, { - pub(super) fn size(&self) -> Size { - self.item.as_widget().size() - } + // pub(super) fn size(&self) -> Size { + // self.item.as_widget().size() + // } pub(super) fn tag(&self) -> tree::Tag { tree::Tag::stateless() @@ -623,6 +630,7 @@ where } /// tree: Tree{stateless, \[widget_tree, menu_tree]} + #[allow(clippy::option_if_let_else)] pub(super) fn diff(&self, tree: &mut Tree) { if let Some(t0) = tree.children.get_mut(0) { t0.diff(&self.item); @@ -696,7 +704,7 @@ where layout, cursor, viewport, - ) + ); } } @@ -718,7 +726,6 @@ struct Aod { } impl Aod { /// Returns (child position, offset position, child direction) - #[allow(clippy::too_many_arguments)] fn adaptive( parent_pos: f32, parent_size: f32, @@ -868,7 +875,7 @@ impl Aod { } else { Direction::Negative }; - Aod { + Self { horizontal_overlap: false, vertical_overlap: true, horizontal_direction, @@ -884,7 +891,7 @@ impl Aod { Direction::Negative }; let vertical_direction = pdy; - Aod { + Self { horizontal_overlap: true, vertical_overlap: false, horizontal_direction, diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index a54048db..2fc74e44 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -1,8 +1,5 @@ //! Change the appearance of menu bars and their menus. -use iced::{ - Background, Border, Color, Padding, Shadow, Vector, Theme, -}; - +use iced::{Background, Border, Color, Padding, Shadow, Theme, Vector}; /// The appearance of a menu bar and its menus. #[derive(Debug, Clone, Copy)] From dbb92640e1937f49826b01c713cd866f8f27f8a0 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Thu, 22 Feb 2024 13:07:02 +0800 Subject: [PATCH 27/40] docs --- examples/menu/src/main.rs | 34 ++++++++++++++--------------- src/native/menu/menu_bar_overlay.rs | 3 --- src/native/menu/menu_tree.rs | 16 ++++---------- src/style/menu_bar.rs | 5 ----- 4 files changed, 21 insertions(+), 37 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 1fd0701b..2451821c 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -140,22 +140,22 @@ impl Application for App { } fn view(&self) -> iced::Element<'_, Self::Message, iced::Theme, iced::Renderer> { - let menu_temp_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); - let menu_temp_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); + let menu_tpl_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); + let menu_tpl_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); let mb = menu_bar!((debug_button_s("Nested Menus"), { - let sub5 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + let sub5 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( debug_button("Item") )(debug_button("Item"))( debug_button("Item") ))); - let sub4 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + let sub4 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( debug_button("Item") )(debug_button("Item")))) .width(200.0); - let sub3 = menu_temp_2(menu_items!((debug_button("You can"))(debug_button( + let sub3 = menu_tpl_2(menu_items!((debug_button("You can"))(debug_button( "nest menus" ))(submenu_button("SUB"), sub4)( debug_button("how ever") @@ -164,7 +164,7 @@ impl Application for App { ))) .width(180.0); - let sub2 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + let sub2 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( debug_button("Item") )( submenu_button("More sub menus"), sub3 @@ -173,7 +173,7 @@ impl Application for App { )(debug_button("Item")))) .width(160.0); - let sub1 = menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + let sub1 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( submenu_button("Another sub menu"), sub2 )(debug_button("Item"))( @@ -181,7 +181,7 @@ impl Application for App { )(debug_button("Item")))) .width(220.0); - menu_temp_1(menu_items!((debug_button("Item"))(debug_button("Item"))( + menu_tpl_1(menu_items!((debug_button("Item"))(debug_button("Item"))( submenu_button("A sub menu"), sub1 )(debug_button("Item"))( @@ -190,7 +190,7 @@ impl Application for App { .width(140.0) })( debug_button_s("Widgets"), - menu_temp_1(menu_items!((debug_button("You can use any widget"))( + menu_tpl_1(menu_items!((debug_button("You can use any widget"))( debug_button("as a menu item") )( button( @@ -220,7 +220,7 @@ impl Application for App { .padding([0, 8]) .height(30.0) .align_y(alignment::Vertical::Center), - menu_temp_2(menu_items!((debug_button("Item"))(debug_button("Item"))( + menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( debug_button("Item") )(debug_button("Item")))) )(debug_button("Separator"))( @@ -235,7 +235,7 @@ impl Application for App { .width(240.0) )( debug_button_s("Controls"), - menu_temp_1(menu_items!((row![toggler( + menu_tpl_1(menu_items!((row![toggler( Some("Dark Mode".into()), self.dark_mode, Message::ThemeChange @@ -253,7 +253,7 @@ impl Application for App { { let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); - menu_temp_2(menu_items!((slider(0..=255, r, move |x| { + menu_tpl_2(menu_items!((slider(0..=255, r, move |x| { Message::ColorChange(Color::from_rgb8(x, g, b)) }))(slider( 0..=255, @@ -268,7 +268,7 @@ impl Application for App { ))) )( debug_button_s("Scroll"), - menu_temp_1(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + menu_tpl_1(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( debug_button("clkjhbf") )(debug_button("dekjdaud"))( debug_button("ecsh") @@ -278,7 +278,7 @@ impl Application for App { debug_button("isabe") )( submenu_button("jcsu"), - menu_temp_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + menu_tpl_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( debug_button("clkjhbf") )(debug_button("dekjdaud"))( debug_button("ecsh") @@ -290,7 +290,7 @@ impl Application for App { debug_button("kaljkahd") )( submenu_button("luyortp"), - menu_temp_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + menu_tpl_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( debug_button("clkjhbf") )(debug_button("dekjdaud"))( debug_button("ecsh") @@ -328,7 +328,7 @@ impl Application for App { debug_button("bsdfho") )(debug_button("clkjhbf"))( submenu_button("dekjdaud"), - menu_temp_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( + menu_tpl_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( debug_button("clkjhbf") )(debug_button("dekjdaud"))( debug_button("ecsh") @@ -387,7 +387,7 @@ impl Application for App { let pad = 20; let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); - menu_temp_1(menu_items!((labeled_separator("Primary"))( + menu_tpl_1(menu_items!((labeled_separator("Primary"))( row![ vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( x, g, b diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 5fee0397..bcf8bd19 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -61,8 +61,6 @@ where &mut self, renderer: &Renderer, bounds: Size, - // position: Point, - // translation: Vector, ) -> Node { let translation = self.translation; @@ -175,7 +173,6 @@ where parent_bounds, parent_direction, &Rectangle::new( - // Point::new(translation.x, translation.y), Point::ORIGIN, bounds, ), diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 43074299..f4619b06 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -86,7 +86,6 @@ where { pub(super) items: Vec>, pub(super) spacing: f32, - pub(super) padding: Padding, pub(super) max_width: f32, pub(super) width: Length, pub(super) height: Length, @@ -103,7 +102,6 @@ where Self { items, spacing: 0.0, - padding: Padding::ZERO, max_width: f32::MAX, width: Length::Fill, height: Length::Shrink, @@ -130,19 +128,13 @@ where self } - /// Sets the padding of the [`Menu`]. - pub fn padding(mut self, padding: impl Into) -> Self { - self.padding = padding.into(); - self - } - - /// The offset from the bounds of the menu's parent item. + /// The offset from the menu's parent item. pub fn offset(mut self, offset: f32) -> Self { self.offset = offset; self } - /// rebuild state tree + /// Rebuild state tree pub(super) fn tree(&self) -> Tree { Tree { tag: self.tag(), @@ -203,7 +195,7 @@ where &limits, self.width, self.height, - self.padding, + Padding::ZERO, self.spacing, alignment::Alignment::Center, &self.items.iter().map(|i| &i.item).collect::>(), @@ -594,7 +586,7 @@ where } } - /// rebuild state tree + /// Rebuild state tree pub(super) fn tree(&self) -> Tree { Tree { tag: self.tag(), diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 2fc74e44..50e583f6 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -92,11 +92,6 @@ impl StyleSheet for Theme { MenuBarStyle::Default => Appearance { bar_background: palette.background.base.color.into(), menu_background: palette.background.base.color.into(), - // border: Border{ - // color: palette.background.weak.color.into(), - // width: 1.0, - // radius: [6.0; 4].into(), - // }, ..Default::default() }, MenuBarStyle::Custom(c) => c.appearance(self), From 17f33a760dcb06f611b5ea781638619a3d744486 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Thu, 22 Feb 2024 18:28:27 +0800 Subject: [PATCH 28/40] draw path --- examples/menu/src/main.rs | 4 +- src/native/menu.rs | 7 ++-- src/native/menu/common.rs | 28 +++++++++++++ src/native/menu/menu_bar.rs | 63 +++++++++++++++++------------ src/native/menu/menu_bar_overlay.rs | 58 ++++++++++++-------------- src/native/menu/menu_tree.rs | 52 +++++++++++++++++------- src/style/menu_bar.rs | 11 +++++ 7 files changed, 145 insertions(+), 78 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 2451821c..9a0deadc 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -3,9 +3,7 @@ use iced::widget::{ toggler, vertical_slider, }; use iced::widget::{column as col, vertical_space}; -use iced::{ - alignment, theme, Application, Border, Color, Element, Length, Pixels, Size, -}; +use iced::{alignment, theme, Application, Border, Color, Element, Length, Pixels, Size}; use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; use iced_aw::menu::{Item, Menu}; diff --git a/src/native/menu.rs b/src/native/menu.rs index dd3d914d..43b9996b 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -86,9 +86,9 @@ //! ) //! ``` //! -//! Here a menu_template function/closure is used in example 2, +//! Here a menu_template function/closure is used in example 2, //! usually some properties are sync across all menus while others are not, -//! using template functions can reduce the repeated code. +//! using template functions can reduce the repeated code. //! If you find writing menu_template(menu_items!()) too cumbersome, //! there's a menu! macro you can use to create template macros //! @@ -118,7 +118,7 @@ //! Technically you can create menu template functions with the menu! macro, //! but turns out closures can't infer the proper generic types in this case, //! and creating a function for it you have to write a bunch of generic annotations -//! +//! //! ## Example 4 //! //! ``` @@ -152,3 +152,4 @@ mod menu_tree; pub use crate::style::menu_bar::{Appearance, StyleSheet}; pub use menu_bar::MenuBar; pub use menu_tree::{Item, Menu}; +pub use common::DrawPath; diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index a24ff67d..f25296f5 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -14,6 +14,34 @@ pub struct CloseCondition { } */ +/// +/// ## FakeHovering: +/// +/// Places cursors at the path items, i.e. fake hovering, +/// useful when you want to customize the styling of each item in the path, +/// or you simple want the look of the items when they are hovered over. +/// +/// The downside is when some widgets in the path don't response to hovering, +/// the path won't be fully drawn, and when you want uniform path styling +/// but some widgets response to hovering differently. +/// +/// ## Backdrop: +/// +/// Draws a rectangle behind each path item, +/// requires path items to have transparent backgrounds, +/// useful when you want uniform path styling. +/// +/// The downside is, +/// depend on the style you're going for, +/// sometimes manually syncing the path styling to the path items is needed +/// +pub enum DrawPath{ + /// FakeHovering + FakeHovering, + /// Backdrop + Backdrop, +} + /// X+ goes right and Y+ goes down #[derive(Debug, Clone, Copy)] pub(super) enum Direction { diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index fd3a7619..c0873ba1 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -13,7 +13,7 @@ use iced::{ Clipboard, Layout, Shell, Widget, }, alignment, event, Element, Event, Length, Padding, Rectangle, Size, - }; +}; use super::{common::*, flex, menu_bar_overlay::MenuBarOverlay, menu_tree::*}; use crate::style::menu_bar::*; @@ -38,6 +38,7 @@ where width: Length, height: Length, check_bounds_width: f32, + draw_path: DrawPath, style: Theme::Style, } impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> @@ -60,6 +61,7 @@ where width: Length::Shrink, height: Length::Shrink, check_bounds_width: 50.0, + draw_path: DrawPath::FakeHovering, style: Theme::Style::default(), } } @@ -120,19 +122,12 @@ where /// \[Tree{stateless, \[widget_state, menu_state]}...] fn children(&self) -> Vec { - self.roots - .iter() - .map(Item::tree) - .collect::>() + self.roots.iter().map(Item::tree).collect::>() } /// tree: Tree{bar_state, \[item_tree...]} fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.roots, - |tree, item| item.diff(tree), - Item::tree, - ); + tree.diff_children_custom(&self.roots, |tree, item| item.diff(tree), Item::tree); } /// tree: Tree{bar_state, \[item_tree...]} @@ -243,24 +238,9 @@ where theme: &Theme, style: &renderer::Style, layout: Layout<'_>, - cursor: mouse::Cursor, + mut cursor: mouse::Cursor, viewport: &Rectangle, ) { - let state = tree.state.downcast_ref::(); - let cursor = if state.open { - state - .active_root - .and_then(|active| { - layout - .children() - .nth(active) - .map(|l| mouse::Cursor::Available(l.bounds().center())) - }) - .unwrap_or(cursor) - } else { - cursor - }; - let styling = theme.appearance(&self.style); renderer.fill_quad( renderer::Quad { @@ -271,6 +251,36 @@ where styling.bar_background, ); + let state = tree.state.downcast_ref::(); + if state.open { + if let Some(active) = state.active_root{ + let Some(active_bounds) = layout.children() + .nth(active) + .map(|l| l.bounds()) + else{ + return; + }; + + match self.draw_path{ + DrawPath::Backdrop => { + renderer.fill_quad( + renderer::Quad { + bounds: active_bounds, + border: styling.path_border, + ..Default::default() + }, + styling.path + ); + } + DrawPath::FakeHovering => { + if !cursor.is_over(active_bounds){ + cursor = mouse::Cursor::Available(active_bounds.center()) + } + } + } + } + } + self.roots .iter() // [Item...] .zip(tree.children.iter()) // [item_tree...] @@ -301,6 +311,7 @@ where init_bar_bounds, init_root_bounds, check_bounds_width: self.check_bounds_width, + draw_path: &self.draw_path, style: &self.style, } .overlay_element(), diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index bcf8bd19..db36b5c3 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -11,9 +11,9 @@ use iced::{ layout::{Limits, Node}, mouse, overlay, renderer, widget::Tree, - Clipboard, Layout, Shell, }, - event, Event, Point, Rectangle, Size, - Vector, + Clipboard, Layout, Shell, + }, + event, Event, Point, Rectangle, Size, Vector, }; use super::{common::*, menu_bar::MenuBarState, menu_tree::*}; @@ -40,6 +40,7 @@ where pub(super) init_bar_bounds: Rectangle, pub(super) init_root_bounds: Vec, pub(super) check_bounds_width: f32, + pub(super) draw_path: &'b DrawPath, pub(super) style: &'b Theme::Style, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> @@ -57,11 +58,7 @@ where Theme: StyleSheet, Renderer: renderer::Renderer, { - fn layout( - &mut self, - renderer: &Renderer, - bounds: Size, - ) -> Node { + fn layout(&mut self, renderer: &Renderer, bounds: Size) -> Node { let translation = self.translation; let bar = self.tree.state.downcast_ref::(); @@ -172,10 +169,7 @@ where self.check_bounds_width, parent_bounds, parent_direction, - &Rectangle::new( - Point::ORIGIN, - bounds, - ), + &Rectangle::new(Point::ORIGIN, bounds), ); Node::with_children( @@ -426,6 +420,7 @@ where let active_tree = &self.tree.children[active]; fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( + draw_path: &DrawPath, tree: &Tree, item: &Item<'a, Message, Theme, Renderer>, layout_iter: &mut impl Iterator>, @@ -443,8 +438,11 @@ where return; }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} - let mut draw_menu = |cursor| { + let menu_state = menu_tree.state.downcast_ref::(); + + let mut draw_menu = || { menu.draw( + draw_path, menu_tree, renderer, theme, @@ -456,32 +454,27 @@ where ); }; - let menu_state = menu_tree.state.downcast_ref::(); - if let Some(active) = menu_state.active { let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; - let mut mc = menu_layout.children(); - let slice_layout = mc.next().unwrap(); // slice_node - let active_bounds = { - let Some(layout) = slice_layout - .children() - .nth(active - menu_state.slice.start_index) - else { - return; - }; - layout.bounds() - }; + // let mut mc = menu_layout.children(); + // let slice_layout = mc.next().unwrap(); // slice_node + // let active_bounds = { + // let Some(layout) = slice_layout + // .children() + // .nth(active - menu_state.slice.start_index) + // else { + // return; + // }; + // layout.bounds() + // }; - draw_menu(if cursor.is_over(active_bounds) { - cursor - } else { - mouse::Cursor::Available(active_bounds.center()) - }); + draw_menu(); renderer.with_layer(*viewport, |r| { rec( + draw_path, next_tree, next_item, layout_iter, @@ -494,11 +487,12 @@ where ); }); } else { - draw_menu(cursor); + draw_menu(); } } rec( + &self.draw_path, active_tree, active_root, &mut menu_layouts, diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index f4619b06..aaebda83 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -17,9 +17,9 @@ use iced::{ layout::{Layout, Limits, Node}, mouse, renderer, widget::tree::{self, Tree}, - Clipboard, Shell, }, - alignment, event, Element, Event, Length, Padding, Point, Rectangle, - Size, Vector, + Clipboard, Shell, + }, + alignment, event, Element, Event, Length, Padding, Point, Rectangle, Size, Vector, }; use std::iter::once; @@ -167,11 +167,7 @@ where /// tree: Tree{menu_state, \[item_tree...]} pub(super) fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.items, - |tree, item| item.diff(tree), - Item::tree, - ); + tree.diff_children_custom(&self.items, |tree, item| item.diff(tree), Item::tree); } /// tree: Tree{ menu_state, \[item_tree...] } @@ -347,7 +343,7 @@ where ) }) .fold(Ignored, event::Status::merge); - + match event { Event::Mouse(mouse::Event::WheelScrolled { delta }) => { if cursor.is_over(prescroll) { @@ -369,13 +365,14 @@ where /// layout: Node{inf, \[ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} pub(super) fn draw( &self, + draw_path: &DrawPath, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, theme_style: &Theme::Style, layout: Layout<'_>, - cursor: mouse::Cursor, + mut cursor: mouse::Cursor, viewport: &Rectangle, ) { let mut lc = layout.children(); @@ -401,8 +398,35 @@ where styling.menu_background, ); - let mut slc = slice_layout.children(); + // draw path + if let Some(active) = menu_state.active { + let Some(active_bounds) = slice_layout.children() + .nth(active - menu_state.slice.start_index) + .map(|l| l.bounds()) + else { + return; + }; + match draw_path { + DrawPath::Backdrop => { + renderer.fill_quad( + renderer::Quad { + bounds: active_bounds, + border: styling.path_border, + ..Default::default() + }, + styling.path, + ); + } + DrawPath::FakeHovering => { + if !cursor.is_over(active_bounds) { + cursor = mouse::Cursor::Available(active_bounds.center()); + } + } + } + } + + // draw start let Some(start) = self.items.get(slice.start_index) else { return; @@ -410,7 +434,7 @@ where let Some(start_tree) = tree.children.get(slice.start_index) else { return; }; - let Some(start_layout) = slc.next() else { + let Some(start_layout) = slice_layout.children().next() else { return; }; @@ -433,14 +457,14 @@ where // draw the rest let Some(items) = self .items - .get(slice.start_index + 1..slice.end_index.saturating_add(1)) + .get(slice.start_index + 1..=slice.end_index) else { return; }; let Some(trees) = tree .children - .get(slice.start_index + 1..slice.end_index.saturating_add(1)) + .get(slice.start_index + 1..=slice.end_index) else { return; }; diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 50e583f6..b34f0d59 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -21,6 +21,11 @@ pub struct Appearance { pub menu_shadow: Shadow, /// Expand the menu background pub menu_background_expand: Padding, + + /// The backgraound of the path + pub path: Background, + /// The border of the path + pub path_border: Border, } impl std::default::Default for Appearance { fn default() -> Self { @@ -44,6 +49,11 @@ impl std::default::Default for Appearance { blur_radius: 10.0, }, menu_background_expand: [5; 4].into(), + path: Color::from([0.3;3]).into(), + path_border: Border{ + radius: [6.0; 4].into(), + ..Default::default() + } } } } @@ -92,6 +102,7 @@ impl StyleSheet for Theme { MenuBarStyle::Default => Appearance { bar_background: palette.background.base.color.into(), menu_background: palette.background.base.color.into(), + path: palette.primary.weak.color.into(), ..Default::default() }, MenuBarStyle::Custom(c) => c.appearance(self), From 1f3f1074e3ee5f738292415761ce3882c59058cb Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 23 Feb 2024 18:22:02 +0800 Subject: [PATCH 29/40] mouse interations --- examples/menu/src/main.rs | 585 ++++++++++++++-------------- src/native/menu.rs | 2 +- src/native/menu/common.rs | 32 +- src/native/menu/menu_bar.rs | 14 +- src/native/menu/menu_bar_overlay.rs | 131 +++++-- src/native/menu/menu_tree.rs | 64 ++- src/native/quad.rs | 68 ++-- src/style/menu_bar.rs | 6 +- 8 files changed, 519 insertions(+), 383 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 9a0deadc..b6df5bca 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -141,278 +141,294 @@ impl Application for App { let menu_tpl_1 = |items| Menu::new(items).max_width(180.0).offset(15.0).spacing(5.0); let menu_tpl_2 = |items| Menu::new(items).max_width(180.0).offset(0.0).spacing(5.0); - let mb = menu_bar!((debug_button_s("Nested Menus"), { - let sub5 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( - debug_button("Item") - )(debug_button("Item"))( - debug_button("Item") - ))); - - let sub4 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( - debug_button("Item") - )(debug_button("Item")))) - .width(200.0); - - let sub3 = menu_tpl_2(menu_items!((debug_button("You can"))(debug_button( - "nest menus" - ))(submenu_button("SUB"), sub4)( - debug_button("how ever") - )(debug_button("You like"))( - submenu_button("SUB"), sub5 - ))) - .width(180.0); - - let sub2 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( - debug_button("Item") - )( - submenu_button("More sub menus"), sub3 - )(debug_button("Item"))( - debug_button("Item") - )(debug_button("Item")))) - .width(160.0); - - let sub1 = menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( - submenu_button("Another sub menu"), - sub2 - )(debug_button("Item"))( - debug_button("Item") - )(debug_button("Item")))) - .width(220.0); - - menu_tpl_1(menu_items!((debug_button("Item"))(debug_button("Item"))( - submenu_button("A sub menu"), - sub1 - )(debug_button("Item"))( - debug_button("Item") - )(debug_button("Item")))) - .width(140.0) - })( - debug_button_s("Widgets"), - menu_tpl_1(menu_items!((debug_button("You can use any widget"))( - debug_button("as a menu item") - )( - button( + #[rustfmt::skip] + let mb = menu_bar!( + (debug_button_s("Nested Menus"), { + let sub5 = menu_tpl_2(menu_items!( + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + )); + + let sub4 = menu_tpl_2(menu_items!( + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + )).width(200.0); + + let sub3 = menu_tpl_2(menu_items!( + (debug_button("You can")) + (debug_button("nest menus")) + (submenu_button("SUB"), sub4) + (debug_button("how ever")) + (debug_button("You like")) + (submenu_button("SUB"), sub5) + )).width(180.0); + + let sub2 = menu_tpl_2(menu_items!( + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (submenu_button("More sub menus"), sub3) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + )).width(160.0); + + let sub1 = menu_tpl_2(menu_items!( + (debug_button("Item")) + (debug_button("Item")) + (submenu_button("Another sub menu"), sub2) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + )).width(220.0); + + menu_tpl_1(menu_items!( + (debug_button("Item")) + (debug_button("Item")) + (submenu_button("A sub menu"), sub1) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + )).width(140.0) + }) + (debug_button_s("Widgets"), menu_tpl_1(menu_items!( + (debug_button("You can use any widget")) + (debug_button("as a menu item")) + (button( text("Button") .width(Length::Fill) .vertical_alignment(alignment::Vertical::Center), + ) + .width(Length::Fill) + .on_press(Message::Debug("Button".into())) ) - .width(Length::Fill) - .on_press(Message::Debug("Button".into())) - )( - // checkbox("Checkbox", self.check, Message::CheckChange).width(Length::Fill) - checkbox("Checkbox", self.check) - .on_toggle(|x| Message::CheckChange(x)) + (checkbox("Checkbox", self.check).on_toggle(|x| Message::CheckChange(x)) .width(Length::Fill) - )(row![ - "Slider", - horizontal_space().width(Length::Fixed(8.0)), - slider(0..=255, self.value, Message::ValueChange) - ])( - text_input("", &self.text).on_input(Message::TextChange) - )( - container(toggler( - Some("Or as a sub menu item".to_string()), - self.toggle, - Message::ToggleChange, - )) - .padding([0, 8]) - .height(30.0) - .align_y(alignment::Vertical::Center), - menu_tpl_2(menu_items!((debug_button("Item"))(debug_button("Item"))( - debug_button("Item") - )(debug_button("Item")))) - )(debug_button("Separator"))( - separator() - )(debug_button( - "Labeled Separator" - ))(labeled_separator("Separator"))( - debug_button("Dot Separator") - )(dot_separator(&self.theme))( - debug_button("Item") - ))) - .width(240.0) - )( - debug_button_s("Controls"), - menu_tpl_1(menu_items!((row![toggler( - Some("Dark Mode".into()), - self.dark_mode, - Message::ThemeChange - )] - .padding([0, 8]))(color_button([ - 0.45, 0.25, 0.57 - ]))(color_button([ - 0.15, 0.59, 0.64 - ]))(color_button([ - 0.76, 0.82, 0.20 - ]))(color_button([ - 0.17, 0.27, 0.33 - ]))( - labeled_button("Primary", Message::None).width(Length::Fill), - { - let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); - - menu_tpl_2(menu_items!((slider(0..=255, r, move |x| { - Message::ColorChange(Color::from_rgb8(x, g, b)) - }))(slider( - 0..=255, - g, - move |x| { Message::ColorChange(Color::from_rgb8(r, x, b)) } - ))(slider( - 0..=255, - b, - move |x| { Message::ColorChange(Color::from_rgb8(r, g, x)) } - )))) - } - ))) - )( - debug_button_s("Scroll"), - menu_tpl_1(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( - debug_button("clkjhbf") - )(debug_button("dekjdaud"))( - debug_button("ecsh") - )(debug_button("fweiu"))( - debug_button("giwe") - )(debug_button("heruyv"))( - debug_button("isabe") - )( - submenu_button("jcsu"), - menu_tpl_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( - debug_button("clkjhbf") - )(debug_button("dekjdaud"))( - debug_button("ecsh") - )(debug_button("fweiu"))( - debug_button("giwe") - )(debug_button("heruyv"))( - debug_button("isabe") - )(debug_button("jcsu"))( - debug_button("kaljkahd") - )( - submenu_button("luyortp"), - menu_tpl_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( - debug_button("clkjhbf") - )(debug_button("dekjdaud"))( - debug_button("ecsh") - )(debug_button("fweiu"))( - debug_button("giwe") - )(debug_button("heruyv"))( - debug_button("isabe") - )(debug_button("jcsu"))( - debug_button("kaljkahd") - )(debug_button("luyortp"))( - debug_button("mmdyrc") - )(debug_button("nquc")))) - )(debug_button("mmdyrc"))( - debug_button("nquc") - ))) - )(debug_button("kaljkahd"))( - debug_button("luyortp") - )(debug_button("mmdyrc"))( - debug_button("nquc") - )(debug_button("ajrs"))( - debug_button("bsdfho") - )(debug_button("clkjhbf"))( - debug_button("dekjdaud") - )(debug_button("ecsh"))( - debug_button("fweiu") - )(debug_button("giwe"))( - debug_button("heruyv") - )(debug_button("isabe"))( - debug_button("jcsu") - )(debug_button("kaljkahd"))( - debug_button("luyortp") - )(debug_button("mmdyrc"))( - debug_button("nquc") - )(debug_button("ajrs"))( - debug_button("bsdfho") - )(debug_button("clkjhbf"))( - submenu_button("dekjdaud"), - menu_tpl_2(menu_items!((debug_button("ajrs"))(debug_button("bsdfho"))( - debug_button("clkjhbf") - )(debug_button("dekjdaud"))( - debug_button("ecsh") - )(debug_button("fweiu"))( - debug_button("giwe") - )(debug_button("heruyv"))( - debug_button("isabe") - )(debug_button("jcsu"))( - debug_button("kaljkahd") - )(debug_button("luyortp"))( - debug_button("mmdyrc") - )(debug_button("nquc"))( - debug_button("ajrs") - )(debug_button("bsdfho"))( - debug_button("clkjhbf") - )(debug_button("dekjdaud"))( - debug_button("ecsh") - )(debug_button("fweiu"))( - debug_button("giwe") - )(debug_button("heruyv"))( - debug_button("isabe") - )(debug_button("jcsu"))( - debug_button("kaljkahd") - )(debug_button("luyortp"))( - debug_button("mmdyrc") - )(debug_button("nquc")))) - )(debug_button("ecsh"))( - debug_button("fweiu") - )(debug_button("giwe"))( - debug_button("heruyv") - )(debug_button("isabe"))( - debug_button("jcsu") - )(debug_button("kaljkahd"))( - debug_button("luyortp") - )(debug_button("mmdyrc"))( - debug_button("nquc") - )(debug_button("ajrs"))( - debug_button("bsdfho") - )(debug_button("clkjhbf"))( - debug_button("dekjdaud") - )(debug_button("ecsh"))( - debug_button("fweiu") - )(debug_button("giwe"))( - debug_button("heruyv") - )(debug_button("isabe"))( - debug_button("jcsu") - )(debug_button("kaljkahd"))( - debug_button("luyortp") - )(debug_button("mmdyrc"))( - debug_button("nquc") + ) + ( + row![ + "Slider", + horizontal_space().width(Length::Fixed(8.0)), + slider(0..=255, self.value, Message::ValueChange) + ] + ) + (text_input("", &self.text).on_input(Message::TextChange)) + (container(toggler( + Some("Or as a sub menu item".to_string()), + self.toggle, + Message::ToggleChange, + )) + .padding([0, 8]) + .height(30.0) + .align_y(alignment::Vertical::Center), + menu_tpl_2(menu_items!( + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + (debug_button("Item")) + )) + ) + (debug_button("Separator")) + (separator()) + (debug_button("Labeled Separator")) + (labeled_separator("Separator")) + (debug_button("Dot Separator")) + (dot_separator(&self.theme)) + (debug_button("Item")) + (debug_button("Item")) + )).width(240.0)) + (debug_button_s("Controls"), menu_tpl_1(menu_items!( + (row![toggler( + Some("Dark Mode".into()), + self.dark_mode, + Message::ThemeChange + )].padding([0, 8]) + ) + (color_button([0.45, 0.25, 0.57])) + (color_button([0.15, 0.59, 0.64])) + (color_button([0.76, 0.82, 0.20])) + (color_button([0.17, 0.27, 0.33])) + (labeled_button("Primary", Message::None) + .width(Length::Fill), + { + let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); + + menu_tpl_2(menu_items!( + (slider(0..=255, r, move |x| { + Message::ColorChange(Color::from_rgb8(x, g, b)) + })) + (slider(0..=255, g, move |x| { + Message::ColorChange(Color::from_rgb8(r, x, b)) + })) + (slider(0..=255, b, move |x| { + Message::ColorChange(Color::from_rgb8(r, g, x)) + })) + )) + } + ) ))) - )(debug_button_s("Dynamic height"), { - let slider_count = 3; - let slider_width = 30; - let spacing = 5; - let pad = 20; - let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); - - menu_tpl_1(menu_items!((labeled_separator("Primary"))( - row![ - vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( - x, g, b + (debug_button_s("Scroll"), menu_tpl_1(menu_items!( + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (submenu_button("jcsu"), menu_tpl_2(menu_items!( + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (submenu_button("luyortp"), menu_tpl_2(menu_items!( + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) ))) - .width(slider_width), - vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( - r, x, b - ))) - .width(slider_width), - vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( - r, g, x - ))) - .width(slider_width), - ] - .spacing(spacing) - .height(100.0) - )(separator())( - debug_button("AABB").height(40) - )(debug_button("CCDD").height(140))( - debug_button("EEFF").height(30) - )(debug_button("GGHH").height(100))( - debug_button("IIJJ").height(60) - )(debug_button("KKLL").height(120))( - debug_button("MMNN").height(50) + (debug_button("mmdyrc")) + (debug_button("nquc")) + ))) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (submenu_button("dekjdaud"), menu_tpl_2(menu_items!( + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + ))) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) + (debug_button("ajrs")) + (debug_button("bsdfho")) + (debug_button("clkjhbf")) + (debug_button("dekjdaud")) + (debug_button("ecsh")) + (debug_button("fweiu")) + (debug_button("giwe")) + (debug_button("heruyv")) + (debug_button("isabe")) + (debug_button("jcsu")) + (debug_button("kaljkahd")) + (debug_button("luyortp")) + (debug_button("mmdyrc")) + (debug_button("nquc")) ))) - .width(slider_width * slider_count + (slider_count - 1) * spacing + pad) - })); + (debug_button_s("Dynamic height"), { + let slider_count = 3; + let slider_width = 30; + let spacing = 5; + let pad = 20; + let [r, g, b, _] = self.theme.palette().primary.into_rgba8(); + + menu_tpl_1(menu_items!( + (labeled_separator("Primary")) + ( + row![ + vertical_slider(0..=255, r, move |x| Message::ColorChange(Color::from_rgb8( + x, g, b + ))) + .width(slider_width) + , + vertical_slider(0..=255, g, move |x| Message::ColorChange(Color::from_rgb8( + r, x, b + ))) + .width(slider_width) + , + vertical_slider(0..=255, b, move |x| Message::ColorChange(Color::from_rgb8( + r, g, x + ))) + .width(slider_width) + , + ].spacing(spacing) + .height(100.0) + ) + (separator()) + (debug_button("AABB").height(40)) + (debug_button("CCDD").height(140)) + (debug_button("EEFF").height(30)) + (debug_button("GGHH").height(100)) + (debug_button("IIJJ").height(60)) + (debug_button("KKLL").height(120)) + (debug_button("MMNN").height(50)) + )).width(slider_width * slider_count + (slider_count - 1) * spacing + pad) + }) + ); let r = row![ horizontal_space().width(295), @@ -529,10 +545,13 @@ fn color_button<'a>( fn separator<'a>() -> quad::Quad { quad::Quad { - color: [0.5; 3].into(), - border_radius: [4.0; 4], - inner_bounds: InnerBounds::Ratio(0.98, 0.1), - height: Length::Fixed(30.0), + quad_color: Color::from([0.5; 3]).into(), + quad_border: Border { + radius: [4.0; 4].into(), + ..Default::default() + }, + inner_bounds: InnerBounds::Ratio(0.98, 0.2), + height: Length::Fixed(20.0), ..Default::default() } } @@ -540,29 +559,24 @@ fn separator<'a>() -> quad::Quad { fn dot_separator<'a>(theme: &iced::Theme) -> Element<'a, Message, iced::Theme, iced::Renderer> { row((0..20).map(|_| { quad::Quad { - color: theme.extended_palette().background.base.text, - border_radius: [4.0; 4], + quad_color: theme.extended_palette().background.base.text.into(), inner_bounds: InnerBounds::Square(4.0), - ..Default::default() + ..separator() } .into() })) - .height(30.0) + .height(20.0) .into() } fn labeled_separator(label: &'_ str) -> Element<'_, Message, iced::Theme, iced::Renderer> { let q_1 = quad::Quad { - color: [0.5; 3].into(), - border_radius: [4.0; 4], - inner_bounds: InnerBounds::Ratio(0.98, 0.1), - ..Default::default() + height: Length::Fill, + ..separator() }; let q_2 = quad::Quad { - color: [0.5; 3].into(), - border_radius: [4.0; 4], - inner_bounds: InnerBounds::Ratio(0.98, 0.1), - ..Default::default() + height: Length::Fill, + ..separator() }; row![ @@ -572,7 +586,7 @@ fn labeled_separator(label: &'_ str) -> Element<'_, Message, iced::Theme, iced:: .vertical_alignment(alignment::Vertical::Center), q_2, ] - .height(30.0) + .height(20.0) .into() } @@ -580,10 +594,13 @@ fn circle(color: Color) -> quad::Quad { let radius = 10.0; quad::Quad { - color, + quad_color: color.into(), inner_bounds: InnerBounds::Square(radius * 2.0), - border_radius: [radius; 4], - height: 20.0.into(), + quad_border: Border { + radius: [radius; 4].into(), + ..Default::default() + }, + height: Length::Fixed(20.0), ..Default::default() } } diff --git a/src/native/menu.rs b/src/native/menu.rs index 43b9996b..c0013a00 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -150,6 +150,6 @@ mod menu_bar_overlay; mod menu_tree; pub use crate::style::menu_bar::{Appearance, StyleSheet}; +pub use common::DrawPath; pub use menu_bar::MenuBar; pub use menu_tree::{Item, Menu}; -pub use common::DrawPath; diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index f25296f5..8a6c5767 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -15,27 +15,27 @@ pub struct CloseCondition { */ /// -/// ## FakeHovering: +/// ## FakeHovering: /// -/// Places cursors at the path items, i.e. fake hovering, -/// useful when you want to customize the styling of each item in the path, -/// or you simple want the look of the items when they are hovered over. -/// -/// The downside is when some widgets in the path don't response to hovering, -/// the path won't be fully drawn, and when you want uniform path styling +/// Places cursors at the path items, +/// useful when you want to customize the styling of each item in the path, +/// or you simple want the look of the items when they are hovered over. +/// +/// The downside is when some widgets in the path don't response to hovering, +/// the path won't be fully drawn, and when you want uniform path styling /// but some widgets response to hovering differently. -/// +/// /// ## Backdrop: /// -/// Draws a rectangle behind each path item, -/// requires path items to have transparent backgrounds, -/// useful when you want uniform path styling. -/// -/// The downside is, -/// depend on the style you're going for, -/// sometimes manually syncing the path styling to the path items is needed +/// Draws a rectangle behind each path item, +/// requires path items to have transparent backgrounds, +/// useful when you want uniform path styling. +/// +/// The downside is, +/// depend on the style you're going for, +/// oftentimes manually syncing the path's styling to the path items' is necessary /// -pub enum DrawPath{ +pub enum DrawPath { /// FakeHovering FakeHovering, /// Backdrop diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index c0873ba1..cc5ef37e 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -253,15 +253,12 @@ where let state = tree.state.downcast_ref::(); if state.open { - if let Some(active) = state.active_root{ - let Some(active_bounds) = layout.children() - .nth(active) - .map(|l| l.bounds()) - else{ + if let Some(active) = state.active_root { + let Some(active_bounds) = layout.children().nth(active).map(|l| l.bounds()) else { return; }; - match self.draw_path{ + match self.draw_path { DrawPath::Backdrop => { renderer.fill_quad( renderer::Quad { @@ -269,11 +266,11 @@ where border: styling.path_border, ..Default::default() }, - styling.path + styling.path, ); } DrawPath::FakeHovering => { - if !cursor.is_over(active_bounds){ + if !cursor.is_over(active_bounds) { cursor = mouse::Cursor::Available(active_bounds.center()) } } @@ -313,6 +310,7 @@ where check_bounds_width: self.check_bounds_width, draw_path: &self.draw_path, style: &self.style, + // is_over: false, } .overlay_element(), ) diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index db36b5c3..8dbf7a76 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -42,6 +42,7 @@ where pub(super) check_bounds_width: f32, pub(super) draw_path: &'b DrawPath, pub(super) style: &'b Theme::Style, + // pub(super) is_over: bool, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where @@ -394,6 +395,76 @@ where } } + fn mouse_interaction( + &self, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + let bar = self.tree.state.downcast_ref::(); + let Some(active) = bar.active_root else { + return mouse::Interaction::default(); + }; + + // let viewport = layout.bounds(); + let mut lc = layout.children(); + let _bar_bounds = lc.next().unwrap().bounds(); + let _roots_layout = lc.next().unwrap(); + + // let parent_bounds = roots_layout.children().nth(active).unwrap().bounds(); + let menu_layouts_layout = lc.next().unwrap(); // Node{0, [menu_node...]} + let mut menu_layouts = menu_layouts_layout.children(); // [menu_node...] + + let active_root = &self.roots[active]; + let active_tree = &self.tree.children[active]; + + fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( + tree: &Tree, + item: &Item<'a, Message, Theme, Renderer>, + layout_iter: &mut impl Iterator>, + cursor: mouse::Cursor, + renderer: &Renderer, + viewport: &Rectangle, + ) -> mouse::Interaction { + let menu = item.menu.as_ref().expect("No menu defined in this item"); + let menu_tree = &tree.children[1]; + + let Some(menu_layout) = layout_iter.next() else { + return mouse::Interaction::default(); + }; // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} + + let menu_state = menu_tree.state.downcast_ref::(); + + let i = menu.mouse_interaction(menu_tree, menu_layout, cursor, viewport, renderer); + + if let Some(active) = menu_state.active { + let next_tree = &menu_tree.children[active]; + let next_item = &menu.items[active]; + rec( + next_tree, + next_item, + layout_iter, + cursor, + renderer, + viewport, + ) + .max(i) + } else { + i + } + } + + rec( + active_tree, + active_root, + &mut menu_layouts, + cursor, + renderer, + viewport, + ) + } + fn draw( &self, renderer: &mut Renderer, @@ -440,38 +511,22 @@ where let menu_state = menu_tree.state.downcast_ref::(); - let mut draw_menu = || { - menu.draw( - draw_path, - menu_tree, - renderer, - theme, - style, - theme_style, - menu_layout, - cursor, - viewport, - ); - }; + menu.draw( + draw_path, + menu_tree, + renderer, + theme, + style, + theme_style, + menu_layout, + cursor, + viewport, + ); if let Some(active) = menu_state.active { let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; - // let mut mc = menu_layout.children(); - // let slice_layout = mc.next().unwrap(); // slice_node - // let active_bounds = { - // let Some(layout) = slice_layout - // .children() - // .nth(active - menu_state.slice.start_index) - // else { - // return; - // }; - // layout.bounds() - // }; - - draw_menu(); - renderer.with_layer(*viewport, |r| { rec( draw_path, @@ -486,8 +541,6 @@ where viewport, ); }); - } else { - draw_menu(); } } @@ -505,7 +558,25 @@ where ); } - fn is_over(&self, _layout: Layout<'_>, _renderer: &Renderer, _cursor_position: Point) -> bool { + fn is_over(&self, layout: Layout<'_>, _renderer: &Renderer, cursor_position: Point) -> bool { + let mut lc = layout.children(); + let _bar_bounds = lc.next().unwrap().bounds(); + let _roots_layout = lc.next().unwrap(); + let Some(menu_layouts) = lc.next().map(|l| l.children()) else { + return false; + }; // [menu_node...] + + for menu_layout in menu_layouts { + // menu_node: Node{inf, [ slice_node, prescroll, offset_bounds, check_bounds ]} + let mut mc = menu_layout.children(); + let _slice_layout = mc.next().unwrap(); + let prescroll = mc.next().unwrap().bounds(); + + if prescroll.contains(cursor_position) { + return true; + } + } + false } } diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index aaebda83..6ca6aca5 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -301,6 +301,7 @@ where child_direction, ) } + /// tree: Tree{ menu_state, \[item_tree...] } /// /// layout: Node{inf, \[ slice_node, prescroll, offset_bounds, check_bounds ]} @@ -360,6 +361,34 @@ where .merge(status) } + /// tree: Tree{ menu_state, \[item_tree...] } + /// + /// layout: Node{inf, \[ slice_node, prescroll, offset_bounds, check_bounds ]} + pub(super) fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + let mut lc = layout.children(); + let slice_layout = lc.next().unwrap(); + + let menu_state = tree.state.downcast_ref::(); + let slice = &menu_state.slice; + + self.items[slice.start_index..=slice.end_index] + .iter() + .zip(tree.children[slice.start_index..=slice.end_index].iter()) // [item_tree...] + .zip(slice_layout.children()) // [item_layout...] + .map(|((item, tree), layout)| { + item.mouse_interaction(tree, layout, cursor, viewport, renderer) + }) + .max() + .unwrap_or_default() + } + /// tree: Tree{menu_state, \[item_tree...]} /// /// layout: Node{inf, \[ items_node, slice_node, prescroll, offset_bounds, check_bounds ]} @@ -400,7 +429,8 @@ where // draw path if let Some(active) = menu_state.active { - let Some(active_bounds) = slice_layout.children() + let Some(active_bounds) = slice_layout + .children() .nth(active - menu_state.slice.start_index) .map(|l| l.bounds()) else { @@ -425,8 +455,7 @@ where } } } - - + // draw start let Some(start) = self.items.get(slice.start_index) else { return; @@ -455,17 +484,11 @@ where }); // draw the rest - let Some(items) = self - .items - .get(slice.start_index + 1..=slice.end_index) - else { + let Some(items) = self.items.get(slice.start_index + 1..=slice.end_index) else { return; }; - let Some(trees) = tree - .children - .get(slice.start_index + 1..=slice.end_index) - else { + let Some(trees) = tree.children.get(slice.start_index + 1..=slice.end_index) else { return; }; @@ -700,6 +723,25 @@ where ) } + /// tree: Tree{stateless, \[widget_tree, menu_tree]} + /// + pub(super) fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.item.as_widget().mouse_interaction( + &tree.children[0], + layout, + cursor, + viewport, + renderer, + ) + } + /// tree: Tree{stateless, \[widget_tree, menu_tree]} /// pub(super) fn draw( diff --git a/src/native/quad.rs b/src/native/quad.rs index a3ffdcd7..1c97a479 100644 --- a/src/native/quad.rs +++ b/src/native/quad.rs @@ -11,7 +11,7 @@ use iced::{ Layout, Widget, }, mouse::Cursor, - Border, Color, Element, Length, Rectangle, Shadow, Size, + Background, Border, Color, Element, Length, Rectangle, Shadow, Size, }; /// A dummy widget that draws a quad @@ -21,30 +21,46 @@ pub struct Quad { pub width: Length, /// Height of the quad pub height: Length, - /// Color of the quad - pub color: Color, - /// Background color of the quad - pub background: Option, + /// Methods for creating inner bounds pub inner_bounds: InnerBounds, - /// Border radius of the Quad - pub border_radius: [f32; 4], - /// Border width of the quad - pub border_width: f32, - /// Border color of the quad - pub border_color: Color, + + /// Color of the quad + pub quad_color: Background, + /// Border of the quad + pub quad_border: Border, + /// Shadow of the quad + pub quad_shadow: Shadow, + + /// Background color of the quad + pub bg_color: Option, + /// Border of the background + pub bg_border: Border, + /// Shadow of the background + pub bg_shadow: Shadow, } impl Default for Quad { fn default() -> Self { Self { width: Length::Fill, height: Length::Fill, - color: Color::from([0.5; 3]), - background: None, inner_bounds: InnerBounds::Ratio(0.5, 0.5), - border_radius: [0.0, 0.0, 0.0, 0.0], - border_width: 0.0, - border_color: Color::TRANSPARENT, + + quad_color: Color::from([0.5; 3]).into(), + quad_border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: [0.0, 0.0, 0.0, 0.0].into(), + }, + quad_shadow: Shadow::default(), + + bg_color: None, + bg_border: Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: [0.0, 0.0, 0.0, 0.0].into(), + }, + bg_shadow: Shadow::default(), } } } @@ -72,16 +88,12 @@ where _cursor: Cursor, _viewport: &Rectangle, ) { - if let Some(b) = self.background { + if let Some(b) = self.bg_color { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), - border: Border { - radius: self.border_radius.into(), - width: self.border_width, - color: self.border_color, - }, - shadow: Shadow::default(), + border: self.bg_border, + shadow: self.bg_shadow, }, b, ); @@ -89,14 +101,10 @@ where renderer.fill_quad( renderer::Quad { bounds: self.inner_bounds.get_bounds(layout.bounds()), - border: Border { - radius: self.border_radius.into(), - width: self.border_width, - color: self.border_color, - }, - shadow: Shadow::default(), + border: self.quad_border, + shadow: self.quad_shadow, }, - self.color, + self.quad_color, ); } } diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index b34f0d59..1f355f70 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -49,11 +49,11 @@ impl std::default::Default for Appearance { blur_radius: 10.0, }, menu_background_expand: [5; 4].into(), - path: Color::from([0.3;3]).into(), - path_border: Border{ + path: Color::from([0.3; 3]).into(), + path_border: Border { radius: [6.0; 4].into(), ..Default::default() - } + }, } } } From 6f7b7a670dda2c9e20ee288261fbe6cc21316a39 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 23 Feb 2024 19:09:29 +0800 Subject: [PATCH 30/40] styling --- examples/menu/src/main.rs | 13 ++++++-- src/native/menu/common.rs | 8 ++--- src/native/menu/menu_bar.rs | 6 ++++ src/native/menu/menu_bar_overlay.rs | 52 ++++------------------------- src/style/menu_bar.rs | 6 ++-- 5 files changed, 30 insertions(+), 55 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index b6df5bca..0cb61846 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -6,7 +6,8 @@ use iced::widget::{column as col, vertical_space}; use iced::{alignment, theme, Application, Border, Color, Element, Length, Pixels, Size}; use iced_aw::graphics::icons::{BootstrapIcon, BOOTSTRAP_FONT, BOOTSTRAP_FONT_BYTES}; -use iced_aw::menu::{Item, Menu}; +use iced_aw::menu::{self, Item, Menu, StyleSheet}; +use iced_aw::style::MenuBarStyle; use iced_aw::{menu_bar, menu_items}; use iced_aw::{native::InnerBounds, quad}; @@ -428,7 +429,15 @@ impl Application for App { (debug_button("MMNN").height(50)) )).width(slider_width * slider_count + (slider_count - 1) * spacing + pad) }) - ); + ) + .draw_path(menu::DrawPath::Backdrop) + .style(|theme:&iced::Theme| menu::Appearance{ + path_border: Border{ + radius: [6.0; 4].into(), + ..Default::default() + }, + ..theme.appearance(&MenuBarStyle::Default) + }); let r = row![ horizontal_space().width(295), diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index 8a6c5767..f7441c8c 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -17,7 +17,7 @@ pub struct CloseCondition { /// /// ## FakeHovering: /// -/// Places cursors at the path items, +/// Places cursors at the path items, /// useful when you want to customize the styling of each item in the path, /// or you simple want the look of the items when they are hovered over. /// @@ -32,9 +32,9 @@ pub struct CloseCondition { /// useful when you want uniform path styling. /// /// The downside is, -/// depend on the style you're going for, -/// oftentimes manually syncing the path's styling to the path items' is necessary -/// +/// depending on the style you're going for, +/// oftentimes manually syncing the path's styling to the path items' is necessary, +/// the default styling simply can't cover most use cases. pub enum DrawPath { /// FakeHovering FakeHovering, diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index cc5ef37e..a83c6a86 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -89,6 +89,12 @@ where self.check_bounds_width = check_bounds_width; self } + + /// Sets the draw path option of the [`MenuBar`] + pub fn draw_path(mut self, draw_path: DrawPath) -> Self { + self.draw_path = draw_path; + self + } /// Sets the padding of the [`MenuBar`]. pub fn padding(mut self, padding: impl Into) -> Self { diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 8dbf7a76..1613a625 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -217,6 +217,7 @@ where let active_tree = &mut self.tree.children[active]; let mut prev_bounds_list = vec![bar_bounds]; + #[rustfmt::skip] fn rec<'a, 'b, Message, Theme: StyleSheet, Renderer: renderer::Renderer>( tree: &mut Tree, item: &mut Item<'a, Message, Theme, Renderer>, @@ -281,29 +282,13 @@ where RecEvent::Event => RecEvent::Event, RecEvent::Close => { if cursor.is_over(prescroll) { - menu.on_event( - menu_tree, - event, - menu_layout, - cursor, - renderer, - clipboard, - shell, - viewport, - ); + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event } else if cursor.is_over(offset_bounds) { RecEvent::Event } else { - menu.close_event( - menu_tree, - menu_layout, - cursor, - parent_bounds, - prev_bounds_list, - prev, - ); + menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); if prev.is_some() { RecEvent::None } else { @@ -313,16 +298,7 @@ where } RecEvent::None => { if cursor.is_over(prescroll) { - menu.on_event( - menu_tree, - event, - menu_layout, - cursor, - renderer, - clipboard, - shell, - viewport, - ); + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event } else if cursor.is_over(offset_bounds) { @@ -336,29 +312,13 @@ where prev_bounds_list.pop(); if cursor.is_over(prescroll) { - menu.on_event( - menu_tree, - event, - menu_layout, - cursor, - renderer, - clipboard, - shell, - viewport, - ); + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event } else if cursor.is_over(offset_bounds) { RecEvent::Event } else { - menu.close_event( - menu_tree, - menu_layout, - cursor, - parent_bounds, - prev_bounds_list, - prev, - ); + menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); if prev.is_some() { RecEvent::None } else { diff --git a/src/style/menu_bar.rs b/src/style/menu_bar.rs index 1f355f70..f1f2817e 100644 --- a/src/style/menu_bar.rs +++ b/src/style/menu_bar.rs @@ -78,13 +78,13 @@ pub enum MenuBarStyle { Custom(Box>), } -impl From Appearance> for MenuBarStyle { - fn from(f: fn(&Theme) -> Appearance) -> Self { +impl Appearance + 'static> From for MenuBarStyle { + fn from(f: F) -> Self { Self::Custom(Box::new(f)) } } -impl StyleSheet for fn(&Theme) -> Appearance { +impl Appearance> StyleSheet for F { type Style = Theme; fn appearance(&self, style: &Self::Style) -> Appearance { From dd632e81b8e4074b4d5f2066e60a3ec8b8ea16b2 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 23 Feb 2024 21:46:27 +0800 Subject: [PATCH 31/40] improve closing logic --- src/native/menu/common.rs | 8 +++ src/native/menu/menu_bar.rs | 2 +- src/native/menu/menu_bar_overlay.rs | 86 +++++++++++------------------ src/native/menu/menu_tree.rs | 20 ++++++- 4 files changed, 60 insertions(+), 56 deletions(-) diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index f7441c8c..88a65f4f 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -67,6 +67,14 @@ pub(super) enum Axis { pub(super) type Index = Option; +/// Should be returned from the recursive event processing function, +/// tells the caller which type of event has been processed +pub(super) enum RecEvent { + Event, + Close, + None, +} + pub fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { Rectangle { x: rect.x - padding.left, diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index a83c6a86..d0270025 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -89,7 +89,7 @@ where self.check_bounds_width = check_bounds_width; self } - + /// Sets the draw path option of the [`MenuBar`] pub fn draw_path(mut self, draw_path: DrawPath) -> Self { self.draw_path = draw_path; diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 1613a625..5bad3e7a 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -19,14 +19,6 @@ use iced::{ use super::{common::*, menu_bar::MenuBarState, menu_tree::*}; use crate::style::menu_bar::*; -/// Should be returned from the recursive event processing function, -/// tells the caller which type of event has been processed -enum RecEvent { - Event, - Close, - None, -} - pub(super) struct MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where Theme: StyleSheet, @@ -247,7 +239,7 @@ where let menu_state = menu_tree.state.downcast_mut::(); - if let Some(active) = menu_state.active { + let rec_event = if let Some(active) = menu_state.active { let next_tree = &mut menu_tree.children[active]; let next_item = &mut menu.items[active]; let next_parent_bounds = { @@ -255,13 +247,14 @@ where .children() .nth(active - menu_state.slice.start_index) else { + prev_bounds_list.pop(); return RecEvent::Event; }; layout.bounds() }; - let re = rec( + rec( next_tree, next_item, event, @@ -274,55 +267,40 @@ where viewport, prev_bounds_list, &mut menu_state.active, - ); - - prev_bounds_list.pop(); - - match re { - RecEvent::Event => RecEvent::Event, - RecEvent::Close => { - if cursor.is_over(prescroll) { - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); - menu.open_event(menu_tree, menu_layout, cursor); - RecEvent::Event - } else if cursor.is_over(offset_bounds) { - RecEvent::Event - } else { - menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); - if prev.is_some() { - RecEvent::None - } else { - RecEvent::Close - } - } - } - RecEvent::None => { - if cursor.is_over(prescroll) { - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); - menu.open_event(menu_tree, menu_layout, cursor); - RecEvent::Event - } else if cursor.is_over(offset_bounds) { - RecEvent::Event - } else { + ) + } else { + RecEvent::Close + }; + + prev_bounds_list.pop(); + + match rec_event { + RecEvent::Event => RecEvent::Event, + RecEvent::Close => { + if menu_state.pressed || cursor.is_over(prescroll){ + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.open_event(menu_tree, menu_layout, cursor); + RecEvent::Event + } else if cursor.is_over(offset_bounds) { + RecEvent::Event + } else { + menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); + if prev.is_some() { RecEvent::None + } else { + RecEvent::Close } } } - } else { - prev_bounds_list.pop(); - - if cursor.is_over(prescroll) { - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); - menu.open_event(menu_tree, menu_layout, cursor); - RecEvent::Event - } else if cursor.is_over(offset_bounds) { - RecEvent::Event - } else { - menu.close_event(menu_tree, menu_layout, cursor, parent_bounds, prev_bounds_list, prev); - if prev.is_some() { - RecEvent::None + RecEvent::None => { + if menu_state.pressed || cursor.is_over(prescroll){ + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.open_event(menu_tree, menu_layout, cursor); + RecEvent::Event + } else if cursor.is_over(offset_bounds) { + RecEvent::Event } else { - RecEvent::Close + RecEvent::None } } } diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 6ca6aca5..247412e9 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -61,6 +61,7 @@ pub(super) struct MenuState { scroll_offset: f32, pub(super) active: Index, pub(super) slice: MenuSlice, + pub(super) pressed: bool, } impl Default for MenuState { fn default() -> Self { @@ -73,6 +74,7 @@ impl Default for MenuState { lower_bound_rel: 0.0, upper_bound_rel: f32::MAX, }, + pressed: false, } } } @@ -346,6 +348,16 @@ where .fold(Ignored, event::Status::merge); match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + if cursor.is_over(prescroll) { + menu_state.pressed = true; + } + Ignored + } + Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => { + menu_state.pressed = false; + Ignored + } Event::Mouse(mouse::Event::WheelScrolled { delta }) => { if cursor.is_over(prescroll) { process_scroll_event(menu_state, prescroll, *delta, viewport.size()); @@ -546,6 +558,12 @@ where let offset_bounds = lc.next().unwrap().bounds(); let check_bounds = lc.next().unwrap().bounds(); + let menu_state = tree.state.downcast_mut::(); + + if menu_state.pressed { + return; + } + let open = { if cursor.is_over(prescroll) || cursor.is_over(parent_bounds) @@ -561,9 +579,9 @@ where if !open { *prev = None; - let menu_state = tree.state.downcast_mut::(); menu_state.scroll_offset = 0.0; menu_state.active = None; + menu_state.pressed = false; } } } From 082301b9a835301f510f58fe28233da39764ac4c Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 23 Feb 2024 22:29:24 +0800 Subject: [PATCH 32/40] clippy --- src/native/menu/menu_bar.rs | 2 +- src/native/menu/menu_bar_overlay.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index d0270025..b8385636 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -277,7 +277,7 @@ where } DrawPath::FakeHovering => { if !cursor.is_over(active_bounds) { - cursor = mouse::Cursor::Available(active_bounds.center()) + cursor = mouse::Cursor::Available(active_bounds.center()); } } } diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 5bad3e7a..e5a82e54 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -483,7 +483,7 @@ where } rec( - &self.draw_path, + self.draw_path, active_tree, active_root, &mut menu_layouts, @@ -500,7 +500,7 @@ where let mut lc = layout.children(); let _bar_bounds = lc.next().unwrap().bounds(); let _roots_layout = lc.next().unwrap(); - let Some(menu_layouts) = lc.next().map(|l| l.children()) else { + let Some(menu_layouts) = lc.next().map(Layout::children) else { return false; }; // [menu_node...] From 3284e29e2a2b4cb0375c948b9ea9fa4132a709f2 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 23 Feb 2024 22:39:01 +0800 Subject: [PATCH 33/40] menu bar mouse interaction --- src/native/menu/menu_bar.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index b8385636..c31773a5 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -237,6 +237,25 @@ where .merge(status) } + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.roots + .iter() + .zip(&tree.children) + .zip(layout.children()) + .map(|((item, tree), layout)| { + item.mouse_interaction(tree, layout, cursor, viewport, renderer) + }) + .max() + .unwrap_or_default() + } + fn draw( &self, tree: &Tree, From f4422e6483bfab75e9664f3c1a3f3ccb477beecb Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Fri, 23 Feb 2024 23:06:54 +0800 Subject: [PATCH 34/40] fmt and clippy --- src/native/menu/menu_bar_overlay.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index e5a82e54..e4fec59d 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -376,7 +376,7 @@ where let i = menu.mouse_interaction(menu_tree, menu_layout, cursor, viewport, renderer); - if let Some(active) = menu_state.active { + menu_state.active.map_or(i, |active| { let next_tree = &menu_tree.children[active]; let next_item = &menu.items[active]; rec( @@ -388,9 +388,7 @@ where viewport, ) .max(i) - } else { - i - } + }) } rec( From 3577b189d9725b2f8f85c61c11df34ae9cdaddeb Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 24 Feb 2024 00:11:45 +0800 Subject: [PATCH 35/40] fix down scrolling clipping --- src/native/menu/menu_tree.rs | 69 ++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 247412e9..c186bd27 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -264,11 +264,8 @@ where let end_node = { let node = &items_node.children()[slice.end_index]; let bounds = node.bounds(); - Node::with_children( - Size::new(bounds.width, slice.upper_bound_rel - bounds.y), - node.children().iter().map(Clone::clone).collect(), - ) - .move_to(bounds.position()) + let factor = ((slice.upper_bound_rel - bounds.y)/bounds.height).max(0.0); + scale_node_y(node, factor) }; Node::with_children( @@ -468,7 +465,7 @@ where } } - // draw start + // prep start let Some(start) = self.items.get(slice.start_index) else { return; }; @@ -480,37 +477,47 @@ where }; if slice.end_index == slice.start_index { - start.draw( - start_tree, - renderer, - theme, - style, - start_layout, - cursor, - viewport, - ); - } else { - let start_bounds = start_layout.bounds(); - renderer.with_layer(start_bounds, |r| { - start.draw(start_tree, r, theme, style, start_layout, cursor, viewport); - }); - - // draw the rest - let Some(items) = self.items.get(slice.start_index + 1..=slice.end_index) else { + start.draw(start_tree, renderer, theme, style, start_layout, cursor, viewport); + }else{ + // prep end + let Some(end) = self.items.get(slice.end_index) else { return; }; - - let Some(trees) = tree.children.get(slice.start_index + 1..=slice.end_index) else { + let Some(end_tree) = tree.children.get(slice.end_index) else { + return; + }; + let Some(end_layout) = slice_layout.children().last() else{ return; }; - for ((item, tree), layout) in items - .iter() - .zip(trees.iter()) - .zip(slice_layout.children().skip(1)) - { - item.draw(tree, renderer, theme, style, layout, cursor, viewport); + // draw start + renderer.with_layer(start_layout.bounds(), |r| { + start.draw(start_tree, r, theme, style, start_layout, cursor, viewport); + }); + + // draw middle + if (slice.end_index - slice.start_index) > 1 { + let Some(items) = self.items.get(slice.start_index + 1..=slice.end_index-1) else { + return; + }; + + let Some(trees) = tree.children.get(slice.start_index + 1..=slice.end_index-1) else { + return; + }; + + for ((item, tree), layout) in items + .iter() + .zip(trees.iter()) + .zip(slice_layout.children().skip(1)) + { + item.draw(tree, renderer, theme, style, layout, cursor, viewport); + } } + + // draw end + renderer.with_layer(end_layout.bounds(), |r|{ + end.draw(end_tree, r, theme, style, end_layout, cursor, viewport) + }) } } From 5857a554f2d982f159d9e3d6cba7cac8225cee81 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 24 Feb 2024 00:21:40 +0800 Subject: [PATCH 36/40] scroll speed --- src/native/menu/menu_bar.rs | 9 +++++++++ src/native/menu/menu_bar_overlay.rs | 8 ++++++-- src/native/menu/menu_tree.rs | 6 ++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index c31773a5..504b80f9 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -39,6 +39,7 @@ where height: Length, check_bounds_width: f32, draw_path: DrawPath, + scroll_speed: f32, style: Theme::Style, } impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> @@ -62,6 +63,7 @@ where height: Length::Shrink, check_bounds_width: 50.0, draw_path: DrawPath::FakeHovering, + scroll_speed: 1.0, style: Theme::Style::default(), } } @@ -96,6 +98,12 @@ where self } + /// Sets the scroll speed of the [`Menu`]s in the [`MenuBar`] + pub fn scroll_speed(mut self, scroll_speed: f32) -> Self { + self.scroll_speed = scroll_speed; + self + } + /// Sets the padding of the [`MenuBar`]. pub fn padding(mut self, padding: impl Into) -> Self { self.padding = padding.into(); @@ -334,6 +342,7 @@ where init_root_bounds, check_bounds_width: self.check_bounds_width, draw_path: &self.draw_path, + scroll_speed: self.scroll_speed, style: &self.style, // is_over: false, } diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index e4fec59d..1af65e76 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -33,6 +33,7 @@ where pub(super) init_root_bounds: Vec, pub(super) check_bounds_width: f32, pub(super) draw_path: &'b DrawPath, + pub(super) scroll_speed: f32, pub(super) style: &'b Theme::Style, // pub(super) is_over: bool, } @@ -223,6 +224,7 @@ where viewport: &Rectangle, prev_bounds_list: &mut Vec, prev: &mut Index, + scroll_speed: f32, ) -> RecEvent { let menu = item.menu.as_mut().expect("No menu defined in this item"); let menu_tree = &mut tree.children[1]; @@ -267,6 +269,7 @@ where viewport, prev_bounds_list, &mut menu_state.active, + scroll_speed, ) } else { RecEvent::Close @@ -278,7 +281,7 @@ where RecEvent::Event => RecEvent::Event, RecEvent::Close => { if menu_state.pressed || cursor.is_over(prescroll){ - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport, scroll_speed); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event } else if cursor.is_over(offset_bounds) { @@ -294,7 +297,7 @@ where } RecEvent::None => { if menu_state.pressed || cursor.is_over(prescroll){ - menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport); + menu.on_event(menu_tree, event, menu_layout, cursor, renderer, clipboard, shell, viewport, scroll_speed); menu.open_event(menu_tree, menu_layout, cursor); RecEvent::Event } else if cursor.is_over(offset_bounds) { @@ -319,6 +322,7 @@ where &viewport, &mut prev_bounds_list, &mut bar.active_root, + self.scroll_speed ); match re { diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index c186bd27..26904f3d 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -314,6 +314,7 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, + scroll_speed: f32, ) -> event::Status { use event::Status::*; @@ -357,7 +358,7 @@ where } Event::Mouse(mouse::Event::WheelScrolled { delta }) => { if cursor.is_over(prescroll) { - process_scroll_event(menu_state, prescroll, *delta, viewport.size()); + process_scroll_event(menu_state, prescroll, *delta, scroll_speed, viewport.size()); Captured } else if cursor.is_over(offset_bounds) || cursor.is_over(check_bounds) { Captured @@ -991,6 +992,7 @@ fn process_scroll_event( menu_state: &mut MenuState, prescroll_children_bounds: Rectangle, delta: mouse::ScrollDelta, + scroll_speed: f32, viewport_size: Size, ) { use mouse::ScrollDelta; @@ -998,7 +1000,7 @@ fn process_scroll_event( let pcb = prescroll_children_bounds; let delta_y = match delta { - ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y, + ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y * scroll_speed, }; let max_offset = (0.0 - pcb.y).max(0.0); From ac69e4bc52be7868b49ff885fe33b9663f6ac97a Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 24 Feb 2024 00:49:12 +0800 Subject: [PATCH 37/40] fmt --- src/native/menu/menu_bar.rs | 1 - src/native/menu/menu_bar_overlay.rs | 3 +-- src/native/menu/menu_tree.rs | 38 +++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 504b80f9..8dbe9320 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -344,7 +344,6 @@ where draw_path: &self.draw_path, scroll_speed: self.scroll_speed, style: &self.style, - // is_over: false, } .overlay_element(), ) diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index 1af65e76..d4304a28 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -35,7 +35,6 @@ where pub(super) draw_path: &'b DrawPath, pub(super) scroll_speed: f32, pub(super) style: &'b Theme::Style, - // pub(super) is_over: bool, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> where @@ -322,7 +321,7 @@ where &viewport, &mut prev_bounds_list, &mut bar.active_root, - self.scroll_speed + self.scroll_speed, ); match re { diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 26904f3d..27dc5e54 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -264,7 +264,7 @@ where let end_node = { let node = &items_node.children()[slice.end_index]; let bounds = node.bounds(); - let factor = ((slice.upper_bound_rel - bounds.y)/bounds.height).max(0.0); + let factor = ((slice.upper_bound_rel - bounds.y) / bounds.height).max(0.0); scale_node_y(node, factor) }; @@ -358,7 +358,13 @@ where } Event::Mouse(mouse::Event::WheelScrolled { delta }) => { if cursor.is_over(prescroll) { - process_scroll_event(menu_state, prescroll, *delta, scroll_speed, viewport.size()); + process_scroll_event( + menu_state, + prescroll, + *delta, + scroll_speed, + viewport.size(), + ); Captured } else if cursor.is_over(offset_bounds) || cursor.is_over(check_bounds) { Captured @@ -478,8 +484,16 @@ where }; if slice.end_index == slice.start_index { - start.draw(start_tree, renderer, theme, style, start_layout, cursor, viewport); - }else{ + start.draw( + start_tree, + renderer, + theme, + style, + start_layout, + cursor, + viewport, + ); + } else { // prep end let Some(end) = self.items.get(slice.end_index) else { return; @@ -487,7 +501,7 @@ where let Some(end_tree) = tree.children.get(slice.end_index) else { return; }; - let Some(end_layout) = slice_layout.children().last() else{ + let Some(end_layout) = slice_layout.children().last() else { return; }; @@ -498,14 +512,18 @@ where // draw middle if (slice.end_index - slice.start_index) > 1 { - let Some(items) = self.items.get(slice.start_index + 1..=slice.end_index-1) else { + let Some(items) = self.items.get(slice.start_index + 1..=slice.end_index - 1) + else { return; }; - - let Some(trees) = tree.children.get(slice.start_index + 1..=slice.end_index-1) else { + + let Some(trees) = tree + .children + .get(slice.start_index + 1..=slice.end_index - 1) + else { return; }; - + for ((item, tree), layout) in items .iter() .zip(trees.iter()) @@ -516,7 +534,7 @@ where } // draw end - renderer.with_layer(end_layout.bounds(), |r|{ + renderer.with_layer(end_layout.bounds(), |r| { end.draw(end_tree, r, theme, style, end_layout, cursor, viewport) }) } From 2c9041a9e8247a78f0300f23be15d7866f50dd6b Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 24 Feb 2024 09:30:02 +0800 Subject: [PATCH 38/40] fmt and clippy --- src/native/floating_element.rs | 5 ++--- src/native/tabs.rs | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/native/floating_element.rs b/src/native/floating_element.rs index 2bc6fe96..e65bd181 100644 --- a/src/native/floating_element.rs +++ b/src/native/floating_element.rs @@ -206,6 +206,7 @@ where .operate(&mut state.children[0], layout, renderer, operation); } + #[allow(clippy::unwrap_in_result)] fn overlay<'b>( &'b mut self, state: &'b mut Tree, @@ -216,9 +217,7 @@ where let mut group = Group::new(); let mut children = state.children.iter_mut(); - if let Some(underlay) = self.underlay - .as_widget_mut() - .overlay( + if let Some(underlay) = self.underlay.as_widget_mut().overlay( children .next() .expect("missing underlay in floating element"), diff --git a/src/native/tabs.rs b/src/native/tabs.rs index 9488c534..38371e45 100644 --- a/src/native/tabs.rs +++ b/src/native/tabs.rs @@ -350,9 +350,7 @@ where tab_bar_bounds.y + match self.tab_bar_position { TabBarPosition::Top => 0.0, - TabBarPosition::Bottom => { - tab_content_node.bounds().height - } + TabBarPosition::Bottom => tab_content_node.bounds().height, }, )); From 2d391af020007a781188982e9965f7be75cf17e8 Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 24 Feb 2024 09:54:01 +0800 Subject: [PATCH 39/40] improve scroll clipping --- examples/menu/src/main.rs | 4 ++-- src/native/menu/menu_tree.rs | 40 ++++++++++++++---------------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 0cb61846..5348e0e7 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -208,7 +208,7 @@ impl Application for App { .width(Length::Fill) .on_press(Message::Debug("Button".into())) ) - (checkbox("Checkbox", self.check).on_toggle(|x| Message::CheckChange(x)) + (checkbox("Checkbox", self.check).on_toggle(Message::CheckChange) .width(Length::Fill) ) ( @@ -552,7 +552,7 @@ fn color_button<'a>( base_button(circle(color), Message::ColorChange(color)) } -fn separator<'a>() -> quad::Quad { +fn separator() -> quad::Quad { quad::Quad { quad_color: Color::from([0.5; 3]).into(), quad_border: Border { diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index 27dc5e54..fbcb613a 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -243,29 +243,26 @@ where let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; - let factor = ((bounds.height - start_offset) / bounds.height).max(0.0); + let height = slice.upper_bound_rel - slice.lower_bound_rel; Node::with_children( - Size::new( - items_node.bounds().width, - slice.upper_bound_rel - slice.lower_bound_rel, - ), - once(scale_node_y(node, factor).translate([0.0, start_offset])).collect(), + Size::new(items_node.bounds().width, height), + once(clip_node_y(node, height, start_offset)).collect(), ) } else { let start_node = { let node = &items_node.children()[slice.start_index]; let bounds = node.bounds(); let start_offset = slice.lower_bound_rel - bounds.y; - let factor = ((bounds.height - start_offset) / bounds.height).max(0.0); - scale_node_y(node, factor).translate([0.0, start_offset]) + let height = bounds.height - start_offset; + clip_node_y(node, height, start_offset) }; let end_node = { let node = &items_node.children()[slice.end_index]; let bounds = node.bounds(); - let factor = ((slice.upper_bound_rel - bounds.y) / bounds.height).max(0.0); - scale_node_y(node, factor) + let height = slice.upper_bound_rel - bounds.y; + clip_node_y(node, height, 0.0) }; Node::with_children( @@ -484,15 +481,10 @@ where }; if slice.end_index == slice.start_index { - start.draw( - start_tree, - renderer, - theme, - style, - start_layout, - cursor, - viewport, - ); + // draw start + renderer.with_layer(start_layout.bounds(), |r| { + start.draw(start_tree, r, theme, style, start_layout, cursor, viewport); + }); } else { // prep end let Some(end) = self.items.get(slice.end_index) else { @@ -1093,17 +1085,15 @@ fn search_bound(default_left: usize, default_right: usize, bound: f32, list: &[N left } -fn scale_node_y(node: &Node, factor: f32) -> Node { +fn clip_node_y(node: &Node, height: f32, offset: f32) -> Node { let node_bounds = node.bounds(); Node::with_children( - Size::new(node_bounds.width, node_bounds.height * factor), + Size::new(node_bounds.width, height), node.children() .iter() - .map(|n| { - let n_bounds = n.bounds(); - scale_node_y(n, factor).move_to(Point::new(n_bounds.x, n_bounds.y * factor)) - }) + .map(|n| n.clone().translate([0.0, -offset])) .collect(), ) .move_to(node_bounds.position()) + .translate([0.0, offset]) } From 0d536dcfee909924444d60cb275ecaf2047ddb3c Mon Sep 17 00:00:00 2001 From: Latidoremi Date: Sat, 24 Feb 2024 10:18:46 +0800 Subject: [PATCH 40/40] separate scroll speed for line and pixel --- src/native/menu.rs | 2 +- src/native/menu/common.rs | 9 +++++++++ src/native/menu/menu_bar.rs | 9 ++++++--- src/native/menu/menu_bar_overlay.rs | 4 ++-- src/native/menu/menu_tree.rs | 7 ++++--- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/native/menu.rs b/src/native/menu.rs index c0013a00..9210c196 100644 --- a/src/native/menu.rs +++ b/src/native/menu.rs @@ -150,6 +150,6 @@ mod menu_bar_overlay; mod menu_tree; pub use crate::style::menu_bar::{Appearance, StyleSheet}; -pub use common::DrawPath; +pub use common::{DrawPath, ScrollSpeed}; pub use menu_bar::MenuBar; pub use menu_tree::{Item, Menu}; diff --git a/src/native/menu/common.rs b/src/native/menu/common.rs index 88a65f4f..01aee96e 100644 --- a/src/native/menu/common.rs +++ b/src/native/menu/common.rs @@ -75,6 +75,15 @@ pub(super) enum RecEvent { None, } +#[derive(Debug, Clone, Copy)] +/// Scroll speed +pub struct ScrollSpeed { + /// Speed of line-based scroll movement + pub line: f32, + /// Speed of Pixel scroll movement + pub pixel: f32, +} + pub fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle { Rectangle { x: rect.x - padding.left, diff --git a/src/native/menu/menu_bar.rs b/src/native/menu/menu_bar.rs index 8dbe9320..1dcab0d2 100644 --- a/src/native/menu/menu_bar.rs +++ b/src/native/menu/menu_bar.rs @@ -39,7 +39,7 @@ where height: Length, check_bounds_width: f32, draw_path: DrawPath, - scroll_speed: f32, + scroll_speed: ScrollSpeed, style: Theme::Style, } impl<'a, Message, Theme, Renderer> MenuBar<'a, Message, Theme, Renderer> @@ -63,7 +63,10 @@ where height: Length::Shrink, check_bounds_width: 50.0, draw_path: DrawPath::FakeHovering, - scroll_speed: 1.0, + scroll_speed: ScrollSpeed { + line: 60.0, + pixel: 1.0, + }, style: Theme::Style::default(), } } @@ -99,7 +102,7 @@ where } /// Sets the scroll speed of the [`Menu`]s in the [`MenuBar`] - pub fn scroll_speed(mut self, scroll_speed: f32) -> Self { + pub fn scroll_speed(mut self, scroll_speed: ScrollSpeed) -> Self { self.scroll_speed = scroll_speed; self } diff --git a/src/native/menu/menu_bar_overlay.rs b/src/native/menu/menu_bar_overlay.rs index d4304a28..ced9b546 100644 --- a/src/native/menu/menu_bar_overlay.rs +++ b/src/native/menu/menu_bar_overlay.rs @@ -33,7 +33,7 @@ where pub(super) init_root_bounds: Vec, pub(super) check_bounds_width: f32, pub(super) draw_path: &'b DrawPath, - pub(super) scroll_speed: f32, + pub(super) scroll_speed: ScrollSpeed, pub(super) style: &'b Theme::Style, } impl<'a, 'b, Message, Theme, Renderer> MenuBarOverlay<'a, 'b, Message, Theme, Renderer> @@ -223,7 +223,7 @@ where viewport: &Rectangle, prev_bounds_list: &mut Vec, prev: &mut Index, - scroll_speed: f32, + scroll_speed: ScrollSpeed, ) -> RecEvent { let menu = item.menu.as_mut().expect("No menu defined in this item"); let menu_tree = &mut tree.children[1]; diff --git a/src/native/menu/menu_tree.rs b/src/native/menu/menu_tree.rs index fbcb613a..e1b17bb7 100644 --- a/src/native/menu/menu_tree.rs +++ b/src/native/menu/menu_tree.rs @@ -311,7 +311,7 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, - scroll_speed: f32, + scroll_speed: ScrollSpeed, ) -> event::Status { use event::Status::*; @@ -1002,7 +1002,7 @@ fn process_scroll_event( menu_state: &mut MenuState, prescroll_children_bounds: Rectangle, delta: mouse::ScrollDelta, - scroll_speed: f32, + scroll_speed: ScrollSpeed, viewport_size: Size, ) { use mouse::ScrollDelta; @@ -1010,7 +1010,8 @@ fn process_scroll_event( let pcb = prescroll_children_bounds; let delta_y = match delta { - ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => y * scroll_speed, + ScrollDelta::Lines { y, .. } => y * scroll_speed.line, + ScrollDelta::Pixels { y, .. } => y * scroll_speed.pixel, }; let max_offset = (0.0 - pcb.y).max(0.0);