mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-11 22:12:53 +01:00
feat(bar): add first pass at configuration loader
This commit is contained in:
82
Cargo.lock
generated
82
Cargo.lock
generated
@@ -2121,7 +2121,7 @@ checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"gpu-descriptor-types",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2145,7 +2145,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -2162,6 +2162,12 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
@@ -2450,6 +2456,16 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
@@ -2457,7 +2473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2639,7 +2655,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json_lenient",
|
||||
"serde_yaml",
|
||||
"serde_yaml 0.9.34+deprecated",
|
||||
"shadow-rs",
|
||||
"strum",
|
||||
"sysinfo 0.30.13",
|
||||
@@ -2661,17 +2677,22 @@ dependencies = [
|
||||
name = "komorebi-bar"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"catppuccin-egui",
|
||||
"chrono",
|
||||
"clap",
|
||||
"color-eyre",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"eframe",
|
||||
"egui-phosphor",
|
||||
"font-loader",
|
||||
"image",
|
||||
"komorebi-client",
|
||||
"netdev",
|
||||
"serde_json",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json_lenient",
|
||||
"serde_yaml 0.8.26",
|
||||
"starship-battery",
|
||||
"sysinfo 0.31.3",
|
||||
"windows 0.54.0",
|
||||
@@ -2717,7 +2738,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json_lenient",
|
||||
"serde_yaml",
|
||||
"serde_yaml 0.9.34+deprecated",
|
||||
"shadow-rs",
|
||||
"sysinfo 0.30.13",
|
||||
"thiserror",
|
||||
@@ -2851,6 +2872,12 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
@@ -3079,7 +3106,7 @@ dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"codespan-reporting",
|
||||
"hexf-parse",
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"log",
|
||||
"num-traits",
|
||||
"rustc-hash",
|
||||
@@ -3746,7 +3773,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3805,7 +3832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"quick-xml 0.32.0",
|
||||
"serde",
|
||||
"time",
|
||||
@@ -4539,13 +4566,25 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
|
||||
dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"ryu",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@@ -4586,9 +4625,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shadow-rs"
|
||||
version = "0.29.0"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a600f795d0894cda22235b44eea4b85c2a35b405f65523645ac8e35b306817a"
|
||||
checksum = "69fe0bac8a8752586a618a1c80d01d8ca5d40fce4f6077fbc851e48dcbdb90df"
|
||||
dependencies = [
|
||||
"const_format",
|
||||
"git2",
|
||||
@@ -5188,7 +5227,7 @@ version = "0.19.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"toml_datetime",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
@@ -5199,7 +5238,7 @@ version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"toml_datetime",
|
||||
"winnow 0.5.40",
|
||||
]
|
||||
@@ -5210,7 +5249,7 @@ version = "0.22.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@@ -5809,7 +5848,7 @@ dependencies = [
|
||||
"cfg_aliases 0.1.1",
|
||||
"codespan-reporting",
|
||||
"document-features",
|
||||
"indexmap",
|
||||
"indexmap 2.2.6",
|
||||
"log",
|
||||
"naga",
|
||||
"once_cell",
|
||||
@@ -6495,6 +6534,15 @@ version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "3.15.2"
|
||||
|
||||
@@ -14,6 +14,7 @@ members = [
|
||||
color-eyre = "0.6"
|
||||
dirs = "5"
|
||||
dunce = "1"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { package = "serde_json_lenient", version = "0.2" }
|
||||
serde_yaml = "0.9"
|
||||
@@ -22,7 +23,7 @@ uds_windows = "1"
|
||||
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "32a45cebf132c3d651ee22c0c40033a6b7edc945" }
|
||||
windows-implement = { version = "0.53" }
|
||||
windows-interface = { version = "0.53" }
|
||||
shadow-rs = "0.29"
|
||||
shadow-rs = "0.34"
|
||||
|
||||
[workspace.dependencies.windows]
|
||||
version = "0.54"
|
||||
|
||||
@@ -8,17 +8,22 @@ edition = "2021"
|
||||
[dependencies]
|
||||
komorebi-client = { path = "../komorebi-client" }
|
||||
|
||||
anyhow = "1"
|
||||
catppuccin-egui = { version = "5.1", default-features = false, features = ["egui28"] }
|
||||
chrono = "0.4"
|
||||
color-eyre = "0.6"
|
||||
crossbeam-channel = "0.5"
|
||||
dirs = { workspace = true }
|
||||
eframe = "0.28"
|
||||
egui-phosphor = "0.6.0"
|
||||
font-loader = "0.11"
|
||||
image = "0.25"
|
||||
netdev = "0.30"
|
||||
serde_json = "1"
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = "0.8"
|
||||
starship-battery = "0.10"
|
||||
sysinfo = "0.31"
|
||||
windows = { workspace = true }
|
||||
windows-icons = "0.1"
|
||||
egui-phosphor = "0.6.0"
|
||||
clap = { version = "4", features = ["derive", "wrap_help"] }
|
||||
|
||||
184
komorebi-bar/src/bar.rs
Normal file
184
komorebi-bar/src/bar.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
use crate::config::KomobarConfig;
|
||||
use crate::config::Theme;
|
||||
use crate::komorebi::Komorebi;
|
||||
use crate::komorebi::KomorebiNotificationState;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::widget::WidgetConfig;
|
||||
use crossbeam_channel::Receiver;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::CentralPanel;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontData;
|
||||
use eframe::egui::FontDefinitions;
|
||||
use eframe::egui::FontFamily;
|
||||
use eframe::egui::Frame;
|
||||
use eframe::egui::Layout;
|
||||
use eframe::egui::Margin;
|
||||
use font_loader::system_fonts;
|
||||
use font_loader::system_fonts::FontPropertyBuilder;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Komobar {
|
||||
pub config: KomobarConfig,
|
||||
pub komorebi_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
||||
pub left_widgets: Vec<Box<dyn BarWidget>>,
|
||||
pub right_widgets: Vec<Box<dyn BarWidget>>,
|
||||
pub rx_gui: Receiver<komorebi_client::Notification>,
|
||||
}
|
||||
|
||||
impl Komobar {
|
||||
pub fn new(
|
||||
cc: &eframe::CreationContext<'_>,
|
||||
rx_gui: Receiver<komorebi_client::Notification>,
|
||||
config: Arc<KomobarConfig>,
|
||||
) -> Self {
|
||||
if let Some(font_family) = &config.font_family {
|
||||
Self::add_custom_font(&cc.egui_ctx, font_family);
|
||||
}
|
||||
|
||||
match config.theme {
|
||||
Some(Theme::CatppuccinFrappe) => {
|
||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::FRAPPE);
|
||||
}
|
||||
Some(Theme::CatppuccinMacchiato) => {
|
||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MACCHIATO);
|
||||
}
|
||||
Some(Theme::CatppuccinMocha) => {
|
||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MOCHA);
|
||||
}
|
||||
Some(Theme::Default) | None => {}
|
||||
}
|
||||
|
||||
let mut komorebi_widget = None;
|
||||
let mut komorebi_widget_idx = None;
|
||||
let mut komorebi_notification_state = None;
|
||||
let mut side = None;
|
||||
|
||||
for (idx, widget_config) in config.left_widgets.iter().enumerate() {
|
||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||
komorebi_widget = Some(Komorebi::from(*config));
|
||||
komorebi_widget_idx = Some(idx);
|
||||
side = Some(Side::Left);
|
||||
}
|
||||
}
|
||||
|
||||
for (idx, widget_config) in config.right_widgets.iter().enumerate() {
|
||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||
komorebi_widget = Some(Komorebi::from(*config));
|
||||
komorebi_widget_idx = Some(idx);
|
||||
side = Some(Side::Right);
|
||||
}
|
||||
}
|
||||
|
||||
let mut left_widgets = config
|
||||
.left_widgets
|
||||
.iter()
|
||||
.map(|config| config.as_boxed_bar_widget())
|
||||
.collect::<Vec<Box<dyn BarWidget>>>();
|
||||
|
||||
let mut right_widgets = config
|
||||
.right_widgets
|
||||
.iter()
|
||||
.map(|config| config.as_boxed_bar_widget())
|
||||
.collect::<Vec<Box<dyn BarWidget>>>();
|
||||
|
||||
if let (Some(idx), Some(widget), Some(side)) = (komorebi_widget_idx, komorebi_widget, side)
|
||||
{
|
||||
komorebi_notification_state = Some(widget.komorebi_notification_state.clone());
|
||||
let boxed: Box<dyn BarWidget> = Box::new(widget);
|
||||
match side {
|
||||
Side::Left => left_widgets[idx] = boxed,
|
||||
Side::Right => right_widgets[idx] = boxed,
|
||||
}
|
||||
}
|
||||
|
||||
right_widgets.reverse();
|
||||
|
||||
Self {
|
||||
config: config.deref().clone(),
|
||||
komorebi_notification_state,
|
||||
left_widgets,
|
||||
right_widgets,
|
||||
rx_gui,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_custom_font(ctx: &Context, name: &str) {
|
||||
let mut fonts = FontDefinitions::default();
|
||||
egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular);
|
||||
|
||||
let property = FontPropertyBuilder::new().family(name).build();
|
||||
|
||||
if let Some((font, _)) = system_fonts::get(&property) {
|
||||
fonts
|
||||
.font_data
|
||||
.insert(name.to_owned(), FontData::from_owned(font));
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(FontFamily::Proportional)
|
||||
.or_default()
|
||||
.insert(0, name.to_owned());
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(FontFamily::Monospace)
|
||||
.or_default()
|
||||
.push(name.to_owned());
|
||||
|
||||
// Tell egui to use these fonts:
|
||||
ctx.set_fonts(fonts);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl eframe::App for Komobar {
|
||||
// TODO: I think this is needed for transparency??
|
||||
// fn clear_color(&self, _visuals: &Visuals) -> [f32; 4] {
|
||||
// egui::Rgba::TRANSPARENT.to_array()
|
||||
// let mut background = Color32::from_gray(18).to_normalized_gamma_f32();
|
||||
// background[3] = 0.9;
|
||||
// background
|
||||
// }
|
||||
|
||||
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||
if let Some(komorebi_notification_state) = &self.komorebi_notification_state {
|
||||
komorebi_notification_state
|
||||
.borrow_mut()
|
||||
.handle_notification(self.config.monitor.index, self.rx_gui.clone());
|
||||
}
|
||||
|
||||
let frame = if let Some(frame) = &self.config.frame {
|
||||
Frame::none().outer_margin(Margin::symmetric(
|
||||
frame.outer_margin.x,
|
||||
frame.outer_margin.y,
|
||||
))
|
||||
} else {
|
||||
Frame::none()
|
||||
};
|
||||
|
||||
CentralPanel::default().frame(frame).show(ctx, |ui| {
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||
for w in &mut self.left_widgets {
|
||||
w.render(ctx, ui);
|
||||
}
|
||||
});
|
||||
|
||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
for w in &mut self.right_widgets {
|
||||
w.render(ctx, ui);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Side {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use starship_battery::units::ratio::percent;
|
||||
use starship_battery::Manager;
|
||||
use starship_battery::State;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct BatteryConfig {
|
||||
/// Enable the Battery widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
@@ -102,7 +107,7 @@ impl BarWidget for Battery {
|
||||
);
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
106
komorebi-bar/src/config.rs
Normal file
106
komorebi-bar/src/config.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use crate::widget::WidgetConfig;
|
||||
use eframe::egui::Pos2;
|
||||
use eframe::egui::TextBuffer;
|
||||
use eframe::egui::Vec2;
|
||||
use komorebi_client::Rect;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomobarConfig {
|
||||
/// Viewport options (see: https://docs.rs/egui/latest/egui/viewport/struct.ViewportBuilder.html)
|
||||
pub viewport: Option<ViewportConfig>,
|
||||
/// Frame options (see: https://docs.rs/egui/latest/egui/containers/struct.Frame.html)
|
||||
pub frame: Option<FrameConfig>,
|
||||
/// Monitor options
|
||||
pub monitor: MonitorConfig,
|
||||
/// Font family
|
||||
pub font_family: Option<String>,
|
||||
/// Theme
|
||||
pub theme: Option<Theme>,
|
||||
/// Left side widgets (ordered left-to-right)
|
||||
pub left_widgets: Vec<WidgetConfig>,
|
||||
/// Right side widgets (ordered left-to-right)
|
||||
pub right_widgets: Vec<WidgetConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ViewportConfig {
|
||||
/// The desired starting position of the bar (0,0 = top left of the screen)
|
||||
pub position: Option<Position>,
|
||||
/// The desired size of the bar from the starting position (usually monitor width x desired height)
|
||||
pub inner_size: Option<Position>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct FrameConfig {
|
||||
/// Margin outside the painted frame
|
||||
pub outer_margin: Position,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MonitorConfig {
|
||||
/// Komorebi monitor index of the monitor on which to render the bar
|
||||
pub index: usize,
|
||||
/// Automatically apply a work area offset for this monitor to accommodate the bar
|
||||
pub work_area_offset: Option<Rect>,
|
||||
}
|
||||
|
||||
impl KomobarConfig {
|
||||
pub fn read(path: &PathBuf) -> color_eyre::Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
let mut value: Self = match path.extension().unwrap().to_string_lossy().as_str() {
|
||||
"yaml" => serde_yaml::from_str(&content)?,
|
||||
"json" => serde_json::from_str(&content)?,
|
||||
_ => panic!("unsupported format"),
|
||||
};
|
||||
|
||||
if value.frame.is_none() {
|
||||
value.frame = Some(FrameConfig {
|
||||
outer_margin: Position { x: 10.0, y: 10.0 },
|
||||
});
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct Position {
|
||||
/// X coordinate
|
||||
pub x: f32,
|
||||
/// Y coordinate
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl From<Position> for Vec2 {
|
||||
fn from(value: Position) -> Self {
|
||||
Self {
|
||||
x: value.x,
|
||||
y: value.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Position> for Pos2 {
|
||||
fn from(value: Position) -> Self {
|
||||
Self {
|
||||
x: value.x,
|
||||
y: value.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum Theme {
|
||||
/// Default egui theme
|
||||
Default,
|
||||
/// Catpuccin Frappe
|
||||
CatppuccinFrappe,
|
||||
/// Catpuccin Macchiato
|
||||
CatppuccinMacchiato,
|
||||
/// Catpuccin Mocha
|
||||
CatppuccinMocha,
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct DateConfig {
|
||||
/// Enable the Date widget
|
||||
pub enable: bool,
|
||||
/// Set the Date format
|
||||
pub format: DateFormat,
|
||||
}
|
||||
|
||||
@@ -19,12 +25,18 @@ impl From<DateConfig> for Date {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum DateFormat {
|
||||
/// Month/Date/Year format (09/08/24)
|
||||
MonthDateYear,
|
||||
/// Year-Month-Date format (2024-09-08)
|
||||
YearMonthDate,
|
||||
/// Date-Month-Year format (8-Sep-2024)
|
||||
DateMonthYear,
|
||||
/// Day Date Month Year format (8 September 2024)
|
||||
DayDateMonthYear,
|
||||
/// Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl DateFormat {
|
||||
@@ -34,6 +46,7 @@ impl DateFormat {
|
||||
DateFormat::YearMonthDate => *self = Self::DateMonthYear,
|
||||
DateFormat::DateMonthYear => *self = Self::DayDateMonthYear,
|
||||
DateFormat::DayDateMonthYear => *self = Self::MonthDateYear,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,11 +56,12 @@ impl DateFormat {
|
||||
DateFormat::YearMonthDate => String::from("%F"),
|
||||
DateFormat::DateMonthYear => String::from("%v"),
|
||||
DateFormat::DayDateMonthYear => String::from("%A %e %B %Y"),
|
||||
DateFormat::Custom(custom) => custom.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Date {
|
||||
pub enable: bool,
|
||||
pub format: DateFormat,
|
||||
@@ -82,7 +96,7 @@ impl BarWidget for Date {
|
||||
}
|
||||
|
||||
// TODO: make spacing configurable
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,59 @@
|
||||
use crate::img_to_texture;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::KomorebiNotificationState;
|
||||
use crate::WIDGET_SPACING;
|
||||
use crossbeam_channel::Receiver;
|
||||
use eframe::egui::ColorImage;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Image;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::SelectableLabel;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextureHandle;
|
||||
use eframe::egui::TextureOptions;
|
||||
use eframe::egui::Ui;
|
||||
use image::RgbaImage;
|
||||
use komorebi_client::CycleDirection;
|
||||
use komorebi_client::SocketMessage;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiConfig {
|
||||
pub enable: bool,
|
||||
pub monitor_index: usize,
|
||||
/// Configure the Workspaces widget
|
||||
pub workspaces: KomorebiWorkspacesConfig,
|
||||
/// Configure the Layout widget
|
||||
pub layout: KomorebiLayoutConfig,
|
||||
/// Configure the Focused Window widget
|
||||
pub focused_window: KomorebiFocusedWindowConfig,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiWorkspacesConfig {
|
||||
/// Enable the Komorebi Workspaces widget
|
||||
pub enable: bool,
|
||||
/// Hide workspaces without any windows
|
||||
pub hide_empty_workspaces: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiLayoutConfig {
|
||||
/// Enable the Komorebi Layout widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiFocusedWindowConfig {
|
||||
/// Enable the Komorebi Focused Window widget
|
||||
pub enable: bool,
|
||||
/// Show the icon of the currently focused window
|
||||
pub show_icon: bool,
|
||||
}
|
||||
|
||||
impl From<KomorebiConfig> for Komorebi {
|
||||
fn from(value: KomorebiConfig) -> Self {
|
||||
Self {
|
||||
enable: value.enable,
|
||||
komorebi_notification_state: Rc::new(RefCell::new(KomorebiNotificationState {
|
||||
selected_workspace: String::new(),
|
||||
focused_window_title: String::new(),
|
||||
@@ -49,7 +61,6 @@ impl From<KomorebiConfig> for Komorebi {
|
||||
focused_window_icon: None,
|
||||
layout: String::new(),
|
||||
workspaces: vec![],
|
||||
monitor_index: value.monitor_index,
|
||||
hide_empty_workspaces: value.workspaces.hide_empty_workspaces,
|
||||
})),
|
||||
workspaces: value.workspaces,
|
||||
@@ -61,7 +72,6 @@ impl From<KomorebiConfig> for Komorebi {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Komorebi {
|
||||
pub enable: bool,
|
||||
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||
pub workspaces: KomorebiWorkspacesConfig,
|
||||
pub layout: KomorebiLayoutConfig,
|
||||
@@ -70,72 +80,150 @@ pub struct Komorebi {
|
||||
|
||||
impl BarWidget for Komorebi {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
if self.enable {
|
||||
let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut();
|
||||
let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut();
|
||||
|
||||
if self.workspaces.enable {
|
||||
let mut update = None;
|
||||
|
||||
if self.workspaces.enable {
|
||||
for (i, ws) in komorebi_notification_state.workspaces.iter().enumerate() {
|
||||
if ui
|
||||
.add(SelectableLabel::new(
|
||||
komorebi_notification_state.selected_workspace.eq(ws),
|
||||
ws.to_string(),
|
||||
))
|
||||
.clicked()
|
||||
{
|
||||
update = Some(ws.to_string());
|
||||
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(false))
|
||||
.unwrap();
|
||||
komorebi_client::send_message(&SocketMessage::FocusWorkspaceNumber(i))
|
||||
.unwrap();
|
||||
// TODO: store MFF value from state and restore that here instead of "true"
|
||||
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(true))
|
||||
.unwrap();
|
||||
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(update) = update {
|
||||
komorebi_notification_state.selected_workspace = update;
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
}
|
||||
|
||||
if self.layout.enable {
|
||||
for (i, ws) in komorebi_notification_state.workspaces.iter().enumerate() {
|
||||
if ui
|
||||
.add(
|
||||
Label::new(&komorebi_notification_state.layout)
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.add(SelectableLabel::new(
|
||||
komorebi_notification_state.selected_workspace.eq(ws),
|
||||
ws.to_string(),
|
||||
))
|
||||
.clicked()
|
||||
{
|
||||
komorebi_client::send_message(&SocketMessage::CycleLayout(
|
||||
CycleDirection::Next,
|
||||
))
|
||||
.unwrap();
|
||||
update = Some(ws.to_string());
|
||||
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(false))
|
||||
.unwrap();
|
||||
komorebi_client::send_message(&SocketMessage::FocusWorkspaceNumber(i)).unwrap();
|
||||
// TODO: store MFF value from state and restore that here instead of "true"
|
||||
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(true)).unwrap();
|
||||
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
}
|
||||
|
||||
if self.focused_window.enable {
|
||||
if self.focused_window.show_icon {
|
||||
if let Some(img) = &komorebi_notification_state.focused_window_icon {
|
||||
ui.add(
|
||||
Image::from(&img_to_texture(ctx, img))
|
||||
.maintain_aspect_ratio(true)
|
||||
.max_height(15.0),
|
||||
);
|
||||
if let Some(update) = update {
|
||||
komorebi_notification_state.selected_workspace = update;
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
|
||||
if self.layout.enable {
|
||||
if ui
|
||||
.add(
|
||||
Label::new(&komorebi_notification_state.layout)
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
komorebi_client::send_message(&SocketMessage::CycleLayout(CycleDirection::Next))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
|
||||
if self.focused_window.enable {
|
||||
if self.focused_window.show_icon {
|
||||
if let Some(img) = &komorebi_notification_state.focused_window_icon {
|
||||
ui.add(
|
||||
Image::from(&img_to_texture(ctx, img))
|
||||
.maintain_aspect_ratio(true)
|
||||
.max_height(15.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ui.add(Label::new(&komorebi_notification_state.focused_window_title).selectable(false));
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
||||
let size = [rgba_image.width() as usize, rgba_image.height() as usize];
|
||||
let pixels = rgba_image.as_flat_samples();
|
||||
let color_image = ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
|
||||
ctx.load_texture("icon", color_image, TextureOptions::default())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KomorebiNotificationState {
|
||||
pub workspaces: Vec<String>,
|
||||
pub selected_workspace: String,
|
||||
pub focused_window_title: String,
|
||||
pub focused_window_pid: Option<u32>,
|
||||
pub focused_window_icon: Option<RgbaImage>,
|
||||
pub layout: String,
|
||||
pub hide_empty_workspaces: bool,
|
||||
}
|
||||
|
||||
impl KomorebiNotificationState {
|
||||
pub fn handle_notification(
|
||||
&mut self,
|
||||
monitor_index: usize,
|
||||
rx_gui: Receiver<komorebi_client::Notification>,
|
||||
) {
|
||||
if let Ok(notification) = rx_gui.try_recv() {
|
||||
let monitor = ¬ification.state.monitors.elements()[monitor_index];
|
||||
let focused_workspace_idx = monitor.focused_workspace_idx();
|
||||
|
||||
let mut workspaces = vec![];
|
||||
self.selected_workspace = monitor.workspaces()[focused_workspace_idx]
|
||||
.name()
|
||||
.to_owned()
|
||||
.unwrap_or_else(|| format!("{}", focused_workspace_idx + 1));
|
||||
|
||||
for (i, ws) in monitor.workspaces().iter().enumerate() {
|
||||
let should_add = if self.hide_empty_workspaces {
|
||||
focused_workspace_idx == i || !ws.containers().is_empty()
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if should_add {
|
||||
workspaces.push(ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
self.workspaces = workspaces;
|
||||
self.layout = match monitor.workspaces()[focused_workspace_idx].layout() {
|
||||
komorebi_client::Layout::Default(layout) => layout.to_string(),
|
||||
komorebi_client::Layout::Custom(_) => String::from("Custom"),
|
||||
};
|
||||
|
||||
if let Some(container) = monitor.workspaces()[focused_workspace_idx].focused_container()
|
||||
{
|
||||
if let Some(window) = container.focused_window() {
|
||||
if let Ok(title) = window.title() {
|
||||
self.focused_window_title.clone_from(&title);
|
||||
self.focused_window_pid = Some(window.process_id());
|
||||
let img = windows_icons::get_icon_by_process_id(window.process_id());
|
||||
self.focused_window_icon = Some(img);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.focused_window_title.clear();
|
||||
self.focused_window_icon = None;
|
||||
}
|
||||
|
||||
ui.add(
|
||||
Label::new(&komorebi_notification_state.focused_window_title).selectable(false),
|
||||
);
|
||||
if let Some(container) = monitor.workspaces()[focused_workspace_idx].monocle_container()
|
||||
{
|
||||
if let Some(window) = container.focused_window() {
|
||||
if let Ok(title) = window.title() {
|
||||
self.focused_window_title.clone_from(&title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
if let Some(window) = monitor.workspaces()[focused_workspace_idx].maximized_window() {
|
||||
if let Ok(title) = window.title() {
|
||||
self.focused_window_title.clone_from(&title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod bar;
|
||||
mod battery;
|
||||
mod config;
|
||||
mod date;
|
||||
mod komorebi;
|
||||
mod media;
|
||||
@@ -8,161 +10,125 @@ mod storage;
|
||||
mod time;
|
||||
mod widget;
|
||||
|
||||
use crate::battery::Battery;
|
||||
use crate::battery::BatteryConfig;
|
||||
use crate::date::Date;
|
||||
use crate::date::DateConfig;
|
||||
use crate::date::DateFormat;
|
||||
use crate::komorebi::Komorebi;
|
||||
use crate::komorebi::KomorebiConfig;
|
||||
use crate::komorebi::KomorebiFocusedWindowConfig;
|
||||
use crate::komorebi::KomorebiLayoutConfig;
|
||||
use crate::komorebi::KomorebiWorkspacesConfig;
|
||||
use crate::media::Media;
|
||||
use crate::media::MediaConfig;
|
||||
use crate::memory::Memory;
|
||||
use crate::memory::MemoryConfig;
|
||||
use crate::network::Network;
|
||||
use crate::network::NetworkConfig;
|
||||
use crate::storage::Storage;
|
||||
use crate::storage::StorageConfig;
|
||||
use crate::time::TimeConfig;
|
||||
use crate::time::TimeFormat;
|
||||
use crate::widget::BarWidget;
|
||||
use crossbeam_channel::Receiver;
|
||||
use eframe::egui;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::ColorImage;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Layout;
|
||||
use eframe::egui::TextureHandle;
|
||||
use crate::bar::Komobar;
|
||||
use crate::config::KomobarConfig;
|
||||
use crate::config::Position;
|
||||
use clap::Parser;
|
||||
use eframe::egui::ViewportBuilder;
|
||||
use eframe::emath::Pos2;
|
||||
use eframe::emath::Vec2;
|
||||
use font_loader::system_fonts;
|
||||
use font_loader::system_fonts::FontPropertyBuilder;
|
||||
use image::RgbaImage;
|
||||
use komorebi_client::SocketMessage;
|
||||
use std::cell::RefCell;
|
||||
use schemars::gen::SchemaSettings;
|
||||
use std::io::BufReader;
|
||||
use std::io::Read;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use time::Time;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Position {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
pub static WIDGET_SPACING: f32 = 10.0;
|
||||
|
||||
impl From<Position> for Vec2 {
|
||||
fn from(value: Position) -> Self {
|
||||
Self {
|
||||
x: value.x,
|
||||
y: value.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Position> for Pos2 {
|
||||
fn from(value: Position) -> Self {
|
||||
Self {
|
||||
x: value.x,
|
||||
y: value.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
inner_size: Position,
|
||||
position: Position,
|
||||
outer_margin: Position,
|
||||
monitor_index: usize,
|
||||
monitor_work_area_offset: Option<komorebi_client::Rect>,
|
||||
font_family: Option<String>,
|
||||
time: TimeConfig,
|
||||
date: DateConfig,
|
||||
storage: StorageConfig,
|
||||
memory: MemoryConfig,
|
||||
media: MediaConfig,
|
||||
battery: BatteryConfig,
|
||||
network: NetworkConfig,
|
||||
komorebi: KomorebiConfig,
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Theme {
|
||||
Default,
|
||||
CatppuccinFrappe,
|
||||
CatppuccinMacchiato,
|
||||
CatppuccinMocha,
|
||||
#[derive(Parser)]
|
||||
#[clap(author, about, version)]
|
||||
struct Opts {
|
||||
/// Print the JSON schema of the configuration file and exit
|
||||
#[clap(long)]
|
||||
schema: bool,
|
||||
/// Path to a JSON or YAML configuration file
|
||||
#[clap(short, long)]
|
||||
config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
let config = Config {
|
||||
inner_size: Position { x: 5120.0, y: 20.0 },
|
||||
position: Position { x: 0.0, y: 0.0 },
|
||||
outer_margin: Position { x: 10.0, y: 10.0 },
|
||||
monitor_index: 0,
|
||||
font_family: Some(String::from("JetBrains Mono")),
|
||||
monitor_work_area_offset: Some(komorebi_client::Rect {
|
||||
left: 0,
|
||||
top: 40,
|
||||
right: 0,
|
||||
bottom: 40,
|
||||
}),
|
||||
time: TimeConfig {
|
||||
enable: true,
|
||||
format: TimeFormat::TwentyFourHour,
|
||||
let opts: Opts = Opts::parse();
|
||||
|
||||
if opts.schema {
|
||||
let settings = SchemaSettings::default().with(|s| {
|
||||
s.option_nullable = false;
|
||||
s.option_add_null_type = false;
|
||||
s.inline_subschemas = true;
|
||||
});
|
||||
|
||||
let gen = settings.into_generator();
|
||||
let socket_message = gen.into_root_schema_for::<KomobarConfig>();
|
||||
let schema = serde_json::to_string_pretty(&socket_message).unwrap();
|
||||
|
||||
println!("{schema}");
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||
|_| dirs::home_dir().expect("there is no home directory"),
|
||||
|home_path| {
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!("$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory");
|
||||
}
|
||||
},
|
||||
date: DateConfig {
|
||||
enable: true,
|
||||
format: DateFormat::DayDateMonthYear,
|
||||
);
|
||||
|
||||
let config_path = opts.config.map_or_else(
|
||||
|| {
|
||||
let mut config = home_dir.join("komorebi.bar.json");
|
||||
if !config.is_file() {
|
||||
config.pop();
|
||||
config.push("komorebi.bar.yaml");
|
||||
}
|
||||
|
||||
if !config.is_file() {
|
||||
None
|
||||
} else {
|
||||
Some(config)
|
||||
}
|
||||
},
|
||||
storage: StorageConfig { enable: true },
|
||||
memory: MemoryConfig { enable: true },
|
||||
media: MediaConfig { enable: true },
|
||||
battery: BatteryConfig { enable: true },
|
||||
network: NetworkConfig {
|
||||
enable: true,
|
||||
show_data: true,
|
||||
},
|
||||
komorebi: KomorebiConfig {
|
||||
enable: true,
|
||||
monitor_index: 0,
|
||||
workspaces: KomorebiWorkspacesConfig {
|
||||
enable: true,
|
||||
hide_empty_workspaces: true,
|
||||
},
|
||||
layout: KomorebiLayoutConfig { enable: true },
|
||||
focused_window: KomorebiFocusedWindowConfig {
|
||||
enable: true,
|
||||
show_icon: true,
|
||||
},
|
||||
},
|
||||
theme: Theme::CatppuccinMacchiato,
|
||||
Option::from,
|
||||
);
|
||||
|
||||
let config = match config_path {
|
||||
None => panic!(
|
||||
"no komorebi.bar.json or komorebi.bar.yaml found in {}",
|
||||
home_dir.as_path().to_string_lossy()
|
||||
),
|
||||
Some(config) => KomobarConfig::read(&config).unwrap(),
|
||||
};
|
||||
|
||||
// TODO: ensure that config.monitor_index represents a valid komorebi monitor index
|
||||
let mut builder = ViewportBuilder::default()
|
||||
.with_decorations(false)
|
||||
// .with_transparent(config.transparent)
|
||||
.with_taskbar(false)
|
||||
.with_position(Position { x: 0.0, y: 0.0 })
|
||||
.with_inner_size({
|
||||
let state = serde_json::from_str::<komorebi_client::State>(
|
||||
&komorebi_client::send_query(&SocketMessage::State).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Position {
|
||||
x: state.monitors.elements()[config.monitor.index].size().right as f32,
|
||||
y: 20.0,
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(viewport) = &config.viewport {
|
||||
if let Some(position) = &viewport.position {
|
||||
let b = builder.clone();
|
||||
builder = b.with_position(*position);
|
||||
}
|
||||
|
||||
if let Some(inner_size) = &viewport.inner_size {
|
||||
let b = builder.clone();
|
||||
builder = b.with_inner_size(*inner_size);
|
||||
}
|
||||
}
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: ViewportBuilder::default()
|
||||
.with_decorations(false)
|
||||
// .with_transparent(config.transparent)
|
||||
.with_position(config.position)
|
||||
.with_taskbar(false)
|
||||
.with_inner_size(config.inner_size),
|
||||
viewport: builder,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(rect) = &config.monitor_work_area_offset {
|
||||
if let Some(rect) = &config.monitor.work_area_offset {
|
||||
komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset(
|
||||
config.monitor_index,
|
||||
config.monitor.index,
|
||||
*rect,
|
||||
))
|
||||
.unwrap();
|
||||
@@ -207,10 +173,10 @@ fn main() -> eframe::Result<()> {
|
||||
}
|
||||
|
||||
// here we have reconnected
|
||||
if let Some(rect) = &config_cl.monitor_work_area_offset {
|
||||
if let Some(rect) = &config_cl.monitor.work_area_offset {
|
||||
while komorebi_client::send_message(
|
||||
&SocketMessage::MonitorWorkAreaOffset(
|
||||
config_cl.monitor_index,
|
||||
config_cl.monitor.index,
|
||||
*rect,
|
||||
),
|
||||
)
|
||||
@@ -241,214 +207,3 @@ fn main() -> eframe::Result<()> {
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
struct Komobar {
|
||||
config: Config,
|
||||
komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||
left_widgets: Vec<Box<dyn BarWidget>>,
|
||||
right_widgets: Vec<Box<dyn BarWidget>>,
|
||||
rx_gui: Receiver<komorebi_client::Notification>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct KomorebiNotificationState {
|
||||
monitor_index: usize,
|
||||
workspaces: Vec<String>,
|
||||
selected_workspace: String,
|
||||
focused_window_title: String,
|
||||
focused_window_pid: Option<u32>,
|
||||
focused_window_icon: Option<RgbaImage>,
|
||||
layout: String,
|
||||
hide_empty_workspaces: bool,
|
||||
}
|
||||
|
||||
impl KomorebiNotificationState {
|
||||
fn handle_notification(&mut self, rx_gui: Receiver<komorebi_client::Notification>) {
|
||||
if let Ok(notification) = rx_gui.try_recv() {
|
||||
let monitor = ¬ification.state.monitors.elements()[self.monitor_index];
|
||||
let focused_workspace_idx = monitor.focused_workspace_idx();
|
||||
|
||||
let mut workspaces = vec![];
|
||||
self.selected_workspace = monitor.workspaces()[focused_workspace_idx]
|
||||
.name()
|
||||
.to_owned()
|
||||
.unwrap_or_else(|| format!("{}", focused_workspace_idx + 1));
|
||||
|
||||
for (i, ws) in monitor.workspaces().iter().enumerate() {
|
||||
let should_add = if self.hide_empty_workspaces {
|
||||
focused_workspace_idx == i || !ws.containers().is_empty()
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if should_add {
|
||||
workspaces.push(ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
self.workspaces = workspaces;
|
||||
self.layout = match monitor.workspaces()[focused_workspace_idx].layout() {
|
||||
komorebi_client::Layout::Default(layout) => layout.to_string(),
|
||||
komorebi_client::Layout::Custom(_) => String::from("Custom"),
|
||||
};
|
||||
|
||||
if let Some(container) = monitor.workspaces()[focused_workspace_idx].focused_container()
|
||||
{
|
||||
if let Some(window) = container.focused_window() {
|
||||
if let Ok(title) = window.title() {
|
||||
self.focused_window_title.clone_from(&title);
|
||||
self.focused_window_pid = Some(window.process_id());
|
||||
let img = windows_icons::get_icon_by_process_id(window.process_id());
|
||||
self.focused_window_icon = Some(img);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.focused_window_title.clear();
|
||||
self.focused_window_icon = None;
|
||||
}
|
||||
|
||||
if let Some(container) = monitor.workspaces()[focused_workspace_idx].monocle_container()
|
||||
{
|
||||
if let Some(window) = container.focused_window() {
|
||||
if let Ok(title) = window.title() {
|
||||
self.focused_window_title.clone_from(&title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(window) = monitor.workspaces()[focused_workspace_idx].maximized_window() {
|
||||
if let Ok(title) = window.title() {
|
||||
self.focused_window_title.clone_from(&title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_custom_font(ctx: &Context, name: &str) {
|
||||
let mut fonts = egui::FontDefinitions::default();
|
||||
egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular);
|
||||
|
||||
let property = FontPropertyBuilder::new().family(name).build();
|
||||
|
||||
if let Some((font, _)) = system_fonts::get(&property) {
|
||||
// Install my own font (maybe supporting non-latin characters).
|
||||
// .ttf and .otf files supported.
|
||||
fonts
|
||||
.font_data
|
||||
.insert(name.to_owned(), egui::FontData::from_owned(font));
|
||||
|
||||
// Put my font first (highest priority) for proportional text:
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Proportional)
|
||||
.or_default()
|
||||
.insert(0, name.to_owned());
|
||||
|
||||
// Put my font as last fallback for monospace:
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Monospace)
|
||||
.or_default()
|
||||
.push(name.to_owned());
|
||||
|
||||
// Tell egui to use these fonts:
|
||||
ctx.set_fonts(fonts);
|
||||
}
|
||||
}
|
||||
impl Komobar {
|
||||
fn new(
|
||||
cc: &eframe::CreationContext<'_>,
|
||||
rx: Receiver<komorebi_client::Notification>,
|
||||
config: Arc<Config>,
|
||||
) -> Self {
|
||||
if let Some(font_family) = &config.font_family {
|
||||
add_custom_font(&cc.egui_ctx, font_family);
|
||||
}
|
||||
|
||||
match config.theme {
|
||||
Theme::Default => {}
|
||||
Theme::CatppuccinFrappe => {
|
||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::FRAPPE);
|
||||
}
|
||||
Theme::CatppuccinMacchiato => {
|
||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MACCHIATO);
|
||||
}
|
||||
Theme::CatppuccinMocha => {
|
||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MOCHA);
|
||||
}
|
||||
}
|
||||
|
||||
// Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
|
||||
// Restore app state using cc.storage (requires the "persistence" feature).
|
||||
// Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
|
||||
// for e.g. egui::PaintCallback.
|
||||
let komorebi_workspaces = Komorebi::from(config.komorebi);
|
||||
let komorebi_notification_state = komorebi_workspaces.komorebi_notification_state.clone();
|
||||
|
||||
let left_widgets: Vec<Box<dyn BarWidget>> = vec![Box::new(komorebi_workspaces.clone())];
|
||||
|
||||
let mut right_widgets: Vec<Box<dyn BarWidget>> = vec![
|
||||
Box::new(Media::from(config.media)),
|
||||
Box::new(Storage::from(config.storage)),
|
||||
Box::new(Memory::from(config.memory)),
|
||||
Box::new(Network::from(config.network)),
|
||||
Box::new(Date::from(config.date)),
|
||||
Box::new(Time::from(config.time)),
|
||||
Box::new(Battery::from(config.battery)),
|
||||
];
|
||||
|
||||
right_widgets.reverse();
|
||||
|
||||
Self {
|
||||
config: config.deref().clone(),
|
||||
komorebi_notification_state,
|
||||
left_widgets,
|
||||
right_widgets,
|
||||
rx_gui: rx,
|
||||
}
|
||||
}
|
||||
}
|
||||
fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
||||
let size = [rgba_image.width() as usize, rgba_image.height() as usize];
|
||||
let pixels = rgba_image.as_flat_samples();
|
||||
let color_image = ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
|
||||
ctx.load_texture("icon", color_image, egui::TextureOptions::default())
|
||||
}
|
||||
|
||||
impl eframe::App for Komobar {
|
||||
// TODO: I think this is needed for transparency??
|
||||
// fn clear_color(&self, _visuals: &Visuals) -> [f32; 4] {
|
||||
// egui::Rgba::TRANSPARENT.to_array()
|
||||
// let mut background = Color32::from_gray(18).to_normalized_gamma_f32();
|
||||
// background[3] = 0.9;
|
||||
// background
|
||||
// }
|
||||
|
||||
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||
self.komorebi_notification_state
|
||||
.borrow_mut()
|
||||
.handle_notification(self.rx_gui.clone());
|
||||
|
||||
egui::CentralPanel::default()
|
||||
.frame(egui::Frame::none().outer_margin(egui::Margin::symmetric(
|
||||
self.config.outer_margin.x,
|
||||
self.config.outer_margin.y,
|
||||
)))
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||
for w in &mut self.left_widgets {
|
||||
w.render(ctx, ui);
|
||||
}
|
||||
});
|
||||
|
||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
for w in &mut self.right_widgets {
|
||||
w.render(ctx, ui);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use windows::Media::Control::GlobalSystemMediaTransportControlsSessionManager;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MediaConfig {
|
||||
/// Enable the Media widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
@@ -79,7 +84,7 @@ impl BarWidget for Media {
|
||||
self.toggle();
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use sysinfo::RefreshKind;
|
||||
use sysinfo::System;
|
||||
|
||||
pub struct Memory {
|
||||
pub enable: bool,
|
||||
system: System,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MemoryConfig {
|
||||
/// Enable the Memory widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
@@ -35,6 +34,12 @@ impl From<MemoryConfig> for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Memory {
|
||||
pub enable: bool,
|
||||
system: System,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
fn output(&mut self) -> Vec<String> {
|
||||
let now = Instant::now();
|
||||
@@ -68,7 +73,7 @@ impl BarWidget for Memory {
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use sysinfo::Networks;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct NetworkConfig {
|
||||
/// Enable the Network widget
|
||||
pub enable: bool,
|
||||
/// Show network transfer data
|
||||
pub show_data: bool,
|
||||
}
|
||||
|
||||
@@ -140,7 +146,7 @@ impl BarWidget for Network {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use sysinfo::Disks;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct StorageConfig {
|
||||
/// Enable the Storage widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
@@ -87,7 +92,7 @@ impl BarWidget for Storage {
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct TimeConfig {
|
||||
/// Enable the Time widget
|
||||
pub enable: bool,
|
||||
/// Set the Time format
|
||||
pub format: TimeFormat,
|
||||
}
|
||||
|
||||
@@ -19,10 +25,14 @@ impl From<TimeConfig> for Time {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum TimeFormat {
|
||||
/// Twelve-hour format (with seconds)
|
||||
TwelveHour,
|
||||
/// Twenty-four-hour format (with seconds)
|
||||
TwentyFourHour,
|
||||
/// Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl TimeFormat {
|
||||
@@ -30,6 +40,7 @@ impl TimeFormat {
|
||||
match self {
|
||||
TimeFormat::TwelveHour => *self = TimeFormat::TwentyFourHour,
|
||||
TimeFormat::TwentyFourHour => *self = TimeFormat::TwelveHour,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,11 +48,12 @@ impl TimeFormat {
|
||||
match self {
|
||||
TimeFormat::TwelveHour => String::from("%l:%M:%S %p"),
|
||||
TimeFormat::TwentyFourHour => String::from("%T"),
|
||||
TimeFormat::Custom(format) => format.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Time {
|
||||
pub enable: bool,
|
||||
pub format: TimeFormat,
|
||||
@@ -72,7 +84,7 @@ impl BarWidget for Time {
|
||||
}
|
||||
|
||||
// TODO: make spacing configurable
|
||||
ui.add_space(10.0);
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,52 @@
|
||||
use crate::battery::Battery;
|
||||
use crate::battery::BatteryConfig;
|
||||
use crate::date::Date;
|
||||
use crate::date::DateConfig;
|
||||
use crate::komorebi::Komorebi;
|
||||
use crate::komorebi::KomorebiConfig;
|
||||
use crate::media::Media;
|
||||
use crate::media::MediaConfig;
|
||||
use crate::memory::Memory;
|
||||
use crate::memory::MemoryConfig;
|
||||
use crate::network::Network;
|
||||
use crate::network::NetworkConfig;
|
||||
use crate::storage::Storage;
|
||||
use crate::storage::StorageConfig;
|
||||
use crate::time::Time;
|
||||
use crate::time::TimeConfig;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
pub trait BarWidget {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum WidgetConfig {
|
||||
Battery(BatteryConfig),
|
||||
Date(DateConfig),
|
||||
Komorebi(KomorebiConfig),
|
||||
Media(MediaConfig),
|
||||
Memory(MemoryConfig),
|
||||
Network(NetworkConfig),
|
||||
Storage(StorageConfig),
|
||||
Time(TimeConfig),
|
||||
}
|
||||
|
||||
impl WidgetConfig {
|
||||
pub fn as_boxed_bar_widget(&self) -> Box<dyn BarWidget> {
|
||||
match self {
|
||||
WidgetConfig::Battery(config) => Box::new(Battery::from(*config)),
|
||||
WidgetConfig::Date(config) => Box::new(Date::from(config.clone())),
|
||||
WidgetConfig::Komorebi(config) => Box::new(Komorebi::from(*config)),
|
||||
WidgetConfig::Media(config) => Box::new(Media::from(*config)),
|
||||
WidgetConfig::Memory(config) => Box::new(Memory::from(*config)),
|
||||
WidgetConfig::Network(config) => Box::new(Network::from(*config)),
|
||||
WidgetConfig::Storage(config) => Box::new(Storage::from(*config)),
|
||||
WidgetConfig::Time(config) => Box::new(Time::from(config.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ os_info = "3.8"
|
||||
parking_lot = "0.12"
|
||||
paste = "1"
|
||||
regex = "1"
|
||||
schemars = "0.8"
|
||||
schemars = { workspace = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
|
||||
788
schema.bar.json
Normal file
788
schema.bar.json
Normal file
@@ -0,0 +1,788 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "KomobarConfig",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"left_widgets",
|
||||
"monitor",
|
||||
"right_widgets",
|
||||
"theme"
|
||||
],
|
||||
"properties": {
|
||||
"font_family": {
|
||||
"description": "Font family",
|
||||
"type": "string"
|
||||
},
|
||||
"frame": {
|
||||
"description": "Frame options (see: https://docs.rs/egui/latest/egui/containers/struct.Frame.html)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"outer_margin"
|
||||
],
|
||||
"properties": {
|
||||
"outer_margin": {
|
||||
"description": "Margin outside the painted frame",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"description": "X coordinate",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"y": {
|
||||
"description": "Y coordinate",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"left_widgets": {
|
||||
"description": "Left side widgets (ordered left-to-right)",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Battery"
|
||||
],
|
||||
"properties": {
|
||||
"Battery": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Battery widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Date"
|
||||
],
|
||||
"properties": {
|
||||
"Date": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"format"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Date widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"format": {
|
||||
"description": "Set the Date format",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Month/Date/Year format (09/08/24)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"MonthDateYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Year-Month-Date format (2024-09-08)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"YearMonthDate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Date-Month-Year format (8-Sep-2024)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DateMonthYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Day Date Month Year format (8 September 2024)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DayDateMonthYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Custom"
|
||||
],
|
||||
"properties": {
|
||||
"Custom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Komorebi"
|
||||
],
|
||||
"properties": {
|
||||
"Komorebi": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"focused_window",
|
||||
"layout",
|
||||
"workspaces"
|
||||
],
|
||||
"properties": {
|
||||
"focused_window": {
|
||||
"description": "Configure the Focused Window widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"show_icon"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Focused Window widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"show_icon": {
|
||||
"description": "Show the icon of the currently focused window",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"description": "Configure the Layout widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Layout widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Configure the Workspaces widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"hide_empty_workspaces"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Workspaces widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hide_empty_workspaces": {
|
||||
"description": "Hide workspaces without any windows",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Media"
|
||||
],
|
||||
"properties": {
|
||||
"Media": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Media widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Memory"
|
||||
],
|
||||
"properties": {
|
||||
"Memory": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Memory widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Network"
|
||||
],
|
||||
"properties": {
|
||||
"Network": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"show_data"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Network widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"show_data": {
|
||||
"description": "Show network transfer data",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Storage"
|
||||
],
|
||||
"properties": {
|
||||
"Storage": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Storage widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Time"
|
||||
],
|
||||
"properties": {
|
||||
"Time": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"format"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Time widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"format": {
|
||||
"description": "Set the Time format",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Twelve-hour format (with seconds)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"TwelveHour"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Twenty-four-hour format (with seconds)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"TwentyFourHour"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Custom"
|
||||
],
|
||||
"properties": {
|
||||
"Custom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"monitor": {
|
||||
"description": "Monitor options",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"index": {
|
||||
"description": "Komorebi monitor index of the monitor on which to render the bar",
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"work_area_offset": {
|
||||
"description": "Automatically apply a work area offset for this monitor to accommodate the bar",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"bottom",
|
||||
"left",
|
||||
"right",
|
||||
"top"
|
||||
],
|
||||
"properties": {
|
||||
"bottom": {
|
||||
"description": "The bottom point in a Win32 Rect",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"left": {
|
||||
"description": "The left point in a Win32 Rect",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"right": {
|
||||
"description": "The right point in a Win32 Rect",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"top": {
|
||||
"description": "The top point in a Win32 Rect",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"right_widgets": {
|
||||
"description": "Right side widgets (ordered left-to-right)",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Battery"
|
||||
],
|
||||
"properties": {
|
||||
"Battery": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Battery widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Date"
|
||||
],
|
||||
"properties": {
|
||||
"Date": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"format"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Date widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"format": {
|
||||
"description": "Set the Date format",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Month/Date/Year format (09/08/24)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"MonthDateYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Year-Month-Date format (2024-09-08)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"YearMonthDate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Date-Month-Year format (8-Sep-2024)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DateMonthYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Day Date Month Year format (8 September 2024)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"DayDateMonthYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Custom"
|
||||
],
|
||||
"properties": {
|
||||
"Custom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Komorebi"
|
||||
],
|
||||
"properties": {
|
||||
"Komorebi": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"focused_window",
|
||||
"layout",
|
||||
"workspaces"
|
||||
],
|
||||
"properties": {
|
||||
"focused_window": {
|
||||
"description": "Configure the Focused Window widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"show_icon"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Focused Window widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"show_icon": {
|
||||
"description": "Show the icon of the currently focused window",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"description": "Configure the Layout widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Layout widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Configure the Workspaces widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"hide_empty_workspaces"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Workspaces widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hide_empty_workspaces": {
|
||||
"description": "Hide workspaces without any windows",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Media"
|
||||
],
|
||||
"properties": {
|
||||
"Media": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Media widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Memory"
|
||||
],
|
||||
"properties": {
|
||||
"Memory": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Memory widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Network"
|
||||
],
|
||||
"properties": {
|
||||
"Network": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"show_data"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Network widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"show_data": {
|
||||
"description": "Show network transfer data",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Storage"
|
||||
],
|
||||
"properties": {
|
||||
"Storage": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Storage widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Time"
|
||||
],
|
||||
"properties": {
|
||||
"Time": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"format"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Time widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"format": {
|
||||
"description": "Set the Time format",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Twelve-hour format (with seconds)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"TwelveHour"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Twenty-four-hour format (with seconds)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"TwentyFourHour"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Custom"
|
||||
],
|
||||
"properties": {
|
||||
"Custom": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"description": "Theme",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Default egui theme",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Catpuccin Frappe",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"CatppuccinFrappe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Catpuccin Macchiato",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"CatppuccinMacchiato"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Catpuccin Mocha",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"CatppuccinMocha"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"viewport": {
|
||||
"description": "Viewport options (see: https://docs.rs/egui/latest/egui/viewport/struct.ViewportBuilder.html)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inner_size": {
|
||||
"description": "The desired size of the bar from the starting position (usually monitor width x desired height)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"description": "X coordinate",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"y": {
|
||||
"description": "Y coordinate",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
}
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"description": "The desired starting position of the bar (0,0 = top left of the screen)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"properties": {
|
||||
"x": {
|
||||
"description": "X coordinate",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
},
|
||||
"y": {
|
||||
"description": "Y coordinate",
|
||||
"type": "number",
|
||||
"format": "float"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user