diff --git a/examples/cosmic/src/main.rs b/examples/cosmic/src/main.rs index d900bae..6583536 100644 --- a/examples/cosmic/src/main.rs +++ b/examples/cosmic/src/main.rs @@ -7,7 +7,8 @@ use cosmic::{ list_view, nav_button, toggler, - HeaderBar, nav_bar_style, Expander, ExpanderMsg, + nav_bar_style, + header_bar, }, settings, iced::{self, theme, Alignment, Application, Color, Command, Element, Length, Theme}, @@ -25,7 +26,6 @@ use cosmic::{ }, iced_lazy::responsive, iced_winit::window::drag, - WindowMsg, scrollable }; @@ -40,8 +40,6 @@ pub fn main() -> cosmic::iced::Result { #[derive(Default)] struct Window { - headerbar: HeaderBar, - expander: Expander, page: u8, debug: bool, theme: Theme, @@ -49,9 +47,30 @@ struct Window { checkbox_value: bool, toggler_value: bool, pick_list_selected: Option<&'static str>, + sidebar_toggled: bool, + show_minimize: bool, + show_maximize: bool, exit: bool, } +impl Window { + pub fn sidebar_toggled(mut self, toggled: bool) -> Self { + self.sidebar_toggled = toggled; + self + } + + pub fn show_maximize(mut self, show: bool) -> Self { + self.show_maximize = show; + self + } + + pub fn show_minimize(mut self, show: bool) -> Self { + self.show_minimize = show; + self + } +} + +#[allow(dead_code)] #[derive(Clone, Copy, Debug)] enum Message { Page(u8), @@ -62,20 +81,11 @@ enum Message { CheckboxToggled(bool), TogglerToggled(bool), PickListSelected(&'static str), - Window(WindowMsg), - Expander(ExpanderMsg) -} - -impl From for Message { - fn from(message: WindowMsg) -> Self { - Self::Window(message) - } -} - -impl From for Message { - fn from(message: ExpanderMsg) -> Self { - Self::Expander(message) - } + Close, + ToggleSidebar(bool), + Drag, + Minimize, + Maximize, } impl Application for Window { @@ -85,19 +95,17 @@ impl Application for Window { type Theme = Theme; fn new(_flags: ()) -> (Self, Command) { - let mut window = Window::default(); - window.headerbar.title = String::from("COSMIC Design System - Iced"); - window.headerbar.nav_title = String::from("WiFi Settings"); - window.headerbar.sidebar_active = true; - window.headerbar.show_minimize = true; - window.headerbar.show_maximize = true; + let mut window = Window::default() + .sidebar_toggled(true) + .show_maximize(true) + .show_minimize(true); window.slider_value = 50.0; window.pick_list_selected = Some("Option 1"); (window, Command::none()) } fn title(&self) -> String { - self.headerbar.title.clone() + String::from("COSMIC Design System - Iced") } fn update(&mut self, message: Message) -> iced::Command { @@ -110,24 +118,27 @@ impl Application for Window { Message::CheckboxToggled(value) => self.checkbox_value = value, Message::TogglerToggled(value) => self.toggler_value = value, Message::PickListSelected(value) => self.pick_list_selected = Some(value), - Message::Window(msg) => match msg { - WindowMsg::Close => self.exit = true, - WindowMsg::ToggleSidebar => self.headerbar.sidebar_active = !self.headerbar.sidebar_active, - WindowMsg::Drag => return drag(), - WindowMsg::Minimize => {} - WindowMsg::Maximize => {} - } - Message::Expander(msg) => match msg { - ExpanderMsg::Expand => self.expander.expanded = !self.expander.expanded, - } + Message::Close => self.exit = true, + Message::ToggleSidebar(toggled) => self.sidebar_toggled = toggled, + Message::Drag => return drag(), + Message::Minimize => {}, + Message::Maximize => {}, } iced::Command::none() } fn view(&self) -> Element { - let mut header = self.headerbar.render(); - // let expander = self.expander.render(); + let mut header: Element = header_bar( + self.title().as_str(), + self.sidebar_toggled, + self.show_minimize, + self.show_maximize, + |toggled| Message::ToggleSidebar(toggled), + || Message::Close, + || Message::Drag + ).into(); + if self.debug { header = header.explain(Color::WHITE); } @@ -150,15 +161,8 @@ impl Application for Window { 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 }), - self.expander.render( - vec![ - text("Content").into(), - text("Content 2").into(), - text("Content 3").into() - ] - ), ] - .active(self.headerbar.sidebar_active) + .active(self.sidebar_toggled) .condensed(condensed) .style(theme::Container::Custom(nav_bar_style)) .into(); diff --git a/examples/settings/Cargo.toml b/examples/settings/Cargo.toml new file mode 100644 index 0000000..7e6fe77 --- /dev/null +++ b/examples/settings/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "settings" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libcosmic = { path = "../..", features = ["debug"] } \ No newline at end of file diff --git a/examples/settings/src/main.rs b/examples/settings/src/main.rs new file mode 100644 index 0000000..6543540 --- /dev/null +++ b/examples/settings/src/main.rs @@ -0,0 +1,10 @@ +use cosmic::{settings, iced::Application}; +mod window; +use window::*; + +fn main() -> cosmic::iced::Result { + let mut settings = settings(); + settings.window.min_size = Some((600, 300)); + + App::run(settings) +} diff --git a/examples/settings/src/window.rs b/examples/settings/src/window.rs new file mode 100644 index 0000000..7302b93 --- /dev/null +++ b/examples/settings/src/window.rs @@ -0,0 +1,150 @@ +use cosmic::{ + widget::{ + header_bar, + nav_bar_style + }, + iced, + iced::{ + Theme, + Application, + Element, + widget::{ + container, + column + }, + }, + iced_winit::{ + Command, + Length, + widget::row, + window::drag, + theme + }, + nav_button, + iced_lazy::responsive +}; + +#[derive(Default)] +pub struct App { + page: u8, + theme: Theme, + sidebar_toggled: bool, + show_minimize: bool, + show_maximize: bool, + exit: bool, +} + +impl App { + pub fn sidebar_toggled(mut self, toggled: bool) -> Self { + self.sidebar_toggled = toggled; + self + } + + pub fn show_maximize(mut self, show: bool) -> Self { + self.show_maximize = show; + self + } + + pub fn show_minimize(mut self, show: bool) -> Self { + self.show_minimize = show; + self + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum AppMsg { + Close, + ToggleSidebar(bool), + Drag, + Minimize, + Maximize, + Page(u8), +} + +impl Application for App { + type Executor = iced::executor::Default; + type Flags = (); + type Message = AppMsg; + type Theme = Theme; + + fn new(_flags: Self::Flags) -> (Self, Command) { + ( + App::default() + .sidebar_toggled(true) + .show_maximize(true) + .show_minimize(true), + Command::none() + ) + } + + fn title(&self) -> String { + String::from("COSMIC Settings") + } + + fn update(&mut self, message: Self::Message) -> Command { + match message { + AppMsg::Close => self.exit = true, + AppMsg::ToggleSidebar(toggled) => self.sidebar_toggled = toggled, + AppMsg::Drag => return drag(), + AppMsg::Minimize => {}, + AppMsg::Maximize => {}, + AppMsg::Page(page) => self.page = page, + } + Command::none() + } + + fn view(&self) -> iced::Element { + let header = header_bar( + self.title().as_str(), + self.sidebar_toggled, + self.show_minimize, + self.show_maximize, + |toggled| AppMsg::ToggleSidebar(toggled), + || AppMsg::Close, + || AppMsg::Drag + ).into(); + + let content = responsive(|size| { + let condensed = size.width < 900.0; + + let sidebar: Element<_> = cosmic::navbar![ + nav_button!("network-wireless", "Wi-Fi", condensed) + .on_press(AppMsg::Page(0)) + .style(if self.page == 0 { theme::Button::Primary } else { theme::Button::Text }) + , + nav_button!("preferences-desktop", "Desktop", condensed) + .on_press(AppMsg::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(AppMsg::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(); + + let mut widgets = Vec::with_capacity(2); + + widgets.push(sidebar); + + container(row(widgets)) + .padding([8, 8]) + .width(Length::Fill) + .height(Length::Fill) + .into() + }).into(); + + column(vec![header, content]).into() + } + + fn should_exit(&self) -> bool { + self.exit + } + + fn theme(&self) -> Theme { + self.theme + } +} \ No newline at end of file diff --git a/settings/Cargo.toml b/settings/Cargo.toml new file mode 100644 index 0000000..73889c2 --- /dev/null +++ b/settings/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "settings" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/settings/src/main.rs b/settings/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/settings/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/widget/header_bar.rs b/src/widget/header_bar.rs new file mode 100644 index 0000000..964891f --- /dev/null +++ b/src/widget/header_bar.rs @@ -0,0 +1,204 @@ +use apply::Apply; +use iced::{self, Element, Length, widget, alignment::Vertical, theme, Renderer}; +use iced_lazy::Component; + +pub struct HeaderBar +{ + title: String, + nav_title: String, + sidebar_active: bool, + show_minimize: bool, + show_maximize: bool, + on_sidebar_active: Box Message>, + on_close: Box Message>, + on_drag: Box Message>, +} + +pub fn header_bar( + title: &str, + toggled: bool, + show_minimize: bool, + show_maximize: bool, + on_sidebar_active: impl Fn(bool) -> Message + 'static, + on_close: impl Fn() -> Message + 'static, + on_drag: impl Fn() -> Message + 'static, +) -> HeaderBar { + HeaderBar::new( + title, + toggled, + show_minimize, + show_maximize, + on_sidebar_active, + on_close, + on_drag + ) +} + +#[derive(Debug, Clone)] +pub enum HeaderEvent { + Close, + ToggleSidebar, + Drag, + Minimize, + Maximize +} + +impl HeaderBar { + pub fn new( + title: &str, + toggled: bool, + show_minimize: bool, + show_maximize: bool, + on_sidebar_active: impl Fn(bool) -> Message + 'static, + on_close: impl Fn() -> Message + 'static, + on_drag: impl Fn() -> Message + 'static, + ) -> Self { + Self { + title: String::from(title), + nav_title: String::new(), + sidebar_active: toggled, + show_minimize, + show_maximize, + on_sidebar_active: Box::new(on_sidebar_active), + on_close: Box::new(on_close), + on_drag: Box::new(on_drag), + } + } + + pub fn title(mut self, title: &str) -> Self { + self.title = title.to_string(); + self + } + + pub fn nav_title(mut self, nav_title: &str) -> Self { + self.nav_title = nav_title.to_string(); + self + } + + pub fn sidebar_active(mut self, sidebar_active: bool) -> Self { + self.sidebar_active = sidebar_active; + self + } + + pub fn show_minimize(mut self, show_minimize: bool) -> Self { + self.show_minimize = show_minimize; + self + } + + pub fn show_maximize(mut self, show_maximize: bool) -> Self { + self.show_maximize = show_maximize; + self + } +} + +impl Component for HeaderBar +{ + type State = (); + + type Event = HeaderEvent; + + fn update( + &mut self, + _state: &mut Self::State, + event: Self::Event, + ) -> Option { + match event { + HeaderEvent::Close => Some((self.on_close)()), + HeaderEvent::ToggleSidebar => { + self.sidebar_active = !self.sidebar_active; + Some((self.on_sidebar_active)(self.sidebar_active)) + }, + HeaderEvent::Drag => Some((self.on_drag)()), + HeaderEvent::Minimize => None, + HeaderEvent::Maximize => None, + } + } + + fn view(&self, _state: &Self::State) -> Element + { + let nav_button = { + let text = widget::text(&self.nav_title) + .vertical_alignment(Vertical::Center) + .width(Length::Shrink) + .height(Length::Fill); + + let icon = super::icon( + if self.sidebar_active { + "go-previous-symbolic" + } else { + "go-next-symbolic" + }, + 24, + ) + .width(Length::Units(24)) + .height(Length::Fill); + + widget::row!(text, icon) + .padding(4) + .spacing(4) + .apply(widget::button) + .style(theme::Button::Primary) + .on_press(HeaderEvent::ToggleSidebar) + .apply(widget::container) + .center_y() + .height(Length::Fill) + .into() + }; + + let content = widget::container(widget::text(&self.title)) + .center_x() + .center_y() + .width(Length::Fill) + .height(Length::Fill) + .into(); + + let window_controls = { + let mut widgets: Vec> = Vec::with_capacity(3); + + let icon = |name, size, on_press| { + super::icon(name, size) + .apply(widget::button) + .style(theme::Button::Primary) + .on_press(on_press) + }; + + if self.show_minimize { + widgets.push( + icon("window-minimize-symbolic", 16, HeaderEvent::Minimize).into() + ); + } + + if self.show_maximize { + widgets.push( + icon("window-maximize-symbolic", 16, HeaderEvent::Maximize).into() + ); + } + + widgets.push( + icon("window-close-symbolic", 16, HeaderEvent::Close).into() + ); + + widget::row(widgets) + .spacing(8) + .apply(widget::container) + .height(Length::Fill) + .center_y() + .into() + }; + + widget::row(vec![nav_button, content, window_controls]) + .height(Length::Units(50)) + .padding(10) + .apply(widget::event_container) + .center_y() + .on_press(HeaderEvent::Drag) + .into() + } +} + +impl<'a, Message: Clone + 'a> From> for Element<'a, Message> +{ + fn from(header_bar: HeaderBar) -> Self { + iced_lazy::component(header_bar) + } +} \ No newline at end of file diff --git a/src/widget/headerbar.rs b/src/widget/headerbar.rs deleted file mode 100644 index aa572f3..0000000 --- a/src/widget/headerbar.rs +++ /dev/null @@ -1,100 +0,0 @@ -use apply::Apply; -use iced::{self, alignment::Vertical, Element, Length, theme, widget}; -use crate::WindowMsg; - -#[derive(Default)] -pub struct HeaderBar { - pub title: String, - pub nav_title: String, - pub sidebar_active: bool, - pub show_minimize: bool, - pub show_maximize: bool, -} - -impl HeaderBar { - pub fn render(&self) -> Element<'_, T> - where T: Clone + From + 'static - { - let navbutton = { - let text = widget::text(&self.nav_title) - .vertical_alignment(Vertical::Center) - .width(Length::Shrink) - .height(Length::Fill); - - let icon = super::icon( - if self.sidebar_active { - "go-previous-symbolic" - } else { - "go-next-symbolic" - }, - 24, - ) - .width(Length::Units(24)) - .height(Length::Fill); - - widget::row!(text, icon) - .padding(4) - .spacing(4) - .apply(widget::button) - .on_press(T::from(WindowMsg::ToggleSidebar)) - .apply(widget::container) - .center_y() - .height(Length::Fill) - .into() - }; - - let content = widget::container(widget::text(&self.title)) - .center_x() - .center_y() - .width(Length::Fill) - .height(Length::Fill) - .into(); - - let window_controls = { - let mut widgets: Vec> = Vec::with_capacity(3); - - if self.show_minimize { - widgets.push( - super::icon("window-minimize-symbolic", 16) - .apply(widget::button) - .on_press(T::from(WindowMsg::Minimize)) - .style(theme::Button::Primary) - .into(), - ); - } - - if self.show_maximize { - widgets.push( - super::icon("window-maximize-symbolic", 16) - .apply(widget::button) - .on_press(T::from(WindowMsg::Maximize)) - .style(crate::iced::theme::Button::Primary) - .into(), - ); - } - - widgets.push( - super::icon("window-close-symbolic", 16) - .apply(widget::button) - .on_press(T::from(WindowMsg::Close)) - .style(crate::iced::theme::Button::Primary) - .into(), - ); - - widget::row(widgets) - .spacing(8) - .apply(widget::container) - .height(Length::Fill) - .center_y() - .into() - }; - - widget::row(vec![navbutton, content, window_controls]) - .height(Length::Units(50)) - .padding(8) - .apply(widget::event_container) - .center_y() - .on_press(T::from(WindowMsg::Drag)) - .into() - } -} diff --git a/src/widget/mod.rs b/src/widget/mod.rs index ca6aca3..101ea8e 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -1,8 +1,8 @@ mod button; pub use button::*; -mod headerbar; -pub use headerbar::*; +mod header_bar; +pub use header_bar::*; mod icon; pub use self::icon::*;