From 07e53ddadd7206f01091dae1042dfdb542062895 Mon Sep 17 00:00:00 2001 From: Eduardo Flores Date: Thu, 13 Oct 2022 02:23:37 -0700 Subject: [PATCH] Reimplemented NavigationBar - Navigation Bar was reimplemented to support sections and pages. - Created new widget called separator, a horizontal rule with the COSMIC theme. --- examples/cosmic/src/window.rs | 155 ++++++---- src/widget/expander.rs | 11 +- src/widget/list/list_box.rs | 63 ++-- src/widget/list/macros.rs | 16 +- src/widget/mod.rs | 3 + src/widget/navigation/macros.rs | 14 +- src/widget/navigation/navbar.rs | 504 ++++++++++++++------------------ src/widget/separator.rs | 7 + 8 files changed, 390 insertions(+), 383 deletions(-) create mode 100644 src/widget/separator.rs diff --git a/examples/cosmic/src/window.rs b/examples/cosmic/src/window.rs index 26c758c5..ecf070b7 100644 --- a/examples/cosmic/src/window.rs +++ b/examples/cosmic/src/window.rs @@ -1,4 +1,4 @@ -use cosmic::widget::{expander, ListBox}; +use cosmic::widget::{expander, nav_bar, nav_bar_page, nav_bar_section}; use cosmic::{ iced::widget::{ checkbox, column, container, horizontal_space, pick_list, progress_bar, radio, row, slider, @@ -7,9 +7,10 @@ use cosmic::{ iced::{self, theme, Alignment, Application, Color, Command, Element, Length, Theme}, iced_lazy::responsive, iced_winit::window::{drag, maximize, minimize}, - list_item, list_row, list_section, list_view, nav_button, scrollable, - widget::{button, header_bar, list_row, list_view::*, nav_bar::nav_bar_style, toggler}, + list_view, list_view_item, list_view_row, list_view_section, scrollable, + widget::{button, header_bar, list_box, list_row, list_view::*, toggler}, }; +use std::collections::BTreeMap; #[derive(Default)] pub struct Window { @@ -101,7 +102,7 @@ impl Application for Window { Message::RowSelected(row) => println!("Selected row {row}"), } - iced::Command::none() + Command::none() } fn view(&self) -> Element { @@ -128,38 +129,84 @@ impl Application for Window { let content = responsive(|size| { let condensed = size.width < 900.0; - let sidebar: Element<_> = cosmic::navbar![ - nav_button!("network-wireless", "Wi-Fi", condensed) - .on_press(Message::Page(0)) - .style(if self.page == 0 { - theme::Button::Primary - } else { - theme::Button::Text - }), - nav_button!("preferences-desktop", "Desktop", condensed) - .on_press(Message::Page(1)) - .style(if self.page == 1 { - theme::Button::Primary - } else { - theme::Button::Text - }), - nav_button!("system-software-update", "OS Upgrade & Recovery", condensed) - .on_press(Message::Page(2)) - .style(if self.page == 2 { - theme::Button::Primary - } else { - theme::Button::Text - }), - ] - .active(self.sidebar_toggled) - .condensed(condensed) - .style(theme::Container::Custom(nav_bar_style)) - .into(); + // cosmic::navbar![ + // nav_button!("network-wireless", "Network & Wireless", condensed) + // .on_press(Message::Page(0)) + // .style(if self.page == 0 { + // theme::Button::Primary + // } else { + // theme::Button::Text + // }), + // nav_button!("preferences-desktop", "Bluetooth", condensed) + // .on_press(Message::Page(1)) + // .style(if self.page == 1 { + // theme::Button::Primary + // } else { + // theme::Button::Text + // }), + // nav_button!("system-software-update", "Personalization", condensed) + // .on_press(Message::Page(2)) + // .style(if self.page == 2 { + // theme::Button::Primary + // } else { + // theme::Button::Text + // }), + // ] + + let sidebar: Element<_> = nav_bar() + .source(BTreeMap::from([ + ( + nav_bar_section() + .title("Network & Wireless") + .icon("network-wireless"), + vec![nav_bar_page("Wi-Fi")], + ), + ( + nav_bar_section() + .title("Bluetooth") + .icon("cs-bluetooth"), + vec![nav_bar_page("Devices")], + ), + ( + nav_bar_section() + .title("Personalization") + .icon("applications-system"), + vec![ + nav_bar_page("Desktop Session"), + nav_bar_page("Wallpaper"), + nav_bar_page("Appearance"), + nav_bar_page("Dock & Top Panel"), + nav_bar_page("Workspaces"), + nav_bar_page("Notifications"), + ], + ), + ( + nav_bar_section() + .title("Input Devices") + .icon("input-keyboard"), + vec![nav_bar_page("Keyboard")], + ), + ( + nav_bar_section().title("Displays").icon("cs-display"), + vec![nav_bar_page("Keyboard")], + ), + ( + nav_bar_section() + .title("Power & Battery") + .icon("battery"), + vec![nav_bar_page("Status")], + ), + ( + nav_bar_section().title("Sound").icon("sound"), + vec![nav_bar_page("Volume")], + ), + ])) + .active(self.sidebar_toggled) + .condensed(condensed) + .into(); let choose_theme = [Theme::Light, Theme::Dark].iter().fold( - row![text("Debug theme:")] - .spacing(10) - .align_items(Alignment::Center), + row![].spacing(10).align_items(Alignment::Center), |row, theme| { row.push(radio( format!("{:?}", theme), @@ -171,14 +218,17 @@ impl Application for Window { ); let content: Element<_> = list_view!( - list_section!( + list_view_section!( "Debug", - choose_theme, - toggler(String::from("Debug layout"), self.debug, Message::Debug,) + list_view_item!("Debug theme", choose_theme), + list_view_item!( + "Debug layout", + toggler(String::from("Debug layout"), self.debug, Message::Debug,) + ) ), - list_section!( + list_view_section!( "Buttons", - list_row!( + list_view_row!( button!("Primary") .style(theme::Button::Primary) .on_press(Message::ButtonPressed), @@ -195,7 +245,7 @@ impl Application for Window { .style(theme::Button::Text) .on_press(Message::ButtonPressed), ), - list_row!( + list_view_row!( button!("Primary").style(theme::Button::Primary), button!("Secondary").style(theme::Button::Secondary), button!("Positive").style(theme::Button::Positive), @@ -203,13 +253,13 @@ impl Application for Window { button!("Text").style(theme::Button::Text), ), ), - list_section!( + list_view_section!( "Controls", - list_item!( + list_view_item!( "Toggler", toggler(None, self.toggler_value, Message::TogglerToggled) ), - list_item!( + list_view_item!( "Pick List (TODO)", pick_list( vec!["Option 1", "Option 2", "Option 3", "Option 4",], @@ -218,12 +268,12 @@ impl Application for Window { ) .padding([8, 0, 8, 16]) ), - list_item!( + list_view_item!( "Slider", slider(0.0..=100.0, self.slider_value, Message::SliderChanged) .width(Length::Units(250)) ), - list_item!( + list_view_item!( "Progress", progress_bar(0.0..=100.0, self.slider_value) .width(Length::Units(250)) @@ -231,7 +281,7 @@ impl Application for Window { ), checkbox("Checkbox", self.checkbox_value, Message::CheckboxToggled), ), - list_section!( + list_view_section!( "Expander", expander() .title("Label") @@ -247,19 +297,18 @@ impl Application for Window { list_row().title("Label") ]) ), - list_section!( + list_view_section!( "List Box", - ListBox::with_children( - vec![ + list_box() + .style(theme::Container::Custom(list_section_style)) + .children(vec![ cosmic::list_box_row!("Title").into(), cosmic::list_box_row!("Title", "Subtitle").into(), cosmic::list_box_row!("Title", "", "edit-paste").into(), cosmic::list_box_row!("", "Subtitle", "edit-paste").into(), cosmic::list_box_row!("Title", "Subtitle", "edit-paste").into() - ], - true, - ) - .style(theme::Container::Custom(list_section_style)) + ]) + .render() ), ) .into(); diff --git a/src/widget/expander.rs b/src/widget/expander.rs index d200aad0..484e3614 100644 --- a/src/widget/expander.rs +++ b/src/widget/expander.rs @@ -1,6 +1,6 @@ use std::vec; -use crate::{list_box_row, widget::ListRow}; +use crate::{list_box_row, separator, widget::ListRow}; use apply::Apply; use derive_setters::Setters; use iced::{ @@ -9,7 +9,7 @@ use iced::{ Alignment, Background, Element, Length, Renderer, Theme, }; use iced_lazy::Component; -use iced_native::widget::{column, event_container, horizontal_rule}; +use iced_native::widget::{column, event_container}; #[derive(Setters)] pub struct Expander<'a, Message> { @@ -145,12 +145,7 @@ impl<'a, Message: Clone + 'a> Component for Expander<'a, Mess .enumerate() .flat_map(|(index, child)| { if index != rows.len() - 1 { - vec![ - child, - horizontal_rule(1) - .style(theme::Rule::Custom(separator_style)) - .into(), - ] + vec![child, separator!(1).into()] } else { vec![child] } diff --git a/src/widget/list/list_box.rs b/src/widget/list/list_box.rs index a0a52902..f8ce7dba 100644 --- a/src/widget/list/list_box.rs +++ b/src/widget/list/list_box.rs @@ -1,3 +1,4 @@ +use crate::separator; use derive_setters::Setters; use iced::mouse::Interaction; use iced::{overlay, Alignment, Length, Padding, Point, Rectangle}; @@ -6,7 +7,7 @@ use iced_native::layout::flex::{resolve, Axis}; use iced_native::layout::{Limits, Node}; use iced_native::overlay::from_children; use iced_native::renderer::Style; -use iced_native::widget::{column, horizontal_rule, Operation, Tree}; +use iced_native::widget::{column, Operation, Tree}; use iced_native::{ renderer, row, Background, Clipboard, Color, Element, Event, Layout, Shell, Widget, }; @@ -15,6 +16,7 @@ use iced_style::theme; use iced_style::theme::Container; #[derive(Setters)] +#[allow(dead_code)] pub struct ListBox<'a, Message, Renderer> where Renderer: iced_native::Renderer, @@ -22,6 +24,7 @@ where ::Theme: iced_style::rule::StyleSheet, { spacing: u16, + #[setters(into)] padding: Padding, width: Length, height: Length, @@ -31,6 +34,7 @@ where children: Vec>, #[setters(strip_option)] placeholder: Option>, + show_separators: bool, on_item_selected: Option Message + 'a>>, } @@ -57,51 +61,54 @@ where /// Creates an empty [`ListBox`]. pub fn new() -> Self { - Self::with_children(Vec::>::new(), true) + Self::with_children(Vec::>::new()).render() } /// Creates a new [`ListBox`]. /// /// [`ListBox`]: struct.ListBox.html - pub fn with_children( - children: Vec>, - show_separators: bool, - ) -> Self { - let children_size = children.len(); - let children: Vec> = children - .into_iter() - .enumerate() - .map(|(index, child)| { - let row_items = if show_separators && index != children_size - 1 { - vec![ - row![child].align_items(Alignment::Center).into(), - horizontal_rule(1) - .style(theme::Rule::Custom(separator_style)) - .into(), - ] - } else { - vec![row![child].align_items(Alignment::Center).into()] - }; - column(row_items).into() - }) - .collect(); - Self { + pub fn with_children(children: Vec>) -> Self { + let list_box = Self { spacing: 0, padding: Padding::from(Self::DEFAULT_PADDING), width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, - align_items: Alignment::Start, + align_items: Alignment::Center, style: Default::default(), children, placeholder: None, + show_separators: true, on_item_selected: None, - } + }; + list_box.render() + } + + pub fn render(mut self) -> Self { + let children_size = self.children.len(); + self.children = self + .children + .into_iter() + .enumerate() + .map(|(index, child)| { + let row_items = if self.show_separators && index != children_size - 1 { + vec![ + row![child].align_items(self.align_items).into(), + separator!(1).into(), + ] + } else { + vec![row![child].align_items(self.align_items).into()] + }; + column(row_items).spacing(self.spacing).into() + }) + .collect(); + self } /// Adds an element to the [`ListBox`]. pub fn push(mut self, child: impl Into>) -> Self { self.children.push(child.into()); + self = self.render(); self } } @@ -444,5 +451,3 @@ macro_rules! list_box_heading { }; } pub use list_box_heading; - -use crate::widget::separator_style; diff --git a/src/widget/list/macros.rs b/src/widget/list/macros.rs index 52609320..67ec3c16 100644 --- a/src/widget/list/macros.rs +++ b/src/widget/list/macros.rs @@ -14,7 +14,7 @@ pub mod list_view { } #[macro_export] - macro_rules! list_row { + macro_rules! list_view_row { ($($x:expr),+ $(,)?) => ( $crate::iced::widget::Row::with_children(vec![ $($crate::iced::Element::from($x)),+ @@ -26,7 +26,7 @@ pub mod list_view { } #[macro_export] - macro_rules! list_section { + macro_rules! list_view_section { ($title:expr, $($x:expr),+ $(,)?) => ( $crate::iced::widget::Column::with_children(vec![ $crate::iced::widget::Text::new($title) @@ -39,7 +39,7 @@ pub mod list_view { //TODO: more efficient method for adding separators let mut i = 1; while i < children.len() { - children.insert(i, $crate::iced::widget::horizontal_rule(12).into()); + children.insert(i, $crate::separator!(12).into()); i += 2; } @@ -57,9 +57,9 @@ pub mod list_view { } #[macro_export] - macro_rules! list_item { + macro_rules! list_view_item { ($title:expr, $($x:expr),+ $(,)?) => ( - $crate::list_row!( + $crate::list_view_row!( $crate::iced::widget::Text::new($title), $crate::iced::widget::horizontal_space( $crate::iced::Length::Fill @@ -84,10 +84,10 @@ pub mod list_view { use iced::widget; use iced_style::Theme; - pub use list_item; - pub use list_row; - pub use list_section; pub use list_view; + pub use list_view_item; + pub use list_view_row; + pub use list_view_section; } pub mod list_box { diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 83e27946..196a011f 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -21,3 +21,6 @@ pub use expander::*; pub mod list; pub use list::*; + +pub mod separator; +pub use separator::*; diff --git a/src/widget/navigation/macros.rs b/src/widget/navigation/macros.rs index 674c0df0..e00f8784 100644 --- a/src/widget/navigation/macros.rs +++ b/src/widget/navigation/macros.rs @@ -16,7 +16,7 @@ pub mod nav_bar { }}; } - pub fn nav_bar_style(theme: &Theme) -> widget::container::Appearance { + pub fn nav_bar_sections_style(theme: &Theme) -> widget::container::Appearance { let cosmic = &theme.cosmic().primary; widget::container::Appearance { text_color: Some(cosmic.on.into()), @@ -27,5 +27,17 @@ pub mod nav_bar { } } + pub fn nav_bar_pages_style(theme: &Theme) -> widget::container::Appearance { + let primary = &theme.cosmic().primary; + let secondary = &theme.cosmic().secondary; + widget::container::Appearance { + text_color: Some(primary.on.into()), + background: Some(Background::Color(secondary.component.base.into())), + border_radius: 8.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + } + } + pub use nav_button; } diff --git a/src/widget/navigation/navbar.rs b/src/widget/navigation/navbar.rs index c15c897a..67a99815 100644 --- a/src/widget/navigation/navbar.rs +++ b/src/widget/navigation/navbar.rs @@ -1,313 +1,249 @@ -use iced::{ - alignment, - widget::{scrollable, Column, Container}, - Alignment, Element, Length, Padding, -}; -use iced_native::{ - renderer, row, - widget::{ - container::{draw_background, layout}, - Tree, - }, - Widget, -}; -use iced_style::container::StyleSheet; +use crate::scrollable; +use crate::widget::nav_bar::{nav_bar_pages_style, nav_bar_sections_style}; +use crate::widget::{icon, Background}; +use derive_setters::Setters; +use iced::Length; +use iced_lazy::Component; +use iced_native::widget::{button, column, container, text}; +use iced_native::{row, Alignment, Element}; +use iced_style::button::Appearance; +use iced_style::{scrollable, theme, Theme}; +use std::collections::BTreeMap; -pub struct NavBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - spacing: u16, - padding: Padding, - width: Length, - height: Length, - max_width: u32, - max_height: u32, - align_items: Alignment, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, - style: ::Style, - condensed: bool, +#[derive(Setters, Default)] +pub struct NavBar<'a, Message> { + source: BTreeMap>, active: bool, - content: Element<'a, Message, Renderer>, + condensed: bool, + on_page_selected: Option Message + 'a>>, } -impl<'a, Message: 'a, Renderer> NavBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - /// Creates a [`NavBar`] with the given elements. - pub fn with_children(children: Vec>) -> Self - where - ::Theme: iced_style::scrollable::StyleSheet, - { - let nav = Self::default(); - NavBar { - content: Container::new( - scrollable(row![Column::with_children(children) - .spacing(nav.spacing) - .padding(nav.padding)]) - .scrollbar_width(6) - .scroller_width(6), - ) - .into(), - ..Default::default() - } - } - - pub fn condensed(mut self, condensed: bool) -> Self { - self.condensed = condensed; - self - } - - pub fn active(mut self, active: bool) -> Self { - self.active = active; - self - } - /// Sets the horizontal 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, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the [`Padding`] of the [`NavBar`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`NavBar`]. - pub fn width(mut self, width: iced::Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`NavBar`]. - pub fn height(mut self, height: iced::Length) -> Self { - self.height = height; - self - } - - /// Sets the vertical alignment of the contents of the [`NavBar`] . - pub fn align_items(mut self, align: iced::Alignment) -> Self { - self.align_items = align; - self - } - - /// Sets the maximum width of the [`NavBar`]. - pub fn max_width(mut self, max_width: u32) -> Self { - self.max_width = max_width; - self - } - - /// Sets the maximum height of the [`NavBar`]. - pub fn max_height(mut self, max_height: u32) -> Self { - self.max_height = max_height; - self - } - - /// Sets the style of the [`NavBar`]. - pub fn style(mut self, style: impl Into<::Style>) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message: 'a, Renderer> Default for NavBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn default() -> Self { +impl<'a, Message> NavBar<'a, Message> { + pub fn new() -> Self { Self { - spacing: 12, - padding: Padding::new(12), - width: Length::Shrink, - height: Length::Fill, - max_width: 300, - max_height: u32::MAX, - align_items: Alignment::Start, - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - style: Default::default(), + source: Default::default(), + active: false, condensed: false, - active: true, - content: Container::new(row![Column::new()]).into(), + on_page_selected: None, } } } -impl<'a, Message, Renderer> Widget for NavBar<'a, Message, Renderer> +pub fn nav_bar<'a, Message>() -> NavBar<'a, Message> { + NavBar::new() +} + +#[derive(Setters, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct NavBarSection { + #[setters(into)] + title: String, + #[setters(into)] + icon: String, +} + +impl NavBarSection { + pub fn new() -> Self { + Self { + title: String::new(), + icon: String::new(), + } + } +} + +pub fn nav_bar_section() -> NavBarSection { + NavBarSection::new() +} + +#[derive(Default, Clone, Setters, PartialOrd, Ord, PartialEq, Eq)] +pub struct NavBarPage { + #[setters(into)] + title: String, +} + +impl NavBarPage { + pub fn new() -> Self { + Self { + title: String::new(), + } + } +} + +pub fn nav_bar_page(title: &str) -> NavBarPage { + let mut page = NavBarPage::new(); + page.title = title.to_string(); + page +} + +#[derive(Clone)] +pub enum NavBarEvent { + SectionSelected(NavBarSection), + PageSelected(NavBarSection, NavBarPage), +} + +#[derive(Default)] +pub struct NavBarState { + selected_section: NavBarSection, + section_active: bool, + selected_page: Option, + page_active: bool, +} + +impl<'a, Message, Renderer> Component for NavBar<'a, Message> where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, + Renderer: iced_native::Renderer + iced_native::text::Renderer + iced_native::svg::Renderer + 'a, + ::Theme: + container::StyleSheet + button::StyleSheet + text::StyleSheet + scrollable::StyleSheet, + <::Theme as button::StyleSheet>::Style: From, + <::Theme as container::StyleSheet>::Style: + From, + <::Theme as text::StyleSheet>::Style: From, { - fn width(&self) -> Length { - self.width - } + type State = NavBarState; + type Event = NavBarEvent; - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &iced_native::layout::Limits, - ) -> iced_native::layout::Node { - layout( - renderer, - limits, - self.width, - self.height, - if self.condensed { 100 } else { self.max_width }, - self.max_height, - if self.active { - self.padding - } else { - Padding::ZERO - }, - self.horizontal_alignment, - self.vertical_alignment, - |renderer, limits| { - if self.active { - self.content.as_widget().layout(renderer, limits) + fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option { + match event { + NavBarEvent::SectionSelected(section) => { + if state.selected_section == section { + state.section_active = !state.section_active; } else { - let content: Element = - Container::new(row![Column::new()]).into(); - content.as_widget().layout(renderer, limits) + state.selected_section = section; + state.section_active = true; } - }, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - renderer_style: &iced_native::renderer::Style, - layout: iced_native::Layout<'_>, - cursor_position: iced::Point, - viewport: &iced::Rectangle, - ) { - if self.active { - let style = theme.appearance(self.style); - - draw_background(renderer, &style, layout.bounds()); - - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - text_color: style.text_color.unwrap_or(renderer_style.text_color), - }, - layout.children().next().unwrap(), - cursor_position, - viewport, - ); + state.selected_page = None; + state.page_active = false; + None + } + NavBarEvent::PageSelected(section, page) => { + if state.selected_page.is_some() && &page == state.selected_page.as_ref().unwrap() { + state.page_active = !state.page_active; + } else { + state.selected_page = Some(page.clone()); + state.page_active = true; + } + self.on_page_selected + .as_ref() + .map(|on_page_selected| (on_page_selected)(section, page)) + } } } - fn children(&self) -> Vec { - vec![Tree::new(&self.content)] - } + fn view(&self, state: &Self::State) -> Element<'a, Self::Event, Renderer> { + if self.active { + let mut sections: Vec> = vec![]; + let mut pages: Vec> = vec![]; - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } + for (section, section_pages) in &self.source { + sections.push( + button( + column(vec![ + icon(§ion.icon, 20).into(), + text(§ion.title).size(14).into(), + ]) + .width(Length::Units(100)) + .height(Length::Units(50)) + .align_items(Alignment::Center), + ) + .style(if *section == state.selected_section && state.section_active { + theme::Button::Primary.into() + } else { + theme::Button::Text.into() + }) + .on_press(NavBarEvent::SectionSelected(section.clone())) + .into(), + ); + if *section == state.selected_section { + for page in section_pages { + pages.push( + button(row![text(&page.title).size(16).width(Length::Fill)]) + .padding(10) + .style( + if let Some(selected_page) = &state.selected_page { + if state.page_active && page == selected_page { + theme::Button::Primary.into() + } else { + theme::Button::Text.into() + } + } else { + theme::Button::Text.into() + } + ) + .on_press(NavBarEvent::PageSelected(section.clone(), page.clone())) + .into(), + ); + } + } + } - fn operate( - &self, - tree: &mut Tree, - layout: iced_native::Layout<'_>, - operation: &mut dyn iced_native::widget::Operation, - ) { - operation.container(None, &mut |operation| { - self.content.as_widget().operate( - &mut tree.children[0], - layout.children().next().unwrap(), - operation, - ); - }); - } - - fn on_event( - &mut self, - tree: &mut iced_native::widget::Tree, - event: iced::Event, - layout: iced_native::Layout<'_>, - cursor_position: iced::Point, - renderer: &Renderer, - clipboard: &mut dyn iced_native::Clipboard, - shell: &mut iced_native::Shell<'_, Message>, - ) -> iced::event::Status { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout.children().next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn mouse_interaction( - &self, - tree: &iced_native::widget::Tree, - layout: iced_native::Layout<'_>, - cursor_position: iced::Point, - viewport: &iced::Rectangle, - renderer: &Renderer, - ) -> iced_native::mouse::Interaction { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout.children().next().unwrap(), - cursor_position, - viewport, - renderer, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut iced_native::widget::Tree, - layout: iced_native::Layout<'_>, - renderer: &Renderer, - ) -> Option> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) + let nav_bar: Element = + container(if self.condensed && state.selected_page.is_some() { + row![container(scrollable!(column(pages) + .spacing(10) + .padding(10) + .max_width(200) + .width(Length::Units(200)) + .height(Length::Shrink))) + .height(Length::Fill) + .style(theme::Container::Custom(nav_bar_pages_style))] + } else if !state.section_active || self.condensed && state.selected_page.is_none() { + row![scrollable!(column(sections) + .spacing(10) + .padding(10) + .max_width(100) + .align_items(Alignment::Center) + .height(Length::Shrink))] + } else { + row![ + scrollable!(column(sections) + .spacing(10) + .padding(10) + .max_width(100) + .align_items(Alignment::Center) + .height(Length::Shrink)), + container(scrollable!(column(pages) + .spacing(10) + .padding(10) + .max_width(200) + .width(Length::Units(200)) + .height(Length::Shrink))) + .height(Length::Fill) + .style(theme::Container::Custom(nav_bar_pages_style)), + ] + }) + .height(Length::Fill) + .style(theme::Container::Custom(nav_bar_sections_style)) + .into(); + nav_bar + } else { + row![].into() + } } } -impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> +impl<'a, Message: 'a, Renderer> From> + for Element<'a, Message, Renderer> where - Message: 'a, - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, + Renderer: iced_native::text::Renderer + iced_native::svg::Renderer + 'a, + ::Theme: + container::StyleSheet + button::StyleSheet + text::StyleSheet + scrollable::StyleSheet, + <::Theme as button::StyleSheet>::Style: From, + <::Theme as container::StyleSheet>::Style: + From, + <::Theme as text::StyleSheet>::Style: From, { - fn from(navbar: NavBar<'a, Message, Renderer>) -> Self { - Self::new(navbar) + fn from(nav_bar: NavBar<'a, Message>) -> Self { + iced_lazy::component(nav_bar) } } -/// Creates a [NavBar`] with the given children. -/// -/// [`NavBar`]: widget::NavBar -#[macro_export] -macro_rules! navbar { - ($($x:expr),+ $(,)?) => ( - $crate::widget::NavBar::with_children(vec![$($crate::iced::Element::from($x)),+]) - ); +pub fn section_button_style(theme: &Theme) -> Appearance { + let primary = &theme.cosmic().primary; + Appearance { + shadow_offset: Default::default(), + background: Some(Background::Color(primary.base.into())), + border_radius: 5.0, + border_width: 0.0, + border_color: Default::default(), + text_color: Default::default(), + } } diff --git a/src/widget/separator.rs b/src/widget/separator.rs new file mode 100644 index 00000000..d69fb922 --- /dev/null +++ b/src/widget/separator.rs @@ -0,0 +1,7 @@ +#[macro_export] +macro_rules! separator { + ($size:expr) => { + $crate::iced::widget::horizontal_rule($size) + .style(theme::Rule::Custom($crate::widget::separator_style)) + }; +}