Use libcomsic-applet in status-area, network, graphics

This commit is contained in:
Ian Douglas Scott 2022-07-05 16:21:23 -07:00
parent aac43de65d
commit 765e3af815
13 changed files with 154 additions and 330 deletions

3
Cargo.lock generated
View file

@ -368,6 +368,7 @@ version = "0.1.0"
dependencies = [
"cosmic-panel-config",
"gtk4",
"libcosmic-applet",
"once_cell",
"relm4-macros",
"tokio",
@ -383,6 +384,7 @@ dependencies = [
"futures-util",
"gtk4",
"itertools",
"libcosmic-applet",
"libcosmic-widgets",
"once_cell",
"relm4-macros",
@ -431,6 +433,7 @@ dependencies = [
"cosmic-panel-config",
"futures",
"gtk4",
"libcosmic-applet",
"once_cell",
"serde",
"zbus",

View file

@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_2"] }
libcosmic-applet = { path = "../../libcosmic-applet" }
once_cell = "1.9.0"
relm4-macros = { git = "https://github.com/Relm4/Relm4.git", branch = "next" }
tokio = { version = "1.16.1", features = ["full"] }

View file

@ -10,29 +10,16 @@ pub mod graphics;
pub mod mode_box;
use self::{dbus::PowerDaemonProxy, graphics::Graphics, mode_box::ModeSelection};
use cosmic_panel_config::config::CosmicPanelConfig;
use gtk4::{
gdk::Display,
gio::ApplicationFlags,
glib::{self, clone, MainContext, PRIORITY_DEFAULT},
prelude::*,
Align, CssProvider, Label, ListBox, ListBoxRow, Orientation, Overlay, Separator, Spinner,
StyleContext, STYLE_PROVIDER_PRIORITY_APPLICATION,
Align, Label, ListBox, ListBoxRow, Orientation, Overlay, Separator, Spinner,
};
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime"));
fn main() {
let application = gtk4::Application::new(
Some("com.system76.cosmic.applets.graphics"),
ApplicationFlags::default(),
);
application.connect_activate(build_ui);
application.run();
}
async fn get_current_graphics() -> zbus::Result<Graphics> {
let connection = zbus::Connection::system().await?;
let proxy = PowerDaemonProxy::new(&connection).await?;
@ -53,7 +40,9 @@ fn row_clicked(_: &ListBox, row: &ListBoxRow) {
selector.emit_activate();
}
fn build_ui(application: &gtk4::Application) {
fn main() {
gtk4::init().unwrap();
let provider = gtk4::CssProvider::new();
provider.load_from_data(include_bytes!("style.css"));
gtk4::StyleContext::add_provider_for_display(
@ -62,141 +51,119 @@ fn build_ui(application: &gtk4::Application) {
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
let window = gtk4::ApplicationWindow::builder()
.application(application)
.title("COSMIC Graphics Applet")
.decorated(false)
.resizable(false)
.width_request(1)
.height_request(1)
.css_classes(vec!["root_window".to_string()])
.build();
let config = CosmicPanelConfig::load_from_env().unwrap_or_default();
let popover = gtk4::builders::PopoverBuilder::new()
.autohide(true)
.has_arrow(false)
.build();
let button = gtk4::Button::new();
button.add_css_class("panel_icon");
button.connect_clicked(glib::clone!(@weak popover => move |_| {
popover.show();
}));
// TODO cleanup
let image = gtk4::Image::from_icon_name("input-gaming");
image.add_css_class("panel_icon");
image.set_pixel_size(config.get_applet_icon_size().try_into().unwrap());
button.set_child(Some(&image));
let current_graphics = RT
.block_on(get_current_graphics())
.expect("failed to connect to system76-power");
view! {
icon_box = gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 0,
add_css_class: "icon_box",
}
}
let (tx, rx) = MainContext::channel::<bool>(PRIORITY_DEFAULT);
view! {
main_overlay = Overlay {
add_overlay: loading_box = &gtk4::Box {
append: loading_explain_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_halign: Align::Center,
set_valign: Align::Center,
append: loading_spinner = &Spinner {
set_halign: Align::Center,
},
append: loading_explain = &Label {
set_label: "Please wait while your graphics mode is set...",
set_halign: Align::Center,
},
},
set_halign: Align::Center,
set_valign: Align::Center,
set_hexpand: true,
set_vexpand: true,
set_visible: false,
add_css_class: "loading-overlay",
},
window = libcosmic_applet::AppletWindow {
set_title: Some("COSMIC Graphics Applet"),
#[wrap(Some)]
set_child: main_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 10,
set_margin_top: 20,
set_margin_bottom: 20,
set_margin_start: 24,
set_margin_end: 24,
append: mode_label = &Label {
set_text: "Graphics Mode"
},
append: separator = &Separator {
set_orientation: Orientation::Horizontal
},
append: graphics_modes_list = &ListBox {
connect_row_activated: row_clicked,
append: integrated_selector = &ModeSelection {
set_title: "Integrated Graphics",
set_description: "Disables external displays. Requires Restart.",
set_active: (current_graphics == Graphics::Integrated),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Integrated).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: nvidia_selector = &ModeSelection {
set_title: "NVIDIA Graphics",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Nvidia),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Nvidia).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: hybrid_selector = &ModeSelection {
set_title: "Hybrid Graphics",
set_description: "Requires Restart.",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Hybrid),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Hybrid).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: compute_selector = &ModeSelection {
set_title: "Compute Graphics",
set_description: "Disables external displays. Requires Restart.",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Compute),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Compute).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
set_child = &libcosmic_applet::AppletButton {
set_button_icon_name: "input-gaming",
#[wrap(Some)]
set_popover_child: main_overlay = &Overlay {
add_overlay: loading_box = &gtk4::Box {
append: loading_explain_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_halign: Align::Center,
set_valign: Align::Center,
append: loading_spinner = &Spinner {
set_halign: Align::Center,
},
append: loading_explain = &Label {
set_label: "Please wait while your graphics mode is set...",
set_halign: Align::Center,
},
},
set_halign: Align::Center,
set_valign: Align::Center,
set_hexpand: true,
set_vexpand: true,
set_visible: false,
add_css_class: "loading-overlay",
},
#[wrap(Some)]
set_child: main_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 10,
set_margin_top: 20,
set_margin_bottom: 20,
set_margin_start: 24,
set_margin_end: 24,
append: mode_label = &Label {
set_text: "Graphics Mode"
},
append: separator = &Separator {
set_orientation: Orientation::Horizontal
},
append: graphics_modes_list = &ListBox {
connect_row_activated: row_clicked,
append: integrated_selector = &ModeSelection {
set_title: "Integrated Graphics",
set_description: "Disables external displays. Requires Restart.",
set_active: (current_graphics == Graphics::Integrated),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Integrated).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: nvidia_selector = &ModeSelection {
set_title: "NVIDIA Graphics",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Nvidia),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Nvidia).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: hybrid_selector = &ModeSelection {
set_title: "Hybrid Graphics",
set_description: "Requires Restart.",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Hybrid),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Hybrid).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: compute_selector = &ModeSelection {
set_title: "Compute Graphics",
set_description: "Disables external displays. Requires Restart.",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Compute),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Compute).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
}
}
}
}
}
}
rx.attach(
None,
clone!(@weak loading_box, @weak loading_spinner => @default-return Continue(true), move |val| {
@ -205,11 +172,9 @@ fn build_ui(application: &gtk4::Application) {
Continue(true)
}),
);
popover.set_child(Some(&main_overlay));
icon_box.append(&button);
icon_box.append(&popover);
window.set_child(Some(&icon_box));
window.show();
let main_loop = glib::MainLoop::new(None, false);
main_loop.run();
}

View file

@ -2,32 +2,3 @@
background-color: #2f2f2f;
opacity: 0.85;
}
image.panel_icon {
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
}
button.panel_icon {
border-radius: 12px;
transition: 100ms;
padding: 4px;
border-color: transparent;
background: transparent;
outline-color: transparent;
}
button.panel_icon:hover {
border-radius: 12px;
transition: 100ms;
padding: 4px;
border-color: rgba(255, 255, 255, 0.1);
outline-color: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.1);
}
window.root_window {
background: transparent;
}

View file

@ -14,5 +14,6 @@ relm4-macros = { git = "https://github.com/Relm4/Relm4.git", branch = "next" }
slotmap = "1.0.6"
tokio = { version = "1.15.0", features = ["full"] }
zbus = "2.0.1"
libcosmic-applet = { path = "../../libcosmic-applet" }
libcosmic-widgets = { git = "https://github.com/pop-os/libcosmic", branch = "relm4-next" }
cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", features = ["gtk4"]}

View file

@ -7,90 +7,43 @@ pub mod task;
pub mod ui;
pub mod widgets;
use cosmic_panel_config::config::CosmicPanelConfig;
use gtk4::{gio::ApplicationFlags, glib, prelude::*, Orientation, Separator};
use gtk4::{glib, prelude::*, Orientation, Separator};
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime"));
fn main() {
let application = gtk4::Application::new(
Some("com.system76.cosmic.applets.network"),
ApplicationFlags::default(),
);
application.connect_activate(build_ui);
application.run();
}
fn build_ui(application: &gtk4::Application) {
let provider = gtk4::CssProvider::new();
provider.load_from_data(include_bytes!("style.css"));
gtk4::StyleContext::add_provider_for_display(
&gtk4::gdk::Display::default().expect("Could not connect to a display."),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
let window = gtk4::ApplicationWindow::builder()
.application(application)
.title("COSMIC Network Applet")
.decorated(false)
.resizable(false)
.width_request(1)
.height_request(1)
.css_classes(vec!["root_window".to_string()])
.build();
gtk4::init().unwrap();
view! {
main_box = gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 10,
set_margin_top: 20,
set_margin_bottom: 20,
set_margin_start: 24,
set_margin_end: 24
window = libcosmic_applet::AppletWindow {
set_title: Some("COSMIC Network Applet"),
#[wrap(Some)]
set_child: button = &libcosmic_applet::AppletButton {
set_button_icon_name: "preferences-system-network",
#[wrap(Some)]
set_popover_child: main_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 10,
set_margin_top: 20,
set_margin_bottom: 20,
set_margin_start: 24,
set_margin_end: 24
}
}
}
}
let config = CosmicPanelConfig::load_from_env().unwrap_or_default();
let popover = gtk4::builders::PopoverBuilder::new()
.autohide(true)
.has_arrow(false)
.build();
let button = gtk4::Button::new();
button.add_css_class("panel_icon");
button.connect_clicked(glib::clone!(@weak popover => move |_| {
popover.show();
}));
// TODO cleanup
let image = gtk4::Image::from_icon_name("preferences-system-network");
image.add_css_class("panel_icon");
image.set_pixel_size(config.get_applet_icon_size().try_into().unwrap());
button.set_child(Some(&image));
view! {
icon_box = gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 0,
add_css_class: "icon_box",
}
}
popover.set_child(Some(&main_box));
icon_box.append(&button);
icon_box.append(&popover);
ui::current_networks::add_current_networks(&main_box, &image);
ui::current_networks::add_current_networks(&main_box, &button);
main_box.append(&Separator::new(Orientation::Horizontal));
ui::toggles::add_toggles(&main_box);
let available_wifi_separator = Separator::new(Orientation::Horizontal);
main_box.append(&available_wifi_separator);
available_wifi_separator.hide();
ui::available_wifi::add_available_wifi(&main_box, available_wifi_separator);
window.set_child(Some(&icon_box));
window.show();
let main_loop = glib::MainLoop::new(None, false);
main_loop.run();
}

View file

@ -1,28 +0,0 @@
image.panel_icon {
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
}
button.panel_icon {
border-radius: 12px;
transition: 100ms;
padding: 4px;
border-color: transparent;
background: transparent;
outline-color: transparent;
}
button.panel_icon:hover {
border-radius: 12px;
transition: 100ms;
padding: 4px;
border-color: rgba(255, 255, 255, 0.1);
outline-color: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.1);
}
window.root_window {
background: transparent;
}

View file

@ -18,7 +18,7 @@ use gtk4::{
use std::{cell::RefCell, net::IpAddr, rc::Rc};
use zbus::Connection;
pub fn add_current_networks(target: &gtk4::Box, icon_image: &gtk4::Image) {
pub fn add_current_networks(target: &gtk4::Box, icon_image: &libcosmic_applet::AppletButton) {
let networks_list = ListBox::builder().show_separators(true).build();
let entries = Rc::<RefCell<Vec<ListBoxRow>>>::default();
let (tx, rx) = MainContext::channel::<Vec<ActiveConnectionInfo>>(PRIORITY_DEFAULT);
@ -38,7 +38,7 @@ fn display_active_connections(
connections: Vec<ActiveConnectionInfo>,
target: &ListBox,
entries: &mut Vec<ListBoxRow>,
icon_image: &gtk4::Image,
icon_image: &libcosmic_applet::AppletButton,
) {
for old_entry in entries.drain(..) {
target.remove(&old_entry);
@ -51,7 +51,7 @@ fn display_active_connections(
speed,
ip_addresses,
} => {
icon_image.set_icon_name(Some("network-wired-symbolic"));
icon_image.set_button_icon_name("network-wired-symbolic");
render_wired_connection(name, speed, ip_addresses)
}
ActiveConnectionInfo::WiFi {
@ -62,7 +62,7 @@ fn display_active_connections(
wpa_flags,
} => continue,
ActiveConnectionInfo::Vpn { name, ip_addresses } => {
icon_image.set_icon_name(Some("network-vpn-symbolic"));
icon_image.set_button_icon_name("network-vpn-symbolic");
render_vpn(name, ip_addresses)
}
};

View file

@ -8,6 +8,7 @@ license = "GPL-3.0-or-later"
cascade = "1"
futures = "0.3"
gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs" }
libcosmic-applet = { path = "../../libcosmic-applet" }
once_cell = "1.12"
serde = "1"
zbus = "2.0.1"

View file

@ -1,3 +1,4 @@
use cascade::cascade;
use gtk4::{glib, prelude::*};
mod dbus_service;
@ -14,24 +15,12 @@ fn main() {
// XXX Implement DBus service somewhere other than applet?
glib::MainContext::default().spawn_local(status_notifier_watcher::start());
let provider = gtk4::CssProvider::new();
provider.load_from_data(include_bytes!("style.css"));
gtk4::StyleContext::add_provider_for_display(
&gtk4::gdk::Display::default().expect("Could not connect to a display."),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
let status_area = StatusArea::new();
gtk4::Window::builder()
.decorated(false)
.child(&status_area)
.resizable(false)
.width_request(1)
.height_request(1)
.css_classes(vec!["root_window".to_string()])
.build()
.show();
cascade! {
libcosmic_applet::AppletWindow::new();
..set_child(Some(&status_area));
..show();
};
let main_loop = glib::MainLoop::new(None, false);
main_loop.run();

View file

@ -19,7 +19,7 @@ struct Menu {
#[derive(Default)]
pub struct StatusMenuInner {
menu_button: DerefCell<gtk4::MenuButton>,
menu_button: DerefCell<libcosmic_applet::AppletButton>,
vbox: DerefCell<gtk4::Box>,
item: DerefCell<StatusNotifierItemProxy<'static>>,
dbus_menu: DerefCell<DBusMenuProxy<'static>>,
@ -43,17 +43,10 @@ impl ObjectImpl for StatusMenuInner {
gtk4::Box::new(gtk4::Orientation::Vertical, 0);
};
let popover = cascade! {
gtk4::Popover::new();
..set_child(Some(&vbox));
};
let menu_button = cascade! {
gtk4::MenuButton::new();
..add_css_class("panel_icon");
..set_has_frame(false);
libcosmic_applet::AppletButton::new();
..set_parent(obj);
..set_popover(Some(&popover));
..set_popover_child(Some(&vbox));
};
self.menu_button.set(menu_button);
@ -88,7 +81,7 @@ impl StatusMenu {
.await?;
let obj = glib::Object::new::<Self>(&[]).unwrap();
let icon_name = item.icon_name().await?;
obj.inner().menu_button.set_icon_name(&icon_name);
obj.inner().menu_button.set_button_icon_name(&icon_name);
let menu = item.menu().await?;
let menu = DBusMenuProxy::builder(&connection)

View file

@ -1,33 +0,0 @@
.loading-overlay {
background-color: #2f2f2f;
opacity: 0.85;
}
image.panel_icon {
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
}
button.panel_icon {
border-radius: 12px;
transition: 100ms;
padding: 4px;
border-color: transparent;
background: transparent;
outline-color: transparent;
}
button.panel_icon:hover {
border-radius: 12px;
transition: 100ms;
padding: 4px;
border-color: rgba(255, 255, 255, 0.1);
outline-color: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.1);
}
window.root_window {
background: transparent;
}

View file

@ -123,4 +123,12 @@ impl AppletButton {
pub fn set_popover_child(&self, child: Option<&impl IsA<gtk4::Widget>>) {
self.inner().popover.set_child(child);
}
pub fn popdown(&self) {
self.inner().popover.popdown();
}
pub fn popup(&self) {
self.inner().popover.popup();
}
}