diff --git a/src/ui/Cargo.toml b/src/ui/Cargo.toml index 30198e8..bcb4c55 100644 --- a/src/ui/Cargo.toml +++ b/src/ui/Cargo.toml @@ -15,6 +15,7 @@ i18n-embed-fl = "0.8" once_cell = "1.19.0" open = "5.1.3" rust-embed = "8.3.0" +slotmap = "1.0.7" tokio = { version = "1.37.0", features = ["full"] } [dependencies.libcosmic] @@ -24,4 +25,4 @@ features = ["dbus-config", "tokio", "winit", "wgpu"] [dependencies.i18n-embed] version = "0.14" -features = ["fluent-system", "desktop-requester"] \ No newline at end of file +features = ["fluent-system", "desktop-requester"] diff --git a/src/ui/justfile b/src/ui/justfile index 42b218e..cca1ecf 100644 --- a/src/ui/justfile +++ b/src/ui/justfile @@ -1,5 +1,5 @@ name := 'cosmic-app-template' -export APPID := 'com.example.CosmicAppTemplate' +export APPID := 'dev.edfloreshz.Devmode' rootdir := '' prefix := '/usr' diff --git a/src/ui/res/com.example.CosmicAppTemplate.desktop b/src/ui/res/dev.edfloreshz.Devmode.desktop similarity index 84% rename from src/ui/res/com.example.CosmicAppTemplate.desktop rename to src/ui/res/dev.edfloreshz.Devmode.desktop index 8967fd2..4391c6d 100644 --- a/src/ui/res/com.example.CosmicAppTemplate.desktop +++ b/src/ui/res/dev.edfloreshz.Devmode.desktop @@ -4,7 +4,7 @@ Exec=cosmic-app-template %F Terminal=false Type=Application StartupNotify=true -Icon=com.example.CosmicAppTemplate +Icon=dev.edfloreshz.Devmode Categories=COSMIC;Utility; Keywords=Folder;Manager; MimeType=inode/directory; diff --git a/src/ui/res/com.example.CosmicAppTemplate.metainfo.xml b/src/ui/res/dev.edfloreshz.Devmode.metainfo.xml similarity index 82% rename from src/ui/res/com.example.CosmicAppTemplate.metainfo.xml rename to src/ui/res/dev.edfloreshz.Devmode.metainfo.xml index 960ddde..8fc5352 100644 --- a/src/ui/res/com.example.CosmicAppTemplate.metainfo.xml +++ b/src/ui/res/dev.edfloreshz.Devmode.metainfo.xml @@ -1,6 +1,6 @@ - com.example.CosmicAppTemplate + dev.edfloreshz.Devmode CC0-1.0 GPL-3.0-only COSMIC @@ -13,8 +13,8 @@

A template for COSMIC applications

- com.example.CosmicAppTemplate.desktop - https://raw.githubusercontent.com/edfloreshz/cosmic-app-template/master/res/icons/hicolor/256x256/apps/com.example.CosmicAppTemplate.svg + dev.edfloreshz.Devmode.desktop + https://raw.githubusercontent.com/edfloreshz/cosmic-app-template/master/res/icons/hicolor/256x256/apps/dev.edfloreshz.Devmode.svg diff --git a/src/ui/res/icons/hicolor/16x16/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/16x16/apps/com.example.CosmicAppTemplate.svg deleted file mode 100644 index 056d46d..0000000 --- a/src/ui/res/icons/hicolor/16x16/apps/com.example.CosmicAppTemplate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ui/res/icons/hicolor/24x24/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/24x24/apps/com.example.CosmicAppTemplate.svg deleted file mode 100644 index 056d46d..0000000 --- a/src/ui/res/icons/hicolor/24x24/apps/com.example.CosmicAppTemplate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ui/res/icons/hicolor/256x256/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/256x256/apps/com.example.CosmicAppTemplate.svg deleted file mode 100644 index 056d46d..0000000 --- a/src/ui/res/icons/hicolor/256x256/apps/com.example.CosmicAppTemplate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ui/res/icons/hicolor/32x32/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/32x32/apps/com.example.CosmicAppTemplate.svg deleted file mode 100644 index 056d46d..0000000 --- a/src/ui/res/icons/hicolor/32x32/apps/com.example.CosmicAppTemplate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ui/res/icons/hicolor/48x48/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/48x48/apps/com.example.CosmicAppTemplate.svg deleted file mode 100644 index 056d46d..0000000 --- a/src/ui/res/icons/hicolor/48x48/apps/com.example.CosmicAppTemplate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ui/res/icons/hicolor/64x64/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/64x64/apps/com.example.CosmicAppTemplate.svg deleted file mode 100644 index 056d46d..0000000 --- a/src/ui/res/icons/hicolor/64x64/apps/com.example.CosmicAppTemplate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ui/res/icons/hicolor/128x128/apps/com.example.CosmicAppTemplate.svg b/src/ui/res/icons/hicolor/scalable/apps/dev.edfloreshz.Devmode.svg similarity index 100% rename from src/ui/res/icons/hicolor/128x128/apps/com.example.CosmicAppTemplate.svg rename to src/ui/res/icons/hicolor/scalable/apps/dev.edfloreshz.Devmode.svg diff --git a/src/ui/src/app.rs b/src/ui/src/app.rs index 0f2bb01..14c17bb 100644 --- a/src/ui/src/app.rs +++ b/src/ui/src/app.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; -use crate::fl; +use crate::pages::clone; +use crate::{fl, pages}; use cosmic::app::{Command, Core}; use cosmic::iced::alignment::{Horizontal, Vertical}; use cosmic::iced::{Alignment, Length}; @@ -13,14 +14,12 @@ const REPOSITORY: &str = "https://github.com/edfloreshz/cosmic-app-template"; /// This is the struct that represents your application. /// It is used to define the data that will be used by your application. -pub struct YourApp { - /// Application state which is managed by the COSMIC runtime. +pub struct Devmode { core: Core, - /// Display a context drawer with the designated page if defined. context_page: ContextPage, - /// Key bindings for the application's menu bar. + page: Page, + clone: pages::clone::ClonePage, key_binds: HashMap, - /// A model that contains all of the pages assigned to the nav bar panel. nav: nav_bar::Model, } @@ -31,13 +30,15 @@ pub struct YourApp { pub enum Message { LaunchUrl(String), ToggleContextPage(ContextPage), + Clone(clone::Message), } /// Identifies a page in the application. pub enum Page { - Page1, - Page2, - Page3, + Clone, + Workspaces, + Open, + Config, } /// Identifies a context page to display in the context drawer. @@ -78,14 +79,14 @@ impl menu::action::MenuAction for MenuAction { /// - `Flags` is the data that your application needs to use before it starts. /// - `Message` is the enum that contains all the possible variants that your application will need to transmit messages. /// - `APP_ID` is the unique identifier of your application. -impl Application for YourApp { +impl Application for Devmode { type Executor = cosmic::executor::Default; type Flags = (); type Message = Message; - const APP_ID: &'static str = "com.example.CosmicAppTemplate"; + const APP_ID: &'static str = "dev.edfloreshz.Devmode"; fn core(&self) -> &Core { &self.core @@ -111,31 +112,36 @@ impl Application for YourApp { let mut nav = nav_bar::Model::default(); nav.insert() - .text("Page 1") - .data::(Page::Page1) - .icon(icon::from_name("applications-science-symbolic")) + .text("Clone") + .data::(Page::Clone) + .icon(icon::from_name("browser-download-symbolic")) .activate(); nav.insert() - .text("Page 2") - .data::(Page::Page2) - .icon(icon::from_name("applications-system-symbolic")); + .text("Workspaces") + .data::(Page::Workspaces) + .icon(icon::from_name("multitasking-symbolic")); nav.insert() - .text("Page 3") - .data::(Page::Page3) - .icon(icon::from_name("applications-games-symbolic")); + .text("Open") + .data::(Page::Open) + .icon(icon::from_name("folder-open-symbolic")); - let mut app = YourApp { + nav.insert() + .text("Config") + .data::(Page::Config) + .icon(icon::from_name("settings-symbolic")); + + let app = Devmode { core, context_page: ContextPage::default(), + page: Page::Clone, + clone: pages::clone::ClonePage::new(), key_binds: HashMap::new(), nav, }; - let command = app.update_titles(); - - (app, command) + (app, Command::none()) } /// Elements to pack at the start of the header bar. @@ -151,15 +157,19 @@ impl Application for YourApp { vec![menu_bar.into()] } - /// This is the main view of your application, it is the root of your widget tree. - /// - /// The `Element` type is used to represent the visual elements of your application, - /// it has a `Message` associated with it, which dictates what type of message it can send. - /// - /// To get a better sense of which widgets are available, check out the `widget` module. fn view(&self) -> Element { - widget::text::title1(fl!("welcome")) + let spacing = theme::active().cosmic().spacing; + + let page: Element = match self.page { + Page::Clone => self.clone.view().map(Message::Clone), + Page::Workspaces => todo!(), + Page::Open => todo!(), + Page::Config => todo!(), + }; + + widget::container(page) .apply(widget::container) + .padding(spacing.space_xxs) .width(Length::Fill) .height(Length::Fill) .align_x(Horizontal::Center) @@ -172,6 +182,15 @@ impl Application for YourApp { /// background thread managed by the application's executor. fn update(&mut self, message: Self::Message) -> Command { match message { + Message::Clone(message) => { + for command in self.clone.update(message) { + match command { + clone::Command::Clone(_repository, _workspace) => { + todo!("Implement cloning mechanism.") + } + } + } + } Message::LaunchUrl(url) => { let _result = open::that_detached(url); } @@ -209,18 +228,17 @@ impl Application for YourApp { // Activate the page in the model. self.nav.activate(id); - self.update_titles() + Command::none() } } -impl YourApp { +impl Devmode { /// The about page for this app. pub fn about(&self) -> Element { let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; let icon = widget::svg(widget::svg::Handle::from_memory( - &include_bytes!("../res/icons/hicolor/128x128/apps/com.example.CosmicAppTemplate.svg") - [..], + &include_bytes!("../res/icons/hicolor/scalable/apps/dev.edfloreshz.Devmode.svg")[..], )); let title = widget::text::title3(fl!("app-title")); @@ -237,19 +255,4 @@ impl YourApp { .spacing(space_xxs) .into() } - - /// Updates the header and window titles. - pub fn update_titles(&mut self) -> Command { - let mut window_title = fl!("app-title"); - let mut header_title = String::new(); - - if let Some(page) = self.nav.text(self.nav.active()) { - window_title.push_str(" — "); - window_title.push_str(page); - header_title.push_str(page); - } - - self.set_header_title(header_title); - self.set_window_title(window_title) - } } diff --git a/src/ui/src/main.rs b/src/ui/src/main.rs index 5704fcb..c0bce08 100644 --- a/src/ui/src/main.rs +++ b/src/ui/src/main.rs @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only -use app::YourApp; +use app::Devmode; /// The `app` module is used by convention to indicate the main component of our application. mod app; mod core; +mod pages; /// The `cosmic::app::run()` function is the starting point of your application. /// It takes two arguments: @@ -12,5 +13,5 @@ mod core; /// If your app does not need any flags, you can pass in `()`. fn main() -> cosmic::iced::Result { let settings = cosmic::app::Settings::default(); - cosmic::app::run::(settings, ()) + cosmic::app::run::(settings, ()) } diff --git a/src/ui/src/pages.rs b/src/ui/src/pages.rs new file mode 100644 index 0000000..95c370b --- /dev/null +++ b/src/ui/src/pages.rs @@ -0,0 +1 @@ +pub mod clone; diff --git a/src/ui/src/pages/clone.rs b/src/ui/src/pages/clone.rs new file mode 100644 index 0000000..c8353c0 --- /dev/null +++ b/src/ui/src/pages/clone.rs @@ -0,0 +1,115 @@ +use cosmic::{ + iced::{Alignment, Length}, + theme, widget, Apply, Element, +}; +use slotmap::{DefaultKey, SecondaryMap, SlotMap}; + +#[derive(Debug, Default)] +pub struct Repository { + url: String, + selected: bool, +} + +#[derive(Debug, Default)] +pub struct ClonePage { + workspace: String, + repository: Repository, + repositories: SlotMap, + editing: SecondaryMap, + repository_input_ids: SecondaryMap, +} + +#[derive(Debug, Clone)] +pub enum Message { + Clone, + Select(DefaultKey, bool), + TitleSubmit(DefaultKey), + TitleUpdate(DefaultKey, String), + EditMode(DefaultKey, bool), +} + +pub enum Command { + Clone(String, String), +} + +impl ClonePage { + pub fn new() -> Self { + let mut repositories = SlotMap::new(); + let mut repository_input_ids = SecondaryMap::new(); + let id = repositories.insert(Repository { + url: "https://github.com/edfloreshz/tasks".into(), + selected: false, + }); + repository_input_ids.insert(id, widget::Id::unique()); + Self { + repositories, + repository_input_ids, + ..Default::default() + } + } + + fn header(&self) -> Element { + widget::row::with_capacity(2) + .push(widget::text::title2("Clone")) + .into() + } + + pub fn view(&self) -> Element { + let spacing = theme::active().cosmic().spacing; + + let mut items = widget::list::list_column() + .style(theme::Container::ContextDrawer) + .spacing(spacing.space_xxxs) + .padding([spacing.space_none, spacing.space_xxs]); + + for (id, item) in &self.repositories { + let item_checkbox = + widget::checkbox("", item.selected, move |value| Message::Select(id, value)); + + let item_text = widget::editable_input( + "", + &item.url, + *self.editing.get(id).unwrap_or(&false), + move |editing| Message::EditMode(id, editing), + ) + .id(self.repository_input_ids[id].clone()) + .on_submit(Message::TitleSubmit(id)) + .on_input(move |text| Message::TitleUpdate(id, text)) + .width(Length::Fill); + + let row = widget::row::with_capacity(4) + .align_items(Alignment::Center) + .spacing(spacing.space_xxs) + .padding([spacing.space_xxxs, spacing.space_xxs]) + .push(item_checkbox) + .push(item_text); + + items = items.add(row); + } + + widget::column::with_capacity(2) + .push(self.header()) + .push(items) + .spacing(spacing.space_xxs) + .apply(widget::container) + .height(Length::Shrink) + .apply(widget::scrollable) + .height(Length::Fill) + .into() + } + + pub fn update(&self, message: Message) -> Vec { + let mut commands = vec![]; + match message { + Message::Clone => commands.push(Command::Clone( + self.repository.url.clone(), + self.workspace.clone(), + )), + Message::Select(_, _) => todo!(), + Message::TitleSubmit(_) => todo!(), + Message::TitleUpdate(_, _) => todo!(), + Message::EditMode(_, _) => todo!(), + } + commands + } +}