Separate CosmicAppletWindow and CosmicAppletButton
This commit is contained in:
parent
74f6c2eca6
commit
aac43de65d
16 changed files with 421 additions and 343 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -401,6 +401,7 @@ dependencies = [
|
|||
"gtk4",
|
||||
"libcosmic-applet",
|
||||
"once_cell",
|
||||
"relm4-macros",
|
||||
"serde",
|
||||
"zbus",
|
||||
"zbus_names",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<Cell<Option<u32>>>,
|
||||
pub position: Rc<Cell<PanelAnchor>>,
|
||||
pub tx: OnceCell<mpsc::Sender<Event>>,
|
||||
pub config: OnceCell<CosmicPanelConfig>
|
||||
pub config: OnceCell<CosmicPanelConfig>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Runtime> = 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! {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ pub fn spawn_workspaces(tx: glib::Sender<State>) -> SyncSender<WorkspaceEvent> {
|
|||
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<State>) -> SyncSender<WorkspaceEvent> {
|
|||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
});
|
||||
|
||||
|
|
|
|||
126
libcosmic-applet/src/button.rs
Normal file
126
libcosmic-applet/src/button.rs
Normal file
|
|
@ -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<gtk4::MenuButton>,
|
||||
panel_config: DerefCell<CosmicPanelConfig>,
|
||||
popover: DerefCell<gtk4::Popover>,
|
||||
}
|
||||
|
||||
#[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<AppletButtonInner>)
|
||||
@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<gtk4::Widget>>) {
|
||||
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<gtk4::Widget>>) {
|
||||
self.inner().popover.set_child(child);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CosmicPanelConfig>,
|
||||
menu_button: DerefCell<gtk4::MenuButton>,
|
||||
popover: DerefCell<gtk4::Popover>,
|
||||
}
|
||||
|
||||
#[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<AppletInner>)
|
||||
@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<gtk4::Widget>>) {
|
||||
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<gtk4::Widget>>) {
|
||||
self.inner().popover.set_child(child);
|
||||
}
|
||||
}
|
||||
// TODO also handle non-popover button? Is GtkMenuButton particularly special, or just use a toggle button?
|
||||
|
|
|
|||
58
libcosmic-applet/src/window.rs
Normal file
58
libcosmic-applet/src/window.rs
Normal file
|
|
@ -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<AppletWindowInner>)
|
||||
@extends gtk4::Widget, gtk4::Window;
|
||||
}
|
||||
|
||||
impl Default for AppletWindow {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AppletWindow {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new(&[]).unwrap()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue