update libcosmic

This commit is contained in:
Ashley Wulber 2023-04-05 20:40:22 -04:00 committed by Ashley Wulber
parent 2c74a551c7
commit 903a5589a3
19 changed files with 2261 additions and 1877 deletions

1452
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,6 @@ use cosmic::iced;
use cosmic::iced::wayland::actions::window::SctkWindowSettings; 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::wayland::SurfaceIdWrapper;
use cosmic::iced::widget::mouse_listener; use cosmic::iced::widget::mouse_listener;
use cosmic::iced::widget::{column, row}; use cosmic::iced::widget::{column, row};
use cosmic::iced::Settings; use cosmic::iced::Settings;
@ -39,7 +38,6 @@ use cosmic::{Element, Theme};
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 iced::widget::container; use iced::widget::container;
use iced::widget::horizontal_space;
use iced::Alignment; use iced::Alignment;
use iced::Background; use iced::Background;
use iced::Length; use iced::Length;
@ -380,211 +378,202 @@ impl Application for CosmicAppList {
Command::none() Command::none()
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if let Some(Toplevel {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), toplevels,
SurfaceIdWrapper::Window(_) => { desktop_info,
let (favorites, running) = self.toplevel_list.iter().fold( ..
(Vec::new(), Vec::new()), }) = self.toplevel_list.iter().find(|t| t.popup == Some(id))
|(mut favorites, mut running), {
Toplevel { let is_favorite = self.config.favorites.contains(&desktop_info.id)
id, || self.config.favorites.contains(&desktop_info.name);
toplevels,
desktop_info,
..
}| {
let cosmic_icon = cosmic::widget::icon(
Path::new(&desktop_info.icon),
self.applet_helper.suggested_size().0,
);
let dot_radius = 2; let mut content = column![
let dots = (0..toplevels.len()) iced::widget::text(&desktop_info.name).horizontal_alignment(Horizontal::Center),
.into_iter() cosmic::widget::button(Button::Text)
.map(|_| { .custom(vec![iced::widget::text(fl!("new-window")).into()])
container(vertical_space(Length::Units(0))) .on_press(Message::Exec(desktop_info.exec.clone())),
.padding(dot_radius) ]
.style(<Self::Theme as container::StyleSheet>::Style::Custom( .padding(8)
|theme| container::Appearance { .spacing(4)
text_color: Some(Color::TRANSPARENT), .align_items(Alignment::Center);
background: Some(Background::Color( if !toplevels.is_empty() {
theme.cosmic().on_bg_color().into(), let mut list_col = column![];
)), for (handle, info) in toplevels {
border_radius: 4.0, let title = if info.title.len() > 20 {
border_width: 0.0, format!("{:.24}...", &info.title)
border_color: Color::TRANSPARENT, } else {
}, info.title.clone()
)) };
.into() list_col = list_col.push(
}) cosmic::widget::button(Button::Text)
.collect_vec(); .custom(vec![iced::widget::text(title).into()])
let icon_wrapper = match &self.applet_helper.anchor { .on_press(Message::Activate(handle.clone())),
PanelAnchor::Left => { );
row(vec![column(dots).spacing(4).into(), cosmic_icon.into()]) }
.align_items(iced::Alignment::Center) content = content.push(divider::horizontal::light());
.spacing(4) content = content.push(list_col);
.into() content = content.push(divider::horizontal::light());
} }
PanelAnchor::Right => { content = content.push(if is_favorite {
row(vec![cosmic_icon.into(), column(dots).spacing(4).into()]) cosmic::widget::button(Button::Text)
.align_items(iced::Alignment::Center) .custom(vec![iced::widget::text(fl!("unfavorite")).into()])
.spacing(4) .on_press(Message::UnFavorite(desktop_info.id.clone()))
.into() } else {
} cosmic::widget::button(Button::Text)
PanelAnchor::Top => { .custom(vec![iced::widget::text(fl!("favorite")).into()])
column(vec![row(dots).spacing(4).into(), cosmic_icon.into()]) .on_press(Message::Favorite(desktop_info.id.clone()))
.align_items(iced::Alignment::Center) });
.spacing(4)
.into()
}
PanelAnchor::Bottom => {
column(vec![cosmic_icon.into(), row(dots).spacing(4).into()])
.align_items(iced::Alignment::Center)
.spacing(4)
.into()
}
};
let mut icon_button = cosmic::widget::button(Button::Text)
.custom(vec![icon_wrapper])
.padding(8);
if self.popup.is_none() {
icon_button = icon_button.on_press(
toplevels
.first()
.map(|t| Message::Activate(t.0.clone()))
.unwrap_or_else(|| Message::Exec(desktop_info.exec.clone())),
);
}
// TODO tooltip on hover content = match toplevels.len() {
let icon_button = mouse_listener( 0 => content,
icon_button.width(Length::Shrink).height(Length::Shrink), 1 => content.push(
) cosmic::widget::button(Button::Text)
.on_right_release(Message::Popup(desktop_info.id.clone())); .custom(vec![iced::widget::text(fl!("quit")).into()])
let icon_button = if let Some(tracker) = self.rectangle_tracker.as_ref() { .on_press(Message::Quit(desktop_info.id.clone())),
tracker.container(*id, icon_button).into() ),
} else { _ => content.push(
icon_button.into() cosmic::widget::button(Button::Text)
}; .custom(vec![iced::widget::text(&fl!("quit-all")).into()])
if self.config.favorites.contains(&desktop_info.id) .on_press(Message::Quit(desktop_info.id.clone())),
|| self.config.favorites.contains(&desktop_info.name) ),
{ };
favorites.push(icon_button) // return Container::new(Container::new(content.width(Length::Shrink).height(Length::Shrink)).style(
} else { // cosmic::Container::Custom(|theme| container::Appearance {
running.push(icon_button); // text_color: Some(theme.cosmic().on_bg_color().into()),
} // background: Some(theme.extended_palette().background.base.color.into()),
(favorites, running) // border_radius: 12.0,
}, // border_width: 0.0,
// border_color: Color::TRANSPARENT,
// }),
// )).into();
return self.applet_helper.popup_container(content).into();
}
let (favorites, running) = self.toplevel_list.iter().fold(
(Vec::new(), Vec::new()),
|(mut favorites, mut running),
Toplevel {
id,
toplevels,
desktop_info,
..
}| {
let cosmic_icon = cosmic::widget::icon(
Path::new(&desktop_info.icon),
self.applet_helper.suggested_size().0,
); );
let (w, h) = match self.applet_helper.anchor { let dot_radius = 2;
PanelAnchor::Top | PanelAnchor::Bottom => (Length::Shrink, Length::Fill), let dots = (0..toplevels.len())
PanelAnchor::Left | PanelAnchor::Right => (Length::Fill, Length::Shrink), .into_iter()
}; .map(|_| {
container(vertical_space(Length::Units(0)))
let content = match &self.applet_helper.anchor { .padding(dot_radius)
PanelAnchor::Left | PanelAnchor::Right => container( .style(<Self::Theme as container::StyleSheet>::Style::Custom(
column![ |theme| container::Appearance {
column(favorites), text_color: Some(Color::TRANSPARENT),
divider::horizontal::light(), background: Some(Background::Color(
column(running) theme.cosmic().on_bg_color().into(),
] )),
.spacing(4) border_radius: 4.0,
.align_items(Alignment::Center) border_width: 0.0,
.height(h) border_color: Color::TRANSPARENT,
.width(w), },
), ))
PanelAnchor::Top | PanelAnchor::Bottom => container( .into()
row![row(favorites), vertical_rule(1), row(running)] })
.collect_vec();
let icon_wrapper = match &self.applet_helper.anchor {
PanelAnchor::Left => {
row(vec![column(dots).spacing(4).into(), cosmic_icon.into()])
.align_items(iced::Alignment::Center)
.spacing(4) .spacing(4)
.align_items(Alignment::Center) .into()
.height(h)
.width(w),
),
};
if self.popup.is_some() {
mouse_listener(content)
.on_right_press(Message::ClosePopup)
.on_press(Message::ClosePopup)
.into()
} else {
content.into()
}
}
SurfaceIdWrapper::Popup(p) => {
if let Some(Toplevel {
toplevels,
desktop_info,
..
}) = self.toplevel_list.iter().find(|t| t.popup == Some(p))
{
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 { PanelAnchor::Right => {
cosmic::widget::button(Button::Text) row(vec![cosmic_icon.into(), column(dots).spacing(4).into()])
.custom(vec![iced::widget::text(fl!("unfavorite")).into()]) .align_items(iced::Alignment::Center)
.on_press(Message::UnFavorite(desktop_info.id.clone())) .spacing(4)
} else { .into()
cosmic::widget::button(Button::Text) }
.custom(vec![iced::widget::text(fl!("favorite")).into()]) PanelAnchor::Top => {
.on_press(Message::Favorite(desktop_info.id.clone())) column(vec![row(dots).spacing(4).into(), cosmic_icon.into()])
}); .align_items(iced::Alignment::Center)
.spacing(4)
content = match toplevels.len() { .into()
0 => content, }
1 => content.push( PanelAnchor::Bottom => {
cosmic::widget::button(Button::Text) column(vec![cosmic_icon.into(), row(dots).spacing(4).into()])
.custom(vec![iced::widget::text(fl!("quit")).into()]) .align_items(iced::Alignment::Center)
.on_press(Message::Quit(desktop_info.id.clone())), .spacing(4)
), .into()
_ => content.push( }
cosmic::widget::button(Button::Text) };
.custom(vec![iced::widget::text(&fl!("quit-all")).into()]) let mut icon_button = cosmic::widget::button(Button::Text)
.on_press(Message::Quit(desktop_info.id.clone())), .custom(vec![icon_wrapper])
), .padding(8);
}; if self.popup.is_none() {
// return Container::new(Container::new(content.width(Length::Shrink).height(Length::Shrink)).style( icon_button = icon_button.on_press(
// cosmic::Container::Custom(|theme| container::Appearance { toplevels
// text_color: Some(theme.cosmic().on_bg_color().into()), .first()
// background: Some(theme.extended_palette().background.base.color.into()), .map(|t| Message::Activate(t.0.clone()))
// border_radius: 12.0, .unwrap_or_else(|| Message::Exec(desktop_info.exec.clone())),
// border_width: 0.0, );
// border_color: Color::TRANSPARENT,
// }),
// )).into();
return self.applet_helper.popup_container(content).into();
} }
horizontal_space(Length::Units(1)).into()
} // TODO tooltip on hover
let icon_button =
mouse_listener(icon_button.width(Length::Shrink).height(Length::Shrink))
.on_right_release(Message::Popup(desktop_info.id.clone()));
let icon_button = if let Some(tracker) = self.rectangle_tracker.as_ref() {
tracker.container(*id, icon_button).into()
} else {
icon_button.into()
};
if self.config.favorites.contains(&desktop_info.id)
|| self.config.favorites.contains(&desktop_info.name)
{
favorites.push(icon_button)
} else {
running.push(icon_button);
}
(favorites, running)
},
);
let (w, h) = match self.applet_helper.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => (Length::Shrink, Length::Fill),
PanelAnchor::Left | PanelAnchor::Right => (Length::Fill, Length::Shrink),
};
let content = match &self.applet_helper.anchor {
PanelAnchor::Left | PanelAnchor::Right => container(
column![
column(favorites),
divider::horizontal::light(),
column(running)
]
.spacing(4)
.align_items(Alignment::Center)
.height(h)
.width(w),
),
PanelAnchor::Top | PanelAnchor::Bottom => container(
row![row(favorites), vertical_rule(1), row(running)]
.spacing(4)
.align_items(Alignment::Center)
.height(h)
.width(w),
),
};
if self.popup.is_some() {
mouse_listener(content)
.on_right_press(Message::ClosePopup)
.on_press(Message::ClosePopup)
.into()
} else {
content.into()
} }
} }
@ -614,7 +603,7 @@ impl Application for CosmicAppList {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }

View file

@ -11,6 +11,5 @@ 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 = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "69bffe5" }
log = "0.4.14" log = "0.4.14"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"

View file

@ -1,4 +1,3 @@
use cosmic::iced::wayland::SurfaceIdWrapper;
use cosmic::iced::widget; use cosmic::iced::widget;
use cosmic::iced_native::alignment::Horizontal; use cosmic::iced_native::alignment::Horizontal;
use cosmic::iced_native::layout::Limits; use cosmic::iced_native::layout::Limits;
@ -97,7 +96,7 @@ impl Application for Audio {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }
@ -268,134 +267,131 @@ impl Application for Audio {
pulse::connect().map(Message::Pulse) pulse::connect().map(Message::Pulse)
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), self.applet_helper
SurfaceIdWrapper::Window(_) => self
.applet_helper
.icon_button(&self.icon_name) .icon_button(&self.icon_name)
.on_press(Message::TogglePopup) .on_press(Message::TogglePopup)
.into(), .into()
SurfaceIdWrapper::Popup(_) => { } else {
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
.as_ref() .as_ref()
.map(|o| o.volume.avg()) .map(|o| o.volume.avg())
.unwrap_or_default(), .unwrap_or_default(),
) )
.0 * 100.0; .0 * 100.0;
let in_f64 = VolumeLinear::from( let in_f64 = VolumeLinear::from(
self.current_input self.current_input
.as_ref() .as_ref()
.map(|o| o.volume.avg()) .map(|o| o.volume.avg())
.unwrap_or_default(), .unwrap_or_default(),
) )
.0 * 100.0; .0 * 100.0;
let audio_content = if audio_disabled { let audio_content = if audio_disabled {
column![text("PulseAudio Disconnected") column![text("PulseAudio Disconnected")
.width(Length::Fill) .width(Length::Fill)
.horizontal_alignment(Horizontal::Center) .horizontal_alignment(Horizontal::Center)
.size(24),] .size(24),]
} else { } else {
column![ column![
row![ row![
icon("audio-volume-high-symbolic", 32) icon("audio-volume-high-symbolic", 32)
.width(Length::Units(24)) .width(Length::Units(24))
.height(Length::Units(24)) .height(Length::Units(24))
.style(Svg::Symbolic), .style(Svg::Symbolic),
slider(0.0..=100.0, out_f64, Message::SetOutputVolume) slider(0.0..=100.0, out_f64, Message::SetOutputVolume)
.width(Length::FillPortion(5)), .width(Length::FillPortion(5)),
text(format!("{}%", out_f64.round())) text(format!("{}%", out_f64.round()))
.width(Length::FillPortion(1)) .width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right) .horizontal_alignment(Horizontal::Right)
]
.spacing(12)
.align_items(Alignment::Center)
.padding([8, 24]),
row![
icon("audio-input-microphone-symbolic", 32)
.width(Length::Units(24))
.height(Length::Units(24))
.style(Svg::Symbolic),
slider(0.0..=100.0, in_f64, Message::SetInputVolume)
.width(Length::FillPortion(5)),
text(format!("{}%", in_f64.round()))
.width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right)
]
.spacing(12)
.align_items(Alignment::Center)
.padding([8, 24]),
container(divider::horizontal::light())
.padding([12, 24])
.width(Length::Fill),
revealer(
self.is_open == IsOpen::Output,
"Output",
match &self.current_output {
Some(output) => pretty_name(output.description.clone()),
None => String::from("No device selected"),
},
self.outputs
.clone()
.into_iter()
.map(|output| (
output.name.clone().unwrap_or_default(),
pretty_name(output.description)
))
.collect(),
Message::OutputToggle,
Message::OutputChanged,
),
revealer(
self.is_open == IsOpen::Input,
"Input",
match &self.current_input {
Some(input) => pretty_name(input.description.clone()),
None => String::from("No device selected"),
},
self.inputs
.clone()
.into_iter()
.map(|input| (
input.name.clone().unwrap_or_default(),
pretty_name(input.description)
))
.collect(),
Message::InputToggle,
Message::InputChanged,
)
] ]
.align_items(Alignment::Start) .spacing(12)
}; .align_items(Alignment::Center)
let content = column![ .padding([8, 24]),
audio_content, row![
icon("audio-input-microphone-symbolic", 32)
.width(Length::Units(24))
.height(Length::Units(24))
.style(Svg::Symbolic),
slider(0.0..=100.0, in_f64, Message::SetInputVolume)
.width(Length::FillPortion(5)),
text(format!("{}%", in_f64.round()))
.width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right)
]
.spacing(12)
.align_items(Alignment::Center)
.padding([8, 24]),
container(divider::horizontal::light()) container(divider::horizontal::light())
.padding([12, 24]) .padding([12, 24])
.width(Length::Fill), .width(Length::Fill),
container(toggler( revealer(
Some("Show Media Controls on Top Panel".into()), self.is_open == IsOpen::Output,
self.show_media_controls_in_top_panel, "Output",
Message::ToggleMediaControlsInTopPanel, match &self.current_output {
)) Some(output) => pretty_name(output.description.clone()),
.padding([0, 24]), None => String::from("No device selected"),
container(divider::horizontal::light()) },
.padding([12, 24]) self.outputs
.width(Length::Fill), .clone()
button(APPLET_BUTTON_THEME) .into_iter()
.text("Sound Settings...") .map(|output| (
.padding([8, 24]) output.name.clone().unwrap_or_default(),
.width(Length::Fill) pretty_name(output.description)
))
.collect(),
Message::OutputToggle,
Message::OutputChanged,
),
revealer(
self.is_open == IsOpen::Input,
"Input",
match &self.current_input {
Some(input) => pretty_name(input.description.clone()),
None => String::from("No device selected"),
},
self.inputs
.clone()
.into_iter()
.map(|input| (
input.name.clone().unwrap_or_default(),
pretty_name(input.description)
))
.collect(),
Message::InputToggle,
Message::InputChanged,
)
] ]
.align_items(Alignment::Start) .align_items(Alignment::Start)
.padding([8, 0]); };
let content = column![
audio_content,
container(divider::horizontal::light())
.padding([12, 24])
.width(Length::Fill),
container(toggler(
Some("Show Media Controls on Top Panel".into()),
self.show_media_controls_in_top_panel,
Message::ToggleMediaControlsInTopPanel,
))
.padding([0, 24]),
container(divider::horizontal::light())
.padding([12, 24])
.width(Length::Fill),
button(APPLET_BUTTON_THEME)
.text("Sound Settings...")
.padding([8, 24])
.width(Length::Fill)
]
.align_items(Alignment::Start)
.padding([8, 0]);
self.applet_helper self.applet_helper
.popup_container(container(content)) .popup_container(container(content))
.into() .into()
}
} }
} }
} }

View file

@ -6,7 +6,6 @@ edition = "2021"
[dependencies] [dependencies]
once_cell = "1.16.0" once_cell = "1.16.0"
libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "69bffe5" }
futures = "0.3" futures = "0.3"
zbus = { version = "3.5", default-features = false, features = ["tokio"] } zbus = { version = "3.5", default-features = false, features = ["tokio"] }
log = "0.4" log = "0.4"

View file

@ -13,7 +13,6 @@ use crate::upower_kbdbacklight::{
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME}; use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
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::wayland::SurfaceIdWrapper;
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, Application, Command, Length, Subscription,
@ -213,163 +212,158 @@ impl Application for CosmicBatteryApplet {
} }
Command::none() Command::none()
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), self.applet_helper
SurfaceIdWrapper::Window(_) => self
.applet_helper
.icon_button(&self.icon_name) .icon_button(&self.icon_name)
.on_press(Message::TogglePopup) .on_press(Message::TogglePopup)
.into(), .into()
SurfaceIdWrapper::Popup(_) => { } else {
let name = text(fl!("battery")).size(18); let name = text(fl!("battery")).size(18);
let description = text( let description = text(
if "battery-full-charging-symbolic" == self.icon_name if "battery-full-charging-symbolic" == self.icon_name
|| "battery-full-charged-symbolic" == self.icon_name || "battery-full-charged-symbolic" == self.icon_name
{ {
format!("{}%", self.battery_percent) format!("{}%", self.battery_percent)
} else { } else {
format!( format!(
"{} {} ({:.0}%)", "{} {} ({:.0}%)",
format_duration(self.time_remaining), format_duration(self.time_remaining),
fl!("until-empty"), fl!("until-empty"),
self.battery_percent self.battery_percent
)
},
)
.size(12);
self.applet_helper
.popup_container(
column![
row![
icon(&*self.icon_name, 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
column![name, description]
]
.padding([0, 24])
.spacing(8)
.align_items(Alignment::Center),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(fl!("battery")).size(14),
text(fl!("battery-desc")).size(12)
]
.width(Length::Fill),
icon("emblem-ok-symbolic", 12).size(12).style(
match self.power_profile {
Power::Battery => Svg::SymbolicActive,
_ => Svg::Default,
}
),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectProfile(Power::Battery))
.width(Length::Fill),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(fl!("balanced")).size(14),
text(fl!("balanced-desc")).size(12)
]
.width(Length::Fill),
icon("emblem-ok-symbolic", 12).size(12).style(
match self.power_profile {
Power::Balanced => Svg::SymbolicActive,
_ => Svg::Default,
}
),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectProfile(Power::Balanced))
.width(Length::Fill),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(fl!("performance")).size(14),
text(fl!("performance-desc")).size(12)
]
.width(Length::Fill),
icon("emblem-ok-symbolic", 12).size(12).style(
match self.power_profile {
Power::Performance => Svg::SymbolicActive,
_ => Svg::Default,
}
),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectProfile(Power::Performance))
.width(Length::Fill),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
container(toggler(fl!("max-charge"), self.charging_limit, |_| {
Message::SetChargingLimit(!self.charging_limit)
}))
.padding([0, 24])
.width(Length::Fill),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
row![
icon("display-brightness-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
slider(
1..=100,
(self.screen_brightness * 100.0) as i32,
Message::SetScreenBrightness
),
text(format!("{:.0}%", self.screen_brightness * 100.0))
.width(Length::Units(40))
.horizontal_alignment(Horizontal::Right)
]
.padding([0, 24])
.spacing(12),
row![
icon("keyboard-brightness-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
slider(
0..=100,
(self.kbd_brightness * 100.0) as i32,
Message::SetKbdBrightness
),
text(format!("{:.0}%", self.kbd_brightness * 100.0))
.width(Length::Units(40))
.horizontal_alignment(Horizontal::Right)
]
.padding([0, 24])
.spacing(12),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
button(APPLET_BUTTON_THEME)
.custom(vec![text(fl!("power-settings"))
.width(Length::Fill)
.into()])
.on_press(Message::OpenBatterySettings)
.width(Length::Fill)
.padding([8, 24])
]
.spacing(8)
.padding([8, 0]),
) )
.into() },
} )
.size(12);
self.applet_helper
.popup_container(
column![
row![
icon(&*self.icon_name, 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
column![name, description]
]
.padding([0, 24])
.spacing(8)
.align_items(Alignment::Center),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(fl!("battery")).size(14),
text(fl!("battery-desc")).size(12)
]
.width(Length::Fill),
icon("emblem-ok-symbolic", 12).size(12).style(
match self.power_profile {
Power::Battery => Svg::SymbolicActive,
_ => Svg::Default,
}
),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectProfile(Power::Battery))
.width(Length::Fill),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(fl!("balanced")).size(14),
text(fl!("balanced-desc")).size(12)
]
.width(Length::Fill),
icon("emblem-ok-symbolic", 12).size(12).style(
match self.power_profile {
Power::Balanced => Svg::SymbolicActive,
_ => Svg::Default,
}
),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectProfile(Power::Balanced))
.width(Length::Fill),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(fl!("performance")).size(14),
text(fl!("performance-desc")).size(12)
]
.width(Length::Fill),
icon("emblem-ok-symbolic", 12).size(12).style(
match self.power_profile {
Power::Performance => Svg::SymbolicActive,
_ => Svg::Default,
}
),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectProfile(Power::Performance))
.width(Length::Fill),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
container(toggler(fl!("max-charge"), self.charging_limit, |_| {
Message::SetChargingLimit(!self.charging_limit)
}))
.padding([0, 24])
.width(Length::Fill),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
row![
icon("display-brightness-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
slider(
1..=100,
(self.screen_brightness * 100.0) as i32,
Message::SetScreenBrightness
),
text(format!("{:.0}%", self.screen_brightness * 100.0))
.width(Length::Units(40))
.horizontal_alignment(Horizontal::Right)
]
.padding([0, 24])
.spacing(12),
row![
icon("keyboard-brightness-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
slider(
0..=100,
(self.kbd_brightness * 100.0) as i32,
Message::SetKbdBrightness
),
text(format!("{:.0}%", self.kbd_brightness * 100.0))
.width(Length::Units(40))
.horizontal_alignment(Horizontal::Right)
]
.padding([0, 24])
.spacing(12),
container(divider::horizontal::light())
.width(Length::Fill)
.padding([0, 12]),
button(APPLET_BUTTON_THEME)
.custom(vec![text(fl!("power-settings")).width(Length::Fill).into()])
.on_press(Message::OpenBatterySettings)
.width(Length::Fill)
.padding([8, 24])
]
.spacing(8)
.padding([8, 0]),
)
.into()
} }
} }
@ -406,7 +400,7 @@ impl Application for CosmicBatteryApplet {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Message {
Message::Ignore Message::Ignore
} }

View file

@ -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 = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "69bffe5" }
futures = "0.3" futures = "0.3"
log = "0.4" log = "0.4"
pretty_env_logger = "0.4" pretty_env_logger = "0.4"
@ -21,4 +20,4 @@ anyhow = "1.0"
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] } i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] }
i18n-embed-fl = "0.6.4" i18n-embed-fl = "0.6.4"
rust-embed = "6.3.0" rust-embed = "6.3.0"
rand = "0.8" rand = "0.8"

View file

@ -6,7 +6,6 @@ use cosmic::{
iced::{ iced::{
wayland::{ wayland::{
popup::{destroy_popup, get_popup}, popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
}, },
widget::{column, container, row, scrollable, text, Column}, widget::{column, container, row, scrollable, text, Column},
Alignment, Application, Color, Command, Length, Subscription, Alignment, Application, Color, Command, Length, Subscription,
@ -275,7 +274,7 @@ impl Application for CosmicBluetoothApplet {
} }
Command::none() Command::none()
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
let button_style = Button::Custom { let button_style = Button::Custom {
active: |t| iced_style::button::Appearance { active: |t| iced_style::button::Appearance {
border_radius: BorderRadius::from(0.0), border_radius: BorderRadius::from(0.0),
@ -286,230 +285,228 @@ impl Application for CosmicBluetoothApplet {
..t.hovered(&Button::Text) ..t.hovered(&Button::Text)
}, },
}; };
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), self.applet_helper
SurfaceIdWrapper::Window(_) => self
.applet_helper
.icon_button(&self.icon_name) .icon_button(&self.icon_name)
.on_press(Message::TogglePopup) .on_press(Message::TogglePopup)
.into(), .into()
SurfaceIdWrapper::Popup(_) => { } 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
.request_confirmation .request_confirmation
.as_ref() .as_ref()
.map_or(false, |(dev, _, _)| d.address == dev.address) .map_or(false, |(dev, _, _)| d.address == dev.address)
}) { }) {
let mut row = row![ let mut row = row![
icon(dev.icon.as_str(), 16).style(Svg::Symbolic), icon(dev.icon.as_str(), 16).style(Svg::Symbolic),
text(dev.name.clone()) text(dev.name.clone())
.size(14) .size(14)
.horizontal_alignment(Horizontal::Left)
.vertical_alignment(Vertical::Center)
.width(Length::Fill)
]
.align_items(Alignment::Center)
.spacing(12);
match &dev.status {
BluerDeviceStatus::Connected => {
row = row.push(
text(fl!("connected"))
.size(14)
.horizontal_alignment(Horizontal::Right)
.vertical_alignment(Vertical::Center),
);
}
BluerDeviceStatus::Paired => {}
BluerDeviceStatus::Connecting | BluerDeviceStatus::Disconnecting => {
row = row.push(
icon("process-working-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
);
}
BluerDeviceStatus::Disconnected | BluerDeviceStatus::Pairing => continue,
};
known_bluetooth = known_bluetooth.push(
button(APPLET_BUTTON_THEME)
.custom(vec![row.into()])
.style(APPLET_BUTTON_THEME)
.on_press(match dev.status {
BluerDeviceStatus::Connected => {
Message::Request(BluerRequest::DisconnectDevice(dev.address))
}
BluerDeviceStatus::Disconnected => {
Message::Request(BluerRequest::PairDevice(dev.address))
}
BluerDeviceStatus::Paired => {
Message::Request(BluerRequest::ConnectDevice(dev.address))
}
BluerDeviceStatus::Connecting => {
Message::Request(BluerRequest::CancelConnect(dev.address))
}
BluerDeviceStatus::Disconnecting => Message::Ignore, // Start connecting?
BluerDeviceStatus::Pairing => Message::Ignore, // Cancel pairing?
})
.width(Length::Fill),
);
}
let mut content = column![
column![
toggler(fl!("bluetooth"), self.bluer_state.bluetooth_enabled, |m| {
Message::Request(BluerRequest::SetBluetoothEnabled(m))
},)
.text_size(14)
.width(Length::Fill),
// these are not in the UX mockup, but they are useful imo
toggler(fl!("discoverable"), self.bluer_state.discoverable, |m| {
Message::Request(BluerRequest::SetDiscoverable(m))
},)
.text_size(14)
.width(Length::Fill),
toggler(fl!("pairable"), self.bluer_state.pairable, |m| {
Message::Request(BluerRequest::SetPairable(m))
},)
.text_size(14)
.width(Length::Fill)
]
.spacing(8)
.padding([0, 12]),
divider::horizontal::light(),
known_bluetooth,
]
.align_items(Alignment::Center)
.spacing(8)
.padding([8, 0]);
let dropdown_icon = if self.show_visible_devices {
"go-down-symbolic"
} else {
"go-next-symbolic"
};
let available_connections_btn = button(Button::Secondary)
.custom(
vec![
text(fl!("other-devices"))
.size(14)
.width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),
container(
icon(dropdown_icon, 14)
.style(Svg::Symbolic)
.width(Length::Units(14))
.height(Length::Units(14)),
)
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::ToggleVisibleDevices(!self.show_visible_devices));
content = content.push(available_connections_btn);
let mut list_column: Vec<Element<'_, Message>> =
Vec::with_capacity(self.bluer_state.devices.len());
if let Some((device, pin, _)) = self.request_confirmation.as_ref() {
let row = column![
icon(device.icon.as_str(), 16).style(Svg::Symbolic),
text(&device.name)
.horizontal_alignment(Horizontal::Left)
.vertical_alignment(Vertical::Center)
.width(Length::Fill),
text(fl!(
"confirm-pin",
HashMap::from_iter(vec![("deviceName", device.name.clone())])
))
.horizontal_alignment(Horizontal::Left) .horizontal_alignment(Horizontal::Left)
.vertical_alignment(Vertical::Center) .vertical_alignment(Vertical::Center)
.width(Length::Fill) .width(Length::Fill)
.size(14), ]
text(pin) .align_items(Alignment::Center)
.horizontal_alignment(Horizontal::Center) .spacing(12);
.vertical_alignment(Vertical::Center)
.width(Length::Fill) match &dev.status {
.size(32), BluerDeviceStatus::Connected => {
row![ row = row.push(
button(Button::Secondary) text(fl!("connected"))
.custom( .size(14)
vec![text(fl!("cancel")) .horizontal_alignment(Horizontal::Right)
.size(14) .vertical_alignment(Vertical::Center),
.width(Length::Fill) );
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::Cancel)
.width(Length::Fill),
button(Button::Secondary)
.custom(
vec![text(fl!("confirm"))
.size(14)
.width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::Confirm)
.width(Length::Fill),
]
]
.padding([0, 24])
.spacing(12);
list_column.push(row.into());
}
let mut visible_devices_count = 0;
if self.show_visible_devices {
if self.bluer_state.bluetooth_enabled {
let mut visible_devices = column![];
for dev in self.bluer_state.devices.iter().filter(|d| {
matches!(
d.status,
BluerDeviceStatus::Disconnected | BluerDeviceStatus::Pairing
) && !self
.request_confirmation
.as_ref()
.map_or(false, |(dev, _, _)| d.address == dev.address)
}) {
let row = row![
icon(dev.icon.as_str(), 16).style(Svg::Symbolic),
text(dev.name.clone())
.horizontal_alignment(Horizontal::Left)
.size(14),
]
.width(Length::Fill)
.align_items(Alignment::Center)
.spacing(12);
visible_devices = visible_devices.push(
button(APPLET_BUTTON_THEME)
.custom(vec![row.width(Length::Fill).into()])
.on_press(Message::Request(BluerRequest::PairDevice(
dev.address.clone(),
)))
.width(Length::Fill),
);
visible_devices_count += 1;
}
list_column.push(visible_devices.into());
} }
BluerDeviceStatus::Paired => {}
BluerDeviceStatus::Connecting | BluerDeviceStatus::Disconnecting => {
row = row.push(
icon("process-working-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
);
}
BluerDeviceStatus::Disconnected | BluerDeviceStatus::Pairing => continue,
};
known_bluetooth = known_bluetooth.push(
button(APPLET_BUTTON_THEME)
.custom(vec![row.into()])
.style(APPLET_BUTTON_THEME)
.on_press(match dev.status {
BluerDeviceStatus::Connected => {
Message::Request(BluerRequest::DisconnectDevice(dev.address))
}
BluerDeviceStatus::Disconnected => {
Message::Request(BluerRequest::PairDevice(dev.address))
}
BluerDeviceStatus::Paired => {
Message::Request(BluerRequest::ConnectDevice(dev.address))
}
BluerDeviceStatus::Connecting => {
Message::Request(BluerRequest::CancelConnect(dev.address))
}
BluerDeviceStatus::Disconnecting => Message::Ignore, // Start connecting?
BluerDeviceStatus::Pairing => Message::Ignore, // Cancel pairing?
})
.width(Length::Fill),
);
}
let mut content = column![
column![
toggler(fl!("bluetooth"), self.bluer_state.bluetooth_enabled, |m| {
Message::Request(BluerRequest::SetBluetoothEnabled(m))
},)
.text_size(14)
.width(Length::Fill),
// these are not in the UX mockup, but they are useful imo
toggler(fl!("discoverable"), self.bluer_state.discoverable, |m| {
Message::Request(BluerRequest::SetDiscoverable(m))
},)
.text_size(14)
.width(Length::Fill),
toggler(fl!("pairable"), self.bluer_state.pairable, |m| {
Message::Request(BluerRequest::SetPairable(m))
},)
.text_size(14)
.width(Length::Fill)
]
.spacing(8)
.padding([0, 12]),
divider::horizontal::light(),
known_bluetooth,
]
.align_items(Alignment::Center)
.spacing(8)
.padding([8, 0]);
let dropdown_icon = if self.show_visible_devices {
"go-down-symbolic"
} else {
"go-next-symbolic"
};
let available_connections_btn = button(Button::Secondary)
.custom(
vec![
text(fl!("other-devices"))
.size(14)
.width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),
container(
icon(dropdown_icon, 14)
.style(Svg::Symbolic)
.width(Length::Units(14))
.height(Length::Units(14)),
)
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::ToggleVisibleDevices(!self.show_visible_devices));
content = content.push(available_connections_btn);
let mut list_column: Vec<Element<'_, Message>> =
Vec::with_capacity(self.bluer_state.devices.len());
if let Some((device, pin, _)) = self.request_confirmation.as_ref() {
let row = column![
icon(device.icon.as_str(), 16).style(Svg::Symbolic),
text(&device.name)
.horizontal_alignment(Horizontal::Left)
.vertical_alignment(Vertical::Center)
.width(Length::Fill),
text(fl!(
"confirm-pin",
HashMap::from_iter(vec![("deviceName", device.name.clone())])
))
.horizontal_alignment(Horizontal::Left)
.vertical_alignment(Vertical::Center)
.width(Length::Fill)
.size(14),
text(pin)
.horizontal_alignment(Horizontal::Center)
.vertical_alignment(Vertical::Center)
.width(Length::Fill)
.size(32),
row![
button(Button::Secondary)
.custom(
vec![text(fl!("cancel"))
.size(14)
.width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::Cancel)
.width(Length::Fill),
button(Button::Secondary)
.custom(
vec![text(fl!("confirm"))
.size(14)
.width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::Confirm)
.width(Length::Fill),
]
]
.padding([0, 24])
.spacing(12);
list_column.push(row.into());
}
let mut visible_devices_count = 0;
if self.show_visible_devices {
if self.bluer_state.bluetooth_enabled {
let mut visible_devices = column![];
for dev in self.bluer_state.devices.iter().filter(|d| {
matches!(
d.status,
BluerDeviceStatus::Disconnected | BluerDeviceStatus::Pairing
) && !self
.request_confirmation
.as_ref()
.map_or(false, |(dev, _, _)| d.address == dev.address)
}) {
let row = row![
icon(dev.icon.as_str(), 16).style(Svg::Symbolic),
text(dev.name.clone())
.horizontal_alignment(Horizontal::Left)
.size(14),
]
.width(Length::Fill)
.align_items(Alignment::Center)
.spacing(12);
visible_devices = visible_devices.push(
button(APPLET_BUTTON_THEME)
.custom(vec![row.width(Length::Fill).into()])
.on_press(Message::Request(BluerRequest::PairDevice(
dev.address.clone(),
)))
.width(Length::Fill),
);
visible_devices_count += 1;
}
list_column.push(visible_devices.into());
} }
let item_counter = visible_devices_count }
let item_counter = visible_devices_count
// request confirmation is pretty big // request confirmation is pretty big
+ if self.request_confirmation.is_some() { + if self.request_confirmation.is_some() {
5 5
@ -517,15 +514,14 @@ impl Application for CosmicBluetoothApplet {
0 0
}; };
if item_counter > 10 { if item_counter > 10 {
content = content.push( content = content.push(
scrollable(Column::with_children(list_column)).height(Length::Units(300)), scrollable(Column::with_children(list_column)).height(Length::Units(300)),
); );
} 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.applet_helper.popup_container(content).into()
} }
} }
@ -537,7 +533,7 @@ impl Application for CosmicBluetoothApplet {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }

View file

@ -8,7 +8,6 @@ edition = "2021"
[dependencies] [dependencies]
zbus = "3.4" zbus = "3.4"
libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "69bffe5" }
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"] }

View file

@ -3,7 +3,6 @@ use crate::fl;
use crate::graphics::{get_current_graphics, set_graphics, Graphics}; use crate::graphics::{get_current_graphics, set_graphics, Graphics};
use cosmic::applet::CosmicAppletHelper; use cosmic::applet::CosmicAppletHelper;
use cosmic::iced::wayland::popup::{destroy_popup, get_popup}; use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
use cosmic::iced::wayland::SurfaceIdWrapper;
use cosmic::iced_native::alignment::Horizontal; use cosmic::iced_native::alignment::Horizontal;
use cosmic::iced_native::Alignment; use cosmic::iced_native::Alignment;
use cosmic::iced_style::application::{self, Appearance}; use cosmic::iced_style::application::{self, Appearance};
@ -121,7 +120,7 @@ impl Application for Window {
Message::CurrentGraphics(match cur_graphics { Message::CurrentGraphics(match cur_graphics {
Ok(g) => Some(g), Ok(g) => Some(g),
Err(err) => { Err(err) => {
dbg!(err); eprintln!("{err}");
None None
} }
}) })
@ -184,10 +183,9 @@ impl Application for Window {
Command::none() Command::none()
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), match self.applet_helper.anchor {
SurfaceIdWrapper::Window(_) => match self.applet_helper.anchor {
PanelAnchor::Left | PanelAnchor::Right => self PanelAnchor::Left | PanelAnchor::Right => self
.applet_helper .applet_helper
.icon_button("input-gaming-symbolic") .icon_button("input-gaming-symbolic")
@ -219,187 +217,185 @@ impl Application for Window {
.width(Length::Shrink) .width(Length::Shrink)
.height(Length::Shrink) .height(Length::Shrink)
.into(), .into(),
}, }
SurfaceIdWrapper::Popup(_) => { } else {
let content_list = vec![ let content_list = vec![
button(APPLET_BUTTON_THEME) button(APPLET_BUTTON_THEME)
.custom(vec![row![ .custom(vec![row![
column![ column![
text(format!("{} {}", fl!("integrated"), fl!("graphics"))).size(14), text(format!("{} {}", fl!("integrated"), fl!("graphics"))).size(14),
text(fl!("integrated-desc")).size(12) text(fl!("integrated-desc")).size(12)
] ]
.width(Length::Fill), .width(Length::Fill),
icon( icon(
match self.graphics_mode { match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Integrated,
..
}) => "process-working-symbolic",
_ => "emblem-ok-symbolic",
},
12
)
.size(12)
.style(match self.graphics_mode {
Some(GraphicsMode::CurrentGraphicsMode(Graphics::Integrated)) =>
Svg::SymbolicActive,
Some(GraphicsMode::AppliedGraphicsMode(Graphics::Integrated)) =>
Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode { Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Integrated, new: Graphics::Integrated,
.. ..
}) => Svg::Symbolic, }) => "process-working-symbolic",
_ => Svg::Default, _ => "emblem-ok-symbolic",
},), },
] 12
.align_items(Alignment::Center) )
.into()]) .size(12)
.padding([8, 24]) .style(match self.graphics_mode {
.on_press(Message::SelectGraphicsMode(Graphics::Integrated)) Some(GraphicsMode::CurrentGraphicsMode(Graphics::Integrated)) =>
.width(Length::Fill) Svg::SymbolicActive,
.into(), Some(GraphicsMode::AppliedGraphicsMode(Graphics::Integrated)) =>
button(APPLET_BUTTON_THEME) Svg::SymbolicActive,
.custom(vec![row![ Some(GraphicsMode::SelectedGraphicsMode {
column![ new: Graphics::Integrated,
text(format!("{} {}", fl!("nvidia"), fl!("graphics"))).size(14), ..
] }) => Svg::Symbolic,
_ => Svg::Default,
},),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectGraphicsMode(Graphics::Integrated))
.width(Length::Fill)
.into(),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![text(format!("{} {}", fl!("nvidia"), fl!("graphics"))).size(14),]
.width(Length::Fill), .width(Length::Fill),
icon( icon(
match self.graphics_mode { match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Nvidia,
..
}) => "process-working-symbolic",
_ => "emblem-ok-symbolic",
},
12
)
.size(12)
.style(match self.graphics_mode {
Some(GraphicsMode::CurrentGraphicsMode(Graphics::Nvidia)) =>
Svg::SymbolicActive,
Some(GraphicsMode::AppliedGraphicsMode(Graphics::Nvidia)) =>
Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode { Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Nvidia, new: Graphics::Nvidia,
.. ..
}) => Svg::Symbolic, }) => "process-working-symbolic",
_ => Svg::Default, _ => "emblem-ok-symbolic",
}), },
12
)
.size(12)
.style(match self.graphics_mode {
Some(GraphicsMode::CurrentGraphicsMode(Graphics::Nvidia)) =>
Svg::SymbolicActive,
Some(GraphicsMode::AppliedGraphicsMode(Graphics::Nvidia)) =>
Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Nvidia,
..
}) => Svg::Symbolic,
_ => Svg::Default,
}),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectGraphicsMode(Graphics::Nvidia))
.width(Length::Fill)
.into(),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(format!("{} {}", fl!("hybrid"), fl!("graphics"))).size(14),
text(fl!("hybrid-desc")).size(12)
] ]
.align_items(Alignment::Center) .width(Length::Fill),
.into()]) icon(
.padding([8, 24]) match self.graphics_mode {
.on_press(Message::SelectGraphicsMode(Graphics::Nvidia))
.width(Length::Fill)
.into(),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(format!("{} {}", fl!("hybrid"), fl!("graphics"))).size(14),
text(fl!("hybrid-desc")).size(12)
]
.width(Length::Fill),
icon(
match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Hybrid,
..
}) => "process-working-symbolic",
_ => "emblem-ok-symbolic",
},
12
)
.size(12)
.style(match self.graphics_mode {
Some(GraphicsMode::CurrentGraphicsMode(Graphics::Hybrid)) =>
Svg::SymbolicActive,
Some(GraphicsMode::AppliedGraphicsMode(Graphics::Hybrid)) =>
Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode { Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Hybrid, new: Graphics::Hybrid,
.. ..
}) => Svg::Symbolic, }) => "process-working-symbolic",
_ => Svg::Default, _ => "emblem-ok-symbolic",
}) },
12
)
.size(12)
.style(match self.graphics_mode {
Some(GraphicsMode::CurrentGraphicsMode(Graphics::Hybrid)) =>
Svg::SymbolicActive,
Some(GraphicsMode::AppliedGraphicsMode(Graphics::Hybrid)) =>
Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Hybrid,
..
}) => Svg::Symbolic,
_ => Svg::Default,
})
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectGraphicsMode(Graphics::Hybrid))
.width(Length::Fill)
.into(),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(format!("{} {}", fl!("compute"), fl!("graphics"))).size(14),
text(fl!("compute-desc")).size(12)
] ]
.align_items(Alignment::Center) .width(Length::Fill),
.into()]) icon(
.padding([8, 24]) match self.graphics_mode {
.on_press(Message::SelectGraphicsMode(Graphics::Hybrid))
.width(Length::Fill)
.into(),
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(format!("{} {}", fl!("compute"), fl!("graphics"))).size(14),
text(fl!("compute-desc")).size(12)
]
.width(Length::Fill),
icon(
match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Compute,
..
}) => "process-working-symbolic",
_ => "emblem-ok-symbolic",
},
12
)
.size(12)
.style(match self.graphics_mode {
Some(GraphicsMode::CurrentGraphicsMode(Graphics::Compute)) =>
Svg::SymbolicActive,
Some(GraphicsMode::AppliedGraphicsMode(Graphics::Compute)) =>
Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode { Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Compute, new: Graphics::Compute,
.. ..
}) => Svg::Symbolic, }) => "process-working-symbolic",
_ => Svg::Default, _ => "emblem-ok-symbolic",
}), },
] 12
.align_items(Alignment::Center) )
.into()]) .size(12)
.padding([8, 24]) .style(match self.graphics_mode {
.on_press(Message::SelectGraphicsMode(Graphics::Compute)) Some(GraphicsMode::CurrentGraphicsMode(Graphics::Compute)) =>
.width(Length::Fill) Svg::SymbolicActive,
.into(), Some(GraphicsMode::AppliedGraphicsMode(Graphics::Compute)) =>
]; Svg::SymbolicActive,
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Compute,
..
}) => Svg::Symbolic,
_ => Svg::Default,
}),
]
.align_items(Alignment::Center)
.into()])
.padding([8, 24])
.on_press(Message::SelectGraphicsMode(Graphics::Compute))
.width(Length::Fill)
.into(),
];
self.applet_helper self.applet_helper
.popup_container( .popup_container(
column(vec![ column(vec![
text(fl!("graphics-mode")) text(fl!("graphics-mode"))
.width(Length::Fill) .width(Length::Fill)
.horizontal_alignment(Horizontal::Center) .horizontal_alignment(Horizontal::Center)
.size(24) .size(24)
.into(), .into(),
container(divider::horizontal::light()) container(divider::horizontal::light())
.padding([0, 12]) .padding([0, 12])
.width(Length::Fill) .width(Length::Fill)
.into(), .into(),
column(content_list).into(), column(content_list).into(),
]) ])
.padding([8, 0]) .padding([8, 0])
.spacing(12), .spacing(12),
) )
.into() .into()
}
} }
} }
fn close_requested(&self, id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, id: window::Id) -> Self::Message {
match id { if id != window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) | SurfaceIdWrapper::Window(_) => unimplemented!(), Message::PopupClosed(id)
SurfaceIdWrapper::Popup(id) => Message::PopupClosed(id), } else {
unimplemented!();
} }
} }
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style { fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
<Self::Theme as application::StyleSheet>::Style::Custom(|theme| Appearance { <Self::Theme as application::StyleSheet>::Style::Custom(|theme| Appearance {
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0), background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
text_color: theme.cosmic().on_bg_color().into(), text_color: theme.cosmic().background.on.into(),
}) })
} }

View file

@ -10,7 +10,6 @@ cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bi
# cosmic-dbus-networkmanager = { path = "../../../dbus-settings-bindings/networkmanager" } # cosmic-dbus-networkmanager = { path = "../../../dbus-settings-bindings/networkmanager" }
futures-util = "0.3.21" futures-util = "0.3.21"
libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "69bffe5" }
futures = "0.3" futures = "0.3"
zbus = { version = "3.7", default-features = false } zbus = { version = "3.7", default-features = false }
log = "0.4" log = "0.4"

View file

@ -2,10 +2,7 @@ use cosmic::iced_style;
use cosmic::{ use cosmic::{
applet::CosmicAppletHelper, applet::CosmicAppletHelper,
iced::{ iced::{
wayland::{ wayland::popup::{destroy_popup, get_popup},
popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
},
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, Application, Color, Command, Length, Subscription,
}, },
@ -34,7 +31,8 @@ use crate::{
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
let helper = CosmicAppletHelper::default(); let helper = CosmicAppletHelper::default();
CosmicNetworkApplet::run(helper.window_settings()) let settings = helper.window_settings();
CosmicNetworkApplet::run(settings)
} }
#[derive(Debug)] #[derive(Debug)]
@ -338,7 +336,7 @@ impl Application for CosmicNetworkApplet {
} }
Command::none() Command::none()
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
let button_style = Button::Custom { let button_style = Button::Custom {
active: |t| iced_style::button::Appearance { active: |t| iced_style::button::Appearance {
border_radius: BorderRadius::from(0.0), border_radius: BorderRadius::from(0.0),
@ -349,349 +347,344 @@ impl Application for CosmicNetworkApplet {
..t.hovered(&Button::Text) ..t.hovered(&Button::Text)
}, },
}; };
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), self.applet_helper
SurfaceIdWrapper::Window(_) => self
.applet_helper
.icon_button(&self.icon_name) .icon_button(&self.icon_name)
.on_press(Message::TogglePopup) .on_press(Message::TogglePopup)
.into(), .into()
SurfaceIdWrapper::Popup(_) => { } 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 {
match conn { match conn {
ActiveConnectionInfo::Vpn { name, ip_addresses } => { ActiveConnectionInfo::Vpn { name, ip_addresses } => {
let mut ipv4 = Vec::with_capacity(ip_addresses.len()); let mut ipv4 = Vec::with_capacity(ip_addresses.len());
for addr in ip_addresses { for addr in ip_addresses {
ipv4.push( ipv4.push(
text(format!("{}: {}", fl!("ipv4"), addr.to_string())) text(format!("{}: {}", fl!("ipv4"), addr.to_string()))
.size(12) .size(12)
.into(), .into(),
); );
}
vpn_ethernet_col = vpn_ethernet_col
.push(column![text(name), Column::with_children(ipv4)].spacing(4));
} }
ActiveConnectionInfo::Wired { vpn_ethernet_col = vpn_ethernet_col
name, .push(column![text(name), Column::with_children(ipv4)].spacing(4));
hw_address: _, }
speed, ActiveConnectionInfo::Wired {
ip_addresses, name,
} => { hw_address: _,
let mut ipv4 = Vec::with_capacity(ip_addresses.len()); speed,
for addr in ip_addresses { ip_addresses,
ipv4.push( } => {
text(format!("{}: {}", fl!("ipv4"), addr.to_string())) let mut ipv4 = Vec::with_capacity(ip_addresses.len());
.size(12) for addr in ip_addresses {
.into(), ipv4.push(
); text(format!("{}: {}", fl!("ipv4"), addr.to_string()))
} .size(12)
vpn_ethernet_col = vpn_ethernet_col.push( .into(),
column![ );
row![ }
text(name), vpn_ethernet_col = vpn_ethernet_col.push(
text(format!("{speed} {}", fl!("megabits-per-second"))) column![
] row![
.spacing(16), text(name),
Column::with_children(ipv4), text(format!("{speed} {}", fl!("megabits-per-second")))
] ]
.spacing(4), .spacing(16),
Column::with_children(ipv4),
]
.spacing(4),
);
}
ActiveConnectionInfo::WiFi {
name,
ip_addresses,
state,
..
} => {
let mut ipv4 = Vec::with_capacity(ip_addresses.len());
for addr in ip_addresses {
ipv4.push(
text(format!("{}: {}", fl!("ipv4"), addr.to_string()))
.size(12)
.into(),
); );
} }
ActiveConnectionInfo::WiFi { let mut btn_content = vec![
name, icon("network-wireless-symbolic", 24)
ip_addresses,
state,
..
} => {
let mut ipv4 = Vec::with_capacity(ip_addresses.len());
for addr in ip_addresses {
ipv4.push(
text(format!("{}: {}", fl!("ipv4"), addr.to_string()))
.size(12)
.into(),
);
}
let mut btn_content = vec![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
column![text(name).size(14), Column::with_children(ipv4)]
.width(Length::Fill)
.into(),
];
match state {
ActiveConnectionState::Activating
| ActiveConnectionState::Deactivating => {
btn_content.push(
icon("process-working-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
);
}
ActiveConnectionState::Activated => btn_content.push(
text(format!("{}", fl!("connected")))
.size(14)
.horizontal_alignment(Horizontal::Right)
.vertical_alignment(Vertical::Center)
.into(),
),
_ => {}
};
known_wifi = known_wifi.push(
column![button(Button::Secondary)
.custom(btn_content)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::Disconnect(name.clone()))]
.align_items(Alignment::Center),
);
}
};
}
for known in &self.nm_state.known_access_points {
let mut btn_content = vec![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
text(&known.ssid).size(14).width(Length::Fill).into(),
];
if known.working {
btn_content.push(
icon("process-working-symbolic", 24)
.style(Svg::Symbolic) .style(Svg::Symbolic)
.width(Length::Units(24)) .width(Length::Units(24))
.height(Length::Units(24)) .height(Length::Units(24))
.into(), .into(),
); column![text(name).size(14), Column::with_children(ipv4)]
}
let mut btn = button(Button::Secondary)
.custom(btn_content)
.padding([8, 24])
.width(Length::Fill)
.style(button_style.clone());
btn = match known.state {
DeviceState::Failed
| DeviceState::Unknown
| DeviceState::Unmanaged
| DeviceState::Disconnected
| DeviceState::NeedAuth => {
btn.on_press(Message::ActivateKnownWifi(known.ssid.clone()))
}
DeviceState::Activated => {
btn.on_press(Message::Disconnect(known.ssid.clone()))
}
_ => btn,
};
known_wifi = known_wifi.push(row![btn].align_items(Alignment::Center));
}
let mut content = column![
vpn_ethernet_col,
container(
toggler(fl!("airplane-mode"), self.nm_state.airplane_mode, |m| {
Message::ToggleAirplaneMode(m)
})
.width(Length::Fill)
)
.padding([0, 12]),
divider::horizontal::light(),
container(
toggler(fl!("wifi"), self.nm_state.wifi_enabled, |m| {
Message::ToggleWiFi(m)
})
.width(Length::Fill)
)
.padding([0, 12]),
divider::horizontal::light(),
known_wifi,
]
.align_items(Alignment::Center)
.spacing(8)
.padding([8, 0]);
let dropdown_icon = if self.show_visible_networks {
"go-down-symbolic"
} else {
"go-next-symbolic"
};
let available_connections_btn = button(Button::Secondary)
.custom(
vec![
text(fl!("visible-wireless-networks"))
.size(14)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(), .into(),
container( ];
icon(dropdown_icon, 14) match state {
.style(Svg::Symbolic) ActiveConnectionState::Activating
.width(Length::Units(14)) | ActiveConnectionState::Deactivating => {
.height(Length::Units(14)), btn_content.push(
)
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::ToggleVisibleNetworks);
content = content.push(available_connections_btn);
if self.show_visible_networks {
if let Some(new_conn_state) = self.new_connection.as_ref() {
match new_conn_state {
NewConnectionState::EnterPassword {
access_point,
password,
} => {
let id = row![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
text(&access_point.ssid).size(14),
]
.align_items(Alignment::Center)
.width(Length::Fill)
.padding([0, 24])
.spacing(12);
content = content.push(id);
let col = column![
text(fl!("enter-password")),
text_input("", password, Message::Password)
.on_submit(Message::SubmitPassword)
.password(),
container(text(fl!("router-wps-button"))).padding(8),
row![
button(Button::Secondary)
.custom(vec![container(text(fl!("cancel")))
.padding([0, 24])
.into()])
.on_press(Message::CancelNewConnection),
button(Button::Secondary)
.custom(vec![container(text(fl!("connect")))
.padding([0, 24])
.into()])
.on_press(Message::SubmitPassword)
]
.spacing(24)
]
.spacing(8)
.padding([0, 48])
.align_items(Alignment::Center);
content = content.push(col);
}
NewConnectionState::Waiting(access_point) => {
let id = row![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
text(&access_point.ssid).size(14),
]
.align_items(Alignment::Center)
.width(Length::Fill)
.spacing(12);
let connecting = row![
id,
icon("process-working-symbolic", 24) icon("process-working-symbolic", 24)
.style(Svg::Symbolic) .style(Svg::Symbolic)
.width(Length::Units(24)) .width(Length::Units(24))
.height(Length::Units(24)),
]
.spacing(8)
.padding([0, 24]);
content = content.push(connecting);
}
NewConnectionState::Failure(access_point) => {
let id = row![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
text(&access_point.ssid).size(14),
]
.align_items(Alignment::Center)
.width(Length::Fill)
.padding([0, 24])
.spacing(12);
content = content.push(id);
let col = column![
text(fl!("unable-to-connect")),
text(fl!("check-wifi-connection")),
row![
button(Button::Secondary)
.custom(vec![container(text("Cancel"))
.padding([0, 24])
.into()])
.on_press(Message::CancelNewConnection),
button(Button::Secondary)
.custom(vec![container(text("Connect"))
.padding([0, 24])
.into()])
.on_press(Message::SelectWirelessAccessPoint(
access_point.clone()
))
]
.spacing(24)
]
.spacing(16)
.padding([0, 48])
.align_items(Alignment::Center);
content = content.push(col);
}
}
} else if self.nm_state.wifi_enabled {
let mut list_col =
Vec::with_capacity(self.nm_state.wireless_access_points.len());
for ap in &self.nm_state.wireless_access_points {
if self
.nm_state
.active_conns
.iter()
.any(|a| ap.ssid == a.name())
{
continue;
}
let button = button(button_style)
.custom(vec![row![
icon("network-wireless-symbolic", 16)
.style(Svg::Symbolic)
.width(Length::Units(16))
.height(Length::Units(16)),
text(&ap.ssid)
.size(14)
.height(Length::Units(24)) .height(Length::Units(24))
.vertical_alignment(Vertical::Center) .into(),
] );
.align_items(Alignment::Center) }
.spacing(12) ActiveConnectionState::Activated => btn_content.push(
.into()]) text(format!("{}", fl!("connected")))
.on_press(Message::SelectWirelessAccessPoint(ap.clone())) .size(14)
.width(Length::Fill) .horizontal_alignment(Horizontal::Right)
.padding([8, 24]); .vertical_alignment(Vertical::Center)
list_col.push(button.into()); .into(),
} ),
content = content.push( _ => {}
scrollable(Column::with_children(list_col)).height(Length::Units(300)), };
known_wifi = known_wifi.push(
column![button(Button::Secondary)
.custom(btn_content)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::Disconnect(name.clone()))]
.align_items(Alignment::Center),
); );
} }
} };
self.applet_helper.popup_container(content).into()
} }
for known in &self.nm_state.known_access_points {
let mut btn_content = vec![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
text(&known.ssid).size(14).width(Length::Fill).into(),
];
if known.working {
btn_content.push(
icon("process-working-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
);
}
let mut btn = button(Button::Secondary)
.custom(btn_content)
.padding([8, 24])
.width(Length::Fill)
.style(button_style.clone());
btn = match known.state {
DeviceState::Failed
| DeviceState::Unknown
| DeviceState::Unmanaged
| DeviceState::Disconnected
| DeviceState::NeedAuth => {
btn.on_press(Message::ActivateKnownWifi(known.ssid.clone()))
}
DeviceState::Activated => btn.on_press(Message::Disconnect(known.ssid.clone())),
_ => btn,
};
known_wifi = known_wifi.push(row![btn].align_items(Alignment::Center));
}
let mut content = column![
vpn_ethernet_col,
container(
toggler(fl!("airplane-mode"), self.nm_state.airplane_mode, |m| {
Message::ToggleAirplaneMode(m)
})
.width(Length::Fill)
)
.padding([0, 12]),
divider::horizontal::light(),
container(
toggler(fl!("wifi"), self.nm_state.wifi_enabled, |m| {
Message::ToggleWiFi(m)
})
.width(Length::Fill)
)
.padding([0, 12]),
divider::horizontal::light(),
known_wifi,
]
.align_items(Alignment::Center)
.spacing(8)
.padding([8, 0]);
let dropdown_icon = if self.show_visible_networks {
"go-down-symbolic"
} else {
"go-next-symbolic"
};
let available_connections_btn = button(Button::Secondary)
.custom(
vec![
text(fl!("visible-wireless-networks"))
.size(14)
.width(Length::Fill)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
.into(),
container(
icon(dropdown_icon, 14)
.style(Svg::Symbolic)
.width(Length::Units(14))
.height(Length::Units(14)),
)
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.width(Length::Units(24))
.height(Length::Units(24))
.into(),
]
.into(),
)
.padding([8, 24])
.style(button_style.clone())
.on_press(Message::ToggleVisibleNetworks);
content = content.push(available_connections_btn);
if self.show_visible_networks {
if let Some(new_conn_state) = self.new_connection.as_ref() {
match new_conn_state {
NewConnectionState::EnterPassword {
access_point,
password,
} => {
let id = row![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
text(&access_point.ssid).size(14),
]
.align_items(Alignment::Center)
.width(Length::Fill)
.padding([0, 24])
.spacing(12);
content = content.push(id);
let col = column![
text(fl!("enter-password")),
text_input("", password, Message::Password)
.on_submit(Message::SubmitPassword)
.password(),
container(text(fl!("router-wps-button"))).padding(8),
row![
button(Button::Secondary)
.custom(vec![container(text(fl!("cancel")))
.padding([0, 24])
.into()])
.on_press(Message::CancelNewConnection),
button(Button::Secondary)
.custom(vec![container(text(fl!("connect")))
.padding([0, 24])
.into()])
.on_press(Message::SubmitPassword)
]
.spacing(24)
]
.spacing(8)
.padding([0, 48])
.align_items(Alignment::Center);
content = content.push(col);
}
NewConnectionState::Waiting(access_point) => {
let id = row![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
text(&access_point.ssid).size(14),
]
.align_items(Alignment::Center)
.width(Length::Fill)
.spacing(12);
let connecting = row![
id,
icon("process-working-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
]
.spacing(8)
.padding([0, 24]);
content = content.push(connecting);
}
NewConnectionState::Failure(access_point) => {
let id = row![
icon("network-wireless-symbolic", 24)
.style(Svg::Symbolic)
.width(Length::Units(24))
.height(Length::Units(24)),
text(&access_point.ssid).size(14),
]
.align_items(Alignment::Center)
.width(Length::Fill)
.padding([0, 24])
.spacing(12);
content = content.push(id);
let col = column![
text(fl!("unable-to-connect")),
text(fl!("check-wifi-connection")),
row![
button(Button::Secondary)
.custom(vec![container(text("Cancel"))
.padding([0, 24])
.into()])
.on_press(Message::CancelNewConnection),
button(Button::Secondary)
.custom(vec![container(text("Connect"))
.padding([0, 24])
.into()])
.on_press(Message::SelectWirelessAccessPoint(
access_point.clone()
))
]
.spacing(24)
]
.spacing(16)
.padding([0, 48])
.align_items(Alignment::Center);
content = content.push(col);
}
}
} else if self.nm_state.wifi_enabled {
let mut list_col =
Vec::with_capacity(self.nm_state.wireless_access_points.len());
for ap in &self.nm_state.wireless_access_points {
if self
.nm_state
.active_conns
.iter()
.any(|a| ap.ssid == a.name())
{
continue;
}
let button = button(button_style)
.custom(vec![row![
icon("network-wireless-symbolic", 16)
.style(Svg::Symbolic)
.width(Length::Units(16))
.height(Length::Units(16)),
text(&ap.ssid)
.size(14)
.height(Length::Units(24))
.vertical_alignment(Vertical::Center)
]
.align_items(Alignment::Center)
.spacing(12)
.into()])
.on_press(Message::SelectWirelessAccessPoint(ap.clone()))
.width(Length::Fill)
.padding([8, 24]);
list_col.push(button.into());
}
content = content.push(
scrollable(Column::with_children(list_col)).height(Length::Units(300)),
);
}
}
self.applet_helper.popup_container(content).into()
} }
} }
@ -703,7 +696,7 @@ impl Application for CosmicNetworkApplet {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }

View file

@ -1,7 +1,6 @@
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME}; use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
use cosmic::iced::wayland::{ use cosmic::iced::wayland::{
popup::{destroy_popup, get_popup}, popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
}; };
use cosmic::iced::{ use cosmic::iced::{
widget::{button, column, row, text, Row, Space}, widget::{button, column, row, text, Row, Space},
@ -65,7 +64,7 @@ impl Application for Notifications {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }
@ -112,57 +111,54 @@ impl Application for Notifications {
} }
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), self.applet_helper
SurfaceIdWrapper::Window(_) => self
.applet_helper
.icon_button(&self.icon_name) .icon_button(&self.icon_name)
.on_press(Message::TogglePopup) .on_press(Message::TogglePopup)
.into(), .into()
SurfaceIdWrapper::Popup(_) => { } else {
let do_not_disturb = let do_not_disturb =
row![ row![
toggler(String::from("Do Not Disturb"), self.do_not_disturb, |b| { toggler(String::from("Do Not Disturb"), self.do_not_disturb, |b| {
Message::DoNotDisturb(b) Message::DoNotDisturb(b)
}) })
.width(Length::Fill) .width(Length::Fill)
]
.padding([0, 24]);
let settings =
row_button(vec!["Notification Settings...".into()]).on_press(Message::Settings);
let notifications = if self.notifications.len() == 0 {
row![
Space::with_width(Length::Fill),
column![text_icon(&self.icon_name, 40), "No Notifications"]
.align_items(Alignment::Center),
Space::with_width(Length::Fill)
]
.spacing(12)
} else {
row![text("TODO: make app worky with notifications")]
};
let main_content = column![
divider::horizontal::light(),
notifications,
divider::horizontal::light()
] ]
.padding([0, 24]) .padding([0, 24]);
.spacing(12);
let content = column![] let settings =
.align_items(Alignment::Start) row_button(vec!["Notification Settings...".into()]).on_press(Message::Settings);
.spacing(12)
.padding([12, 0])
.push(do_not_disturb)
.push(main_content)
.push(settings);
self.applet_helper.popup_container(content).into() let notifications = if self.notifications.len() == 0 {
} row![
Space::with_width(Length::Fill),
column![text_icon(&self.icon_name, 40), "No Notifications"]
.align_items(Alignment::Center),
Space::with_width(Length::Fill)
]
.spacing(12)
} else {
row![text("TODO: make app worky with notifications")]
};
let main_content = column![
divider::horizontal::light(),
notifications,
divider::horizontal::light()
]
.padding([0, 24])
.spacing(12);
let content = column![]
.align_items(Alignment::Start)
.spacing(12)
.padding([12, 0])
.push(do_not_disturb)
.push(main_content)
.push(settings);
self.applet_helper.popup_container(content).into()
} }
} }
} }

View file

@ -10,7 +10,6 @@ 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 = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "69bffe5" }
nix = "0.26.1" nix = "0.26.1"
zbus = "3.7" zbus = "3.7"
logind-zbus = "3.1" logind-zbus = "3.1"

View file

@ -2,7 +2,6 @@ use std::process;
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME}; use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
use cosmic::iced::wayland::popup::{destroy_popup, get_popup}; use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
use cosmic::iced::wayland::SurfaceIdWrapper;
use cosmic::iced_native::layout::Limits; use cosmic::iced_native::layout::Limits;
use cosmic::iced_native::widget::Space; use cosmic::iced_native::widget::Space;
use cosmic::widget::{button, divider, icon}; use cosmic::widget::{button, divider, icon};
@ -80,7 +79,7 @@ impl Application for Power {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }
@ -139,61 +138,56 @@ impl Application for Power {
} }
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), self.applet_helper
SurfaceIdWrapper::Window(_) => self
.applet_helper
.icon_button(&self.icon_name) .icon_button(&self.icon_name)
.on_press(Message::TogglePopup) .on_press(Message::TogglePopup)
.into(), .into()
SurfaceIdWrapper::Popup(_) => { } else {
let settings = row_button(vec!["Settings...".into()]).on_press(Message::Settings); let settings = row_button(vec!["Settings...".into()]).on_press(Message::Settings);
let session = column![ let session = column![
row_button(vec![ row_button(vec![
text_icon("system-lock-screen-symbolic", 24).into(), text_icon("system-lock-screen-symbolic", 24).into(),
"Lock Screen".into(), "Lock Screen".into(),
Space::with_width(Length::Fill).into(), Space::with_width(Length::Fill).into(),
"Super + Escape".into(), "Super + Escape".into(),
]) ])
.on_press(Message::Lock), .on_press(Message::Lock),
row_button(vec![ row_button(vec![
text_icon("system-log-out-symbolic", 24).into(), text_icon("system-log-out-symbolic", 24).into(),
"Log Out".into(), "Log Out".into(),
Space::with_width(Length::Fill).into(), Space::with_width(Length::Fill).into(),
"Ctrl + Alt + Delete".into(), "Ctrl + Alt + Delete".into(),
]) ])
.on_press(Message::LogOut), .on_press(Message::LogOut),
]; ];
let power = row![ let power = row![
power_buttons("system-lock-screen-symbolic", "Suspend") power_buttons("system-lock-screen-symbolic", "Suspend").on_press(Message::Suspend),
.on_press(Message::Suspend), power_buttons("system-restart-symbolic", "Restart").on_press(Message::Restart),
power_buttons("system-restart-symbolic", "Restart").on_press(Message::Restart), power_buttons("system-shutdown-symbolic", "Shutdown").on_press(Message::Shutdown),
power_buttons("system-shutdown-symbolic", "Shutdown") ]
.on_press(Message::Shutdown), .spacing(24)
] .padding([0, 24]);
.spacing(24)
.padding([0, 24]);
let content = column![ let content = column![
settings, settings,
container(divider::horizontal::light()) container(divider::horizontal::light())
.padding([0, 12]) .padding([0, 12])
.width(Length::Fill), .width(Length::Fill),
session, session,
container(divider::horizontal::light()) container(divider::horizontal::light())
.padding([0, 12]) .padding([0, 12])
.width(Length::Fill), .width(Length::Fill),
power power
] ]
.align_items(Alignment::Start) .align_items(Alignment::Start)
.spacing(12) .spacing(12)
.padding([8, 0]); .padding([8, 0]);
self.applet_helper.popup_container(content).into() self.applet_helper.popup_container(content).into()
}
} }
} }
} }

View file

@ -1,8 +1,5 @@
use cosmic::applet::{cosmic_panel_config::PanelAnchor, CosmicAppletHelper}; use cosmic::applet::{cosmic_panel_config::PanelAnchor, CosmicAppletHelper};
use cosmic::iced::wayland::{ use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
};
use cosmic::iced::{ use cosmic::iced::{
time, time,
wayland::InitialSurface, wayland::InitialSurface,
@ -28,9 +25,7 @@ pub fn main() -> cosmic::iced::Result {
s.iced_settings.min_size = Some((1, 1)); s.iced_settings.min_size = Some((1, 1));
s.iced_settings.max_size = None; s.iced_settings.max_size = None;
s.autosize = true; s.autosize = true;
s.size_limits = Limits::NONE s.size_limits = Limits::NONE.min_height(1).min_width(1);
.min_height(1)
.min_width(1);
} }
_ => {} _ => {}
}; };
@ -98,7 +93,7 @@ impl Application for Time {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore Message::Ignore
} }
@ -198,61 +193,57 @@ impl Application for Time {
} }
} }
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, id: window::Id) -> Element<Message> {
match id { if id == window::Id::new(0) {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), let button = button(
SurfaceIdWrapper::Window(_) => { if matches!(
let button = button( self.applet_helper.anchor,
if matches!( PanelAnchor::Top | PanelAnchor::Bottom
self.applet_helper.anchor, ) {
PanelAnchor::Top | PanelAnchor::Bottom column![text(self.now.format("%b %-d %-I:%M %p").to_string())]
) {
column![text(self.now.format("%b %-d %-I:%M %p").to_string())]
} else {
let mut date_time_col = column![
icon(
"emoji-recent-symbolic",
self.applet_helper.suggested_size().0
)
.style(theme::Svg::Symbolic),
text(self.now.format("%I").to_string()),
text(self.now.format("%M").to_string()),
text(self.now.format("%p").to_string()),
vertical_space(Length::Units(4)),
// TODO better calendar icon?
icon(
"calendar-go-today-symbolic",
self.applet_helper.suggested_size().0
)
.style(theme::Svg::Symbolic),
]
.align_items(Alignment::Center)
.spacing(4);
for d in self.now.format("%x").to_string().split("/") {
date_time_col = date_time_col.push(text(d.to_string()));
}
date_time_col
},
)
.on_press(Message::TogglePopup)
.style(theme::Button::Text);
if let Some(tracker) = self.rectangle_tracker.as_ref() {
tracker.container(0, button).into()
} else { } else {
button.into() let mut date_time_col = column![
} icon(
} "emoji-recent-symbolic",
SurfaceIdWrapper::Popup(_) => { self.applet_helper.suggested_size().0
let content = column![] )
.align_items(Alignment::Start) .style(theme::Svg::Symbolic),
.spacing(12) text(self.now.format("%I").to_string()),
.padding([24, 0]) text(self.now.format("%M").to_string()),
.push(text(&self.msg)) text(self.now.format("%p").to_string()),
.padding(8); vertical_space(Length::Units(4)),
// TODO better calendar icon?
icon(
"calendar-go-today-symbolic",
self.applet_helper.suggested_size().0
)
.style(theme::Svg::Symbolic),
]
.align_items(Alignment::Center)
.spacing(4);
for d in self.now.format("%x").to_string().split("/") {
date_time_col = date_time_col.push(text(d.to_string()));
}
date_time_col
},
)
.on_press(Message::TogglePopup)
.style(theme::Button::Text);
self.applet_helper.popup_container(content).into() if let Some(tracker) = self.rectangle_tracker.as_ref() {
tracker.container(0, button).into()
} else {
button.into()
} }
} else {
let content = column![]
.align_items(Alignment::Start)
.spacing(12)
.padding([24, 0])
.push(text(&self.msg))
.padding(8);
self.applet_helper.popup_container(content).into()
} }
} }
} }

View file

@ -4,7 +4,7 @@ use cosmic::applet::CosmicAppletHelper;
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::actions::window::SctkWindowSettings;
use cosmic::iced::wayland::{window::resize_window, InitialSurface, SurfaceIdWrapper}; use cosmic::iced::wayland::{window::resize_window, InitialSurface};
use cosmic::iced::widget::{column, container, row, text}; use cosmic::iced::widget::{column, container, row, text};
use cosmic::iced::{ use cosmic::iced::{
subscription, widget::button, window, Application, Command, Event::Mouse, Length, Settings, subscription, widget::button, window, Application, Command, Event::Mouse, Length, Settings,
@ -131,7 +131,7 @@ impl Application for IcedWorkspacesApplet {
Command::none() Command::none()
} }
fn view(&self, _id: SurfaceIdWrapper) -> Element<Message> { fn view(&self, _id: window::Id) -> Element<Message> {
if self.workspaces.is_empty() { if self.workspaces.is_empty() {
return row![].padding(8).into(); return row![].padding(8).into();
} }
@ -200,7 +200,7 @@ impl Application for IcedWorkspacesApplet {
self.theme self.theme
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message { fn close_requested(&self, _id: window::Id) -> Self::Message {
unimplemented!() unimplemented!()
} }

View file

@ -97,7 +97,7 @@ pub fn spawn_workspaces(tx: mpsc::Sender<WorkspaceList>) -> SyncSender<Workspace
.workspace_groups() .workspace_groups()
.iter() .iter()
.find_map(|g| { .find_map(|g| {
if g.output != state.expected_output { if !g.outputs.iter().any(|o| Some(o) == state.expected_output.as_ref()) {
return None; return None;
} }
g.workspaces g.workspaces
@ -179,7 +179,7 @@ impl State {
.workspace_groups() .workspace_groups()
.iter() .iter()
.filter_map(|g| { .filter_map(|g| {
if g.output == self.expected_output { if g.outputs.iter().any(|o| Some(o) == self.expected_output.as_ref()) {
Some(g.workspaces.iter().map(|w| { Some(g.workspaces.iter().map(|w| {
( (
w.name.clone(), w.name.clone(),

View file

@ -2,11 +2,12 @@ use cosmic::{
applet::CosmicAppletHelper, applet::CosmicAppletHelper,
iced::{ iced::{
self, self,
wayland::{InitialSurface, SurfaceIdWrapper}, wayland::InitialSurface,
Application, Application,
}, },
iced_sctk::layout::Limits, iced_sctk::layout::Limits,
iced_style::application, iced_style::application,
iced_native::window,
}; };
use freedesktop_desktop_entry::DesktopEntry; use freedesktop_desktop_entry::DesktopEntry;
use std::{env, fs, process::Command}; use std::{env, fs, process::Command};
@ -41,7 +42,7 @@ impl iced::Application for Button {
String::from("Button") String::from("Button")
} }
fn close_requested(&self, _id: SurfaceIdWrapper) -> Msg { fn close_requested(&self, _id: window::Id) -> Msg {
unimplemented!() unimplemented!()
} }
@ -65,7 +66,7 @@ impl iced::Application for Button {
} }
} }
fn view(&self, _id: SurfaceIdWrapper) -> cosmic::Element<Msg> { fn view(&self, _id: window::Id) -> 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)