Use cosmic::Application/cosmic::app::applet
This saves a bit of duplicated boilerplate. Applets seem to work fairly well with this. Though we should see if any other changes to initial window settings are needed in certain applets for the exact behavior we want.
This commit is contained in:
parent
3ad64df5f3
commit
a23f181b7f
28 changed files with 2029 additions and 2597 deletions
716
Cargo.lock
generated
716
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"applet",
|
|
||||||
"cosmic-app-list",
|
"cosmic-app-list",
|
||||||
"cosmic-applet-audio",
|
"cosmic-applet-audio",
|
||||||
"cosmic-applet-battery",
|
"cosmic-applet-battery",
|
||||||
|
|
@ -20,8 +19,8 @@ resolver="2"
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "e39748e" }
|
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "e39748e" }
|
||||||
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default-features = false, features = ["client"], rev = "e39748e" }
|
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default-features = false, features = ["client"], rev = "e39748e" }
|
||||||
cosmic-time = { git = "https://github.com/pop-os/cosmic-time", rev = "c39e737", default-features = false, features = ["libcosmic", "once_cell"] }
|
cosmic-time = { git = "https://github.com/pop-os/cosmic-time", default-features = false, features = ["libcosmic", "once_cell"] }
|
||||||
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["tokio", "wayland"] }
|
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["applet", "tokio", "wayland"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "cosmic-applet"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libcosmic.workspace = true
|
|
||||||
ron = { version = "0.8" }
|
|
||||||
serde = { version = "1.0" }
|
|
||||||
cosmic-panel-config = { git = "https://github.com/pop-os/cosmic-panel" }
|
|
||||||
log = { version = "0.4" }
|
|
||||||
|
|
@ -1,258 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use cosmic::{
|
|
||||||
cosmic_config::{config_subscription, CosmicConfigEntry},
|
|
||||||
cosmic_theme::util::CssColor,
|
|
||||||
iced::{
|
|
||||||
alignment::{Horizontal, Vertical},
|
|
||||||
wayland::InitialSurface,
|
|
||||||
widget::{self, Container},
|
|
||||||
window, Color, Element, Length, Limits, Rectangle, Settings,
|
|
||||||
},
|
|
||||||
iced_futures::Subscription,
|
|
||||||
iced_style, iced_widget, sctk,
|
|
||||||
theme::Button,
|
|
||||||
Renderer,
|
|
||||||
};
|
|
||||||
use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize};
|
|
||||||
use iced_style::{button::StyleSheet, container::Appearance};
|
|
||||||
use iced_widget::runtime::command::platform_specific::wayland::{
|
|
||||||
popup::{SctkPopupSettings, SctkPositioner},
|
|
||||||
window::SctkWindowSettings,
|
|
||||||
};
|
|
||||||
use log::error;
|
|
||||||
use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
|
|
||||||
|
|
||||||
pub use cosmic_panel_config;
|
|
||||||
|
|
||||||
const APPLET_PADDING: u32 = 8;
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn applet_button_theme() -> Button {
|
|
||||||
Button::Custom {
|
|
||||||
active: Box::new(|t| iced_style::button::Appearance {
|
|
||||||
border_radius: 0.0.into(),
|
|
||||||
..t.active(&Button::Text)
|
|
||||||
}),
|
|
||||||
hover: Box::new(|t| iced_style::button::Appearance {
|
|
||||||
border_radius: 0.0.into(),
|
|
||||||
..t.hovered(&Button::Text)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CosmicAppletHelper {
|
|
||||||
pub size: Size,
|
|
||||||
pub anchor: PanelAnchor,
|
|
||||||
pub background: CosmicPanelBackground,
|
|
||||||
pub output_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Size {
|
|
||||||
PanelSize(PanelSize),
|
|
||||||
// (width, height)
|
|
||||||
Hardcoded((u16, u16)),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CosmicAppletHelper {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
size: Size::PanelSize(
|
|
||||||
std::env::var("COSMIC_PANEL_SIZE")
|
|
||||||
.ok()
|
|
||||||
.and_then(|size| ron::from_str(size.as_str()).ok())
|
|
||||||
.unwrap_or(PanelSize::S),
|
|
||||||
),
|
|
||||||
anchor: std::env::var("COSMIC_PANEL_ANCHOR")
|
|
||||||
.ok()
|
|
||||||
.and_then(|size| ron::from_str(size.as_str()).ok())
|
|
||||||
.unwrap_or(PanelAnchor::Top),
|
|
||||||
background: std::env::var("COSMIC_PANEL_BACKGROUND")
|
|
||||||
.ok()
|
|
||||||
.and_then(|size| ron::from_str(size.as_str()).ok())
|
|
||||||
.unwrap_or(CosmicPanelBackground::ThemeDefault),
|
|
||||||
output_name: std::env::var("COSMIC_PANEL_OUTPUT").unwrap_or_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CosmicAppletHelper {
|
|
||||||
#[must_use]
|
|
||||||
pub fn suggested_size(&self) -> (u16, u16) {
|
|
||||||
match &self.size {
|
|
||||||
Size::PanelSize(size) => match size {
|
|
||||||
PanelSize::XL => (64, 64),
|
|
||||||
PanelSize::L => (36, 36),
|
|
||||||
PanelSize::M => (24, 24),
|
|
||||||
PanelSize::S => (16, 16),
|
|
||||||
PanelSize::XS => (12, 12),
|
|
||||||
},
|
|
||||||
Size::Hardcoded((width, height)) => (*width, *height),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the default window size. Helper for application init with hardcoded size.
|
|
||||||
pub fn window_size(&mut self, width: u16, height: u16) {
|
|
||||||
self.size = Size::Hardcoded((width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn window_settings<F: Default>(&self) -> Settings<F> {
|
|
||||||
self.window_settings_with_flags(F::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
|
||||||
pub fn window_settings_with_flags<F>(&self, flags: F) -> Settings<F> {
|
|
||||||
let (width, height) = self.suggested_size();
|
|
||||||
let width = u32::from(width);
|
|
||||||
let height = u32::from(height);
|
|
||||||
Settings {
|
|
||||||
initial_surface: InitialSurface::XdgWindow(SctkWindowSettings {
|
|
||||||
size: (width + APPLET_PADDING * 2, height + APPLET_PADDING * 2),
|
|
||||||
size_limits: Limits::NONE
|
|
||||||
.min_height(height as f32 + APPLET_PADDING as f32 * 2.0)
|
|
||||||
.max_height(height as f32 + APPLET_PADDING as f32 * 2.0)
|
|
||||||
.min_width(width as f32 + APPLET_PADDING as f32 * 2.0)
|
|
||||||
.max_width(width as f32 + APPLET_PADDING as f32 * 2.0),
|
|
||||||
resizable: None,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
default_text_size: 18.0,
|
|
||||||
default_font: cosmic::font::FONT,
|
|
||||||
..Settings::with_flags(flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn icon_button<'a, Message: 'static>(
|
|
||||||
&self,
|
|
||||||
icon_name: &'a str,
|
|
||||||
) -> widget::Button<'a, Message, Renderer> {
|
|
||||||
cosmic::widget::button(cosmic::theme::Button::Text)
|
|
||||||
.icon(
|
|
||||||
cosmic::theme::Svg::Symbolic,
|
|
||||||
icon_name,
|
|
||||||
self.suggested_size().0,
|
|
||||||
)
|
|
||||||
.padding(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO popup container which tracks the size of itself and requests the popup to resize to match
|
|
||||||
pub fn popup_container<'a, Message: 'static>(
|
|
||||||
&self,
|
|
||||||
content: impl Into<Element<'a, Message, Renderer>>,
|
|
||||||
) -> Container<'a, Message, Renderer> {
|
|
||||||
let (vertical_align, horizontal_align) = match self.anchor {
|
|
||||||
PanelAnchor::Left => (Vertical::Center, Horizontal::Left),
|
|
||||||
PanelAnchor::Right => (Vertical::Center, Horizontal::Right),
|
|
||||||
PanelAnchor::Top => (Vertical::Top, Horizontal::Center),
|
|
||||||
PanelAnchor::Bottom => (Vertical::Bottom, Horizontal::Center),
|
|
||||||
};
|
|
||||||
|
|
||||||
Container::<Message, Renderer>::new(Container::<Message, Renderer>::new(content).style(
|
|
||||||
cosmic::theme::Container::custom(|theme| Appearance {
|
|
||||||
text_color: Some(theme.cosmic().background.on.into()),
|
|
||||||
background: Some(Color::from(theme.cosmic().background.base).into()),
|
|
||||||
border_radius: 12.0.into(),
|
|
||||||
border_width: 0.0,
|
|
||||||
border_color: Color::TRANSPARENT,
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
.width(Length::Shrink)
|
|
||||||
.height(Length::Shrink)
|
|
||||||
.align_x(horizontal_align)
|
|
||||||
.align_y(vertical_align)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
|
||||||
pub fn get_popup_settings(
|
|
||||||
&self,
|
|
||||||
parent: window::Id,
|
|
||||||
id: window::Id,
|
|
||||||
size: Option<(u32, u32)>,
|
|
||||||
width_padding: Option<i32>,
|
|
||||||
height_padding: Option<i32>,
|
|
||||||
) -> SctkPopupSettings {
|
|
||||||
let (width, height) = self.suggested_size();
|
|
||||||
let pixel_offset = 8;
|
|
||||||
let (offset, anchor, gravity) = match self.anchor {
|
|
||||||
PanelAnchor::Left => ((pixel_offset, 0), Anchor::Right, Gravity::Right),
|
|
||||||
PanelAnchor::Right => ((-pixel_offset, 0), Anchor::Left, Gravity::Left),
|
|
||||||
PanelAnchor::Top => ((0, pixel_offset), Anchor::Bottom, Gravity::Bottom),
|
|
||||||
PanelAnchor::Bottom => ((0, -pixel_offset), Anchor::Top, Gravity::Top),
|
|
||||||
};
|
|
||||||
SctkPopupSettings {
|
|
||||||
parent,
|
|
||||||
id,
|
|
||||||
positioner: SctkPositioner {
|
|
||||||
anchor,
|
|
||||||
gravity,
|
|
||||||
offset,
|
|
||||||
size,
|
|
||||||
anchor_rect: Rectangle {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: width_padding.unwrap_or(APPLET_PADDING as i32) * 2 + i32::from(width),
|
|
||||||
height: height_padding.unwrap_or(APPLET_PADDING as i32) * 2 + i32::from(height),
|
|
||||||
},
|
|
||||||
reactive: true,
|
|
||||||
constraint_adjustment: 15, // slide_y, slide_x, flip_x, flip_y
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
parent_size: None,
|
|
||||||
grab: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn theme(&self) -> cosmic::theme::Theme {
|
|
||||||
match self.background {
|
|
||||||
CosmicPanelBackground::ThemeDefault | CosmicPanelBackground::Color(_) => {
|
|
||||||
let Ok(helper) = cosmic::cosmic_config::Config::new(
|
|
||||||
cosmic::cosmic_theme::NAME,
|
|
||||||
cosmic::cosmic_theme::Theme::<CssColor>::version() as u64,
|
|
||||||
) else {
|
|
||||||
return cosmic::theme::Theme::dark();
|
|
||||||
};
|
|
||||||
let t = cosmic::cosmic_theme::Theme::get_entry(&helper)
|
|
||||||
.map(|t| t.into_srgba())
|
|
||||||
.unwrap_or_else(|(errors, theme)| {
|
|
||||||
for err in errors {
|
|
||||||
error!("{:?}", err);
|
|
||||||
}
|
|
||||||
theme.into_srgba()
|
|
||||||
});
|
|
||||||
cosmic::theme::Theme::custom(Arc::new(t))
|
|
||||||
}
|
|
||||||
CosmicPanelBackground::Dark => cosmic::theme::Theme::dark(),
|
|
||||||
CosmicPanelBackground::Light => cosmic::theme::Theme::light(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn theme_subscription(&self, id: u64) -> Subscription<cosmic::theme::Theme> {
|
|
||||||
match self.background {
|
|
||||||
CosmicPanelBackground::ThemeDefault | CosmicPanelBackground::Color(_) => {
|
|
||||||
config_subscription::<u64, cosmic::cosmic_theme::Theme<CssColor>>(
|
|
||||||
id,
|
|
||||||
cosmic::cosmic_theme::NAME.into(),
|
|
||||||
cosmic::cosmic_theme::Theme::<CssColor>::version() as u64,
|
|
||||||
)
|
|
||||||
.map(|(_, res)| {
|
|
||||||
let theme =
|
|
||||||
res.map(|theme| theme.into_srgba())
|
|
||||||
.unwrap_or_else(|(errors, theme)| {
|
|
||||||
for err in errors {
|
|
||||||
error!("{:?}", err);
|
|
||||||
}
|
|
||||||
theme.into_srgba()
|
|
||||||
});
|
|
||||||
cosmic::theme::Theme::custom(Arc::new(theme))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
CosmicPanelBackground::Dark | CosmicPanelBackground::Light => Subscription::none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,7 +8,6 @@ edition = "2021"
|
||||||
cctk.workspace = true
|
cctk.workspace = true
|
||||||
cosmic-protocols.workspace = true
|
cosmic-protocols.workspace = true
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
# libcosmic = { path = "../../libcosmic", default-features = false, features = ["wayland", "tokio"] }
|
# libcosmic = { path = "../../libcosmic", default-features = false, features = ["wayland", "tokio"] }
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,16 @@ use cctk::sctk::reexports::calloop::channel::Sender;
|
||||||
use cctk::toplevel_info::ToplevelInfo;
|
use cctk::toplevel_info::ToplevelInfo;
|
||||||
use cctk::wayland_client::protocol::wl_data_device_manager::DndAction;
|
use cctk::wayland_client::protocol::wl_data_device_manager::DndAction;
|
||||||
use cctk::wayland_client::protocol::wl_seat::WlSeat;
|
use cctk::wayland_client::protocol::wl_seat::WlSeat;
|
||||||
|
use cosmic::app::{
|
||||||
|
applet::{cosmic_panel_config::PanelAnchor, CosmicAppletHelper},
|
||||||
|
Command,
|
||||||
|
};
|
||||||
use cosmic::cosmic_config;
|
use cosmic::cosmic_config;
|
||||||
use cosmic::cosmic_config::Config;
|
use cosmic::cosmic_config::Config;
|
||||||
use cosmic::iced;
|
use cosmic::iced;
|
||||||
use cosmic::iced::subscription::events_with;
|
use cosmic::iced::subscription::events_with;
|
||||||
use cosmic::iced::wayland::actions::data_device::DataFromMimeType;
|
use cosmic::iced::wayland::actions::data_device::DataFromMimeType;
|
||||||
use cosmic::iced::wayland::actions::data_device::DndIcon;
|
use cosmic::iced::wayland::actions::data_device::DndIcon;
|
||||||
use cosmic::iced::wayland::actions::window::SctkWindowSettings;
|
|
||||||
use cosmic::iced::wayland::popup::destroy_popup;
|
use cosmic::iced::wayland::popup::destroy_popup;
|
||||||
use cosmic::iced::wayland::popup::get_popup;
|
use cosmic::iced::wayland::popup::get_popup;
|
||||||
use cosmic::iced::widget::dnd_listener;
|
use cosmic::iced::widget::dnd_listener;
|
||||||
|
|
@ -23,9 +26,7 @@ use cosmic::iced::widget::vertical_rule;
|
||||||
use cosmic::iced::widget::vertical_space;
|
use cosmic::iced::widget::vertical_space;
|
||||||
use cosmic::iced::widget::{column, dnd_source, mouse_area, row, Column, Row};
|
use cosmic::iced::widget::{column, dnd_source, mouse_area, row, Column, Row};
|
||||||
use cosmic::iced::Color;
|
use cosmic::iced::Color;
|
||||||
use cosmic::iced::Limits;
|
use cosmic::iced::{window, Subscription};
|
||||||
use cosmic::iced::Settings;
|
|
||||||
use cosmic::iced::{window, Application, Command, Subscription};
|
|
||||||
use cosmic::iced_runtime::core::alignment::Horizontal;
|
use cosmic::iced_runtime::core::alignment::Horizontal;
|
||||||
use cosmic::iced_runtime::core::event;
|
use cosmic::iced_runtime::core::event;
|
||||||
use cosmic::iced_sctk::commands::data_device::accept_mime_type;
|
use cosmic::iced_sctk::commands::data_device::accept_mime_type;
|
||||||
|
|
@ -33,16 +34,13 @@ use cosmic::iced_sctk::commands::data_device::finish_dnd;
|
||||||
use cosmic::iced_sctk::commands::data_device::request_dnd_data;
|
use cosmic::iced_sctk::commands::data_device::request_dnd_data;
|
||||||
use cosmic::iced_sctk::commands::data_device::set_actions;
|
use cosmic::iced_sctk::commands::data_device::set_actions;
|
||||||
use cosmic::iced_sctk::commands::data_device::start_drag;
|
use cosmic::iced_sctk::commands::data_device::start_drag;
|
||||||
use cosmic::iced_sctk::settings::InitialSurface;
|
use cosmic::iced_style::application;
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
|
||||||
use cosmic::theme::Button;
|
use cosmic::theme::Button;
|
||||||
use cosmic::widget::divider;
|
use cosmic::widget::divider;
|
||||||
use cosmic::widget::rectangle_tracker::rectangle_tracker_subscription;
|
use cosmic::widget::rectangle_tracker::rectangle_tracker_subscription;
|
||||||
use cosmic::widget::rectangle_tracker::RectangleTracker;
|
use cosmic::widget::rectangle_tracker::RectangleTracker;
|
||||||
use cosmic::widget::rectangle_tracker::RectangleUpdate;
|
use cosmic::widget::rectangle_tracker::RectangleUpdate;
|
||||||
use cosmic::{Element, Theme};
|
use cosmic::{Element, Theme};
|
||||||
use cosmic_applet::cosmic_panel_config::PanelAnchor;
|
|
||||||
use cosmic_applet::CosmicAppletHelper;
|
|
||||||
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1;
|
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1;
|
||||||
use freedesktop_desktop_entry::DesktopEntry;
|
use freedesktop_desktop_entry::DesktopEntry;
|
||||||
use futures::future::pending;
|
use futures::future::pending;
|
||||||
|
|
@ -64,30 +62,7 @@ use url::Url;
|
||||||
static MIME_TYPE: &str = "text/uri-list";
|
static MIME_TYPE: &str = "text/uri-list";
|
||||||
|
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<CosmicAppList>(false, ())
|
||||||
let pixel_size = helper.suggested_size().0;
|
|
||||||
let padding = 8;
|
|
||||||
let dot_size = 4;
|
|
||||||
let spacing = 4;
|
|
||||||
let thickness = (pixel_size + 2 * padding + dot_size + spacing) as u32;
|
|
||||||
let (w, h) = match helper.anchor {
|
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => (2000, thickness),
|
|
||||||
PanelAnchor::Left | PanelAnchor::Right => (thickness, 2000),
|
|
||||||
};
|
|
||||||
|
|
||||||
CosmicAppList::run(Settings {
|
|
||||||
initial_surface: InitialSurface::XdgWindow(SctkWindowSettings {
|
|
||||||
autosize: true,
|
|
||||||
size_limits: Limits::NONE
|
|
||||||
.min_height(1.0)
|
|
||||||
.min_width(1.0)
|
|
||||||
.max_height(h as f32)
|
|
||||||
.max_width(w as f32),
|
|
||||||
resizable: None,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
@ -150,7 +125,7 @@ impl DockItem {
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
container(vertical_space(Length::Fixed(0.0)))
|
container(vertical_space(Length::Fixed(0.0)))
|
||||||
.padding(dot_radius)
|
.padding(dot_radius)
|
||||||
.style(<<CosmicAppList as cosmic::iced::Application>::Theme as container::StyleSheet>::Style::Custom(Box::new(
|
.style(<Theme as container::StyleSheet>::Style::Custom(Box::new(
|
||||||
|theme| container::Appearance {
|
|theme| container::Appearance {
|
||||||
text_color: Some(Color::TRANSPARENT),
|
text_color: Some(Color::TRANSPARENT),
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(
|
||||||
|
|
@ -224,7 +199,7 @@ struct DndOffer {
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct CosmicAppList {
|
struct CosmicAppList {
|
||||||
theme: Theme,
|
core: cosmic::app::Core,
|
||||||
popup: Option<(window::Id, DockItem)>,
|
popup: Option<(window::Id, DockItem)>,
|
||||||
surface_id_ctr: u128,
|
surface_id_ctr: u128,
|
||||||
subscription_ctr: u32,
|
subscription_ctr: u32,
|
||||||
|
|
@ -234,7 +209,6 @@ struct CosmicAppList {
|
||||||
dnd_source: Option<(window::Id, DockItem, DndAction)>,
|
dnd_source: Option<(window::Id, DockItem, DndAction)>,
|
||||||
config: AppListConfig,
|
config: AppListConfig,
|
||||||
toplevel_sender: Option<Sender<ToplevelRequest>>,
|
toplevel_sender: Option<Sender<ToplevelRequest>>,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
seat: Option<WlSeat>,
|
seat: Option<WlSeat>,
|
||||||
rectangle_tracker: Option<RectangleTracker<u32>>,
|
rectangle_tracker: Option<RectangleTracker<u32>>,
|
||||||
rectangles: HashMap<u32, iced::Rectangle>,
|
rectangles: HashMap<u32, iced::Rectangle>,
|
||||||
|
|
@ -268,7 +242,6 @@ enum Message {
|
||||||
StopListeningForDnd,
|
StopListeningForDnd,
|
||||||
IncrementSubscriptionCtr,
|
IncrementSubscriptionCtr,
|
||||||
ConfigUpdated(AppListConfig),
|
ConfigUpdated(AppListConfig),
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
@ -317,7 +290,16 @@ fn desktop_info_for_app_ids(mut app_ids: Vec<String>) -> Vec<DesktopInfo> {
|
||||||
})
|
})
|
||||||
.collect_vec(),
|
.collect_vec(),
|
||||||
);
|
);
|
||||||
ret.sort_by(|a, b| app_ids_clone.iter().position(|id| id == &a.id || id.eq(&a.name)).cmp(&app_ids_clone.iter().position(|id| id == &b.id || id.eq(&b.name))));
|
ret.sort_by(|a, b| {
|
||||||
|
app_ids_clone
|
||||||
|
.iter()
|
||||||
|
.position(|id| id == &a.id || id.eq(&a.name))
|
||||||
|
.cmp(
|
||||||
|
&app_ids_clone
|
||||||
|
.iter()
|
||||||
|
.position(|id| id == &b.id || id.eq(&b.name)),
|
||||||
|
)
|
||||||
|
});
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,17 +346,16 @@ fn index_in_list(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for CosmicAppList {
|
impl cosmic::Application for CosmicAppList {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = config::APP_ID;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let config = config::AppListConfig::load().unwrap_or_default();
|
let config = config::AppListConfig::load().unwrap_or_default();
|
||||||
let helper = CosmicAppletHelper::default();
|
|
||||||
let theme = helper.theme();
|
|
||||||
let mut self_ = CosmicAppList {
|
let mut self_ = CosmicAppList {
|
||||||
|
core,
|
||||||
favorite_list: desktop_info_for_app_ids(config.favorites.clone())
|
favorite_list: desktop_info_for_app_ids(config.favorites.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -384,9 +365,7 @@ impl Application for CosmicAppList {
|
||||||
desktop_info: e,
|
desktop_info: e,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
applet_helper: helper,
|
|
||||||
config,
|
config,
|
||||||
theme,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
self_.item_ctr = self_.favorite_list.len() as u32;
|
self_.item_ctr = self_.favorite_list.len() as u32;
|
||||||
|
|
@ -394,8 +373,12 @@ impl Application for CosmicAppList {
|
||||||
(self_, Command::none())
|
(self_, Command::none())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
config::APP_ID.to_string()
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -419,7 +402,7 @@ impl Application for CosmicAppList {
|
||||||
let new_id = window::Id(self.surface_id_ctr);
|
let new_id = window::Id(self.surface_id_ctr);
|
||||||
self.popup = Some((new_id, toplevel_group.clone()));
|
self.popup = Some((new_id, toplevel_group.clone()));
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -562,8 +545,8 @@ impl Application for CosmicAppList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::DndEnter(x, y) => {
|
Message::DndEnter(x, y) => {
|
||||||
let item_size = self.applet_helper.suggested_size().0;
|
let item_size = self.core.applet_helper.suggested_size().0;
|
||||||
let pos_in_list = match self.applet_helper.anchor {
|
let pos_in_list = match self.core.applet_helper.anchor {
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => x,
|
PanelAnchor::Top | PanelAnchor::Bottom => x,
|
||||||
PanelAnchor::Left | PanelAnchor::Right => y,
|
PanelAnchor::Left | PanelAnchor::Right => y,
|
||||||
};
|
};
|
||||||
|
|
@ -593,8 +576,8 @@ impl Application for CosmicAppList {
|
||||||
}
|
}
|
||||||
Message::DndMotion(x, y) => {
|
Message::DndMotion(x, y) => {
|
||||||
if let Some(DndOffer { preview_index, .. }) = self.dnd_offer.as_mut() {
|
if let Some(DndOffer { preview_index, .. }) = self.dnd_offer.as_mut() {
|
||||||
let item_size = self.applet_helper.suggested_size().0;
|
let item_size = self.core.applet_helper.suggested_size().0;
|
||||||
let pos_in_list = match self.applet_helper.anchor {
|
let pos_in_list = match self.core.applet_helper.anchor {
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => x,
|
PanelAnchor::Top | PanelAnchor::Bottom => x,
|
||||||
PanelAnchor::Left | PanelAnchor::Right => y,
|
PanelAnchor::Left | PanelAnchor::Right => y,
|
||||||
};
|
};
|
||||||
|
|
@ -674,7 +657,13 @@ impl Application for CosmicAppList {
|
||||||
|
|
||||||
self.favorite_list
|
self.favorite_list
|
||||||
.insert(index.min(self.favorite_list.len()), dock_item);
|
.insert(index.min(self.favorite_list.len()), dock_item);
|
||||||
self.config.update_favorites(self.favorite_list.iter().map(|dock_item| dock_item.desktop_info.id.clone()).collect(), &Config::new(APP_ID, 1).unwrap());
|
self.config.update_favorites(
|
||||||
|
self.favorite_list
|
||||||
|
.iter()
|
||||||
|
.map(|dock_item| dock_item.desktop_info.id.clone())
|
||||||
|
.collect(),
|
||||||
|
&Config::new(APP_ID, 1).unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return finish_dnd();
|
return finish_dnd();
|
||||||
}
|
}
|
||||||
|
|
@ -715,7 +704,7 @@ impl Application for CosmicAppList {
|
||||||
let subscription_ctr = self.subscription_ctr;
|
let subscription_ctr = self.subscription_ctr;
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let rand_d = rng.gen_range(0..100);
|
let rand_d = rng.gen_range(0..100);
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
async move {
|
async move {
|
||||||
if let Some(millis) = 2u64
|
if let Some(millis) = 2u64
|
||||||
.checked_pow(subscription_ctr)
|
.checked_pow(subscription_ctr)
|
||||||
|
|
@ -727,7 +716,8 @@ impl Application for CosmicAppList {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|_| Message::IncrementSubscriptionCtr,
|
|_| Message::IncrementSubscriptionCtr,
|
||||||
);
|
)
|
||||||
|
.map(cosmic::app::message::app);
|
||||||
}
|
}
|
||||||
ToplevelUpdate::RemoveToplevel(handle) => {
|
ToplevelUpdate::RemoveToplevel(handle) => {
|
||||||
for t in self
|
for t in self
|
||||||
|
|
@ -810,10 +800,14 @@ impl Application for CosmicAppList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pull back configured items into the favorites list
|
// pull back configured items into the favorites list
|
||||||
self.favorite_list = desktop_info_for_app_ids(self.config.favorites.clone()).into_iter().map(|new_dock_item| {
|
self.favorite_list = desktop_info_for_app_ids(self.config.favorites.clone())
|
||||||
if let Some(p) = self.active_list.iter().position(|dock_item| {
|
.into_iter()
|
||||||
dock_item.desktop_info.id == new_dock_item.id
|
.map(|new_dock_item| {
|
||||||
}) {
|
if let Some(p) = self
|
||||||
|
.active_list
|
||||||
|
.iter()
|
||||||
|
.position(|dock_item| dock_item.desktop_info.id == new_dock_item.id)
|
||||||
|
{
|
||||||
self.active_list.remove(p)
|
self.active_list.remove(p)
|
||||||
} else {
|
} else {
|
||||||
DockItem {
|
DockItem {
|
||||||
|
|
@ -822,99 +816,25 @@ impl Application for CosmicAppList {
|
||||||
desktop_info: new_dock_item,
|
desktop_info: new_dock_item,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).collect();
|
})
|
||||||
}
|
.collect();
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
let is_horizontal = match self.applet_helper.anchor {
|
let is_horizontal = match self.core.applet_helper.anchor {
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => true,
|
PanelAnchor::Top | PanelAnchor::Bottom => true,
|
||||||
PanelAnchor::Left | PanelAnchor::Right => false,
|
PanelAnchor::Left | PanelAnchor::Right => false,
|
||||||
};
|
};
|
||||||
if let Some((_, item, _)) = self.dnd_source.as_ref().filter(|s| s.0 == id) {
|
|
||||||
return cosmic::widget::icon(
|
|
||||||
Path::new(&item.desktop_info.icon),
|
|
||||||
self.applet_helper.suggested_size().0,
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
if let Some((
|
|
||||||
_popup_id,
|
|
||||||
DockItem {
|
|
||||||
toplevels,
|
|
||||||
desktop_info,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
)) = self.popup.as_ref().filter(|p| id == p.0)
|
|
||||||
{
|
|
||||||
let is_favorite = self.config.favorites.contains(&desktop_info.id)
|
|
||||||
|| self.config.favorites.contains(&desktop_info.name);
|
|
||||||
|
|
||||||
let mut content = column![
|
|
||||||
iced::widget::text(&desktop_info.name).horizontal_alignment(Horizontal::Center),
|
|
||||||
cosmic::widget::button(Button::Text)
|
|
||||||
.custom(vec![iced::widget::text(fl!("new-window")).into()])
|
|
||||||
.on_press(Message::Exec(desktop_info.exec.clone())),
|
|
||||||
]
|
|
||||||
.padding(8)
|
|
||||||
.spacing(4)
|
|
||||||
.align_items(Alignment::Center);
|
|
||||||
if !toplevels.is_empty() {
|
|
||||||
let mut list_col = column![];
|
|
||||||
for (handle, info) in toplevels {
|
|
||||||
let title = if info.title.len() > 20 {
|
|
||||||
format!("{:.24}...", &info.title)
|
|
||||||
} else {
|
|
||||||
info.title.clone()
|
|
||||||
};
|
|
||||||
list_col = list_col.push(
|
|
||||||
cosmic::widget::button(Button::Text)
|
|
||||||
.custom(vec![iced::widget::text(title).into()])
|
|
||||||
.on_press(Message::Activate(handle.clone())),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
content = content.push(divider::horizontal::light());
|
|
||||||
content = content.push(list_col);
|
|
||||||
content = content.push(divider::horizontal::light());
|
|
||||||
}
|
|
||||||
content = content.push(if is_favorite {
|
|
||||||
cosmic::widget::button(Button::Text)
|
|
||||||
.custom(vec![iced::widget::text(fl!("unfavorite")).into()])
|
|
||||||
.on_press(Message::UnFavorite(desktop_info.id.clone()))
|
|
||||||
} else {
|
|
||||||
cosmic::widget::button(Button::Text)
|
|
||||||
.custom(vec![iced::widget::text(fl!("favorite")).into()])
|
|
||||||
.on_press(Message::Favorite(desktop_info.id.clone()))
|
|
||||||
});
|
|
||||||
|
|
||||||
content = match toplevels.len() {
|
|
||||||
0 => content,
|
|
||||||
1 => content.push(
|
|
||||||
cosmic::widget::button(Button::Text)
|
|
||||||
.custom(vec![iced::widget::text(fl!("quit")).into()])
|
|
||||||
.on_press(Message::Quit(desktop_info.id.clone())),
|
|
||||||
),
|
|
||||||
_ => content.push(
|
|
||||||
cosmic::widget::button(Button::Text)
|
|
||||||
.custom(vec![iced::widget::text(&fl!("quit-all")).into()])
|
|
||||||
.on_press(Message::Quit(desktop_info.id.clone())),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
return self.applet_helper.popup_container(content).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut favorites: Vec<_> = self
|
let mut favorites: Vec<_> = self
|
||||||
.favorite_list
|
.favorite_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|dock_item| {
|
.map(|dock_item| {
|
||||||
dock_item.as_icon(
|
dock_item.as_icon(
|
||||||
&self.applet_helper,
|
&self.core.applet_helper,
|
||||||
self.rectangle_tracker.as_ref(),
|
self.rectangle_tracker.as_ref(),
|
||||||
self.popup.is_none(),
|
self.popup.is_none(),
|
||||||
)
|
)
|
||||||
|
|
@ -926,13 +846,13 @@ impl Application for CosmicAppList {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|o| o.dock_item.as_ref().map(|item| (item, o.preview_index)))
|
.and_then(|o| o.dock_item.as_ref().map(|item| (item, o.preview_index)))
|
||||||
{
|
{
|
||||||
favorites.insert(index, item.as_icon(&self.applet_helper, None, false));
|
favorites.insert(index, item.as_icon(&self.core.applet_helper, None, false));
|
||||||
} else if self.is_listening_for_dnd && self.favorite_list.is_empty() {
|
} else if self.is_listening_for_dnd && self.favorite_list.is_empty() {
|
||||||
// show star indicating favorite_list is drag target
|
// show star indicating favorite_list is drag target
|
||||||
favorites.push(
|
favorites.push(
|
||||||
container(cosmic::widget::icon(
|
container(cosmic::widget::icon(
|
||||||
"starred-symbolic.symbolic",
|
"starred-symbolic.symbolic",
|
||||||
self.applet_helper.suggested_size().0,
|
self.core.applet_helper.suggested_size().0,
|
||||||
))
|
))
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -944,7 +864,7 @@ impl Application for CosmicAppList {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|dock_item| {
|
.map(|dock_item| {
|
||||||
dock_item.as_icon(
|
dock_item.as_icon(
|
||||||
&self.applet_helper,
|
&self.core.applet_helper,
|
||||||
self.rectangle_tracker.as_ref(),
|
self.rectangle_tracker.as_ref(),
|
||||||
self.popup.is_none(),
|
self.popup.is_none(),
|
||||||
)
|
)
|
||||||
|
|
@ -1011,12 +931,12 @@ impl Application for CosmicAppList {
|
||||||
} else {
|
} else {
|
||||||
vec![cosmic::widget::icon(
|
vec![cosmic::widget::icon(
|
||||||
"com.system76.CosmicAppList",
|
"com.system76.CosmicAppList",
|
||||||
self.applet_helper.suggested_size().0,
|
self.core.applet_helper.suggested_size().0,
|
||||||
)
|
)
|
||||||
.into()]
|
.into()]
|
||||||
};
|
};
|
||||||
|
|
||||||
let content = match &self.applet_helper.anchor {
|
let content = match &self.core.applet_helper.anchor {
|
||||||
PanelAnchor::Left | PanelAnchor::Right => container(
|
PanelAnchor::Left | PanelAnchor::Right => container(
|
||||||
Column::with_children(content_list)
|
Column::with_children(content_list)
|
||||||
.spacing(4)
|
.spacing(4)
|
||||||
|
|
@ -1042,9 +962,83 @@ impl Application for CosmicAppList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, id: window::Id) -> Element<Message> {
|
||||||
|
if let Some((_, item, _)) = self.dnd_source.as_ref().filter(|s| s.0 == id) {
|
||||||
|
cosmic::widget::icon(
|
||||||
|
Path::new(&item.desktop_info.icon),
|
||||||
|
self.core.applet_helper.suggested_size().0,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else if let Some((
|
||||||
|
_popup_id,
|
||||||
|
DockItem {
|
||||||
|
toplevels,
|
||||||
|
desktop_info,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
)) = self.popup.as_ref().filter(|p| id == p.0)
|
||||||
|
{
|
||||||
|
let is_favorite = self.config.favorites.contains(&desktop_info.id)
|
||||||
|
|| self.config.favorites.contains(&desktop_info.name);
|
||||||
|
|
||||||
|
let mut content = column![
|
||||||
|
iced::widget::text(&desktop_info.name).horizontal_alignment(Horizontal::Center),
|
||||||
|
cosmic::widget::button(Button::Text)
|
||||||
|
.custom(vec![iced::widget::text(fl!("new-window")).into()])
|
||||||
|
.on_press(Message::Exec(desktop_info.exec.clone())),
|
||||||
|
]
|
||||||
|
.padding(8)
|
||||||
|
.spacing(4)
|
||||||
|
.align_items(Alignment::Center);
|
||||||
|
if !toplevels.is_empty() {
|
||||||
|
let mut list_col = column![];
|
||||||
|
for (handle, info) in toplevels {
|
||||||
|
let title = if info.title.len() > 20 {
|
||||||
|
format!("{:.24}...", &info.title)
|
||||||
|
} else {
|
||||||
|
info.title.clone()
|
||||||
|
};
|
||||||
|
list_col = list_col.push(
|
||||||
|
cosmic::widget::button(Button::Text)
|
||||||
|
.custom(vec![iced::widget::text(title).into()])
|
||||||
|
.on_press(Message::Activate(handle.clone())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
content = content.push(divider::horizontal::light());
|
||||||
|
content = content.push(list_col);
|
||||||
|
content = content.push(divider::horizontal::light());
|
||||||
|
}
|
||||||
|
content = content.push(if is_favorite {
|
||||||
|
cosmic::widget::button(Button::Text)
|
||||||
|
.custom(vec![iced::widget::text(fl!("unfavorite")).into()])
|
||||||
|
.on_press(Message::UnFavorite(desktop_info.id.clone()))
|
||||||
|
} else {
|
||||||
|
cosmic::widget::button(Button::Text)
|
||||||
|
.custom(vec![iced::widget::text(fl!("favorite")).into()])
|
||||||
|
.on_press(Message::Favorite(desktop_info.id.clone()))
|
||||||
|
});
|
||||||
|
|
||||||
|
content = match toplevels.len() {
|
||||||
|
0 => content,
|
||||||
|
1 => content.push(
|
||||||
|
cosmic::widget::button(Button::Text)
|
||||||
|
.custom(vec![iced::widget::text(fl!("quit")).into()])
|
||||||
|
.on_press(Message::Quit(desktop_info.id.clone())),
|
||||||
|
),
|
||||||
|
_ => content.push(
|
||||||
|
cosmic::widget::button(Button::Text)
|
||||||
|
.custom(vec![iced::widget::text(&fl!("quit-all")).into()])
|
||||||
|
.on_press(Message::Quit(desktop_info.id.clone())),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
self.core.applet_helper.popup_container(content).into()
|
||||||
|
} else {
|
||||||
|
iced::widget::text("").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
toplevel_subscription(self.subscription_ctr).map(Message::Toplevel),
|
toplevel_subscription(self.subscription_ctr).map(Message::Toplevel),
|
||||||
events_with(|e, _| match e {
|
events_with(|e, _| match e {
|
||||||
cosmic::iced_runtime::core::Event::PlatformSpecific(
|
cosmic::iced_runtime::core::Event::PlatformSpecific(
|
||||||
|
|
@ -1095,18 +1089,7 @@ impl Application for CosmicAppList {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
self.theme.clone()
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
|
||||||
Message::Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ libpulse-glib-binding = "2.25.0"
|
||||||
tokio = { version = "1.20.1", features=["full"] }
|
tokio = { version = "1.20.1", features=["full"] }
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-time.workspace = true
|
cosmic-time.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
# Application i18n
|
# Application i18n
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0-only
|
// SPDX-License-Identifier: MPL-2.0-only
|
||||||
|
|
||||||
|
use cosmic_time::once_cell::sync::Lazy;
|
||||||
use i18n_embed::{
|
use i18n_embed::{
|
||||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||||
DefaultLocalizer, LanguageLoader, Localizer,
|
DefaultLocalizer, LanguageLoader, Localizer,
|
||||||
};
|
};
|
||||||
use cosmic_time::once_cell::sync::Lazy;
|
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
mod localize;
|
mod localize;
|
||||||
|
|
||||||
|
use cosmic::app::Command;
|
||||||
use cosmic::iced::widget;
|
use cosmic::iced::widget;
|
||||||
use cosmic::iced::Limits;
|
use cosmic::iced::Limits;
|
||||||
use cosmic::iced_runtime::core::alignment::Horizontal;
|
use cosmic::iced_runtime::core::alignment::Horizontal;
|
||||||
use cosmic::theme::Svg;
|
use cosmic::theme::Svg;
|
||||||
|
|
||||||
|
use cosmic::app::applet::applet_button_theme;
|
||||||
use cosmic::widget::{button, divider, icon};
|
use cosmic::widget::{button, divider, icon};
|
||||||
use cosmic::Renderer;
|
use cosmic::Renderer;
|
||||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
|
||||||
|
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
self,
|
self,
|
||||||
widget::{column, row, slider, text},
|
widget::{column, row, slider, text},
|
||||||
window, Alignment, Application, Command, Length, Subscription,
|
window, Alignment, Length, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application;
|
||||||
use cosmic::{Element, Theme};
|
use cosmic::{Element, Theme};
|
||||||
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
||||||
|
|
||||||
use iced::wayland::popup::{destroy_popup, get_popup};
|
use iced::wayland::popup::{destroy_popup, get_popup};
|
||||||
use iced::widget::container;
|
use iced::widget::container;
|
||||||
use iced::Color;
|
|
||||||
|
|
||||||
mod pulse;
|
mod pulse;
|
||||||
use crate::localize::localize;
|
use crate::localize::localize;
|
||||||
|
|
@ -33,24 +33,22 @@ pub fn main() -> cosmic::iced::Result {
|
||||||
// Prepare i18n
|
// Prepare i18n
|
||||||
localize();
|
localize();
|
||||||
|
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<Audio>(false, ())
|
||||||
Audio::run(helper.window_settings())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SHOW_MEDIA_CONTROLS: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
static SHOW_MEDIA_CONTROLS: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Audio {
|
struct Audio {
|
||||||
|
core: cosmic::app::Core,
|
||||||
is_open: IsOpen,
|
is_open: IsOpen,
|
||||||
current_output: Option<DeviceInfo>,
|
current_output: Option<DeviceInfo>,
|
||||||
current_input: Option<DeviceInfo>,
|
current_input: Option<DeviceInfo>,
|
||||||
outputs: Vec<DeviceInfo>,
|
outputs: Vec<DeviceInfo>,
|
||||||
inputs: Vec<DeviceInfo>,
|
inputs: Vec<DeviceInfo>,
|
||||||
pulse_state: PulseState,
|
pulse_state: PulseState,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
input_icon_name: String,
|
input_icon_name: String,
|
||||||
theme: Theme,
|
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
show_media_controls_in_top_panel: bool,
|
show_media_controls_in_top_panel: bool,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
|
|
@ -125,24 +123,21 @@ enum Message {
|
||||||
OutputChanged(String),
|
OutputChanged(String),
|
||||||
InputChanged(String),
|
InputChanged(String),
|
||||||
Pulse(pulse::Event),
|
Pulse(pulse::Event),
|
||||||
Ignore,
|
|
||||||
TogglePopup,
|
TogglePopup,
|
||||||
ToggleMediaControlsInTopPanel(chain::Toggler, bool),
|
ToggleMediaControlsInTopPanel(chain::Toggler, bool),
|
||||||
Frame(Instant),
|
Frame(Instant),
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Audio {
|
impl cosmic::Application for Audio {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicAppletAudio";
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Audio, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Audio, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
(
|
||||||
Audio {
|
Audio {
|
||||||
|
core,
|
||||||
is_open: IsOpen::None,
|
is_open: IsOpen::None,
|
||||||
current_output: None,
|
current_output: None,
|
||||||
current_input: None,
|
current_input: None,
|
||||||
|
|
@ -150,31 +145,22 @@ impl Application for Audio {
|
||||||
inputs: vec![],
|
inputs: vec![],
|
||||||
icon_name: "audio-volume-high-symbolic".to_string(),
|
icon_name: "audio-volume-high-symbolic".to_string(),
|
||||||
input_icon_name: "audio-input-microphone-symbolic".to_string(),
|
input_icon_name: "audio-input-microphone-symbolic".to_string(),
|
||||||
applet_helper,
|
|
||||||
theme,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
String::from("Audio")
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
self.theme.clone()
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
Message::Ignore
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -191,7 +177,7 @@ impl Application for Audio {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -327,14 +313,10 @@ impl Application for Audio {
|
||||||
}
|
}
|
||||||
pulse::Event::Disconnected => self.pulse_state.disconnected(),
|
pulse::Event::Disconnected => self.pulse_state.disconnected(),
|
||||||
},
|
},
|
||||||
Message::Ignore => {}
|
|
||||||
Message::ToggleMediaControlsInTopPanel(chain, enabled) => {
|
Message::ToggleMediaControlsInTopPanel(chain, enabled) => {
|
||||||
self.timeline.set_chain(chain).start();
|
self.timeline.set_chain(chain).start();
|
||||||
self.show_media_controls_in_top_panel = enabled;
|
self.show_media_controls_in_top_panel = enabled;
|
||||||
}
|
}
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
|
|
@ -342,7 +324,6 @@ impl Application for Audio {
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
pulse::connect().map(Message::Pulse),
|
pulse::connect().map(Message::Pulse),
|
||||||
self.timeline
|
self.timeline
|
||||||
.as_subscription()
|
.as_subscription()
|
||||||
|
|
@ -350,13 +331,15 @@ impl Application for Audio {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
if id == window::Id(0) {
|
self.core
|
||||||
self.applet_helper
|
.applet_helper
|
||||||
.icon_button(&self.icon_name)
|
.icon_button(&self.icon_name)
|
||||||
.on_press(Message::TogglePopup)
|
.on_press(Message::TogglePopup)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let audio_disabled = matches!(self.pulse_state, PulseState::Disconnected(_));
|
let audio_disabled = matches!(self.pulse_state, PulseState::Disconnected(_));
|
||||||
let out_f64 = VolumeLinear::from(
|
let out_f64 = VolumeLinear::from(
|
||||||
self.current_output
|
self.current_output
|
||||||
|
|
@ -474,11 +457,11 @@ impl Application for Audio {
|
||||||
.align_items(Alignment::Start)
|
.align_items(Alignment::Start)
|
||||||
.padding([8, 0]);
|
.padding([8, 0]);
|
||||||
|
|
||||||
self.applet_helper
|
self.core
|
||||||
|
.applet_helper
|
||||||
.popup_container(container(content))
|
.popup_container(container(content))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn revealer(
|
fn revealer(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.16.0"
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-time.workspace = true
|
cosmic-time.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
zbus = { version = "3.13", default-features = false, features = ["tokio"] }
|
zbus = { version = "3.13", default-features = false, features = ["tokio"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,18 @@ use crate::upower_device::{device_subscription, DeviceDbusEvent};
|
||||||
use crate::upower_kbdbacklight::{
|
use crate::upower_kbdbacklight::{
|
||||||
kbd_backlight_subscription, KeyboardBacklightRequest, KeyboardBacklightUpdate,
|
kbd_backlight_subscription, KeyboardBacklightRequest, KeyboardBacklightUpdate,
|
||||||
};
|
};
|
||||||
|
use cosmic::app::{applet::applet_button_theme, Command};
|
||||||
use cosmic::iced::alignment::Horizontal;
|
use cosmic::iced::alignment::Horizontal;
|
||||||
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
||||||
use cosmic::iced::Color;
|
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
widget::{column, container, row, slider, text},
|
widget::{column, container, row, slider, text},
|
||||||
window, Alignment, Application, Command, Length, Subscription,
|
window, Alignment, Length, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic::iced_runtime::core::layout::Limits;
|
use cosmic::iced_runtime::core::layout::Limits;
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application;
|
||||||
use cosmic::theme::Svg;
|
use cosmic::theme::Svg;
|
||||||
use cosmic::widget::{button, divider, icon};
|
use cosmic::widget::{button, divider, icon};
|
||||||
use cosmic::{Element, Theme};
|
use cosmic::{Element, Theme};
|
||||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
|
||||||
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
@ -46,17 +45,16 @@ fn format_duration(duration: Duration) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<CosmicBatteryApplet>(false, ())
|
||||||
CosmicBatteryApplet::run(helper.window_settings())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static MAX_CHARGE: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
static MAX_CHARGE: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct CosmicBatteryApplet {
|
struct CosmicBatteryApplet {
|
||||||
|
core: cosmic::app::Core,
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
display_icon_name: String,
|
display_icon_name: String,
|
||||||
theme: Theme,
|
|
||||||
charging_limit: bool,
|
charging_limit: bool,
|
||||||
battery_percent: f64,
|
battery_percent: f64,
|
||||||
on_battery: bool,
|
on_battery: bool,
|
||||||
|
|
@ -67,7 +65,6 @@ struct CosmicBatteryApplet {
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
screen_sender: Option<UnboundedSender<ScreenBacklightRequest>>,
|
screen_sender: Option<UnboundedSender<ScreenBacklightRequest>>,
|
||||||
kbd_sender: Option<UnboundedSender<KeyboardBacklightRequest>>,
|
kbd_sender: Option<UnboundedSender<KeyboardBacklightRequest>>,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
power_profile: Power,
|
power_profile: Power,
|
||||||
power_profile_sender: Option<UnboundedSender<PowerProfileRequest>>,
|
power_profile_sender: Option<UnboundedSender<PowerProfileRequest>>,
|
||||||
timeline: Timeline,
|
timeline: Timeline,
|
||||||
|
|
@ -144,37 +141,36 @@ enum Message {
|
||||||
InitKbdBacklight(UnboundedSender<KeyboardBacklightRequest>, f64),
|
InitKbdBacklight(UnboundedSender<KeyboardBacklightRequest>, f64),
|
||||||
InitScreenBacklight(UnboundedSender<ScreenBacklightRequest>, f64),
|
InitScreenBacklight(UnboundedSender<ScreenBacklightRequest>, f64),
|
||||||
Errored(String),
|
Errored(String),
|
||||||
Ignore,
|
|
||||||
InitProfile(UnboundedSender<PowerProfileRequest>, Power),
|
InitProfile(UnboundedSender<PowerProfileRequest>, Power),
|
||||||
Profile(Power),
|
Profile(Power),
|
||||||
SelectProfile(Power),
|
SelectProfile(Power),
|
||||||
Frame(Instant),
|
Frame(Instant),
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for CosmicBatteryApplet {
|
impl cosmic::Application for CosmicBatteryApplet {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = config::APP_ID;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
(
|
||||||
CosmicBatteryApplet {
|
CosmicBatteryApplet {
|
||||||
|
core,
|
||||||
icon_name: "battery-symbolic".to_string(),
|
icon_name: "battery-symbolic".to_string(),
|
||||||
display_icon_name: "display-brightness-symbolic".to_string(),
|
display_icon_name: "display-brightness-symbolic".to_string(),
|
||||||
applet_helper,
|
|
||||||
theme,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
config::APP_ID.to_string()
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -217,7 +213,7 @@ impl Application for CosmicBatteryApplet {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -246,7 +242,6 @@ impl Application for CosmicBatteryApplet {
|
||||||
Message::UpdateKbdBrightness(b) => {
|
Message::UpdateKbdBrightness(b) => {
|
||||||
self.kbd_brightness = b;
|
self.kbd_brightness = b;
|
||||||
}
|
}
|
||||||
Message::Ignore => {}
|
|
||||||
Message::InitKbdBacklight(tx, brightness) => {
|
Message::InitKbdBacklight(tx, brightness) => {
|
||||||
let _ = tx.send(KeyboardBacklightRequest::Get);
|
let _ = tx.send(KeyboardBacklightRequest::Get);
|
||||||
self.kbd_sender = Some(tx);
|
self.kbd_sender = Some(tx);
|
||||||
|
|
@ -278,19 +273,19 @@ impl Application for CosmicBatteryApplet {
|
||||||
let _ = tx.send(PowerProfileRequest::Set(profile));
|
let _ = tx.send(PowerProfileRequest::Set(profile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
|
||||||
if id == window::Id(0) {
|
fn view(&self) -> Element<Message> {
|
||||||
self.applet_helper
|
self.core
|
||||||
|
.applet_helper
|
||||||
.icon_button(&self.icon_name)
|
.icon_button(&self.icon_name)
|
||||||
.on_press(Message::TogglePopup)
|
.on_press(Message::TogglePopup)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let name = text(fl!("battery")).size(14);
|
let name = text(fl!("battery")).size(14);
|
||||||
let description = text(if !self.on_battery {
|
let description = text(if !self.on_battery {
|
||||||
format!("{}%", self.battery_percent)
|
format!("{}%", self.battery_percent)
|
||||||
|
|
@ -303,7 +298,8 @@ impl Application for CosmicBatteryApplet {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.size(10);
|
.size(10);
|
||||||
self.applet_helper
|
self.core
|
||||||
|
.applet_helper
|
||||||
.popup_container(
|
.popup_container(
|
||||||
column![
|
column![
|
||||||
row![
|
row![
|
||||||
|
|
@ -438,11 +434,9 @@ impl Application for CosmicBatteryApplet {
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
device_subscription(0).map(
|
device_subscription(0).map(
|
||||||
|DeviceDbusEvent::Update {
|
|DeviceDbusEvent::Update {
|
||||||
on_battery,
|
on_battery,
|
||||||
|
|
@ -473,18 +467,7 @@ impl Application for CosmicBatteryApplet {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
self.theme.clone()
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Message {
|
|
||||||
Message::Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ once_cell = "1.16.0"
|
||||||
bluer = { version = "0.15", features = ["bluetoothd", "id"] }
|
bluer = { version = "0.15", features = ["bluetoothd", "id"] }
|
||||||
futures-util = "0.3.21"
|
futures-util = "0.3.21"
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.5"
|
pretty_env_logger = "0.5"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::bluetooth::{BluerDeviceStatus, BluerRequest, BluerState};
|
use crate::bluetooth::{BluerDeviceStatus, BluerRequest, BluerState};
|
||||||
|
use cosmic::app::{applet::applet_button_theme, Command};
|
||||||
use cosmic::iced_style;
|
use cosmic::iced_style;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
|
self,
|
||||||
wayland::popup::{destroy_popup, get_popup},
|
wayland::popup::{destroy_popup, get_popup},
|
||||||
widget::{column, container, row, scrollable, text, Column},
|
widget::{column, container, row, scrollable, text, Column},
|
||||||
Alignment, Application, Color, Command, Length, Subscription,
|
Alignment, Length, Subscription,
|
||||||
},
|
},
|
||||||
iced_runtime::core::{
|
iced_runtime::core::{
|
||||||
alignment::{Horizontal, Vertical},
|
alignment::{Horizontal, Vertical},
|
||||||
|
|
@ -16,7 +18,6 @@ use cosmic::{
|
||||||
widget::{button, divider, icon, toggler},
|
widget::{button, divider, icon, toggler},
|
||||||
Element, Theme,
|
Element, Theme,
|
||||||
};
|
};
|
||||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
@ -25,17 +26,15 @@ use crate::bluetooth::{bluetooth_subscription, BluerDevice, BluerEvent};
|
||||||
use crate::{config, fl};
|
use crate::{config, fl};
|
||||||
|
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<CosmicBluetoothApplet>(false, ())
|
||||||
CosmicBluetoothApplet::run(helper.window_settings())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CosmicBluetoothApplet {
|
struct CosmicBluetoothApplet {
|
||||||
|
core: cosmic::app::Core,
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
theme: Theme,
|
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
bluer_state: BluerState,
|
bluer_state: BluerState,
|
||||||
bluer_sender: Option<Sender<BluerRequest>>,
|
bluer_sender: Option<Sender<BluerRequest>>,
|
||||||
// UI state
|
// UI state
|
||||||
|
|
@ -63,31 +62,31 @@ enum Message {
|
||||||
Request(BluerRequest),
|
Request(BluerRequest),
|
||||||
Cancel,
|
Cancel,
|
||||||
Confirm,
|
Confirm,
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for CosmicBluetoothApplet {
|
impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = config::APP_ID;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
(
|
||||||
CosmicBluetoothApplet {
|
CosmicBluetoothApplet {
|
||||||
|
core,
|
||||||
icon_name: "bluetooth-symbolic".to_string(),
|
icon_name: "bluetooth-symbolic".to_string(),
|
||||||
theme,
|
|
||||||
applet_helper,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
config::APP_ID.to_string()
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -101,7 +100,7 @@ impl Application for CosmicBluetoothApplet {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -116,13 +115,13 @@ impl Application for CosmicBluetoothApplet {
|
||||||
.max_width(400.0);
|
.max_width(400.0);
|
||||||
let tx = self.bluer_sender.as_ref().cloned();
|
let tx = self.bluer_sender.as_ref().cloned();
|
||||||
return Command::batch(vec![
|
return Command::batch(vec![
|
||||||
Command::perform(
|
iced::Command::perform(
|
||||||
async {
|
async {
|
||||||
if let Some(tx) = tx {
|
if let Some(tx) = tx {
|
||||||
let _ = tx.send(BluerRequest::StateUpdate).await;
|
let _ = tx.send(BluerRequest::StateUpdate).await;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|_| Message::Ignore,
|
|_| cosmic::app::message::app(Message::Ignore),
|
||||||
),
|
),
|
||||||
get_popup(popup_settings),
|
get_popup(popup_settings),
|
||||||
]);
|
]);
|
||||||
|
|
@ -148,13 +147,13 @@ impl Application for CosmicBluetoothApplet {
|
||||||
if self.popup.is_some() && self.bluer_sender.is_some() =>
|
if self.popup.is_some() && self.bluer_sender.is_some() =>
|
||||||
{
|
{
|
||||||
let tx = self.bluer_sender.as_ref().cloned().unwrap();
|
let tx = self.bluer_sender.as_ref().cloned().unwrap();
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
async move {
|
async move {
|
||||||
// sleep for a bit before requesting state update again
|
// sleep for a bit before requesting state update again
|
||||||
tokio::time::sleep(Duration::from_millis(3000)).await;
|
tokio::time::sleep(Duration::from_millis(3000)).await;
|
||||||
let _ = tx.send(BluerRequest::StateUpdate).await;
|
let _ = tx.send(BluerRequest::StateUpdate).await;
|
||||||
},
|
},
|
||||||
|_| Message::Ignore,
|
|_| cosmic::app::message::app(Message::Ignore),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -243,42 +242,48 @@ impl Application for CosmicBluetoothApplet {
|
||||||
_ => {} // TODO
|
_ => {} // TODO
|
||||||
}
|
}
|
||||||
if let Some(tx) = self.bluer_sender.as_mut().cloned() {
|
if let Some(tx) = self.bluer_sender.as_mut().cloned() {
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
async move {
|
async move {
|
||||||
let _ = tx.send(r).await;
|
let _ = tx.send(r).await;
|
||||||
},
|
},
|
||||||
|_| Message::Ignore, // Error handling
|
|_| cosmic::app::message::app(Message::Ignore), // Error handling
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Cancel => {
|
Message::Cancel => {
|
||||||
if let Some((_, _, tx)) = self.request_confirmation.take() {
|
if let Some((_, _, tx)) = self.request_confirmation.take() {
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
async move {
|
async move {
|
||||||
let _ = tx.send(false).await;
|
let _ = tx.send(false).await;
|
||||||
},
|
},
|
||||||
|_| Message::Ignore,
|
|_| cosmic::app::message::app(Message::Ignore),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Confirm => {
|
Message::Confirm => {
|
||||||
if let Some((_, _, tx)) = self.request_confirmation.take() {
|
if let Some((_, _, tx)) = self.request_confirmation.take() {
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
async move {
|
async move {
|
||||||
let _ = tx.send(true).await;
|
let _ = tx.send(true).await;
|
||||||
},
|
},
|
||||||
|_| Message::Ignore,
|
|_| cosmic::app::message::app(Message::Ignore),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.update_icon();
|
self.update_icon();
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
|
||||||
|
fn view(&self) -> Element<Message> {
|
||||||
|
self.core
|
||||||
|
.applet_helper
|
||||||
|
.icon_button(&self.icon_name)
|
||||||
|
.on_press(Message::TogglePopup)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let button_style = || Button::Custom {
|
let button_style = || Button::Custom {
|
||||||
active: Box::new(|t| iced_style::button::Appearance {
|
active: Box::new(|t| iced_style::button::Appearance {
|
||||||
border_radius: 0.0.into(),
|
border_radius: 0.0.into(),
|
||||||
|
|
@ -289,12 +294,6 @@ impl Application for CosmicBluetoothApplet {
|
||||||
..t.hovered(&Button::Text)
|
..t.hovered(&Button::Text)
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
if id == window::Id(0) {
|
|
||||||
self.applet_helper
|
|
||||||
.icon_button(&self.icon_name)
|
|
||||||
.on_press(Message::TogglePopup)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
let mut known_bluetooth = column![];
|
let mut known_bluetooth = column![];
|
||||||
for dev in self.bluer_state.devices.iter().filter(|d| {
|
for dev in self.bluer_state.devices.iter().filter(|d| {
|
||||||
!self
|
!self
|
||||||
|
|
@ -510,37 +509,19 @@ impl Application for CosmicBluetoothApplet {
|
||||||
};
|
};
|
||||||
|
|
||||||
if item_counter > 10 {
|
if item_counter > 10 {
|
||||||
content = content.push(
|
content = content
|
||||||
scrollable(Column::with_children(list_column)).height(Length::Fixed(300.0)),
|
.push(scrollable(Column::with_children(list_column)).height(Length::Fixed(300.0)));
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
content = content.push(Column::with_children(list_column));
|
content = content.push(Column::with_children(list_column));
|
||||||
}
|
}
|
||||||
self.applet_helper.popup_container(content).into()
|
self.core.applet_helper.popup_container(content).into()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
bluetooth_subscription(0).map(Message::BluetoothEvent)
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
bluetooth_subscription(0).map(Message::BluetoothEvent),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
self.theme.clone()
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
|
||||||
Message::Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| {
|
|
||||||
application::Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zbus = "3.13"
|
zbus = "3.13"
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
# Application i18n
|
# Application i18n
|
||||||
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] }
|
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] }
|
||||||
|
|
|
||||||
|
|
@ -3,34 +3,8 @@ mod graphics;
|
||||||
mod localize;
|
mod localize;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use cosmic::{
|
|
||||||
iced::{wayland::InitialSurface, Application, Settings},
|
|
||||||
iced_runtime::core::layout::Limits,
|
|
||||||
};
|
|
||||||
use cosmic_applet::{cosmic_panel_config::PanelAnchor, CosmicAppletHelper};
|
|
||||||
|
|
||||||
use window::*;
|
use window::*;
|
||||||
|
|
||||||
pub fn main() -> cosmic::iced::Result {
|
pub fn main() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<Window>(true, ())
|
||||||
let mut settings: Settings<()> = helper.window_settings();
|
|
||||||
match helper.anchor {
|
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => {
|
|
||||||
match &mut settings.initial_surface {
|
|
||||||
InitialSurface::LayerSurface(_) => todo!(),
|
|
||||||
InitialSurface::XdgWindow(w) => {
|
|
||||||
w.autosize = true;
|
|
||||||
w.resizable = None;
|
|
||||||
w.size_limits = Limits::NONE
|
|
||||||
.min_height(1.0)
|
|
||||||
.max_height(200.0)
|
|
||||||
.min_width(1.0)
|
|
||||||
.max_width(1000.0);
|
|
||||||
}
|
|
||||||
InitialSurface::None => unimplemented!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
Window::run(settings)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,24 @@
|
||||||
use crate::dbus::{self, PowerDaemonProxy};
|
use crate::dbus::{self, PowerDaemonProxy};
|
||||||
use crate::fl;
|
use crate::fl;
|
||||||
use crate::graphics::{get_current_graphics, set_graphics, Graphics};
|
use crate::graphics::{get_current_graphics, set_graphics, Graphics};
|
||||||
|
use cosmic::app::{
|
||||||
|
applet::{applet_button_theme, cosmic_panel_config::PanelAnchor},
|
||||||
|
Command,
|
||||||
|
};
|
||||||
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
||||||
use cosmic::iced::Color;
|
|
||||||
use cosmic::iced_futures::Subscription;
|
|
||||||
use cosmic::iced_runtime::core::alignment::Horizontal;
|
use cosmic::iced_runtime::core::alignment::Horizontal;
|
||||||
use cosmic::iced_runtime::core::Alignment;
|
use cosmic::iced_runtime::core::Alignment;
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application;
|
||||||
use cosmic::theme::Button;
|
use cosmic::theme::Button;
|
||||||
use cosmic::widget::icon;
|
use cosmic::widget::icon;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::widget::{column, container, row, text},
|
iced::widget::{column, container, row, text},
|
||||||
iced::{self, Application, Command, Length},
|
iced::{self, Length},
|
||||||
iced_runtime::core::window,
|
iced_runtime::core::window,
|
||||||
theme::{Svg, Theme},
|
theme::{Svg, Theme},
|
||||||
widget::{button, divider},
|
widget::{button, divider},
|
||||||
Element,
|
Element,
|
||||||
};
|
};
|
||||||
use cosmic_applet::{applet_button_theme, cosmic_panel_config::PanelAnchor, CosmicAppletHelper};
|
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
const ID: &str = "com.system76.CosmicAppletGraphics";
|
const ID: &str = "com.system76.CosmicAppletGraphics";
|
||||||
|
|
@ -41,12 +42,11 @@ impl GraphicsMode {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
|
core: cosmic::app::Core,
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
graphics_mode: Option<GraphicsMode>,
|
graphics_mode: Option<GraphicsMode>,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
theme: Theme,
|
|
||||||
dbus: Option<(Connection, PowerDaemonProxy<'static>)>,
|
dbus: Option<(Connection, PowerDaemonProxy<'static>)>,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
@ -58,35 +58,37 @@ pub enum Message {
|
||||||
SelectGraphicsMode(Graphics),
|
SelectGraphicsMode(Graphics),
|
||||||
TogglePopup,
|
TogglePopup,
|
||||||
PopupClosed(window::Id),
|
PopupClosed(window::Id),
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Window {
|
impl cosmic::Application for Window {
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
const APP_ID: &'static str = ID;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
let window = Window {
|
let window = Window {
|
||||||
theme,
|
core,
|
||||||
applet_helper,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
(window, Command::perform(dbus::init(), Message::DBusInit))
|
(
|
||||||
|
window,
|
||||||
|
iced::Command::perform(dbus::init(), |x| {
|
||||||
|
cosmic::app::message::app(Message::DBusInit(x))
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
String::from("Cosmic Graphics Applet")
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> iced::Command<Self::Message> {
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, message: Message) -> Command<Self::Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
|
||||||
Message::SelectGraphicsMode(new) => {
|
Message::SelectGraphicsMode(new) => {
|
||||||
if let Some((_, proxy)) = self.dbus.as_ref() {
|
if let Some((_, proxy)) = self.dbus.as_ref() {
|
||||||
let prev = self
|
let prev = self
|
||||||
|
|
@ -94,9 +96,14 @@ impl Application for Window {
|
||||||
.map(|m| m.inner())
|
.map(|m| m.inner())
|
||||||
.unwrap_or_else(|| Graphics::Integrated);
|
.unwrap_or_else(|| Graphics::Integrated);
|
||||||
self.graphics_mode = Some(GraphicsMode::SelectedGraphicsMode { prev, new });
|
self.graphics_mode = Some(GraphicsMode::SelectedGraphicsMode { prev, new });
|
||||||
return Command::perform(set_graphics(proxy.clone(), new), move |success| {
|
return iced::Command::perform(
|
||||||
Message::AppliedGraphics(success.ok().map(|_| new))
|
set_graphics(proxy.clone(), new),
|
||||||
});
|
move |success| {
|
||||||
|
cosmic::app::message::app(Message::AppliedGraphics(
|
||||||
|
success.ok().map(|_| new),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::TogglePopup => {
|
Message::TogglePopup => {
|
||||||
|
|
@ -108,12 +115,12 @@ impl Application for Window {
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
let mut commands = Vec::new();
|
let mut commands = Vec::new();
|
||||||
if let Some((_, proxy)) = self.dbus.as_ref() {
|
if let Some((_, proxy)) = self.dbus.as_ref() {
|
||||||
commands.push(Command::perform(
|
commands.push(iced::Command::perform(
|
||||||
get_current_graphics(proxy.clone()),
|
get_current_graphics(proxy.clone()),
|
||||||
|cur_graphics| Message::CurrentGraphics(cur_graphics.ok()),
|
|cur_graphics| Message::CurrentGraphics(cur_graphics.ok()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let popup_settings = self.applet_helper.get_popup_settings(
|
let popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -121,12 +128,12 @@ impl Application for Window {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
commands.push(get_popup(popup_settings));
|
commands.push(get_popup(popup_settings));
|
||||||
return Command::batch(commands);
|
return iced::Command::batch(commands).map(cosmic::app::message::app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::DBusInit(dbus) => {
|
Message::DBusInit(dbus) => {
|
||||||
self.dbus = dbus;
|
self.dbus = dbus;
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
get_current_graphics(self.dbus.as_ref().unwrap().1.clone()),
|
get_current_graphics(self.dbus.as_ref().unwrap().1.clone()),
|
||||||
|cur_graphics| {
|
|cur_graphics| {
|
||||||
Message::CurrentGraphics(match cur_graphics {
|
Message::CurrentGraphics(match cur_graphics {
|
||||||
|
|
@ -137,7 +144,8 @@ impl Application for Window {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
.map(cosmic::app::message::app);
|
||||||
}
|
}
|
||||||
Message::CurrentGraphics(g) => {
|
Message::CurrentGraphics(g) => {
|
||||||
if let Some(g) = g {
|
if let Some(g) = g {
|
||||||
|
|
@ -166,16 +174,17 @@ impl Application for Window {
|
||||||
// Reset to prev after failing
|
// Reset to prev after failing
|
||||||
// https://github.com/pop-os/system76-power/issues/387
|
// https://github.com/pop-os/system76-power/issues/387
|
||||||
if let Some((_, proxy)) = self.dbus.as_ref() {
|
if let Some((_, proxy)) = self.dbus.as_ref() {
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
set_graphics(proxy.clone(), prev),
|
set_graphics(proxy.clone(), prev),
|
||||||
move |success| {
|
move |success| {
|
||||||
Message::AppliedGraphics(success.ok().map(|_| new))
|
Message::AppliedGraphics(success.ok().map(|_| new))
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
.map(cosmic::app::message::app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Command::perform(
|
return iced::Command::perform(
|
||||||
get_current_graphics(self.dbus.as_ref().unwrap().1.clone()),
|
get_current_graphics(self.dbus.as_ref().unwrap().1.clone()),
|
||||||
|cur_graphics| {
|
|cur_graphics| {
|
||||||
Message::CurrentGraphics(match cur_graphics {
|
Message::CurrentGraphics(match cur_graphics {
|
||||||
|
|
@ -187,6 +196,7 @@ impl Application for Window {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.map(cosmic::app::message::app)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -195,10 +205,10 @@ impl Application for Window {
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
if id == window::Id(0) {
|
match self.core.applet_helper.anchor {
|
||||||
match self.applet_helper.anchor {
|
|
||||||
PanelAnchor::Left | PanelAnchor::Right => self
|
PanelAnchor::Left | PanelAnchor::Right => self
|
||||||
|
.core
|
||||||
.applet_helper
|
.applet_helper
|
||||||
.icon_button(ID)
|
.icon_button(ID)
|
||||||
.on_press(Message::TogglePopup)
|
.on_press(Message::TogglePopup)
|
||||||
|
|
@ -206,7 +216,7 @@ impl Application for Window {
|
||||||
.into(),
|
.into(),
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => button(Button::Text)
|
PanelAnchor::Top | PanelAnchor::Bottom => button(Button::Text)
|
||||||
.custom(vec![row![
|
.custom(vec![row![
|
||||||
icon(ID, self.applet_helper.suggested_size().0,).style(Svg::Symbolic),
|
icon(ID, self.core.applet_helper.suggested_size().0,).style(Svg::Symbolic),
|
||||||
text(match self.graphics_mode.map(|g| g.inner()) {
|
text(match self.graphics_mode.map(|g| g.inner()) {
|
||||||
Some(Graphics::Integrated) => fl!("integrated"),
|
Some(Graphics::Integrated) => fl!("integrated"),
|
||||||
Some(Graphics::Nvidia) => fl!("nvidia"),
|
Some(Graphics::Nvidia) => fl!("nvidia"),
|
||||||
|
|
@ -217,7 +227,7 @@ impl Application for Window {
|
||||||
.size(14)
|
.size(14)
|
||||||
]
|
]
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
.padding([0, self.applet_helper.suggested_size().0 / 2])
|
.padding([0, self.core.applet_helper.suggested_size().0 / 2])
|
||||||
.align_items(Alignment::Center)
|
.align_items(Alignment::Center)
|
||||||
.into()])
|
.into()])
|
||||||
.style(Button::Text)
|
.style(Button::Text)
|
||||||
|
|
@ -227,7 +237,9 @@ impl Application for Window {
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let content_list = vec![
|
let content_list = vec![
|
||||||
button(applet_button_theme())
|
button(applet_button_theme())
|
||||||
.custom(vec![row![
|
.custom(vec![row![
|
||||||
|
|
@ -372,7 +384,8 @@ impl Application for Window {
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
|
|
||||||
self.applet_helper
|
self.core
|
||||||
|
.applet_helper
|
||||||
.popup_container(
|
.popup_container(
|
||||||
column(vec![
|
column(vec![
|
||||||
text(fl!("graphics-mode"))
|
text(fl!("graphics-mode"))
|
||||||
|
|
@ -391,31 +404,8 @@ impl Application for Window {
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close_requested(&self, id: window::Id) -> Self::Message {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
if id != window::Id(0) {
|
Some(cosmic::app::applet::style())
|
||||||
Message::PopupClosed(id)
|
|
||||||
} else {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().background.on.into(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_exit(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
|
||||||
self.theme.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bi
|
||||||
futures-util = "0.3.21"
|
futures-util = "0.3.21"
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-time.workspace = true
|
cosmic-time.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
zbus = { version = "3.13", default-features = false }
|
zbus = { version = "3.13", default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
use cosmic::app::Command;
|
||||||
use cosmic::iced_style;
|
use cosmic::iced_style;
|
||||||
use cosmic::iced_widget::Row;
|
use cosmic::iced_widget::Row;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
wayland::popup::{destroy_popup, get_popup},
|
wayland::popup::{destroy_popup, get_popup},
|
||||||
widget::{column, container, row, scrollable, text, text_input, Column},
|
widget::{column, container, row, scrollable, text, text_input, Column},
|
||||||
Alignment, Application, Color, Command, Length, Subscription,
|
Alignment, Length, Subscription,
|
||||||
},
|
},
|
||||||
iced_runtime::core::{
|
iced_runtime::core::{
|
||||||
alignment::{Horizontal, Vertical},
|
alignment::{Horizontal, Vertical},
|
||||||
|
|
@ -16,7 +17,6 @@ use cosmic::{
|
||||||
widget::{button, divider, icon},
|
widget::{button, divider, icon},
|
||||||
Element, Theme,
|
Element, Theme,
|
||||||
};
|
};
|
||||||
use cosmic_applet::CosmicAppletHelper;
|
|
||||||
use cosmic_dbus_networkmanager::interface::enums::{ActiveConnectionState, DeviceState};
|
use cosmic_dbus_networkmanager::interface::enums::{ActiveConnectionState, DeviceState};
|
||||||
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
||||||
|
|
||||||
|
|
@ -36,9 +36,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<CosmicNetworkApplet>(false, ())
|
||||||
let settings = helper.window_settings();
|
|
||||||
CosmicNetworkApplet::run(settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -83,11 +81,10 @@ static AIRPLANE_MODE: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CosmicNetworkApplet {
|
struct CosmicNetworkApplet {
|
||||||
|
core: cosmic::app::Core,
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
theme: Theme,
|
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
nm_state: NetworkManagerState,
|
nm_state: NetworkManagerState,
|
||||||
// UI state
|
// UI state
|
||||||
nm_sender: Option<UnboundedSender<NetworkManagerRequest>>,
|
nm_sender: Option<UnboundedSender<NetworkManagerRequest>>,
|
||||||
|
|
@ -175,46 +172,42 @@ pub(crate) enum Message {
|
||||||
ToggleAirplaneMode(bool),
|
ToggleAirplaneMode(bool),
|
||||||
ToggleWiFi(bool),
|
ToggleWiFi(bool),
|
||||||
ToggleVisibleNetworks,
|
ToggleVisibleNetworks,
|
||||||
Ignore,
|
|
||||||
NetworkManagerEvent(NetworkManagerEvent),
|
NetworkManagerEvent(NetworkManagerEvent),
|
||||||
SelectWirelessAccessPoint(AccessPoint),
|
SelectWirelessAccessPoint(AccessPoint),
|
||||||
CancelNewConnection,
|
CancelNewConnection,
|
||||||
Password(String),
|
Password(String),
|
||||||
SubmitPassword,
|
SubmitPassword,
|
||||||
Frame(Instant),
|
Frame(Instant),
|
||||||
Theme(Theme),
|
|
||||||
// Errored(String),
|
// Errored(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for CosmicNetworkApplet {
|
impl cosmic::Application for CosmicNetworkApplet {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = config::APP_ID;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
(
|
||||||
CosmicNetworkApplet {
|
CosmicNetworkApplet {
|
||||||
|
core,
|
||||||
icon_name: "network-offline-symbolic".to_string(),
|
icon_name: "network-offline-symbolic".to_string(),
|
||||||
theme,
|
|
||||||
applet_helper,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
config::APP_ID.to_string()
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
|
||||||
Message::Frame(now) => self.timeline.now(now),
|
Message::Frame(now) => self.timeline.now(now),
|
||||||
Message::TogglePopup => {
|
Message::TogglePopup => {
|
||||||
if let Some(p) = self.popup.take() {
|
if let Some(p) = self.popup.take() {
|
||||||
|
|
@ -226,7 +219,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -243,7 +236,6 @@ impl Application for CosmicNetworkApplet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Message::Errored(_) => todo!(),
|
// Message::Errored(_) => todo!(),
|
||||||
Message::Ignore => {}
|
|
||||||
Message::ToggleAirplaneMode(enabled) => {
|
Message::ToggleAirplaneMode(enabled) => {
|
||||||
self.toggle_wifi_ctr += 1;
|
self.toggle_wifi_ctr += 1;
|
||||||
if let Some(tx) = self.nm_sender.as_mut() {
|
if let Some(tx) = self.nm_sender.as_mut() {
|
||||||
|
|
@ -397,7 +389,16 @@ impl Application for CosmicNetworkApplet {
|
||||||
}
|
}
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
|
||||||
|
fn view(&self) -> Element<Message> {
|
||||||
|
self.core
|
||||||
|
.applet_helper
|
||||||
|
.icon_button(&self.icon_name)
|
||||||
|
.on_press(Message::TogglePopup)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let button_style = || Button::Custom {
|
let button_style = || Button::Custom {
|
||||||
active: Box::new(|t| iced_style::button::Appearance {
|
active: Box::new(|t| iced_style::button::Appearance {
|
||||||
border_radius: 0.0.into(),
|
border_radius: 0.0.into(),
|
||||||
|
|
@ -408,12 +409,6 @@ impl Application for CosmicNetworkApplet {
|
||||||
..t.hovered(&Button::Text)
|
..t.hovered(&Button::Text)
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
if id == window::Id(0) {
|
|
||||||
self.applet_helper
|
|
||||||
.icon_button(&self.icon_name)
|
|
||||||
.on_press(Message::TogglePopup)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
let mut vpn_ethernet_col = column![];
|
let mut vpn_ethernet_col = column![];
|
||||||
let mut known_wifi = column![];
|
let mut known_wifi = column![];
|
||||||
for conn in &self.nm_state.active_conns {
|
for conn in &self.nm_state.active_conns {
|
||||||
|
|
@ -478,8 +473,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
match state {
|
match state {
|
||||||
ActiveConnectionState::Activating
|
ActiveConnectionState::Activating | ActiveConnectionState::Deactivating => {
|
||||||
| ActiveConnectionState::Deactivating => {
|
|
||||||
btn_content.push(
|
btn_content.push(
|
||||||
icon("process-working-symbolic", 24)
|
icon("process-working-symbolic", 24)
|
||||||
.style(Svg::Symbolic)
|
.style(Svg::Symbolic)
|
||||||
|
|
@ -604,9 +598,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
| DeviceState::NeedAuth => {
|
| DeviceState::NeedAuth => {
|
||||||
btn.on_press(Message::ActivateKnownWifi(known.ssid.clone()))
|
btn.on_press(Message::ActivateKnownWifi(known.ssid.clone()))
|
||||||
}
|
}
|
||||||
DeviceState::Activated => {
|
DeviceState::Activated => btn.on_press(Message::Disconnect(known.ssid.clone())),
|
||||||
btn.on_press(Message::Disconnect(known.ssid.clone()))
|
|
||||||
}
|
|
||||||
_ => btn,
|
_ => btn,
|
||||||
};
|
};
|
||||||
known_wifi = known_wifi.push(row![btn].align_items(Alignment::Center));
|
known_wifi = known_wifi.push(row![btn].align_items(Alignment::Center));
|
||||||
|
|
@ -648,8 +640,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
password,
|
password,
|
||||||
} => {
|
} => {
|
||||||
let id = row![
|
let id = row![
|
||||||
icon("network-wireless-acquiring-symbolic", 24)
|
icon("network-wireless-acquiring-symbolic", 24).style(Svg::Symbolic),
|
||||||
.style(Svg::Symbolic),
|
|
||||||
text(&access_point.ssid).size(14),
|
text(&access_point.ssid).size(14),
|
||||||
]
|
]
|
||||||
.align_items(Alignment::Center)
|
.align_items(Alignment::Center)
|
||||||
|
|
@ -686,8 +677,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
}
|
}
|
||||||
NewConnectionState::Waiting(access_point) => {
|
NewConnectionState::Waiting(access_point) => {
|
||||||
let id = row![
|
let id = row![
|
||||||
icon("network-wireless-acquiring-symbolic", 24)
|
icon("network-wireless-acquiring-symbolic", 24).style(Svg::Symbolic),
|
||||||
.style(Svg::Symbolic),
|
|
||||||
text(&access_point.ssid).size(14),
|
text(&access_point.ssid).size(14),
|
||||||
]
|
]
|
||||||
.align_items(Alignment::Center)
|
.align_items(Alignment::Center)
|
||||||
|
|
@ -716,9 +706,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
text(fl!("check-wifi-connection")),
|
text(fl!("check-wifi-connection")),
|
||||||
row![
|
row![
|
||||||
button(Button::Secondary)
|
button(Button::Secondary)
|
||||||
.custom(vec![container(text("Cancel"))
|
.custom(vec![container(text("Cancel")).padding([0, 24]).into()])
|
||||||
.padding([0, 24])
|
|
||||||
.into()])
|
|
||||||
.on_press(Message::CancelNewConnection),
|
.on_press(Message::CancelNewConnection),
|
||||||
button(Button::Secondary)
|
button(Button::Secondary)
|
||||||
.custom(vec![container(text("Connect"))
|
.custom(vec![container(text("Connect"))
|
||||||
|
|
@ -737,8 +725,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.nm_state.wifi_enabled {
|
} else if self.nm_state.wifi_enabled {
|
||||||
let mut list_col =
|
let mut list_col = Vec::with_capacity(self.nm_state.wireless_access_points.len());
|
||||||
Vec::with_capacity(self.nm_state.wireless_access_points.len());
|
|
||||||
for ap in &self.nm_state.wireless_access_points {
|
for ap in &self.nm_state.wireless_access_points {
|
||||||
if self
|
if self
|
||||||
.nm_state
|
.nm_state
|
||||||
|
|
@ -764,13 +751,11 @@ impl Application for CosmicNetworkApplet {
|
||||||
.padding([8, 24]);
|
.padding([8, 24]);
|
||||||
list_col.push(button.into());
|
list_col.push(button.into());
|
||||||
}
|
}
|
||||||
content = content.push(
|
content = content
|
||||||
scrollable(Column::with_children(list_col)).height(Length::Fixed(300.0)),
|
.push(scrollable(Column::with_children(list_col)).height(Length::Fixed(300.0)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.applet_helper.popup_container(content).into()
|
self.core.applet_helper.popup_container(content).into()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
|
|
@ -782,7 +767,6 @@ impl Application for CosmicNetworkApplet {
|
||||||
|
|
||||||
if let Some(conn) = self.conn.as_ref() {
|
if let Some(conn) = self.conn.as_ref() {
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
timeline,
|
timeline,
|
||||||
network_sub,
|
network_sub,
|
||||||
active_conns_subscription(self.toggle_wifi_ctr, conn.clone())
|
active_conns_subscription(self.toggle_wifi_ctr, conn.clone())
|
||||||
|
|
@ -797,20 +781,7 @@ impl Application for CosmicNetworkApplet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
self.theme.clone()
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
|
||||||
Message::Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| {
|
|
||||||
application::Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ license = "GPL-3.0-or-later"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-time.workspace = true
|
cosmic-time.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
nix = "0.26"
|
nix = "0.26"
|
||||||
tokio = { version = "1.24.1", features = ["sync", "rt", "tracing", "macros", "net", "io-util", "io-std"] }
|
tokio = { version = "1.24.1", features = ["sync", "rt", "tracing", "macros", "net", "io-util", "io-std"] }
|
||||||
cosmic-notifications-util = { git = "https://github.com/pop-os/cosmic-notifications" }
|
cosmic-notifications-util = { git = "https://github.com/pop-os/cosmic-notifications" }
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
mod localize;
|
mod localize;
|
||||||
mod subscriptions;
|
mod subscriptions;
|
||||||
|
|
||||||
|
use cosmic::app::{applet::applet_button_theme, Command};
|
||||||
use cosmic::cosmic_config::{config_subscription, Config, CosmicConfigEntry};
|
use cosmic::cosmic_config::{config_subscription, Config, CosmicConfigEntry};
|
||||||
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
||||||
use cosmic::iced::Limits;
|
use cosmic::iced::Limits;
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
widget::{button, column, row, text, Row},
|
widget::{button, column, row, text, Row},
|
||||||
window, Alignment, Application, Color, Command, Length, Subscription,
|
window, Alignment, Length, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic::iced_core::alignment::Horizontal;
|
use cosmic::iced_core::alignment::Horizontal;
|
||||||
use cosmic::iced_core::image;
|
use cosmic::iced_core::image;
|
||||||
use cosmic::iced_widget::image::Handle;
|
use cosmic::iced_widget::image::Handle;
|
||||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
|
||||||
|
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application;
|
||||||
|
|
||||||
use cosmic::iced_widget::{horizontal_rule, scrollable, Column};
|
use cosmic::iced_widget::{horizontal_rule, scrollable, Column};
|
||||||
use cosmic::theme::Svg;
|
use cosmic::theme::Svg;
|
||||||
|
|
@ -25,7 +25,7 @@ use cosmic_notifications_util::{Image, Notification};
|
||||||
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
@ -38,15 +38,13 @@ pub async fn main() -> cosmic::iced::Result {
|
||||||
|
|
||||||
info!("Notifications applet");
|
info!("Notifications applet");
|
||||||
|
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<Notifications>(false, ())
|
||||||
Notifications::run(helper.window_settings())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Notifications {
|
struct Notifications {
|
||||||
applet_helper: CosmicAppletHelper,
|
core: cosmic::app::Core,
|
||||||
theme: Theme,
|
|
||||||
config: NotificationsConfig,
|
config: NotificationsConfig,
|
||||||
config_helper: Option<Config>,
|
config_helper: Option<Config>,
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
|
|
@ -88,9 +86,7 @@ enum Message {
|
||||||
TogglePopup,
|
TogglePopup,
|
||||||
DoNotDisturb(chain::Toggler, bool),
|
DoNotDisturb(chain::Toggler, bool),
|
||||||
Settings,
|
Settings,
|
||||||
Ignore,
|
|
||||||
Frame(Instant),
|
Frame(Instant),
|
||||||
Theme(Theme),
|
|
||||||
NotificationEvent(Notification),
|
NotificationEvent(Notification),
|
||||||
Config(NotificationsConfig),
|
Config(NotificationsConfig),
|
||||||
DbusEvent(subscriptions::dbus::Output),
|
DbusEvent(subscriptions::dbus::Output),
|
||||||
|
|
@ -99,15 +95,13 @@ enum Message {
|
||||||
CardsToggled(String, bool),
|
CardsToggled(String, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Notifications {
|
impl cosmic::Application for Notifications {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicAppletNotifications";
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Notifications, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
let helper = Config::new(
|
let helper = Config::new(
|
||||||
cosmic_notifications_config::ID,
|
cosmic_notifications_config::ID,
|
||||||
NotificationsConfig::version(),
|
NotificationsConfig::version(),
|
||||||
|
|
@ -126,8 +120,7 @@ impl Application for Notifications {
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut _self = Notifications {
|
let mut _self = Notifications {
|
||||||
applet_helper,
|
core,
|
||||||
theme,
|
|
||||||
config_helper: helper,
|
config_helper: helper,
|
||||||
config,
|
config,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -136,28 +129,20 @@ impl Application for Notifications {
|
||||||
(_self, Command::none())
|
(_self, Command::none())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
String::from("Notifications")
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
self.theme.clone()
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
Message::Ignore
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
config_subscription::<u64, NotificationsConfig>(
|
config_subscription::<u64, NotificationsConfig>(
|
||||||
0,
|
0,
|
||||||
cosmic_notifications_config::ID.into(),
|
cosmic_notifications_config::ID.into(),
|
||||||
|
|
@ -182,9 +167,6 @@ impl Application for Notifications {
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
|
||||||
Message::Frame(now) => {
|
Message::Frame(now) => {
|
||||||
self.timeline.now(now);
|
self.timeline.now(now);
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +178,7 @@ impl Application for Notifications {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -247,7 +229,6 @@ impl Application for Notifications {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Ignore => {}
|
|
||||||
Message::Config(config) => {
|
Message::Config(config) => {
|
||||||
self.config = config;
|
self.config = config;
|
||||||
}
|
}
|
||||||
|
|
@ -320,13 +301,15 @@ impl Application for Notifications {
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
if id == window::Id(0) {
|
self.core
|
||||||
self.applet_helper
|
.applet_helper
|
||||||
.icon_button(&self.icon_name)
|
.icon_button(&self.icon_name)
|
||||||
.on_press(Message::TogglePopup)
|
.on_press(Message::TogglePopup)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let do_not_disturb = row![anim!(
|
let do_not_disturb = row![anim!(
|
||||||
DO_NOT_DISTURB,
|
DO_NOT_DISTURB,
|
||||||
&self.timeline,
|
&self.timeline,
|
||||||
|
|
@ -337,8 +320,8 @@ impl Application for Notifications {
|
||||||
.width(Length::Fill)]
|
.width(Length::Fill)]
|
||||||
.padding([0, 24]);
|
.padding([0, 24]);
|
||||||
|
|
||||||
let settings = row_button(vec![text(fl!("notification-settings")).into()])
|
let settings =
|
||||||
.on_press(Message::Settings);
|
row_button(vec![text(fl!("notification-settings")).into()]).on_press(Message::Settings);
|
||||||
|
|
||||||
let notifications = if self.cards.is_empty() {
|
let notifications = if self.cards.is_empty() {
|
||||||
row![container(
|
row![container(
|
||||||
|
|
@ -453,9 +436,9 @@ impl Application for Notifications {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
data,
|
data,
|
||||||
}) => Some(cosmic::widget::IconSource::Handle(
|
}) => Some(cosmic::widget::IconSource::Handle(icon::Handle::Image(
|
||||||
icon::Handle::Image(Handle::from_pixels(width, height, data)),
|
Handle::from_pixels(width, height, data),
|
||||||
)),
|
))),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
} else if let Some(path) = url::Url::parse(&n.app_icon)
|
} else if let Some(path) = url::Url::parse(&n.app_icon)
|
||||||
|
|
@ -501,8 +484,7 @@ impl Application for Notifications {
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
.padding([16, 0]);
|
.padding([16, 0]);
|
||||||
|
|
||||||
self.applet_helper.popup_container(content).into()
|
self.core.applet_helper.popup_container(content).into()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ libpulse-binding = "2.26.0"
|
||||||
libpulse-glib-binding = "2.25.0"
|
libpulse-glib-binding = "2.25.0"
|
||||||
tokio = { version = "1.20.1", features=["full"] }
|
tokio = { version = "1.20.1", features=["full"] }
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
# cosmic-applet = { path = "../applet" }
|
||||||
nix = "0.26.2"
|
nix = "0.26.2"
|
||||||
zbus = "3.13"
|
zbus = "3.13"
|
||||||
logind-zbus = "3.1"
|
logind-zbus = "3.1"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ use std::collections::HashMap;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use cosmic::app::applet::applet_button_theme;
|
||||||
|
use cosmic::iced;
|
||||||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
use cosmic::iced::alignment::{Horizontal, Vertical};
|
||||||
use cosmic::iced::event::wayland::{self, LayerEvent};
|
use cosmic::iced::event::wayland::{self, LayerEvent};
|
||||||
use cosmic::iced::event::PlatformSpecific;
|
use cosmic::iced::event::PlatformSpecific;
|
||||||
|
|
@ -15,16 +17,15 @@ use cosmic::iced_sctk::commands::layer_surface::{
|
||||||
use cosmic::iced_widget::mouse_area;
|
use cosmic::iced_widget::mouse_area;
|
||||||
use cosmic::widget::{button, divider, icon};
|
use cosmic::widget::{button, divider, icon};
|
||||||
use cosmic::Renderer;
|
use cosmic::Renderer;
|
||||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
|
||||||
|
|
||||||
use cosmic::iced::Color;
|
use cosmic::iced::Color;
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
widget::{self, column, container, row, space::Space, text, Row},
|
widget::{self, column, container, row, space::Space, text, Row},
|
||||||
window, Alignment, Application, Command, Length, Subscription,
|
window, Alignment, Length, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application;
|
||||||
use cosmic::theme::{self, Svg};
|
use cosmic::theme::{self, Svg};
|
||||||
use cosmic::{Element, Theme};
|
use cosmic::{app::Command, Element, Theme};
|
||||||
|
|
||||||
use logind_zbus::manager::ManagerProxy;
|
use logind_zbus::manager::ManagerProxy;
|
||||||
use logind_zbus::session::{SessionProxy, SessionType};
|
use logind_zbus::session::{SessionProxy, SessionType};
|
||||||
|
|
@ -41,15 +42,13 @@ use crate::cosmic_session::CosmicSessionProxy;
|
||||||
use crate::session_manager::SessionManagerProxy;
|
use crate::session_manager::SessionManagerProxy;
|
||||||
|
|
||||||
pub fn main() -> cosmic::iced::Result {
|
pub fn main() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<Power>(false, ())
|
||||||
Power::run(helper.window_settings())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Power {
|
struct Power {
|
||||||
applet_helper: CosmicAppletHelper,
|
core: cosmic::app::Core,
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
theme: Theme,
|
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
action_to_confirm: Option<(window::Id, PowerAction)>,
|
action_to_confirm: Option<(window::Id, PowerAction)>,
|
||||||
|
|
@ -72,24 +71,19 @@ enum Message {
|
||||||
Settings,
|
Settings,
|
||||||
Confirm,
|
Confirm,
|
||||||
Cancel,
|
Cancel,
|
||||||
Closed(window::Id),
|
|
||||||
Zbus(Result<(), zbus::Error>),
|
Zbus(Result<(), zbus::Error>),
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Power {
|
impl cosmic::Application for Power {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicAppletPower";
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Power, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Power, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
(
|
||||||
Power {
|
Power {
|
||||||
applet_helper,
|
core,
|
||||||
theme,
|
|
||||||
icon_name: "system-shutdown-symbolic".to_string(),
|
icon_name: "system-shutdown-symbolic".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
@ -97,28 +91,19 @@ impl Application for Power {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
fl!("power")
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
self.theme.clone()
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_requested(&self, id: window::Id) -> Self::Message {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
Message::Closed(id)
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
events_with(|e, _status| match e {
|
events_with(|e, _status| match e {
|
||||||
cosmic::iced::Event::PlatformSpecific(PlatformSpecific::Wayland(
|
cosmic::iced::Event::PlatformSpecific(PlatformSpecific::Wayland(
|
||||||
wayland::Event::Layer(LayerEvent::Unfocused, ..),
|
wayland::Event::Layer(LayerEvent::Unfocused, ..),
|
||||||
|
|
@ -127,16 +112,11 @@ impl Application for Power {
|
||||||
// wayland::Event::Seat(wayland::SeatEvent::Leave, _),
|
// wayland::Event::Seat(wayland::SeatEvent::Leave, _),
|
||||||
// )) => Some(Message::Cancel),
|
// )) => Some(Message::Cancel),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
})
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
Command::none()
|
|
||||||
}
|
|
||||||
Message::TogglePopup => {
|
Message::TogglePopup => {
|
||||||
if let Some(p) = self.popup.take() {
|
if let Some(p) = self.popup.take() {
|
||||||
destroy_popup(p)
|
destroy_popup(p)
|
||||||
|
|
@ -145,7 +125,7 @@ impl Application for Power {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -169,8 +149,8 @@ impl Application for Power {
|
||||||
let id = window::Id(self.id_ctr);
|
let id = window::Id(self.id_ctr);
|
||||||
self.action_to_confirm = Some((id, action));
|
self.action_to_confirm = Some((id, action));
|
||||||
return Command::batch(vec![
|
return Command::batch(vec![
|
||||||
Command::perform(sleep(Duration::from_secs(60)), move |_| {
|
iced::Command::perform(sleep(Duration::from_secs(60)), move |_| {
|
||||||
Message::Timeout(id)
|
cosmic::app::message::app(Message::Timeout(id))
|
||||||
}),
|
}),
|
||||||
get_layer_surface(SctkLayerSurfaceSettings {
|
get_layer_surface(SctkLayerSurfaceSettings {
|
||||||
id,
|
id,
|
||||||
|
|
@ -191,14 +171,15 @@ impl Application for Power {
|
||||||
}
|
}
|
||||||
Message::Confirm => {
|
Message::Confirm => {
|
||||||
if let Some((id, a)) = self.action_to_confirm.take() {
|
if let Some((id, a)) = self.action_to_confirm.take() {
|
||||||
|
let msg = |m| cosmic::app::message::app(Message::Zbus(m));
|
||||||
Command::batch(vec![
|
Command::batch(vec![
|
||||||
destroy_layer_surface(id),
|
destroy_layer_surface(id),
|
||||||
match a {
|
match a {
|
||||||
PowerAction::Lock => Command::perform(lock(), Message::Zbus),
|
PowerAction::Lock => iced::Command::perform(lock(), msg),
|
||||||
PowerAction::LogOut => Command::perform(log_out(), Message::Zbus),
|
PowerAction::LogOut => iced::Command::perform(log_out(), msg),
|
||||||
PowerAction::Suspend => Command::perform(suspend(), Message::Zbus),
|
PowerAction::Suspend => iced::Command::perform(suspend(), msg),
|
||||||
PowerAction::Restart => Command::perform(restart(), Message::Zbus),
|
PowerAction::Restart => iced::Command::perform(restart(), msg),
|
||||||
PowerAction::Shutdown => Command::perform(shutdown(), Message::Zbus),
|
PowerAction::Shutdown => iced::Command::perform(shutdown(), msg),
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -211,32 +192,19 @@ impl Application for Power {
|
||||||
}
|
}
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
Message::Closed(id) => {
|
|
||||||
if let Some((surface_id, _)) = self.action_to_confirm {
|
|
||||||
if id == surface_id {
|
|
||||||
self.action_to_confirm = None;
|
|
||||||
return destroy_layer_surface(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if id == window::Id(0) {
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
Command::none()
|
|
||||||
}
|
|
||||||
Message::Timeout(id) => {
|
Message::Timeout(id) => {
|
||||||
if let Some((surface_id, a)) = self.action_to_confirm {
|
if let Some((surface_id, a)) = self.action_to_confirm {
|
||||||
if id == surface_id {
|
if id == surface_id {
|
||||||
self.action_to_confirm = None;
|
self.action_to_confirm = None;
|
||||||
|
let msg = |m: zbus::Result<()>| cosmic::app::message::app(Message::Zbus(m));
|
||||||
return Command::batch(vec![
|
return Command::batch(vec![
|
||||||
destroy_layer_surface(id),
|
destroy_layer_surface(id),
|
||||||
match a {
|
match a {
|
||||||
PowerAction::Lock => Command::perform(lock(), Message::Zbus),
|
PowerAction::Lock => iced::Command::perform(lock(), msg),
|
||||||
PowerAction::LogOut => Command::perform(log_out(), Message::Zbus),
|
PowerAction::LogOut => iced::Command::perform(log_out(), msg),
|
||||||
PowerAction::Suspend => Command::perform(suspend(), Message::Zbus),
|
PowerAction::Suspend => iced::Command::perform(suspend(), msg),
|
||||||
PowerAction::Restart => Command::perform(restart(), Message::Zbus),
|
PowerAction::Restart => iced::Command::perform(restart(), msg),
|
||||||
PowerAction::Shutdown => {
|
PowerAction::Shutdown => iced::Command::perform(shutdown(), msg),
|
||||||
Command::perform(shutdown(), Message::Zbus)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +214,15 @@ impl Application for Power {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
|
self.core
|
||||||
|
.applet_helper
|
||||||
|
.icon_button(&self.icon_name)
|
||||||
|
.on_press(Message::TogglePopup)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, id: window::Id) -> Element<Message> {
|
||||||
if matches!(self.popup, Some(p) if p == id) {
|
if matches!(self.popup, Some(p) if p == id) {
|
||||||
let settings =
|
let settings =
|
||||||
row_button(vec![text(fl!("settings")).size(14).into()]).on_press(Message::Settings);
|
row_button(vec![text(fl!("settings")).size(14).into()]).on_press(Message::Settings);
|
||||||
|
|
@ -294,7 +270,7 @@ impl Application for Power {
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
.padding([8, 0]);
|
.padding([8, 0]);
|
||||||
|
|
||||||
self.applet_helper.popup_container(content).into()
|
self.core.applet_helper.popup_container(content).into()
|
||||||
} else if matches!(self.action_to_confirm, Some((c_id, _)) if c_id == id) {
|
} else if matches!(self.action_to_confirm, Some((c_id, _)) if c_id == id) {
|
||||||
let action = match self.action_to_confirm.as_ref().unwrap().1 {
|
let action = match self.action_to_confirm.as_ref().unwrap().1 {
|
||||||
PowerAction::Lock => "lock-screen",
|
PowerAction::Lock => "lock-screen",
|
||||||
|
|
@ -350,10 +326,8 @@ impl Application for Power {
|
||||||
.on_middle_press(Message::Cancel)
|
.on_middle_press(Message::Cancel)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
self.applet_helper
|
//panic!("no view for window {}", id.0)
|
||||||
.icon_button(&self.icon_name)
|
widget::text("").into()
|
||||||
.on_press(Message::TogglePopup)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,5 @@ license = "GPL-3.0-or-later"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
icon-loader = { version = "0.3.6", features = ["gtk"] }
|
icon-loader = { version = "0.3.6", features = ["gtk"] }
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
nix = "0.26.2"
|
nix = "0.26.2"
|
||||||
chrono = { version = "0.4.23", features = ["clock"] }
|
chrono = { version = "0.4.23", features = ["clock"] }
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,26 @@
|
||||||
|
use cosmic::app::{self, applet::cosmic_panel_config::PanelAnchor, Command};
|
||||||
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
||||||
use cosmic::iced::Limits;
|
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
time,
|
time,
|
||||||
wayland::InitialSurface,
|
|
||||||
widget::{button, column, text, vertical_space},
|
widget::{button, column, text, vertical_space},
|
||||||
window, Alignment, Application, Color, Command, Length, Rectangle, Subscription,
|
window, Alignment, Length, Rectangle, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application;
|
||||||
use cosmic::theme;
|
use cosmic::theme;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
widget::{icon, rectangle_tracker::*},
|
widget::{icon, rectangle_tracker::*},
|
||||||
Element, Theme,
|
Element, Theme,
|
||||||
};
|
};
|
||||||
use cosmic_applet::{cosmic_panel_config::PanelAnchor, CosmicAppletHelper};
|
|
||||||
|
|
||||||
use chrono::{DateTime, Local, Timelike};
|
use chrono::{DateTime, Local, Timelike};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub fn main() -> cosmic::iced::Result {
|
pub fn main() -> cosmic::iced::Result {
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<Time>(true, ())
|
||||||
let mut settings = helper.window_settings();
|
|
||||||
match &mut settings.initial_surface {
|
|
||||||
InitialSurface::XdgWindow(s) => {
|
|
||||||
s.autosize = true;
|
|
||||||
s.resizable = None;
|
|
||||||
s.size_limits = Limits::NONE.min_height(1.0).min_width(1.0);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
Time::run(settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Time {
|
struct Time {
|
||||||
applet_helper: CosmicAppletHelper,
|
core: cosmic::app::Core,
|
||||||
theme: Theme,
|
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
update_at: Every,
|
update_at: Every,
|
||||||
|
|
@ -43,22 +30,6 @@ struct Time {
|
||||||
rectangle: Rectangle,
|
rectangle: Rectangle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Time {
|
|
||||||
fn default() -> Self {
|
|
||||||
Time {
|
|
||||||
applet_helper: CosmicAppletHelper::default(),
|
|
||||||
theme: Theme::default(),
|
|
||||||
popup: None,
|
|
||||||
id_ctr: 0,
|
|
||||||
update_at: Every::Minute,
|
|
||||||
now: Local::now(),
|
|
||||||
msg: String::new(),
|
|
||||||
rectangle_tracker: None,
|
|
||||||
rectangle: Rectangle::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
enum Every {
|
enum Every {
|
||||||
|
|
@ -70,47 +41,41 @@ enum Every {
|
||||||
enum Message {
|
enum Message {
|
||||||
TogglePopup,
|
TogglePopup,
|
||||||
Tick,
|
Tick,
|
||||||
Ignore,
|
|
||||||
Rectangle(RectangleUpdate<u32>),
|
Rectangle(RectangleUpdate<u32>),
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Time {
|
impl cosmic::Application for Time {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicAppletTime";
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Time, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
(
|
||||||
Time {
|
Time {
|
||||||
applet_helper,
|
core,
|
||||||
theme,
|
popup: None,
|
||||||
..Default::default()
|
id_ctr: 0,
|
||||||
|
update_at: Every::Minute,
|
||||||
|
now: Local::now(),
|
||||||
|
msg: String::new(),
|
||||||
|
rectangle_tracker: None,
|
||||||
|
rectangle: Rectangle::default(),
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
String::from("Time")
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
self.theme.clone()
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
Message::Ignore
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
|
|
@ -129,7 +94,6 @@ impl Application for Time {
|
||||||
.expect("Setting nanoseconds to 0 should always be possible.");
|
.expect("Setting nanoseconds to 0 should always be possible.");
|
||||||
let wait = 1.max((next - now).num_milliseconds());
|
let wait = 1.max((next - now).num_milliseconds());
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
|
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
|
||||||
time::every(Duration::from_millis(
|
time::every(Duration::from_millis(
|
||||||
wait.try_into().unwrap_or(FALLBACK_DELAY),
|
wait.try_into().unwrap_or(FALLBACK_DELAY),
|
||||||
|
|
@ -140,10 +104,6 @@ impl Application for Time {
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
Command::none()
|
|
||||||
}
|
|
||||||
Message::TogglePopup => {
|
Message::TogglePopup => {
|
||||||
if let Some(p) = self.popup.take() {
|
if let Some(p) = self.popup.take() {
|
||||||
destroy_popup(p)
|
destroy_popup(p)
|
||||||
|
|
@ -166,7 +126,7 @@ impl Application for Time {
|
||||||
let new_id = window::Id(self.id_ctr);
|
let new_id = window::Id(self.id_ctr);
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
|
|
||||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
let mut popup_settings = self.core.applet_helper.get_popup_settings(
|
||||||
window::Id(0),
|
window::Id(0),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -192,7 +152,6 @@ impl Application for Time {
|
||||||
self.now = Local::now();
|
self.now = Local::now();
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
Message::Ignore => Command::none(),
|
|
||||||
Message::Rectangle(u) => {
|
Message::Rectangle(u) => {
|
||||||
match u {
|
match u {
|
||||||
RectangleUpdate::Rectangle(r) => {
|
RectangleUpdate::Rectangle(r) => {
|
||||||
|
|
@ -207,11 +166,10 @@ impl Application for Time {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
if id == window::Id(0) {
|
|
||||||
let button = button(
|
let button = button(
|
||||||
if matches!(
|
if matches!(
|
||||||
self.applet_helper.anchor,
|
self.core.applet_helper.anchor,
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom
|
PanelAnchor::Top | PanelAnchor::Bottom
|
||||||
) {
|
) {
|
||||||
column![text(self.now.format("%b %-d %-I:%M %p").to_string()).size(14)]
|
column![text(self.now.format("%b %-d %-I:%M %p").to_string()).size(14)]
|
||||||
|
|
@ -219,7 +177,7 @@ impl Application for Time {
|
||||||
let mut date_time_col = column![
|
let mut date_time_col = column![
|
||||||
icon(
|
icon(
|
||||||
"emoji-recent-symbolic",
|
"emoji-recent-symbolic",
|
||||||
self.applet_helper.suggested_size().0
|
self.core.applet_helper.suggested_size().0
|
||||||
)
|
)
|
||||||
.style(theme::Svg::Symbolic),
|
.style(theme::Svg::Symbolic),
|
||||||
text(self.now.format("%I").to_string()).size(14),
|
text(self.now.format("%I").to_string()).size(14),
|
||||||
|
|
@ -229,7 +187,7 @@ impl Application for Time {
|
||||||
// TODO better calendar icon?
|
// TODO better calendar icon?
|
||||||
icon(
|
icon(
|
||||||
"calendar-go-today-symbolic",
|
"calendar-go-today-symbolic",
|
||||||
self.applet_helper.suggested_size().0
|
self.core.applet_helper.suggested_size().0
|
||||||
)
|
)
|
||||||
.style(theme::Svg::Symbolic),
|
.style(theme::Svg::Symbolic),
|
||||||
]
|
]
|
||||||
|
|
@ -249,7 +207,9 @@ impl Application for Time {
|
||||||
} else {
|
} else {
|
||||||
button.into()
|
button.into()
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
fn view_window(&self, _id: window::Id) -> Element<Message> {
|
||||||
let content = column![]
|
let content = column![]
|
||||||
.align_items(Alignment::Start)
|
.align_items(Alignment::Start)
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
|
|
@ -257,7 +217,6 @@ impl Application for Time {
|
||||||
.push(text(&self.msg).size(14))
|
.push(text(&self.msg).size(14))
|
||||||
.padding(8);
|
.padding(8);
|
||||||
|
|
||||||
self.applet_helper.popup_container(content).into()
|
self.core.applet_helper.popup_container(content).into()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
cctk.workspace = true
|
cctk.workspace = true
|
||||||
cosmic-protocols.workspace = true
|
cosmic-protocols.workspace = true
|
||||||
nix = "0.26.1"
|
nix = "0.26.1"
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,13 @@
|
||||||
use cctk::sctk::reexports::{calloop::channel::SyncSender, client::backend::ObjectId};
|
use cctk::sctk::reexports::{calloop::channel::SyncSender, client::backend::ObjectId};
|
||||||
|
use cosmic::app::{applet::cosmic_panel_config::PanelAnchor, Command};
|
||||||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
use cosmic::iced::alignment::{Horizontal, Vertical};
|
||||||
use cosmic::iced::mouse::{self, ScrollDelta};
|
use cosmic::iced::mouse::{self, ScrollDelta};
|
||||||
use cosmic::iced::wayland::actions::window::SctkWindowSettings;
|
|
||||||
use cosmic::iced::wayland::InitialSurface;
|
|
||||||
use cosmic::iced::widget::{column, container, row, text};
|
use cosmic::iced::widget::{column, container, row, text};
|
||||||
use cosmic::iced::Color;
|
use cosmic::iced::{subscription, widget::button, Event::Mouse, Length, Subscription};
|
||||||
use cosmic::iced::{
|
use cosmic::iced_style::application;
|
||||||
subscription, widget::button, window, Application, Command, Event::Mouse, Length, Settings,
|
|
||||||
Subscription,
|
|
||||||
};
|
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
|
||||||
use cosmic::theme::Button;
|
use cosmic::theme::Button;
|
||||||
use cosmic::{Element, Theme};
|
use cosmic::{Element, Theme};
|
||||||
use cosmic_applet::cosmic_panel_config::PanelAnchor;
|
|
||||||
use cosmic_applet::CosmicAppletHelper;
|
|
||||||
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
|
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
|
@ -22,16 +16,7 @@ use crate::wayland::{WorkspaceEvent, WorkspaceList};
|
||||||
use crate::wayland_subscription::{workspaces, WorkspacesUpdate};
|
use crate::wayland_subscription::{workspaces, WorkspacesUpdate};
|
||||||
|
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
let settings = Settings {
|
cosmic::app::applet::run::<IcedWorkspacesApplet>(true, ())
|
||||||
initial_surface: InitialSurface::XdgWindow(SctkWindowSettings {
|
|
||||||
size: (32, 32),
|
|
||||||
autosize: true,
|
|
||||||
resizable: None,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
IcedWorkspacesApplet::run(settings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
@ -41,11 +26,10 @@ pub enum Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IcedWorkspacesApplet {
|
struct IcedWorkspacesApplet {
|
||||||
theme: Theme,
|
core: cosmic::app::Core,
|
||||||
workspaces: WorkspaceList,
|
workspaces: WorkspaceList,
|
||||||
workspace_tx: Option<SyncSender<WorkspaceEvent>>,
|
workspace_tx: Option<SyncSender<WorkspaceEvent>>,
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
helper: CosmicAppletHelper,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -53,35 +37,36 @@ enum Message {
|
||||||
WorkspaceUpdate(WorkspacesUpdate),
|
WorkspaceUpdate(WorkspacesUpdate),
|
||||||
WorkspacePressed(ObjectId),
|
WorkspacePressed(ObjectId),
|
||||||
WheelScrolled(ScrollDelta),
|
WheelScrolled(ScrollDelta),
|
||||||
Theme(Theme),
|
|
||||||
Errored,
|
Errored,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for IcedWorkspacesApplet {
|
impl cosmic::Application for IcedWorkspacesApplet {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Theme = Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
|
const APP_ID: &'static str = config::APP_ID;
|
||||||
|
|
||||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
|
||||||
(
|
(
|
||||||
IcedWorkspacesApplet {
|
IcedWorkspacesApplet {
|
||||||
layout: match &applet_helper.anchor {
|
layout: match &core.applet_helper.anchor {
|
||||||
PanelAnchor::Left | PanelAnchor::Right => Layout::Column,
|
PanelAnchor::Left | PanelAnchor::Right => Layout::Column,
|
||||||
PanelAnchor::Top | PanelAnchor::Bottom => Layout::Row,
|
PanelAnchor::Top | PanelAnchor::Bottom => Layout::Row,
|
||||||
},
|
},
|
||||||
theme: applet_helper.theme(),
|
core,
|
||||||
workspaces: Vec::new(),
|
workspaces: Vec::new(),
|
||||||
workspace_tx: Default::default(),
|
workspace_tx: Default::default(),
|
||||||
helper: Default::default(),
|
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
config::APP_ID.to_string()
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -120,12 +105,11 @@ impl Application for IcedWorkspacesApplet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Errored => {}
|
Message::Errored => {}
|
||||||
Message::Theme(t) => self.theme = t,
|
|
||||||
}
|
}
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, _id: window::Id) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
if self.workspaces.is_empty() {
|
if self.workspaces.is_empty() {
|
||||||
return row![].padding(8).into();
|
return row![].padding(8).into();
|
||||||
}
|
}
|
||||||
|
|
@ -141,8 +125,12 @@ impl Application for IcedWorkspacesApplet {
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill),
|
.height(Length::Fill),
|
||||||
)
|
)
|
||||||
.width(Length::Fixed(self.helper.suggested_size().0 as f32 + 16.0))
|
.width(Length::Fixed(
|
||||||
.height(Length::Fixed(self.helper.suggested_size().0 as f32 + 16.0))
|
self.core.applet_helper.suggested_size().0 as f32 + 16.0,
|
||||||
|
))
|
||||||
|
.height(Length::Fixed(
|
||||||
|
self.core.applet_helper.suggested_size().0 as f32 + 16.0,
|
||||||
|
))
|
||||||
.on_press(Message::WorkspacePressed(w.2.clone()))
|
.on_press(Message::WorkspacePressed(w.2.clone()))
|
||||||
.padding(0);
|
.padding(0);
|
||||||
Some(
|
Some(
|
||||||
|
|
@ -179,7 +167,6 @@ impl Application for IcedWorkspacesApplet {
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(
|
Subscription::batch(
|
||||||
vec![
|
vec![
|
||||||
self.helper.theme_subscription(0).map(Message::Theme),
|
|
||||||
workspaces(0).map(Message::WorkspaceUpdate),
|
workspaces(0).map(Message::WorkspaceUpdate),
|
||||||
subscription::events_with(|e, _| match e {
|
subscription::events_with(|e, _| match e {
|
||||||
Mouse(mouse::Event::WheelScrolled { delta }) => {
|
Mouse(mouse::Event::WheelScrolled { delta }) => {
|
||||||
|
|
@ -192,18 +179,7 @@ impl Application for IcedWorkspacesApplet {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn theme(&self) -> Theme {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
self.theme.clone()
|
Some(cosmic::app::applet::style())
|
||||||
}
|
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
|
||||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,3 @@ license = "GPL-3.0-or-later"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
freedesktop-desktop-entry = "0.5.0"
|
freedesktop-desktop-entry = "0.5.0"
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
use cosmic::{
|
use cosmic::{app, iced, iced_style::application, theme::Theme};
|
||||||
iced::Limits,
|
|
||||||
iced::{self, wayland::InitialSurface, Application},
|
|
||||||
iced_runtime::core::window,
|
|
||||||
iced_style::application,
|
|
||||||
theme::Theme,
|
|
||||||
};
|
|
||||||
use cosmic_applet::CosmicAppletHelper;
|
|
||||||
use freedesktop_desktop_entry::DesktopEntry;
|
use freedesktop_desktop_entry::DesktopEntry;
|
||||||
use std::{env, fs, process::Command};
|
use std::{env, fs, process::Command};
|
||||||
|
|
||||||
|
|
@ -17,70 +10,47 @@ struct Desktop {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Button {
|
struct Button {
|
||||||
|
core: cosmic::app::Core,
|
||||||
desktop: Desktop,
|
desktop: Desktop,
|
||||||
applet_helper: CosmicAppletHelper,
|
|
||||||
theme: Theme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Msg {
|
enum Msg {
|
||||||
Press,
|
Press,
|
||||||
Theme(Theme),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl iced::Application for Button {
|
impl cosmic::Application for Button {
|
||||||
type Message = Msg;
|
type Message = Msg;
|
||||||
type Theme = cosmic::Theme;
|
|
||||||
type Executor = cosmic::SingleThreadExecutor;
|
type Executor = cosmic::SingleThreadExecutor;
|
||||||
type Flags = Desktop;
|
type Flags = Desktop;
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicPanelButton";
|
||||||
|
|
||||||
fn new(desktop: Desktop) -> (Self, iced::Command<Msg>) {
|
fn init(core: cosmic::app::Core, desktop: Desktop) -> (Self, app::Command<Msg>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
(Button { core, desktop }, app::Command::none())
|
||||||
let theme = applet_helper.theme();
|
|
||||||
(
|
|
||||||
Button {
|
|
||||||
desktop,
|
|
||||||
applet_helper,
|
|
||||||
theme,
|
|
||||||
},
|
|
||||||
iced::Command::none(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn core(&self) -> &cosmic::app::Core {
|
||||||
String::from("Button")
|
&self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_requested(&self, _id: window::Id) -> Msg {
|
fn core_mut(&mut self) -> &mut cosmic::app::Core {
|
||||||
unimplemented!()
|
&mut self.core
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
|
||||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| {
|
Some(cosmic::app::applet::style())
|
||||||
application::Appearance {
|
|
||||||
background_color: iced::Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
|
||||||
text_color: theme.cosmic().on_bg_color().into(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> iced::Subscription<Msg> {
|
fn update(&mut self, message: Msg) -> app::Command<Msg> {
|
||||||
self.applet_helper.theme_subscription(0).map(Msg::Theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, message: Msg) -> iced::Command<Msg> {
|
|
||||||
match message {
|
match message {
|
||||||
Msg::Press => {
|
Msg::Press => {
|
||||||
let _ = Command::new("sh").arg("-c").arg(&self.desktop.exec).spawn();
|
let _ = Command::new("sh").arg("-c").arg(&self.desktop.exec).spawn();
|
||||||
}
|
}
|
||||||
Msg::Theme(t) => {
|
|
||||||
self.theme = t;
|
|
||||||
}
|
}
|
||||||
}
|
app::Command::none()
|
||||||
iced::Command::none()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, _id: window::Id) -> cosmic::Element<Msg> {
|
fn view(&self) -> cosmic::Element<Msg> {
|
||||||
// TODO icon?
|
// TODO icon?
|
||||||
cosmic::widget::button(cosmic::theme::Button::Text)
|
cosmic::widget::button(cosmic::theme::Button::Text)
|
||||||
.text(&self.desktop.name)
|
.text(&self.desktop.name)
|
||||||
|
|
@ -119,17 +89,5 @@ pub fn main() -> iced::Result {
|
||||||
let desktop = desktop.expect(&format!(
|
let desktop = desktop.expect(&format!(
|
||||||
"Failed to find valid desktop file '{filename}' in search paths"
|
"Failed to find valid desktop file '{filename}' in search paths"
|
||||||
));
|
));
|
||||||
let helper = CosmicAppletHelper::default();
|
cosmic::app::applet::run::<Button>(true, desktop)
|
||||||
let mut settings = iced::Settings {
|
|
||||||
flags: desktop,
|
|
||||||
..helper.window_settings()
|
|
||||||
};
|
|
||||||
match &mut settings.initial_surface {
|
|
||||||
InitialSurface::XdgWindow(s) => {
|
|
||||||
s.autosize = true;
|
|
||||||
s.size_limits = Limits::NONE.min_height(1.0).min_width(1.0);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Button::run(settings)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue