mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-04-19 19:34:24 +02:00
refactor: begin restructuring core code to better encapsulate
This is a first pass towards trying to structure things a bit better, with data generally encapsulated under a single hierarchical tree, rather than lots of globals all over the place. Lots of work is still required. The plan is that with this and some more work, #291 should become a lot easier to sort.
This commit is contained in:
parent
08e354e019
commit
b2fa19ab6c
14 changed files with 490 additions and 415 deletions
400
src/bar.rs
400
src/bar.rs
|
@ -3,8 +3,7 @@ use crate::modules::{
|
|||
create_module, set_widget_identifiers, wrap_widget, ModuleInfo, ModuleLocation,
|
||||
};
|
||||
use crate::popup::Popup;
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::{Config, GlobalState};
|
||||
use crate::{Config, Ironbar};
|
||||
use color_eyre::Result;
|
||||
use gtk::gdk::Monitor;
|
||||
use gtk::prelude::*;
|
||||
|
@ -13,129 +12,228 @@ use std::cell::RefCell;
|
|||
use std::rc::Rc;
|
||||
use tracing::{debug, info};
|
||||
|
||||
/// Creates a new window for a bar,
|
||||
/// sets it up and adds its widgets.
|
||||
pub fn create_bar(
|
||||
app: &Application,
|
||||
monitor: &Monitor,
|
||||
monitor_name: &str,
|
||||
config: Config,
|
||||
global_state: &Rc<RefCell<GlobalState>>,
|
||||
) -> Result<()> {
|
||||
let win = ApplicationWindow::builder().application(app).build();
|
||||
let bar_name = config
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("bar-{}", get_unique_usize()));
|
||||
|
||||
win.set_widget_name(&bar_name);
|
||||
info!("Creating bar {}", bar_name);
|
||||
|
||||
setup_layer_shell(
|
||||
&win,
|
||||
monitor,
|
||||
config.position,
|
||||
config.anchor_to_edges,
|
||||
config.margin,
|
||||
);
|
||||
|
||||
let orientation = config.position.get_orientation();
|
||||
|
||||
let content = gtk::Box::builder()
|
||||
.orientation(orientation)
|
||||
.spacing(0)
|
||||
.hexpand(false)
|
||||
.name("bar");
|
||||
|
||||
let content = if orientation == Orientation::Horizontal {
|
||||
content.height_request(config.height)
|
||||
} else {
|
||||
content.width_request(config.height)
|
||||
}
|
||||
.build();
|
||||
|
||||
content.style_context().add_class("container");
|
||||
|
||||
let start = create_container("start", orientation);
|
||||
let center = create_container("center", orientation);
|
||||
let end = create_container("end", orientation);
|
||||
|
||||
content.add(&start);
|
||||
content.set_center_widget(Some(¢er));
|
||||
content.pack_end(&end, false, false, 0);
|
||||
|
||||
let load_result = load_modules(&start, ¢er, &end, app, config, monitor, monitor_name)?;
|
||||
global_state
|
||||
.borrow_mut()
|
||||
.popups_mut()
|
||||
.insert(bar_name.into(), load_result.popup);
|
||||
|
||||
win.add(&content);
|
||||
|
||||
win.connect_destroy_event(|_, _| {
|
||||
info!("Shutting down");
|
||||
gtk::main_quit();
|
||||
Inhibit(false)
|
||||
});
|
||||
|
||||
debug!("Showing bar");
|
||||
|
||||
// show each box but do not use `show_all`.
|
||||
// this ensures `show_if` option works as intended.
|
||||
start.show();
|
||||
center.show();
|
||||
end.show();
|
||||
content.show();
|
||||
win.show();
|
||||
|
||||
Ok(())
|
||||
#[derive(Debug, Clone)]
|
||||
enum Inner {
|
||||
New { config: Option<Config> },
|
||||
Loaded { popup: Rc<RefCell<Popup>> },
|
||||
}
|
||||
|
||||
/// Sets up GTK layer shell for a provided application window.
|
||||
fn setup_layer_shell(
|
||||
win: &ApplicationWindow,
|
||||
monitor: &Monitor,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bar {
|
||||
name: String,
|
||||
monitor_name: String,
|
||||
position: BarPosition,
|
||||
anchor_to_edges: bool,
|
||||
margin: MarginConfig,
|
||||
) {
|
||||
gtk_layer_shell::init_for_window(win);
|
||||
gtk_layer_shell::set_monitor(win, monitor);
|
||||
gtk_layer_shell::set_layer(win, gtk_layer_shell::Layer::Top);
|
||||
gtk_layer_shell::auto_exclusive_zone_enable(win);
|
||||
gtk_layer_shell::set_namespace(win, env!("CARGO_PKG_NAME"));
|
||||
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Top, margin.top);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Bottom, margin.bottom);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Left, margin.left);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Right, margin.right);
|
||||
window: ApplicationWindow,
|
||||
|
||||
let bar_orientation = position.get_orientation();
|
||||
content: gtk::Box,
|
||||
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Top,
|
||||
position == BarPosition::Top
|
||||
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
||||
);
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Bottom,
|
||||
position == BarPosition::Bottom
|
||||
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
||||
);
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Left,
|
||||
position == BarPosition::Left
|
||||
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
||||
);
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Right,
|
||||
position == BarPosition::Right
|
||||
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
||||
);
|
||||
start: gtk::Box,
|
||||
center: gtk::Box,
|
||||
end: gtk::Box,
|
||||
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
impl Bar {
|
||||
pub fn new(app: &Application, monitor_name: String, config: Config) -> Self {
|
||||
let window = ApplicationWindow::builder().application(app).build();
|
||||
let name = config
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("bar-{}", Ironbar::unique_id()));
|
||||
|
||||
window.set_widget_name(&name);
|
||||
|
||||
let position = config.position;
|
||||
let orientation = position.get_orientation();
|
||||
|
||||
let content = gtk::Box::builder()
|
||||
.orientation(orientation)
|
||||
.spacing(0)
|
||||
.hexpand(false)
|
||||
.name("bar");
|
||||
|
||||
let content = if orientation == Orientation::Horizontal {
|
||||
content.height_request(config.height)
|
||||
} else {
|
||||
content.width_request(config.height)
|
||||
}
|
||||
.build();
|
||||
|
||||
content.style_context().add_class("container");
|
||||
|
||||
let start = create_container("start", orientation);
|
||||
let center = create_container("center", orientation);
|
||||
let end = create_container("end", orientation);
|
||||
|
||||
content.add(&start);
|
||||
content.set_center_widget(Some(¢er));
|
||||
content.pack_end(&end, false, false, 0);
|
||||
|
||||
window.add(&content);
|
||||
|
||||
window.connect_destroy_event(|_, _| {
|
||||
info!("Shutting down");
|
||||
gtk::main_quit();
|
||||
Inhibit(false)
|
||||
});
|
||||
|
||||
Bar {
|
||||
name,
|
||||
monitor_name,
|
||||
position,
|
||||
window,
|
||||
content,
|
||||
start,
|
||||
center,
|
||||
end,
|
||||
inner: Inner::New {
|
||||
config: Some(config),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(mut self, monitor: &Monitor) -> Result<Self> {
|
||||
let Inner::New { ref mut config } = self.inner else {
|
||||
return Ok(self);
|
||||
};
|
||||
|
||||
let Some(config) = config.take() else {
|
||||
return Ok(self);
|
||||
};
|
||||
|
||||
info!(
|
||||
"Initializing bar '{}' on '{}'",
|
||||
self.name, self.monitor_name
|
||||
);
|
||||
|
||||
self.setup_layer_shell(config.anchor_to_edges, config.margin, monitor);
|
||||
|
||||
let load_result = self.load_modules(config, monitor)?;
|
||||
|
||||
self.show();
|
||||
|
||||
self.inner = Inner::Loaded {
|
||||
popup: load_result.popup,
|
||||
};
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Sets up GTK layer shell for a provided application window.
|
||||
fn setup_layer_shell(&self, anchor_to_edges: bool, margin: MarginConfig, monitor: &Monitor) {
|
||||
let win = &self.window;
|
||||
let position = self.position;
|
||||
|
||||
gtk_layer_shell::init_for_window(win);
|
||||
gtk_layer_shell::set_monitor(win, monitor);
|
||||
gtk_layer_shell::set_layer(win, gtk_layer_shell::Layer::Top);
|
||||
gtk_layer_shell::auto_exclusive_zone_enable(win);
|
||||
gtk_layer_shell::set_namespace(win, env!("CARGO_PKG_NAME"));
|
||||
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Top, margin.top);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Bottom, margin.bottom);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Left, margin.left);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Right, margin.right);
|
||||
|
||||
let bar_orientation = position.get_orientation();
|
||||
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Top,
|
||||
position == BarPosition::Top
|
||||
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
||||
);
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Bottom,
|
||||
position == BarPosition::Bottom
|
||||
|| (bar_orientation == Orientation::Vertical && anchor_to_edges),
|
||||
);
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Left,
|
||||
position == BarPosition::Left
|
||||
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
||||
);
|
||||
gtk_layer_shell::set_anchor(
|
||||
win,
|
||||
gtk_layer_shell::Edge::Right,
|
||||
position == BarPosition::Right
|
||||
|| (bar_orientation == Orientation::Horizontal && anchor_to_edges),
|
||||
);
|
||||
}
|
||||
|
||||
/// Loads the configured modules onto a bar.
|
||||
fn load_modules(&self, config: Config, monitor: &Monitor) -> Result<BarLoadResult> {
|
||||
let icon_theme = IconTheme::new();
|
||||
if let Some(ref theme) = config.icon_theme {
|
||||
icon_theme.set_custom_theme(Some(theme));
|
||||
}
|
||||
|
||||
let app = &self.window.application().expect("to exist");
|
||||
|
||||
macro_rules! info {
|
||||
($location:expr) => {
|
||||
ModuleInfo {
|
||||
app,
|
||||
bar_position: config.position,
|
||||
monitor,
|
||||
output_name: &self.monitor_name,
|
||||
location: $location,
|
||||
icon_theme: &icon_theme,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// popup ignores module location so can bodge this for now
|
||||
let popup = Popup::new(&info!(ModuleLocation::Left), config.popup_gap);
|
||||
let popup = Rc::new(RefCell::new(popup));
|
||||
|
||||
if let Some(modules) = config.start {
|
||||
let info = info!(ModuleLocation::Left);
|
||||
add_modules(&self.start, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
if let Some(modules) = config.center {
|
||||
let info = info!(ModuleLocation::Center);
|
||||
add_modules(&self.center, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
if let Some(modules) = config.end {
|
||||
let info = info!(ModuleLocation::Right);
|
||||
add_modules(&self.end, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
let result = BarLoadResult { popup };
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn show(&self) {
|
||||
debug!("Showing bar: {}", self.name);
|
||||
|
||||
// show each box but do not use `show_all`.
|
||||
// this ensures `show_if` option works as intended.
|
||||
self.start.show();
|
||||
self.center.show();
|
||||
self.end.show();
|
||||
self.content.show();
|
||||
self.window.show();
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn popup(&self) -> Rc<RefCell<Popup>> {
|
||||
match &self.inner {
|
||||
Inner::New { .. } => {
|
||||
panic!("Attempted to get popup of uninitialized bar. This is a serious bug!")
|
||||
}
|
||||
Inner::Loaded { popup } => popup.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `gtk::Box` container to place widgets inside.
|
||||
|
@ -155,58 +253,6 @@ struct BarLoadResult {
|
|||
popup: Rc<RefCell<Popup>>,
|
||||
}
|
||||
|
||||
/// Loads the configured modules onto a bar.
|
||||
fn load_modules(
|
||||
left: >k::Box,
|
||||
center: >k::Box,
|
||||
right: >k::Box,
|
||||
app: &Application,
|
||||
config: Config,
|
||||
monitor: &Monitor,
|
||||
output_name: &str,
|
||||
) -> Result<BarLoadResult> {
|
||||
let icon_theme = IconTheme::new();
|
||||
if let Some(ref theme) = config.icon_theme {
|
||||
icon_theme.set_custom_theme(Some(theme));
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($location:expr) => {
|
||||
ModuleInfo {
|
||||
app,
|
||||
bar_position: config.position,
|
||||
monitor,
|
||||
output_name,
|
||||
location: $location,
|
||||
icon_theme: &icon_theme,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// popup ignores module location so can bodge this for now
|
||||
let popup = Popup::new(&info!(ModuleLocation::Left), config.popup_gap);
|
||||
let popup = Rc::new(RefCell::new(popup));
|
||||
|
||||
if let Some(modules) = config.start {
|
||||
let info = info!(ModuleLocation::Left);
|
||||
add_modules(left, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
if let Some(modules) = config.center {
|
||||
let info = info!(ModuleLocation::Center);
|
||||
add_modules(center, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
if let Some(modules) = config.end {
|
||||
let info = info!(ModuleLocation::Right);
|
||||
add_modules(right, modules, &info, &popup)?;
|
||||
}
|
||||
|
||||
let result = BarLoadResult { popup };
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Adds modules into a provided GTK box,
|
||||
/// which should be one of its left, center or right containers.
|
||||
fn add_modules(
|
||||
|
@ -235,7 +281,7 @@ fn add_modules(
|
|||
}
|
||||
|
||||
for config in modules {
|
||||
let id = get_unique_usize();
|
||||
let id = Ironbar::unique_id();
|
||||
match config {
|
||||
#[cfg(feature = "clipboard")]
|
||||
ModuleConfig::Clipboard(mut module) => add_module!(module, id),
|
||||
|
@ -261,3 +307,13 @@ fn add_modules(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_bar(
|
||||
app: &Application,
|
||||
monitor: &Monitor,
|
||||
monitor_name: String,
|
||||
config: Config,
|
||||
) -> Result<Bar> {
|
||||
let bar = Bar::new(app, monitor_name, config);
|
||||
bar.init(monitor)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::unique_id::get_unique_usize;
|
||||
use crate::{arc_mut, lock, send};
|
||||
use crate::{arc_mut, lock, send, Ironbar};
|
||||
use async_once::AsyncOnce;
|
||||
use color_eyre::Report;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -25,7 +24,7 @@ pub struct TrayEventReceiver {
|
|||
|
||||
impl TrayEventReceiver {
|
||||
async fn new() -> system_tray::error::Result<Self> {
|
||||
let id = format!("ironbar-{}", get_unique_usize());
|
||||
let id = format!("ironbar-{}", Ironbar::unique_id());
|
||||
|
||||
let (tx, rx) = mpsc::channel(16);
|
||||
let (b_tx, b_rx) = broadcast::channel(16);
|
||||
|
|
|
@ -7,8 +7,7 @@ use self::device::{DataControlDeviceDataExt, DataControlDeviceHandler};
|
|||
use self::offer::{DataControlDeviceOffer, DataControlOfferHandler, SelectionOffer};
|
||||
use self::source::DataControlSourceHandler;
|
||||
use crate::clients::wayland::Environment;
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::{lock, send};
|
||||
use crate::{lock, send, Ironbar};
|
||||
use device::DataControlDevice;
|
||||
use glib::Bytes;
|
||||
use nix::fcntl::{fcntl, F_GETPIPE_SZ, F_SETPIPE_SZ};
|
||||
|
@ -145,7 +144,7 @@ impl Environment {
|
|||
};
|
||||
|
||||
Ok(ClipboardItem {
|
||||
id: get_unique_usize(),
|
||||
id: Ironbar::unique_id(),
|
||||
value,
|
||||
mime_type: mime_type.value.clone(),
|
||||
})
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use super::manager::ToplevelManagerState;
|
||||
use crate::lock;
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::{lock, Ironbar};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tracing::trace;
|
||||
|
@ -68,7 +67,7 @@ pub struct ToplevelInfo {
|
|||
impl Default for ToplevelInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: get_unique_usize(),
|
||||
id: Ironbar::unique_id(),
|
||||
app_id: String::new(),
|
||||
title: String::new(),
|
||||
fullscreen: false,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(feature = "ipc")]
|
||||
use crate::ironvar::get_variable_manager;
|
||||
use crate::script::Script;
|
||||
use crate::send;
|
||||
#[cfg(feature = "ipc")]
|
||||
use crate::Ironbar;
|
||||
use cfg_if::cfg_if;
|
||||
use glib::Continue;
|
||||
use serde::Deserialize;
|
||||
|
@ -55,7 +55,7 @@ impl DynamicBool {
|
|||
}
|
||||
#[cfg(feature = "ipc")]
|
||||
DynamicBool::Variable(variable) => {
|
||||
let variable_manager = get_variable_manager();
|
||||
let variable_manager = Ironbar::variable_manager();
|
||||
|
||||
let variable_name = variable[1..].into(); // remove hash
|
||||
let mut rx = crate::write_lock!(variable_manager).subscribe(variable_name);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#[cfg(feature = "ipc")]
|
||||
use crate::ironvar::get_variable_manager;
|
||||
use crate::script::{OutputStream, Script};
|
||||
#[cfg(feature = "ipc")]
|
||||
use crate::Ironbar;
|
||||
use crate::{arc_mut, lock, send};
|
||||
use gtk::prelude::*;
|
||||
use tokio::spawn;
|
||||
|
@ -72,7 +72,7 @@ where
|
|||
lock!(label_parts).push(String::new());
|
||||
|
||||
spawn(async move {
|
||||
let variable_manager = get_variable_manager();
|
||||
let variable_manager = Ironbar::variable_manager();
|
||||
let mut rx = crate::write_lock!(variable_manager).subscribe(name);
|
||||
|
||||
while let Ok(value) = rx.recv().await {
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
use crate::popup::Popup;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Global application state shared across all bars.
|
||||
///
|
||||
/// Data that needs to be accessed from anywhere
|
||||
/// that is not otherwise accessible should be placed on here.
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalState {
|
||||
popups: HashMap<Box<str>, Rc<RefCell<Popup>>>,
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
popups: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn popups(&self) -> &HashMap<Box<str>, Rc<RefCell<Popup>>> {
|
||||
&self.popups
|
||||
}
|
||||
|
||||
pub fn popups_mut(&mut self) -> &mut HashMap<Box<str>, Rc<RefCell<Popup>>> {
|
||||
&mut self.popups
|
||||
}
|
||||
|
||||
pub fn with_popup_mut<F, T>(&self, monitor_name: &str, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(RefMut<Popup>) -> T,
|
||||
{
|
||||
let popup = self.popups().get(monitor_name);
|
||||
|
||||
if let Some(popup) = popup {
|
||||
let popup = popup.borrow_mut();
|
||||
Some(f(popup))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,25 +3,21 @@ pub mod commands;
|
|||
pub mod responses;
|
||||
mod server;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::GlobalState;
|
||||
pub use commands::Command;
|
||||
pub use responses::Response;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ipc {
|
||||
path: PathBuf,
|
||||
global_state: Rc<RefCell<GlobalState>>,
|
||||
}
|
||||
|
||||
impl Ipc {
|
||||
/// Creates a new IPC instance.
|
||||
/// This can be used as both a server and client.
|
||||
pub fn new(global_state: Rc<RefCell<GlobalState>>) -> Self {
|
||||
pub fn new() -> Self {
|
||||
let ipc_socket_file = std::env::var("XDG_RUNTIME_DIR")
|
||||
.map_or_else(|_| PathBuf::from("/tmp"), PathBuf::from)
|
||||
.join("ironbar-ipc.sock");
|
||||
|
@ -32,7 +28,6 @@ impl Ipc {
|
|||
|
||||
Self {
|
||||
path: ipc_socket_file,
|
||||
global_state,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::cell::RefCell;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
@ -15,10 +14,9 @@ use tracing::{debug, error, info, warn};
|
|||
|
||||
use crate::bridge_channel::BridgeChannel;
|
||||
use crate::ipc::{Command, Response};
|
||||
use crate::ironvar::get_variable_manager;
|
||||
use crate::modules::PopupButton;
|
||||
use crate::style::load_css;
|
||||
use crate::{read_lock, send_async, try_send, write_lock, GlobalState};
|
||||
use crate::{read_lock, send_async, try_send, write_lock, Ironbar};
|
||||
|
||||
use super::Ipc;
|
||||
|
||||
|
@ -26,7 +24,7 @@ impl Ipc {
|
|||
/// Starts the IPC server on its socket.
|
||||
///
|
||||
/// Once started, the server will begin accepting connections.
|
||||
pub fn start(&self, application: &Application) {
|
||||
pub fn start(&self, application: &Application, ironbar: Rc<Ironbar>) {
|
||||
let bridge = BridgeChannel::<Command>::new();
|
||||
let cmd_tx = bridge.create_sender();
|
||||
let (res_tx, mut res_rx) = mpsc::channel(32);
|
||||
|
@ -70,9 +68,8 @@ impl Ipc {
|
|||
});
|
||||
|
||||
let application = application.clone();
|
||||
let global_state = self.global_state.clone();
|
||||
bridge.recv(move |command| {
|
||||
let res = Self::handle_command(command, &application, &global_state);
|
||||
let res = Self::handle_command(command, &application, ironbar.clone());
|
||||
try_send!(res_tx, res);
|
||||
Continue(true)
|
||||
});
|
||||
|
@ -115,7 +112,7 @@ impl Ipc {
|
|||
fn handle_command(
|
||||
command: Command,
|
||||
application: &Application,
|
||||
global_state: &Rc<RefCell<GlobalState>>,
|
||||
ironbar: Rc<Ironbar>,
|
||||
) -> Response {
|
||||
match command {
|
||||
Command::Inspect => {
|
||||
|
@ -129,12 +126,12 @@ impl Ipc {
|
|||
window.close();
|
||||
}
|
||||
|
||||
crate::load_interface(application, global_state);
|
||||
*ironbar.bars.borrow_mut() = crate::load_interface(application);
|
||||
|
||||
Response::Ok
|
||||
}
|
||||
Command::Set { key, value } => {
|
||||
let variable_manager = get_variable_manager();
|
||||
let variable_manager = Ironbar::variable_manager();
|
||||
let mut variable_manager = write_lock!(variable_manager);
|
||||
match variable_manager.set(key, value) {
|
||||
Ok(()) => Response::Ok,
|
||||
|
@ -142,7 +139,7 @@ impl Ipc {
|
|||
}
|
||||
}
|
||||
Command::Get { key } => {
|
||||
let variable_manager = get_variable_manager();
|
||||
let variable_manager = Ironbar::variable_manager();
|
||||
let value = read_lock!(variable_manager).get(&key);
|
||||
match value {
|
||||
Some(value) => Response::OkValue { value },
|
||||
|
@ -158,68 +155,85 @@ impl Ipc {
|
|||
}
|
||||
}
|
||||
Command::TogglePopup { bar_name, name } => {
|
||||
let global_state = global_state.borrow();
|
||||
let response = global_state.with_popup_mut(&bar_name, |mut popup| {
|
||||
let current_widget = popup.current_widget();
|
||||
popup.hide();
|
||||
let bar = ironbar.bar_by_name(&bar_name);
|
||||
|
||||
let data = popup
|
||||
.cache
|
||||
.iter()
|
||||
.find(|(_, (module_name, _))| module_name == &name)
|
||||
.map(|module| (module, module.1 .1.buttons.first()));
|
||||
match bar {
|
||||
Some(bar) => {
|
||||
let popup = bar.popup();
|
||||
let current_widget = popup.borrow().current_widget();
|
||||
|
||||
match data {
|
||||
Some(((&id, _), Some(button))) if current_widget != Some(id) => {
|
||||
let button_id = button.popup_id();
|
||||
popup.show(id, button_id);
|
||||
popup.borrow_mut().hide();
|
||||
|
||||
Response::Ok
|
||||
let data = popup
|
||||
.borrow()
|
||||
.cache
|
||||
.iter()
|
||||
.find(|(_, value)| value.name == name)
|
||||
.map(|(id, value)| (*id, value.content.buttons.first().cloned()));
|
||||
|
||||
match data {
|
||||
Some((id, Some(button))) if current_widget != Some(id) => {
|
||||
let button_id = button.popup_id();
|
||||
let mut popup = popup.borrow_mut();
|
||||
|
||||
if popup.is_visible() {
|
||||
popup.hide();
|
||||
} else {
|
||||
popup.show(id, button_id);
|
||||
}
|
||||
|
||||
Response::Ok
|
||||
}
|
||||
Some((_, None)) => Response::error("Module has no popup functionality"),
|
||||
Some(_) => Response::Ok,
|
||||
None => Response::error("Invalid module name"),
|
||||
}
|
||||
Some((_, None)) => Response::error("Module has no popup functionality"),
|
||||
Some(_) => Response::Ok,
|
||||
None => Response::error("Invalid module name"),
|
||||
}
|
||||
});
|
||||
|
||||
response.unwrap_or_else(|| Response::error("Invalid monitor name"))
|
||||
None => Response::error("Invalid bar name"),
|
||||
}
|
||||
}
|
||||
Command::OpenPopup { bar_name, name } => {
|
||||
let global_state = global_state.borrow();
|
||||
let response = global_state.with_popup_mut(&bar_name, |mut popup| {
|
||||
// only one popup per bar, so hide if open for another widget
|
||||
popup.hide();
|
||||
let bar = ironbar.bar_by_name(&bar_name);
|
||||
|
||||
let data = popup
|
||||
.cache
|
||||
.iter()
|
||||
.find(|(_, (module_name, _))| module_name == &name)
|
||||
.map(|module| (module, module.1 .1.buttons.first()));
|
||||
match bar {
|
||||
Some(bar) => {
|
||||
let popup = bar.popup();
|
||||
|
||||
match data {
|
||||
Some(((&id, _), Some(button))) => {
|
||||
let button_id = button.popup_id();
|
||||
popup.show(id, button_id);
|
||||
// only one popup per bar, so hide if open for another widget
|
||||
popup.borrow_mut().hide();
|
||||
|
||||
Response::Ok
|
||||
let data = popup
|
||||
.borrow()
|
||||
.cache
|
||||
.iter()
|
||||
.find(|(_, value)| value.name == name)
|
||||
.map(|(id, value)| (*id, value.content.buttons.first().cloned()));
|
||||
|
||||
match data {
|
||||
Some((id, Some(button))) => {
|
||||
let button_id = button.popup_id();
|
||||
popup.borrow_mut().show(id, button_id);
|
||||
|
||||
Response::Ok
|
||||
}
|
||||
Some((_, None)) => Response::error("Module has no popup functionality"),
|
||||
None => Response::error("Invalid module name"),
|
||||
}
|
||||
Some((_, None)) => Response::error("Module has no popup functionality"),
|
||||
None => Response::error("Invalid module name"),
|
||||
}
|
||||
});
|
||||
|
||||
response.unwrap_or_else(|| Response::error("Invalid monitor name"))
|
||||
None => Response::error("Invalid bar name"),
|
||||
}
|
||||
}
|
||||
Command::ClosePopup { bar_name } => {
|
||||
let global_state = global_state.borrow();
|
||||
let popup_found = global_state
|
||||
.with_popup_mut(&bar_name, |mut popup| popup.hide())
|
||||
.is_some();
|
||||
let bar = ironbar.bar_by_name(&bar_name);
|
||||
|
||||
if popup_found {
|
||||
Response::Ok
|
||||
} else {
|
||||
Response::error("Invalid monitor name")
|
||||
match bar {
|
||||
Some(bar) => {
|
||||
let popup = bar.popup();
|
||||
popup.borrow_mut().hide();
|
||||
|
||||
Response::Ok
|
||||
}
|
||||
None => Response::error("Invalid bar name"),
|
||||
}
|
||||
}
|
||||
Command::Ping => Response::Ok,
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
#![doc = include_str!("../docs/Ironvars.md")]
|
||||
|
||||
use crate::{arc_rw, send};
|
||||
use crate::send;
|
||||
use color_eyre::{Report, Result};
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
lazy_static! {
|
||||
static ref VARIABLE_MANAGER: Arc<RwLock<VariableManager>> = arc_rw!(VariableManager::new());
|
||||
}
|
||||
|
||||
pub fn get_variable_manager() -> Arc<RwLock<VariableManager>> {
|
||||
VARIABLE_MANAGER.clone()
|
||||
}
|
||||
|
||||
/// Global singleton manager for `IronVar` variables.
|
||||
pub struct VariableManager {
|
||||
variables: HashMap<Box<str>, IronVar>,
|
||||
}
|
||||
|
||||
impl Default for VariableManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl VariableManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
255
src/main.rs
255
src/main.rs
|
@ -1,12 +1,15 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::future::Future;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::mpsc;
|
||||
#[cfg(feature = "ipc")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
#[cfg(feature = "cli")]
|
||||
|
@ -14,9 +17,11 @@ use clap::Parser;
|
|||
use color_eyre::eyre::Result;
|
||||
use color_eyre::Report;
|
||||
use dirs::config_dir;
|
||||
use glib::PropertySet;
|
||||
use gtk::gdk::Display;
|
||||
use gtk::prelude::*;
|
||||
use gtk::Application;
|
||||
use lazy_static::lazy_static;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::task::{block_in_place, spawn_blocking};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
@ -24,10 +29,11 @@ use universal_config::ConfigLoader;
|
|||
|
||||
use clients::wayland;
|
||||
|
||||
use crate::bar::create_bar;
|
||||
use crate::bar::{create_bar, Bar};
|
||||
use crate::config::{Config, MonitorConfig};
|
||||
use crate::error::ExitCode;
|
||||
use crate::global_state::GlobalState;
|
||||
#[cfg(feature = "ipc")]
|
||||
use crate::ironvar::VariableManager;
|
||||
use crate::style::load_css;
|
||||
|
||||
mod bar;
|
||||
|
@ -39,7 +45,6 @@ mod config;
|
|||
mod desktop_file;
|
||||
mod dynamic_value;
|
||||
mod error;
|
||||
mod global_state;
|
||||
mod gtk_helpers;
|
||||
mod image;
|
||||
#[cfg(feature = "ipc")]
|
||||
|
@ -52,7 +57,6 @@ mod modules;
|
|||
mod popup;
|
||||
mod script;
|
||||
mod style;
|
||||
mod unique_id;
|
||||
|
||||
const GTK_APP_ID: &str = "dev.jstanger.ironbar";
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
@ -61,103 +65,160 @@ const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|||
async fn main() {
|
||||
let _guard = logging::install_logging();
|
||||
|
||||
let global_state = Rc::new(RefCell::new(GlobalState::new()));
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "cli")] {
|
||||
run_with_args(global_state).await;
|
||||
run_with_args().await;
|
||||
} else {
|
||||
start_ironbar(global_state);
|
||||
start_ironbar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
async fn run_with_args(global_state: Rc<RefCell<GlobalState>>) {
|
||||
async fn run_with_args() {
|
||||
let args = cli::Args::parse();
|
||||
|
||||
match args.command {
|
||||
Some(command) => {
|
||||
let ipc = ipc::Ipc::new(global_state);
|
||||
let ipc = ipc::Ipc::new();
|
||||
match ipc.send(command).await {
|
||||
Ok(res) => cli::handle_response(res),
|
||||
Err(err) => error!("{err:?}"),
|
||||
};
|
||||
}
|
||||
None => start_ironbar(global_state),
|
||||
None => start_ironbar(),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_ironbar(global_state: Rc<RefCell<GlobalState>>) {
|
||||
info!("Ironbar version {}", VERSION);
|
||||
info!("Starting application");
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
let app = Application::builder().application_id(GTK_APP_ID).build();
|
||||
let _ = wayland::get_client(); // force-init
|
||||
#[cfg(feature = "ipc")]
|
||||
lazy_static! {
|
||||
static ref VARIABLE_MANAGER: Arc<RwLock<VariableManager>> = arc_rw!(VariableManager::new());
|
||||
}
|
||||
|
||||
let running = Rc::new(Cell::new(false));
|
||||
#[derive(Debug)]
|
||||
pub struct Ironbar {
|
||||
bars: Rc<RefCell<Vec<Bar>>>,
|
||||
}
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
if running.get() {
|
||||
info!("Ironbar already running, returning");
|
||||
return;
|
||||
impl Ironbar {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
bars: Rc::new(RefCell::new(vec![])),
|
||||
}
|
||||
}
|
||||
|
||||
running.set(true);
|
||||
fn start(self) {
|
||||
info!("Ironbar version {}", VERSION);
|
||||
info!("Starting application");
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "ipc")] {
|
||||
let ipc = ipc::Ipc::new(global_state.clone());
|
||||
ipc.start(app);
|
||||
let app = Application::builder().application_id(GTK_APP_ID).build();
|
||||
|
||||
let running = AtomicBool::new(false);
|
||||
|
||||
let instance = Rc::new(self);
|
||||
|
||||
// force start wayland client ahead of ui
|
||||
let wl = wayland::get_client();
|
||||
lock!(wl).roundtrip();
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
if running.load(Ordering::Relaxed) {
|
||||
info!("Ironbar already running, returning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
load_interface(app, &global_state);
|
||||
running.set(true);
|
||||
|
||||
let style_path = env::var("IRONBAR_CSS").ok().map_or_else(
|
||||
|| {
|
||||
config_dir().map_or_else(
|
||||
|| {
|
||||
let report = Report::msg("Failed to locate user config dir");
|
||||
error!("{:?}", report);
|
||||
exit(ExitCode::CreateBars as i32);
|
||||
},
|
||||
|dir| dir.join("ironbar").join("style.css"),
|
||||
)
|
||||
},
|
||||
PathBuf::from,
|
||||
);
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "ipc")] {
|
||||
let ipc = ipc::Ipc::new();
|
||||
ipc.start(app, instance.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if style_path.exists() {
|
||||
load_css(style_path);
|
||||
}
|
||||
*instance.bars.borrow_mut() = load_interface(app);
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let style_path = env::var("IRONBAR_CSS").ok().map_or_else(
|
||||
|| {
|
||||
config_dir().map_or_else(
|
||||
|| {
|
||||
let report = Report::msg("Failed to locate user config dir");
|
||||
error!("{:?}", report);
|
||||
exit(ExitCode::CreateBars as i32);
|
||||
},
|
||||
|dir| dir.join("ironbar").join("style.css"),
|
||||
)
|
||||
},
|
||||
PathBuf::from,
|
||||
);
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
let ipc_path = ipc.path().to_path_buf();
|
||||
spawn_blocking(move || {
|
||||
rx.recv().expect("to receive from channel");
|
||||
if style_path.exists() {
|
||||
load_css(style_path);
|
||||
}
|
||||
|
||||
info!("Shutting down");
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
ipc::Ipc::shutdown(ipc_path);
|
||||
let ipc_path = ipc.path().to_path_buf();
|
||||
spawn_blocking(move || {
|
||||
rx.recv().expect("to receive from channel");
|
||||
|
||||
exit(0);
|
||||
info!("Shutting down");
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
ipc::Ipc::shutdown(ipc_path);
|
||||
|
||||
exit(0);
|
||||
});
|
||||
|
||||
ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel."))
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
// TODO: Start wayland client - listen for outputs
|
||||
// All bar loading should happen as an event response to this
|
||||
});
|
||||
|
||||
ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel."))
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
});
|
||||
// Ignore CLI args
|
||||
// Some are provided by swaybar_config but not currently supported
|
||||
app.run_with_args(&Vec::<&str>::new());
|
||||
}
|
||||
|
||||
// Ignore CLI args
|
||||
// Some are provided by swaybar_config but not currently supported
|
||||
app.run_with_args(&Vec::<&str>::new());
|
||||
/// Gets a `usize` ID value that is unique to the entire Ironbar instance.
|
||||
/// This is just a static `AtomicUsize` that increments every time this function is called.
|
||||
pub fn unique_id() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Gets the `Ironvar` manager singleton.
|
||||
#[cfg(feature = "ipc")]
|
||||
#[must_use]
|
||||
pub fn variable_manager() -> Arc<RwLock<VariableManager>> {
|
||||
VARIABLE_MANAGER.clone()
|
||||
}
|
||||
|
||||
/// Gets a clone of a bar by its unique name.
|
||||
///
|
||||
/// Since the bar contains mostly GTK objects,
|
||||
/// the clone is cheap enough to not worry about.
|
||||
#[must_use]
|
||||
pub fn bar_by_name(&self, name: &str) -> Option<Bar> {
|
||||
self.bars
|
||||
.borrow()
|
||||
.iter()
|
||||
.find(|&bar| bar.name() == name)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
fn start_ironbar() {
|
||||
let ironbar = Ironbar::new();
|
||||
ironbar.start();
|
||||
}
|
||||
|
||||
/// Loads the Ironbar config and interface.
|
||||
pub fn load_interface(app: &Application, global_state: &Rc<RefCell<GlobalState>>) {
|
||||
pub fn load_interface(app: &Application) -> Vec<Bar> {
|
||||
let display = Display::default().map_or_else(
|
||||
|| {
|
||||
let report = Report::msg("Failed to get default GTK display");
|
||||
|
@ -185,7 +246,7 @@ pub fn load_interface(app: &Application, global_state: &Rc<RefCell<GlobalState>>
|
|||
|
||||
#[cfg(feature = "ipc")]
|
||||
if let Some(ironvars) = config.ironvar_defaults.take() {
|
||||
let variable_manager = ironvar::get_variable_manager();
|
||||
let variable_manager = Ironbar::variable_manager();
|
||||
for (k, v) in ironvars {
|
||||
if write_lock!(variable_manager).set(k.clone(), v).is_err() {
|
||||
warn!("Ignoring invalid ironvar: '{k}'");
|
||||
|
@ -193,21 +254,20 @@ pub fn load_interface(app: &Application, global_state: &Rc<RefCell<GlobalState>>
|
|||
}
|
||||
}
|
||||
|
||||
if let Err(err) = create_bars(app, &display, &config, global_state) {
|
||||
error!("{:?}", err);
|
||||
exit(ExitCode::CreateBars as i32);
|
||||
match create_bars(app, &display, &config) {
|
||||
Ok(bars) => {
|
||||
debug!("Created {} bars", bars.len());
|
||||
bars
|
||||
}
|
||||
Err(err) => {
|
||||
error!("{:?}", err);
|
||||
exit(ExitCode::CreateBars as i32);
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Created bars");
|
||||
}
|
||||
|
||||
/// Creates each of the bars across each of the (configured) outputs.
|
||||
fn create_bars(
|
||||
app: &Application,
|
||||
display: &Display,
|
||||
config: &Config,
|
||||
global_state: &Rc<RefCell<GlobalState>>,
|
||||
) -> Result<()> {
|
||||
fn create_bars(app: &Application, display: &Display, config: &Config) -> Result<Vec<Bar>> {
|
||||
let wl = wayland::get_client();
|
||||
let outputs = lock!(wl).get_outputs();
|
||||
|
||||
|
@ -216,6 +276,7 @@ fn create_bars(
|
|||
|
||||
let num_monitors = display.n_monitors();
|
||||
|
||||
let mut all_bars = vec![];
|
||||
for i in 0..num_monitors {
|
||||
let monitor = display
|
||||
.monitor(i)
|
||||
|
@ -228,33 +289,35 @@ fn create_bars(
|
|||
continue;
|
||||
};
|
||||
|
||||
config.monitors.as_ref().map_or_else(
|
||||
|| {
|
||||
info!("Creating bar on '{}'", monitor_name);
|
||||
create_bar(app, &monitor, monitor_name, config.clone(), global_state)
|
||||
},
|
||||
|config| {
|
||||
let config = config.get(monitor_name);
|
||||
match &config {
|
||||
Some(MonitorConfig::Single(config)) => {
|
||||
info!("Creating bar on '{}'", monitor_name);
|
||||
create_bar(app, &monitor, monitor_name, config.clone(), global_state)
|
||||
}
|
||||
Some(MonitorConfig::Multiple(configs)) => {
|
||||
for config in configs {
|
||||
info!("Creating bar on '{}'", monitor_name);
|
||||
create_bar(app, &monitor, monitor_name, config.clone(), global_state)?;
|
||||
}
|
||||
let mut bars = match config
|
||||
.monitors
|
||||
.as_ref()
|
||||
.and_then(|config| config.get(monitor_name))
|
||||
{
|
||||
Some(MonitorConfig::Single(config)) => {
|
||||
vec![create_bar(
|
||||
app,
|
||||
&monitor,
|
||||
monitor_name.to_string(),
|
||||
config.clone(),
|
||||
)?]
|
||||
}
|
||||
Some(MonitorConfig::Multiple(configs)) => configs
|
||||
.iter()
|
||||
.map(|config| create_bar(app, &monitor, monitor_name.to_string(), config.clone()))
|
||||
.collect::<Result<_>>()?,
|
||||
None => vec![create_bar(
|
||||
app,
|
||||
&monitor,
|
||||
monitor_name.to_string(),
|
||||
config.clone(),
|
||||
)?],
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
},
|
||||
)?;
|
||||
all_bars.append(&mut bars);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(all_bars)
|
||||
}
|
||||
|
||||
/// Blocks on a `Future` until it resolves.
|
||||
|
|
18
src/popup.rs
18
src/popup.rs
|
@ -8,12 +8,18 @@ use tracing::debug;
|
|||
use crate::config::BarPosition;
|
||||
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
|
||||
use crate::modules::{ModuleInfo, ModulePopupParts, PopupButton};
|
||||
use crate::unique_id::get_unique_usize;
|
||||
use crate::Ironbar;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PopupCacheValue {
|
||||
pub name: String,
|
||||
pub content: ModulePopupParts,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Popup {
|
||||
pub window: ApplicationWindow,
|
||||
pub cache: HashMap<usize, (String, ModulePopupParts)>,
|
||||
pub cache: HashMap<usize, PopupCacheValue>,
|
||||
monitor: Monitor,
|
||||
pos: BarPosition,
|
||||
current_widget: Option<usize>,
|
||||
|
@ -121,17 +127,17 @@ impl Popup {
|
|||
debug!("Registered popup content for #{}", key);
|
||||
|
||||
for button in &content.buttons {
|
||||
let id = get_unique_usize();
|
||||
let id = Ironbar::unique_id();
|
||||
button.set_tag("popup-id", id);
|
||||
}
|
||||
|
||||
self.cache.insert(key, (name, content));
|
||||
self.cache.insert(key, PopupCacheValue { name, content });
|
||||
}
|
||||
|
||||
pub fn show(&mut self, widget_id: usize, button_id: usize) {
|
||||
self.clear_window();
|
||||
|
||||
if let Some((_name, content)) = self.cache.get(&widget_id) {
|
||||
if let Some(PopupCacheValue { content, .. }) = self.cache.get(&widget_id) {
|
||||
self.current_widget = Some(widget_id);
|
||||
|
||||
content.container.style_context().add_class("popup");
|
||||
|
@ -155,7 +161,7 @@ impl Popup {
|
|||
pub fn show_at(&self, widget_id: usize, geometry: WidgetGeometry) {
|
||||
self.clear_window();
|
||||
|
||||
if let Some((_name, content)) = self.cache.get(&widget_id) {
|
||||
if let Some(PopupCacheValue { content, .. }) = self.cache.get(&widget_id) {
|
||||
content.container.style_context().add_class("popup");
|
||||
self.window.add(&content.container);
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
/// Gets a `usize` ID value that is unique to the entire Ironbar instance.
|
||||
/// This is just an `AtomicUsize` that increments every time this function is called.
|
||||
pub fn get_unique_usize() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
Loading…
Add table
Reference in a new issue