tangled
alpha
login
or
join now
lesbian.skin
/
nanel
[MIRROR] https://codeberg.org/naomi/nanel
1
fork
atom
overview
issues
pulls
pipelines
Compare changes
Choose any two refs to compare.
base:
refactor
main
no tags found
compare:
refactor
main
no tags found
go
+250
-1073
18 changed files
expand all
collapse all
unified
split
examples
default.ron
src
config.rs
main.rs
modules.rs
msg.rs
panel.rs
widgets
icon.rs
main.rs
mod.rs
right.rs
taskbar_button.rs
windows
calendar_menu.rs
directory_menu.rs
mod.rs
notification_menu.rs
quick_settings_menu.rs
start_menu.rs
systray_menu.rs
+14
-29
examples/default.ron
···
1
1
(
2
2
-
position: Bottom,
3
3
-
monitor: All,
4
4
-
theme: Dark,
5
5
-
floating: false,
6
6
-
modules: (
7
7
-
left: [
8
8
-
StartMenu,
9
9
-
Workspaces,
10
10
-
Taskbar(
11
11
-
merge_windows: true,
12
12
-
merge_pinned: true
13
13
-
),
14
14
-
],
15
15
-
middle: [],
16
16
-
right: [
17
17
-
SystemTray(hidden: true),
18
18
-
QuickSettings(
19
19
-
show_connection: true,
20
20
-
show_volume: true,
21
21
-
show_power: false,
22
22
-
),
23
23
-
DateTime(
24
24
-
calendar: true,
25
25
-
upper_format: "%H:%M", // https://docs.rs/chrono/latest/chrono/format/strftime/index.html
26
26
-
lower_format: "%d/%m/%Y",
27
27
-
),
28
28
-
NotificationTray,
29
29
-
ViewDesktop(show_on_hover: true, thin: true)
30
30
-
]
2
2
+
hidden_icon_menu: true,
3
3
+
taskbar_items: (
4
4
+
search: Hide,
5
5
+
task_view: true,
6
6
+
widgets: false,
7
7
+
),
8
8
+
taskbar_behaviours: (
9
9
+
position: Bottom,
10
10
+
alignment: Left,
11
11
+
auto_hide: false,
12
12
+
show_badges: true,
13
13
+
all_displays: true,
14
14
+
desktop_corner: true,
15
15
+
hide_labels: Always,
31
16
)
32
17
)
+56
-227
src/config.rs
···
1
1
-
use clap::ValueEnum;
2
2
-
use iced::{color, theme::palette};
3
1
use serde::Deserialize;
4
2
5
5
-
use crate::modules::Module;
6
6
-
7
3
pub const BUTTON_SIZE: f32 = 40.0;
8
4
pub const BUTTON_PADDING: u16 = 6;
9
5
pub const GAP: u16 = 4;
10
6
pub const CORNER_RADIUS: u16 = 8;
11
7
12
12
-
#[derive(Default, Deserialize, Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, ValueEnum)]
8
8
+
#[derive(Deserialize, Debug)]
9
9
+
pub enum SearchType {
10
10
+
Hide,
11
11
+
IconOnly,
12
12
+
IconAndLabel,
13
13
+
SearchBox,
14
14
+
}
15
15
+
16
16
+
#[derive(Deserialize, Debug)]
17
17
+
pub struct TaskbarItems {
18
18
+
pub search: SearchType,
19
19
+
pub task_view: bool,
20
20
+
pub widgets: bool,
21
21
+
}
22
22
+
23
23
+
#[derive(Deserialize, Debug)]
13
24
pub enum Position {
14
25
Top,
15
15
-
#[default]
16
26
Bottom,
17
27
Left,
18
28
Right,
19
29
}
20
30
21
21
-
#[derive(Default, Deserialize, Debug)]
22
22
-
pub enum Monitor {
23
23
-
Single(String),
24
24
-
// Multiple(Vec<String>),
25
25
-
#[default]
26
26
-
All,
31
31
+
#[derive(Deserialize, Debug)]
32
32
+
pub enum Alignment {
33
33
+
Left,
34
34
+
Center,
27
35
}
28
36
29
29
-
#[derive(Default, Deserialize, Debug)]
30
30
-
pub struct Modules {
31
31
-
pub left: Vec<Module>,
32
32
-
pub middle: Vec<Module>,
33
33
-
pub right: Vec<Module>,
37
37
+
#[derive(Deserialize, Debug)]
38
38
+
pub enum HideLabels {
39
39
+
Always,
40
40
+
WhenFull,
41
41
+
Never,
34
42
}
35
43
36
36
-
#[derive(Default, Deserialize, Debug)]
37
37
-
pub struct Config {
44
44
+
#[derive(Deserialize, Debug)]
45
45
+
pub struct TaskbarBehaviours {
38
46
pub position: Position,
39
39
-
pub monitor: Monitor,
40
40
-
pub theme: Theme,
41
41
-
pub floating: bool,
42
42
-
pub modules: Modules,
47
47
+
pub alignment: Alignment,
48
48
+
pub auto_hide: bool,
49
49
+
pub show_badges: bool,
50
50
+
pub all_displays: bool,
51
51
+
pub desktop_corner: bool,
52
52
+
pub hide_labels: HideLabels,
43
53
}
44
54
45
45
-
#[derive(Default, Deserialize, Debug, Clone, Copy)]
46
46
-
pub enum Theme {
47
47
-
#[default]
48
48
-
Dark,
49
49
-
Light,
50
50
-
CatppuccinFrappe,
51
51
-
CatppuccinLatte,
52
52
-
CatppuccinMocha,
53
53
-
CatppuccinMacchiato,
54
54
-
Dracula,
55
55
-
TokyoNight,
56
56
-
TokyoNightStorm,
57
57
-
TokyoNightLight,
58
58
-
KanagawaWave,
59
59
-
KanagawaDragon,
60
60
-
KanagawaLotus,
61
61
-
Moonfly,
62
62
-
Nightfly,
63
63
-
Oxocarbon,
64
64
-
Ferra,
65
65
-
Nord,
66
66
-
SolarizedLight,
67
67
-
SolarizedDark,
68
68
-
GruvboxLight,
69
69
-
GruvboxDark,
70
70
-
Custom {
71
71
-
background: i32,
72
72
-
text_color: i32,
73
73
-
primary: i32,
74
74
-
success: i32,
75
75
-
danger: i32,
76
76
-
},
77
77
-
CustomAdvanced {
78
78
-
dark: bool,
79
79
-
text_color: i32,
80
80
-
background: i32,
81
81
-
background_weak: i32,
82
82
-
background_strong: i32,
83
83
-
primary: i32,
84
84
-
primary_weak: i32,
85
85
-
primary_strong: i32,
86
86
-
secondary: i32,
87
87
-
secondary_weak: i32,
88
88
-
secondary_strong: i32,
89
89
-
success: i32,
90
90
-
success_weak: i32,
91
91
-
success_strong: i32,
92
92
-
danger: i32,
93
93
-
danger_weak: i32,
94
94
-
danger_strong: i32,
95
95
-
},
55
55
+
#[derive(Deserialize, Debug)]
56
56
+
pub struct Config {
57
57
+
pub hidden_icon_menu: bool,
58
58
+
pub taskbar_items: TaskbarItems,
59
59
+
pub taskbar_behaviours: TaskbarBehaviours,
96
60
}
97
61
98
98
-
impl Into<iced::Theme> for Theme {
99
99
-
fn into(self) -> iced::Theme {
100
100
-
match self {
101
101
-
Theme::Dark => iced::Theme::Dark,
102
102
-
Theme::Light => iced::Theme::Light,
103
103
-
Theme::CatppuccinFrappe => iced::Theme::CatppuccinFrappe,
104
104
-
Theme::CatppuccinLatte => iced::Theme::CatppuccinLatte,
105
105
-
Theme::CatppuccinMocha => iced::Theme::CatppuccinMocha,
106
106
-
Theme::CatppuccinMacchiato => iced::Theme::CatppuccinMacchiato,
107
107
-
Theme::Dracula => iced::Theme::Dracula,
108
108
-
Theme::Custom {
109
109
-
background,
110
110
-
text_color,
111
111
-
primary,
112
112
-
success,
113
113
-
danger,
114
114
-
} => iced::Theme::custom(
115
115
-
String::from("custom-theme"),
116
116
-
palette::Palette {
117
117
-
background: color!(background),
118
118
-
text: color!(text_color),
119
119
-
primary: color!(primary),
120
120
-
success: color!(success),
121
121
-
danger: color!(danger),
122
122
-
},
123
123
-
),
124
124
-
Theme::CustomAdvanced {
125
125
-
dark,
126
126
-
background,
127
127
-
background_weak,
128
128
-
background_strong,
129
129
-
text_color,
130
130
-
primary,
131
131
-
primary_weak,
132
132
-
primary_strong,
133
133
-
secondary,
134
134
-
secondary_weak,
135
135
-
secondary_strong,
136
136
-
success,
137
137
-
success_weak,
138
138
-
success_strong,
139
139
-
danger,
140
140
-
danger_weak,
141
141
-
danger_strong,
142
142
-
} => {
143
143
-
let generate = |_| iced::theme::palette::Extended {
144
144
-
background: palette::Background {
145
145
-
base: palette::Pair {
146
146
-
color: color!(background),
147
147
-
text: color!(text_color),
148
148
-
},
149
149
-
weak: palette::Pair {
150
150
-
color: color!(background_weak),
151
151
-
text: color!(text_color),
152
152
-
},
153
153
-
strong: palette::Pair {
154
154
-
color: color!(background_strong),
155
155
-
text: color!(text_color),
156
156
-
},
157
157
-
},
158
158
-
primary: palette::Primary {
159
159
-
base: palette::Pair {
160
160
-
color: color!(primary),
161
161
-
text: color!(text_color),
162
162
-
},
163
163
-
weak: palette::Pair {
164
164
-
color: color!(primary_weak),
165
165
-
text: color!(text_color),
166
166
-
},
167
167
-
strong: palette::Pair {
168
168
-
color: color!(primary_strong),
169
169
-
text: color!(text_color),
170
170
-
},
171
171
-
},
172
172
-
secondary: palette::Secondary {
173
173
-
base: palette::Pair {
174
174
-
color: color!(secondary),
175
175
-
text: color!(text_color),
176
176
-
},
177
177
-
weak: palette::Pair {
178
178
-
color: color!(secondary_weak),
179
179
-
text: color!(text_color),
180
180
-
},
181
181
-
strong: palette::Pair {
182
182
-
color: color!(secondary_strong),
183
183
-
text: color!(text_color),
184
184
-
},
185
185
-
},
186
186
-
success: palette::Success {
187
187
-
base: palette::Pair {
188
188
-
color: color!(success),
189
189
-
text: color!(text_color),
190
190
-
},
191
191
-
weak: palette::Pair {
192
192
-
color: color!(success_weak),
193
193
-
text: color!(text_color),
194
194
-
},
195
195
-
strong: palette::Pair {
196
196
-
color: color!(success_strong),
197
197
-
text: color!(text_color),
198
198
-
},
199
199
-
},
200
200
-
danger: palette::Danger {
201
201
-
base: palette::Pair {
202
202
-
color: color!(danger),
203
203
-
text: color!(text_color),
204
204
-
},
205
205
-
weak: palette::Pair {
206
206
-
color: color!(danger_weak),
207
207
-
text: color!(text_color),
208
208
-
},
209
209
-
strong: palette::Pair {
210
210
-
color: color!(danger_strong),
211
211
-
text: color!(text_color),
212
212
-
},
213
213
-
},
214
214
-
is_dark: dark,
215
215
-
};
216
216
-
217
217
-
iced::Theme::custom_with_fn(
218
218
-
String::from("custom-theme"),
219
219
-
iced::theme::Palette {
220
220
-
background: color!(background),
221
221
-
text: color!(text_color),
222
222
-
primary: color!(primary),
223
223
-
success: color!(success),
224
224
-
danger: color!(danger),
225
225
-
},
226
226
-
generate,
227
227
-
)
228
228
-
}
229
229
-
Theme::TokyoNight => iced::Theme::TokyoNight,
230
230
-
Theme::TokyoNightStorm => iced::Theme::TokyoNightStorm,
231
231
-
Theme::TokyoNightLight => iced::Theme::TokyoNightLight,
232
232
-
Theme::KanagawaWave => iced::Theme::KanagawaWave,
233
233
-
Theme::KanagawaDragon => iced::Theme::KanagawaDragon,
234
234
-
Theme::KanagawaLotus => iced::Theme::KanagawaLotus,
235
235
-
Theme::Moonfly => iced::Theme::Moonfly,
236
236
-
Theme::Nightfly => iced::Theme::Nightfly,
237
237
-
Theme::Oxocarbon => iced::Theme::Oxocarbon,
238
238
-
Theme::Ferra => iced::Theme::Ferra,
239
239
-
Theme::Nord => iced::Theme::Nord,
240
240
-
Theme::SolarizedLight => iced::Theme::SolarizedLight,
241
241
-
Theme::SolarizedDark => iced::Theme::SolarizedDark,
242
242
-
Theme::GruvboxLight => iced::Theme::GruvboxLight,
243
243
-
Theme::GruvboxDark => iced::Theme::GruvboxDark,
62
62
+
impl Default for Config {
63
63
+
fn default() -> Self {
64
64
+
Config {
65
65
+
hidden_icon_menu: true,
66
66
+
taskbar_items: TaskbarItems {
67
67
+
search: SearchType::Hide,
68
68
+
task_view: true,
69
69
+
widgets: false,
70
70
+
},
71
71
+
taskbar_behaviours: TaskbarBehaviours {
72
72
+
position: Position::Bottom,
73
73
+
alignment: Alignment::Left,
74
74
+
auto_hide: false,
75
75
+
show_badges: true,
76
76
+
all_displays: true,
77
77
+
desktop_corner: true,
78
78
+
hide_labels: HideLabels::Always,
79
79
+
},
244
80
}
245
81
}
246
82
}
247
247
-
248
248
-
#[derive(Deserialize, Debug)]
249
249
-
pub enum SeperatorStyle {
250
250
-
Solid,
251
251
-
Dotted,
252
252
-
Handle,
253
253
-
}
+1
-9
src/main.rs
···
4
4
use std::{fs, path::PathBuf};
5
5
6
6
mod config;
7
7
-
mod modules;
8
7
mod msg;
9
8
mod panel;
10
9
mod styles;
11
10
mod widgets;
12
12
-
mod windows;
13
11
14
12
#[derive(Parser)]
15
13
#[command(version, about, long_about = None)]
16
14
struct NanelCli {
17
17
-
/// Custom config file to read from
18
15
#[arg(short, long, value_name = "FILE")]
19
16
config: Option<PathBuf>,
20
20
-
#[arg(value_enum, short, long, value_name = "FILE")]
21
21
-
position: Option<config::Position>,
22
17
}
23
18
24
19
fn main() {
···
28
23
29
24
if let Some(config_path) = cli.config {
30
25
let contents = fs::read_to_string(config_path).expect("Config file could not be read.");
31
31
-
config = ron::from_str(&contents).expect("Invalid config");
32
32
-
}
33
26
34
34
-
if let Some(position) = cli.position {
35
35
-
config.position = position;
27
27
+
config = ron::from_str(&contents).expect("Invalid config");
36
28
}
37
29
38
30
panel_main(config).unwrap();
-247
src/modules.rs
···
1
1
-
use chrono::{DateTime, Datelike, Local, Timelike};
2
2
-
use iced::{
3
3
-
Alignment, Element, Length,
4
4
-
widget::{column, row, text},
5
5
-
};
6
6
-
use iced_fonts::Bootstrap;
7
7
-
use serde::Deserialize;
8
8
-
9
9
-
use crate::{
10
10
-
config,
11
11
-
msg::{Message, NanelWindowType},
12
12
-
panel::label,
13
13
-
styles::{solid, transparent},
14
14
-
widgets::{
15
15
-
button_with_position::ButtonInfo, icon, small_icon, taskbar_button, taskbar_button_thin,
16
16
-
taskbar_button_with_info,
17
17
-
},
18
18
-
windows::{
19
19
-
calendar_menu, directory_menu, notification_menu, quick_settings_menu, start_menu,
20
20
-
systray_menu,
21
21
-
},
22
22
-
};
23
23
-
24
24
-
#[derive(Deserialize, Debug)]
25
25
-
pub enum Module {
26
26
-
StartMenu,
27
27
-
Workspaces,
28
28
-
DirectoryMenu,
29
29
-
VerticalSeperator,
30
30
-
Taskbar {
31
31
-
merge_windows: bool,
32
32
-
merge_pinned: bool,
33
33
-
},
34
34
-
SystemTray {
35
35
-
hidden: bool,
36
36
-
},
37
37
-
QuickSettings {
38
38
-
show_connection: bool,
39
39
-
show_volume: bool,
40
40
-
show_power: bool,
41
41
-
},
42
42
-
DateTime {
43
43
-
calendar: bool,
44
44
-
upper_format: String,
45
45
-
lower_format: String,
46
46
-
},
47
47
-
NotificationTray,
48
48
-
ViewDesktop {
49
49
-
show_on_hover: bool,
50
50
-
thin: bool,
51
51
-
},
52
52
-
}
53
53
-
54
54
-
pub fn get_connection<'a, Message>(show_connection: &bool) -> Option<Element<'a, Message>> {
55
55
-
match show_connection {
56
56
-
true => Some(small_icon(Bootstrap::Wifi).into()),
57
57
-
false => None,
58
58
-
}
59
59
-
}
60
60
-
61
61
-
pub fn get_volume<'a, Message>(show_volume: &bool) -> Option<Element<'a, Message>> {
62
62
-
match show_volume {
63
63
-
true => Some(small_icon(Bootstrap::VolumeUpFill).into()),
64
64
-
false => None,
65
65
-
}
66
66
-
}
67
67
-
68
68
-
pub fn get_power<'a, Message>(show_power: &bool) -> Option<Element<'a, Message>> {
69
69
-
match show_power {
70
70
-
true => Some(small_icon(Bootstrap::Battery).into()),
71
71
-
false => None,
72
72
-
}
73
73
-
}
74
74
-
pub fn start_menu<'a>(vertical: &bool) -> Element<'a, Message> {
75
75
-
taskbar_button_with_info(icon(Bootstrap::Microsoft), true, vertical, transparent)
76
76
-
.on_press_with(|info| Message::UserToggledWindow {
77
77
-
x: info.position.x as i32,
78
78
-
y: info.position.y as i32,
79
79
-
viewport: (info.viewport.0 as i32, info.viewport.1 as i32),
80
80
-
window_type: NanelWindowType::Start,
81
81
-
container_id: start_menu::START_MENU.clone(),
82
82
-
})
83
83
-
.into()
84
84
-
}
85
85
-
86
86
-
pub fn workspaces<'a>(vertical: &bool) -> Element<'a, Message> {
87
87
-
taskbar_button(icon(Bootstrap::Stack), true, vertical, transparent)
88
88
-
.on_press(Message::UserToggledWorkspaces)
89
89
-
.into()
90
90
-
}
91
91
-
92
92
-
pub fn directory_menu<'a>(vertical: &bool) -> Element<'a, Message> {
93
93
-
taskbar_button_with_info(icon(Bootstrap::FolderFill), true, vertical, transparent)
94
94
-
.on_press_with(|info| Message::UserToggledWindow {
95
95
-
x: info.position.x as i32,
96
96
-
y: info.position.y as i32,
97
97
-
viewport: (info.viewport.0 as i32, info.viewport.1 as i32),
98
98
-
window_type: NanelWindowType::Directory,
99
99
-
container_id: directory_menu::DIRECTORY_MENU.clone(),
100
100
-
})
101
101
-
.into()
102
102
-
}
103
103
-
104
104
-
pub fn system_tray<'a>(
105
105
-
hidden_menu: &bool,
106
106
-
vertical: &bool,
107
107
-
position: config::Position,
108
108
-
) -> Element<'a, Message> {
109
109
-
let icon = match position {
110
110
-
config::Position::Top => small_icon(Bootstrap::ChevronDown),
111
111
-
config::Position::Bottom => small_icon(Bootstrap::ChevronUp),
112
112
-
config::Position::Left => small_icon(Bootstrap::ChevronRight),
113
113
-
config::Position::Right => small_icon(Bootstrap::ChevronLeft),
114
114
-
};
115
115
-
116
116
-
match hidden_menu {
117
117
-
false => text!("TODO: systray").into(),
118
118
-
true => taskbar_button_with_info(icon, false, vertical, transparent)
119
119
-
.on_press_with(|info| Message::UserToggledWindow {
120
120
-
x: info.position.x as i32,
121
121
-
y: info.position.y as i32,
122
122
-
viewport: (info.viewport.0 as i32, info.viewport.1 as i32),
123
123
-
window_type: NanelWindowType::SystemTray,
124
124
-
container_id: systray_menu::SYSTRAY_MENU.clone(),
125
125
-
})
126
126
-
.into(),
127
127
-
}
128
128
-
}
129
129
-
130
130
-
pub fn notification_tray<'a>(vertical: &bool) -> Element<'a, Message> {
131
131
-
taskbar_button_with_info(small_icon(Bootstrap::Bell), false, vertical, transparent)
132
132
-
.on_press_with(|info| Message::UserToggledWindow {
133
133
-
x: info.position.x as i32,
134
134
-
y: info.position.y as i32,
135
135
-
viewport: (info.viewport.0 as i32, info.viewport.1 as i32),
136
136
-
window_type: NanelWindowType::Notification,
137
137
-
container_id: notification_menu::NOTIFICATION_MENU.clone(),
138
138
-
})
139
139
-
.into()
140
140
-
}
141
141
-
142
142
-
pub fn quick_settings<'a>(
143
143
-
connection: Option<Element<'a, Message>>,
144
144
-
volume: Option<Element<'a, Message>>,
145
145
-
power: Option<Element<'a, Message>>,
146
146
-
vertical: &bool,
147
147
-
) -> Element<'a, Message> {
148
148
-
taskbar_button_with_info(
149
149
-
match vertical {
150
150
-
true => Into::<Element<Message>>::into(
151
151
-
column![]
152
152
-
.push_maybe(connection)
153
153
-
.push_maybe(volume)
154
154
-
.push_maybe(power)
155
155
-
.spacing(6)
156
156
-
.align_x(Alignment::Center)
157
157
-
.width(Length::Fill),
158
158
-
),
159
159
-
false => Into::<Element<Message>>::into(
160
160
-
row![]
161
161
-
.push_maybe(connection)
162
162
-
.push_maybe(volume)
163
163
-
.push_maybe(power)
164
164
-
.spacing(6)
165
165
-
.align_y(Alignment::Center)
166
166
-
.height(Length::Fill),
167
167
-
),
168
168
-
},
169
169
-
false,
170
170
-
vertical,
171
171
-
transparent,
172
172
-
)
173
173
-
.on_press_with(|info| Message::UserToggledWindow {
174
174
-
x: info.position.x as i32,
175
175
-
y: info.position.y as i32,
176
176
-
viewport: (info.viewport.0 as i32, info.viewport.1 as i32),
177
177
-
window_type: NanelWindowType::QuickSettings,
178
178
-
container_id: quick_settings_menu::QUICK_SETTINGS_MENU.clone(),
179
179
-
})
180
180
-
.into()
181
181
-
}
182
182
-
183
183
-
pub fn date_time<'a>(
184
184
-
datetime: DateTime<Local>,
185
185
-
calendar: &bool,
186
186
-
upper_format: &String,
187
187
-
lower_format: &String,
188
188
-
vertical: &bool,
189
189
-
) -> Element<'a, Message> {
190
190
-
taskbar_button_with_info(
191
191
-
match vertical {
192
192
-
true => Into::<Element<Message>>::into(
193
193
-
row![text("TODO")].clip(false).align_y(Alignment::Center),
194
194
-
),
195
195
-
false => Into::<Element<Message>>::into(
196
196
-
column![
197
197
-
label(
198
198
-
(String::from(" ")
199
199
-
+ datetime.format(upper_format.as_str()).to_string().as_str()
200
200
-
+ " ")
201
201
-
.as_str()
202
202
-
),
203
203
-
label(
204
204
-
(String::from(" ")
205
205
-
+ datetime.format(lower_format.as_str()).to_string().as_str()
206
206
-
+ " ")
207
207
-
.as_str()
208
208
-
),
209
209
-
]
210
210
-
.clip(false)
211
211
-
.align_x(Alignment::Center),
212
212
-
),
213
213
-
},
214
214
-
false,
215
215
-
vertical,
216
216
-
transparent,
217
217
-
)
218
218
-
.on_press_with_maybe(match calendar {
219
219
-
true => Some(|info: ButtonInfo| Message::UserToggledWindow {
220
220
-
x: info.position.x as i32,
221
221
-
y: info.position.y as i32,
222
222
-
viewport: (info.viewport.0 as i32, info.viewport.1 as i32),
223
223
-
window_type: NanelWindowType::Calendar,
224
224
-
container_id: calendar_menu::CALENDAR_MENU.clone(),
225
225
-
}),
226
226
-
false => None,
227
227
-
})
228
228
-
.padding(1)
229
229
-
.into()
230
230
-
}
231
231
-
232
232
-
pub fn view_desktop<'a>(thin: &bool, vertical: &bool) -> Element<'a, Message> {
233
233
-
match thin {
234
234
-
true => taskbar_button_thin(
235
235
-
text(if *vertical { "โ" } else { "|" })
236
236
-
.align_x(Alignment::Center)
237
237
-
.align_y(Alignment::Center),
238
238
-
vertical,
239
239
-
transparent,
240
240
-
)
241
241
-
.on_press(Message::UserToggledDesktop)
242
242
-
.into(),
243
243
-
false => taskbar_button(icon(Bootstrap::Display), true, vertical, solid)
244
244
-
.on_press(Message::UserToggledDesktop)
245
245
-
.into(),
246
246
-
}
247
247
-
}
+1
-21
src/msg.rs
···
1
1
-
use iced::{font, widget::container};
1
1
+
use iced::font;
2
2
use iced_layershell::to_layer_message;
3
3
4
4
#[to_layer_message(multi)]
5
5
#[derive(Clone, Debug)]
6
6
pub enum Message {
7
7
FontLoaded(Result<(), font::Error>),
8
8
-
UserToggledWindow {
9
9
-
x: i32,
10
10
-
y: i32,
11
11
-
viewport: (i32, i32),
12
12
-
window_type: NanelWindowType,
13
13
-
container_id: container::Id,
14
14
-
},
15
15
-
UserToggledWorkspaces,
16
16
-
UserToggledDesktop,
17
8
PanelTicked,
18
18
-
UserClosedWindow(iced::window::Id),
19
19
-
}
20
20
-
21
21
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22
22
-
pub enum NanelWindowType {
23
23
-
Start,
24
24
-
Directory,
25
25
-
Notification,
26
26
-
Calendar,
27
27
-
SystemTray,
28
28
-
QuickSettings,
29
9
}
+27
-279
src/panel.rs
···
1
1
-
use std::collections::{BTreeMap, HashMap};
2
2
-
3
1
use chrono::prelude::*;
4
2
use iced::{
5
5
-
Alignment, Element, Font, Length, Subscription, Task, Theme, time,
6
6
-
widget::{Text, column, container, row, text, vertical_rule},
7
7
-
window::Action as WindowAction,
3
3
+
Element, Font, Length, Subscription, Task, time,
4
4
+
widget::{column, container, row},
8
5
};
9
6
use iced_layershell::{
10
10
-
Appearance,
11
7
build_pattern::{MainSettings, daemon},
12
12
-
reexport::{Anchor, Layer, NewLayerShellSettings},
8
8
+
reexport::{Anchor, Layer},
13
9
settings::{LayerShellSettings, StartMode},
14
10
};
15
15
-
use iced_runtime::{Action, font, task};
11
11
+
use iced_runtime::font;
16
12
17
13
use crate::{
18
18
-
config::{BUTTON_SIZE, Config, GAP, Position},
19
19
-
modules::{
20
20
-
Module, date_time, directory_menu, get_connection, get_power, get_volume,
21
21
-
notification_tray, quick_settings, start_menu, system_tray, view_desktop, workspaces,
22
22
-
},
23
23
-
msg::{Message, NanelWindowType},
24
24
-
styles::{rounded_container, squared_container},
25
25
-
windows::{
26
26
-
calendar_menu, directory_menu, notification_menu, quick_settings_menu, start_menu,
27
27
-
systray_menu,
28
28
-
},
14
14
+
config::{Alignment, BUTTON_SIZE, Config, GAP, Position},
15
15
+
msg::Message,
16
16
+
widgets::{main, right},
29
17
};
30
18
31
19
pub struct Nanel {
32
20
pub config: Config,
33
21
pub datetime: DateTime<Local>,
34
34
-
pub window_ids: BTreeMap<iced::window::Id, NanelWindow>,
35
35
-
}
36
36
-
37
37
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38
38
-
pub struct ParentPosition {
39
39
-
pub panel: Position,
40
40
-
pub x: i32,
41
41
-
pub y: i32,
42
42
-
}
43
43
-
44
44
-
#[derive(Debug, Clone, PartialEq, Eq)]
45
45
-
pub struct NanelWindow {
46
46
-
pub parent_postion: ParentPosition,
47
47
-
pub window_type: NanelWindowType,
48
48
-
pub viewport: (i32, i32),
49
49
-
pub container_id: container::Id,
50
50
-
}
51
51
-
52
52
-
pub fn label<'a>(string: &str) -> Text<'a> {
53
53
-
text(String::from(string))
54
54
-
.font(Font::with_name("Selawik"))
55
55
-
.align_x(Alignment::Center)
56
56
-
.align_y(Alignment::Center)
57
22
}
58
23
59
24
impl Nanel {
60
60
-
fn window_id(&self, info: &NanelWindow) -> Option<&iced::window::Id> {
61
61
-
for (k, v) in self.window_ids.iter() {
62
62
-
if info == v {
63
63
-
return Some(k);
64
64
-
}
65
65
-
}
66
66
-
None
67
67
-
}
68
68
-
69
69
-
fn construct_widget_list(&self, modules: &Vec<Module>, expand: bool) -> Element<Message> {
70
70
-
let mut children = match expand {
71
71
-
true => match self.is_vertical() {
72
72
-
true => vec![column![].height(Length::Fill).into()],
73
73
-
false => vec![row![].width(Length::Fill).into()],
74
74
-
},
75
75
-
76
76
-
false => vec![],
77
77
-
};
78
78
-
79
79
-
for module in modules.iter() {
80
80
-
children.push(self.get_widget(module));
81
81
-
}
82
82
-
83
83
-
if expand {
84
84
-
match self.is_vertical() {
85
85
-
true => children.push(column![].height(Length::Fill).into()),
86
86
-
false => children.push(row![].width(Length::Fill).into()),
87
87
-
}
88
88
-
}
89
89
-
90
90
-
match self.is_vertical() {
91
91
-
true => Into::<Element<Message>>::into(column(children).spacing(GAP)),
92
92
-
false => Into::<Element<Message>>::into(row(children).spacing(GAP)),
93
93
-
}
94
94
-
.into()
95
95
-
}
96
96
-
97
25
pub fn is_vertical(&self) -> bool {
98
98
-
match self.config.position {
26
26
+
match self.config.taskbar_behaviours.position {
99
27
Position::Left | Position::Right => true,
100
28
Position::Top | Position::Bottom => false,
101
29
}
102
30
}
103
31
104
104
-
fn left(&self) -> Element<Message> {
105
105
-
self.construct_widget_list(&self.config.modules.left, false)
106
106
-
}
107
107
-
108
108
-
fn middle(&self) -> Element<Message> {
109
109
-
self.construct_widget_list(&self.config.modules.middle, true)
110
110
-
}
111
111
-
112
112
-
fn right(&self) -> Element<Message> {
113
113
-
self.construct_widget_list(&self.config.modules.right, false)
114
114
-
}
115
115
-
116
32
fn tick_time(&self) -> Subscription<Message> {
117
33
time::every(time::Duration::from_millis(200)).map(|_x| Message::PanelTicked)
118
34
}
119
35
120
120
-
pub fn get_widget(&self, module: &Module) -> Element<Message> {
121
121
-
match module {
122
122
-
Module::StartMenu => start_menu(&self.is_vertical()),
123
123
-
Module::Workspaces => workspaces(&self.is_vertical()),
124
124
-
Module::DirectoryMenu => directory_menu(&self.is_vertical()),
125
125
-
Module::VerticalSeperator => vertical_rule(GAP).into(),
126
126
-
Module::Taskbar {
127
127
-
merge_windows: _,
128
128
-
merge_pinned: _,
129
129
-
} => text!("TODO: taskbar").into(),
130
130
-
Module::SystemTray { hidden } => {
131
131
-
system_tray(hidden, &self.is_vertical(), self.config.position)
132
132
-
}
133
133
-
Module::NotificationTray => notification_tray(&self.is_vertical()),
134
134
-
Module::QuickSettings {
135
135
-
show_connection,
136
136
-
show_volume,
137
137
-
show_power,
138
138
-
} => quick_settings(
139
139
-
get_connection(show_connection),
140
140
-
get_volume(show_volume),
141
141
-
get_power(show_power),
142
142
-
&self.is_vertical(),
143
143
-
),
144
144
-
Module::DateTime {
145
145
-
calendar,
146
146
-
upper_format,
147
147
-
lower_format,
148
148
-
} => date_time(
149
149
-
self.datetime,
150
150
-
calendar,
151
151
-
upper_format,
152
152
-
lower_format,
153
153
-
&self.is_vertical(),
154
154
-
),
155
155
-
Module::ViewDesktop {
156
156
-
show_on_hover: _,
157
157
-
thin,
158
158
-
} => view_desktop(thin, &self.is_vertical()),
159
159
-
}
160
160
-
}
161
161
-
162
162
-
fn id_info(&self, id: iced::window::Id) -> Option<NanelWindow> {
163
163
-
self.window_ids.get(&id).cloned()
164
164
-
}
165
165
-
166
166
-
fn remove_id(&mut self, id: iced::window::Id) {
167
167
-
self.window_ids.remove(&id);
36
36
+
fn remove_id(&mut self, _id: iced::window::Id) {
37
37
+
// self.window_ids.remove(&id);
168
38
}
169
39
170
40
fn new(flags: Config) -> (Self, Task<Message>) {
···
172
42
Self {
173
43
config: flags,
174
44
datetime: Local::now(),
175
175
-
window_ids: BTreeMap::new(),
176
45
},
177
46
Task::batch(vec![
178
47
font::load(iced_fonts::BOOTSTRAP_FONT_BYTES).map(Message::FontLoaded),
···
184
53
Subscription::batch(vec![self.tick_time()])
185
54
}
186
55
187
187
-
fn open_popup(&mut self, window: NanelWindow) -> Task<Message> {
188
188
-
let id = iced::window::Id::unique();
189
189
-
if let Some(_) = self.window_id(&window) {
190
190
-
Task::none()
191
191
-
} else {
192
192
-
let size = (400, 400);
193
193
-
let h_margin = if window.parent_postion.x - size.0 as i32 / 2 >= 0 {
194
194
-
if window.parent_postion.x + size.0 as i32 / 2 > window.viewport.0 {
195
195
-
window.viewport.0 - size.0 as i32 - 5
196
196
-
} else {
197
197
-
window.parent_postion.x - size.0 as i32 / 2
198
198
-
}
199
199
-
} else {
200
200
-
5
201
201
-
};
202
202
-
let v_margin = if window.parent_postion.y - size.1 as i32 / 2 >= 0 {
203
203
-
if window.parent_postion.y + size.1 as i32 / 2 > window.viewport.1 {
204
204
-
window.viewport.1 - size.1 as i32 - 5
205
205
-
} else {
206
206
-
window.parent_postion.y - size.1 as i32 / 2
207
207
-
}
208
208
-
} else {
209
209
-
5
210
210
-
};
211
211
-
212
212
-
let anchor = match window.parent_postion.panel {
213
213
-
Position::Top => Anchor::Left | Anchor::Top,
214
214
-
Position::Bottom => Anchor::Left | Anchor::Bottom,
215
215
-
Position::Left => Anchor::Top | Anchor::Left,
216
216
-
Position::Right => Anchor::Top | Anchor::Right,
217
217
-
};
218
218
-
219
219
-
// (top, right, bottom, left)
220
220
-
let margin = Some(match window.parent_postion.panel {
221
221
-
Position::Top => (v_margin, 0, 0, h_margin),
222
222
-
Position::Bottom => (0, 0, v_margin, h_margin),
223
223
-
Position::Left => (v_margin, 0, 0, h_margin),
224
224
-
Position::Right => (v_margin, h_margin, 0, 0),
225
225
-
});
226
226
-
227
227
-
self.window_ids.insert(id, window);
228
228
-
229
229
-
Task::done(Message::NewLayerShell {
230
230
-
settings: NewLayerShellSettings {
231
231
-
size: Some(size),
232
232
-
layer: Layer::Top,
233
233
-
anchor,
234
234
-
exclusive_zone: None,
235
235
-
margin,
236
236
-
use_last_output: false,
237
237
-
..Default::default()
238
238
-
},
239
239
-
id,
240
240
-
})
241
241
-
}
242
242
-
}
243
243
-
244
56
fn update(&mut self, message: Message) -> Task<Message> {
245
57
match message {
246
58
Message::FontLoaded(_) => Task::none(),
247
247
-
Message::UserToggledWindow {
248
248
-
x,
249
249
-
y,
250
250
-
viewport,
251
251
-
window_type,
252
252
-
container_id,
253
253
-
} => {
254
254
-
let window = NanelWindow {
255
255
-
parent_postion: ParentPosition {
256
256
-
panel: self.config.position,
257
257
-
x,
258
258
-
y,
259
259
-
},
260
260
-
window_type,
261
261
-
viewport,
262
262
-
container_id,
263
263
-
};
264
264
-
if let Some(id) = self.window_id(&window) {
265
265
-
return Task::done(Message::UserClosedWindow(*id));
266
266
-
}
267
267
-
self.open_popup(window)
268
268
-
}
269
269
-
Message::UserToggledWorkspaces => Task::none(),
270
270
-
Message::UserToggledDesktop => Task::none(),
271
59
Message::PanelTicked => {
272
60
self.datetime = Local::now();
273
61
Task::none()
274
62
}
275
275
-
Message::UserClosedWindow(id) => {
276
276
-
self.window_ids.remove(&id);
277
277
-
task::effect(Action::Window(WindowAction::Close(id)))
278
278
-
}
279
63
_ => unreachable!(),
280
64
}
281
65
}
282
66
283
283
-
fn view(&self, id: iced::window::Id) -> Element<Message> {
284
284
-
if let Some(window) = self.id_info(id) {
285
285
-
return match window.window_type {
286
286
-
NanelWindowType::Start => start_menu::view(id).style(rounded_container),
287
287
-
NanelWindowType::Directory => directory_menu::view(id).style(rounded_container),
288
288
-
NanelWindowType::Notification => {
289
289
-
notification_menu::view(id).style(rounded_container)
290
290
-
}
291
291
-
NanelWindowType::Calendar => {
292
292
-
calendar_menu::view(id, self.datetime).style(rounded_container)
293
293
-
}
294
294
-
NanelWindowType::SystemTray => systray_menu::view(id).style(rounded_container),
295
295
-
NanelWindowType::QuickSettings => {
296
296
-
quick_settings_menu::view(id).style(rounded_container)
297
297
-
}
298
298
-
}
299
299
-
.into();
300
300
-
}
67
67
+
fn view(&self, _id: iced::window::Id) -> Element<Message> {
68
68
+
let children = vec![
69
69
+
main::view(
70
70
+
self.is_vertical(),
71
71
+
match self.config.taskbar_behaviours.alignment {
72
72
+
Alignment::Left => false,
73
73
+
Alignment::Center => true,
74
74
+
},
75
75
+
self.config.taskbar_items.task_view,
76
76
+
),
77
77
+
right::view(self.is_vertical()),
78
78
+
];
301
79
302
302
-
// Actual panel
303
303
-
let children = vec![self.left(), self.middle(), self.right()];
304
80
container(match self.is_vertical() {
305
81
true => Into::<Element<Message>>::into(column(children).spacing(GAP).padding(GAP)),
306
82
false => Into::<Element<Message>>::into(row(children).spacing(GAP).padding(GAP)),
307
83
})
308
84
.width(Length::Fill)
309
85
.height(Length::Fill)
310
310
-
.style(match &self.config.floating {
311
311
-
true => rounded_container,
312
312
-
false => squared_container,
313
313
-
})
314
86
.into()
315
87
}
316
88
317
89
fn namespace(&self) -> String {
318
90
String::from("nanel")
319
91
}
320
320
-
321
321
-
fn theme(&self) -> Theme {
322
322
-
self.config.theme.into()
323
323
-
}
324
324
-
325
325
-
fn style(&self, theme: &Theme) -> Appearance {
326
326
-
Appearance {
327
327
-
background_color: iced::Color {
328
328
-
r: 0.0,
329
329
-
g: 0.0,
330
330
-
b: 0.0,
331
331
-
a: 0.0,
332
332
-
},
333
333
-
text_color: theme.palette().text,
334
334
-
}
335
335
-
}
336
92
}
337
93
338
94
pub fn panel_main(config: Config) -> Result<(), iced_layershell::Error> {
339
95
let panel_size = BUTTON_SIZE as u16 + (GAP * 2);
340
96
341
341
-
let anchor = match &config.position {
97
97
+
let anchor = match &config.taskbar_behaviours.position {
342
98
Position::Top => Anchor::Top | Anchor::Left | Anchor::Right,
343
99
Position::Bottom => Anchor::Bottom | Anchor::Left | Anchor::Right,
344
100
Position::Left => Anchor::Left | Anchor::Top | Anchor::Bottom,
345
101
Position::Right => Anchor::Right | Anchor::Top | Anchor::Bottom,
346
102
};
347
103
348
348
-
let size = match &config.position {
104
104
+
let size = match &config.taskbar_behaviours.position {
349
105
Position::Top | Position::Bottom => (0, panel_size as u32),
350
106
Position::Left | Position::Right => (panel_size as u32, 0),
351
107
};
352
108
353
353
-
let start_mode = match &config.monitor {
354
354
-
crate::config::Monitor::Single(target) => StartMode::TargetScreen(target.clone()),
355
355
-
crate::config::Monitor::All => StartMode::AllScreens,
356
356
-
};
357
357
-
358
358
-
let margin = match &config.floating {
359
359
-
true => (GAP as i32, GAP as i32, GAP as i32, GAP as i32),
360
360
-
false => (0, 0, 0, 0),
109
109
+
let start_mode = match &config.taskbar_behaviours.all_displays {
110
110
+
true => StartMode::AllScreens,
111
111
+
false => StartMode::Active,
361
112
};
362
113
363
114
daemon(
···
374
125
exclusive_zone: panel_size as i32,
375
126
size: Some(size),
376
127
start_mode,
377
377
-
margin,
378
128
..Default::default()
379
129
},
380
130
antialiasing: true,
381
131
default_font: Font::with_name("Selawik"),
382
132
..Default::default()
383
133
})
384
384
-
.theme(Nanel::theme)
385
385
-
.style(Nanel::style)
386
134
.run_with(|| Nanel::new(config))
387
135
}
+16
src/widgets/icon.rs
···
1
1
+
use iced::{Alignment, widget::Text};
2
2
+
use iced_fonts::bootstrap::to_text;
3
3
+
4
4
+
pub fn icon<'a>(icon: iced_fonts::Bootstrap) -> Text<'a> {
5
5
+
to_text(icon)
6
6
+
.size(24)
7
7
+
.align_x(Alignment::Center)
8
8
+
.align_y(Alignment::Center)
9
9
+
}
10
10
+
11
11
+
pub fn small_icon<'a>(icon: iced_fonts::Bootstrap) -> Text<'a> {
12
12
+
to_text(icon)
13
13
+
.size(18)
14
14
+
.align_x(Alignment::Center)
15
15
+
.align_y(Alignment::Center)
16
16
+
}
+28
src/widgets/main.rs
···
1
1
+
use iced::{Renderer, Theme};
2
2
+
use iced_core::Element;
3
3
+
use iced_fonts::Bootstrap;
4
4
+
5
5
+
use crate::{msg::Message, styles::transparent};
6
6
+
7
7
+
use super::{MaybePush, icon::icon, taskbar_button, taskbar_section};
8
8
+
9
9
+
pub fn view<'a>(
10
10
+
vertical: bool,
11
11
+
centered: bool,
12
12
+
task_view: bool,
13
13
+
) -> Element<'a, Message, Theme, Renderer> {
14
14
+
taskbar_section(
15
15
+
vec![
16
16
+
taskbar_button::button(icon(Bootstrap::Microsoft), true, &vertical, transparent).into(),
17
17
+
]
18
18
+
.push_maybe(match task_view {
19
19
+
true => Some(
20
20
+
taskbar_button::button(icon(Bootstrap::Stack), true, &vertical, transparent).into(),
21
21
+
),
22
22
+
false => None,
23
23
+
}),
24
24
+
centered,
25
25
+
!centered,
26
26
+
vertical,
27
27
+
)
28
28
+
}
+45
-82
src/widgets/mod.rs
···
1
1
pub mod button_with_position;
2
2
-
use button_with_position::{ButtonWithPosition, button_with_position};
2
2
+
pub mod icon;
3
3
+
pub mod main;
4
4
+
pub mod right;
5
5
+
pub mod taskbar_button;
3
6
use iced::{
4
4
-
Alignment, Element, Length, Renderer, Theme,
5
5
-
widget::{Button, Text, button},
7
7
+
Element, Length, Renderer, Theme,
8
8
+
widget::{column, row},
6
9
};
7
7
-
use iced_fonts::bootstrap::to_text;
8
10
9
9
-
use crate::{
10
10
-
config::{BUTTON_PADDING, BUTTON_SIZE},
11
11
-
msg::Message,
12
12
-
};
11
11
+
use crate::msg::Message;
13
12
14
14
-
pub fn taskbar_button_with_info<'a>(
15
15
-
content: impl Into<Element<'a, Message, Theme, Renderer>>,
16
16
-
equal_size: bool,
17
17
-
vertical: &bool,
18
18
-
style: impl Fn(&Theme, button::Status) -> button::Style + 'a,
19
19
-
) -> ButtonWithPosition<'a, Message> {
20
20
-
match equal_size {
21
21
-
true => button_with_position(content)
22
22
-
.width(BUTTON_SIZE)
23
23
-
.height(BUTTON_SIZE)
24
24
-
.padding(BUTTON_PADDING)
25
25
-
.style(style),
26
26
-
false => match vertical {
27
27
-
true => button_with_position(content)
28
28
-
.width(BUTTON_SIZE)
29
29
-
.padding(BUTTON_PADDING)
30
30
-
.style(style),
31
31
-
false => button_with_position(content)
32
32
-
.height(BUTTON_SIZE)
33
33
-
.padding(BUTTON_PADDING)
34
34
-
.style(style),
35
35
-
},
36
36
-
}
13
13
+
pub trait MaybePush<T> {
14
14
+
fn push_maybe(self, child: Option<T>) -> Self;
37
15
}
38
16
39
39
-
pub fn taskbar_button<'a>(
40
40
-
content: impl Into<Element<'a, Message, Theme, Renderer>>,
41
41
-
equal_size: bool,
42
42
-
vertical: &bool,
43
43
-
style: impl Fn(&Theme, button::Status) -> button::Style + 'a,
44
44
-
) -> Button<'a, Message> {
45
45
-
match equal_size {
46
46
-
true => button(content)
47
47
-
.width(BUTTON_SIZE)
48
48
-
.height(BUTTON_SIZE)
49
49
-
.padding(BUTTON_PADDING)
50
50
-
.style(style),
51
51
-
false => match vertical {
52
52
-
true => button(content)
53
53
-
.width(BUTTON_SIZE)
54
54
-
.padding(BUTTON_PADDING)
55
55
-
.style(style),
56
56
-
false => button(content)
57
57
-
.height(BUTTON_SIZE)
58
58
-
.padding(BUTTON_PADDING)
59
59
-
.style(style),
60
60
-
},
17
17
+
impl<T> MaybePush<T> for Vec<T> {
18
18
+
fn push_maybe(mut self, child: Option<T>) -> Self {
19
19
+
if let Some(child) = child {
20
20
+
self.push(child);
21
21
+
self
22
22
+
} else {
23
23
+
self
24
24
+
}
61
25
}
62
26
}
63
27
64
64
-
pub fn taskbar_button_thin<'a>(
65
65
-
content: impl Into<Element<'a, Message, Theme, Renderer>>,
66
66
-
vertical: &bool,
67
67
-
style: impl Fn(&Theme, button::Status) -> button::Style + 'a,
68
68
-
) -> Button<'a, Message> {
28
28
+
fn taskbar_section<'a>(
29
29
+
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
30
30
+
expand_left: bool,
31
31
+
expand_right: bool,
32
32
+
vertical: bool,
33
33
+
) -> Element<'a, Message> {
69
34
match vertical {
70
70
-
true => button(content)
71
71
-
.height(Length::Shrink)
72
72
-
.width(BUTTON_SIZE)
73
73
-
.padding(0) // Higher padding is too thick
74
74
-
.style(style),
75
75
-
false => button(content)
76
76
-
.width(Length::Shrink)
77
77
-
.height(BUTTON_SIZE)
78
78
-
.padding(2) // Higher padding is too thick
79
79
-
.style(style),
35
35
+
true => column![]
36
36
+
.push_maybe(match expand_left {
37
37
+
true => Some(column![].height(Length::Fill)),
38
38
+
false => None,
39
39
+
})
40
40
+
.push(column(children))
41
41
+
.push_maybe(match expand_right {
42
42
+
true => Some(column![].height(Length::Fill)),
43
43
+
false => None,
44
44
+
})
45
45
+
.into(),
46
46
+
false => row![]
47
47
+
.push_maybe(match expand_left {
48
48
+
true => Some(row![].width(Length::Fill)),
49
49
+
false => None,
50
50
+
})
51
51
+
.push(row(children))
52
52
+
.push_maybe(match expand_right {
53
53
+
true => Some(row![].width(Length::Fill)),
54
54
+
false => None,
55
55
+
})
56
56
+
.into(),
80
57
}
81
58
}
82
82
-
83
83
-
pub fn icon<'a>(icon: iced_fonts::Bootstrap) -> Text<'a> {
84
84
-
to_text(icon)
85
85
-
.size(24)
86
86
-
.align_x(Alignment::Center)
87
87
-
.align_y(Alignment::Center)
88
88
-
}
89
89
-
90
90
-
pub fn small_icon<'a>(icon: iced_fonts::Bootstrap) -> Text<'a> {
91
91
-
to_text(icon)
92
92
-
.size(18)
93
93
-
.align_x(Alignment::Center)
94
94
-
.align_y(Alignment::Center)
95
95
-
}
+10
src/widgets/right.rs
···
1
1
+
use iced::{Renderer, Theme, widget::text};
2
2
+
use iced_core::Element;
3
3
+
4
4
+
use crate::msg::Message;
5
5
+
6
6
+
use super::taskbar_section;
7
7
+
8
8
+
pub fn view<'a>(vertical: bool) -> Element<'a, Message, Theme, Renderer> {
9
9
+
taskbar_section(vec![text!("right").into()], true, false, vertical)
10
10
+
}
+52
src/widgets/taskbar_button.rs
···
1
1
+
use iced::{Element, Length, Renderer, Theme};
2
2
+
3
3
+
use crate::{
4
4
+
config::{BUTTON_PADDING, BUTTON_SIZE},
5
5
+
msg::Message,
6
6
+
};
7
7
+
8
8
+
use super::button_with_position::{ButtonWithPosition, button_with_position};
9
9
+
10
10
+
pub fn button<'a>(
11
11
+
content: impl Into<Element<'a, Message, Theme, Renderer>>,
12
12
+
equal_size: bool,
13
13
+
vertical: &bool,
14
14
+
style: impl Fn(&Theme, iced::widget::button::Status) -> iced::widget::button::Style + 'a,
15
15
+
) -> ButtonWithPosition<'a, Message> {
16
16
+
match equal_size {
17
17
+
true => button_with_position(content)
18
18
+
.width(BUTTON_SIZE)
19
19
+
.height(BUTTON_SIZE)
20
20
+
.padding(BUTTON_PADDING)
21
21
+
.style(style),
22
22
+
false => match vertical {
23
23
+
true => button_with_position(content)
24
24
+
.width(BUTTON_SIZE)
25
25
+
.padding(BUTTON_PADDING)
26
26
+
.style(style),
27
27
+
false => button_with_position(content)
28
28
+
.height(BUTTON_SIZE)
29
29
+
.padding(BUTTON_PADDING)
30
30
+
.style(style),
31
31
+
},
32
32
+
}
33
33
+
}
34
34
+
35
35
+
pub fn thin<'a>(
36
36
+
content: impl Into<Element<'a, Message, Theme, Renderer>>,
37
37
+
vertical: &bool,
38
38
+
style: impl Fn(&Theme, iced::widget::button::Status) -> iced::widget::button::Style + 'a,
39
39
+
) -> iced::widget::Button<'a, Message> {
40
40
+
match vertical {
41
41
+
true => iced::widget::button(content)
42
42
+
.height(Length::Shrink)
43
43
+
.width(BUTTON_SIZE)
44
44
+
.padding(0) // Higher padding is too thick
45
45
+
.style(style),
46
46
+
false => iced::widget::button(content)
47
47
+
.width(Length::Shrink)
48
48
+
.height(BUTTON_SIZE)
49
49
+
.padding(2) // Higher padding is too thick
50
50
+
.style(style),
51
51
+
}
52
52
+
}
-97
src/windows/calendar_menu.rs
···
1
1
-
use chrono::{DateTime, Datelike, Local};
2
2
-
use iced::{
3
3
-
Alignment::Center,
4
4
-
Length,
5
5
-
widget::{Container, button, column, container, row, text},
6
6
-
window,
7
7
-
};
8
8
-
use iced_fonts::Bootstrap;
9
9
-
use once_cell::sync::Lazy;
10
10
-
11
11
-
use crate::{
12
12
-
config::{BUTTON_SIZE, GAP},
13
13
-
msg::Message,
14
14
-
styles,
15
15
-
widgets::small_icon,
16
16
-
};
17
17
-
18
18
-
pub static CALENDAR_MENU: Lazy<container::Id> = Lazy::new(|| container::Id::new("cal_menu"));
19
19
-
20
20
-
pub fn view<'a>(_window_id: window::Id, datetime: DateTime<Local>) -> Container<'a, Message> {
21
21
-
let weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
22
22
-
23
23
-
let header = row(weekdays.iter().map(|day| {
24
24
-
container(text(*day))
25
25
-
.width(Length::Fixed(BUTTON_SIZE))
26
26
-
.align_x(Center)
27
27
-
.align_y(Center)
28
28
-
.into()
29
29
-
}));
30
30
-
31
31
-
// first day of month and total days
32
32
-
let first_day = datetime
33
33
-
.with_day(1)
34
34
-
.unwrap()
35
35
-
.weekday()
36
36
-
.num_days_from_sunday() as usize;
37
37
-
38
38
-
let days_in_month = (datetime
39
39
-
.with_day(1)
40
40
-
.unwrap()
41
41
-
.with_month(datetime.month() + 1)
42
42
-
.unwrap_or(datetime.with_month(1).unwrap())
43
43
-
.signed_duration_since(datetime.with_day(1).unwrap())
44
44
-
.num_days()
45
45
-
+ 1) as usize;
46
46
-
47
47
-
let grid = column((0..6).map(|r| {
48
48
-
row((0..7).map(|col| {
49
49
-
let day_num = r * 7 + col;
50
50
-
let (content, style): (
51
51
-
String,
52
52
-
for<'b> fn(
53
53
-
&'b iced::Theme,
54
54
-
iced::widget::button::Status,
55
55
-
) -> iced::widget::button::Style,
56
56
-
) = if day_num >= first_day && day_num < first_day + days_in_month {
57
57
-
let day = day_num - first_day + 1;
58
58
-
(
59
59
-
day.to_string(),
60
60
-
match day == datetime.day() as usize {
61
61
-
true => styles::solid,
62
62
-
false => styles::transparent,
63
63
-
},
64
64
-
)
65
65
-
} else {
66
66
-
(String::new(), styles::transparent)
67
67
-
};
68
68
-
69
69
-
button(text(content).align_x(Center).align_y(Center))
70
70
-
.width(Length::Fixed(BUTTON_SIZE))
71
71
-
.height(Length::Fixed(BUTTON_SIZE))
72
72
-
.style(style)
73
73
-
.into()
74
74
-
}))
75
75
-
.spacing(1)
76
76
-
.into()
77
77
-
}))
78
78
-
.spacing(1);
79
79
-
80
80
-
container(column![
81
81
-
row![
82
82
-
text(datetime.format("%a %-e %B %Y").to_string()).size(24),
83
83
-
button(small_icon(Bootstrap::ChevronLeft))
84
84
-
.style(styles::transparent)
85
85
-
.width(Length::Shrink),
86
86
-
button(small_icon(Bootstrap::ChevronRight))
87
87
-
.style(styles::transparent)
88
88
-
.width(Length::Shrink),
89
89
-
],
90
90
-
header,
91
91
-
grid
92
92
-
])
93
93
-
.width(Length::Shrink)
94
94
-
.width(Length::Shrink)
95
95
-
.padding(GAP)
96
96
-
.id(CALENDAR_MENU.clone())
97
97
-
}
-15
src/windows/directory_menu.rs
···
1
1
-
use iced::{
2
2
-
widget::{Container, button, container},
3
3
-
window,
4
4
-
};
5
5
-
use once_cell::sync::Lazy;
6
6
-
7
7
-
use crate::{config::GAP, msg::Message};
8
8
-
9
9
-
pub static DIRECTORY_MENU: Lazy<container::Id> = Lazy::new(|| container::Id::new("dir_menu"));
10
10
-
11
11
-
pub fn view<'a>(window_id: window::Id) -> Container<'a, Message> {
12
12
-
container(button("Close Directory").on_press(Message::UserClosedWindow(window_id)))
13
13
-
.padding(GAP)
14
14
-
.id(DIRECTORY_MENU.clone())
15
15
-
}
-6
src/windows/mod.rs
···
1
1
-
pub mod calendar_menu;
2
2
-
pub mod directory_menu;
3
3
-
pub mod notification_menu;
4
4
-
pub mod quick_settings_menu;
5
5
-
pub mod start_menu;
6
6
-
pub mod systray_menu;
-15
src/windows/notification_menu.rs
···
1
1
-
use iced::{
2
2
-
widget::{Container, button, container},
3
3
-
window,
4
4
-
};
5
5
-
use once_cell::sync::Lazy;
6
6
-
7
7
-
use crate::{config::GAP, msg::Message};
8
8
-
9
9
-
pub static NOTIFICATION_MENU: Lazy<container::Id> = Lazy::new(|| container::Id::new("notif_menu"));
10
10
-
11
11
-
pub fn view<'a>(window_id: window::Id) -> Container<'a, Message> {
12
12
-
container(button("Close Notification").on_press(Message::UserClosedWindow(window_id)))
13
13
-
.padding(GAP)
14
14
-
.id(NOTIFICATION_MENU.clone())
15
15
-
}
-16
src/windows/quick_settings_menu.rs
···
1
1
-
use iced::{
2
2
-
widget::{Container, button, container},
3
3
-
window,
4
4
-
};
5
5
-
use once_cell::sync::Lazy;
6
6
-
7
7
-
use crate::{config::GAP, msg::Message};
8
8
-
9
9
-
pub static QUICK_SETTINGS_MENU: Lazy<container::Id> =
10
10
-
Lazy::new(|| container::Id::new("quick_menu"));
11
11
-
12
12
-
pub fn view<'a>(window_id: window::Id) -> Container<'a, Message> {
13
13
-
container(button("Close QuickSettings").on_press(Message::UserClosedWindow(window_id)))
14
14
-
.padding(GAP)
15
15
-
.id(QUICK_SETTINGS_MENU.clone())
16
16
-
}
-15
src/windows/start_menu.rs
···
1
1
-
use iced::{
2
2
-
widget::{Container, button, container},
3
3
-
window,
4
4
-
};
5
5
-
use once_cell::sync::Lazy;
6
6
-
7
7
-
use crate::{config::GAP, msg::Message};
8
8
-
9
9
-
pub static START_MENU: Lazy<container::Id> = Lazy::new(|| container::Id::new("start_menu"));
10
10
-
11
11
-
pub fn view<'a>(window_id: window::Id) -> Container<'a, Message> {
12
12
-
container(button("Close Start").on_press(Message::UserClosedWindow(window_id)))
13
13
-
.padding(GAP)
14
14
-
.id(START_MENU.clone())
15
15
-
}
-15
src/windows/systray_menu.rs
···
1
1
-
use iced::{
2
2
-
widget::{Container, button, container},
3
3
-
window,
4
4
-
};
5
5
-
use once_cell::sync::Lazy;
6
6
-
7
7
-
use crate::{config::GAP, msg::Message};
8
8
-
9
9
-
pub static SYSTRAY_MENU: Lazy<container::Id> = Lazy::new(|| container::Id::new("sys_menu"));
10
10
-
11
11
-
pub fn view<'a>(window_id: window::Id) -> Container<'a, Message> {
12
12
-
container(button("Close Systray").on_press(Message::UserClosedWindow(window_id)))
13
13
-
.padding(GAP)
14
14
-
.id(SYSTRAY_MENU.clone())
15
15
-
}