2022-09-25 22:49:00 +01:00
|
|
|
use crate::bridge_channel::BridgeChannel;
|
2022-08-14 15:56:21 +01:00
|
|
|
use crate::config::{BarPosition, ModuleConfig};
|
2022-09-25 22:49:00 +01:00
|
|
|
use crate::modules::launcher::{ItemEvent, LauncherUpdate};
|
|
|
|
use crate::modules::mpd::{PlayerCommand, SongUpdate};
|
|
|
|
use crate::modules::workspaces::WorkspaceUpdate;
|
|
|
|
use crate::modules::{Module, ModuleInfoBuilder, ModuleLocation, ModuleUpdateEvent, WidgetContext};
|
|
|
|
use crate::popup::Popup;
|
2022-08-14 14:30:13 +01:00
|
|
|
use crate::Config;
|
2022-09-25 22:49:00 +01:00
|
|
|
use chrono::{DateTime, Local};
|
2022-08-21 23:36:07 +01:00
|
|
|
use color_eyre::Result;
|
2022-08-14 14:30:13 +01:00
|
|
|
use gtk::gdk::Monitor;
|
|
|
|
use gtk::prelude::*;
|
|
|
|
use gtk::{Application, ApplicationWindow, Orientation};
|
2022-09-25 22:49:00 +01:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
use stray::message::NotifierItemCommand;
|
|
|
|
use stray::NotifierItemMessage;
|
|
|
|
use tokio::sync::mpsc;
|
2022-08-25 21:53:57 +01:00
|
|
|
use tracing::{debug, info};
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-08-28 16:57:41 +01:00
|
|
|
/// Creates a new window for a bar,
|
|
|
|
/// sets it up and adds its widgets.
|
2022-08-21 23:36:07 +01:00
|
|
|
pub fn create_bar(
|
|
|
|
app: &Application,
|
|
|
|
monitor: &Monitor,
|
|
|
|
monitor_name: &str,
|
|
|
|
config: Config,
|
|
|
|
) -> Result<()> {
|
2022-08-14 14:30:13 +01:00
|
|
|
let win = ApplicationWindow::builder().application(app).build();
|
|
|
|
|
2022-08-14 15:56:21 +01:00
|
|
|
setup_layer_shell(&win, monitor, &config.position);
|
2022-08-14 14:30:13 +01:00
|
|
|
|
|
|
|
let content = gtk::Box::builder()
|
|
|
|
.orientation(Orientation::Horizontal)
|
|
|
|
.spacing(0)
|
|
|
|
.hexpand(false)
|
2022-08-14 20:41:38 +01:00
|
|
|
.height_request(config.height)
|
2022-08-14 14:30:13 +01:00
|
|
|
.name("bar")
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let left = gtk::Box::builder().spacing(0).name("left").build();
|
|
|
|
let center = gtk::Box::builder().spacing(0).name("center").build();
|
|
|
|
let right = gtk::Box::builder().spacing(0).name("right").build();
|
|
|
|
|
|
|
|
content.style_context().add_class("container");
|
|
|
|
left.style_context().add_class("container");
|
|
|
|
center.style_context().add_class("container");
|
|
|
|
right.style_context().add_class("container");
|
|
|
|
|
|
|
|
content.add(&left);
|
|
|
|
content.set_center_widget(Some(¢er));
|
|
|
|
content.pack_end(&right, false, false, 0);
|
|
|
|
|
2022-08-21 23:36:07 +01:00
|
|
|
load_modules(&left, ¢er, &right, app, config, monitor, monitor_name)?;
|
2022-08-14 14:30:13 +01:00
|
|
|
win.add(&content);
|
|
|
|
|
|
|
|
win.connect_destroy_event(|_, _| {
|
2022-08-25 21:53:57 +01:00
|
|
|
info!("Shutting down");
|
2022-08-14 14:30:13 +01:00
|
|
|
gtk::main_quit();
|
|
|
|
Inhibit(false)
|
|
|
|
});
|
|
|
|
|
2022-08-25 21:53:57 +01:00
|
|
|
debug!("Showing bar");
|
2022-08-14 14:30:13 +01:00
|
|
|
win.show_all();
|
2022-08-21 23:36:07 +01:00
|
|
|
|
|
|
|
Ok(())
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
2022-08-28 16:57:41 +01:00
|
|
|
/// Loads the configured modules onto a bar.
|
2022-08-14 14:30:13 +01:00
|
|
|
fn load_modules(
|
|
|
|
left: >k::Box,
|
|
|
|
center: >k::Box,
|
|
|
|
right: >k::Box,
|
|
|
|
app: &Application,
|
|
|
|
config: Config,
|
2022-08-15 21:11:00 +01:00
|
|
|
monitor: &Monitor,
|
2022-08-14 20:40:11 +01:00
|
|
|
output_name: &str,
|
2022-08-21 23:36:07 +01:00
|
|
|
) -> Result<()> {
|
2022-09-25 22:49:00 +01:00
|
|
|
let mut info_builder = ModuleInfoBuilder::default();
|
|
|
|
let info_builder = info_builder
|
|
|
|
.app(app)
|
|
|
|
.bar_position(&config.position)
|
|
|
|
.monitor(monitor)
|
|
|
|
.output_name(output_name);
|
|
|
|
|
2022-08-14 14:30:13 +01:00
|
|
|
if let Some(modules) = config.left {
|
2022-09-25 22:49:00 +01:00
|
|
|
let info_builder = info_builder.location(ModuleLocation::Left);
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-09-25 22:49:00 +01:00
|
|
|
add_modules(left, modules, info_builder)?;
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(modules) = config.center {
|
2022-09-25 22:49:00 +01:00
|
|
|
let info_builder = info_builder.location(ModuleLocation::Center);
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-09-25 22:49:00 +01:00
|
|
|
add_modules(center, modules, info_builder)?;
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(modules) = config.right {
|
2022-09-25 22:49:00 +01:00
|
|
|
let info_builder = info_builder.location(ModuleLocation::Right);
|
2022-08-14 14:30:13 +01:00
|
|
|
|
2022-09-25 22:49:00 +01:00
|
|
|
add_modules(right, modules, info_builder)?;
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
2022-08-21 23:36:07 +01:00
|
|
|
|
|
|
|
Ok(())
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
2022-08-28 16:57:41 +01:00
|
|
|
/// Adds modules into a provided GTK box,
|
|
|
|
/// which should be one of its left, center or right containers.
|
2022-09-25 22:49:00 +01:00
|
|
|
fn add_modules(
|
|
|
|
content: >k::Box,
|
|
|
|
modules: Vec<ModuleConfig>,
|
|
|
|
info_builder: &mut ModuleInfoBuilder,
|
|
|
|
) -> Result<()> {
|
|
|
|
let base_popup_info = info_builder.module_name("").build()?;
|
|
|
|
let popup = Popup::new(&base_popup_info);
|
|
|
|
let popup = Arc::new(RwLock::new(popup));
|
|
|
|
|
2022-08-14 23:32:20 +01:00
|
|
|
macro_rules! add_module {
|
2022-09-25 22:49:00 +01:00
|
|
|
($module:expr, $id:expr, $name:literal, $send_message:ty, $receive_message:ty) => {
|
|
|
|
let info = info_builder.module_name($name).build()?;
|
|
|
|
|
|
|
|
let (w_tx, w_rx) = glib::MainContext::channel::<$send_message>(glib::PRIORITY_DEFAULT);
|
|
|
|
let (p_tx, p_rx) = glib::MainContext::channel::<$send_message>(glib::PRIORITY_DEFAULT);
|
|
|
|
|
|
|
|
let channel = BridgeChannel::<ModuleUpdateEvent<$send_message>>::new();
|
|
|
|
let (ui_tx, ui_rx) = mpsc::channel::<$receive_message>(16);
|
|
|
|
|
|
|
|
$module.spawn_controller(&info, channel.create_sender(), ui_rx)?;
|
|
|
|
|
|
|
|
let context = WidgetContext {
|
|
|
|
id: $id,
|
|
|
|
widget_rx: w_rx,
|
|
|
|
popup_rx: p_rx,
|
|
|
|
tx: channel.create_sender(),
|
|
|
|
controller_tx: ui_tx,
|
|
|
|
};
|
|
|
|
|
|
|
|
let widget = $module.into_widget(context, &info)?;
|
|
|
|
|
|
|
|
content.add(&widget.widget);
|
|
|
|
widget.widget.set_widget_name(info.module_name);
|
|
|
|
|
|
|
|
let has_popup = widget.popup.is_some();
|
|
|
|
if let Some(popup_content) = widget.popup {
|
|
|
|
popup
|
|
|
|
.write()
|
|
|
|
.expect("Failed to get write lock on popup")
|
|
|
|
.register_content($id, popup_content);
|
|
|
|
}
|
|
|
|
|
|
|
|
let popup2 = Arc::clone(&popup);
|
|
|
|
channel.recv(move |ev| {
|
|
|
|
let popup = popup2.clone();
|
|
|
|
match ev {
|
|
|
|
ModuleUpdateEvent::Update(update) => {
|
|
|
|
if has_popup {
|
|
|
|
p_tx.send(update.clone())
|
|
|
|
.expect("Failed to send update to popup");
|
|
|
|
}
|
|
|
|
|
|
|
|
w_tx.send(update).expect("Failed to send update to module");
|
|
|
|
}
|
|
|
|
ModuleUpdateEvent::TogglePopup((x, w)) => {
|
|
|
|
debug!("Toggling popup for {} [#{}]", $name, $id);
|
|
|
|
let popup = popup.read().expect("Failed to get read lock on popup");
|
|
|
|
if popup.is_visible() {
|
|
|
|
popup.hide()
|
|
|
|
} else {
|
|
|
|
popup.show_content($id);
|
|
|
|
popup.show(x, w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ModuleUpdateEvent::OpenPopup((x, w)) => {
|
|
|
|
debug!("Opening popup for {} [#{}]", $name, $id);
|
|
|
|
|
|
|
|
let popup = popup.read().expect("Failed to get read lock on popup");
|
|
|
|
popup.hide();
|
|
|
|
popup.show(x, w);
|
|
|
|
popup.show_content($id);
|
|
|
|
}
|
|
|
|
ModuleUpdateEvent::ClosePopup => {
|
|
|
|
debug!("Closing popup for {} [#{}]", $name, $id);
|
|
|
|
|
|
|
|
let popup = popup.read().expect("Failed to get read lock on popup");
|
|
|
|
popup.hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Continue(true)
|
|
|
|
});
|
|
|
|
};
|
2022-08-14 23:32:20 +01:00
|
|
|
}
|
|
|
|
|
2022-09-25 22:49:00 +01:00
|
|
|
for (id, config) in modules.into_iter().enumerate() {
|
2022-08-14 14:30:13 +01:00
|
|
|
match config {
|
2022-09-25 22:49:00 +01:00
|
|
|
ModuleConfig::Clock(module) => {
|
|
|
|
add_module!(module, id, "clock", DateTime<Local>, ());
|
|
|
|
}
|
|
|
|
ModuleConfig::Script(module) => {
|
|
|
|
add_module!(module, id, "script", String, ());
|
|
|
|
}
|
|
|
|
ModuleConfig::SysInfo(module) => {
|
|
|
|
add_module!(module, id, "sysinfo", HashMap<String, String>, ());
|
|
|
|
}
|
|
|
|
ModuleConfig::Focused(module) => {
|
|
|
|
add_module!(module, id, "focused", (String, String), ());
|
|
|
|
}
|
|
|
|
ModuleConfig::Workspaces(module) => {
|
|
|
|
add_module!(module, id, "workspaces", WorkspaceUpdate, String);
|
|
|
|
}
|
|
|
|
ModuleConfig::Tray(module) => {
|
|
|
|
add_module!(module, id, "tray", NotifierItemMessage, NotifierItemCommand);
|
|
|
|
}
|
|
|
|
ModuleConfig::Mpd(module) => {
|
|
|
|
add_module!(module, id, "mpd", Option<SongUpdate>, PlayerCommand);
|
|
|
|
}
|
|
|
|
ModuleConfig::Launcher(module) => {
|
|
|
|
add_module!(module, id, "launcher", LauncherUpdate, ItemEvent);
|
|
|
|
}
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
}
|
2022-08-21 23:36:07 +01:00
|
|
|
|
|
|
|
Ok(())
|
2022-08-14 14:30:13 +01:00
|
|
|
}
|
|
|
|
|
2022-09-25 22:49:00 +01:00
|
|
|
/// Sets up GTK layer shell for a provided application window.
|
2022-08-14 15:56:21 +01:00
|
|
|
fn setup_layer_shell(win: &ApplicationWindow, monitor: &Monitor, position: &BarPosition) {
|
2022-08-14 14:30:13 +01:00
|
|
|
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_margin(win, gtk_layer_shell::Edge::Top, 0);
|
|
|
|
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Bottom, 0);
|
|
|
|
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Left, 0);
|
|
|
|
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Right, 0);
|
|
|
|
|
2022-08-14 20:40:11 +01:00
|
|
|
gtk_layer_shell::set_anchor(
|
|
|
|
win,
|
|
|
|
gtk_layer_shell::Edge::Top,
|
|
|
|
position == &BarPosition::Top,
|
|
|
|
);
|
|
|
|
gtk_layer_shell::set_anchor(
|
|
|
|
win,
|
|
|
|
gtk_layer_shell::Edge::Bottom,
|
|
|
|
position == &BarPosition::Bottom,
|
|
|
|
);
|
2022-08-14 14:30:13 +01:00
|
|
|
gtk_layer_shell::set_anchor(win, gtk_layer_shell::Edge::Left, true);
|
|
|
|
gtk_layer_shell::set_anchor(win, gtk_layer_shell::Edge::Right, true);
|
|
|
|
}
|