mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-02 08:57:38 +01:00
Compare commits
13 Commits
feature/st
...
monitor-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ceed09c20 | ||
|
|
1712042dda | ||
|
|
3556f38469 | ||
|
|
62770033f2 | ||
|
|
e294dbbe93 | ||
|
|
47f0ab1ef3 | ||
|
|
c4d62fc4f6 | ||
|
|
69680b4238 | ||
|
|
0dc17e9cb3 | ||
|
|
0f44efaa82 | ||
|
|
05af7ce16a | ||
|
|
92447723d2 | ||
|
|
2a45f981e6 |
3
.github/workflows/windows.yaml
vendored
3
.github/workflows/windows.yaml
vendored
@@ -93,9 +93,10 @@ jobs:
|
|||||||
target/${{ matrix.target }}/release/komorebi.exe
|
target/${{ matrix.target }}/release/komorebi.exe
|
||||||
target/${{ matrix.target }}/release/komorebic.exe
|
target/${{ matrix.target }}/release/komorebic.exe
|
||||||
target/${{ matrix.target }}/release/komorebic-no-console.exe
|
target/${{ matrix.target }}/release/komorebic-no-console.exe
|
||||||
|
target/${{ matrix.target }}/release/komorebi-gui.exe
|
||||||
target/${{ matrix.target }}/release/komorebi.pdb
|
target/${{ matrix.target }}/release/komorebi.pdb
|
||||||
target/${{ matrix.target }}/release/komorebic.pdb
|
target/${{ matrix.target }}/release/komorebic.pdb
|
||||||
target/${{ matrix.target }}/release/komorebic-no-console.pdb
|
target/${{ matrix.target }}/release/komorebi-gui.pdb
|
||||||
target/wix/komorebi-*.msi
|
target/wix/komorebi-*.msi
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Check GoReleaser
|
- name: Check GoReleaser
|
||||||
|
|||||||
@@ -35,6 +35,15 @@ builds:
|
|||||||
post:
|
post:
|
||||||
- mkdir -p dist/windows_amd64
|
- mkdir -p dist/windows_amd64
|
||||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic-no-console.exe" ".\dist\komorebic-no-console_windows_amd64_v1\komorebic-no-console.exe"
|
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic-no-console.exe" ".\dist\komorebic-no-console_windows_amd64_v1\komorebic-no-console.exe"
|
||||||
|
- id: komorebi-gui
|
||||||
|
main: dummy.go
|
||||||
|
goos: [ "windows" ]
|
||||||
|
goarch: [ "amd64" ]
|
||||||
|
binary: komorebi-gui
|
||||||
|
hooks:
|
||||||
|
post:
|
||||||
|
- mkdir -p dist/windows_amd64
|
||||||
|
- cp ".\target\x86_64-pc-windows-msvc\release\komorebi-gui.exe" ".\dist\komorebi-gui_windows_amd64_v1\komorebi-gui.exe"
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
- name_template: "{{ .ProjectName }}-{{ .Version }}-x86_64-pc-windows-msvc"
|
- name_template: "{{ .ProjectName }}-{{ .Version }}-x86_64-pc-windows-msvc"
|
||||||
|
|||||||
3088
Cargo.lock
generated
3088
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ members = [
|
|||||||
"komorebi",
|
"komorebi",
|
||||||
"komorebi-client",
|
"komorebi-client",
|
||||||
"komorebi-core",
|
"komorebi-core",
|
||||||
|
"komorebi-gui",
|
||||||
"komorebic",
|
"komorebic",
|
||||||
"komorebic-no-console",
|
"komorebic-no-console",
|
||||||
]
|
]
|
||||||
|
|||||||
16
docs/cli/cycle-move-workspace-to-monitor.md
Normal file
16
docs/cli/cycle-move-workspace-to-monitor.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# cycle-move-workspace-to-monitor
|
||||||
|
|
||||||
|
```
|
||||||
|
Move the focused workspace monitor in the given cycle direction
|
||||||
|
|
||||||
|
Usage: komorebic.exe cycle-move-workspace-to-monitor <CYCLE_DIRECTION>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<CYCLE_DIRECTION>
|
||||||
|
[possible values: previous, next]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help
|
||||||
|
Print help
|
||||||
|
|
||||||
|
```
|
||||||
12
docs/cli/gui.md
Normal file
12
docs/cli/gui.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# gui
|
||||||
|
|
||||||
|
```
|
||||||
|
Launch the komorebi-gui debugging tool
|
||||||
|
|
||||||
|
Usage: komorebic.exe gui
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help
|
||||||
|
Print help
|
||||||
|
|
||||||
|
```
|
||||||
16
docs/cli/promote-window.md
Normal file
16
docs/cli/promote-window.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# promote-window
|
||||||
|
|
||||||
|
```
|
||||||
|
Promote the window in the specified direction
|
||||||
|
|
||||||
|
Usage: komorebic.exe promote-window <OPERATION_DIRECTION>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<OPERATION_DIRECTION>
|
||||||
|
[possible values: left, right, up, down]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help
|
||||||
|
Print help
|
||||||
|
|
||||||
|
```
|
||||||
@@ -114,6 +114,7 @@ Clone the git repository, enter the directory, and build the following binaries:
|
|||||||
cargo +stable install --path komorebi --locked
|
cargo +stable install --path komorebi --locked
|
||||||
cargo +stable install --path komorebic --locked
|
cargo +stable install --path komorebic --locked
|
||||||
cargo +stable install --path komorebic-no-console --locked
|
cargo +stable install --path komorebic-no-console --locked
|
||||||
|
cargo +stable install --path komorebi-gui --locked
|
||||||
```
|
```
|
||||||
|
|
||||||
If the binaries have been built and added to your `$PATH` correctly, you should
|
If the binaries have been built and added to your `$PATH` correctly, you should
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi-client"
|
name = "komorebi-client"
|
||||||
version = "0.1.26-dev.0"
|
version = "0.1.27-dev.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub use komorebi_core::Layout;
|
|||||||
pub use komorebi_core::OperationDirection;
|
pub use komorebi_core::OperationDirection;
|
||||||
pub use komorebi_core::Rect;
|
pub use komorebi_core::Rect;
|
||||||
pub use komorebi_core::SocketMessage;
|
pub use komorebi_core::SocketMessage;
|
||||||
|
pub use komorebi_core::StackbarLabel;
|
||||||
pub use komorebi_core::StackbarMode;
|
pub use komorebi_core::StackbarMode;
|
||||||
pub use komorebi_core::WindowKind;
|
pub use komorebi_core::WindowKind;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi-core"
|
name = "komorebi-core"
|
||||||
version = "0.1.26-dev.0"
|
version = "0.1.27-dev.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
14
komorebi-gui/Cargo.toml
Normal file
14
komorebi-gui/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "komorebi-gui"
|
||||||
|
version = "0.1.27-dev.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
egui_extras = { version = "0.27" }
|
||||||
|
eframe = "0.27"
|
||||||
|
komorebi-client = { path = "../komorebi-client" }
|
||||||
|
serde_json = "1"
|
||||||
|
random_word = { version = "0.4.3", features = ["en"] }
|
||||||
|
windows = { workspace = true }
|
||||||
805
komorebi-gui/src/main.rs
Normal file
805
komorebi-gui/src/main.rs
Normal file
@@ -0,0 +1,805 @@
|
|||||||
|
use eframe::egui;
|
||||||
|
use eframe::egui::color_picker::Alpha;
|
||||||
|
use eframe::egui::Color32;
|
||||||
|
use eframe::egui::ViewportBuilder;
|
||||||
|
use komorebi_client::BorderStyle;
|
||||||
|
use komorebi_client::Colour;
|
||||||
|
use komorebi_client::DefaultLayout;
|
||||||
|
use komorebi_client::GlobalState;
|
||||||
|
use komorebi_client::Layout;
|
||||||
|
use komorebi_client::Rect;
|
||||||
|
use komorebi_client::Rgb;
|
||||||
|
use komorebi_client::RuleDebug;
|
||||||
|
use komorebi_client::SocketMessage;
|
||||||
|
use komorebi_client::StackbarLabel;
|
||||||
|
use komorebi_client::StackbarMode;
|
||||||
|
use komorebi_client::State;
|
||||||
|
use komorebi_client::Window;
|
||||||
|
use komorebi_client::WindowKind;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let native_options = eframe::NativeOptions {
|
||||||
|
viewport: ViewportBuilder::default()
|
||||||
|
.with_always_on_top()
|
||||||
|
.with_inner_size([320.0, 500.0]),
|
||||||
|
follow_system_theme: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = eframe::run_native(
|
||||||
|
"komorebi-gui",
|
||||||
|
native_options,
|
||||||
|
Box::new(|cc| Box::new(KomorebiGui::new(cc))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BorderColours {
|
||||||
|
single: Color32,
|
||||||
|
stack: Color32,
|
||||||
|
monocle: Color32,
|
||||||
|
unfocused: Color32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BorderConfig {
|
||||||
|
border_enabled: bool,
|
||||||
|
border_colours: BorderColours,
|
||||||
|
border_style: BorderStyle,
|
||||||
|
border_offset: i32,
|
||||||
|
border_width: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StackbarConfig {
|
||||||
|
mode: StackbarMode,
|
||||||
|
label: StackbarLabel,
|
||||||
|
height: i32,
|
||||||
|
width: i32,
|
||||||
|
focused_text_colour: Color32,
|
||||||
|
unfocused_text_colour: Color32,
|
||||||
|
background_colour: Color32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonitorConfig {
|
||||||
|
size: Rect,
|
||||||
|
work_area_offset: Rect,
|
||||||
|
workspaces: Vec<WorkspaceConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&komorebi_client::Monitor> for MonitorConfig {
|
||||||
|
fn from(value: &komorebi_client::Monitor) -> Self {
|
||||||
|
let mut workspaces = vec![];
|
||||||
|
for ws in value.workspaces() {
|
||||||
|
workspaces.push(WorkspaceConfig::from(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
size: *value.size(),
|
||||||
|
work_area_offset: value.work_area_offset().unwrap_or_default(),
|
||||||
|
workspaces,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WorkspaceConfig {
|
||||||
|
name: String,
|
||||||
|
tile: bool,
|
||||||
|
layout: DefaultLayout,
|
||||||
|
container_padding: i32,
|
||||||
|
workspace_padding: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&komorebi_client::Workspace> for WorkspaceConfig {
|
||||||
|
fn from(value: &komorebi_client::Workspace) -> Self {
|
||||||
|
let layout = match value.layout() {
|
||||||
|
Layout::Default(layout) => *layout,
|
||||||
|
Layout::Custom(_) => DefaultLayout::BSP,
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = value
|
||||||
|
.name()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap_or_else(|| random_word::gen(random_word::Lang::En).to_string());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
layout,
|
||||||
|
name,
|
||||||
|
tile: *value.tile(),
|
||||||
|
workspace_padding: value.workspace_padding().unwrap_or(20),
|
||||||
|
container_padding: value.container_padding().unwrap_or(20),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KomorebiGui {
|
||||||
|
border_config: BorderConfig,
|
||||||
|
stackbar_config: StackbarConfig,
|
||||||
|
mouse_follows_focus: bool,
|
||||||
|
monitors: Vec<MonitorConfig>,
|
||||||
|
workspace_names: HashMap<usize, Vec<String>>,
|
||||||
|
debug_hwnd: isize,
|
||||||
|
debug_windows: Vec<Window>,
|
||||||
|
debug_rule: Option<RuleDebug>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn colour32(colour: Option<Colour>) -> Color32 {
|
||||||
|
match colour {
|
||||||
|
Some(Colour::Rgb(rgb)) => Color32::from_rgb(rgb.r as u8, rgb.g as u8, rgb.b as u8),
|
||||||
|
Some(Colour::Hex(hex)) => {
|
||||||
|
let rgb = Rgb::from(hex);
|
||||||
|
Color32::from_rgb(rgb.r as u8, rgb.g as u8, rgb.b as u8)
|
||||||
|
}
|
||||||
|
None => Color32::from_rgb(0, 0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KomorebiGui {
|
||||||
|
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
|
// 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 global_state: GlobalState = serde_json::from_str(
|
||||||
|
&komorebi_client::send_query(&SocketMessage::GlobalState).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let state: State =
|
||||||
|
serde_json::from_str(&komorebi_client::send_query(&SocketMessage::State).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let border_colours = BorderColours {
|
||||||
|
single: colour32(global_state.border_colours.single),
|
||||||
|
stack: colour32(global_state.border_colours.stack),
|
||||||
|
monocle: colour32(global_state.border_colours.monocle),
|
||||||
|
unfocused: colour32(global_state.border_colours.unfocused),
|
||||||
|
};
|
||||||
|
|
||||||
|
let border_config = BorderConfig {
|
||||||
|
border_enabled: global_state.border_enabled,
|
||||||
|
border_colours,
|
||||||
|
border_style: global_state.border_style,
|
||||||
|
border_offset: global_state.border_offset,
|
||||||
|
border_width: global_state.border_width,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut monitors = vec![];
|
||||||
|
for m in state.monitors.elements() {
|
||||||
|
monitors.push(MonitorConfig::from(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut workspace_names = HashMap::new();
|
||||||
|
|
||||||
|
for (monitor_idx, m) in monitors.iter().enumerate() {
|
||||||
|
for ws in &m.workspaces {
|
||||||
|
let names = workspace_names.entry(monitor_idx).or_insert_with(Vec::new);
|
||||||
|
names.push(ws.name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stackbar_config = StackbarConfig {
|
||||||
|
mode: global_state.stackbar_mode,
|
||||||
|
height: global_state.stackbar_height,
|
||||||
|
width: global_state.stackbar_tab_width,
|
||||||
|
label: global_state.stackbar_label,
|
||||||
|
focused_text_colour: colour32(Some(global_state.stackbar_focused_text_colour)),
|
||||||
|
unfocused_text_colour: colour32(Some(global_state.stackbar_unfocused_text_colour)),
|
||||||
|
background_colour: colour32(Some(global_state.stackbar_tab_background_colour)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut debug_windows = vec![];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
EnumWindows(
|
||||||
|
Some(enum_window),
|
||||||
|
windows::Win32::Foundation::LPARAM(&mut debug_windows as *mut Vec<Window> as isize),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
border_config,
|
||||||
|
mouse_follows_focus: state.mouse_follows_focus,
|
||||||
|
monitors,
|
||||||
|
workspace_names,
|
||||||
|
debug_hwnd: 0,
|
||||||
|
debug_windows,
|
||||||
|
stackbar_config,
|
||||||
|
debug_rule: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "system" fn enum_window(
|
||||||
|
hwnd: windows::Win32::Foundation::HWND,
|
||||||
|
lparam: windows::Win32::Foundation::LPARAM,
|
||||||
|
) -> windows::Win32::Foundation::BOOL {
|
||||||
|
let windows = unsafe { &mut *(lparam.0 as *mut Vec<Window>) };
|
||||||
|
let window = Window { hwnd: hwnd.0 };
|
||||||
|
|
||||||
|
if window.is_window()
|
||||||
|
&& !window.is_miminized()
|
||||||
|
&& window.is_visible()
|
||||||
|
&& window.title().is_ok()
|
||||||
|
&& window.exe().is_ok()
|
||||||
|
{
|
||||||
|
windows.push(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
true.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_view_ui(ui: &mut egui::Ui, code: &str) {
|
||||||
|
let language = "json";
|
||||||
|
let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx());
|
||||||
|
egui_extras::syntax_highlighting::code_view_ui(ui, &theme, code, language);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for KomorebiGui {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ctx.set_pixels_per_point(2.0);
|
||||||
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
ui.set_width(ctx.screen_rect().width());
|
||||||
|
ui.collapsing("Debugging", |ui| {
|
||||||
|
ui.collapsing("Window Rules", |ui| {
|
||||||
|
let window = Window {
|
||||||
|
hwnd: self.debug_hwnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
let label = if let (Ok(title), Ok(exe)) = (window.title(), window.exe()) {
|
||||||
|
format!("{title} ({exe})")
|
||||||
|
} else {
|
||||||
|
String::from("Select a Window")
|
||||||
|
};
|
||||||
|
|
||||||
|
if ui.button("Refresh Windows").clicked() {
|
||||||
|
let mut debug_windows = vec![];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
EnumWindows(
|
||||||
|
Some(enum_window),
|
||||||
|
windows::Win32::Foundation::LPARAM(
|
||||||
|
&mut debug_windows as *mut Vec<Window> as isize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.debug_windows = debug_windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
egui::ComboBox::from_label("Select a Window")
|
||||||
|
.selected_text(label)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for w in &self.debug_windows {
|
||||||
|
if ui
|
||||||
|
.selectable_value(
|
||||||
|
&mut self.debug_hwnd,
|
||||||
|
w.hwnd,
|
||||||
|
format!(
|
||||||
|
"{} ({})",
|
||||||
|
w.title().unwrap(),
|
||||||
|
w.exe().unwrap()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
let debug_rule: RuleDebug = serde_json::from_str(
|
||||||
|
&komorebi_client::send_query(
|
||||||
|
&SocketMessage::DebugWindow(self.debug_hwnd),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.debug_rule = Some(debug_rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(debug_rule) = &self.debug_rule {
|
||||||
|
json_view_ui(ui, &serde_json::to_string_pretty(debug_rule).unwrap())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Mouse", |ui| {
|
||||||
|
if ui
|
||||||
|
.toggle_value(&mut self.mouse_follows_focus, "Mouse Follows Focus")
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
|
||||||
|
self.mouse_follows_focus,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Border", |ui| {
|
||||||
|
if ui
|
||||||
|
.toggle_value(&mut self.border_config.border_enabled, "Border")
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::Border(
|
||||||
|
self.border_config.border_enabled,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.collapsing("Colours", |ui| {
|
||||||
|
ui.collapsing("Single", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.border_config.border_colours.single,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderColour(
|
||||||
|
WindowKind::Single,
|
||||||
|
self.border_config.border_colours.single.r() as u32,
|
||||||
|
self.border_config.border_colours.single.g() as u32,
|
||||||
|
self.border_config.border_colours.single.b() as u32,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Stack", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.border_config.border_colours.stack,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderColour(
|
||||||
|
WindowKind::Stack,
|
||||||
|
self.border_config.border_colours.stack.r() as u32,
|
||||||
|
self.border_config.border_colours.stack.g() as u32,
|
||||||
|
self.border_config.border_colours.stack.b() as u32,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Monocle", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.border_config.border_colours.monocle,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderColour(
|
||||||
|
WindowKind::Monocle,
|
||||||
|
self.border_config.border_colours.monocle.r() as u32,
|
||||||
|
self.border_config.border_colours.monocle.g() as u32,
|
||||||
|
self.border_config.border_colours.monocle.b() as u32,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Unfocused", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.border_config.border_colours.unfocused,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderColour(
|
||||||
|
WindowKind::Unfocused,
|
||||||
|
self.border_config.border_colours.unfocused.r() as u32,
|
||||||
|
self.border_config.border_colours.unfocused.g() as u32,
|
||||||
|
self.border_config.border_colours.unfocused.b() as u32,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Style", |ui| {
|
||||||
|
for option in [
|
||||||
|
BorderStyle::System,
|
||||||
|
BorderStyle::Rounded,
|
||||||
|
BorderStyle::Square,
|
||||||
|
] {
|
||||||
|
if ui
|
||||||
|
.add(egui::SelectableLabel::new(
|
||||||
|
self.border_config.border_style == option,
|
||||||
|
option.to_string(),
|
||||||
|
))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.border_config.border_style = option;
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderStyle(
|
||||||
|
self.border_config.border_style,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
|
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Width", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Slider::new(
|
||||||
|
&mut self.border_config.border_width,
|
||||||
|
-50..=50,
|
||||||
|
))
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderWidth(
|
||||||
|
self.border_config.border_width,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Offset", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Slider::new(
|
||||||
|
&mut self.border_config.border_offset,
|
||||||
|
-50..=50,
|
||||||
|
))
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::BorderOffset(
|
||||||
|
self.border_config.border_offset,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Stackbar", |ui| {
|
||||||
|
for option in [
|
||||||
|
StackbarMode::Never,
|
||||||
|
StackbarMode::OnStack,
|
||||||
|
StackbarMode::Always,
|
||||||
|
] {
|
||||||
|
if ui
|
||||||
|
.add(egui::SelectableLabel::new(
|
||||||
|
self.stackbar_config.mode == option,
|
||||||
|
option.to_string(),
|
||||||
|
))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.stackbar_config.mode = option;
|
||||||
|
komorebi_client::send_message(&SocketMessage::StackbarMode(
|
||||||
|
self.stackbar_config.mode,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
komorebi_client::send_message(&SocketMessage::Retile).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.collapsing("Label", |ui| {
|
||||||
|
for option in [StackbarLabel::Process, StackbarLabel::Title] {
|
||||||
|
if ui
|
||||||
|
.add(egui::SelectableLabel::new(
|
||||||
|
self.stackbar_config.label == option,
|
||||||
|
option.to_string(),
|
||||||
|
))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.stackbar_config.label = option;
|
||||||
|
komorebi_client::send_message(&SocketMessage::StackbarLabel(
|
||||||
|
self.stackbar_config.label,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Colours", |ui| {
|
||||||
|
ui.collapsing("Focused Text", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.stackbar_config.focused_text_colour,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::StackbarFocusedTextColour(
|
||||||
|
self.stackbar_config.focused_text_colour.r() as u32,
|
||||||
|
self.stackbar_config.focused_text_colour.g() as u32,
|
||||||
|
self.stackbar_config.focused_text_colour.b() as u32,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Unfocused Text", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.stackbar_config.unfocused_text_colour,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::StackbarUnfocusedTextColour(
|
||||||
|
self.stackbar_config.unfocused_text_colour.r() as u32,
|
||||||
|
self.stackbar_config.unfocused_text_colour.g() as u32,
|
||||||
|
self.stackbar_config.unfocused_text_colour.b() as u32,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Background", |ui| {
|
||||||
|
if egui::color_picker::color_picker_color32(
|
||||||
|
ui,
|
||||||
|
&mut self.stackbar_config.background_colour,
|
||||||
|
Alpha::Opaque,
|
||||||
|
) {
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::StackbarBackgroundColour(
|
||||||
|
self.stackbar_config.background_colour.r() as u32,
|
||||||
|
self.stackbar_config.background_colour.g() as u32,
|
||||||
|
self.stackbar_config.background_colour.b() as u32,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Width", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Slider::new(&mut self.stackbar_config.width, 0..=500))
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::StackbarTabWidth(
|
||||||
|
self.stackbar_config.width,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
komorebi_client::send_message(&SocketMessage::Retile).unwrap()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Height", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Slider::new(&mut self.stackbar_config.height, 0..=100))
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::StackbarHeight(
|
||||||
|
self.stackbar_config.height,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
komorebi_client::send_message(&SocketMessage::Retile).unwrap()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (monitor_idx, monitor) in self.monitors.iter_mut().enumerate() {
|
||||||
|
ui.collapsing(
|
||||||
|
format!(
|
||||||
|
"Monitor {monitor_idx} ({}x{})",
|
||||||
|
monitor.size.right, monitor.size.bottom
|
||||||
|
),
|
||||||
|
|ui| {
|
||||||
|
ui.collapsing("Work Area Offset", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
egui::Slider::new(
|
||||||
|
&mut monitor.work_area_offset.left,
|
||||||
|
0..=500,
|
||||||
|
)
|
||||||
|
.text("Left"),
|
||||||
|
)
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::MonitorWorkAreaOffset(
|
||||||
|
monitor_idx,
|
||||||
|
monitor.work_area_offset,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
egui::Slider::new(
|
||||||
|
&mut monitor.work_area_offset.top,
|
||||||
|
0..=500,
|
||||||
|
)
|
||||||
|
.text("Top"),
|
||||||
|
)
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::MonitorWorkAreaOffset(
|
||||||
|
monitor_idx,
|
||||||
|
monitor.work_area_offset,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
egui::Slider::new(
|
||||||
|
&mut monitor.work_area_offset.right,
|
||||||
|
0..=500,
|
||||||
|
)
|
||||||
|
.text("Right"),
|
||||||
|
)
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::MonitorWorkAreaOffset(
|
||||||
|
monitor_idx,
|
||||||
|
monitor.work_area_offset,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
egui::Slider::new(
|
||||||
|
&mut monitor.work_area_offset.bottom,
|
||||||
|
0..=500,
|
||||||
|
)
|
||||||
|
.text("Bottom"),
|
||||||
|
)
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::MonitorWorkAreaOffset(
|
||||||
|
monitor_idx,
|
||||||
|
monitor.work_area_offset,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Workspaces", |ui| {
|
||||||
|
for (workspace_idx, workspace) in
|
||||||
|
monitor.workspaces.iter_mut().enumerate()
|
||||||
|
{
|
||||||
|
ui.collapsing(
|
||||||
|
format!("Workspace {workspace_idx} ({})", workspace.name),
|
||||||
|
|ui| {
|
||||||
|
if ui.button("Focus").clicked() {
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::MouseFollowsFocus(false),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::FocusMonitorWorkspaceNumber(
|
||||||
|
monitor_idx,
|
||||||
|
workspace_idx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::MouseFollowsFocus(
|
||||||
|
self.mouse_follows_focus,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.toggle_value(&mut workspace.tile, "Tiling")
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::WorkspaceTiling(
|
||||||
|
monitor_idx,
|
||||||
|
workspace_idx,
|
||||||
|
workspace.tile,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.collapsing("Name", |ui| {
|
||||||
|
let monitor_workspaces = self
|
||||||
|
.workspace_names
|
||||||
|
.get_mut(&monitor_idx)
|
||||||
|
.unwrap();
|
||||||
|
let workspace_name =
|
||||||
|
&mut monitor_workspaces[workspace_idx];
|
||||||
|
if ui
|
||||||
|
.text_edit_singleline(workspace_name)
|
||||||
|
.lost_focus()
|
||||||
|
{
|
||||||
|
workspace.name = workspace_name.clone();
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::WorkspaceName(
|
||||||
|
monitor_idx,
|
||||||
|
workspace_idx,
|
||||||
|
workspace.name.clone(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Layout", |ui| {
|
||||||
|
for option in [
|
||||||
|
DefaultLayout::BSP,
|
||||||
|
DefaultLayout::Columns,
|
||||||
|
DefaultLayout::Rows,
|
||||||
|
DefaultLayout::VerticalStack,
|
||||||
|
DefaultLayout::HorizontalStack,
|
||||||
|
DefaultLayout::UltrawideVerticalStack,
|
||||||
|
DefaultLayout::Grid,
|
||||||
|
] {
|
||||||
|
if ui
|
||||||
|
.add(egui::SelectableLabel::new(
|
||||||
|
workspace.layout == option,
|
||||||
|
option.to_string(),
|
||||||
|
))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
workspace.layout = option;
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::WorkspaceLayout(
|
||||||
|
monitor_idx,
|
||||||
|
workspace_idx,
|
||||||
|
workspace.layout,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Container Padding", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Slider::new(
|
||||||
|
&mut workspace.container_padding,
|
||||||
|
0..=100,
|
||||||
|
))
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::ContainerPadding(
|
||||||
|
monitor_idx,
|
||||||
|
workspace_idx,
|
||||||
|
workspace.container_padding,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.collapsing("Workspace Padding", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(egui::Slider::new(
|
||||||
|
&mut workspace.workspace_padding,
|
||||||
|
0..=100,
|
||||||
|
))
|
||||||
|
.drag_stopped()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(
|
||||||
|
&SocketMessage::WorkspacePadding(
|
||||||
|
monitor_idx,
|
||||||
|
workspace_idx,
|
||||||
|
workspace.workspace_padding,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi"
|
name = "komorebi"
|
||||||
version = "0.1.26-dev.0"
|
version = "0.1.27-dev.0"
|
||||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||||
description = "A tiling window manager for Windows"
|
description = "A tiling window manager for Windows"
|
||||||
categories = ["tiling-window-manager", "windows"]
|
categories = ["tiling-window-manager", "windows"]
|
||||||
|
|||||||
@@ -7,20 +7,13 @@ use serde::Deserialize;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
use crate::stackbar::Stackbar;
|
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
use crate::WindowsApi;
|
|
||||||
use crate::STACKBAR_MODE;
|
|
||||||
use komorebi_core::StackbarMode;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
#[getset(get = "pub")]
|
#[getset(get = "pub")]
|
||||||
id: String,
|
id: String,
|
||||||
windows: Ring<Window>,
|
windows: Ring<Window>,
|
||||||
#[serde(skip)]
|
|
||||||
#[getset(get = "pub", get_mut = "pub")]
|
|
||||||
stackbar: Option<Stackbar>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ring_elements!(Container, Window);
|
impl_ring_elements!(Container, Window);
|
||||||
@@ -30,10 +23,6 @@ impl Default for Container {
|
|||||||
Self {
|
Self {
|
||||||
id: nanoid!(),
|
id: nanoid!(),
|
||||||
windows: Ring::default(),
|
windows: Ring::default(),
|
||||||
stackbar: match STACKBAR_MODE.load() {
|
|
||||||
StackbarMode::Always => Stackbar::create().ok(),
|
|
||||||
StackbarMode::Never | StackbarMode::OnStack => None,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,10 +35,6 @@ impl PartialEq for Container {
|
|||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
pub fn hide(&self, omit: Option<isize>) {
|
pub fn hide(&self, omit: Option<isize>) {
|
||||||
if let Some(stackbar) = self.stackbar() {
|
|
||||||
stackbar.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
for window in self.windows().iter().rev() {
|
for window in self.windows().iter().rev() {
|
||||||
let mut should_hide = omit.is_none();
|
let mut should_hide = omit.is_none();
|
||||||
|
|
||||||
@@ -68,10 +53,6 @@ impl Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&self) {
|
pub fn restore(&self) {
|
||||||
if let Some(stackbar) = self.stackbar() {
|
|
||||||
stackbar.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(window) = self.focused_window() {
|
if let Some(window) = self.focused_window() {
|
||||||
window.restore();
|
window.restore();
|
||||||
}
|
}
|
||||||
@@ -124,13 +105,6 @@ impl Container {
|
|||||||
pub fn remove_window_by_idx(&mut self, idx: usize) -> Option<Window> {
|
pub fn remove_window_by_idx(&mut self, idx: usize) -> Option<Window> {
|
||||||
let window = self.windows_mut().remove(idx);
|
let window = self.windows_mut().remove(idx);
|
||||||
|
|
||||||
if matches!(STACKBAR_MODE.load(), StackbarMode::OnStack) && self.windows().len() <= 1 {
|
|
||||||
if let Some(stackbar) = &self.stackbar {
|
|
||||||
let _ = WindowsApi::close_window(stackbar.hwnd());
|
|
||||||
self.stackbar = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
self.focus_window(idx - 1);
|
self.focus_window(idx - 1);
|
||||||
};
|
};
|
||||||
@@ -145,14 +119,6 @@ impl Container {
|
|||||||
|
|
||||||
pub fn add_window(&mut self, window: Window) {
|
pub fn add_window(&mut self, window: Window) {
|
||||||
self.windows_mut().push_back(window);
|
self.windows_mut().push_back(window);
|
||||||
|
|
||||||
if matches!(STACKBAR_MODE.load(), StackbarMode::OnStack)
|
|
||||||
&& self.windows().len() > 1
|
|
||||||
&& self.stackbar.is_none()
|
|
||||||
{
|
|
||||||
self.stackbar = Stackbar::create().ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.focus_window(self.windows().len() - 1);
|
self.focus_window(self.windows().len() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,41 +127,4 @@ impl Container {
|
|||||||
tracing::info!("focusing window");
|
tracing::info!("focusing window");
|
||||||
self.windows.focus(idx);
|
self.windows.focus(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_stackbar_mode(&mut self, mode: StackbarMode) {
|
|
||||||
match mode {
|
|
||||||
StackbarMode::Always => {
|
|
||||||
if self.stackbar.is_none() {
|
|
||||||
self.stackbar = Stackbar::create().ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StackbarMode::Never => {
|
|
||||||
if let Some(stackbar) = &self.stackbar {
|
|
||||||
let _ = WindowsApi::close_window(stackbar.hwnd());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stackbar = None
|
|
||||||
}
|
|
||||||
StackbarMode::OnStack => {
|
|
||||||
if self.windows().len() > 1 && self.stackbar().is_none() {
|
|
||||||
self.stackbar = Stackbar::create().ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(stackbar) = &self.stackbar {
|
|
||||||
if self.windows().len() == 1 {
|
|
||||||
let _ = WindowsApi::close_window(stackbar.hwnd());
|
|
||||||
self.stackbar = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn renew_stackbar(&mut self) {
|
|
||||||
if let Some(stackbar) = &self.stackbar {
|
|
||||||
if !WindowsApi::is_window(stackbar.hwnd()) {
|
|
||||||
self.stackbar = Stackbar::create().ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ pub mod monitor_reconciliator;
|
|||||||
pub mod process_command;
|
pub mod process_command;
|
||||||
pub mod process_event;
|
pub mod process_event;
|
||||||
pub mod process_movement;
|
pub mod process_movement;
|
||||||
|
pub mod reaper;
|
||||||
pub mod set_window_position;
|
pub mod set_window_position;
|
||||||
pub mod stackbar;
|
pub mod stackbar_manager;
|
||||||
pub mod static_config;
|
pub mod static_config;
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
@@ -40,7 +41,6 @@ use std::sync::Arc;
|
|||||||
pub use colour::*;
|
pub use colour::*;
|
||||||
pub use process_command::*;
|
pub use process_command::*;
|
||||||
pub use process_event::*;
|
pub use process_event::*;
|
||||||
pub use stackbar::*;
|
|
||||||
pub use static_config::*;
|
pub use static_config::*;
|
||||||
pub use window::*;
|
pub use window::*;
|
||||||
pub use window_manager::*;
|
pub use window_manager::*;
|
||||||
@@ -49,7 +49,6 @@ pub use windows_api::WindowsApi;
|
|||||||
pub use windows_api::*;
|
pub use windows_api::*;
|
||||||
|
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
|
||||||
use komorebi_core::config_generation::IdWithIdentifier;
|
use komorebi_core::config_generation::IdWithIdentifier;
|
||||||
use komorebi_core::config_generation::MatchingRule;
|
use komorebi_core::config_generation::MatchingRule;
|
||||||
use komorebi_core::config_generation::MatchingStrategy;
|
use komorebi_core::config_generation::MatchingStrategy;
|
||||||
@@ -57,8 +56,6 @@ use komorebi_core::ApplicationIdentifier;
|
|||||||
use komorebi_core::HidingBehaviour;
|
use komorebi_core::HidingBehaviour;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
use komorebi_core::SocketMessage;
|
use komorebi_core::SocketMessage;
|
||||||
use komorebi_core::StackbarLabel;
|
|
||||||
use komorebi_core::StackbarMode;
|
|
||||||
use os_info::Version;
|
use os_info::Version;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -215,14 +212,6 @@ pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
|||||||
|
|
||||||
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
|
|
||||||
pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947); // gray text
|
|
||||||
pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray
|
|
||||||
pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40);
|
|
||||||
pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
|
|
||||||
pub static STACKBAR_LABEL: AtomicCell<StackbarLabel> = AtomicCell::new(StackbarLabel::Process);
|
|
||||||
pub static STACKBAR_MODE: AtomicCell<StackbarMode> = AtomicCell::new(StackbarMode::Never);
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
||||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ use komorebi::process_command::listen_for_commands;
|
|||||||
use komorebi::process_command::listen_for_commands_tcp;
|
use komorebi::process_command::listen_for_commands_tcp;
|
||||||
use komorebi::process_event::listen_for_events;
|
use komorebi::process_event::listen_for_events;
|
||||||
use komorebi::process_movement::listen_for_movements;
|
use komorebi::process_movement::listen_for_movements;
|
||||||
|
use komorebi::reaper;
|
||||||
|
use komorebi::stackbar_manager;
|
||||||
use komorebi::static_config::StaticConfig;
|
use komorebi::static_config::StaticConfig;
|
||||||
use komorebi::window_manager::WindowManager;
|
use komorebi::window_manager::WindowManager;
|
||||||
use komorebi::windows_api::WindowsApi;
|
use komorebi::windows_api::WindowsApi;
|
||||||
@@ -254,8 +256,10 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
border_manager::listen_for_notifications(wm.clone());
|
border_manager::listen_for_notifications(wm.clone());
|
||||||
|
stackbar_manager::listen_for_notifications(wm.clone());
|
||||||
workspace_reconciliator::listen_for_notifications(wm.clone());
|
workspace_reconciliator::listen_for_notifications(wm.clone());
|
||||||
monitor_reconciliator::listen_for_notifications(wm.clone())?;
|
monitor_reconciliator::listen_for_notifications(wm.clone())?;
|
||||||
|
reaper::watch_for_orphans(wm.clone());
|
||||||
|
|
||||||
let (ctrlc_sender, ctrlc_receiver) = crossbeam_channel::bounded(1);
|
let (ctrlc_sender, ctrlc_receiver) = crossbeam_channel::bounded(1);
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ use crate::border_manager::STYLE;
|
|||||||
use crate::colour::Rgb;
|
use crate::colour::Rgb;
|
||||||
use crate::current_virtual_desktop;
|
use crate::current_virtual_desktop;
|
||||||
use crate::notify_subscribers;
|
use crate::notify_subscribers;
|
||||||
|
use crate::stackbar_manager;
|
||||||
use crate::static_config::StaticConfig;
|
use crate::static_config::StaticConfig;
|
||||||
use crate::window::RuleDebug;
|
use crate::window::RuleDebug;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
@@ -64,18 +65,18 @@ use crate::MONITOR_INDEX_PREFERENCES;
|
|||||||
use crate::NO_TITLEBAR;
|
use crate::NO_TITLEBAR;
|
||||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||||
use crate::REMOVE_TITLEBARS;
|
use crate::REMOVE_TITLEBARS;
|
||||||
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::STACKBAR_LABEL;
|
|
||||||
use crate::STACKBAR_MODE;
|
|
||||||
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
|
||||||
use crate::STACKBAR_TAB_HEIGHT;
|
|
||||||
use crate::STACKBAR_TAB_WIDTH;
|
|
||||||
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::SUBSCRIPTION_PIPES;
|
use crate::SUBSCRIPTION_PIPES;
|
||||||
use crate::SUBSCRIPTION_SOCKETS;
|
use crate::SUBSCRIPTION_SOCKETS;
|
||||||
use crate::TCP_CONNECTIONS;
|
use crate::TCP_CONNECTIONS;
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
use crate::WORKSPACE_RULES;
|
use crate::WORKSPACE_RULES;
|
||||||
|
use stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use stackbar_manager::STACKBAR_LABEL;
|
||||||
|
use stackbar_manager::STACKBAR_MODE;
|
||||||
|
use stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
|
use stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||||
|
use stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||||
|
use stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
|
pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
|
||||||
@@ -214,8 +215,18 @@ impl WindowManager {
|
|||||||
WindowsApi::center_cursor_in_rect(&focused_window_rect)?;
|
WindowsApi::center_cursor_in_rect(&focused_window_rect)?;
|
||||||
WindowsApi::left_click();
|
WindowsApi::left_click();
|
||||||
}
|
}
|
||||||
SocketMessage::Close => self.focused_window()?.close()?,
|
SocketMessage::Close => {
|
||||||
SocketMessage::Minimize => self.focused_window()?.minimize(),
|
Window {
|
||||||
|
hwnd: WindowsApi::foreground_window()?,
|
||||||
|
}
|
||||||
|
.close()?;
|
||||||
|
}
|
||||||
|
SocketMessage::Minimize => {
|
||||||
|
Window {
|
||||||
|
hwnd: WindowsApi::foreground_window()?,
|
||||||
|
}
|
||||||
|
.minimize();
|
||||||
|
}
|
||||||
SocketMessage::ToggleFloat => self.toggle_float()?,
|
SocketMessage::ToggleFloat => self.toggle_float()?,
|
||||||
SocketMessage::ToggleMonocle => self.toggle_monocle()?,
|
SocketMessage::ToggleMonocle => self.toggle_monocle()?,
|
||||||
SocketMessage::ToggleMaximize => self.toggle_maximize()?,
|
SocketMessage::ToggleMaximize => self.toggle_maximize()?,
|
||||||
@@ -1245,14 +1256,6 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
SocketMessage::StackbarMode(mode) => {
|
SocketMessage::StackbarMode(mode) => {
|
||||||
STACKBAR_MODE.store(mode);
|
STACKBAR_MODE.store(mode);
|
||||||
|
|
||||||
for m in self.monitors_mut() {
|
|
||||||
for w in m.workspaces_mut() {
|
|
||||||
for c in w.containers_mut() {
|
|
||||||
c.set_stackbar_mode(mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SocketMessage::StackbarLabel(label) => {
|
SocketMessage::StackbarLabel(label) => {
|
||||||
STACKBAR_LABEL.store(label);
|
STACKBAR_LABEL.store(label);
|
||||||
@@ -1342,6 +1345,7 @@ impl WindowManager {
|
|||||||
|
|
||||||
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
||||||
border_manager::event_tx().send(border_manager::Notification)?;
|
border_manager::event_tx().send(border_manager::Notification)?;
|
||||||
|
stackbar_manager::event_tx().send(stackbar_manager::Notification)?;
|
||||||
|
|
||||||
tracing::info!("processed");
|
tracing::info!("processed");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use parking_lot::Mutex;
|
|||||||
use komorebi_core::OperationDirection;
|
use komorebi_core::OperationDirection;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
use komorebi_core::Sizing;
|
use komorebi_core::Sizing;
|
||||||
use komorebi_core::StackbarLabel;
|
|
||||||
use komorebi_core::WindowContainerBehaviour;
|
use komorebi_core::WindowContainerBehaviour;
|
||||||
|
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
@@ -19,6 +18,7 @@ use crate::border_manager::BORDER_OFFSET;
|
|||||||
use crate::border_manager::BORDER_WIDTH;
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
use crate::current_virtual_desktop;
|
use crate::current_virtual_desktop;
|
||||||
use crate::notify_subscribers;
|
use crate::notify_subscribers;
|
||||||
|
use crate::stackbar_manager;
|
||||||
use crate::window::should_act;
|
use crate::window::should_act;
|
||||||
use crate::window::RuleDebug;
|
use crate::window::RuleDebug;
|
||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
@@ -33,7 +33,6 @@ use crate::NotificationEvent;
|
|||||||
use crate::DATA_DIR;
|
use crate::DATA_DIR;
|
||||||
use crate::HIDDEN_HWNDS;
|
use crate::HIDDEN_HWNDS;
|
||||||
use crate::REGEX_IDENTIFIERS;
|
use crate::REGEX_IDENTIFIERS;
|
||||||
use crate::STACKBAR_LABEL;
|
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
@@ -122,37 +121,11 @@ impl WindowManager {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.work_area_offset;
|
for monitor in self.monitors_mut() {
|
||||||
|
for workspace in monitor.workspaces_mut() {
|
||||||
for (i, monitor) in self.monitors_mut().iter_mut().enumerate() {
|
|
||||||
let work_area = *monitor.work_area_size();
|
|
||||||
let window_based_work_area_offset = (
|
|
||||||
monitor.window_based_work_area_offset_limit(),
|
|
||||||
monitor.window_based_work_area_offset(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let offset = if monitor.work_area_offset().is_some() {
|
|
||||||
monitor.work_area_offset()
|
|
||||||
} else {
|
|
||||||
offset
|
|
||||||
};
|
|
||||||
|
|
||||||
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
|
|
||||||
if let WindowManagerEvent::FocusChange(_, window) = event {
|
if let WindowManagerEvent::FocusChange(_, window) = event {
|
||||||
let _ = workspace.focus_changed(window.hwnd);
|
let _ = workspace.focus_changed(window.hwnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reaped_orphans = workspace.reap_orphans()?;
|
|
||||||
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
|
|
||||||
workspace.update(&work_area, offset, window_based_work_area_offset)?;
|
|
||||||
tracing::info!(
|
|
||||||
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
|
|
||||||
reaped_orphans.0,
|
|
||||||
reaped_orphans.1,
|
|
||||||
i,
|
|
||||||
j
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,23 +242,6 @@ impl WindowManager {
|
|||||||
WindowManagerEvent::Show(_, window)
|
WindowManagerEvent::Show(_, window)
|
||||||
| WindowManagerEvent::Manage(window)
|
| WindowManagerEvent::Manage(window)
|
||||||
| WindowManagerEvent::Uncloak(_, window) => {
|
| WindowManagerEvent::Uncloak(_, window) => {
|
||||||
if matches!(
|
|
||||||
event,
|
|
||||||
WindowManagerEvent::Show(WinEvent::ObjectNameChange, _)
|
|
||||||
) {
|
|
||||||
if matches!(STACKBAR_LABEL.load(), StackbarLabel::Title) {
|
|
||||||
for m in self.monitors() {
|
|
||||||
for ws in m.workspaces() {
|
|
||||||
if let Some(container) = ws.container_for_window(window.hwnd) {
|
|
||||||
if let Some(stackbar) = container.stackbar() {
|
|
||||||
stackbar.update(container.windows(), window.hwnd)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let focused_monitor_idx = self.focused_monitor_idx();
|
let focused_monitor_idx = self.focused_monitor_idx();
|
||||||
let focused_workspace_idx =
|
let focused_workspace_idx =
|
||||||
self.focused_workspace_idx_for_monitor_idx(focused_monitor_idx)?;
|
self.focused_workspace_idx_for_monitor_idx(focused_monitor_idx)?;
|
||||||
@@ -353,8 +309,10 @@ impl WindowManager {
|
|||||||
if proceed {
|
if proceed {
|
||||||
let behaviour = self.window_container_behaviour;
|
let behaviour = self.window_container_behaviour;
|
||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
|
let workspace_contains_window = workspace.contains_window(window.hwnd);
|
||||||
|
let monocle_container = workspace.monocle_container().clone();
|
||||||
|
|
||||||
if !workspace.contains_window(window.hwnd) && !needs_reconciliation {
|
if !workspace_contains_window && !needs_reconciliation {
|
||||||
match behaviour {
|
match behaviour {
|
||||||
WindowContainerBehaviour::Create => {
|
WindowContainerBehaviour::Create => {
|
||||||
workspace.new_container_for_window(window);
|
workspace.new_container_for_window(window);
|
||||||
@@ -369,6 +327,21 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if workspace_contains_window {
|
||||||
|
let mut monocle_window_event = false;
|
||||||
|
if let Some(ref monocle) = monocle_container {
|
||||||
|
if let Some(monocle_window) = monocle.focused_window() {
|
||||||
|
if monocle_window.hwnd == window.hwnd {
|
||||||
|
monocle_window_event = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !monocle_window_event && monocle_container.is_some() {
|
||||||
|
window.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowManagerEvent::MoveResizeStart(_, window) => {
|
WindowManagerEvent::MoveResizeStart(_, window) => {
|
||||||
@@ -461,10 +434,7 @@ impl WindowManager {
|
|||||||
|
|
||||||
// If we have moved across the monitors, use that override, otherwise determine
|
// If we have moved across the monitors, use that override, otherwise determine
|
||||||
// if a move has taken place by ruling out a resize
|
// if a move has taken place by ruling out a resize
|
||||||
let right_bottom_constant = ((BORDER_WIDTH.load(Ordering::SeqCst)
|
let right_bottom_constant = 0;
|
||||||
+ BORDER_OFFSET.load(Ordering::SeqCst))
|
|
||||||
* 2)
|
|
||||||
.abs();
|
|
||||||
|
|
||||||
let is_move = moved_across_monitors
|
let is_move = moved_across_monitors
|
||||||
|| resize.right.abs() == right_bottom_constant
|
|| resize.right.abs() == right_bottom_constant
|
||||||
@@ -601,10 +571,9 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowManagerEvent::ForceUpdate(_) => {
|
WindowManagerEvent::MouseCapture(..)
|
||||||
self.update_focused_workspace(false, true)?;
|
| WindowManagerEvent::Cloak(..)
|
||||||
}
|
| WindowManagerEvent::TitleUpdate(..) => {}
|
||||||
WindowManagerEvent::MouseCapture(..) | WindowManagerEvent::Cloak(..) => {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
|
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
|
||||||
@@ -640,8 +609,18 @@ impl WindowManager {
|
|||||||
|
|
||||||
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
notify_subscribers(&serde_json::to_string(¬ification)?)?;
|
||||||
border_manager::event_tx().send(border_manager::Notification)?;
|
border_manager::event_tx().send(border_manager::Notification)?;
|
||||||
|
stackbar_manager::event_tx().send(stackbar_manager::Notification)?;
|
||||||
|
|
||||||
|
// Too many spammy OBJECT_NAMECHANGE events from JetBrains IDEs
|
||||||
|
if !matches!(
|
||||||
|
event,
|
||||||
|
WindowManagerEvent::Show(WinEvent::ObjectNameChange, _)
|
||||||
|
) {
|
||||||
|
tracing::info!("processed: {}", event.window().to_string());
|
||||||
|
} else {
|
||||||
|
tracing::trace!("processed: {}", event.window().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
tracing::info!("processed: {}", event.window().to_string());
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
komorebi/src/reaper.rs
Normal file
66
komorebi/src/reaper.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
|
use crate::border_manager;
|
||||||
|
use crate::WindowManager;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub fn watch_for_orphans(wm: Arc<Mutex<WindowManager>>) {
|
||||||
|
std::thread::spawn(move || loop {
|
||||||
|
match find_orphans(wm.clone()) {
|
||||||
|
Ok(()) => {
|
||||||
|
tracing::warn!("restarting finished thread");
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
tracing::error!("restarting failed thread: {:?}", error)
|
||||||
|
} else {
|
||||||
|
tracing::error!("restarting failed thread: {}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_orphans(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||||
|
tracing::info!("watching");
|
||||||
|
|
||||||
|
let arc = wm.clone();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
|
|
||||||
|
let mut wm = arc.lock();
|
||||||
|
let offset = wm.work_area_offset;
|
||||||
|
|
||||||
|
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
||||||
|
let work_area = *monitor.work_area_size();
|
||||||
|
let window_based_work_area_offset = (
|
||||||
|
monitor.window_based_work_area_offset_limit(),
|
||||||
|
monitor.window_based_work_area_offset(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let offset = if monitor.work_area_offset().is_some() {
|
||||||
|
monitor.work_area_offset()
|
||||||
|
} else {
|
||||||
|
offset
|
||||||
|
};
|
||||||
|
|
||||||
|
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
|
||||||
|
let reaped_orphans = workspace.reap_orphans()?;
|
||||||
|
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
|
||||||
|
workspace.update(&work_area, offset, window_based_work_area_offset)?;
|
||||||
|
border_manager::event_tx().send(border_manager::Notification)?;
|
||||||
|
tracing::info!(
|
||||||
|
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
|
||||||
|
reaped_orphans.0,
|
||||||
|
reaped_orphans.1,
|
||||||
|
i,
|
||||||
|
j
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
use std::collections::VecDeque;
|
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use windows::core::PCWSTR;
|
|
||||||
use windows::Win32::Foundation::COLORREF;
|
|
||||||
use windows::Win32::Foundation::HWND;
|
|
||||||
use windows::Win32::Foundation::LPARAM;
|
|
||||||
use windows::Win32::Foundation::LRESULT;
|
|
||||||
use windows::Win32::Foundation::WPARAM;
|
|
||||||
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
|
|
||||||
use windows::Win32::Graphics::Gdi::CreatePen;
|
|
||||||
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
|
||||||
use windows::Win32::Graphics::Gdi::DrawTextW;
|
|
||||||
use windows::Win32::Graphics::Gdi::GetDC;
|
|
||||||
use windows::Win32::Graphics::Gdi::ReleaseDC;
|
|
||||||
use windows::Win32::Graphics::Gdi::SelectObject;
|
|
||||||
use windows::Win32::Graphics::Gdi::SetBkColor;
|
|
||||||
use windows::Win32::Graphics::Gdi::SetTextColor;
|
|
||||||
use windows::Win32::Graphics::Gdi::DT_CENTER;
|
|
||||||
use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS;
|
|
||||||
use windows::Win32::Graphics::Gdi::DT_SINGLELINE;
|
|
||||||
use windows::Win32::Graphics::Gdi::DT_VCENTER;
|
|
||||||
use windows::Win32::Graphics::Gdi::FONT_QUALITY;
|
|
||||||
use windows::Win32::Graphics::Gdi::FW_BOLD;
|
|
||||||
use windows::Win32::Graphics::Gdi::LOGFONTW;
|
|
||||||
use windows::Win32::Graphics::Gdi::PROOF_QUALITY;
|
|
||||||
use windows::Win32::Graphics::Gdi::PS_SOLID;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SW_SHOW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
|
|
||||||
|
|
||||||
use komorebi_core::Rect;
|
|
||||||
|
|
||||||
use crate::window::Window;
|
|
||||||
use crate::windows_api::WindowsApi;
|
|
||||||
use crate::StackbarLabel;
|
|
||||||
use crate::DEFAULT_CONTAINER_PADDING;
|
|
||||||
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::STACKBAR_LABEL;
|
|
||||||
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
|
||||||
use crate::STACKBAR_TAB_HEIGHT;
|
|
||||||
use crate::STACKBAR_TAB_WIDTH;
|
|
||||||
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::WINDOWS_BY_BAR_HWNDS;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
|
||||||
pub struct Stackbar {
|
|
||||||
pub hwnd: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stackbar {
|
|
||||||
unsafe extern "system" fn window_proc(
|
|
||||||
hwnd: HWND,
|
|
||||||
msg: u32,
|
|
||||||
w_param: WPARAM,
|
|
||||||
l_param: LPARAM,
|
|
||||||
) -> LRESULT {
|
|
||||||
match msg {
|
|
||||||
WM_LBUTTONDOWN => {
|
|
||||||
let win_hwnds_by_topbar = WINDOWS_BY_BAR_HWNDS.lock();
|
|
||||||
if let Some(win_hwnds) = win_hwnds_by_topbar.get(&hwnd.0) {
|
|
||||||
let x = l_param.0 as i32 & 0xFFFF;
|
|
||||||
let y = (l_param.0 as i32 >> 16) & 0xFFFF;
|
|
||||||
|
|
||||||
let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst);
|
|
||||||
let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
|
||||||
let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
for (index, win_hwnd) in win_hwnds.iter().enumerate() {
|
|
||||||
let left = gap + (index as i32 * (width + gap));
|
|
||||||
let right = left + width;
|
|
||||||
let top = 0;
|
|
||||||
let bottom = height;
|
|
||||||
|
|
||||||
if x >= left && x <= right && y >= top && y <= bottom {
|
|
||||||
let window = Window { hwnd: *win_hwnd };
|
|
||||||
window.restore();
|
|
||||||
if let Err(err) = window.focus(false) {
|
|
||||||
tracing::error!("Stackbar focus error: HWND:{} {}", *win_hwnd, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WINDOWS_BY_BAR_HWNDS.force_unlock();
|
|
||||||
LRESULT(0)
|
|
||||||
}
|
|
||||||
WM_DESTROY => {
|
|
||||||
PostQuitMessage(0);
|
|
||||||
LRESULT(0)
|
|
||||||
}
|
|
||||||
_ => DefWindowProcW(hwnd, msg, w_param, l_param),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn hwnd(&self) -> HWND {
|
|
||||||
HWND(self.hwnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create() -> Result<Stackbar> {
|
|
||||||
let name: Vec<u16> = "komorebi_stackbar\0".encode_utf16().collect();
|
|
||||||
let class_name = PCWSTR(name.as_ptr());
|
|
||||||
|
|
||||||
let h_module = WindowsApi::module_handle_w()?;
|
|
||||||
|
|
||||||
let wnd_class = WNDCLASSW {
|
|
||||||
style: CS_HREDRAW | CS_VREDRAW,
|
|
||||||
lpfnWndProc: Some(Self::window_proc),
|
|
||||||
hInstance: h_module.into(),
|
|
||||||
lpszClassName: class_name,
|
|
||||||
hbrBackground: WindowsApi::create_solid_brush(0),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
RegisterClassW(&wnd_class);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (hwnd_sender, hwnd_receiver) = crossbeam_channel::bounded::<HWND>(1);
|
|
||||||
|
|
||||||
let name_cl = name.clone();
|
|
||||||
std::thread::spawn(move || -> Result<()> {
|
|
||||||
unsafe {
|
|
||||||
let hwnd = CreateWindowExW(
|
|
||||||
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
|
|
||||||
PCWSTR(name_cl.as_ptr()),
|
|
||||||
PCWSTR(name_cl.as_ptr()),
|
|
||||||
WS_POPUP | WS_VISIBLE,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
h_module,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?;
|
|
||||||
hwnd_sender.send(hwnd)?;
|
|
||||||
|
|
||||||
let mut msg = MSG::default();
|
|
||||||
while GetMessageW(&mut msg, hwnd, 0, 0).into() {
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessageW(&msg);
|
|
||||||
std::thread::sleep(Duration::from_millis(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
hwnd: hwnd_receiver.recv()?.0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> {
|
|
||||||
WindowsApi::position_window(self.hwnd(), layout, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_position_from_container_layout(&self, layout: &Rect) -> Rect {
|
|
||||||
Rect {
|
|
||||||
bottom: STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst),
|
|
||||||
..*layout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, windows: &VecDeque<Window>, focused_hwnd: isize) -> Result<()> {
|
|
||||||
let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst);
|
|
||||||
let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
|
||||||
let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
|
||||||
let background = STACKBAR_TAB_BACKGROUND_COLOUR.load(Ordering::SeqCst);
|
|
||||||
let focused_text_colour = STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst);
|
|
||||||
let unfocused_text_colour = STACKBAR_UNFOCUSED_TEXT_COLOUR.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let hdc = GetDC(self.hwnd());
|
|
||||||
|
|
||||||
let hpen = CreatePen(PS_SOLID, 0, COLORREF(background));
|
|
||||||
let hbrush = CreateSolidBrush(COLORREF(background));
|
|
||||||
|
|
||||||
SelectObject(hdc, hpen);
|
|
||||||
SelectObject(hdc, hbrush);
|
|
||||||
SetBkColor(hdc, COLORREF(background));
|
|
||||||
|
|
||||||
let hfont = CreateFontIndirectW(&LOGFONTW {
|
|
||||||
lfWeight: FW_BOLD.0 as i32,
|
|
||||||
lfQuality: FONT_QUALITY(PROOF_QUALITY.0),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
SelectObject(hdc, hfont);
|
|
||||||
|
|
||||||
for (i, window) in windows.iter().enumerate() {
|
|
||||||
if window.hwnd == focused_hwnd {
|
|
||||||
SetTextColor(hdc, COLORREF(focused_text_colour));
|
|
||||||
} else {
|
|
||||||
SetTextColor(hdc, COLORREF(unfocused_text_colour));
|
|
||||||
}
|
|
||||||
|
|
||||||
let left = gap + (i as i32 * (width + gap));
|
|
||||||
let mut tab_box = Rect {
|
|
||||||
top: 0,
|
|
||||||
left,
|
|
||||||
right: left + width,
|
|
||||||
bottom: height,
|
|
||||||
};
|
|
||||||
|
|
||||||
WindowsApi::round_rect(hdc, &tab_box, 8);
|
|
||||||
|
|
||||||
let label = match STACKBAR_LABEL.load() {
|
|
||||||
StackbarLabel::Process => {
|
|
||||||
let exe = window.exe()?;
|
|
||||||
exe.trim_end_matches(".exe").to_string()
|
|
||||||
}
|
|
||||||
StackbarLabel::Title => window.title()?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tab_title: Vec<u16> = label.encode_utf16().collect();
|
|
||||||
|
|
||||||
tab_box.left_padding(10);
|
|
||||||
tab_box.right_padding(10);
|
|
||||||
|
|
||||||
DrawTextW(
|
|
||||||
hdc,
|
|
||||||
&mut tab_title,
|
|
||||||
&mut tab_box.into(),
|
|
||||||
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseDC(self.hwnd(), hdc);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut windows_hwdns: VecDeque<isize> = VecDeque::new();
|
|
||||||
for window in windows {
|
|
||||||
windows_hwdns.push_back(window.hwnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
WINDOWS_BY_BAR_HWNDS.lock().insert(self.hwnd, windows_hwdns);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hide(&self) {
|
|
||||||
WindowsApi::hide_window(self.hwnd())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn restore(&self) {
|
|
||||||
WindowsApi::show_window(self.hwnd(), SW_SHOW)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
210
komorebi/src/stackbar_manager/mod.rs
Normal file
210
komorebi/src/stackbar_manager/mod.rs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
mod stackbar;
|
||||||
|
|
||||||
|
use crate::container::Container;
|
||||||
|
use crate::stackbar_manager::stackbar::Stackbar;
|
||||||
|
use crate::WindowManager;
|
||||||
|
use crate::WindowsApi;
|
||||||
|
use crate::DEFAULT_CONTAINER_PADDING;
|
||||||
|
use crossbeam_channel::Receiver;
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
|
use komorebi_core::StackbarLabel;
|
||||||
|
use komorebi_core::StackbarMode;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::atomic::AtomicI32;
|
||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
use windows::Win32::Foundation::HWND;
|
||||||
|
|
||||||
|
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
|
||||||
|
pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947); // gray text
|
||||||
|
pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray
|
||||||
|
pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40);
|
||||||
|
pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
|
||||||
|
pub static STACKBAR_LABEL: AtomicCell<StackbarLabel> = AtomicCell::new(StackbarLabel::Process);
|
||||||
|
pub static STACKBAR_MODE: AtomicCell<StackbarMode> = AtomicCell::new(StackbarMode::OnStack);
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref STACKBAR_STATE: Mutex<HashMap<String, Stackbar>> = Mutex::new(HashMap::new());
|
||||||
|
static ref STACKBARS_MONITORS: Mutex<HashMap<String, usize>> = Mutex::new(HashMap::new());
|
||||||
|
static ref STACKBARS_CONTAINERS: Mutex<HashMap<isize, Container>> = Mutex::new(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Notification;
|
||||||
|
|
||||||
|
static CHANNEL: OnceLock<(Sender<Notification>, Receiver<Notification>)> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn channel() -> &'static (Sender<Notification>, Receiver<Notification>) {
|
||||||
|
CHANNEL.get_or_init(crossbeam_channel::unbounded)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_tx() -> Sender<Notification> {
|
||||||
|
channel().0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_rx() -> Receiver<Notification> {
|
||||||
|
channel().1.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_have_stackbar(window_count: usize) -> bool {
|
||||||
|
match STACKBAR_MODE.load() {
|
||||||
|
StackbarMode::Always => true,
|
||||||
|
StackbarMode::OnStack => window_count > 1,
|
||||||
|
StackbarMode::Never => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||||
|
std::thread::spawn(move || loop {
|
||||||
|
match handle_notifications(wm.clone()) {
|
||||||
|
Ok(()) => {
|
||||||
|
tracing::warn!("restarting finished thread");
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!("restarting failed thread: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||||
|
tracing::info!("listening");
|
||||||
|
|
||||||
|
let receiver = event_rx();
|
||||||
|
|
||||||
|
'receiver: for _ in receiver {
|
||||||
|
let mut stackbars = STACKBAR_STATE.lock();
|
||||||
|
let mut stackbars_monitors = STACKBARS_MONITORS.lock();
|
||||||
|
|
||||||
|
// Check the wm state every time we receive a notification
|
||||||
|
let mut state = wm.lock();
|
||||||
|
|
||||||
|
// If stackbars are disabled
|
||||||
|
if matches!(STACKBAR_MODE.load(), StackbarMode::Never) {
|
||||||
|
for (_, stackbar) in stackbars.iter() {
|
||||||
|
stackbar.destroy()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
stackbars.clear();
|
||||||
|
continue 'receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (monitor_idx, m) in state.monitors_mut().iter_mut().enumerate() {
|
||||||
|
// Only operate on the focused workspace of each monitor
|
||||||
|
if let Some(ws) = m.focused_workspace_mut() {
|
||||||
|
// Workspaces with tiling disabled don't have stackbars
|
||||||
|
if !ws.tile() {
|
||||||
|
let mut to_remove = vec![];
|
||||||
|
for (id, border) in stackbars.iter() {
|
||||||
|
if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx {
|
||||||
|
border.destroy()?;
|
||||||
|
to_remove.push(id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in &to_remove {
|
||||||
|
stackbars.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue 'receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_maximized = WindowsApi::is_zoomed(HWND(
|
||||||
|
WindowsApi::foreground_window().unwrap_or_default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Handle the monocle container separately
|
||||||
|
if ws.monocle_container().is_some() || is_maximized {
|
||||||
|
// Destroy any stackbars associated with the focused workspace
|
||||||
|
let mut to_remove = vec![];
|
||||||
|
for (id, stackbar) in stackbars.iter() {
|
||||||
|
if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx {
|
||||||
|
stackbar.destroy()?;
|
||||||
|
to_remove.push(id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in &to_remove {
|
||||||
|
stackbars.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue 'receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy any stackbars not associated with the focused workspace
|
||||||
|
let container_ids = ws
|
||||||
|
.containers()
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.id().clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut to_remove = vec![];
|
||||||
|
for (id, stackbar) in stackbars.iter() {
|
||||||
|
if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx
|
||||||
|
&& !container_ids.contains(id)
|
||||||
|
{
|
||||||
|
stackbar.destroy()?;
|
||||||
|
to_remove.push(id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in &to_remove {
|
||||||
|
stackbars.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let container_padding = ws
|
||||||
|
.container_padding()
|
||||||
|
.unwrap_or_else(|| DEFAULT_CONTAINER_PADDING.load_consume());
|
||||||
|
|
||||||
|
'containers: for container in ws.containers_mut() {
|
||||||
|
let should_add_stackbar = match STACKBAR_MODE.load() {
|
||||||
|
StackbarMode::Always => true,
|
||||||
|
StackbarMode::OnStack => container.windows().len() > 1,
|
||||||
|
StackbarMode::Never => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !should_add_stackbar {
|
||||||
|
if let Some(stackbar) = stackbars.get(container.id()) {
|
||||||
|
stackbar.destroy()?
|
||||||
|
}
|
||||||
|
|
||||||
|
stackbars.remove(container.id());
|
||||||
|
stackbars_monitors.remove(container.id());
|
||||||
|
continue 'containers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the stackbar entry for this container from the map or create one
|
||||||
|
let stackbar = match stackbars.entry(container.id().clone()) {
|
||||||
|
Entry::Occupied(entry) => entry.into_mut(),
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
if let Ok(stackbar) = Stackbar::create(container.id()) {
|
||||||
|
entry.insert(stackbar)
|
||||||
|
} else {
|
||||||
|
continue 'receiver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stackbars_monitors.insert(container.id().clone(), monitor_idx);
|
||||||
|
|
||||||
|
let rect = WindowsApi::window_rect(
|
||||||
|
container
|
||||||
|
.focused_window()
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.hwnd(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
stackbar.update(container_padding, container, &rect)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
328
komorebi/src/stackbar_manager/stackbar.rs
Normal file
328
komorebi/src/stackbar_manager/stackbar.rs
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
use crate::border_manager::BORDER_OFFSET;
|
||||||
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
|
use crate::border_manager::STYLE;
|
||||||
|
use crate::container::Container;
|
||||||
|
use crate::stackbar_manager::STACKBARS_CONTAINERS;
|
||||||
|
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_LABEL;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||||
|
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::WindowsApi;
|
||||||
|
use crate::DEFAULT_CONTAINER_PADDING;
|
||||||
|
use crate::WINDOWS_11;
|
||||||
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
|
use komorebi_core::BorderStyle;
|
||||||
|
use komorebi_core::Rect;
|
||||||
|
use komorebi_core::StackbarLabel;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::time::Duration;
|
||||||
|
use windows::core::PCWSTR;
|
||||||
|
use windows::Win32::Foundation::COLORREF;
|
||||||
|
use windows::Win32::Foundation::HWND;
|
||||||
|
use windows::Win32::Foundation::LPARAM;
|
||||||
|
use windows::Win32::Foundation::LRESULT;
|
||||||
|
use windows::Win32::Foundation::WPARAM;
|
||||||
|
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
|
||||||
|
use windows::Win32::Graphics::Gdi::CreatePen;
|
||||||
|
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||||
|
use windows::Win32::Graphics::Gdi::DrawTextW;
|
||||||
|
use windows::Win32::Graphics::Gdi::GetDC;
|
||||||
|
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||||
|
use windows::Win32::Graphics::Gdi::ReleaseDC;
|
||||||
|
use windows::Win32::Graphics::Gdi::RoundRect;
|
||||||
|
use windows::Win32::Graphics::Gdi::SelectObject;
|
||||||
|
use windows::Win32::Graphics::Gdi::SetBkColor;
|
||||||
|
use windows::Win32::Graphics::Gdi::SetTextColor;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_CENTER;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_SINGLELINE;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_VCENTER;
|
||||||
|
use windows::Win32::Graphics::Gdi::FONT_QUALITY;
|
||||||
|
use windows::Win32::Graphics::Gdi::FW_BOLD;
|
||||||
|
use windows::Win32::Graphics::Gdi::LOGFONTW;
|
||||||
|
use windows::Win32::Graphics::Gdi::PROOF_QUALITY;
|
||||||
|
use windows::Win32::Graphics::Gdi::PS_SOLID;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Stackbar {
|
||||||
|
pub hwnd: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<isize> for Stackbar {
|
||||||
|
fn from(value: isize) -> Self {
|
||||||
|
Self { hwnd: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stackbar {
|
||||||
|
pub const fn hwnd(&self) -> HWND {
|
||||||
|
HWND(self.hwnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(id: &str) -> color_eyre::Result<Self> {
|
||||||
|
let name: Vec<u16> = format!("komostackbar-{id}\0").encode_utf16().collect();
|
||||||
|
let class_name = PCWSTR(name.as_ptr());
|
||||||
|
|
||||||
|
let h_module = WindowsApi::module_handle_w()?;
|
||||||
|
|
||||||
|
let window_class = WNDCLASSW {
|
||||||
|
style: CS_HREDRAW | CS_VREDRAW,
|
||||||
|
lpfnWndProc: Some(Self::callback),
|
||||||
|
hInstance: h_module.into(),
|
||||||
|
lpszClassName: class_name,
|
||||||
|
hbrBackground: WindowsApi::create_solid_brush(0),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = WindowsApi::register_class_w(&window_class);
|
||||||
|
|
||||||
|
let (hwnd_sender, hwnd_receiver) = mpsc::channel();
|
||||||
|
|
||||||
|
let name_cl = name.clone();
|
||||||
|
std::thread::spawn(move || -> color_eyre::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let hwnd = CreateWindowExW(
|
||||||
|
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
|
||||||
|
PCWSTR(name_cl.as_ptr()),
|
||||||
|
PCWSTR(name_cl.as_ptr()),
|
||||||
|
WS_POPUP | WS_VISIBLE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
h_module,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?;
|
||||||
|
hwnd_sender.send(hwnd)?;
|
||||||
|
|
||||||
|
let mut msg = MSG::default();
|
||||||
|
while GetMessageW(&mut msg, hwnd, 0, 0).into() {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
std::thread::sleep(Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
hwnd: hwnd_receiver.recv()?.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&self) -> color_eyre::Result<()> {
|
||||||
|
WindowsApi::close_window(self.hwnd())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&self,
|
||||||
|
container_padding: i32,
|
||||||
|
container: &mut Container,
|
||||||
|
layout: &Rect,
|
||||||
|
) -> color_eyre::Result<()> {
|
||||||
|
let width = STACKBAR_TAB_WIDTH.load_consume();
|
||||||
|
let height = STACKBAR_TAB_HEIGHT.load_consume();
|
||||||
|
let gap = DEFAULT_CONTAINER_PADDING.load_consume();
|
||||||
|
let background = STACKBAR_TAB_BACKGROUND_COLOUR.load_consume();
|
||||||
|
let focused_text_colour = STACKBAR_FOCUSED_TEXT_COLOUR.load_consume();
|
||||||
|
let unfocused_text_colour = STACKBAR_UNFOCUSED_TEXT_COLOUR.load_consume();
|
||||||
|
|
||||||
|
let mut stackbars_containers = STACKBARS_CONTAINERS.lock();
|
||||||
|
stackbars_containers.insert(self.hwnd, container.clone());
|
||||||
|
|
||||||
|
let mut layout = *layout;
|
||||||
|
let workspace_specific_offset =
|
||||||
|
BORDER_WIDTH.load_consume() + BORDER_OFFSET.load_consume() + container_padding;
|
||||||
|
|
||||||
|
layout.top -= workspace_specific_offset + STACKBAR_TAB_HEIGHT.load_consume();
|
||||||
|
layout.left -= workspace_specific_offset;
|
||||||
|
|
||||||
|
WindowsApi::position_window(self.hwnd(), &layout, false)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let hdc = GetDC(self.hwnd());
|
||||||
|
|
||||||
|
let hpen = CreatePen(PS_SOLID, 0, COLORREF(background));
|
||||||
|
let hbrush = CreateSolidBrush(COLORREF(background));
|
||||||
|
|
||||||
|
SelectObject(hdc, hpen);
|
||||||
|
SelectObject(hdc, hbrush);
|
||||||
|
SetBkColor(hdc, COLORREF(background));
|
||||||
|
|
||||||
|
let hfont = CreateFontIndirectW(&LOGFONTW {
|
||||||
|
lfWeight: FW_BOLD.0 as i32,
|
||||||
|
lfQuality: FONT_QUALITY(PROOF_QUALITY.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectObject(hdc, hfont);
|
||||||
|
|
||||||
|
for (i, window) in container.windows().iter().enumerate() {
|
||||||
|
if window.hwnd == container.focused_window().copied().unwrap_or_default().hwnd {
|
||||||
|
SetTextColor(hdc, COLORREF(focused_text_colour));
|
||||||
|
} else {
|
||||||
|
SetTextColor(hdc, COLORREF(unfocused_text_colour));
|
||||||
|
}
|
||||||
|
|
||||||
|
let left = gap + (i as i32 * (width + gap));
|
||||||
|
let mut rect = Rect {
|
||||||
|
top: 0,
|
||||||
|
left,
|
||||||
|
right: left + width,
|
||||||
|
bottom: height,
|
||||||
|
};
|
||||||
|
|
||||||
|
match *STYLE.lock() {
|
||||||
|
BorderStyle::System => {
|
||||||
|
if *WINDOWS_11 {
|
||||||
|
RoundRect(hdc, rect.left, rect.top, rect.right, rect.bottom, 20, 20);
|
||||||
|
} else {
|
||||||
|
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BorderStyle::Rounded => {
|
||||||
|
RoundRect(hdc, rect.left, rect.top, rect.right, rect.bottom, 20, 20);
|
||||||
|
}
|
||||||
|
BorderStyle::Square => {
|
||||||
|
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let label = match STACKBAR_LABEL.load() {
|
||||||
|
StackbarLabel::Process => {
|
||||||
|
let exe = window.exe()?;
|
||||||
|
exe.trim_end_matches(".exe").to_string()
|
||||||
|
}
|
||||||
|
StackbarLabel::Title => window.title()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tab_title: Vec<u16> = label.encode_utf16().collect();
|
||||||
|
|
||||||
|
rect.left_padding(10);
|
||||||
|
rect.right_padding(10);
|
||||||
|
|
||||||
|
DrawTextW(
|
||||||
|
hdc,
|
||||||
|
&mut tab_title,
|
||||||
|
&mut rect.into(),
|
||||||
|
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseDC(self.hwnd(), hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_position_from_container_layout(&self, layout: &Rect) -> Rect {
|
||||||
|
Rect {
|
||||||
|
bottom: STACKBAR_TAB_HEIGHT.load_consume(),
|
||||||
|
..*layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn callback(
|
||||||
|
hwnd: HWND,
|
||||||
|
msg: u32,
|
||||||
|
w_param: WPARAM,
|
||||||
|
l_param: LPARAM,
|
||||||
|
) -> LRESULT {
|
||||||
|
unsafe {
|
||||||
|
match msg {
|
||||||
|
WM_LBUTTONDOWN => {
|
||||||
|
let stackbars_containers = STACKBARS_CONTAINERS.lock();
|
||||||
|
if let Some(container) = stackbars_containers.get(&hwnd.0) {
|
||||||
|
let x = l_param.0 as i32 & 0xFFFF;
|
||||||
|
let y = (l_param.0 as i32 >> 16) & 0xFFFF;
|
||||||
|
|
||||||
|
let width = STACKBAR_TAB_WIDTH.load_consume();
|
||||||
|
let height = STACKBAR_TAB_HEIGHT.load_consume();
|
||||||
|
let gap = DEFAULT_CONTAINER_PADDING.load_consume();
|
||||||
|
|
||||||
|
let focused_window_idx = container.focused_window_idx();
|
||||||
|
let focused_window_rect = WindowsApi::window_rect(
|
||||||
|
container
|
||||||
|
.focused_window()
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.hwnd(),
|
||||||
|
)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
for (index, window) in container.windows().iter().enumerate() {
|
||||||
|
let left = gap + (index as i32 * (width + gap));
|
||||||
|
let right = left + width;
|
||||||
|
let top = 0;
|
||||||
|
let bottom = height;
|
||||||
|
|
||||||
|
if x >= left && x <= right && y >= top && y <= bottom {
|
||||||
|
// If we are focusing a window that isn't currently focused in the
|
||||||
|
// stackbar, make sure we update its location so that it doesn't render
|
||||||
|
// on top of other tiles before eventually ending up in the correct
|
||||||
|
// tile
|
||||||
|
if index != focused_window_idx {
|
||||||
|
if let Err(err) =
|
||||||
|
window.set_position(&focused_window_rect, false)
|
||||||
|
{
|
||||||
|
tracing::error!(
|
||||||
|
"stackbar WM_LBUTTONDOWN repositioning error: hwnd {} ({})",
|
||||||
|
*window,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the window corresponding to the tab we have clicked
|
||||||
|
window.restore();
|
||||||
|
if let Err(err) = window.focus(false) {
|
||||||
|
tracing::error!(
|
||||||
|
"stackbar WMLBUTTONDOWN focus error: hwnd {} ({})",
|
||||||
|
*window,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Hide any windows in the stack that don't correspond to the window
|
||||||
|
// we have clicked
|
||||||
|
window.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
WM_DESTROY => {
|
||||||
|
PostQuitMessage(0);
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
_ => DefWindowProcW(hwnd, msg, w_param, l_param),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,13 @@ use crate::current_virtual_desktop;
|
|||||||
use crate::monitor::Monitor;
|
use crate::monitor::Monitor;
|
||||||
use crate::monitor_reconciliator;
|
use crate::monitor_reconciliator;
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
|
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_LABEL;
|
||||||
|
use crate::stackbar_manager::STACKBAR_MODE;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||||
|
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
@@ -22,13 +29,6 @@ use crate::MANAGE_IDENTIFIERS;
|
|||||||
use crate::MONITOR_INDEX_PREFERENCES;
|
use crate::MONITOR_INDEX_PREFERENCES;
|
||||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||||
use crate::REGEX_IDENTIFIERS;
|
use crate::REGEX_IDENTIFIERS;
|
||||||
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::STACKBAR_LABEL;
|
|
||||||
use crate::STACKBAR_MODE;
|
|
||||||
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
|
||||||
use crate::STACKBAR_TAB_HEIGHT;
|
|
||||||
use crate::STACKBAR_TAB_WIDTH;
|
|
||||||
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
use crate::WORKSPACE_RULES;
|
use crate::WORKSPACE_RULES;
|
||||||
use komorebi_core::StackbarLabel;
|
use komorebi_core::StackbarLabel;
|
||||||
@@ -229,7 +229,7 @@ impl From<&Monitor> for MonitorConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
/// The `komorebi.json` static configuration file reference for `v0.1.25`
|
/// The `komorebi.json` static configuration file reference for `v0.1.26`
|
||||||
pub struct StaticConfig {
|
pub struct StaticConfig {
|
||||||
/// DEPRECATED from v0.1.22: no longer required
|
/// DEPRECATED from v0.1.22: no longer required
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -322,6 +322,48 @@ pub struct StaticConfig {
|
|||||||
pub stackbar: Option<StackbarConfig>,
|
pub stackbar: Option<StackbarConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StaticConfig {
|
||||||
|
pub fn aliases(raw: &str) {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("border", ["active_window_border"]);
|
||||||
|
map.insert("border_width", ["active_window_border_width"]);
|
||||||
|
map.insert("border_offset", ["active_window_border_offset"]);
|
||||||
|
map.insert("border_colours", ["active_window_border_colours"]);
|
||||||
|
map.insert("border_style", ["active_window_border_style"]);
|
||||||
|
|
||||||
|
let mut display = false;
|
||||||
|
|
||||||
|
for (_, aliases) in &map {
|
||||||
|
for a in aliases {
|
||||||
|
if raw.contains(a) {
|
||||||
|
display = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if display {
|
||||||
|
println!("\nYour configuration file contains some options that have been renamed or deprecated:\n");
|
||||||
|
for (canonical, aliases) in map {
|
||||||
|
for alias in aliases {
|
||||||
|
if raw.contains(alias) {
|
||||||
|
println!(r#""{alias}" is now "{canonical}""#);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deprecated(raw: &str) {
|
||||||
|
let deprecated = ["invisible_borders"];
|
||||||
|
|
||||||
|
for option in deprecated {
|
||||||
|
if raw.contains(option) {
|
||||||
|
println!(r#""{option}" is deprecated and can be removed"#);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct TabsConfig {
|
pub struct TabsConfig {
|
||||||
/// Width of a stackbar tab
|
/// Width of a stackbar tab
|
||||||
@@ -760,16 +802,6 @@ impl StaticConfig {
|
|||||||
|
|
||||||
value.apply_globals()?;
|
value.apply_globals()?;
|
||||||
|
|
||||||
let stackbar_mode = STACKBAR_MODE.load();
|
|
||||||
|
|
||||||
for m in wm.monitors_mut() {
|
|
||||||
for w in m.workspaces_mut() {
|
|
||||||
for c in w.containers_mut() {
|
|
||||||
c.set_stackbar_mode(stackbar_mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(monitors) = value.monitors {
|
if let Some(monitors) = value.monitors {
|
||||||
for (i, monitor) in monitors.iter().enumerate() {
|
for (i, monitor) in monitors.iter().enumerate() {
|
||||||
if let Some(m) = wm.monitors_mut().get_mut(i) {
|
if let Some(m) = wm.monitors_mut().get_mut(i) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ use komorebi_core::OperationBehaviour;
|
|||||||
use komorebi_core::OperationDirection;
|
use komorebi_core::OperationDirection;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
use komorebi_core::Sizing;
|
use komorebi_core::Sizing;
|
||||||
|
use komorebi_core::StackbarLabel;
|
||||||
use komorebi_core::WindowContainerBehaviour;
|
use komorebi_core::WindowContainerBehaviour;
|
||||||
|
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
@@ -46,6 +47,13 @@ use crate::current_virtual_desktop;
|
|||||||
use crate::load_configuration;
|
use crate::load_configuration;
|
||||||
use crate::monitor::Monitor;
|
use crate::monitor::Monitor;
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
|
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_LABEL;
|
||||||
|
use crate::stackbar_manager::STACKBAR_MODE;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||||
|
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
use crate::static_config::StaticConfig;
|
use crate::static_config::StaticConfig;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
@@ -68,12 +76,6 @@ use crate::MONITOR_INDEX_PREFERENCES;
|
|||||||
use crate::NO_TITLEBAR;
|
use crate::NO_TITLEBAR;
|
||||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||||
use crate::REMOVE_TITLEBARS;
|
use crate::REMOVE_TITLEBARS;
|
||||||
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::STACKBAR_MODE;
|
|
||||||
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
|
||||||
use crate::STACKBAR_TAB_HEIGHT;
|
|
||||||
use crate::STACKBAR_TAB_WIDTH;
|
|
||||||
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
use crate::WORKSPACE_RULES;
|
use crate::WORKSPACE_RULES;
|
||||||
use komorebi_core::StackbarMode;
|
use komorebi_core::StackbarMode;
|
||||||
@@ -122,6 +124,7 @@ pub struct GlobalState {
|
|||||||
pub border_offset: i32,
|
pub border_offset: i32,
|
||||||
pub border_width: i32,
|
pub border_width: i32,
|
||||||
pub stackbar_mode: StackbarMode,
|
pub stackbar_mode: StackbarMode,
|
||||||
|
pub stackbar_label: StackbarLabel,
|
||||||
pub stackbar_focused_text_colour: Colour,
|
pub stackbar_focused_text_colour: Colour,
|
||||||
pub stackbar_unfocused_text_colour: Colour,
|
pub stackbar_unfocused_text_colour: Colour,
|
||||||
pub stackbar_tab_background_colour: Colour,
|
pub stackbar_tab_background_colour: Colour,
|
||||||
@@ -164,6 +167,7 @@ impl Default for GlobalState {
|
|||||||
border_offset: border_manager::BORDER_OFFSET.load(Ordering::SeqCst),
|
border_offset: border_manager::BORDER_OFFSET.load(Ordering::SeqCst),
|
||||||
border_width: border_manager::BORDER_WIDTH.load(Ordering::SeqCst),
|
border_width: border_manager::BORDER_WIDTH.load(Ordering::SeqCst),
|
||||||
stackbar_mode: STACKBAR_MODE.load(),
|
stackbar_mode: STACKBAR_MODE.load(),
|
||||||
|
stackbar_label: STACKBAR_LABEL.load(),
|
||||||
stackbar_focused_text_colour: Colour::Rgb(Rgb::from(
|
stackbar_focused_text_colour: Colour::Rgb(Rgb::from(
|
||||||
STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst),
|
STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst),
|
||||||
)),
|
)),
|
||||||
@@ -1129,9 +1133,12 @@ impl WindowManager {
|
|||||||
|
|
||||||
tracing::info!("focusing container");
|
tracing::info!("focusing container");
|
||||||
|
|
||||||
let new_idx = workspace.new_idx_for_direction(direction);
|
let new_idx = if workspace.monocle_container().is_some() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
workspace.new_idx_for_direction(direction)
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: clean this up, this is awful
|
|
||||||
let mut cross_monitor_monocle = false;
|
let mut cross_monitor_monocle = false;
|
||||||
|
|
||||||
// if there is no container in that direction for this workspace
|
// if there is no container in that direction for this workspace
|
||||||
@@ -1162,22 +1169,9 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When switching workspaces and landing focus on a window that is not stack, but a stack
|
|
||||||
// exists, and there is a stackbar visible, when changing focus to that container stack,
|
|
||||||
// the focused text colour will not be applied until the stack has been cycled at least once
|
|
||||||
//
|
|
||||||
// With this piece of code, we check if we have changed focus to a container stack with
|
|
||||||
// a stackbar, and if we have, we run a quick update to make sure the focused text colour
|
|
||||||
// has been applied
|
|
||||||
if !cross_monitor_monocle {
|
if !cross_monitor_monocle {
|
||||||
if let Ok(focused_window) = self.focused_window_mut() {
|
if let Ok(focused_window) = self.focused_window_mut() {
|
||||||
let focused_window_hwnd = focused_window.hwnd;
|
|
||||||
focused_window.focus(self.mouse_follows_focus)?;
|
focused_window.focus(self.mouse_follows_focus)?;
|
||||||
|
|
||||||
let focused_container = self.focused_container()?;
|
|
||||||
if let Some(stackbar) = focused_container.stackbar() {
|
|
||||||
stackbar.update(focused_container.windows(), focused_window_hwnd)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1580,16 +1574,6 @@ impl WindowManager {
|
|||||||
|
|
||||||
self.update_focused_workspace(true, true)?;
|
self.update_focused_workspace(true, true)?;
|
||||||
|
|
||||||
// TODO: fix this ugly hack to restore stackbar after monocle is toggled off
|
|
||||||
let workspace = self.focused_workspace()?;
|
|
||||||
if workspace.monocle_container().is_none() {
|
|
||||||
if let Some(container) = workspace.focused_container() {
|
|
||||||
if container.stackbar().is_some() {
|
|
||||||
self.retile_all(true)?;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1600,12 +1584,7 @@ impl WindowManager {
|
|||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
workspace.new_monocle_container()?;
|
workspace.new_monocle_container()?;
|
||||||
|
|
||||||
if let Some(monocle) = workspace.monocle_container_mut() {
|
|
||||||
monocle.set_stackbar_mode(StackbarMode::Never);
|
|
||||||
}
|
|
||||||
|
|
||||||
for container in workspace.containers_mut() {
|
for container in workspace.containers_mut() {
|
||||||
container.set_stackbar_mode(StackbarMode::Never);
|
|
||||||
container.hide(None);
|
container.hide(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1618,12 +1597,7 @@ impl WindowManager {
|
|||||||
|
|
||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
|
|
||||||
if let Some(monocle) = workspace.monocle_container_mut() {
|
|
||||||
monocle.set_stackbar_mode(STACKBAR_MODE.load());
|
|
||||||
}
|
|
||||||
|
|
||||||
for container in workspace.containers_mut() {
|
for container in workspace.containers_mut() {
|
||||||
container.set_stackbar_mode(STACKBAR_MODE.load());
|
|
||||||
container.restore();
|
container.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub enum WindowManagerEvent {
|
|||||||
Manage(Window),
|
Manage(Window),
|
||||||
Unmanage(Window),
|
Unmanage(Window),
|
||||||
Raise(Window),
|
Raise(Window),
|
||||||
ForceUpdate(Window),
|
TitleUpdate(WinEvent, Window),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for WindowManagerEvent {
|
impl Display for WindowManagerEvent {
|
||||||
@@ -75,8 +75,8 @@ impl Display for WindowManagerEvent {
|
|||||||
Self::Raise(window) => {
|
Self::Raise(window) => {
|
||||||
write!(f, "Raise (Window: {window})")
|
write!(f, "Raise (Window: {window})")
|
||||||
}
|
}
|
||||||
Self::ForceUpdate(window) => {
|
Self::TitleUpdate(winevent, window) => {
|
||||||
write!(f, "ForceUpdate (Window: {window})")
|
write!(f, "TitleUpdate (WinEvent: {winevent}, Window: {window})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ impl WindowManagerEvent {
|
|||||||
| Self::Raise(window)
|
| Self::Raise(window)
|
||||||
| Self::Manage(window)
|
| Self::Manage(window)
|
||||||
| Self::Unmanage(window)
|
| Self::Unmanage(window)
|
||||||
| Self::ForceUpdate(window) => window,
|
| Self::TitleUpdate(_, window) => window,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ impl WindowManagerEvent {
|
|||||||
let class = &window.class().ok()?;
|
let class = &window.class().ok()?;
|
||||||
let path = &window.path().ok()?;
|
let path = &window.path().ok()?;
|
||||||
|
|
||||||
let should_trigger = should_act(
|
let should_trigger_show = should_act(
|
||||||
title,
|
title,
|
||||||
exe_name,
|
exe_name,
|
||||||
class,
|
class,
|
||||||
@@ -151,10 +151,10 @@ impl WindowManagerEvent {
|
|||||||
)
|
)
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
if should_trigger {
|
if should_trigger_show {
|
||||||
Option::from(Self::Show(winevent, window))
|
Option::from(Self::Show(winevent, window))
|
||||||
} else {
|
} else {
|
||||||
None
|
Option::from(Self::TitleUpdate(winevent, window))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ impl WindowsApi {
|
|||||||
monitors.elements_mut().push_back(m);
|
monitors.elements_mut().push_back(m);
|
||||||
} else if let Some(preference) = index_preference {
|
} else if let Some(preference) = index_preference {
|
||||||
let current_len = monitors.elements().len();
|
let current_len = monitors.elements().len();
|
||||||
if *preference > current_len {
|
while *preference > current_len {
|
||||||
monitors.elements_mut().reserve(1);
|
monitors.elements_mut().reserve(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,8 +80,19 @@ pub extern "system" fn win_event_hook(
|
|||||||
Ok(event) => event,
|
Ok(event) => event,
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
||||||
None => return,
|
None => {
|
||||||
|
tracing::trace!(
|
||||||
|
"Unhandled WinEvent: {winevent} (hwnd: {}, exe: {}, title: {}, class: {})",
|
||||||
|
window.hwnd,
|
||||||
|
window.exe().unwrap_or_default(),
|
||||||
|
window.title().unwrap_or_default(),
|
||||||
|
window.class().unwrap_or_default()
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
Some(event) => event,
|
Some(event) => event,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ use crate::border_manager::BORDER_OFFSET;
|
|||||||
use crate::border_manager::BORDER_WIDTH;
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
use crate::container::Container;
|
use crate::container::Container;
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
|
use crate::stackbar_manager;
|
||||||
|
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
|
||||||
use crate::static_config::WorkspaceConfig;
|
use crate::static_config::WorkspaceConfig;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
use crate::window::WindowDetails;
|
use crate::window::WindowDetails;
|
||||||
@@ -33,7 +35,6 @@ use crate::DEFAULT_WORKSPACE_PADDING;
|
|||||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||||
use crate::NO_TITLEBAR;
|
use crate::NO_TITLEBAR;
|
||||||
use crate::REMOVE_TITLEBARS;
|
use crate::REMOVE_TITLEBARS;
|
||||||
use crate::STACKBAR_TAB_HEIGHT;
|
|
||||||
|
|
||||||
#[allow(clippy::struct_field_names)]
|
#[allow(clippy::struct_field_names)]
|
||||||
#[derive(
|
#[derive(
|
||||||
@@ -304,7 +305,7 @@ impl Workspace {
|
|||||||
} else if let Some(window) = self.maximized_window_mut() {
|
} else if let Some(window) = self.maximized_window_mut() {
|
||||||
window.maximize();
|
window.maximize();
|
||||||
} else if !self.containers().is_empty() {
|
} else if !self.containers().is_empty() {
|
||||||
let layouts = self.layout().as_boxed_arrangement().calculate(
|
let mut layouts = self.layout().as_boxed_arrangement().calculate(
|
||||||
&adjusted_work_area,
|
&adjusted_work_area,
|
||||||
NonZeroUsize::new(self.containers().len()).ok_or_else(|| {
|
NonZeroUsize::new(self.containers().len()).ok_or_else(|| {
|
||||||
anyhow!(
|
anyhow!(
|
||||||
@@ -319,24 +320,14 @@ impl Workspace {
|
|||||||
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
|
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
|
||||||
let no_titlebar = NO_TITLEBAR.lock().clone();
|
let no_titlebar = NO_TITLEBAR.lock().clone();
|
||||||
|
|
||||||
let focused_hwnd = self
|
|
||||||
.focused_container()
|
|
||||||
.ok_or_else(|| anyhow!("couldn't find a focused container"))?
|
|
||||||
.focused_window()
|
|
||||||
.ok_or_else(|| anyhow!("couldn't find a focused window"))?
|
|
||||||
.hwnd;
|
|
||||||
|
|
||||||
let container_padding = self.container_padding().unwrap_or(0);
|
let container_padding = self.container_padding().unwrap_or(0);
|
||||||
let containers = self.containers_mut();
|
let containers = self.containers_mut();
|
||||||
|
|
||||||
for (i, container) in containers.iter_mut().enumerate() {
|
for (i, container) in containers.iter_mut().enumerate() {
|
||||||
container.renew_stackbar();
|
let window_count = container.windows().len();
|
||||||
|
|
||||||
let container_windows = container.windows().clone();
|
|
||||||
let container_stackbar = container.stackbar().clone();
|
|
||||||
|
|
||||||
if let (Some(window), Some(layout)) =
|
if let (Some(window), Some(layout)) =
|
||||||
(container.focused_window_mut(), layouts.get(i))
|
(container.focused_window_mut(), layouts.get_mut(i))
|
||||||
{
|
{
|
||||||
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
|
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
|
||||||
window.remove_title_bar()?;
|
window.remove_title_bar()?;
|
||||||
@@ -350,33 +341,23 @@ impl Workspace {
|
|||||||
WindowsApi::restore_window(window.hwnd());
|
WindowsApi::restore_window(window.hwnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rect = *layout;
|
|
||||||
{
|
{
|
||||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||||
rect.add_padding(border_offset);
|
layout.add_padding(border_offset);
|
||||||
|
|
||||||
let width = BORDER_WIDTH.load(Ordering::SeqCst);
|
let width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||||
rect.add_padding(width);
|
layout.add_padding(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stackbar) = container_stackbar {
|
if stackbar_manager::should_have_stackbar(window_count) {
|
||||||
if stackbar
|
let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
||||||
.set_position(
|
let total_height = tab_height + container_padding;
|
||||||
&stackbar.get_position_from_container_layout(layout),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
stackbar.update(&container_windows, focused_hwnd)?;
|
|
||||||
let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
|
||||||
let total_height = tab_height + container_padding;
|
|
||||||
|
|
||||||
rect.top += total_height;
|
layout.top += total_height;
|
||||||
rect.bottom -= total_height;
|
layout.bottom -= total_height;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.set_position(&rect, false)?;
|
window.set_position(layout, false)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,17 +386,10 @@ impl Workspace {
|
|||||||
let containers = self.containers_mut();
|
let containers = self.containers_mut();
|
||||||
|
|
||||||
for container in containers.iter_mut() {
|
for container in containers.iter_mut() {
|
||||||
let container_windows = container.windows().clone();
|
|
||||||
let container_topbar = container.stackbar().clone();
|
|
||||||
|
|
||||||
if let Some(idx) = container.idx_for_window(hwnd) {
|
if let Some(idx) = container.idx_for_window(hwnd) {
|
||||||
container.focus_window(idx);
|
container.focus_window(idx);
|
||||||
container.restore();
|
container.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stackbar) = container_topbar {
|
|
||||||
stackbar.update(&container_windows, hwnd)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -686,6 +660,10 @@ impl Workspace {
|
|||||||
self.set_monocle_container_restore_idx(None);
|
self.set_monocle_container_restore_idx(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for c in self.containers() {
|
||||||
|
c.restore();
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebic-no-console"
|
name = "komorebic-no-console"
|
||||||
version = "0.1.26-dev.0"
|
version = "0.1.27-dev.0"
|
||||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||||
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
|
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
|
||||||
categories = ["cli", "tiling-window-manager", "windows"]
|
categories = ["cli", "tiling-window-manager", "windows"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebic"
|
name = "komorebic"
|
||||||
version = "0.1.26-dev.0"
|
version = "0.1.27-dev.0"
|
||||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||||
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
|
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
|
||||||
categories = ["cli", "tiling-window-manager", "windows"]
|
categories = ["cli", "tiling-window-manager", "windows"]
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
|
|||||||
|
|
||||||
use derive_ahk::AhkFunction;
|
use derive_ahk::AhkFunction;
|
||||||
use derive_ahk::AhkLibrary;
|
use derive_ahk::AhkLibrary;
|
||||||
|
use komorebi_client::StaticConfig;
|
||||||
use komorebi_core::config_generation::ApplicationConfigurationGenerator;
|
use komorebi_core::config_generation::ApplicationConfigurationGenerator;
|
||||||
use komorebi_core::ApplicationIdentifier;
|
use komorebi_core::ApplicationIdentifier;
|
||||||
use komorebi_core::Axis;
|
use komorebi_core::Axis;
|
||||||
@@ -828,6 +829,8 @@ enum SubCommand {
|
|||||||
State,
|
State,
|
||||||
/// Show a JSON representation of the current global state
|
/// Show a JSON representation of the current global state
|
||||||
GlobalState,
|
GlobalState,
|
||||||
|
/// Launch the komorebi-gui debugging tool
|
||||||
|
Gui,
|
||||||
/// Show a JSON representation of visible windows
|
/// Show a JSON representation of visible windows
|
||||||
VisibleWindows,
|
VisibleWindows,
|
||||||
/// Query the current window manager state
|
/// Query the current window manager state
|
||||||
@@ -1952,6 +1955,25 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
|
|||||||
);
|
);
|
||||||
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
|
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
|
||||||
println!("* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands");
|
println!("* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands");
|
||||||
|
|
||||||
|
let static_config = arg.config.map_or_else(
|
||||||
|
|| {
|
||||||
|
let komorebi_json = HOME_DIR.join("komorebi.json");
|
||||||
|
if komorebi_json.is_file() {
|
||||||
|
Option::from(komorebi_json)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Option::from,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(config) = static_config {
|
||||||
|
let path = resolve_home_path(config)?;
|
||||||
|
let raw = std::fs::read_to_string(path)?;
|
||||||
|
StaticConfig::aliases(&raw);
|
||||||
|
StaticConfig::deprecated(&raw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SubCommand::Stop(arg) => {
|
SubCommand::Stop(arg) => {
|
||||||
if arg.whkd {
|
if arg.whkd {
|
||||||
@@ -2130,6 +2152,9 @@ Stop-Process -Name:komorebi -ErrorAction SilentlyContinue
|
|||||||
SubCommand::GlobalState => {
|
SubCommand::GlobalState => {
|
||||||
print_query(&SocketMessage::GlobalState.as_bytes()?);
|
print_query(&SocketMessage::GlobalState.as_bytes()?);
|
||||||
}
|
}
|
||||||
|
SubCommand::Gui => {
|
||||||
|
Command::new("komorebi-gui").spawn()?;
|
||||||
|
}
|
||||||
SubCommand::VisibleWindows => {
|
SubCommand::VisibleWindows => {
|
||||||
print_query(&SocketMessage::VisibleWindows.as_bytes()?);
|
print_query(&SocketMessage::VisibleWindows.as_bytes()?);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ nav:
|
|||||||
- cli/whkdrc.md
|
- cli/whkdrc.md
|
||||||
- cli/state.md
|
- cli/state.md
|
||||||
- cli/global-state.md
|
- cli/global-state.md
|
||||||
|
- cli/gui.md
|
||||||
- cli/visible-windows.md
|
- cli/visible-windows.md
|
||||||
- cli/query.md
|
- cli/query.md
|
||||||
- cli/subscribe-socket.md
|
- cli/subscribe-socket.md
|
||||||
@@ -124,6 +125,7 @@ nav:
|
|||||||
- cli/cycle-monitor.md
|
- cli/cycle-monitor.md
|
||||||
- cli/cycle-workspace.md
|
- cli/cycle-workspace.md
|
||||||
- cli/move-workspace-to-monitor.md
|
- cli/move-workspace-to-monitor.md
|
||||||
|
- cli/cycle-move-workspace-to-monitor.md
|
||||||
- cli/swap-workspaces-with-monitor.md
|
- cli/swap-workspaces-with-monitor.md
|
||||||
- cli/new-workspace.md
|
- cli/new-workspace.md
|
||||||
- cli/resize-delta.md
|
- cli/resize-delta.md
|
||||||
@@ -140,6 +142,7 @@ nav:
|
|||||||
- cli/flip-layout.md
|
- cli/flip-layout.md
|
||||||
- cli/promote.md
|
- cli/promote.md
|
||||||
- cli/promote-focus.md
|
- cli/promote-focus.md
|
||||||
|
- cli/promote-window.md
|
||||||
- cli/retile.md
|
- cli/retile.md
|
||||||
- cli/monitor-index-preference.md
|
- cli/monitor-index-preference.md
|
||||||
- cli/display-index-preference.md
|
- cli/display-index-preference.md
|
||||||
|
|||||||
10
schema.json
10
schema.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "StaticConfig",
|
"title": "StaticConfig",
|
||||||
"description": "The `komorebi.json` static configuration file reference for `v0.1.25`",
|
"description": "The `komorebi.json` static configuration file reference for `v0.1.26`",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"app_specific_configuration_path": {
|
"app_specific_configuration_path": {
|
||||||
@@ -1039,6 +1039,14 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int32"
|
"format": "int32"
|
||||||
},
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "Stackbar height",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Process",
|
||||||
|
"Title"
|
||||||
|
]
|
||||||
|
},
|
||||||
"mode": {
|
"mode": {
|
||||||
"description": "Stackbar mode",
|
"description": "Stackbar mode",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
BIN
wix/License.rtf
BIN
wix/License.rtf
Binary file not shown.
@@ -95,6 +95,9 @@
|
|||||||
<Component Id='binary2' Guid='*'>
|
<Component Id='binary2' Guid='*'>
|
||||||
<File Id='exe2' Name='komorebic-no-console.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebic-no-console.exe' KeyPath='yes' />
|
<File Id='exe2' Name='komorebic-no-console.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebic-no-console.exe' KeyPath='yes' />
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component Id='binary3' Guid='*'>
|
||||||
|
<File Id='exe3' Name='komorebi-gui.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi-gui.exe' KeyPath='yes' />
|
||||||
|
</Component>
|
||||||
</Directory>
|
</Directory>
|
||||||
</Directory>
|
</Directory>
|
||||||
</Directory>
|
</Directory>
|
||||||
@@ -113,6 +116,8 @@
|
|||||||
|
|
||||||
<ComponentRef Id='binary2' />
|
<ComponentRef Id='binary2' />
|
||||||
|
|
||||||
|
<ComponentRef Id='binary3' />
|
||||||
|
|
||||||
<Feature Id='Environment' Title='PATH Environment Variable' Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.' Level='1' Absent='allow'>
|
<Feature Id='Environment' Title='PATH Environment Variable' Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.' Level='1' Absent='allow'>
|
||||||
<ComponentRef Id='Path' />
|
<ComponentRef Id='Path' />
|
||||||
</Feature>
|
</Feature>
|
||||||
|
|||||||
Reference in New Issue
Block a user