diff --git a/examples/design/Cargo.toml b/examples/design/Cargo.toml new file mode 100644 index 00000000..eb89855d --- /dev/null +++ b/examples/design/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "design" +version = "0.1.0" +edition = "2021" + +[dependencies] +tracing = "0.1.37" +tracing-subscriber = "0.3.17" + +[dependencies.libcosmic] +path = "../../" +default-features = false +features = ["debug", "winit", "tokio"] diff --git a/examples/design/src/main.rs b/examples/design/src/main.rs new file mode 100644 index 00000000..81c36efd --- /dev/null +++ b/examples/design/src/main.rs @@ -0,0 +1,325 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: MPL-2.0 + +//! Controls: buttons, radio buttons, toggles, etc. + +use cosmic::app::{Command, Core, Settings}; +use cosmic::widget::{button, column, container, icon, nav_bar, row, scrollable, text}; +use cosmic::{executor, iced, ApplicationExt, Apply, Element}; + +#[derive(Clone, Copy)] +pub enum Page { + Buttons, +} + +impl Page { + const fn as_str(self) -> &'static str { + match self { + Page::Buttons => "Buttons", + } + } +} + +/// Runs application with these settings +#[rustfmt::skip] +fn main() -> Result<(), Box> { + let settings = Settings::default() + .antialiasing(true) + .client_decorations(true) + .debug(false) + .size((1024, 768)) + .theme(cosmic::Theme::dark()); + + cosmic::app::run::(settings, &[Page::Buttons])?; + + Ok(()) +} + +/// Messages that are used specifically by our [`App`]. +#[derive(Clone, Debug)] +pub enum Message { + Clicked, +} + +/// The [`App`] stores application-specific state. +pub struct App { + core: Core, + nav_model: nav_bar::Model, + app_icon: icon::Handle, + bt_icon: icon::Handle, + leading_icon: icon::Handle, + trailing_icon: icon::Handle, +} + +/// Implement [`cosmic::Application`] to integrate with COSMIC. +impl cosmic::Application for App { + /// Default async executor to use with the app. + type Executor = executor::Default; + + /// Argument received [`cosmic::Application::new`]. + type Flags = &'static [Page]; + + /// Message type specific to our [`App`]. + type Message = Message; + + const APP_ID: &'static str = "org.cosmic.DesignDemo"; + + fn core(&self) -> &Core { + &self.core + } + + fn core_mut(&mut self) -> &mut Core { + &mut self.core + } + + /// Creates the application, and optionally emits command on initialize. + fn init(core: Core, input: Self::Flags) -> (Self, Command) { + let mut nav_model = nav_bar::Model::default(); + + for &page in input { + nav_model.insert().text(page.as_str()).data(page); + } + + nav_model.activate_position(0); + + let mut app = App { + core, + app_icon: icon::handle::from_name("firefox").size(16).handle(), + bt_icon: icon::handle::from_name("bluetooth-active-symbolic") + .size(16) + .handle(), + leading_icon: icon::handle::from_name("document-save-symbolic") + .size(16) + .handle(), + trailing_icon: button::hyperlink::icon(), + nav_model, + }; + + let command = app.update_title(); + + (app, command) + } + + /// Allows COSMIC to integrate with your application's [`nav_bar::Model`]. + fn nav_model(&self) -> Option<&nav_bar::Model> { + Some(&self.nav_model) + } + + /// Called when a navigation item is selected. + fn on_nav_select(&mut self, id: nav_bar::Id) -> Command { + self.nav_model.activate(id); + self.update_title() + } + + /// Handle application events here. + fn update(&mut self, _message: Self::Message) -> Command { + Command::none() + } + + /// Creates a view after each update. + fn view(&self) -> Element { + let page_content = match self.nav_model.active_data::() { + Some(Page::Buttons) => self.view_buttons(), + None => cosmic::widget::text("Unknown page selected").into(), + }; + + container(page_content) + .width(iced::Length::Fill) + .align_x(iced::alignment::Horizontal::Center) + .apply(scrollable) + .into() + } +} + +impl App +where + Self: cosmic::Application, +{ + fn active_page_title(&mut self) -> &str { + self.nav_model + .text(self.nav_model.active()) + .unwrap_or("Unknown Page") + } + + fn update_title(&mut self) -> Command { + let title = self.active_page_title().to_owned(); + self.set_title(title) + } + + fn view_buttons(&self) -> Element { + column() + .max_width(800) + .spacing(24) + .push(text::title1("Label Buttons")) + // Suggested button header + .push( + column() + .spacing(8) + .push(text::title3("Suggested Button")) + .push(text("Highest level of attention, there should only be one primary button used on the page.").size(14.0)) + ) + // Suggested button demo + .push( + row() + .spacing(36) + .push(button::suggested("Label").on_press(Message::Clicked)) + .push(button::suggested("Label").on_press(Message::Clicked).leading_icon(self.leading_icon.clone())) + .push(button::suggested("Label").on_press(Message::Clicked).trailing_icon(self.trailing_icon.clone())) + .push(button::suggested("Label").on_press(Message::Clicked).leading_icon(self.app_icon.clone())) + .push( + button::suggested("Label") + .on_press(Message::Clicked) + .leading_icon(self.app_icon.clone()) + .trailing_icon(self.trailing_icon.clone()) + ) + ) + // Destructive button header + .push( + column() + .spacing(8) + .push(text::title3("Destructive Button")) + .push(text("Highest level of attention, there should only be one primary button used on the page.").size(14.0)) + ) + // Destructive button demo + .push( + row() + .spacing(36) + .push(button::destructive("Label").on_press(Message::Clicked)) + .push(button::destructive("Label").on_press(Message::Clicked).leading_icon(self.leading_icon.clone())) + .push(button::destructive("Label").on_press(Message::Clicked).trailing_icon(self.trailing_icon.clone())) + .push(button::destructive("Label").on_press(Message::Clicked).leading_icon(self.app_icon.clone())) + .push( + button::destructive("Label") + .on_press(Message::Clicked) + .leading_icon(self.app_icon.clone()) + .trailing_icon(self.trailing_icon.clone()) + ) + ) + // Standard button header + .push( + column() + .spacing(8) + .push(text::title3("Standard Button")) + .push( + text( + "Requires less attention from the user. Could be more \ + than one button on the page, if necessary." + ) + .size(14.0) + ) + ) + // Standard button demo + .push( + row() + .spacing(36) + .push(button::standard("Label").on_press(Message::Clicked)) + .push(button::standard("Label").on_press(Message::Clicked).leading_icon(self.leading_icon.clone())) + .push(button::standard("Label").on_press(Message::Clicked).trailing_icon(self.trailing_icon.clone())) + .push(button::standard("Label").on_press(Message::Clicked).leading_icon(self.app_icon.clone())) + .push( + button::standard("Label") + .on_press(Message::Clicked) + .leading_icon(self.app_icon.clone()) + .trailing_icon(self.trailing_icon.clone()) + ) + ) + // Text button header + .push( + column() + .spacing(8) + .push(text::title3("Text Button")) + .push(text( + "Lowest priority actions, especially when presenting multiple options. Because text buttons \ + don’t have a visible container in their default state, they don’t distract from nearby \ + content. But they are also more difficult to recognize because of that." + ).size(14.0)) + ) + // Text button demo + .push( + row() + .spacing(36) + .push(button::text("Label").on_press(Message::Clicked)) + .push(button::text("Label").on_press(Message::Clicked).leading_icon(self.leading_icon.clone())) + .push(button::text("Label").on_press(Message::Clicked).trailing_icon(self.trailing_icon.clone())) + .push(button::text("Label").on_press(Message::Clicked).leading_icon(self.app_icon.clone())) + .push( + button::text("Label") + .on_press(Message::Clicked) + .leading_icon(self.app_icon.clone()) + .trailing_icon(self.trailing_icon.clone()) + ) + ) + // Icon buttons + .push(text::title1("Icon Buttons")) + // Extra small icon buttons + .push( + row() + .spacing(36) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).extra_small()) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).extra_small().selected(true)) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).extra_small().label("Label")) + .push( + button::icon(self.bt_icon.clone()) + .on_press(Message::Clicked) + .extra_small() + .label("Label") + .selected(true) + ) + ) + // Small (default) icon buttons + .push( + row() + .spacing(36) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked)) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).selected(true)) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).label("Label")) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).label("Label").selected(true)) + ) + // Medium icon buttons + .push( + row() + .spacing(36) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).medium()) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).medium().selected(true)) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).medium().label("Label")) + .push( + button::icon(self.bt_icon.clone()) + .on_press(Message::Clicked) + .medium() + .label("Label") + .selected(true) + ) + ) + // Large icon buttons + .push( + row() + .spacing(36) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).large()) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).large().selected(true)) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).large().label("Label")) + .push( + button::icon(self.bt_icon.clone()) + .on_press(Message::Clicked) + .large() + .label("Label") + .selected(true) + ) + ) + // Extra large icon buttons + .push( + row() + .spacing(36) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).extra_large()) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).extra_large().selected(true)) + .push(button::icon(self.bt_icon.clone()).on_press(Message::Clicked).extra_large().label("Label")) + .push( + button::icon(self.bt_icon.clone()) + .on_press(Message::Clicked) + .extra_large() + .label("Label") + .selected(true) + ) + ) + .into() + } +} diff --git a/justfile b/justfile index 163ebceb..b83e0cbc 100644 --- a/justfile +++ b/justfile @@ -1,4 +1,4 @@ -projects := 'application cosmic cosmic_sctk open_dialog' +projects := 'application cosmic cosmic_sctk design open_dialog' # Check for errors and linter warnings check *args: