+1
-1
Cargo.toml
+1
-1
Cargo.toml
+1
-1
i18n/en/cosmic_applet_template.ftl
+1
-1
i18n/en/cosmic_applet_template.ftl
+2
-2
justfile
+2
-2
justfile
+3
-3
res/com.example.CosmicAppletTemplate.desktop
res/com.example.eyez.desktop
+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
+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/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/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/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/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/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/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
res/icons/hicolor/64x64/apps/com.example.CosmicAppletTemplate.svg
res/icons/hicolor/64x64/apps/com.example.eyez.svg
+92
-73
src/app.rs
+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
+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
}