Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,035 changes: 465 additions & 2,570 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 3 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,10 @@ depends = ["libwayland-client0", "libpipewire-0.3-0t64", "libpulse0"]
depends = ["libwayland-client", "pipewire-libs", "pulseaudio-libs"]

[dependencies]
iced = { git = "https://github.com/MalpenZibo/iced", rev = "ba37674a834b0c9fe67a09a96b80a3a3d0dd75ef", features = [
iced = { package = "iced_layershell", git = "https://github.com/MalpenZibo/iced_layershell", branch = "main", features = [
"tokio",
"multi-window",
"advanced",
"wgpu",
"winit",
"wayland",
"image",
"svg",
"canvas",
Expand All @@ -52,8 +49,6 @@ libpulse-binding = { version = "2.28", features = ["pa_v15"] }
log = { version = "0.4", features = [] }
flexi_logger = "0.31"
pipewire = "0.9"
wayland-client = "0.31.12"
wayland-protocols = { version = "0.32.10", features = ["client", "unstable"] }
itertools = "0.14"
hex_color = { version = "3", features = ["serde"] }
anyhow = "1"
Expand Down Expand Up @@ -86,6 +81,8 @@ signal-hook = "0.4.3"
signal-hook-tokio = { version = "0.4", features = ["futures-v0_3"] }
libc = "0.2.182"
chrono-tz = "0.10.4"
wayland-client = "0.31"
wayland-protocols = { version = "0.32", features = ["client"] }

[build-dependencies]
allsorts = "0.15"
Expand Down
80 changes: 30 additions & 50 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,15 @@ use crate::{
};
use flexi_logger::LoggerHandle;
use iced::{
Alignment, Color, Element, Gradient, Length, Radians, Subscription, Task, Theme,
daemon::Appearance,
event::{
listen_with,
wayland::{Event as WaylandEvent, OutputEvent},
},
Alignment, Color, Element, Gradient, Length, OutputEvent, Radians, Subscription, SurfaceId,
Task, Theme,
event::listen_with,
gradient::Linear,
keyboard,
keyboard, set_exclusive_zone,
widget::{Row, container, mouse_area},
window::Id,
};
use log::{debug, info, warn};
use std::{collections::HashMap, f32::consts::PI, path::PathBuf};
use wayland_client::protocol::wl_output::WlOutput;

pub struct GeneralConfig {
outputs: config::Outputs,
Expand Down Expand Up @@ -73,8 +68,8 @@ pub struct App {
#[derive(Debug, Clone)]
pub enum Message {
ConfigChanged(Box<Config>),
ToggleMenu(MenuType, Id, ButtonUIRef),
CloseMenu(Id),
ToggleMenu(MenuType, SurfaceId, ButtonUIRef),
CloseMenu(SurfaceId),
Custom(String, custom_module::Message),
Updates(modules::updates::Message),
Workspaces(modules::workspaces::Message),
Expand All @@ -88,7 +83,7 @@ pub enum Message {
Privacy(modules::privacy::Message),
Settings(modules::settings::Message),
MediaPlayer(modules::media_player::Message),
OutputEvent((OutputEvent, WlOutput)),
OutputEvent(OutputEvent),
CloseAllMenus,
ResumeFromSleep,
None,
Expand Down Expand Up @@ -197,23 +192,11 @@ impl App {
));
}

pub fn title(&self, _id: Id) -> String {
String::from("ashell")
}

pub fn theme(&self, _id: Id) -> Theme {
pub fn theme(&self) -> Theme {
self.theme.get_theme().clone()
}

pub fn style(&self, theme: &Theme) -> Appearance {
Appearance {
background_color: Color::TRANSPARENT,
text_color: theme.palette().text,
icon_color: theme.palette().text,
}
}

pub fn scale_factor(&self, _id: Id) -> f64 {
pub fn scale_factor(&self) -> f64 {
self.theme.scale_factor
}

Expand Down Expand Up @@ -372,35 +355,32 @@ impl App {
])
}
},
Message::OutputEvent((event, wl_output)) => match event {
iced::event::wayland::OutputEvent::Created(info) => {
Message::OutputEvent(event) => match event {
OutputEvent::Added(info) => {
info!("Output created: {info:?}");
let name = info
.as_ref()
.and_then(|info| info.description.as_deref())
.unwrap_or("");
let name = &info.name;

self.outputs.add(
self.theme.bar_style,
&self.general_config.outputs,
self.theme.bar_position,
self.general_config.layer,
name,
wl_output,
info.id,
self.theme.scale_factor,
)
}
iced::event::wayland::OutputEvent::Removed => {
OutputEvent::Removed(output_id) => {
info!("Output destroyed");
self.outputs.remove(
self.theme.bar_style,
self.theme.bar_position,
self.general_config.layer,
wl_output,
output_id,
self.theme.scale_factor,
)
}
_ => Task::none(),
OutputEvent::InfoChanged(_) => Task::none(),
},
Message::MediaPlayer(msg) => match self.media_player.update(msg) {
modules::media_player::Action::None => Task::none(),
Expand Down Expand Up @@ -435,16 +415,21 @@ impl App {
0.0
};

Task::batch(self.outputs.iter().filter_map(|(_, shell_info, _)| {
shell_info.as_ref().map(|info| {
iced::platform_specific::shell::commands::layer_surface::set_exclusive_zone(info.id, height as i32)
})
}).collect::<Vec<_>>())
Task::batch(
self.outputs
.iter()
.filter_map(|(_, shell_info, _)| {
shell_info
.as_ref()
.map(|info| set_exclusive_zone(info.id, height as i32))
})
.collect::<Vec<_>>(),
)
}
}
}

pub fn view(&'_ self, id: Id) -> Element<'_, Message> {
pub fn view(&'_ self, id: SurfaceId) -> Element<'_, Message> {
match self.outputs.has(id) {
Some(HasOutput::Main) => {
if !self.visible {
Expand All @@ -465,7 +450,7 @@ impl App {
.padding(if self.theme.bar_style == AppearanceStyle::Islands {
[self.theme.space.xxs, self.theme.space.xxs]
} else {
[0, 0]
[0.0, 0.0]
});

let status_bar = container(centerbox).style(|t: &Theme| container::Style {
Expand Down Expand Up @@ -593,14 +578,9 @@ impl App {
crate::services::ServiceEvent::Update(_) => Message::ResumeFromSleep,
_ => Message::None,
}),
iced::output_events().map(Message::OutputEvent),
listen_with(move |evt, _, _| match evt {
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
WaylandEvent::Output(event, wl_output),
)) => {
debug!("Wayland event: {event:?}");
Some(Message::OutputEvent((event, wl_output)))
}
iced::Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
iced::event::Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
debug!("Keyboard event received: {key:?}");
if matches!(key, keyboard::Key::Named(keyboard::key::Named::Escape)) {
debug!("ESC key pressed, closing all menus");
Expand Down
25 changes: 19 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use serde_with::DisplayFromStr;
use serde_with::serde_as;
use std::path::PathBuf;
use std::time::Duration;
use std::{any::TypeId, collections::HashMap, error::Error, ops::Deref, path::Path};
use std::{collections::HashMap, error::Error, ops::Deref, path::Path};
use tokio::time::sleep;

pub const DEFAULT_CONFIG_FILE_PATH: &str = "~/.config/ashell/config.toml";
Expand Down Expand Up @@ -296,6 +296,20 @@ pub enum WeatherLocation {
Coordinates(f32, f32),
}

impl std::hash::Hash for WeatherLocation {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
WeatherLocation::Current => {}
WeatherLocation::City(city) => city.hash(state),
WeatherLocation::Coordinates(lat, lon) => {
lat.to_bits().hash(state);
lon.to_bits().hash(state);
}
}
}
}

impl Default for TempoModuleConfig {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -876,11 +890,10 @@ enum Event {
}

pub fn subscription(path: &Path) -> Subscription<Message> {
let id = TypeId::of::<Config>();
let path = path.to_path_buf();

Subscription::run_with_id(
id,
Subscription::run_with(path, |path| {
let path = path.clone();
channel(100, async move |mut output| {
match (path.parent(), path.file_name(), Inotify::init()) {
(Some(folder), Some(file_name), Ok(inotify)) => {
Expand Down Expand Up @@ -980,6 +993,6 @@ pub fn subscription(path: &Path) -> Subscription<Message> {
error!("Failed to initialize inotify: {e}");
}
}
}),
)
})
})
}
54 changes: 39 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::config::get_config;
use crate::config::{Position, get_config};
use crate::outputs::Outputs;
use app::App;
use clap::Parser;
use flexi_logger::{
Age, Cleanup, Criterion, FileSpec, LogSpecBuilder, LogSpecification, Logger, Naming,
};
use iced::Font;
use iced::{Anchor, Font, KeyboardInteractivity, Layer, LayerShellSettings};
use log::{debug, error, warn};
use std::backtrace::Backtrace;
use std::panic;
use std::path::PathBuf;
use std::{backtrace::Backtrace, borrow::Cow};

mod app;
mod components;
Expand Down Expand Up @@ -51,8 +52,7 @@ fn get_log_spec(log_level: &str) -> LogSpecification {
}
}

#[tokio::main]
async fn main() -> iced::Result {
fn main() -> iced::Result {
let args = Args::parse();
debug!("args: {args:?}");

Expand Down Expand Up @@ -93,14 +93,38 @@ async fn main() -> iced::Result {
Font::DEFAULT
};

iced::daemon(App::title, App::update, App::view)
.subscription(App::subscription)
.theme(App::theme)
.style(App::style)
.scale_factor(App::scale_factor)
.font(Cow::from(NERD_FONT))
.font(Cow::from(NERD_FONT_MONO))
.font(Cow::from(CUSTOM_FONT))
.default_font(font)
.run_with(App::new((logger, config, config_path)))
let height = Outputs::get_height(config.appearance.style, config.appearance.scale_factor);

let iced_layer = match config.layer {
config::Layer::Top => Layer::Top,
config::Layer::Bottom => Layer::Bottom,
config::Layer::Overlay => Layer::Overlay,
};

iced::application(
App::new((logger, config.clone(), config_path)),
App::update,
App::view,
)
.layer_shell(LayerShellSettings {
anchor: match config.position {
Position::Top => Anchor::TOP,
Position::Bottom => Anchor::BOTTOM,
} | Anchor::LEFT
| Anchor::RIGHT,
layer: iced_layer,
exclusive_zone: height as i32,
size: Some((0, height as u32)),
keyboard_interactivity: KeyboardInteractivity::None,
namespace: "ashell-main-layer".into(),
..Default::default()
})
.subscription(App::subscription)
.theme(App::theme)
.scale_factor(App::scale_factor)
.font(NERD_FONT)
.font(NERD_FONT_MONO)
.font(CUSTOM_FONT)
.default_font(font)
.run()
}
16 changes: 7 additions & 9 deletions src/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ use crate::config::{AppearanceStyle, Position};
use crate::theme::backdrop_color;
use crate::widgets::{self, ButtonUIRef};
use iced::alignment::Vertical;
use iced::platform_specific::shell::commands::layer_surface::{
KeyboardInteractivity, Layer, set_keyboard_interactivity, set_layer,
};
use iced::widget::container::Style;
use iced::window::Id;
use iced::{self, Element, Task, Theme, widget::container};
use iced::{Border, Length, Padding, Pixels};
use iced::{
Border, Element, KeyboardInteractivity, Layer, Length, Padding, Pixels, SurfaceId, Task, Theme,
set_keyboard_interactivity, set_layer, widget::container,
};

#[derive(Eq, PartialEq, Clone, Debug)]
pub enum MenuType {
Expand All @@ -23,12 +21,12 @@ pub enum MenuType {

#[derive(Clone, Debug)]
pub struct Menu {
pub id: Id,
pub id: SurfaceId,
pub menu_info: Option<(MenuType, ButtonUIRef)>,
}

impl Menu {
pub fn new(id: Id) -> Self {
pub fn new(id: SurfaceId) -> Self {
Self {
id,
menu_info: None,
Expand Down Expand Up @@ -145,7 +143,7 @@ impl App {
#[allow(clippy::too_many_arguments)]
pub fn menu_wrapper<'a>(
&'a self,
id: Id,
id: SurfaceId,
content: Element<'a, app::Message>,
button_ui_ref: ButtonUIRef,
) -> Element<'a, app::Message> {
Expand Down
Loading
Loading