test cosmic applet

eyes are a go

nandi 5a8264dc 589c0a78

+1 -1
Cargo.toml
··· 1 1 [package] 2 - name = "cosmic-applet-template" 2 + name = "eyez" 3 3 version = "0.1.0" 4 4 edition = "2021" 5 5 license = "GPL-3.0"
+1 -1
i18n/en/cosmic_applet_template.ftl
··· 1 - example-row = Example row 1 + example-row = Examplez row
+2 -2
justfile
··· 1 - name := 'cosmic-applet-template' 2 - export APPID := 'com.example.CosmicAppletTemplate' 1 + name := 'eyez' 2 + export APPID := 'com.example.eyez' 3 3 4 4 rootdir := '' 5 5 prefix := '/usr'
+3
mise.toml
··· 1 + [tools] 2 + node = "latest" 3 + rust = "latest"
+3 -3
res/com.example.CosmicAppletTemplate.desktop res/com.example.eyez.desktop
··· 1 1 [Desktop Entry] 2 - Name=COSMIC Applet Template 3 - Exec=cosmic-applet-template %F 2 + Name=eyez applet 3 + Exec=eyez %F 4 4 Terminal=false 5 5 Type=Application 6 6 StartupNotify=true 7 - Icon=com.example.CosmicAppletTemplate 7 + Icon=com.example.eyez 8 8 Categories=COSMIC;Utility; 9 9 Keywords=Folder;Manager; 10 10 MimeType=inode/directory;
+3 -3
res/com.example.CosmicAppletTemplate.metainfo.xml res/com.example.eyez.metainfo.xml
··· 1 1 <?xml version="1.0" encoding="UTF-8"?> 2 2 <component type="desktop-application"> 3 - <id>com.example.CosmicAppletTemplate</id> 3 + <id>com.example.eyez</id> 4 4 <metadata_license>CC0-1.0</metadata_license> 5 5 <project_license>GPL-3.0-only</project_license> 6 6 <project_group>COSMIC</project_group> ··· 13 13 <description> 14 14 <p>A template for COSMIC applications</p> 15 15 </description> 16 - <launchable type="desktop-id">com.example.CosmicAppletTemplate.desktop</launchable> 17 - <icon type="remote" height="256" width="256">https://raw.githubusercontent.com/edfloreshz/cosmic-applet-template/master/res/icons/hicolor/256x256/apps/com.example.CosmicAppletTemplate.svg</icon> 16 + <launchable type="desktop-id">com.example.eyez.desktop</launchable> 17 + <icon type="remote" height="256" width="256">https://raw.githubusercontent.com/edfloreshz/cosmic-applet-template/master/res/icons/hicolor/256x256/apps/com.example.eyez.svg</icon> 18 18 <screenshots> 19 19 </screenshots> 20 20 <provides>
res/icons/hicolor/128x128/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/128x128/apps/com.example.eyez.svg
res/icons/hicolor/16x16/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/16x16/apps/com.example.eyez.svg
res/icons/hicolor/24x24/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/24x24/apps/com.example.eyez.svg
res/icons/hicolor/256x256/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/256x256/apps/com.example.eyez.svg
res/icons/hicolor/32x32/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/32x32/apps/com.example.eyez.svg
res/icons/hicolor/48x48/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/48x48/apps/com.example.eyez.svg
res/icons/hicolor/64x64/apps/com.example.CosmicAppletTemplate.svg res/icons/hicolor/64x64/apps/com.example.eyez.svg
+92 -73
src/app.rs
··· 1 1 // SPDX-License-Identifier: GPL-3.0-only 2 2 3 - use cosmic::app::{Core, Task}; 4 - use cosmic::iced::window::Id; 5 - use cosmic::iced::Limits; 6 - use cosmic::iced_winit::commands::popup::{destroy_popup, get_popup}; 7 - use cosmic::widget::{self, settings}; 8 - use cosmic::{Application, Element}; 3 + use cosmic::{ 4 + app::{Core, Task}, 5 + iced::{ 6 + self, 7 + widget::row, 8 + Length, Color, Point, Subscription, 9 + mouse, Border, 10 + }, 11 + Element, 12 + }; 9 13 10 - use crate::fl; 14 + use cosmic::Application; 11 15 12 16 /// This is the struct that represents your application. 13 17 /// It is used to define the data that will be used by your application. 14 - #[derive(Default)] 15 - pub struct YourApp { 18 + pub struct Eyez { 16 19 /// Application state which is managed by the COSMIC runtime. 17 20 core: Core, 18 - /// The popup id. 19 - popup: Option<Id>, 20 - /// Example row toggler. 21 - example_row: bool, 21 + /// The cursor position. 22 + cursor: Point, 22 23 } 23 24 24 25 /// This is the enum that contains all the possible variants that your application will need to transmit messages. ··· 26 27 /// If your application does not need to send messages, you can use an empty enum or `()`. 27 28 #[derive(Debug, Clone)] 28 29 pub enum Message { 29 - TogglePopup, 30 - PopupClosed(Id), 31 - ToggleExampleRow(bool), 30 + Event(iced::Event), 32 31 } 32 + 33 33 34 34 /// Implement the `Application` trait for your application. 35 35 /// This is where you define the behavior of your application. ··· 39 39 /// - `Flags` is the data that your application needs to use before it starts. 40 40 /// - `Message` is the enum that contains all the possible variants that your application will need to transmit messages. 41 41 /// - `APP_ID` is the unique identifier of your application. 42 - impl Application for YourApp { 42 + impl Application for Eyez { 43 43 type Executor = cosmic::executor::Default; 44 44 45 45 type Flags = (); 46 46 47 47 type Message = Message; 48 48 49 - const APP_ID: &'static str = "com.example.CosmicAppletTemplate"; 49 + const APP_ID: &'static str = "com.example.CosmicEyez"; 50 50 51 51 fn core(&self) -> &Core { 52 52 &self.core ··· 64 64 /// - `flags` is used to pass in any data that your application needs to use before it starts. 65 65 /// - `Command` type is used to send messages to your application. `Command::none()` can be used to send no messages to your application. 66 66 fn init(core: Core, _flags: Self::Flags) -> (Self, Task<Self::Message>) { 67 - let app = YourApp { 67 + let app = Eyez { 68 68 core, 69 - ..Default::default() 69 + cursor: Point::default(), 70 70 }; 71 71 72 72 (app, Task::none()) 73 73 } 74 74 75 - fn on_close_requested(&self, id: Id) -> Option<Message> { 76 - Some(Message::PopupClosed(id)) 77 - } 78 - 79 - /// This is the main view of your application, it is the root of your widget tree. 80 - /// 81 - /// The `Element` type is used to represent the visual elements of your application, 82 - /// it has a `Message` associated with it, which dictates what type of message it can send. 83 - /// 84 - /// To get a better sense of which widgets are available, check out the `widget` module. 85 75 fn view(&self) -> Element<Self::Message> { 86 - self.core 87 - .applet 88 - .icon_button("display-symbolic") 89 - .on_press(Message::TogglePopup) 90 - .into() 91 - } 92 - 93 - fn view_window(&self, _id: Id) -> Element<Self::Message> { 94 - let content_list = widget::list_column() 95 - .padding(5) 96 - .spacing(0) 97 - .add(settings::item( 98 - fl!("example-row"), 99 - widget::toggler(self.example_row).on_toggle(Message::ToggleExampleRow), 100 - )); 101 - 102 - self.core.applet.popup_container(content_list).into() 76 + // Auto-size based on panel - scale down for panel display 77 + let panel_scale = 0.6; // Reduce size by 40% for better panel fit 78 + let eye_size = 50.0 * panel_scale; 79 + let pupil_size = 20.0 * panel_scale; 80 + 81 + let draw_eye = move |eye_center: Point| { 82 + // Calculate pupil position based on cursor 83 + let dx = self.cursor.x - eye_center.x; 84 + let dy = self.cursor.y - eye_center.y; 85 + let distance = (dx * dx + dy * dy).sqrt(); 86 + let max_distance = (eye_size - pupil_size) / 2.0; 87 + 88 + let (offset_x, offset_y) = if distance <= max_distance { 89 + (dx, dy) 90 + } else { 91 + let scale = max_distance / distance; 92 + (dx * scale, dy * scale) 93 + }; 94 + 95 + // Create pupil with offset based on cursor position 96 + let pupil = cosmic::widget::container(cosmic::widget::text("")) 97 + .width(Length::Fixed(pupil_size)) 98 + .height(Length::Fixed(pupil_size)) 99 + .style(move |_theme: &cosmic::Theme| { 100 + cosmic::widget::container::Style { 101 + background: Some(Color::BLACK.into()), 102 + border: Border { 103 + radius: (pupil_size / 2.0).into(), 104 + ..Default::default() 105 + }, 106 + ..Default::default() 107 + } 108 + }); 109 + 110 + // Eye container with white background and black border 111 + cosmic::widget::container(pupil) 112 + .width(Length::Fixed(eye_size)) 113 + .height(Length::Fixed(eye_size)) 114 + .padding([ 115 + (eye_size - pupil_size) / 2.0 + offset_y, 116 + (eye_size - pupil_size) / 2.0 - offset_x, 117 + (eye_size - pupil_size) / 2.0 - offset_y, 118 + (eye_size - pupil_size) / 2.0 + offset_x, 119 + ]) 120 + .style(move |_theme: &cosmic::Theme| { 121 + cosmic::widget::container::Style { 122 + background: Some(Color::WHITE.into()), 123 + border: Border { 124 + radius: (eye_size / 2.0).into(), 125 + width: 2.0, 126 + color: Color::BLACK, 127 + }, 128 + ..Default::default() 129 + } 130 + }) 131 + }; 132 + 133 + let eyes = row![ 134 + draw_eye(Point::new(eye_size / 2.0, eye_size / 2.0)), 135 + draw_eye(Point::new(eye_size * 1.5 + 5.0, eye_size / 2.0)), 136 + ] 137 + .spacing((5.0 * panel_scale) as u16); 138 + 139 + self.core.applet.popup_container(eyes).into() 103 140 } 104 141 105 142 /// Application messages are handled here. The application state can be modified based on ··· 107 144 /// background thread managed by the application's executor. 108 145 fn update(&mut self, message: Self::Message) -> Task<Self::Message> { 109 146 match message { 110 - Message::TogglePopup => { 111 - return if let Some(p) = self.popup.take() { 112 - destroy_popup(p) 113 - } else { 114 - let new_id = Id::unique(); 115 - self.popup.replace(new_id); 116 - let mut popup_settings = self.core.applet.get_popup_settings( 117 - self.core.main_window_id().unwrap(), 118 - new_id, 119 - None, 120 - None, 121 - None, 122 - ); 123 - popup_settings.positioner.size_limits = Limits::NONE 124 - .max_width(372.0) 125 - .min_width(300.0) 126 - .min_height(200.0) 127 - .max_height(1080.0); 128 - get_popup(popup_settings) 147 + Message::Event(event) => { 148 + if let iced::Event::Mouse(mouse::Event::CursorMoved { position }) = event { 149 + self.cursor = position; 129 150 } 130 151 } 131 - Message::PopupClosed(id) => { 132 - if self.popup.as_ref() == Some(&id) { 133 - self.popup = None; 134 - } 135 - } 136 - Message::ToggleExampleRow(toggled) => self.example_row = toggled, 137 152 } 138 153 Task::none() 139 154 } 140 155 156 + fn subscription(&self) -> Subscription<Self::Message> { 157 + iced::event::listen().map(Message::Event) 158 + } 159 + 141 160 fn style(&self) -> Option<cosmic::iced_runtime::Appearance> { 142 161 Some(cosmic::applet::style()) 143 162 } 144 - } 163 + }
+2 -2
src/main.rs
··· 1 1 // SPDX-License-Identifier: GPL-3.0-only 2 2 3 - use app::YourApp; 3 + use app::Eyez; 4 4 /// The `app` module is used by convention to indicate the main component of our application. 5 5 mod app; 6 6 mod core; ··· 11 11 /// - `()` is the flags that your app needs to use before it starts. 12 12 /// If your app does not need any flags, you can pass in `()`. 13 13 fn main() -> cosmic::iced::Result { 14 - cosmic::applet::run::<YourApp>(()) 14 + cosmic::applet::run::<Eyez>(()) 15 15 }