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