mirror of
https://github.com/JakeStanger/ironbar.git
synced 2024-11-22 14:04:20 +03:00
feat: new focused window module
This commit is contained in:
parent
e416e03b0a
commit
dc14cb003f
25
src/bar.rs
25
src/bar.rs
@ -48,14 +48,14 @@ fn load_modules(
|
||||
right: >k::Box,
|
||||
app: &Application,
|
||||
config: Config,
|
||||
output_name: &str
|
||||
output_name: &str,
|
||||
) {
|
||||
if let Some(modules) = config.left {
|
||||
let info = ModuleInfo {
|
||||
app,
|
||||
location: ModuleLocation::Left,
|
||||
bar_position: &config.position,
|
||||
output_name
|
||||
output_name,
|
||||
};
|
||||
|
||||
add_modules(left, modules, info);
|
||||
@ -66,7 +66,7 @@ fn load_modules(
|
||||
app,
|
||||
location: ModuleLocation::Center,
|
||||
bar_position: &config.position,
|
||||
output_name
|
||||
output_name,
|
||||
};
|
||||
|
||||
add_modules(center, modules, info);
|
||||
@ -77,7 +77,7 @@ fn load_modules(
|
||||
app,
|
||||
location: ModuleLocation::Right,
|
||||
bar_position: &config.position,
|
||||
output_name
|
||||
output_name,
|
||||
};
|
||||
|
||||
add_modules(right, modules, info);
|
||||
@ -122,6 +122,11 @@ fn add_modules(content: >k::Box, modules: Vec<ModuleConfig>, info: ModuleInfo)
|
||||
widget.set_widget_name("script");
|
||||
content.add(&widget);
|
||||
}
|
||||
ModuleConfig::Focused(module) => {
|
||||
let widget = module.into_widget(&info);
|
||||
widget.set_widget_name("focused");
|
||||
content.add(&widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,8 +142,16 @@ fn setup_layer_shell(win: &ApplicationWindow, monitor: &Monitor, position: &BarP
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Left, 0);
|
||||
gtk_layer_shell::set_margin(win, gtk_layer_shell::Edge::Right, 0);
|
||||
|
||||
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);
|
||||
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,
|
||||
);
|
||||
gtk_layer_shell::set_anchor(win, gtk_layer_shell::Edge::Left, true);
|
||||
gtk_layer_shell::set_anchor(win, gtk_layer_shell::Edge::Right, true);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::modules::clock::ClockModule;
|
||||
use crate::modules::focused::FocusedModule;
|
||||
use crate::modules::launcher::LauncherModule;
|
||||
use crate::modules::mpd::MpdModule;
|
||||
use crate::modules::script::ScriptModule;
|
||||
@ -19,13 +20,14 @@ pub enum ModuleConfig {
|
||||
SysInfo(SysInfoModule),
|
||||
Launcher(LauncherModule),
|
||||
Script(ScriptModule),
|
||||
Focused(FocusedModule),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum BarPosition {
|
||||
Top,
|
||||
Bottom
|
||||
Bottom,
|
||||
}
|
||||
|
||||
impl Default for BarPosition {
|
||||
@ -83,3 +85,10 @@ impl Config {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn default_false() -> bool {
|
||||
false
|
||||
}
|
||||
pub const fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
21
src/main.rs
21
src/main.rs
@ -1,24 +1,21 @@
|
||||
mod bar;
|
||||
mod collection;
|
||||
mod config;
|
||||
mod icon;
|
||||
mod modules;
|
||||
mod popup;
|
||||
mod style;
|
||||
mod sway;
|
||||
|
||||
use crate::bar::create_bar;
|
||||
use crate::config::Config;
|
||||
use crate::style::load_css;
|
||||
use crate::sway::SwayOutput;
|
||||
use dirs::config_dir;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gdk, Application};
|
||||
use ksway::client::Client;
|
||||
use ksway::IpcCommand;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SwayOutput {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@ -27,8 +24,11 @@ async fn main() {
|
||||
.build();
|
||||
|
||||
let mut sway_client = Client::connect().expect("Failed to connect to Sway IPC");
|
||||
let outputs = sway_client.ipc(IpcCommand::GetOutputs).expect("Failed to get Sway outputs");
|
||||
let outputs = serde_json::from_slice::<Vec<SwayOutput>>(&outputs).expect("Failed to deserialize outputs message from Sway IPC");
|
||||
let outputs = sway_client
|
||||
.ipc(IpcCommand::GetOutputs)
|
||||
.expect("Failed to get Sway outputs");
|
||||
let outputs = serde_json::from_slice::<Vec<SwayOutput>>(&outputs)
|
||||
.expect("Failed to deserialize outputs message from Sway IPC");
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
let config = Config::load().unwrap_or_default();
|
||||
@ -42,7 +42,10 @@ async fn main() {
|
||||
let num_monitors = display.n_monitors();
|
||||
for i in 0..num_monitors {
|
||||
let monitor = display.monitor(i).unwrap();
|
||||
let monitor_name = &outputs.get(i as usize).expect("GTK monitor output differs from Sway's").name;
|
||||
let monitor_name = &outputs
|
||||
.get(i as usize)
|
||||
.expect("GTK monitor output differs from Sway's")
|
||||
.name;
|
||||
|
||||
let config = config.monitors.as_ref().map_or(&config, |monitor_config| {
|
||||
monitor_config.get(i as usize).unwrap_or(&config)
|
||||
|
@ -30,7 +30,12 @@ impl Module<Button> for ClockModule {
|
||||
fn into_widget(self, info: &ModuleInfo) -> Button {
|
||||
let button = Button::new();
|
||||
|
||||
let popup = Popup::new("popup-clock", info.app, Orientation::Vertical, info.bar_position);
|
||||
let popup = Popup::new(
|
||||
"popup-clock",
|
||||
info.app,
|
||||
Orientation::Vertical,
|
||||
info.bar_position,
|
||||
);
|
||||
popup.add_clock_widgets();
|
||||
|
||||
button.show_all();
|
||||
|
97
src/modules/focused.rs
Normal file
97
src/modules/focused.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use crate::icon;
|
||||
use crate::modules::{Module, ModuleInfo};
|
||||
use crate::sway::node::get_open_windows;
|
||||
use crate::sway::WindowEvent;
|
||||
use glib::Continue;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{IconTheme, Image, Label, Orientation};
|
||||
use ksway::{Client, IpcEvent};
|
||||
use serde::Deserialize;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct FocusedModule {
|
||||
#[serde(default = "crate::config::default_true")]
|
||||
show_icon: bool,
|
||||
#[serde(default = "crate::config::default_true")]
|
||||
show_title: bool,
|
||||
|
||||
#[serde(default = "default_icon_size")]
|
||||
icon_size: i32,
|
||||
icon_theme: Option<String>,
|
||||
}
|
||||
|
||||
const fn default_icon_size() -> i32 {
|
||||
32
|
||||
}
|
||||
|
||||
impl Module<gtk::Box> for FocusedModule {
|
||||
fn into_widget(self, _info: &ModuleInfo) -> gtk::Box {
|
||||
let icon_theme = IconTheme::new();
|
||||
|
||||
if let Some(theme) = self.icon_theme {
|
||||
icon_theme.set_custom_theme(Some(&theme));
|
||||
}
|
||||
|
||||
let container = gtk::Box::new(Orientation::Horizontal, 5);
|
||||
|
||||
let icon = Image::builder().name("icon").build();
|
||||
let label = Label::builder().name("label").build();
|
||||
|
||||
container.add(&icon);
|
||||
container.add(&label);
|
||||
|
||||
let mut sway = Client::connect().unwrap();
|
||||
|
||||
let srx = sway.subscribe(vec![IpcEvent::Window]).unwrap();
|
||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
let focused = get_open_windows(&mut sway)
|
||||
.into_iter()
|
||||
.find(|node| node.focused);
|
||||
|
||||
if let Some(focused) = focused {
|
||||
tx.send(focused).unwrap();
|
||||
}
|
||||
|
||||
spawn_blocking(move || loop {
|
||||
while let Ok((_, payload)) = srx.try_recv() {
|
||||
let payload: WindowEvent = serde_json::from_slice(&payload).unwrap();
|
||||
|
||||
let update = match payload.change.as_str() {
|
||||
"focus" => true,
|
||||
"title" => payload.container.focused,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if update {
|
||||
tx.send(payload.container).unwrap();
|
||||
}
|
||||
}
|
||||
sway.poll().unwrap();
|
||||
});
|
||||
|
||||
{
|
||||
rx.attach(None, move |node| {
|
||||
let value = node
|
||||
.name
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| node.get_id());
|
||||
|
||||
let pixbuf = icon::get_icon(&icon_theme, node.get_id(), self.icon_size);
|
||||
|
||||
if self.show_icon {
|
||||
icon.set_pixbuf(pixbuf.as_ref());
|
||||
}
|
||||
|
||||
if self.show_title {
|
||||
label.set_label(value);
|
||||
}
|
||||
|
||||
Continue(true)
|
||||
});
|
||||
}
|
||||
|
||||
container
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use crate::collection::Collection;
|
||||
use crate::modules::launcher::icon::{find_desktop_file, get_icon};
|
||||
use crate::modules::launcher::node::SwayNode;
|
||||
use crate::icon::{find_desktop_file, get_icon};
|
||||
use crate::modules::launcher::popup::Popup;
|
||||
use crate::modules::launcher::FocusEvent;
|
||||
use crate::popup::PopupAlignment;
|
||||
use crate::sway::SwayNode;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, IconTheme, Image};
|
||||
use std::process::{Command, Stdio};
|
||||
|
@ -1,13 +1,12 @@
|
||||
mod icon;
|
||||
mod item;
|
||||
mod node;
|
||||
mod popup;
|
||||
|
||||
use crate::collection::Collection;
|
||||
use crate::modules::launcher::item::{ButtonConfig, LauncherItem, LauncherWindow};
|
||||
use crate::modules::launcher::node::{get_open_windows, SwayNode};
|
||||
use crate::modules::launcher::popup::Popup;
|
||||
use crate::modules::{Module, ModuleInfo};
|
||||
use crate::sway::node::get_open_windows;
|
||||
use crate::sway::{SwayNode, WindowEvent};
|
||||
use gtk::prelude::*;
|
||||
use gtk::{IconTheme, Orientation};
|
||||
use ksway::{Client, IpcEvent};
|
||||
@ -20,28 +19,14 @@ use tokio::task::spawn_blocking;
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct LauncherModule {
|
||||
favorites: Option<Vec<String>>,
|
||||
#[serde(default = "default_false")]
|
||||
#[serde(default = "crate::config::default_false")]
|
||||
show_names: bool,
|
||||
#[serde(default = "default_true")]
|
||||
#[serde(default = "crate::config::default_true")]
|
||||
show_icons: bool,
|
||||
|
||||
icon_theme: Option<String>,
|
||||
}
|
||||
|
||||
const fn default_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
const fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct WindowEvent {
|
||||
change: String,
|
||||
container: SwayNode,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FocusEvent {
|
||||
AppId(String),
|
||||
@ -181,7 +166,6 @@ impl Launcher {
|
||||
} else {
|
||||
windows.get_mut(&window.id).unwrap().name = Some(name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +191,12 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
|
||||
let mut sway = Client::connect().unwrap();
|
||||
|
||||
let popup = Popup::new("popup-launcher", info.app, Orientation::Vertical, info.bar_position);
|
||||
let popup = Popup::new(
|
||||
"popup-launcher",
|
||||
info.app,
|
||||
Orientation::Vertical,
|
||||
info.bar_position,
|
||||
);
|
||||
let container = gtk::Box::new(Orientation::Horizontal, 0);
|
||||
|
||||
let (ui_tx, mut ui_rx) = mpsc::channel(32);
|
||||
|
@ -5,6 +5,7 @@
|
||||
/// Clicking the widget opens a popup containing the current time
|
||||
/// with second-level precision and a calendar.
|
||||
pub mod clock;
|
||||
pub mod focused;
|
||||
pub mod launcher;
|
||||
pub mod mpd;
|
||||
pub mod script;
|
||||
@ -12,13 +13,13 @@ pub mod sysinfo;
|
||||
pub mod tray;
|
||||
pub mod workspaces;
|
||||
|
||||
use crate::config::BarPosition;
|
||||
/// Shamelessly stolen from here:
|
||||
/// <https://github.com/zeroeightysix/rustbar/blob/master/src/modules/module.rs>
|
||||
use glib::IsA;
|
||||
use gtk::{Application, Widget};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::Value;
|
||||
use crate::config::BarPosition;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ModuleLocation {
|
||||
@ -31,7 +32,7 @@ pub struct ModuleInfo<'a> {
|
||||
pub app: &'a Application,
|
||||
pub location: ModuleLocation,
|
||||
pub bar_position: &'a BarPosition,
|
||||
pub output_name: &'a str
|
||||
pub output_name: &'a str,
|
||||
}
|
||||
|
||||
pub trait Module<W>
|
||||
|
@ -93,7 +93,12 @@ impl Module<Button> for MpdModule {
|
||||
|
||||
let (ui_tx, mut ui_rx) = mpsc::channel(32);
|
||||
|
||||
let popup = Popup::new("popup-mpd", info.app, Orientation::Horizontal, info.bar_position);
|
||||
let popup = Popup::new(
|
||||
"popup-mpd",
|
||||
info.app,
|
||||
Orientation::Horizontal,
|
||||
info.bar_position,
|
||||
);
|
||||
let mpd_popup = MpdPopup::new(popup, ui_tx);
|
||||
|
||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
@ -148,11 +153,12 @@ impl Module<Button> for MpdModule {
|
||||
match status.state {
|
||||
PlayState::Playing => client.command(commands::SetPause(true)).await,
|
||||
PlayState::Paused => client.command(commands::SetPause(false)).await,
|
||||
PlayState::Stopped => Ok(())
|
||||
PlayState::Stopped => Ok(()),
|
||||
}
|
||||
}
|
||||
PopupEvent::Next => client.command(commands::Next).await
|
||||
}.unwrap();
|
||||
PopupEvent::Next => client.command(commands::Next).await,
|
||||
}
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::modules::{Module, ModuleInfo};
|
||||
use crate::sway::{Workspace, WorkspaceEvent};
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, Orientation};
|
||||
use ksway::client::Client;
|
||||
@ -13,22 +14,10 @@ use tokio::task::spawn_blocking;
|
||||
pub struct WorkspacesModule {
|
||||
name_map: Option<HashMap<String, String>>,
|
||||
|
||||
#[serde(default = "default_false")]
|
||||
#[serde(default = "crate::config::default_false")]
|
||||
all_monitors: bool,
|
||||
}
|
||||
|
||||
const fn default_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Workspace {
|
||||
name: String,
|
||||
focused: bool,
|
||||
// num: i32,
|
||||
output: String,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
fn as_button(&self, name_map: &HashMap<String, String>, tx: &mpsc::Sender<String>) -> Button {
|
||||
let button = Button::builder()
|
||||
@ -52,13 +41,6 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct WorkspaceEvent {
|
||||
change: String,
|
||||
old: Option<Workspace>,
|
||||
current: Option<Workspace>,
|
||||
}
|
||||
|
||||
impl Module<gtk::Box> for WorkspacesModule {
|
||||
fn into_widget(self, info: &ModuleInfo) -> gtk::Box {
|
||||
let mut sway = Client::connect().unwrap();
|
||||
|
49
src/sway/mod.rs
Normal file
49
src/sway/mod.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
pub mod node;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct WorkspaceEvent {
|
||||
pub change: String,
|
||||
pub old: Option<Workspace>,
|
||||
pub current: Option<Workspace>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Workspace {
|
||||
pub name: String,
|
||||
pub focused: bool,
|
||||
// pub num: i32,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct WindowEvent {
|
||||
pub change: String,
|
||||
pub container: SwayNode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SwayNode {
|
||||
#[serde(rename = "type")]
|
||||
pub node_type: String,
|
||||
pub id: i32,
|
||||
pub name: Option<String>,
|
||||
pub app_id: Option<String>,
|
||||
pub focused: bool,
|
||||
pub urgent: bool,
|
||||
pub nodes: Vec<SwayNode>,
|
||||
pub floating_nodes: Vec<SwayNode>,
|
||||
pub shell: Option<String>,
|
||||
pub window_properties: Option<WindowProperties>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct WindowProperties {
|
||||
pub class: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SwayOutput {
|
||||
pub name: String,
|
||||
}
|
@ -1,25 +1,5 @@
|
||||
use crate::sway::SwayNode;
|
||||
use ksway::{Client, IpcCommand};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SwayNode {
|
||||
#[serde(rename = "type")]
|
||||
pub node_type: String,
|
||||
pub id: i32,
|
||||
pub name: Option<String>,
|
||||
pub app_id: Option<String>,
|
||||
pub focused: bool,
|
||||
pub urgent: bool,
|
||||
pub nodes: Vec<SwayNode>,
|
||||
pub floating_nodes: Vec<SwayNode>,
|
||||
pub shell: Option<String>,
|
||||
pub window_properties: Option<WindowProperties>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct WindowProperties {
|
||||
pub class: String,
|
||||
}
|
||||
|
||||
impl SwayNode {
|
||||
pub fn get_id(&self) -> &str {
|
Loading…
Reference in New Issue
Block a user