From aac43de65ddc1765a7d8aaf93dfa1e610e6ef4f0 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 5 Jul 2022 14:41:09 -0700 Subject: [PATCH 1/3] Separate `CosmicAppletWindow` and `CosmicAppletButton` --- Cargo.lock | 1 + .../cosmic-app-list/src/apps_container/mod.rs | 5 +- applets/cosmic-app-list/src/dock_list/imp.rs | 4 +- applets/cosmic-applet-audio/src/main.rs | 151 +++++++------- applets/cosmic-applet-battery/src/main.rs | 185 +++++++++--------- applets/cosmic-applet-graphics/src/main.rs | 4 +- .../cosmic-applet-notifications/Cargo.toml | 1 + .../cosmic-applet-notifications/src/main.rs | 24 +-- applets/cosmic-applet-power/src/main.rs | 49 ++--- applets/cosmic-applet-workspaces/src/main.rs | 2 +- .../cosmic-applet-workspaces/src/wayland.rs | 21 +- .../src/workspace_button/mod.rs | 4 +- .../src/workspace_list/mod.rs | 4 +- libcosmic-applet/src/button.rs | 126 ++++++++++++ libcosmic-applet/src/lib.rs | 125 +----------- libcosmic-applet/src/window.rs | 58 ++++++ 16 files changed, 421 insertions(+), 343 deletions(-) create mode 100644 libcosmic-applet/src/button.rs create mode 100644 libcosmic-applet/src/window.rs diff --git a/Cargo.lock b/Cargo.lock index a6a8f2cb..3a81ae20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,7 @@ dependencies = [ "gtk4", "libcosmic-applet", "once_cell", + "relm4-macros", "serde", "zbus", "zbus_names", diff --git a/applets/cosmic-app-list/src/apps_container/mod.rs b/applets/cosmic-app-list/src/apps_container/mod.rs index 3fc4a7c4..95ef3c33 100644 --- a/applets/cosmic-app-list/src/apps_container/mod.rs +++ b/applets/cosmic-app-list/src/apps_container/mod.rs @@ -5,8 +5,8 @@ use crate::dock_list::DockList; use crate::dock_list::DockListType; use crate::utils::Event; use cascade::cascade; -use cosmic_panel_config::config::PanelAnchor; use cosmic_panel_config::config::CosmicPanelConfig; +use cosmic_panel_config::config::PanelAnchor; use gtk4::prelude::*; use gtk4::subclass::prelude::*; use gtk4::Orientation; @@ -68,13 +68,12 @@ impl AppsContainer { // Setup self_.setup_callbacks(); self_.set_position(config.anchor); - Self::setup_callbacks(&self_); self_ } - + pub fn model(&self, type_: DockListType) -> &gio::ListStore { // Get state let imp = imp::AppsContainer::from_instance(self); diff --git a/applets/cosmic-app-list/src/dock_list/imp.rs b/applets/cosmic-app-list/src/dock_list/imp.rs index 6665e694..1104ad04 100644 --- a/applets/cosmic-app-list/src/dock_list/imp.rs +++ b/applets/cosmic-app-list/src/dock_list/imp.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MPL-2.0-only -use cosmic_panel_config::config::{PanelAnchor, CosmicPanelConfig}; +use cosmic_panel_config::config::{CosmicPanelConfig, PanelAnchor}; use glib::SignalHandlerId; use gtk4::subclass::prelude::*; use gtk4::{gio, glib}; @@ -25,7 +25,7 @@ pub struct DockList { pub popover_menu_index: Rc>>, pub position: Rc>, pub tx: OnceCell>, - pub config: OnceCell + pub config: OnceCell, } #[glib::object_subclass] diff --git a/applets/cosmic-applet-audio/src/main.rs b/applets/cosmic-applet-audio/src/main.rs index 938e1956..b1881f0a 100644 --- a/applets/cosmic-applet-audio/src/main.rs +++ b/applets/cosmic-applet-audio/src/main.rs @@ -76,90 +76,93 @@ fn app(application: &Application) { }); pa.connect().unwrap(); // XXX unwrap view! { - window = libcosmic_applet::Applet { + window = libcosmic_applet::AppletWindow { set_application: Some(application), set_title: Some("COSMIC Network Applet"), - // TODO: adjust based on volume, mute - set_button_icon_name: "multimedia-volume-control-symbolic", #[wrap(Some)] - set_popover_child: window_box = &GtkBox { - set_orientation: Orientation::Vertical, - set_spacing: 24, - append: output_box = &GtkBox { - set_orientation: Orientation::Horizontal, - set_spacing: 16, - append: output_icon = &Image { - set_icon_name: Some("audio-speakers-symbolic"), - }, - append: output_volume = &Scale::with_range(Orientation::Horizontal, 0., 100., 1.) { - set_format_value_func: |_, value| { - format!("{:.0}%", value) - }, - set_value_pos: PositionType::Right, - set_hexpand: true - } - }, - append: input_box = &GtkBox { - set_orientation: Orientation::Horizontal, - set_spacing: 16, - append: input_icon = &Image { - set_icon_name: Some("audio-input-microphone-symbolic"), - }, - append: input_volume = &Scale::with_range(Orientation::Horizontal, 0., 100., 1.) { - set_format_value_func: |_, value| { - format!("{:.0}%", value) - }, - set_value_pos: PositionType::Right, - set_hexpand: true - } - }, - append: _sep = &Separator { - set_orientation: Orientation::Horizontal, - }, - append: output_list_box = &GtkBox { + set_child = &libcosmic_applet::AppletButton { + // TODO: adjust based on volume, mute + set_button_icon_name: "multimedia-volume-control-symbolic", + #[wrap(Some)] + set_popover_child: window_box = &GtkBox { set_orientation: Orientation::Vertical, - append: current_output_button = &Button { - #[wrap(Some)] - set_child: current_output = &Label {}, - connect_clicked[outputs_revealer] => move |_| { - outputs_revealer.set_reveal_child(!outputs_revealer.reveals_child()); + set_spacing: 24, + append: output_box = &GtkBox { + set_orientation: Orientation::Horizontal, + set_spacing: 16, + append: output_icon = &Image { + set_icon_name: Some("audio-speakers-symbolic"), + }, + append: output_volume = &Scale::with_range(Orientation::Horizontal, 0., 100., 1.) { + set_format_value_func: |_, value| { + format!("{:.0}%", value) + }, + set_value_pos: PositionType::Right, + set_hexpand: true } }, - append: outputs_revealer = &Revealer { - set_transition_type: RevealerTransitionType::SlideDown, - #[wrap(Some)] - set_child: outputs = &ListBox { - set_selection_mode: SelectionMode::None, - set_activate_on_single_click: true - } - } - }, - append: _sep = &Separator { - set_orientation: Orientation::Horizontal, - }, - append: input_list_box = &GtkBox { - set_orientation: Orientation::Vertical, - append: current_input_button = &Button { - #[wrap(Some)] - set_child: current_input = &Label {}, - connect_clicked[inputs_revealer] => move |_| { - inputs_revealer.set_reveal_child(!inputs_revealer.reveals_child()); + append: input_box = &GtkBox { + set_orientation: Orientation::Horizontal, + set_spacing: 16, + append: input_icon = &Image { + set_icon_name: Some("audio-input-microphone-symbolic"), + }, + append: input_volume = &Scale::with_range(Orientation::Horizontal, 0., 100., 1.) { + set_format_value_func: |_, value| { + format!("{:.0}%", value) + }, + set_value_pos: PositionType::Right, + set_hexpand: true } }, - append: inputs_revealer = &Revealer { - set_transition_type: RevealerTransitionType::SlideDown, - #[wrap(Some)] - set_child: inputs = &ListBox { - set_selection_mode: SelectionMode::None, - set_activate_on_single_click: true + append: _sep = &Separator { + set_orientation: Orientation::Horizontal, + }, + append: output_list_box = &GtkBox { + set_orientation: Orientation::Vertical, + append: current_output_button = &Button { + #[wrap(Some)] + set_child: current_output = &Label {}, + connect_clicked[outputs_revealer] => move |_| { + outputs_revealer.set_reveal_child(!outputs_revealer.reveals_child()); + } + }, + append: outputs_revealer = &Revealer { + set_transition_type: RevealerTransitionType::SlideDown, + #[wrap(Some)] + set_child: outputs = &ListBox { + set_selection_mode: SelectionMode::None, + set_activate_on_single_click: true + } } + }, + append: _sep = &Separator { + set_orientation: Orientation::Horizontal, + }, + append: input_list_box = &GtkBox { + set_orientation: Orientation::Vertical, + append: current_input_button = &Button { + #[wrap(Some)] + set_child: current_input = &Label {}, + connect_clicked[inputs_revealer] => move |_| { + inputs_revealer.set_reveal_child(!inputs_revealer.reveals_child()); + } + }, + append: inputs_revealer = &Revealer { + set_transition_type: RevealerTransitionType::SlideDown, + #[wrap(Some)] + set_child: inputs = &ListBox { + set_selection_mode: SelectionMode::None, + set_activate_on_single_click: true + } + } + }, + append: _sep = &Separator { + set_orientation: Orientation::Horizontal, + }, + append: playing_apps = &ListBox { + set_selection_mode: SelectionMode::None, } - }, - append: _sep = &Separator { - set_orientation: Orientation::Horizontal, - }, - append: playing_apps = &ListBox { - set_selection_mode: SelectionMode::None, } } } diff --git a/applets/cosmic-applet-battery/src/main.rs b/applets/cosmic-applet-battery/src/main.rs index 616712ed..8f17ea66 100644 --- a/applets/cosmic-applet-battery/src/main.rs +++ b/applets/cosmic-applet-battery/src/main.rs @@ -77,115 +77,118 @@ impl SimpleComponent for AppModel { type Output = (); view! { - libcosmic_applet::Applet { - #[watch] - set_button_icon_name: &model.icon_name, + libcosmic_applet::AppletWindow { #[wrap(Some)] - set_popover_child = >k4::Box { - set_orientation: gtk4::Orientation::Vertical, + set_child = &libcosmic_applet::AppletButton { + #[watch] + set_button_icon_name: &model.icon_name, + #[wrap(Some)] + set_popover_child = >k4::Box { + set_orientation: gtk4::Orientation::Vertical, - // Battery - gtk4::Box { - set_orientation: gtk4::Orientation::Horizontal, - gtk4::Image { - #[watch] - set_icon_name: Some(&model.icon_name), - }, + // Battery gtk4::Box { - set_orientation: gtk4::Orientation::Vertical, - gtk4::Label { - set_halign: gtk4::Align::Start, - set_label: "Battery", - }, - gtk4::Label { - set_halign: gtk4::Align::Start, - // XXX time to full, fully changed, etc. + set_orientation: gtk4::Orientation::Horizontal, + gtk4::Image { #[watch] - set_label: &format!("{} until empty ({:.0}%)", format_duration(model.time_remaining), model.battery_percent), + set_icon_name: Some(&model.icon_name), + }, + gtk4::Box { + set_orientation: gtk4::Orientation::Vertical, + gtk4::Label { + set_halign: gtk4::Align::Start, + set_label: "Battery", + }, + gtk4::Label { + set_halign: gtk4::Align::Start, + // XXX time to full, fully changed, etc. + #[watch] + set_label: &format!("{} until empty ({:.0}%)", format_duration(model.time_remaining), model.battery_percent), + }, }, }, - }, - gtk4::Separator { - }, + gtk4::Separator { + }, - // Limit charging - gtk4::Box { - set_orientation: gtk4::Orientation::Horizontal, + // Limit charging gtk4::Box { - set_orientation: gtk4::Orientation::Vertical, - gtk4::Label { - set_halign: gtk4::Align::Start, - set_label: "Limit Battery Charging", + set_orientation: gtk4::Orientation::Horizontal, + gtk4::Box { + set_orientation: gtk4::Orientation::Vertical, + gtk4::Label { + set_halign: gtk4::Align::Start, + set_label: "Limit Battery Charging", + }, + gtk4::Label { + set_halign: gtk4::Align::Start, + set_label: "Increase the lifespan of your battery by setting a maximum charge value of 80%." + }, + }, + gtk4::Switch { + set_valign: gtk4::Align::Center, + }, + }, + + gtk4::Separator { + }, + + // Brightness + gtk4::Box { + #[watch] + set_visible: model.backlight.is_some(), + set_orientation: gtk4::Orientation::Horizontal, + gtk4::Image { + set_icon_name: Some("display-brightness-symbolic"), + }, + gtk4::Scale { + set_hexpand: true, + set_adjustment: >k4::Adjustment::new(0., 0., 1., 1., 1., 0.), + #[watch] + set_value: model.display_brightness, + connect_change_value[sender] => move |_, _, value| { + sender.input(AppMsg::SetDisplayBrightness(value)); + gtk4::Inhibit(false) + }, }, gtk4::Label { - set_halign: gtk4::Align::Start, - set_label: "Increase the lifespan of your battery by setting a maximum charge value of 80%." + #[watch] + set_label: &format!("{:.0}%", model.display_brightness * 100.), }, }, - gtk4::Switch { - set_valign: gtk4::Align::Center, - }, - }, - - gtk4::Separator { - }, - - // Brightness - gtk4::Box { - #[watch] - set_visible: model.backlight.is_some(), - set_orientation: gtk4::Orientation::Horizontal, - gtk4::Image { - set_icon_name: Some("display-brightness-symbolic"), - }, - gtk4::Scale { - set_hexpand: true, - set_adjustment: >k4::Adjustment::new(0., 0., 1., 1., 1., 0.), + gtk4::Box { #[watch] - set_value: model.display_brightness, - connect_change_value[sender] => move |_, _, value| { - sender.input(AppMsg::SetDisplayBrightness(value)); - gtk4::Inhibit(false) + set_visible: model.kbd_backlight.is_some(), + set_orientation: gtk4::Orientation::Horizontal, + gtk4::Image { + set_icon_name: Some("keyboard-brightness-symbolic"), + }, + gtk4::Scale { + set_hexpand: true, + set_adjustment: >k4::Adjustment::new(0., 0., 1., 1., 1., 0.), + #[watch] + set_value: model.keyboard_brightness, + connect_change_value[sender] => move |_, _, value| { + sender.input(AppMsg::SetKeyboardBrightness(value)); + gtk4::Inhibit(false) + }, + }, + gtk4::Label { + #[watch] + set_label: &format!("{:.0}%", model.keyboard_brightness * 100.), }, }, - gtk4::Label { - #[watch] - set_label: &format!("{:.0}%", model.display_brightness * 100.), - }, - }, - gtk4::Box { - #[watch] - set_visible: model.kbd_backlight.is_some(), - set_orientation: gtk4::Orientation::Horizontal, - gtk4::Image { - set_icon_name: Some("keyboard-brightness-symbolic"), - }, - gtk4::Scale { - set_hexpand: true, - set_adjustment: >k4::Adjustment::new(0., 0., 1., 1., 1., 0.), - #[watch] - set_value: model.keyboard_brightness, - connect_change_value[sender] => move |_, _, value| { - sender.input(AppMsg::SetKeyboardBrightness(value)); - gtk4::Inhibit(false) - }, - }, - gtk4::Label { - #[watch] - set_label: &format!("{:.0}%", model.keyboard_brightness * 100.), - }, - }, - gtk4::Separator { - }, + gtk4::Separator { + }, - gtk4::Button { - set_label: "Power Settings...", - connect_clicked => move |_| { - // XXX open subpanel - let _ = Command::new("cosmic-settings").spawn(); - // TODO hide + gtk4::Button { + set_label: "Power Settings...", + connect_clicked => move |_| { + // XXX open subpanel + let _ = Command::new("cosmic-settings").spawn(); + // TODO hide + } } } } diff --git a/applets/cosmic-applet-graphics/src/main.rs b/applets/cosmic-applet-graphics/src/main.rs index dc70515b..b95ed7d1 100644 --- a/applets/cosmic-applet-graphics/src/main.rs +++ b/applets/cosmic-applet-graphics/src/main.rs @@ -10,6 +10,7 @@ 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, @@ -20,7 +21,6 @@ use gtk4::{ }; use once_cell::sync::Lazy; use tokio::runtime::Runtime; -use cosmic_panel_config::config::CosmicPanelConfig; static RT: Lazy = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime")); @@ -88,7 +88,7 @@ fn build_ui(application: >k4::Application) { 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 + let current_graphics = RT .block_on(get_current_graphics()) .expect("failed to connect to system76-power"); view! { diff --git a/applets/cosmic-applet-notifications/Cargo.toml b/applets/cosmic-applet-notifications/Cargo.toml index bf00089c..bc22b597 100644 --- a/applets/cosmic-applet-notifications/Cargo.toml +++ b/applets/cosmic-applet-notifications/Cargo.toml @@ -10,6 +10,7 @@ futures = "0.3" gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs" } libcosmic-applet = { path = "../../libcosmic-applet" } once_cell = "1.12" +relm4-macros = { git = "https://github.com/Relm4/Relm4.git", branch = "next" } serde = "1" zbus = "2.0.1" zbus_names = "2" diff --git a/applets/cosmic-applet-notifications/src/main.rs b/applets/cosmic-applet-notifications/src/main.rs index e9dc63d7..e31923ed 100644 --- a/applets/cosmic-applet-notifications/src/main.rs +++ b/applets/cosmic-applet-notifications/src/main.rs @@ -1,5 +1,5 @@ -use cascade::cascade; use gtk4::{glib, prelude::*}; +use relm4_macros::view; mod dbus_service; mod deref_cell; @@ -19,18 +19,20 @@ fn main() { let notification_list = NotificationList::new(¬ifications); - let window = cascade! { - libcosmic_applet::Applet::new(); - ..set_button_icon_name("user-invisible-symbolic"); // TODO - ..set_popover_child(Some(¬ification_list)); - ..show(); - }; + view! { + window = libcosmic_applet::AppletWindow { + #[wrap(Some)] + set_child: applet_button = &libcosmic_applet::AppletButton { + set_button_icon_name: "user-invisible-symbolic", // TODO + set_popover_child: Some(¬ification_list) + } + } + } + window.show(); // XXX show in correct place - cascade! { - NotificationPopover::new(¬ifications); - ..set_parent(&window.child().unwrap()); // XXX better way? - }; + let notification_popover = NotificationPopover::new(¬ifications); + notification_popover.set_parent(&applet_button); let main_loop = glib::MainLoop::new(None, false); main_loop.run(); diff --git a/applets/cosmic-applet-power/src/main.rs b/applets/cosmic-applet-power/src/main.rs index 0f131f37..d4bab867 100644 --- a/applets/cosmic-applet-power/src/main.rs +++ b/applets/cosmic-applet-power/src/main.rs @@ -24,34 +24,37 @@ fn main() { fn build_ui(application: >k4::Application) { view! { - window = libcosmic_applet::Applet { + window = libcosmic_applet::AppletWindow { set_title: Some("COSMIC Power Applet"), set_application: Some(application), // TODO adjust battery icon based on charge - set_button_icon_name: "system-shutdown-symbolic", #[wrap(Some)] - set_popover_child: main_box = >k4::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: settings_button = &Button { - #[wrap(Some)] - set_child = &Label { - set_label: "Settings...", - set_halign: Align::Start, - set_hexpand: true + set_child = &libcosmic_applet::AppletButton { + set_button_icon_name: "system-shutdown-symbolic", + #[wrap(Some)] + set_popover_child: main_box = >k4::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: settings_button = &Button { + #[wrap(Some)] + set_child = &Label { + set_label: "Settings...", + set_halign: Align::Start, + set_hexpand: true + }, + connect_clicked => move |_| { + let _ = Command::new("cosmic-settings").spawn(); + } }, - connect_clicked => move |_| { - let _ = Command::new("cosmic-settings").spawn(); - } - }, - append = &Separator {}, - append: &ui::session::build(), - append: second_separator = &Separator {}, - append: &ui::system::build(), + append = &Separator {}, + append: &ui::session::build(), + append: second_separator = &Separator {}, + append: &ui::system::build(), + } } } } diff --git a/applets/cosmic-applet-workspaces/src/main.rs b/applets/cosmic-applet-workspaces/src/main.rs index 681a452b..5a715a5a 100644 --- a/applets/cosmic-applet-workspaces/src/main.rs +++ b/applets/cosmic-applet-workspaces/src/main.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0-only +use calloop::channel::SyncSender; use gtk4::{ gdk::Display, gio::{self, ApplicationFlags}, @@ -13,7 +14,6 @@ use tokio::sync::mpsc; use utils::{Activate, WorkspaceEvent}; use wayland::State; use window::CosmicWorkspacesWindow; -use calloop::channel::SyncSender; mod localize; mod utils; diff --git a/applets/cosmic-applet-workspaces/src/wayland.rs b/applets/cosmic-applet-workspaces/src/wayland.rs index 06485485..59561afd 100644 --- a/applets/cosmic-applet-workspaces/src/wayland.rs +++ b/applets/cosmic-applet-workspaces/src/wayland.rs @@ -92,8 +92,8 @@ pub fn spawn_workspaces(tx: glib::Sender) -> SyncSender { running: true, }; let loop_handle = event_loop.handle(); - loop_handle.insert_source(workspaces_rx, |e, _, state| { - match e { + loop_handle + .insert_source(workspaces_rx, |e, _, state| match e { Event::Msg(WorkspaceEvent::Activate(id)) => { if let Some(w) = state .workspace_groups @@ -136,15 +136,16 @@ pub fn spawn_workspaces(tx: glib::Sender) -> SyncSender { } } } - Event::Closed => if let Some(workspace_manager) = &mut state.workspace_manager { - for g in &mut state.workspace_groups { - g.workspace_group_handle.destroy(); + Event::Closed => { + if let Some(workspace_manager) = &mut state.workspace_manager { + for g in &mut state.workspace_groups { + g.workspace_group_handle.destroy(); + } + workspace_manager.stop(); } - workspace_manager.stop(); - }, - } - - }).unwrap(); + } + }) + .unwrap(); while state.running { event_loop .dispatch(Duration::from_millis(16), &mut state) diff --git a/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs b/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs index 4b8d1c03..4c447474 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs @@ -43,9 +43,7 @@ impl WorkspaceButton { new_button.connect_clicked(move |_| { let id_clone = id.clone(); if !is_active { - let _ = TX.get() - .unwrap() - .send(WorkspaceEvent::Activate(id_clone)); + let _ = TX.get().unwrap().send(WorkspaceEvent::Activate(id_clone)); } }); diff --git a/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs b/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs index 0fe7b019..318bb56f 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs @@ -61,9 +61,7 @@ impl WorkspaceList { .build(); scroll_controller.connect_scroll(|_, dx, dy| { - let _ = TX.get() - .unwrap() - .send(WorkspaceEvent::Scroll(dx + dy)); + let _ = TX.get().unwrap().send(WorkspaceEvent::Scroll(dx + dy)); Inhibit::default() }); diff --git a/libcosmic-applet/src/button.rs b/libcosmic-applet/src/button.rs new file mode 100644 index 00000000..73626fdd --- /dev/null +++ b/libcosmic-applet/src/button.rs @@ -0,0 +1,126 @@ +use cosmic_panel_config::config::CosmicPanelConfig; +use gtk4::{glib, prelude::*, subclass::prelude::*}; +use relm4_macros::view; + +use crate::deref_cell::DerefCell; + +static STYLE: &str = " +button.cosmic_applet_button { + border-radius: 12px; + transition: 100ms; + padding: 4px; + border-color: transparent; + background: transparent; + outline-color: transparent; +} +"; + +#[derive(Default)] +pub struct AppletButtonInner { + menu_button: DerefCell, + panel_config: DerefCell, + popover: DerefCell, +} + +#[glib::object_subclass] +impl ObjectSubclass for AppletButtonInner { + const NAME: &'static str = "CosmicAppletButton"; + type Type = AppletButton; + type ParentType = gtk4::Widget; +} + +impl ObjectImpl for AppletButtonInner { + fn constructed(&self, obj: &AppletButton) { + view! { + menu_button = gtk4::MenuButton { + set_parent: obj, + add_css_class: "cosmic_applet_button", + set_has_frame: false, + #[wrap(Some)] + set_popover: popover = >k4::Popover { + // TODO: change if it can be positioned correctly? + set_has_arrow: false, + } + }, + provider = gtk4::CssProvider { + load_from_data: STYLE.as_bytes(), + } + } + obj.set_layout_manager(Some(>k4::BinLayout::new())); + obj.style_context() + .add_provider(&provider, gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION); + + self.menu_button.set(menu_button); + self.popover.set(popover); + self.panel_config + .set(CosmicPanelConfig::load_from_env().unwrap_or_default()); + } + + fn dispose(&self, _obj: &AppletButton) { + self.menu_button.unparent(); + } +} + +impl WidgetImpl for AppletButtonInner { + fn compute_expand(&self, _obj: &AppletButton, hexpand: &mut bool, vexpand: &mut bool) { + *hexpand = self + .menu_button + .compute_expand(gtk4::Orientation::Horizontal); + *vexpand = self.menu_button.compute_expand(gtk4::Orientation::Vertical); + } + + fn request_mode(&self, _obj: &AppletButton) -> gtk4::SizeRequestMode { + self.menu_button.request_mode() + } +} + +impl WindowImpl for AppletButtonInner {} + +glib::wrapper! { + pub struct AppletButton(ObjectSubclass) + @extends gtk4::Widget; +} + +impl Default for AppletButton { + fn default() -> Self { + Self::new() + } +} + +impl AppletButton { + pub fn new() -> Self { + glib::Object::new(&[]).unwrap() + } + + fn inner(&self) -> &AppletButtonInner { + AppletButtonInner::from_instance(self) + } + + // TODO: avoid multiple instances? + pub fn panel_config(&self) -> &CosmicPanelConfig { + &*self.inner().panel_config + } + + pub fn set_button_child(&self, child: Option<&impl IsA>) { + self.inner().menu_button.set_child(child); + } + + pub fn set_button_icon_name(&self, name: &str) { + let image = gtk4::Image::from_icon_name(name); + image.set_pixel_size( + self.panel_config() + .get_applet_icon_size() + .try_into() + .unwrap(), + ); // XXX unwrap + self.set_button_child(Some(&image)); + } + + pub fn set_button_label(&self, label: &str) { + self.inner().menu_button.set_label(label); + } + + pub fn set_popover_child(&self, child: Option<&impl IsA>) { + self.inner().popover.set_child(child); + } +} diff --git a/libcosmic-applet/src/lib.rs b/libcosmic-applet/src/lib.rs index 10d01904..6e2b20b6 100644 --- a/libcosmic-applet/src/lib.rs +++ b/libcosmic-applet/src/lib.rs @@ -1,9 +1,8 @@ -use cosmic_panel_config::config::CosmicPanelConfig; -use gtk4::{glib, prelude::*, subclass::prelude::*}; -use relm4_macros::view; - +mod button; +pub use button::AppletButton; mod deref_cell; -use deref_cell::DerefCell; +mod window; +pub use window::AppletWindow; // TODO make sure style fits different panel colors? // TODO abstraction to start main loop? Work with relm4. @@ -11,118 +10,4 @@ use deref_cell::DerefCell; // TODO orientation, etc. // TODO make image size dependent on CosmicPanelConfig? // TODO way to have multiple applets with this style, for system tray. - -static STYLE: &str = " -window.cosmic_applet_window { - background: transparent; -} - -button.cosmic_applet_button { - border-radius: 12px; - transition: 100ms; - padding: 4px; - border-color: transparent; - background: transparent; - outline-color: transparent; -} -"; - -#[derive(Default)] -pub struct AppletInner { - panel_config: DerefCell, - menu_button: DerefCell, - popover: DerefCell, -} - -#[glib::object_subclass] -impl ObjectSubclass for AppletInner { - const NAME: &'static str = "CosmicApplet"; - type Type = Applet; - type ParentType = gtk4::Window; -} - -impl ObjectImpl for AppletInner { - fn constructed(&self, obj: &Applet) { - let window = || obj; - view! { - window() { - add_css_class: "cosmic_applet_window", - set_decorated: false, - set_resizable: false, - set_width_request: 1, - set_height_request: 1, - #[wrap(Some)] - set_child: menu_button = >k4::MenuButton { - add_css_class: "cosmic_applet_button", - set_has_frame: false, - #[wrap(Some)] - set_popover: popover = >k4::Popover { - // TODO: change if it can be positioned correctly? - set_has_arrow: false, - } - } - } - } - - let provider = gtk4::CssProvider::new(); - provider.load_from_data(STYLE.as_bytes()); - obj.style_context() - .add_provider(&provider, gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION); - - self.menu_button.set(menu_button); - self.popover.set(popover); - self.panel_config - .set(CosmicPanelConfig::load_from_env().unwrap_or_default()); - } -} - -impl WidgetImpl for AppletInner {} -impl WindowImpl for AppletInner {} - -glib::wrapper! { - pub struct Applet(ObjectSubclass) - @extends gtk4::Widget, gtk4::Window; -} - -impl Default for Applet { - fn default() -> Self { - Self::new() - } -} - -impl Applet { - pub fn new() -> Self { - glib::Object::new(&[]).unwrap() - } - - fn inner(&self) -> &AppletInner { - AppletInner::from_instance(self) - } - - pub fn panel_config(&self) -> &CosmicPanelConfig { - &*self.inner().panel_config - } - - pub fn set_button_child(&self, child: Option<&impl IsA>) { - self.inner().menu_button.set_child(child); - } - - pub fn set_button_icon_name(&self, name: &str) { - let image = gtk4::Image::from_icon_name(name); - image.set_pixel_size( - self.panel_config() - .get_applet_icon_size() - .try_into() - .unwrap(), - ); // XXX unwrap - self.set_button_child(Some(&image)); - } - - pub fn set_button_label(&self, label: &str) { - self.inner().menu_button.set_label(label); - } - - pub fn set_popover_child(&self, child: Option<&impl IsA>) { - self.inner().popover.set_child(child); - } -} +// TODO also handle non-popover button? Is GtkMenuButton particularly special, or just use a toggle button? diff --git a/libcosmic-applet/src/window.rs b/libcosmic-applet/src/window.rs new file mode 100644 index 00000000..257658b3 --- /dev/null +++ b/libcosmic-applet/src/window.rs @@ -0,0 +1,58 @@ +use gtk4::{glib, prelude::*, subclass::prelude::*}; +use relm4_macros::view; + +static STYLE: &str = " +window.cosmic_applet_window { + background: transparent; +} +"; + +#[derive(Default)] +pub struct AppletWindowInner; + +#[glib::object_subclass] +impl ObjectSubclass for AppletWindowInner { + const NAME: &'static str = "CosmicAppletWindow"; + type Type = AppletWindow; + type ParentType = gtk4::Window; +} + +impl ObjectImpl for AppletWindowInner { + fn constructed(&self, obj: &AppletWindow) { + let window = || obj; + view! { + window() { + add_css_class: "cosmic_applet_window", + set_decorated: false, + set_resizable: false, + set_width_request: 1, + set_height_request: 1, + }, + provider = gtk4::CssProvider { + load_from_data: STYLE.as_bytes(), + } + } + obj.style_context() + .add_provider(&provider, gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION); + } +} + +impl WidgetImpl for AppletWindowInner {} +impl WindowImpl for AppletWindowInner {} + +glib::wrapper! { + pub struct AppletWindow(ObjectSubclass) + @extends gtk4::Widget, gtk4::Window; +} + +impl Default for AppletWindow { + fn default() -> Self { + Self::new() + } +} + +impl AppletWindow { + pub fn new() -> Self { + glib::Object::new(&[]).unwrap() + } +} From 765e3af815a36e87ae57dadfe45006fcf6d703b3 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 5 Jul 2022 16:21:23 -0700 Subject: [PATCH 2/3] Use libcomsic-applet in status-area, network, graphics --- Cargo.lock | 3 + applets/cosmic-applet-graphics/Cargo.toml | 1 + applets/cosmic-applet-graphics/src/main.rs | 245 ++++++++---------- applets/cosmic-applet-graphics/src/style.css | 29 --- applets/cosmic-applet-network/Cargo.toml | 1 + applets/cosmic-applet-network/src/main.rs | 89 ++----- applets/cosmic-applet-network/src/style.css | 28 -- .../src/ui/current_networks.rs | 8 +- applets/cosmic-applet-status-area/Cargo.toml | 1 + applets/cosmic-applet-status-area/src/main.rs | 23 +- .../src/status_menu.rs | 15 +- .../cosmic-applet-status-area/src/style.css | 33 --- libcosmic-applet/src/button.rs | 8 + 13 files changed, 154 insertions(+), 330 deletions(-) delete mode 100644 applets/cosmic-applet-network/src/style.css delete mode 100644 applets/cosmic-applet-status-area/src/style.css diff --git a/Cargo.lock b/Cargo.lock index 3a81ae20..4c178b3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/applets/cosmic-applet-graphics/Cargo.toml b/applets/cosmic-applet-graphics/Cargo.toml index 6dd95c55..fd29821c 100644 --- a/applets/cosmic-applet-graphics/Cargo.toml +++ b/applets/cosmic-applet-graphics/Cargo.toml @@ -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"] } diff --git a/applets/cosmic-applet-graphics/src/main.rs b/applets/cosmic-applet-graphics/src/main.rs index b95ed7d1..97f4327b 100644 --- a/applets/cosmic-applet-graphics/src/main.rs +++ b/applets/cosmic-applet-graphics/src/main.rs @@ -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 = 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 { 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: >k4::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: >k4::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::(PRIORITY_DEFAULT); view! { - main_overlay = Overlay { - add_overlay: loading_box = >k4::Box { - append: loading_explain_box = >k4::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 = >k4::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 = >k4::Box { + append: loading_explain_box = >k4::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 = >k4::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: >k4::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(); } diff --git a/applets/cosmic-applet-graphics/src/style.css b/applets/cosmic-applet-graphics/src/style.css index 8a91f3eb..ca16e327 100644 --- a/applets/cosmic-applet-graphics/src/style.css +++ b/applets/cosmic-applet-graphics/src/style.css @@ -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; -} \ No newline at end of file diff --git a/applets/cosmic-applet-network/Cargo.toml b/applets/cosmic-applet-network/Cargo.toml index d8b2c389..85ab9019 100644 --- a/applets/cosmic-applet-network/Cargo.toml +++ b/applets/cosmic-applet-network/Cargo.toml @@ -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"]} diff --git a/applets/cosmic-applet-network/src/main.rs b/applets/cosmic-applet-network/src/main.rs index deaa180b..20ca0bf6 100644 --- a/applets/cosmic-applet-network/src/main.rs +++ b/applets/cosmic-applet-network/src/main.rs @@ -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 = 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: >k4::Application) { - let provider = gtk4::CssProvider::new(); - provider.load_from_data(include_bytes!("style.css")); - gtk4::StyleContext::add_provider_for_display( - >k4::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 = >k4::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(); } diff --git a/applets/cosmic-applet-network/src/style.css b/applets/cosmic-applet-network/src/style.css deleted file mode 100644 index 7b11fdc0..00000000 --- a/applets/cosmic-applet-network/src/style.css +++ /dev/null @@ -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; -} diff --git a/applets/cosmic-applet-network/src/ui/current_networks.rs b/applets/cosmic-applet-network/src/ui/current_networks.rs index 0f389ece..5c1b85f1 100644 --- a/applets/cosmic-applet-network/src/ui/current_networks.rs +++ b/applets/cosmic-applet-network/src/ui/current_networks.rs @@ -18,7 +18,7 @@ use gtk4::{ use std::{cell::RefCell, net::IpAddr, rc::Rc}; use zbus::Connection; -pub fn add_current_networks(target: >k4::Box, icon_image: >k4::Image) { +pub fn add_current_networks(target: >k4::Box, icon_image: &libcosmic_applet::AppletButton) { let networks_list = ListBox::builder().show_separators(true).build(); let entries = Rc::>>::default(); let (tx, rx) = MainContext::channel::>(PRIORITY_DEFAULT); @@ -38,7 +38,7 @@ fn display_active_connections( connections: Vec, target: &ListBox, entries: &mut Vec, - icon_image: >k4::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) } }; diff --git a/applets/cosmic-applet-status-area/Cargo.toml b/applets/cosmic-applet-status-area/Cargo.toml index a6e5707e..b8e1e03c 100644 --- a/applets/cosmic-applet-status-area/Cargo.toml +++ b/applets/cosmic-applet-status-area/Cargo.toml @@ -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" diff --git a/applets/cosmic-applet-status-area/src/main.rs b/applets/cosmic-applet-status-area/src/main.rs index f584acc5..29cfa29e 100644 --- a/applets/cosmic-applet-status-area/src/main.rs +++ b/applets/cosmic-applet-status-area/src/main.rs @@ -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( - >k4::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(); diff --git a/applets/cosmic-applet-status-area/src/status_menu.rs b/applets/cosmic-applet-status-area/src/status_menu.rs index bc827e00..56bf2c72 100644 --- a/applets/cosmic-applet-status-area/src/status_menu.rs +++ b/applets/cosmic-applet-status-area/src/status_menu.rs @@ -19,7 +19,7 @@ struct Menu { #[derive(Default)] pub struct StatusMenuInner { - menu_button: DerefCell, + menu_button: DerefCell, vbox: DerefCell, item: DerefCell>, dbus_menu: DerefCell>, @@ -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::(&[]).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) diff --git a/applets/cosmic-applet-status-area/src/style.css b/applets/cosmic-applet-status-area/src/style.css deleted file mode 100644 index 8a91f3eb..00000000 --- a/applets/cosmic-applet-status-area/src/style.css +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/libcosmic-applet/src/button.rs b/libcosmic-applet/src/button.rs index 73626fdd..d434df3c 100644 --- a/libcosmic-applet/src/button.rs +++ b/libcosmic-applet/src/button.rs @@ -123,4 +123,12 @@ impl AppletButton { pub fn set_popover_child(&self, child: Option<&impl IsA>) { self.inner().popover.set_child(child); } + + pub fn popdown(&self) { + self.inner().popover.popdown(); + } + + pub fn popup(&self) { + self.inner().popover.popup(); + } } From ee142903bba69498e35eae9ab637374199ca3986 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 5 Jul 2022 16:52:04 -0700 Subject: [PATCH 3/3] Use `libcosmic-applet` in time applet Need to get button height right. --- Cargo.lock | 6 +--- applets/cosmic-applet-graphics/Cargo.toml | 1 - applets/cosmic-applet-network/Cargo.toml | 1 - .../cosmic-applet-notifications/Cargo.toml | 1 - applets/cosmic-applet-status-area/Cargo.toml | 1 - applets/cosmic-applet-time/Cargo.toml | 2 +- applets/cosmic-applet-time/src/main.rs | 25 ++++---------- applets/cosmic-applet-time/src/style.css | 33 ------------------- applets/cosmic-applet-time/src/time_button.rs | 20 ++++------- libcosmic-applet/src/button.rs | 9 +++++ 10 files changed, 24 insertions(+), 75 deletions(-) delete mode 100644 applets/cosmic-applet-time/src/style.css diff --git a/Cargo.lock b/Cargo.lock index 4c178b3e..d2e44ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,7 +366,6 @@ dependencies = [ name = "cosmic-applet-graphics" version = "0.1.0" dependencies = [ - "cosmic-panel-config", "gtk4", "libcosmic-applet", "once_cell", @@ -380,7 +379,6 @@ name = "cosmic-applet-network" version = "0.1.0" dependencies = [ "cosmic-dbus-networkmanager", - "cosmic-panel-config", "futures-util", "gtk4", "itertools", @@ -398,7 +396,6 @@ name = "cosmic-applet-notifications" version = "0.1.0" dependencies = [ "cascade", - "cosmic-panel-config", "futures", "gtk4", "libcosmic-applet", @@ -430,7 +427,6 @@ name = "cosmic-applet-status-area" version = "0.1.0" dependencies = [ "cascade", - "cosmic-panel-config", "futures", "gtk4", "libcosmic-applet", @@ -447,9 +443,9 @@ version = "0.1.0" dependencies = [ "cascade", "chrono", - "cosmic-panel-config", "futures", "gtk4", + "libcosmic-applet", "once_cell", "serde", "zbus", diff --git a/applets/cosmic-applet-graphics/Cargo.toml b/applets/cosmic-applet-graphics/Cargo.toml index fd29821c..9fa48c03 100644 --- a/applets/cosmic-applet-graphics/Cargo.toml +++ b/applets/cosmic-applet-graphics/Cargo.toml @@ -12,4 +12,3 @@ once_cell = "1.9.0" relm4-macros = { git = "https://github.com/Relm4/Relm4.git", branch = "next" } tokio = { version = "1.16.1", features = ["full"] } zbus = "2.1.1" -cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", features = ["gtk4"]} diff --git a/applets/cosmic-applet-network/Cargo.toml b/applets/cosmic-applet-network/Cargo.toml index 85ab9019..f7b83b82 100644 --- a/applets/cosmic-applet-network/Cargo.toml +++ b/applets/cosmic-applet-network/Cargo.toml @@ -16,4 +16,3 @@ 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"]} diff --git a/applets/cosmic-applet-notifications/Cargo.toml b/applets/cosmic-applet-notifications/Cargo.toml index bc22b597..a6c752a4 100644 --- a/applets/cosmic-applet-notifications/Cargo.toml +++ b/applets/cosmic-applet-notifications/Cargo.toml @@ -15,4 +15,3 @@ serde = "1" zbus = "2.0.1" zbus_names = "2" zvariant = "3" -cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", features = ["gtk4"]} diff --git a/applets/cosmic-applet-status-area/Cargo.toml b/applets/cosmic-applet-status-area/Cargo.toml index b8e1e03c..e61daf1a 100644 --- a/applets/cosmic-applet-status-area/Cargo.toml +++ b/applets/cosmic-applet-status-area/Cargo.toml @@ -14,4 +14,3 @@ serde = "1" zbus = "2.0.1" zbus_names = "2" zvariant = "3" -cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", features = ["gtk4"]} diff --git a/applets/cosmic-applet-time/Cargo.toml b/applets/cosmic-applet-time/Cargo.toml index 6880dc8e..3f869646 100644 --- a/applets/cosmic-applet-time/Cargo.toml +++ b/applets/cosmic-applet-time/Cargo.toml @@ -9,9 +9,9 @@ cascade = "1" chrono = "0.4" futures = "0.3" gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs", features = [ "v4_6" ] } +libcosmic-applet = { path = "../../libcosmic-applet" } once_cell = "1.12" serde = "1" zbus = "2.0.1" zbus_names = "2" zvariant = "3" -cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", features = ["gtk4"]} diff --git a/applets/cosmic-applet-time/src/main.rs b/applets/cosmic-applet-time/src/main.rs index 6233b522..bbb29413 100644 --- a/applets/cosmic-applet-time/src/main.rs +++ b/applets/cosmic-applet-time/src/main.rs @@ -1,3 +1,4 @@ +use cascade::cascade; use gtk4::{glib, prelude::*}; mod deref_cell; @@ -7,25 +8,11 @@ use time_button::TimeButton; fn main() { gtk4::init().unwrap(); - let provider = gtk4::CssProvider::new(); - provider.load_from_data(include_bytes!("style.css")); - gtk4::StyleContext::add_provider_for_display( - >k4::gdk::Display::default().expect("Could not connect to a display."), - &provider, - gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION, - ); - - let time_button = TimeButton::new(); - - gtk4::Window::builder() - .decorated(false) - .child(&time_button) - .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(&TimeButton::new())); + ..show(); + }; let main_loop = glib::MainLoop::new(None, false); main_loop.run(); diff --git a/applets/cosmic-applet-time/src/style.css b/applets/cosmic-applet-time/src/style.css deleted file mode 100644 index 8a91f3eb..00000000 --- a/applets/cosmic-applet-time/src/style.css +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/applets/cosmic-applet-time/src/time_button.rs b/applets/cosmic-applet-time/src/time_button.rs index c05369b2..d2bcb16d 100644 --- a/applets/cosmic-applet-time/src/time_button.rs +++ b/applets/cosmic-applet-time/src/time_button.rs @@ -11,7 +11,7 @@ use crate::deref_cell::DerefCell; #[derive(Default)] pub struct TimeButtonInner { calendar: DerefCell, - button: DerefCell, + button: DerefCell, label: DerefCell, } @@ -40,21 +40,15 @@ impl ObjectImpl for TimeButtonInner { })); }; - let popover = cascade! { - gtk4::Popover::new(); - ..set_child(Some(&cascade! { + let button = cascade! { + libcosmic_applet::AppletButton::new(); + ..set_parent(obj); + ..connect_activate(clone!(@strong obj => move |_| obj.opening())); + ..set_button_child(Some(&label)); + ..set_popover_child(Some(&cascade! { gtk4::Box::new(gtk4::Orientation::Horizontal, 0); ..append(&calendar); })); - ..connect_show(clone!(@strong obj => move |_| obj.opening())); - }; - - let button = cascade! { - gtk4::MenuButton::new(); - ..set_child(Some(&label)); - ..set_has_frame(false); - ..set_parent(obj); - ..set_popover(Some(&popover)); }; self.calendar.set(calendar); diff --git a/libcosmic-applet/src/button.rs b/libcosmic-applet/src/button.rs index d434df3c..32c4299f 100644 --- a/libcosmic-applet/src/button.rs +++ b/libcosmic-applet/src/button.rs @@ -131,4 +131,13 @@ impl AppletButton { pub fn popup(&self) { self.inner().popover.popup(); } + + // XXX better API? Actual signal + pub fn connect_activate(&self, f: F) -> glib::SignalHandlerId { + self.inner() + .menu_button + .connect_activate(glib::clone!(@weak self as _self => move |_| { + f(&_self) + })) + } }