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::popup::destroy_popup;
use cosmic::iced::wayland::popup::get_popup;
use cosmic::iced::wayland::SurfaceIdWrapper;
use cosmic::iced::widget::mouse_listener;
use cosmic::iced::widget::{column, row};
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 freedesktop_desktop_entry::DesktopEntry;
use iced::widget::container;
use iced::widget::horizontal_space;
use iced::Alignment;
use iced::Background;
use iced::Length;
@ -380,211 +378,202 @@ impl Application for CosmicAppList {
Command::none()
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => {
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,
);
fn view(&self, id: window::Id) -> Element<Message> {
if let Some(Toplevel {
toplevels,
desktop_info,
..
}) = self.toplevel_list.iter().find(|t| t.popup == Some(id))
{
let is_favorite = self.config.favorites.contains(&desktop_info.id)
|| self.config.favorites.contains(&desktop_info.name);
let dot_radius = 2;
let dots = (0..toplevels.len())
.into_iter()
.map(|_| {
container(vertical_space(Length::Units(0)))
.padding(dot_radius)
.style(<Self::Theme as container::StyleSheet>::Style::Custom(
|theme| container::Appearance {
text_color: Some(Color::TRANSPARENT),
background: Some(Background::Color(
theme.cosmic().on_bg_color().into(),
)),
border_radius: 4.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
))
.into()
})
.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)
.into()
}
PanelAnchor::Right => {
row(vec![cosmic_icon.into(), column(dots).spacing(4).into()])
.align_items(iced::Alignment::Center)
.spacing(4)
.into()
}
PanelAnchor::Top => {
column(vec![row(dots).spacing(4).into(), cosmic_icon.into()])
.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())),
);
}
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()))
});
// 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)
},
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 Container::new(Container::new(content.width(Length::Shrink).height(Length::Shrink)).style(
// cosmic::Container::Custom(|theme| container::Appearance {
// text_color: Some(theme.cosmic().on_bg_color().into()),
// background: Some(theme.extended_palette().background.base.color.into()),
// 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 {
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)]
let dot_radius = 2;
let dots = (0..toplevels.len())
.into_iter()
.map(|_| {
container(vertical_space(Length::Units(0)))
.padding(dot_radius)
.style(<Self::Theme as container::StyleSheet>::Style::Custom(
|theme| container::Appearance {
text_color: Some(Color::TRANSPARENT),
background: Some(Background::Color(
theme.cosmic().on_bg_color().into(),
)),
border_radius: 4.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
))
.into()
})
.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)
.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()
}
}
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());
.into()
}
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 Container::new(Container::new(content.width(Length::Shrink).height(Length::Shrink)).style(
// cosmic::Container::Custom(|theme| container::Appearance {
// text_color: Some(theme.cosmic().on_bg_color().into()),
// background: Some(theme.extended_palette().background.base.color.into()),
// border_radius: 12.0,
// border_width: 0.0,
// border_color: Color::TRANSPARENT,
// }),
// )).into();
return self.applet_helper.popup_container(content).into();
PanelAnchor::Right => {
row(vec![cosmic_icon.into(), column(dots).spacing(4).into()])
.align_items(iced::Alignment::Center)
.spacing(4)
.into()
}
PanelAnchor::Top => {
column(vec![row(dots).spacing(4).into(), cosmic_icon.into()])
.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())),
);
}
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
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}

View file

@ -11,6 +11,5 @@ libpulse-binding = "2.26.0"
libpulse-glib-binding = "2.25.0"
tokio = { version = "1.20.1", features=["full"] }
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"
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_native::alignment::Horizontal;
use cosmic::iced_native::layout::Limits;
@ -97,7 +96,7 @@ impl Application for Audio {
self.theme
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}
@ -268,134 +267,131 @@ impl Application for Audio {
pulse::connect().map(Message::Pulse)
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => self
.applet_helper
fn view(&self, id: window::Id) -> Element<Message> {
if id == window::Id::new(0) {
self.applet_helper
.icon_button(&self.icon_name)
.on_press(Message::TogglePopup)
.into(),
SurfaceIdWrapper::Popup(_) => {
let audio_disabled = matches!(self.pulse_state, PulseState::Disconnected(_));
let out_f64 = VolumeLinear::from(
self.current_output
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
)
.0 * 100.0;
let in_f64 = VolumeLinear::from(
self.current_input
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
)
.0 * 100.0;
.into()
} else {
let audio_disabled = matches!(self.pulse_state, PulseState::Disconnected(_));
let out_f64 = VolumeLinear::from(
self.current_output
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
)
.0 * 100.0;
let in_f64 = VolumeLinear::from(
self.current_input
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
)
.0 * 100.0;
let audio_content = if audio_disabled {
column![text("PulseAudio Disconnected")
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.size(24),]
} else {
column![
row![
icon("audio-volume-high-symbolic", 32)
.width(Length::Units(24))
.height(Length::Units(24))
.style(Svg::Symbolic),
slider(0.0..=100.0, out_f64, Message::SetOutputVolume)
.width(Length::FillPortion(5)),
text(format!("{}%", out_f64.round()))
.width(Length::FillPortion(1))
.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,
)
let audio_content = if audio_disabled {
column![text("PulseAudio Disconnected")
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.size(24),]
} else {
column![
row![
icon("audio-volume-high-symbolic", 32)
.width(Length::Units(24))
.height(Length::Units(24))
.style(Svg::Symbolic),
slider(0.0..=100.0, out_f64, Message::SetOutputVolume)
.width(Length::FillPortion(5)),
text(format!("{}%", out_f64.round()))
.width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right)
]
.align_items(Alignment::Start)
};
let content = column![
audio_content,
.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),
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)
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)
.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
.popup_container(container(content))
.into()
}
self.applet_helper
.popup_container(container(content))
.into()
}
}
}

View file

@ -6,7 +6,6 @@ edition = "2021"
[dependencies]
once_cell = "1.16.0"
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"
zbus = { version = "3.5", default-features = false, features = ["tokio"] }
log = "0.4"

View file

@ -13,7 +13,6 @@ use crate::upower_kbdbacklight::{
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
use cosmic::iced::alignment::Horizontal;
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
use cosmic::iced::wayland::SurfaceIdWrapper;
use cosmic::iced::{
widget::{column, container, row, slider, text},
window, Alignment, Application, Command, Length, Subscription,
@ -213,163 +212,158 @@ impl Application for CosmicBatteryApplet {
}
Command::none()
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => self
.applet_helper
fn view(&self, id: window::Id) -> Element<Message> {
if id == window::Id::new(0) {
self.applet_helper
.icon_button(&self.icon_name)
.on_press(Message::TogglePopup)
.into(),
SurfaceIdWrapper::Popup(_) => {
let name = text(fl!("battery")).size(18);
let description = text(
if "battery-full-charging-symbolic" == self.icon_name
|| "battery-full-charged-symbolic" == self.icon_name
{
format!("{}%", self.battery_percent)
} else {
format!(
"{} {} ({:.0}%)",
format_duration(self.time_remaining),
fl!("until-empty"),
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()
} else {
let name = text(fl!("battery")).size(18);
let description = text(
if "battery-full-charging-symbolic" == self.icon_name
|| "battery-full-charged-symbolic" == self.icon_name
{
format!("{}%", self.battery_percent)
} else {
format!(
"{} {} ({:.0}%)",
format_duration(self.time_remaining),
fl!("until-empty"),
self.battery_percent
)
.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
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Message {
Message::Ignore
}

View file

@ -9,7 +9,6 @@ once_cell = "1.16.0"
bluer = { version = "0.15", features = ["bluetoothd", "id"] }
futures-util = "0.3.21"
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"
log = "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-fl = "0.6.4"
rust-embed = "6.3.0"
rand = "0.8"
rand = "0.8"

View file

@ -6,7 +6,6 @@ use cosmic::{
iced::{
wayland::{
popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
},
widget::{column, container, row, scrollable, text, Column},
Alignment, Application, Color, Command, Length, Subscription,
@ -275,7 +274,7 @@ impl Application for CosmicBluetoothApplet {
}
Command::none()
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
fn view(&self, id: window::Id) -> Element<Message> {
let button_style = Button::Custom {
active: |t| iced_style::button::Appearance {
border_radius: BorderRadius::from(0.0),
@ -286,230 +285,228 @@ impl Application for CosmicBluetoothApplet {
..t.hovered(&Button::Text)
},
};
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => self
.applet_helper
if id == window::Id::new(0) {
self.applet_helper
.icon_button(&self.icon_name)
.on_press(Message::TogglePopup)
.into(),
SurfaceIdWrapper::Popup(_) => {
let mut known_bluetooth = column![];
for dev in self.bluer_state.devices.iter().filter(|d| {
!self
.request_confirmation
.as_ref()
.map_or(false, |(dev, _, _)| d.address == dev.address)
}) {
let mut row = row![
icon(dev.icon.as_str(), 16).style(Svg::Symbolic),
text(dev.name.clone())
.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())])
))
.into()
} else {
let mut known_bluetooth = column![];
for dev in self.bluer_state.devices.iter().filter(|d| {
!self
.request_confirmation
.as_ref()
.map_or(false, |(dev, _, _)| d.address == dev.address)
}) {
let mut row = row![
icon(dev.icon.as_str(), 16).style(Svg::Symbolic),
text(dev.name.clone())
.size(14)
.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());
]
.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)
.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
+ if self.request_confirmation.is_some() {
5
@ -517,15 +514,14 @@ impl Application for CosmicBluetoothApplet {
0
};
if item_counter > 10 {
content = content.push(
scrollable(Column::with_children(list_column)).height(Length::Units(300)),
);
} else {
content = content.push(Column::with_children(list_column));
}
self.applet_helper.popup_container(content).into()
if item_counter > 10 {
content = content.push(
scrollable(Column::with_children(list_column)).height(Length::Units(300)),
);
} else {
content = content.push(Column::with_children(list_column));
}
self.applet_helper.popup_container(content).into()
}
}
@ -537,7 +533,7 @@ impl Application for CosmicBluetoothApplet {
self.theme
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}

View file

@ -8,7 +8,6 @@ edition = "2021"
[dependencies]
zbus = "3.4"
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"
# Application i18n
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 cosmic::applet::CosmicAppletHelper;
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;
use cosmic::iced_style::application::{self, Appearance};
@ -121,7 +120,7 @@ impl Application for Window {
Message::CurrentGraphics(match cur_graphics {
Ok(g) => Some(g),
Err(err) => {
dbg!(err);
eprintln!("{err}");
None
}
})
@ -184,10 +183,9 @@ impl Application for Window {
Command::none()
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => match self.applet_helper.anchor {
fn view(&self, id: window::Id) -> Element<Message> {
if id == window::Id::new(0) {
match self.applet_helper.anchor {
PanelAnchor::Left | PanelAnchor::Right => self
.applet_helper
.icon_button("input-gaming-symbolic")
@ -219,187 +217,185 @@ impl Application for Window {
.width(Length::Shrink)
.height(Length::Shrink)
.into(),
},
SurfaceIdWrapper::Popup(_) => {
let content_list = vec![
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(format!("{} {}", fl!("integrated"), fl!("graphics"))).size(14),
text(fl!("integrated-desc")).size(12)
]
.width(Length::Fill),
icon(
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,
}
} else {
let content_list = vec![
button(APPLET_BUTTON_THEME)
.custom(vec![row![
column![
text(format!("{} {}", fl!("integrated"), fl!("graphics"))).size(14),
text(fl!("integrated-desc")).size(12)
]
.width(Length::Fill),
icon(
match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Integrated,
..
}) => 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),
]
}) => "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 {
new: Graphics::Integrated,
..
}) => 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),
icon(
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,
icon(
match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Nvidia,
..
}) => Svg::Symbolic,
_ => Svg::Default,
}),
}) => "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 {
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)
.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)
]
.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,
.width(Length::Fill),
icon(
match self.graphics_mode {
Some(GraphicsMode::SelectedGraphicsMode {
new: Graphics::Hybrid,
..
}) => Svg::Symbolic,
_ => Svg::Default,
})
}) => "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 {
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)
.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)
]
.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,
.width(Length::Fill),
icon(
match self.graphics_mode {
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(),
];
}) => "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 {
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
.popup_container(
column(vec![
text(fl!("graphics-mode"))
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.size(24)
.into(),
container(divider::horizontal::light())
.padding([0, 12])
.width(Length::Fill)
.into(),
column(content_list).into(),
])
.padding([8, 0])
.spacing(12),
)
.into()
}
self.applet_helper
.popup_container(
column(vec![
text(fl!("graphics-mode"))
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.size(24)
.into(),
container(divider::horizontal::light())
.padding([0, 12])
.width(Length::Fill)
.into(),
column(content_list).into(),
])
.padding([8, 0])
.spacing(12),
)
.into()
}
}
fn close_requested(&self, id: SurfaceIdWrapper) -> Self::Message {
match id {
SurfaceIdWrapper::LayerSurface(_) | SurfaceIdWrapper::Window(_) => unimplemented!(),
SurfaceIdWrapper::Popup(id) => Message::PopupClosed(id),
fn close_requested(&self, id: window::Id) -> Self::Message {
if id != window::Id::new(0) {
Message::PopupClosed(id)
} else {
unimplemented!();
}
}
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
<Self::Theme as application::StyleSheet>::Style::Custom(|theme| Appearance {
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" }
futures-util = "0.3.21"
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"
zbus = { version = "3.7", default-features = false }
log = "0.4"

View file

@ -2,10 +2,7 @@ use cosmic::iced_style;
use cosmic::{
applet::CosmicAppletHelper,
iced::{
wayland::{
popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
},
wayland::popup::{destroy_popup, get_popup},
widget::{column, container, row, scrollable, text, text_input, Column},
Alignment, Application, Color, Command, Length, Subscription,
},
@ -34,7 +31,8 @@ use crate::{
pub fn run() -> cosmic::iced::Result {
let helper = CosmicAppletHelper::default();
CosmicNetworkApplet::run(helper.window_settings())
let settings = helper.window_settings();
CosmicNetworkApplet::run(settings)
}
#[derive(Debug)]
@ -338,7 +336,7 @@ impl Application for CosmicNetworkApplet {
}
Command::none()
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
fn view(&self, id: window::Id) -> Element<Message> {
let button_style = Button::Custom {
active: |t| iced_style::button::Appearance {
border_radius: BorderRadius::from(0.0),
@ -349,349 +347,344 @@ impl Application for CosmicNetworkApplet {
..t.hovered(&Button::Text)
},
};
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => self
.applet_helper
if id == window::Id::new(0) {
self.applet_helper
.icon_button(&self.icon_name)
.on_press(Message::TogglePopup)
.into(),
SurfaceIdWrapper::Popup(_) => {
let mut vpn_ethernet_col = column![];
let mut known_wifi = column![];
for conn in &self.nm_state.active_conns {
match conn {
ActiveConnectionInfo::Vpn { name, ip_addresses } => {
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(),
);
}
vpn_ethernet_col = vpn_ethernet_col
.push(column![text(name), Column::with_children(ipv4)].spacing(4));
.into()
} else {
let mut vpn_ethernet_col = column![];
let mut known_wifi = column![];
for conn in &self.nm_state.active_conns {
match conn {
ActiveConnectionInfo::Vpn { name, ip_addresses } => {
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::Wired {
name,
hw_address: _,
speed,
ip_addresses,
} => {
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(),
);
}
vpn_ethernet_col = vpn_ethernet_col.push(
column![
row![
text(name),
text(format!("{speed} {}", fl!("megabits-per-second")))
]
.spacing(16),
Column::with_children(ipv4),
vpn_ethernet_col = vpn_ethernet_col
.push(column![text(name), Column::with_children(ipv4)].spacing(4));
}
ActiveConnectionInfo::Wired {
name,
hw_address: _,
speed,
ip_addresses,
} => {
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(),
);
}
vpn_ethernet_col = vpn_ethernet_col.push(
column![
row![
text(name),
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 {
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(),
);
}
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)
let mut btn_content = vec![
icon("network-wireless-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)
column![text(name).size(14), Column::with_children(ipv4)]
.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,
];
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)),
]
.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)),
.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),
);
}
}
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
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}

View file

@ -1,7 +1,6 @@
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
use cosmic::iced::wayland::{
popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
};
use cosmic::iced::{
widget::{button, column, row, text, Row, Space},
@ -65,7 +64,7 @@ impl Application for Notifications {
self.theme
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}
@ -112,57 +111,54 @@ impl Application for Notifications {
}
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => self
.applet_helper
fn view(&self, id: window::Id) -> Element<Message> {
if id == window::Id::new(0) {
self.applet_helper
.icon_button(&self.icon_name)
.on_press(Message::TogglePopup)
.into(),
SurfaceIdWrapper::Popup(_) => {
let do_not_disturb =
row![
toggler(String::from("Do Not Disturb"), self.do_not_disturb, |b| {
Message::DoNotDisturb(b)
})
.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()
.into()
} else {
let do_not_disturb =
row![
toggler(String::from("Do Not Disturb"), self.do_not_disturb, |b| {
Message::DoNotDisturb(b)
})
.width(Length::Fill)
]
.padding([0, 24])
.spacing(12);
.padding([0, 24]);
let content = column![]
.align_items(Alignment::Start)
.spacing(12)
.padding([12, 0])
.push(do_not_disturb)
.push(main_content)
.push(settings);
let settings =
row_button(vec!["Notification Settings...".into()]).on_press(Message::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"
tokio = { version = "1.20.1", features=["full"] }
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"
zbus = "3.7"
logind-zbus = "3.1"

View file

@ -2,7 +2,6 @@ use std::process;
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
use cosmic::iced::wayland::SurfaceIdWrapper;
use cosmic::iced_native::layout::Limits;
use cosmic::iced_native::widget::Space;
use cosmic::widget::{button, divider, icon};
@ -80,7 +79,7 @@ impl Application for Power {
self.theme
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}
@ -139,61 +138,56 @@ impl Application for Power {
}
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => self
.applet_helper
fn view(&self, id: window::Id) -> Element<Message> {
if id == window::Id::new(0) {
self.applet_helper
.icon_button(&self.icon_name)
.on_press(Message::TogglePopup)
.into(),
SurfaceIdWrapper::Popup(_) => {
let settings = row_button(vec!["Settings...".into()]).on_press(Message::Settings);
.into()
} else {
let settings = row_button(vec!["Settings...".into()]).on_press(Message::Settings);
let session = column![
row_button(vec![
text_icon("system-lock-screen-symbolic", 24).into(),
"Lock Screen".into(),
Space::with_width(Length::Fill).into(),
"Super + Escape".into(),
])
.on_press(Message::Lock),
row_button(vec![
text_icon("system-log-out-symbolic", 24).into(),
"Log Out".into(),
Space::with_width(Length::Fill).into(),
"Ctrl + Alt + Delete".into(),
])
.on_press(Message::LogOut),
];
let session = column![
row_button(vec![
text_icon("system-lock-screen-symbolic", 24).into(),
"Lock Screen".into(),
Space::with_width(Length::Fill).into(),
"Super + Escape".into(),
])
.on_press(Message::Lock),
row_button(vec![
text_icon("system-log-out-symbolic", 24).into(),
"Log Out".into(),
Space::with_width(Length::Fill).into(),
"Ctrl + Alt + Delete".into(),
])
.on_press(Message::LogOut),
];
let power = row![
power_buttons("system-lock-screen-symbolic", "Suspend")
.on_press(Message::Suspend),
power_buttons("system-restart-symbolic", "Restart").on_press(Message::Restart),
power_buttons("system-shutdown-symbolic", "Shutdown")
.on_press(Message::Shutdown),
]
.spacing(24)
.padding([0, 24]);
let power = row![
power_buttons("system-lock-screen-symbolic", "Suspend").on_press(Message::Suspend),
power_buttons("system-restart-symbolic", "Restart").on_press(Message::Restart),
power_buttons("system-shutdown-symbolic", "Shutdown").on_press(Message::Shutdown),
]
.spacing(24)
.padding([0, 24]);
let content = column![
settings,
container(divider::horizontal::light())
.padding([0, 12])
.width(Length::Fill),
session,
container(divider::horizontal::light())
.padding([0, 12])
.width(Length::Fill),
power
]
.align_items(Alignment::Start)
.spacing(12)
.padding([8, 0]);
let content = column![
settings,
container(divider::horizontal::light())
.padding([0, 12])
.width(Length::Fill),
session,
container(divider::horizontal::light())
.padding([0, 12])
.width(Length::Fill),
power
]
.align_items(Alignment::Start)
.spacing(12)
.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::iced::wayland::{
popup::{destroy_popup, get_popup},
SurfaceIdWrapper,
};
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
use cosmic::iced::{
time,
wayland::InitialSurface,
@ -28,9 +25,7 @@ pub fn main() -> cosmic::iced::Result {
s.iced_settings.min_size = Some((1, 1));
s.iced_settings.max_size = None;
s.autosize = true;
s.size_limits = Limits::NONE
.min_height(1)
.min_width(1);
s.size_limits = Limits::NONE.min_height(1).min_width(1);
}
_ => {}
};
@ -98,7 +93,7 @@ impl Application for Time {
self.theme
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
Message::Ignore
}
@ -198,61 +193,57 @@ impl Application for Time {
}
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => {
let button = button(
if matches!(
self.applet_helper.anchor,
PanelAnchor::Top | PanelAnchor::Bottom
) {
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()
fn view(&self, id: window::Id) -> Element<Message> {
if id == window::Id::new(0) {
let button = button(
if matches!(
self.applet_helper.anchor,
PanelAnchor::Top | PanelAnchor::Bottom
) {
column![text(self.now.format("%b %-d %-I:%M %p").to_string())]
} else {
button.into()
}
}
SurfaceIdWrapper::Popup(_) => {
let content = column![]
.align_items(Alignment::Start)
.spacing(12)
.padding([24, 0])
.push(text(&self.msg))
.padding(8);
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);
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::mouse::{self, ScrollDelta};
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::{
subscription, widget::button, window, Application, Command, Event::Mouse, Length, Settings,
@ -131,7 +131,7 @@ impl Application for IcedWorkspacesApplet {
Command::none()
}
fn view(&self, _id: SurfaceIdWrapper) -> Element<Message> {
fn view(&self, _id: window::Id) -> Element<Message> {
if self.workspaces.is_empty() {
return row![].padding(8).into();
}
@ -200,7 +200,7 @@ impl Application for IcedWorkspacesApplet {
self.theme
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Self::Message {
fn close_requested(&self, _id: window::Id) -> Self::Message {
unimplemented!()
}

View file

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

View file

@ -2,11 +2,12 @@ use cosmic::{
applet::CosmicAppletHelper,
iced::{
self,
wayland::{InitialSurface, SurfaceIdWrapper},
wayland::InitialSurface,
Application,
},
iced_sctk::layout::Limits,
iced_style::application,
iced_native::window,
};
use freedesktop_desktop_entry::DesktopEntry;
use std::{env, fs, process::Command};
@ -41,7 +42,7 @@ impl iced::Application for Button {
String::from("Button")
}
fn close_requested(&self, _id: SurfaceIdWrapper) -> Msg {
fn close_requested(&self, _id: window::Id) -> Msg {
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?
cosmic::widget::button(cosmic::theme::Button::Text)
.text(&self.desktop.name)