Compare commits

..

20 Commits

Author SHA1 Message Date
LGUG2Z
62900c59cb chore(release): v0.1.25 2024-04-30 14:49:45 -07:00
MasouShizuka
a2e9a46582 feat(wm): add resize of all layouts except grid with filp
fix: fix horizontal-stack out of monitor with vertical flip (db28e25)

feat: add resize of right-main-vertical-stack layout with filp (28dd546)
2024-04-30 13:09:53 -07:00
dependabot[bot]
871a53821c chore(deps): bump schemars from 0.8.16 to 0.8.17
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.16 to 0.8.17.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.16...v0.8.17)

---
updated-dependencies:
- dependency-name: schemars
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 18:27:57 -07:00
dependabot[bot]
bcd1c50d82 chore(deps): bump parking_lot from 0.12.1 to 0.12.2
Bumps [parking_lot](https://github.com/Amanieu/parking_lot) from 0.12.1 to 0.12.2.
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/0.12.1...0.12.2)

---
updated-dependencies:
- dependency-name: parking_lot
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 18:27:44 -07:00
dependabot[bot]
0c41d9ded2 chore(deps): bump serde from 1.0.197 to 1.0.199
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.197 to 1.0.199.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.197...v1.0.199)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 18:27:17 -07:00
dependabot[bot]
4af62fe97b chore(deps): bump thiserror from 1.0.58 to 1.0.59
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.59.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.59)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 18:26:10 -07:00
LGUG2Z
7cab062124 fix(stackbar): avoid drops on notification events
This commit completely removes the custom Clone and Drop trait
implementation for Stackbar, and moves the handling to be explicit
within container.rs, which helps us to avoid unintentional drops when
the Stackbar struct is cloned for Notification events sent to
subscribers such as Zebar.

re #746
2024-04-29 17:31:27 -07:00
LGUG2Z
611fa34567 fix(stackbar): avoid drops from variable updates
This commit ensures that when we call methods to change
Container.stackbar we are not unintentionally invoking a Drop which
kills a stackbar window that already exists.

Checks have been added to make sure that we not change the value of the
stackbar variable if it is already an Option::Some.

re #746 re #792
2024-04-29 13:56:24 -07:00
LGUG2Z
95990d682b fix(config): ensure tiling with layout rules
This commit ensures that tiling on a workspace will be enabled if a user
specifies layout rules or custom layout rules without providing an
option to the "layout" key.
2024-04-29 08:06:37 -07:00
LGUG2Z
e363a494c3 docs(mkdocs): various updates to reflect v0.1.25-dev.0 2024-04-28 13:51:08 -07:00
LGUG2Z
383533e2d9 feat(wm): add right-main-vertical-stack layout
This commit adds a new RightMainVerticalStack layout, adapting code from
the similarly named LeftWM layout.

It turns out that the horizontal axis flip on the VerticalStack does not
play well with resize offsets.

It was ultimately easier to implement this layout and the logic for
resizing both VerticalStack and RightMainVerticalStack independently
than to make resize offsets and horizontal axis flips work together.

I still have no idea why resize offsets and horizontal axis flips aren't
working properly together.

Horizontal axis flips have been disabled for both the VerticalStack and
RightMainVerticalStack layouts.

re #789
2024-04-28 12:17:53 -07:00
LGUG2Z
0b04e3ef93 fix(wm): avoid out of range container focus op
This commit ensures that when floating the sole window visible on a
workspace that the focused container index for the workspace will not be
decremented below 0.

re #787
2024-04-25 18:30:52 -07:00
LGUG2Z
3370e6acc5 fix(wm): revert mff edge case regression
This commit ensures that the wm's mouse_follows_mouse state is respected when handling FocusChange
WindowManagerEvents, so that applications opened on empty workspaces do not automatically center the
cursor unless configured to do so.

fix #782
2024-04-23 07:19:53 -07:00
LGUG2Z
4ffffc5eec fix(wm): revert regression in ffm raise handler 2024-04-23 07:03:48 -07:00
dependabot[bot]
2b5f737d14 chore(deps): bump proc-macro2 from 1.0.80 to 1.0.81
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.80 to 1.0.81.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.80...1.0.81)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 17:16:27 -07:00
dependabot[bot]
8a455c8ab7 chore(deps): bump serde_json from 1.0.115 to 1.0.116
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.115 to 1.0.116.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.115...v1.0.116)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 17:16:14 -07:00
dependabot[bot]
3d9871c576 chore(deps): bump reqwest from 0.12.3 to 0.12.4
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.12.3 to 0.12.4.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.3...v0.12.4)

---
updated-dependencies:
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 17:15:51 -07:00
dependabot[bot]
cafb8e9a48 chore(deps): bump sysinfo from 0.30.10 to 0.30.11
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.30.10 to 0.30.11.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/compare/v0.30.10...v0.30.11)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 17:15:26 -07:00
Sam Vendittelli
740cb3c877 fix(docs): correct typo in example config
The example configuration mistakenly used the key `border_padding` in
the place of `border_width`. As `border_padding` does not exist in the
spec, modifying its value has no effect.

As this file is used by `komorebic quickstart`, new users will have this
incorrect key in their default configuration. Notably, setting its value
to `0` to remove gaps has no effect. The rest of the documentation uses
the correct key, so users copying and pasting from that would not
encounter the bug.
2024-04-21 17:43:13 -07:00
LGUG2Z
b8b3b3d615 docs(wm): add stackbar config struct docs 2024-04-21 14:12:09 -07:00
35 changed files with 1090 additions and 3993 deletions

View File

@@ -10,8 +10,8 @@ before:
builds:
- id: komorebi
main: dummy.go
goos: ["windows"]
goarch: ["amd64"]
goos: [ "windows" ]
goarch: [ "amd64" ]
binary: komorebi
hooks:
post:
@@ -19,8 +19,8 @@ builds:
- cp ".\target\x86_64-pc-windows-msvc\release\komorebi.exe" ".\dist\komorebi_windows_amd64_v1\komorebi.exe"
- id: komorebic
main: dummy.go
goos: ["windows"]
goarch: ["amd64"]
goos: [ "windows" ]
goarch: [ "amd64" ]
binary: komorebic
hooks:
post:
@@ -28,8 +28,8 @@ builds:
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic.exe" ".\dist\komorebic_windows_amd64_v1\komorebic.exe"
- id: komorebic-no-console
main: dummy.go
goos: ["windows"]
goarch: ["amd64"]
goos: [ "windows" ]
goarch: [ "amd64" ]
binary: komorebic-no-console
hooks:
post:
@@ -40,7 +40,7 @@ archives:
- name_template: "{{ .ProjectName }}-{{ .Version }}-x86_64-pc-windows-msvc"
format: zip
files:
- LICENSE
- LICENSE.md
- CHANGELOG.md
checksum:

2956
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@ members = [
"komorebi",
"komorebi-client",
"komorebi-core",
"komorebi-egui",
"komorebic",
"komorebic-no-console",
]

View File

@@ -338,7 +338,7 @@ every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust c
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
```rust
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.24"}
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.25"}
use anyhow::Result;
use komorebi_client::Notification;

View File

@@ -7,7 +7,7 @@ Usage: komorebic.exe change-layout <DEFAULT_LAYOUT>
Arguments:
<DEFAULT_LAYOUT>
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
Options:
-h, --help

12
docs/cli/global-state.md Normal file
View File

@@ -0,0 +1,12 @@
# global-state
```
Show a JSON representation of the current global state
Usage: komorebic.exe global-state
Options:
-h, --help
Print help
```

View File

@@ -0,0 +1,19 @@
# move-to-monitor-workspace
```
Move the focused window to the specified monitor workspace
Usage: komorebic.exe move-to-monitor-workspace <TARGET_MONITOR> <TARGET_WORKSPACE>
Arguments:
<TARGET_MONITOR>
Target monitor index (zero-indexed)
<TARGET_WORKSPACE>
Workspace index on the target monitor (zero-indexed)
Options:
-h, --help
Print help
```

View File

@@ -13,7 +13,7 @@ Arguments:
The number of window containers on-screen required to trigger this layout rule
<LAYOUT>
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
Options:
-h, --help

View File

@@ -10,7 +10,7 @@ Arguments:
Target workspace name
<VALUE>
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
Options:
-h, --help

View File

@@ -16,7 +16,7 @@ Arguments:
The number of window containers on-screen required to trigger this layout rule
<LAYOUT>
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
Options:
-h, --help

View File

@@ -13,7 +13,7 @@ Arguments:
Workspace index on the specified monitor (zero-indexed)
<VALUE>
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
Options:
-h, --help

View File

@@ -2,7 +2,7 @@
❗️**NOTE**: A significant number of force-manage window rules for the most
common applications are [already generated for
you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
you](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
In some rare cases, a window may not automatically be registered to be managed
by `komorebi`. You can add rules to enforce this behaviour in the

View File

@@ -2,7 +2,7 @@
❗️**NOTE**: A significant number of ignored window rules for the most common
applications are [already generated for
you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
you](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
Sometimes you will want a specific application to never be tiled, and instead
float all the time. You can add rules to enforce this behaviour in the

View File

@@ -101,6 +101,16 @@ monocle.
+-------+-----+
```
### RightMainVerticalStack
```
+-----+-------+
| | |
+-----+ |
| | |
+-----+-------+
```
#### Horizontal Stack
```
@@ -122,6 +132,7 @@ monocle.
```
#### Rows
If you have a vertical monitor, I recommend using this layout.
```
@@ -133,6 +144,7 @@ If you have a vertical monitor, I recommend using this layout.
```
#### Ultrawide Vertical Stack
If you have an ultrawide monitor, I recommend using this layout.
```
@@ -146,6 +158,7 @@ If you have an ultrawide monitor, I recommend using this layout.
```
### Grid
If you like the `grid` layout in [LeftWM](https://github.com/leftwm/leftwm-layouts) this is almost exactly the same!
```
@@ -183,7 +196,8 @@ which shell you use in your terminal.
* `powershell` - set this if you are using the version of PowerShell that comes
installed with Windows 10+ (the executable file for this is `powershell.exe`)
* `pwsh` - set this if you are using PowerShell 7+, which you have installed yourself either through the Windows Store or WinGet (the executable file for this is `pwsh.exe`)
* `pwsh` - set this if you are using PowerShell 7+, which you have installed yourself either through the Windows Store
or WinGet (the executable file for this is `pwsh.exe`)
* `cmd` - set this if you don't want to use PowerShell at all and instead you
want to call commands through the shell used by the old-school Command

View File

@@ -1,11 +1,11 @@
{
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.24/schema.json",
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.25/schema.json",
"app_specific_configuration_path": "$Env:USERPROFILE/applications.yaml",
"window_hiding_behaviour": "Cloak",
"cross_monitor_move_behaviour": "Insert",
"default_workspace_padding": 20,
"default_container_padding": 20,
"border_padding": 8,
"border_width": 8,
"border_offset": -1,
"active_window_border": false,
"active_window_border_colours": {
@@ -13,6 +13,16 @@
"stack": "#00a542",
"monocle": "#ff3399"
},
"stackbar": {
"height": 40,
"mode": "Never",
"tabs": {
"width": 300,
"focused_text": "#00a542",
"unfocused_text": "#b3b3b3",
"background": "#141414"
}
},
"monitors": [
{
"workspaces": [
@@ -35,6 +45,14 @@
{
"name": "V",
"layout": "Rows"
},
{
"name": "VI",
"layout": "Grid"
},
{
"name": "VII",
"layout": "RightMainVerticalStack"
}
]
}

View File

@@ -26,7 +26,6 @@ install:
just install-target komorebic
just install-target komorebic-no-console
just install-target komorebi
just install-target komorebi-egui
run:
just install-target komorebic

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-client"
version = "0.1.25-dev.0"
version = "0.1.25"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-core"
version = "0.1.25-dev.0"
version = "0.1.25"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -44,8 +44,54 @@ impl Arrangement for DefaultLayout {
layout_flip,
calculate_resize_adjustments(resize_dimensions),
),
Self::Columns => columns(area, len),
Self::Rows => rows(area, len),
Self::Columns => {
let mut layouts = columns(area, len);
let adjustment = calculate_columns_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) => match len {
2.. => columns_reverse(&mut layouts),
_ => {}
},
_ => {}
}
layouts
}
Self::Rows => {
let mut layouts = rows(area, len);
let adjustment = calculate_rows_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
match layout_flip {
Some(Axis::Vertical | Axis::HorizontalAndVertical) => match len {
2.. => rows_reverse(&mut layouts),
_ => {}
},
_ => {}
}
layouts
}
Self::VerticalStack => {
let mut layouts: Vec<Rect> = vec![];
@@ -54,16 +100,8 @@ impl Arrangement for DefaultLayout {
_ => area.right / 2,
};
let mut main_left = area.left;
let mut stack_left = area.left + primary_right;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
main_left = main_left + area.right - primary_right;
stack_left = area.left;
}
_ => {}
}
let main_left = area.left;
let stack_left = area.left + primary_right;
if len >= 1 {
layouts.push(Rect {
@@ -86,6 +124,113 @@ impl Arrangement for DefaultLayout {
}
}
let adjustment = calculate_vertical_stack_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) => match len {
2.. => {
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
for rect in rest.iter_mut() {
rect.left = primary.left;
}
primary.left = rest[0].left + rest[0].right;
}
_ => {}
},
_ => {}
}
match layout_flip {
Some(Axis::Vertical | Axis::HorizontalAndVertical) => match len {
3.. => rows_reverse(&mut layouts[1..]),
_ => {}
},
_ => {}
}
layouts
}
Self::RightMainVerticalStack => {
// Shamelessly borrowed from LeftWM: https://github.com/leftwm/leftwm/commit/f673851745295ae7584a102535566f559d96a941
let mut layouts: Vec<Rect> = vec![];
let primary_width = match len {
1 => area.right,
_ => area.right / 2,
};
let primary_left = match len {
1 => 0,
_ => area.right - primary_width,
};
if len >= 1 {
layouts.push(Rect {
left: area.left + primary_left,
top: area.top,
right: primary_width,
bottom: area.bottom,
});
if len > 1 {
layouts.append(&mut rows(
&Rect {
left: area.left,
top: area.top,
right: primary_left,
bottom: area.bottom,
},
len - 1,
));
}
}
let adjustment = calculate_right_vertical_stack_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) => match len {
2.. => {
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
primary.left = rest[0].left;
for rect in rest.iter_mut() {
rect.left = primary.left + primary.right;
}
}
_ => {}
},
_ => {}
}
match layout_flip {
Some(Axis::Vertical | Axis::HorizontalAndVertical) => match len {
3.. => rows_reverse(&mut layouts[1..]),
_ => {}
},
_ => {}
}
layouts
}
Self::HorizontalStack => {
@@ -96,16 +241,8 @@ impl Arrangement for DefaultLayout {
_ => area.bottom / 2,
};
let mut main_top = area.top;
let mut stack_top = area.top + bottom;
match layout_flip {
Some(Axis::Vertical | Axis::HorizontalAndVertical) if len > 1 => {
main_top = main_top + area.bottom - bottom;
stack_top = area.top;
}
_ => {}
}
let main_top = area.top;
let stack_top = area.top + bottom;
if len >= 1 {
layouts.push(Rect {
@@ -128,9 +265,152 @@ impl Arrangement for DefaultLayout {
}
}
let adjustment = calculate_horizontal_stack_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
match layout_flip {
Some(Axis::Vertical | Axis::HorizontalAndVertical) => match len {
2.. => {
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
for rect in rest.iter_mut() {
rect.top = primary.top;
}
primary.top = rest[0].top + rest[0].bottom;
}
_ => {}
},
_ => {}
}
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) => match len {
3.. => columns_reverse(&mut layouts[1..]),
_ => {}
},
_ => {}
}
layouts
}
Self::UltrawideVerticalStack => ultrawide(area, len, layout_flip, resize_dimensions),
Self::UltrawideVerticalStack => {
let mut layouts: Vec<Rect> = vec![];
let primary_right = match len {
1 => area.right,
_ => area.right / 2,
};
let secondary_right = match len {
1 => 0,
2 => area.right - primary_right,
_ => (area.right - primary_right) / 2,
};
let (primary_left, secondary_left, stack_left) = match len {
1 => (area.left, 0, 0),
2 => {
let primary = area.left + secondary_right;
let secondary = area.left;
(primary, secondary, 0)
}
_ => {
let primary = area.left + secondary_right;
let secondary = area.left;
let stack = area.left + primary_right + secondary_right;
(primary, secondary, stack)
}
};
if len >= 1 {
layouts.push(Rect {
left: primary_left,
top: area.top,
right: primary_right,
bottom: area.bottom,
});
if len >= 2 {
layouts.push(Rect {
left: secondary_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
});
if len > 2 {
layouts.append(&mut rows(
&Rect {
left: stack_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
},
len - 2,
));
}
}
}
let adjustment = calculate_ultrawide_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) => match len {
2 => {
let (primary, secondary) = layouts.split_at_mut(1);
let primary = &mut primary[0];
let secondary = &mut secondary[0];
primary.left = secondary.left;
secondary.left = primary.left + primary.right;
}
3.. => {
let (primary, rest) = layouts.split_at_mut(1);
let (secondary, tertiary) = rest.split_at_mut(1);
let primary = &mut primary[0];
let secondary = &mut secondary[0];
for rect in tertiary.iter_mut() {
rect.left = secondary.left;
}
primary.left = tertiary[0].left + tertiary[0].right;
secondary.left = primary.left + primary.right;
}
_ => {}
},
_ => {}
}
match layout_flip {
Some(Axis::Vertical | Axis::HorizontalAndVertical) => match len {
4.. => rows_reverse(&mut layouts[2..]),
_ => {}
},
_ => {}
}
layouts
},
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
@@ -369,6 +649,22 @@ fn rows(area: &Rect, len: usize) -> Vec<Rect> {
layouts
}
fn columns_reverse(columns: &mut [Rect]) {
let len = columns.len();
columns[len - 1].left = columns[0].left;
for i in (0..len - 1).rev() {
columns[i].left = columns[i + 1].left + columns[i + 1].right;
}
}
fn rows_reverse(rows: &mut [Rect]) {
let len = rows.len();
rows[len - 1].top = rows[0].top;
for i in (0..len - 1).rev() {
rows[i].top = rows[i + 1].top + rows[i + 1].bottom;
}
}
fn calculate_resize_adjustments(resize_dimensions: &[Option<Rect>]) -> Vec<Option<Rect>> {
let mut resize_adjustments = resize_dimensions.to_vec();
@@ -562,6 +858,187 @@ fn recursive_fibonacci(
}
}
fn calculate_columns_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
match len {
0 | 1 => (),
_ => {
for (i, rect) in resize_dimensions.iter().enumerate() {
if let Some(rect) = rect {
if i != 0 {
resize_right(&mut result[i - 1], rect.left);
resize_left(&mut result[i], rect.left);
}
if i != len - 1 {
resize_right(&mut result[i], rect.right);
resize_left(&mut result[i + 1], rect.right);
}
}
}
}
};
result
}
fn calculate_rows_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
match len {
0 | 1 => (),
_ => {
for (i, rect) in resize_dimensions.iter().enumerate() {
if let Some(rect) = rect {
if i != 0 {
resize_bottom(&mut result[i - 1], rect.top);
resize_top(&mut result[i], rect.top);
}
if i != len - 1 {
resize_bottom(&mut result[i], rect.bottom);
resize_top(&mut result[i + 1], rect.bottom);
}
}
}
}
};
result
}
fn calculate_vertical_stack_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
match len {
// One container can't be resized
0 | 1 => (),
_ => {
let (master, stack) = result.split_at_mut(1);
let primary = &mut master[0];
if let Some(resize) = resize_dimensions[0] {
resize_right(primary, resize.right);
for s in &mut *stack {
resize_left(s, resize.right);
}
}
// Handle stack on the right
for (i, rect) in resize_dimensions[1..].iter().enumerate() {
if let Some(rect) = rect {
resize_right(primary, rect.left);
stack
.iter_mut()
.for_each(|vertical_element| resize_left(vertical_element, rect.left));
// Containers in stack except first can be resized up displacing container
// above them
if i != 0 {
resize_bottom(&mut stack[i - 1], rect.top);
resize_top(&mut stack[i], rect.top);
}
// Containers in stack except last can be resized down displacing container
// below them
if i != stack.len() - 1 {
resize_bottom(&mut stack[i], rect.bottom);
resize_top(&mut stack[i + 1], rect.bottom);
}
}
}
}
};
result
}
fn calculate_right_vertical_stack_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
match len {
// One container can't be resized
0 | 1 => (),
_ => {
let (master, stack) = result.split_at_mut(1);
let primary = &mut master[0];
if let Some(resize) = resize_dimensions[0] {
resize_left(primary, resize.left);
for s in &mut *stack {
resize_right(s, resize.left);
}
}
// Handle stack on the left
for (i, rect) in resize_dimensions[1..].iter().enumerate() {
if let Some(rect) = rect {
resize_left(primary, rect.right);
stack
.iter_mut()
.for_each(|vertical_element| resize_right(vertical_element, rect.right));
// Containers in stack except first can be resized up displacing container
// above them
if i != 0 {
resize_bottom(&mut stack[i - 1], rect.top);
resize_top(&mut stack[i], rect.top);
}
// Containers in stack except last can be resized down displacing container
// below them
if i != stack.len() - 1 {
resize_bottom(&mut stack[i], rect.bottom);
resize_top(&mut stack[i + 1], rect.bottom);
}
}
}
}
};
result
}
fn calculate_horizontal_stack_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
match len {
0 | 1 => (),
_ => {
let (primary, rest) = result.split_at_mut(1);
let primary = &mut primary[0];
if let Some(resize_primary) = resize_dimensions[0] {
resize_bottom(primary, resize_primary.bottom);
for horizontal_element in &mut *rest {
resize_top(horizontal_element, resize_primary.bottom);
}
}
for (i, rect) in resize_dimensions[1..].iter().enumerate() {
if let Some(rect) = rect {
resize_bottom(primary, rect.top);
rest.iter_mut()
.for_each(|vertical_element| resize_top(vertical_element, rect.top));
if i != 0 {
resize_right(&mut rest[i - 1], rect.left);
resize_left(&mut rest[i], rect.left);
}
if i != rest.len() - 1 {
resize_right(&mut rest[i], rect.right);
resize_left(&mut rest[i + 1], rect.right);
}
}
}
}
};
result
}
fn calculate_ultrawide_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
@@ -652,99 +1129,3 @@ fn resize_top(rect: &mut Rect, resize: i32) {
fn resize_bottom(rect: &mut Rect, resize: i32) {
rect.bottom += resize / 2;
}
fn ultrawide(
area: &Rect,
len: usize,
layout_flip: Option<Axis>,
resize_dimensions: &[Option<Rect>],
) -> Vec<Rect> {
let mut layouts: Vec<Rect> = vec![];
let primary_right = match len {
1 => area.right,
_ => area.right / 2,
};
let secondary_right = match len {
1 => 0,
2 => area.right - primary_right,
_ => (area.right - primary_right) / 2,
};
let (primary_left, secondary_left, stack_left) = match len {
1 => (area.left, 0, 0),
2 => {
let mut primary = area.left + secondary_right;
let mut secondary = area.left;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
primary = area.left;
secondary = area.left + primary_right;
}
_ => {}
}
(primary, secondary, 0)
}
_ => {
let primary = area.left + secondary_right;
let mut secondary = area.left;
let mut stack = area.left + primary_right + secondary_right;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
secondary = area.left + primary_right + secondary_right;
stack = area.left;
}
_ => {}
}
(primary, secondary, stack)
}
};
if len >= 1 {
layouts.push(Rect {
left: primary_left,
top: area.top,
right: primary_right,
bottom: area.bottom,
});
if len >= 2 {
layouts.push(Rect {
left: secondary_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
});
if len > 2 {
layouts.append(&mut rows(
&Rect {
left: stack_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
},
len - 2,
));
}
}
}
let adjustment = calculate_ultrawide_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
layouts
}

View File

@@ -30,6 +30,7 @@ pub enum DefaultLayout {
HorizontalStack,
UltrawideVerticalStack,
Grid,
RightMainVerticalStack,
// NOTE: If any new layout is added, please make sure to register the same in `DefaultLayout::cycle`
}
@@ -44,7 +45,16 @@ impl DefaultLayout {
sizing: Sizing,
delta: i32,
) -> Option<Rect> {
if !matches!(self, Self::BSP) && !matches!(self, Self::UltrawideVerticalStack) {
if !matches!(
self,
Self::BSP
| Self::Columns
| Self::Rows
| Self::VerticalStack
| Self::RightMainVerticalStack
| Self::HorizontalStack
| Self::UltrawideVerticalStack
) {
return None;
};
@@ -146,7 +156,8 @@ impl DefaultLayout {
Self::VerticalStack => Self::HorizontalStack,
Self::HorizontalStack => Self::UltrawideVerticalStack,
Self::UltrawideVerticalStack => Self::Grid,
Self::Grid => Self::BSP,
Self::Grid => Self::RightMainVerticalStack,
Self::RightMainVerticalStack => Self::BSP,
}
}
@@ -159,7 +170,8 @@ impl DefaultLayout {
Self::VerticalStack => Self::Rows,
Self::Rows => Self::Columns,
Self::Columns => Self::Grid,
Self::Grid => Self::BSP,
Self::Grid => Self::RightMainVerticalStack,
Self::RightMainVerticalStack => Self::BSP,
}
}
}

View File

@@ -95,7 +95,7 @@ impl Direction for DefaultLayout {
Self::BSP => count > 2 && idx != 0 && idx != 1,
Self::Columns => false,
Self::Rows | Self::HorizontalStack => idx != 0,
Self::VerticalStack => idx != 0 && idx != 1,
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != 1,
Self::UltrawideVerticalStack => idx > 2,
Self::Grid => !is_grid_edge(op_direction, idx, count),
},
@@ -103,7 +103,7 @@ impl Direction for DefaultLayout {
Self::BSP => count > 2 && idx != count - 1 && idx % 2 != 0,
Self::Columns => false,
Self::Rows => idx != count - 1,
Self::VerticalStack => idx != 0 && idx != count - 1,
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != count - 1,
Self::HorizontalStack => idx == 0,
Self::UltrawideVerticalStack => idx > 1 && idx != count - 1,
Self::Grid => !is_grid_edge(op_direction, idx, count),
@@ -111,6 +111,7 @@ impl Direction for DefaultLayout {
OperationDirection::Left => match self {
Self::BSP => count > 1 && idx != 0,
Self::Columns | Self::VerticalStack => idx != 0,
Self::RightMainVerticalStack => idx == 0,
Self::Rows => false,
Self::HorizontalStack => idx != 0 && idx != 1,
Self::UltrawideVerticalStack => count > 1 && idx != 1,
@@ -121,6 +122,7 @@ impl Direction for DefaultLayout {
Self::Columns => idx != count - 1,
Self::Rows => false,
Self::VerticalStack => idx == 0,
Self::RightMainVerticalStack => idx != 0,
Self::HorizontalStack => idx != 0 && idx != count - 1,
Self::UltrawideVerticalStack => match count {
0 | 1 => false,
@@ -147,7 +149,10 @@ impl Direction for DefaultLayout {
}
}
Self::Columns => unreachable!(),
Self::Rows | Self::VerticalStack | Self::UltrawideVerticalStack => idx - 1,
Self::Rows
| Self::VerticalStack
| Self::UltrawideVerticalStack
| Self::RightMainVerticalStack => idx - 1,
Self::HorizontalStack => 0,
Self::Grid => grid_neighbor(op_direction, idx, count),
}
@@ -160,7 +165,11 @@ impl Direction for DefaultLayout {
count: Option<usize>,
) -> usize {
match self {
Self::BSP | Self::Rows | Self::VerticalStack | Self::UltrawideVerticalStack => idx + 1,
Self::BSP
| Self::Rows
| Self::VerticalStack
| Self::UltrawideVerticalStack
| Self::RightMainVerticalStack => idx + 1,
Self::Columns => unreachable!(),
Self::HorizontalStack => 1,
Self::Grid => grid_neighbor(op_direction, idx, count),
@@ -184,6 +193,7 @@ impl Direction for DefaultLayout {
Self::Columns | Self::HorizontalStack => idx - 1,
Self::Rows => unreachable!(),
Self::VerticalStack => 0,
Self::RightMainVerticalStack => 1,
Self::UltrawideVerticalStack => match idx {
0 => 1,
1 => unreachable!(),
@@ -203,6 +213,7 @@ impl Direction for DefaultLayout {
Self::BSP | Self::Columns | Self::HorizontalStack => idx + 1,
Self::Rows => unreachable!(),
Self::VerticalStack => 1,
Self::RightMainVerticalStack => 0,
Self::UltrawideVerticalStack => match idx {
1 => 0,
0 => 2,

View File

@@ -1,14 +0,0 @@
[package]
name = "komorebi-egui"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
eframe = { version = "0.27" }
komorebi-client = { path = "../komorebi-client" }
serde_json = { workspace = true }
random_word = { version = "0.4.3", features = ["en"] }
windows = { workspace = true }
egui_extras = { version = "0.27" }

View File

@@ -1,760 +0,0 @@
use eframe::egui;
use eframe::egui::color_picker::Alpha;
use eframe::egui::Color32;
use eframe::egui::WindowLevel;
use komorebi_client::ActiveWindowBorderStyle;
use komorebi_client::Colour;
use komorebi_client::DefaultLayout;
use komorebi_client::Layout;
use komorebi_client::Monitor;
use komorebi_client::Rect;
use komorebi_client::Rgb;
use komorebi_client::RuleDebug;
use komorebi_client::SocketMessage;
use komorebi_client::StackbarMode;
use komorebi_client::Window;
use komorebi_client::WindowKind;
use komorebi_client::Workspace;
use random_word::Lang;
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
fn main() {
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_window_level(WindowLevel::AlwaysOnTop)
.with_inner_size([320.0, 500.0]),
follow_system_theme: true,
..Default::default()
};
eframe::run_native(
"komorebi-egui",
native_options,
Box::new(|cc| Box::new(KomorebiEgui::new(cc))),
)
.unwrap();
}
struct KomorebiEgui {
monitors: Vec<MonitorConfig>,
border_config: BorderConfig,
stackbar_config: StackbarConfig,
mouse_follows_focus: bool,
hwnd_lookup: isize,
hwnd_lookup_windows: Vec<Window>,
hwnd_rule_debug: Option<RuleDebug>,
}
fn colour32(colour: Colour) -> Color32 {
match colour {
Colour::Rgb(rgb) => Color32::from_rgb(rgb.r as u8, rgb.g as u8, rgb.b as u8),
Colour::Hex(hex) => {
let rgb = Rgb::from(hex);
Color32::from_rgb(rgb.r as u8, rgb.g as u8, rgb.b as u8)
}
}
}
impl KomorebiEgui {
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 mut state = serde_json::from_str::<komorebi_client::State>(
&komorebi_client::send_query(&SocketMessage::State).unwrap(),
)
.unwrap();
let global_state = serde_json::from_str::<komorebi_client::GlobalState>(
&komorebi_client::send_query(&SocketMessage::GlobalState).unwrap(),
)
.unwrap();
let mut monitors = vec![];
for m in state.monitors.elements_mut() {
monitors.push(MonitorConfig::from(m.clone()));
}
let border_config = BorderConfig {
active_window_border_enabled: global_state.active_window_border_enabled,
active_window_border_style: global_state.active_window_border_style,
border_width: global_state.border_width,
border_offset: global_state.border_offset,
single: colour32(global_state.active_window_border_colours.single),
stack: colour32(global_state.active_window_border_colours.stack),
monocle: colour32(global_state.active_window_border_colours.monocle),
};
let stackbar_config = StackbarConfig {
stackbar_mode: global_state.stackbar_mode,
stackbar_focused_text_colour: colour32(global_state.stackbar_focused_text_colour),
stackbar_unfocused_text_colour: colour32(global_state.stackbar_unfocused_text_colour),
stackbar_tab_background_colour: colour32(global_state.stackbar_tab_background_colour),
stackbar_tab_width: global_state.stackbar_tab_width,
stackbar_height: global_state.stackbar_height,
};
let mut hwnd_lookup_windows = vec![];
unsafe {
EnumWindows(
Some(enum_window),
windows::Win32::Foundation::LPARAM(
&mut hwnd_lookup_windows as *mut Vec<Window> as isize,
),
)
.unwrap();
};
Self {
monitors,
border_config,
stackbar_config,
mouse_follows_focus: state.mouse_follows_focus,
hwnd_lookup: 0,
hwnd_lookup_windows,
hwnd_rule_debug: None,
}
}
}
struct BorderConfig {
active_window_border_enabled: bool,
active_window_border_style: ActiveWindowBorderStyle,
border_width: i32,
border_offset: i32,
single: Color32,
monocle: Color32,
stack: Color32,
}
struct StackbarConfig {
stackbar_mode: StackbarMode,
stackbar_focused_text_colour: Color32,
stackbar_unfocused_text_colour: Color32,
stackbar_tab_background_colour: Color32,
stackbar_tab_width: i32,
stackbar_height: i32,
}
#[derive(Clone)]
struct MonitorConfig {
work_area_offset: Rect,
size: Rect,
workspaces: Vec<WorkspaceConfig>,
}
impl From<Monitor> for MonitorConfig {
fn from(value: Monitor) -> Self {
let mut workspaces = vec![];
for ws in value.workspaces() {
workspaces.push(WorkspaceConfig::from(ws.clone()));
}
Self {
work_area_offset: value.work_area_offset().unwrap_or_default(),
size: *value.size(),
workspaces,
}
}
}
#[derive(Clone)]
struct WorkspaceConfig {
container_padding: i32,
workspace_padding: i32,
layout: DefaultLayout,
name: String,
}
impl From<Workspace> for WorkspaceConfig {
fn from(value: Workspace) -> Self {
Self {
container_padding: value.container_padding().unwrap_or(20),
workspace_padding: value.workspace_padding().unwrap_or(20),
layout: match value.layout() {
Layout::Default(layout) => *layout,
Layout::Custom(_) => DefaultLayout::BSP,
},
name: value
.name()
.clone()
.unwrap_or_else(|| random_word::gen(Lang::En).to_string()),
}
}
}
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 KomorebiEgui {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
ctx.set_pixels_per_point(1.5);
egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
ui.vertical_centered_justified(|ui| {
ui.set_width(ctx.input(|i| i.viewport().inner_rect.unwrap().width()));
ui.collapsing("Debug Windows and Rules", |ui| {
let window = Window {
hwnd: self.hwnd_lookup,
};
let title = if let (Ok(title), Ok(exe)) = (window.title(), window.exe()) {
format!("{} - {:?} - {:?}", window.hwnd, exe, title)
} else {
String::from("Select a Window")
};
if ui.button("Refresh Window List").clicked() {
let mut windows = vec![];
unsafe {
EnumWindows(
Some(enum_window),
windows::Win32::Foundation::LPARAM(
&mut windows as *mut Vec<Window> as isize,
),
)
.unwrap();
};
self.hwnd_lookup_windows = windows;
}
egui::ComboBox::from_label("Select one!")
.selected_text(format!("{:?}", title))
.show_ui(ui, |ui| {
for w in &self.hwnd_lookup_windows {
if ui
.selectable_value(
&mut self.hwnd_lookup,
w.hwnd,
format!(
"{} - {:?} - {:?}",
w.hwnd,
w.exe().unwrap(),
w.title().unwrap()
),
)
.changed()
{
let response = komorebi_client::send_query(
&SocketMessage::DebugWindow(w.hwnd),
)
.unwrap();
let debug: RuleDebug =
serde_json::from_str(&response).unwrap();
self.hwnd_rule_debug = Some(debug);
};
}
});
});
if let Some(debug) = &self.hwnd_rule_debug {
ui.horizontal(|ui| {
json_view_ui(ui, &serde_json::to_string_pretty(&debug).unwrap());
});
}
ui.separator();
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("Borders", |ui| {
if ui
.toggle_value(
&mut self.border_config.active_window_border_enabled,
"Active Window Border",
)
.changed()
{
komorebi_client::send_message(&SocketMessage::ActiveWindowBorder(
self.border_config.active_window_border_enabled,
))
.unwrap();
}
ui.collapsing("Style", |ui| {
for option in [
ActiveWindowBorderStyle::System,
ActiveWindowBorderStyle::Rounded,
ActiveWindowBorderStyle::Square,
] {
if ui
.add(egui::SelectableLabel::new(
option == self.border_config.active_window_border_style,
option.to_string(),
))
.clicked()
{
komorebi_client::send_message(
&SocketMessage::ActiveWindowBorderStyle(option),
)
.unwrap();
self.border_config.active_window_border_style = option;
}
}
});
ui.collapsing("Width", |ui| {
if ui
.add(egui::Slider::new(
&mut self.border_config.border_width,
-10..=30,
))
.drag_stopped()
{
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,
-10..=30,
))
.drag_stopped()
{
komorebi_client::send_message(&SocketMessage::BorderOffset(
self.border_config.border_offset,
))
.unwrap();
};
});
ui.collapsing("Colours", |ui| {
ui.collapsing("Single", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.single,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::ActiveWindowBorderColour(
WindowKind::Single,
self.border_config.single.r() as u32,
self.border_config.single.g() as u32,
self.border_config.single.b() as u32,
),
)
.unwrap();
}
});
ui.collapsing("Monocle", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.monocle,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::ActiveWindowBorderColour(
WindowKind::Single,
self.border_config.monocle.r() as u32,
self.border_config.monocle.g() as u32,
self.border_config.monocle.b() as u32,
),
)
.unwrap();
}
});
ui.collapsing("Stack", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.border_config.stack,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::ActiveWindowBorderColour(
WindowKind::Single,
self.border_config.stack.r() as u32,
self.border_config.stack.g() as u32,
self.border_config.stack.b() as u32,
),
)
.unwrap();
}
});
});
});
ui.collapsing("Stackbar", |ui| {
for option in [
StackbarMode::Never,
StackbarMode::OnStack,
StackbarMode::Always,
] {
if ui
.add(egui::SelectableLabel::new(
option == self.stackbar_config.stackbar_mode,
option.to_string(),
))
.clicked()
{
komorebi_client::send_message(&SocketMessage::StackbarMode(option))
.unwrap();
self.stackbar_config.stackbar_mode = option;
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
}
}
ui.collapsing("Width", |ui| {
if ui
.add(egui::Slider::new(
&mut self.stackbar_config.stackbar_tab_width,
0..=600,
))
.drag_stopped()
{
komorebi_client::send_message(&SocketMessage::StackbarTabWidth(
self.stackbar_config.stackbar_tab_width,
))
.unwrap();
};
});
ui.collapsing("Height", |ui| {
if ui
.add(egui::Slider::new(
&mut self.stackbar_config.stackbar_height,
0..=50,
))
.drag_stopped()
{
komorebi_client::send_message(&SocketMessage::StackbarHeight(
self.stackbar_config.stackbar_height,
))
.unwrap();
};
});
ui.collapsing("Colours", |ui| {
ui.collapsing("Focused Text", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.stackbar_config.stackbar_focused_text_colour,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::StackbarFocusedTextColour(
self.stackbar_config.stackbar_focused_text_colour.r()
as u32,
self.stackbar_config.stackbar_focused_text_colour.g()
as u32,
self.stackbar_config.stackbar_focused_text_colour.b()
as u32,
),
)
.unwrap();
}
});
ui.collapsing("Unfocused Text", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.stackbar_config.stackbar_unfocused_text_colour,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::StackbarUnfocusedTextColour(
self.stackbar_config.stackbar_unfocused_text_colour.r()
as u32,
self.stackbar_config.stackbar_unfocused_text_colour.g()
as u32,
self.stackbar_config.stackbar_unfocused_text_colour.b()
as u32,
),
)
.unwrap();
}
});
ui.collapsing("Tab Background", |ui| {
if egui::color_picker::color_picker_color32(
ui,
&mut self.stackbar_config.stackbar_tab_background_colour,
Alpha::Opaque,
) {
komorebi_client::send_message(
&SocketMessage::StackbarBackgroundColour(
self.stackbar_config.stackbar_tab_background_colour.r()
as u32,
self.stackbar_config.stackbar_tab_background_colour.g()
as u32,
self.stackbar_config.stackbar_tab_background_colour.b()
as u32,
),
)
.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..=1000,
)
.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..=1000,
)
.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..=1000,
)
.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..=1000,
)
.text("Bottom"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::MonitorWorkAreaOffset(
monitor_idx,
monitor.work_area_offset,
),
)
.unwrap();
};
});
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();
}
ui.collapsing("Name", |ui| {
if ui
.text_edit_singleline(&mut workspace.name)
.lost_focus()
{
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(
option == workspace.layout,
option.to_string(),
))
.clicked()
{
komorebi_client::send_message(
&SocketMessage::WorkspaceLayout(
monitor_idx,
workspace_idx,
option,
),
)
.unwrap();
workspace.layout = option;
}
}
});
ui.collapsing("Container Padding", |ui| {
if ui
.add(
egui::Slider::new(
&mut workspace.container_padding,
-100..=100,
)
.text("Container Padding"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::ContainerPadding(
monitor_idx,
workspace_idx,
workspace.container_padding,
),
)
.unwrap();
komorebi_client::send_message(
&SocketMessage::Retile,
)
.unwrap();
};
});
ui.collapsing("Workspace Padding", |ui| {
if ui
.add(
egui::Slider::new(
&mut workspace.workspace_padding,
-100..=100,
)
.text("Workspace Padding"),
)
.drag_stopped()
{
komorebi_client::send_message(
&SocketMessage::WorkspacePadding(
monitor_idx,
workspace_idx,
workspace.workspace_padding,
),
)
.unwrap();
komorebi_client::send_message(
&SocketMessage::Retile,
)
.unwrap();
};
});
},
);
}
},
);
}
});
});
});
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.25-dev.0"
version = "0.1.25"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]

View File

@@ -18,6 +18,7 @@ pub struct Container {
#[getset(get = "pub")]
id: String,
windows: Ring<Window>,
#[serde(skip)]
#[getset(get = "pub", get_mut = "pub")]
stackbar: Option<Stackbar>,
}
@@ -124,7 +125,10 @@ impl Container {
let window = self.windows_mut().remove(idx);
if matches!(*STACKBAR_MODE.lock(), StackbarMode::OnStack) && self.windows().len() <= 1 {
self.stackbar = None;
if let Some(stackbar) = &self.stackbar {
let _ = WindowsApi::close_window(stackbar.hwnd());
self.stackbar = None;
}
}
if idx != 0 {
@@ -159,27 +163,39 @@ impl Container {
}
pub fn set_stackbar_mode(&mut self, mode: StackbarMode) {
self.stackbar = match mode {
StackbarMode::Always => Stackbar::create().ok(),
StackbarMode::Never => None,
StackbarMode::OnStack => {
if self.windows().len() > 1 && self.stackbar().is_none() {
Stackbar::create().ok()
} else {
None
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());
}
pub fn renew_stackbar(&mut self) {
match &self.stackbar {
None => {}
Some(stackbar) => {
if !WindowsApi::is_window(stackbar.hwnd()) {
self.stackbar = Stackbar::create().ok()
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()
}
}
}
}

View File

@@ -184,7 +184,8 @@ impl WindowManager {
}
match event {
WindowManagerEvent::Raise(_window) => {
WindowManagerEvent::Raise(window) => {
window.focus(false)?;
self.has_pending_raise_op = false;
}
WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => {
@@ -259,7 +260,7 @@ impl WindowManager {
already_moved_window_handles.remove(&window.hwnd);
}
WindowManagerEvent::FocusChange(_, window) => {
self.update_focused_workspace(true, false)?;
self.update_focused_workspace(self.mouse_follows_focus, false)?;
let workspace = self.focused_workspace_mut()?;
if !workspace

View File

@@ -64,29 +64,11 @@ use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_BY_BAR_HWNDS;
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct Stackbar {
pub(crate) hwnd: isize,
#[serde(skip)]
pub is_cloned: bool,
}
impl Drop for Stackbar {
fn drop(&mut self) {
if !self.is_cloned {
let _ = WindowsApi::close_window(self.hwnd());
}
}
}
impl Clone for Stackbar {
fn clone(&self) -> Self {
Self {
hwnd: self.hwnd,
is_cloned: true,
}
}
}
impl Stackbar {
unsafe extern "system" fn window_proc(
hwnd: HWND,
@@ -191,7 +173,6 @@ impl Stackbar {
Ok(Self {
hwnd: hwnd_receiver.recv()?.0,
..Default::default()
})
}

View File

@@ -222,7 +222,7 @@ impl From<&Monitor> for MonitorConfig {
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
/// The `komorebi.json` static configuration file reference for `v0.1.24`
/// The `komorebi.json` static configuration file reference for `v0.1.25`
pub struct StaticConfig {
/// DEPRECATED from v0.1.22: no longer required
#[serde(skip_serializing_if = "Option::is_none")]
@@ -304,22 +304,30 @@ pub struct StaticConfig {
/// Set display index preferences
#[serde(skip_serializing_if = "Option::is_none")]
pub display_index_preferences: Option<HashMap<usize, String>>,
/// Stackbar configuration options
#[serde(skip_serializing_if = "Option::is_none")]
pub stackbar: Option<StackbarConfig>,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct TabsConfig {
/// Width of a stackbar tab
width: Option<i32>,
/// Focused tab text colour
focused_text: Option<Colour>,
/// Unfocused tab text colour
unfocused_text: Option<Colour>,
/// Tab background colour
background: Option<Colour>,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct StackbarConfig {
/// Stackbar height
pub height: Option<i32>,
/// Stackbar mode
pub mode: Option<StackbarMode>,
/// Stackbar tab configuration options
pub tabs: Option<TabsConfig>,
}

View File

@@ -774,14 +774,11 @@ impl WindowManager {
pub fn raise_window_at_cursor_pos(&mut self) -> Result<()> {
let mut hwnd = None;
for monitor in self.monitors() {
for workspace in monitor.workspaces() {
if let Some(container_idx) = workspace.container_idx_from_current_point() {
if let Some(container) = workspace.containers().get(container_idx) {
if let Some(window) = container.focused_window() {
hwnd = Some(window.hwnd);
}
}
let workspace = self.focused_workspace()?;
if let Some(container_idx) = workspace.container_idx_from_current_point() {
if let Some(container) = workspace.containers().get(container_idx) {
if let Some(window) = container.focused_window() {
hwnd = Some(window.hwnd);
}
}
}

View File

@@ -131,6 +131,8 @@ impl Workspace {
}
self.set_layout_rules(all_rules);
self.tile = true;
}
if let Some(layout_rules) = &config.custom_layout_rules {
@@ -139,6 +141,8 @@ impl Workspace {
let rule = CustomLayout::from_path(pathbuf)?;
rules.push((*count, Layout::Custom(rule)));
}
self.tile = true;
}
Ok(())
@@ -876,7 +880,7 @@ impl Workspace {
self.containers_mut().remove(focused_idx);
self.resize_dimensions_mut().remove(focused_idx);
if focused_idx == self.containers().len() {
if focused_idx == self.containers().len() && focused_idx != 0 {
self.focus_container(focused_idx - 1);
}
} else {
@@ -894,6 +898,17 @@ impl Workspace {
fn enforce_resize_constraints(&mut self) {
match self.layout {
Layout::Default(DefaultLayout::BSP) => self.enforce_resize_constraints_for_bsp(),
Layout::Default(DefaultLayout::Columns) => self.enforce_resize_for_columns(),
Layout::Default(DefaultLayout::Rows) => self.enforce_resize_for_rows(),
Layout::Default(DefaultLayout::VerticalStack) => {
self.enforce_resize_for_vertical_stack();
}
Layout::Default(DefaultLayout::RightMainVerticalStack) => {
self.enforce_resize_for_right_vertical_stack();
}
Layout::Default(DefaultLayout::HorizontalStack) => {
self.enforce_resize_for_horizontal_stack();
}
Layout::Default(DefaultLayout::UltrawideVerticalStack) => {
self.enforce_resize_for_ultrawide();
}
@@ -927,6 +942,146 @@ impl Workspace {
}
}
fn enforce_resize_for_columns(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {
0 | 1 => self.enforce_no_resize(),
_ => {
let len = resize_dimensions.len();
for (i, rect) in resize_dimensions.iter_mut().enumerate() {
if let Some(rect) = rect {
rect.top = 0;
rect.bottom = 0;
if i == 0 {
rect.left = 0;
}
if i == len - 1 {
rect.right = 0;
}
}
}
}
}
}
fn enforce_resize_for_rows(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {
0 | 1 => self.enforce_no_resize(),
_ => {
let len = resize_dimensions.len();
for (i, rect) in resize_dimensions.iter_mut().enumerate() {
if let Some(rect) = rect {
rect.left = 0;
rect.right = 0;
if i == 0 {
rect.top = 0;
}
if i == len - 1 {
rect.bottom = 0;
}
}
}
}
}
}
fn enforce_resize_for_vertical_stack(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {
// Single window can not be resized at all
0 | 1 => self.enforce_no_resize(),
_ => {
// Zero is actually on the left
if let Some(mut left) = resize_dimensions[0] {
left.top = 0;
left.bottom = 0;
left.left = 0;
}
// Handle stack on the right
let stack_size = resize_dimensions[1..].len();
for (i, rect) in resize_dimensions[1..].iter_mut().enumerate() {
if let Some(rect) = rect {
// No containers can resize to the right
rect.right = 0;
// First container in stack cant resize up
if i == 0 {
rect.top = 0;
} else if i == stack_size - 1 {
// Last cant be resized to the bottom
rect.bottom = 0;
}
}
}
}
}
}
fn enforce_resize_for_right_vertical_stack(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {
// Single window can not be resized at all
0 | 1 => self.enforce_no_resize(),
_ => {
// Zero is actually on the right
if let Some(mut left) = resize_dimensions[1] {
left.top = 0;
left.bottom = 0;
left.right = 0;
}
// Handle stack on the right
let stack_size = resize_dimensions[1..].len();
for (i, rect) in resize_dimensions[1..].iter_mut().enumerate() {
if let Some(rect) = rect {
// No containers can resize to the left
rect.left = 0;
// First container in stack cant resize up
if i == 0 {
rect.top = 0;
} else if i == stack_size - 1 {
// Last cant be resized to the bottom
rect.bottom = 0;
}
}
}
}
}
}
fn enforce_resize_for_horizontal_stack(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {
0 | 1 => self.enforce_no_resize(),
_ => {
if let Some(mut left) = resize_dimensions[0] {
left.top = 0;
left.left = 0;
left.right = 0;
}
let stack_size = resize_dimensions[1..].len();
for (i, rect) in resize_dimensions[1..].iter_mut().enumerate() {
if let Some(rect) = rect {
rect.bottom = 0;
if i == 0 {
rect.left = 0;
}
if i == stack_size - 1 {
rect.right = 0;
}
}
}
}
}
}
fn enforce_resize_for_ultrawide(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic-no-console"
version = "0.1.25-dev.0"
version = "0.1.25"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.25-dev.0"
version = "0.1.25"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]

View File

@@ -1257,9 +1257,15 @@ fn main() -> Result<()> {
let subcommands = cli.get_subcommands_mut();
std::fs::create_dir_all("docs/cli")?;
let ignore = [
"docgen",
"alt-focus-hack",
"identify-border-overflow-application",
];
for cmd in subcommands {
let name = cmd.get_name().to_string();
if name != "docgen" {
if !ignore.contains(&name.as_str()) {
let help_text = cmd.render_long_help().to_string();
let outpath = format!("docs/cli/{name}.md");
let markdown = format!("# {name}\n\n```\n{help_text}\n```");

View File

@@ -45,160 +45,162 @@ plugins:
nav:
- Komorebi:
- index.md
- Design: design.md
- index.md
- Design: design.md
- Getting started:
- Installation: installation.md
- Example configurations: example-configurations.md
- Installation: installation.md
- Example configurations: example-configurations.md
- Common workflows:
- common-workflows/komorebi-config-home.md
- common-workflows/active-window-border.md
- common-workflows/stackbar.md
- common-workflows/remove-gaps.md
- common-workflows/ignore-windows.md
- common-workflows/force-manage-windows.md
- common-workflows/tray-and-multi-window-applications.md
- common-workflows/focus-follows-mouse.md
- common-workflows/mouse-follows-focus.md
- common-workflows/custom-layouts.md
- common-workflows/dynamic-layout-switching.md
# - common-workflows/autohotkey.md
- common-workflows/komorebi-config-home.md
- common-workflows/active-window-border.md
- common-workflows/stackbar.md
- common-workflows/remove-gaps.md
- common-workflows/ignore-windows.md
- common-workflows/force-manage-windows.md
- common-workflows/tray-and-multi-window-applications.md
- common-workflows/focus-follows-mouse.md
- common-workflows/mouse-follows-focus.md
- common-workflows/custom-layouts.md
- common-workflows/dynamic-layout-switching.md
# - common-workflows/autohotkey.md
- Release notes:
- release/v0-1-22.md
- release/v0-1-22.md
- Configuration reference: https://komorebi.lgug2z.com/schema
- CLI reference:
- cli/quickstart.md
- cli/start.md
- cli/stop.md
- cli/check.md
- cli/configuration.md
- cli/whkdrc.md
- cli/state.md
- cli/visible-windows.md
- cli/query.md
- cli/subscribe-socket.md
- cli/unsubscribe-socket.md
- cli/subscribe-pipe.md
- cli/unsubscribe-pipe.md
- cli/log.md
- cli/quick-save-resize.md
- cli/quick-load-resize.md
- cli/save-resize.md
- cli/load-resize.md
- cli/focus.md
- cli/move.md
- cli/minimize.md
- cli/close.md
- cli/force-focus.md
- cli/cycle-focus.md
- cli/cycle-move.md
- cli/stack.md
- cli/resize-edge.md
- cli/resize-axis.md
- cli/unstack.md
- cli/cycle-stack.md
- cli/move-to-monitor.md
- cli/cycle-move-to-monitor.md
- cli/move-to-workspace.md
- cli/move-to-named-workspace.md
- cli/cycle-move-to-workspace.md
- cli/send-to-monitor.md
- cli/cycle-send-to-monitor.md
- cli/send-to-workspace.md
- cli/send-to-named-workspace.md
- cli/cycle-send-to-workspace.md
- cli/send-to-monitor-workspace.md
- cli/focus-monitor.md
- cli/focus-last-workspace.md
- cli/focus-workspace.md
- cli/focus-workspaces.md
- cli/focus-monitor-workspace.md
- cli/focus-named-workspace.md
- cli/cycle-monitor.md
- cli/cycle-workspace.md
- cli/move-workspace-to-monitor.md
- cli/swap-workspaces-with-monitor.md
- cli/new-workspace.md
- cli/resize-delta.md
- cli/invisible-borders.md
- cli/global-work-area-offset.md
- cli/monitor-work-area-offset.md
- cli/focused-workspace-container-padding.md
- cli/focused-workspace-padding.md
- cli/adjust-container-padding.md
- cli/adjust-workspace-padding.md
- cli/change-layout.md
- cli/cycle-layout.md
- cli/load-custom-layout.md
- cli/flip-layout.md
- cli/promote.md
- cli/promote-focus.md
- cli/retile.md
- cli/monitor-index-preference.md
- cli/display-index-preference.md
- cli/ensure-workspaces.md
- cli/ensure-named-workspaces.md
- cli/container-padding.md
- cli/named-workspace-container-padding.md
- cli/workspace-padding.md
- cli/named-workspace-padding.md
- cli/workspace-layout.md
- cli/named-workspace-layout.md
- cli/workspace-custom-layout.md
- cli/named-workspace-custom-layout.md
- cli/workspace-layout-rule.md
- cli/named-workspace-layout-rule.md
- cli/workspace-custom-layout-rule.md
- cli/named-workspace-custom-layout-rule.md
- cli/clear-workspace-layout-rules.md
- cli/clear-named-workspace-layout-rules.md
- cli/workspace-tiling.md
- cli/named-workspace-tiling.md
- cli/workspace-name.md
- cli/toggle-window-container-behaviour.md
- cli/toggle-pause.md
- cli/toggle-tiling.md
- cli/toggle-float.md
- cli/toggle-monocle.md
- cli/toggle-maximize.md
- cli/restore-windows.md
- cli/manage.md
- cli/unmanage.md
- cli/reload-configuration.md
- cli/watch-configuration.md
- cli/complete-configuration.md
- cli/window-hiding-behaviour.md
- cli/cross-monitor-move-behaviour.md
- cli/toggle-cross-monitor-move-behaviour.md
- cli/unmanaged-window-operation-behaviour.md
- cli/float-rule.md
- cli/manage-rule.md
- cli/initial-workspace-rule.md
- cli/initial-named-workspace-rule.md
- cli/workspace-rule.md
- cli/named-workspace-rule.md
- cli/identify-object-name-change-application.md
- cli/identify-tray-application.md
- cli/identify-layered-application.md
- cli/remove-title-bar.md
- cli/toggle-title-bars.md
- cli/active-window-border.md
- cli/active-window-border-colour.md
- cli/active-window-border-width.md
- cli/active-window-border-offset.md
- cli/focus-follows-mouse.md
- cli/toggle-focus-follows-mouse.md
- cli/mouse-follows-focus.md
- cli/toggle-mouse-follows-focus.md
- cli/ahk-library.md
- cli/ahk-app-specific-configuration.md
- cli/pwsh-app-specific-configuration.md
- cli/format-app-specific-configuration.md
- cli/fetch-app-specific-configuration.md
- cli/application-specific-configuration-schema.md
- cli/notification-schema.md
- cli/socket-schema.md
- cli/static-config-schema.md
- cli/generate-static-config.md
- cli/enable-autostart.md
- cli/disable-autostart.md
- cli/quickstart.md
- cli/start.md
- cli/stop.md
- cli/check.md
- cli/configuration.md
- cli/whkdrc.md
- cli/state.md
- cli/global-state.md
- cli/visible-windows.md
- cli/query.md
- cli/subscribe-socket.md
- cli/unsubscribe-socket.md
- cli/subscribe-pipe.md
- cli/unsubscribe-pipe.md
- cli/log.md
- cli/quick-save-resize.md
- cli/quick-load-resize.md
- cli/save-resize.md
- cli/load-resize.md
- cli/focus.md
- cli/move.md
- cli/minimize.md
- cli/close.md
- cli/force-focus.md
- cli/cycle-focus.md
- cli/cycle-move.md
- cli/stack.md
- cli/resize-edge.md
- cli/resize-axis.md
- cli/unstack.md
- cli/cycle-stack.md
- cli/move-to-monitor.md
- cli/cycle-move-to-monitor.md
- cli/move-to-workspace.md
- cli/move-to-named-workspace.md
- cli/cycle-move-to-workspace.md
- cli/send-to-monitor.md
- cli/cycle-send-to-monitor.md
- cli/send-to-workspace.md
- cli/send-to-named-workspace.md
- cli/cycle-send-to-workspace.md
- cli/send-to-monitor-workspace.md
- cli/move-to-monitor-workspace.md
- cli/focus-monitor.md
- cli/focus-last-workspace.md
- cli/focus-workspace.md
- cli/focus-workspaces.md
- cli/focus-monitor-workspace.md
- cli/focus-named-workspace.md
- cli/cycle-monitor.md
- cli/cycle-workspace.md
- cli/move-workspace-to-monitor.md
- cli/swap-workspaces-with-monitor.md
- cli/new-workspace.md
- cli/resize-delta.md
- cli/invisible-borders.md
- cli/global-work-area-offset.md
- cli/monitor-work-area-offset.md
- cli/focused-workspace-container-padding.md
- cli/focused-workspace-padding.md
- cli/adjust-container-padding.md
- cli/adjust-workspace-padding.md
- cli/change-layout.md
- cli/cycle-layout.md
- cli/load-custom-layout.md
- cli/flip-layout.md
- cli/promote.md
- cli/promote-focus.md
- cli/retile.md
- cli/monitor-index-preference.md
- cli/display-index-preference.md
- cli/ensure-workspaces.md
- cli/ensure-named-workspaces.md
- cli/container-padding.md
- cli/named-workspace-container-padding.md
- cli/workspace-padding.md
- cli/named-workspace-padding.md
- cli/workspace-layout.md
- cli/named-workspace-layout.md
- cli/workspace-custom-layout.md
- cli/named-workspace-custom-layout.md
- cli/workspace-layout-rule.md
- cli/named-workspace-layout-rule.md
- cli/workspace-custom-layout-rule.md
- cli/named-workspace-custom-layout-rule.md
- cli/clear-workspace-layout-rules.md
- cli/clear-named-workspace-layout-rules.md
- cli/workspace-tiling.md
- cli/named-workspace-tiling.md
- cli/workspace-name.md
- cli/toggle-window-container-behaviour.md
- cli/toggle-pause.md
- cli/toggle-tiling.md
- cli/toggle-float.md
- cli/toggle-monocle.md
- cli/toggle-maximize.md
- cli/restore-windows.md
- cli/manage.md
- cli/unmanage.md
- cli/reload-configuration.md
- cli/watch-configuration.md
- cli/complete-configuration.md
- cli/window-hiding-behaviour.md
- cli/cross-monitor-move-behaviour.md
- cli/toggle-cross-monitor-move-behaviour.md
- cli/unmanaged-window-operation-behaviour.md
- cli/float-rule.md
- cli/manage-rule.md
- cli/initial-workspace-rule.md
- cli/initial-named-workspace-rule.md
- cli/workspace-rule.md
- cli/named-workspace-rule.md
- cli/identify-object-name-change-application.md
- cli/identify-tray-application.md
- cli/identify-layered-application.md
- cli/remove-title-bar.md
- cli/toggle-title-bars.md
- cli/active-window-border.md
- cli/active-window-border-colour.md
- cli/active-window-border-width.md
- cli/active-window-border-offset.md
- cli/focus-follows-mouse.md
- cli/toggle-focus-follows-mouse.md
- cli/mouse-follows-focus.md
- cli/toggle-mouse-follows-focus.md
- cli/ahk-library.md
- cli/ahk-app-specific-configuration.md
- cli/pwsh-app-specific-configuration.md
- cli/format-app-specific-configuration.md
- cli/fetch-app-specific-configuration.md
- cli/application-specific-configuration-schema.md
- cli/notification-schema.md
- cli/socket-schema.md
- cli/static-config-schema.md
- cli/generate-static-config.md
- cli/enable-autostart.md
- cli/disable-autostart.md

View File

@@ -1,7 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StaticConfig",
"description": "The `komorebi.json` static configuration file reference for `v0.1.20`",
"description": "The `komorebi.json` static configuration file reference for `v0.1.25`",
"type": "object",
"properties": {
"active_window_border": {
@@ -775,7 +775,8 @@
"VerticalStack",
"HorizontalStack",
"UltrawideVerticalStack",
"Grid"
"Grid",
"RightMainVerticalStack"
]
},
"layout_rules": {
@@ -790,7 +791,8 @@
"VerticalStack",
"HorizontalStack",
"UltrawideVerticalStack",
"Grid"
"Grid",
"RightMainVerticalStack"
]
}
},
@@ -942,13 +944,16 @@
"format": "int32"
},
"stackbar": {
"description": "Stackbar configuration options",
"type": "object",
"properties": {
"height": {
"description": "Stackbar height",
"type": "integer",
"format": "int32"
},
"mode": {
"description": "Stackbar mode",
"type": "string",
"enum": [
"Always",
@@ -957,9 +962,11 @@
]
},
"tabs": {
"description": "Stackbar tab configuration options",
"type": "object",
"properties": {
"background": {
"description": "Tab background colour",
"anyOf": [
{
"description": "Colour represented as RGB",
@@ -997,6 +1004,7 @@
]
},
"focused_text": {
"description": "Focused tab text colour",
"anyOf": [
{
"description": "Colour represented as RGB",
@@ -1034,6 +1042,7 @@
]
},
"unfocused_text": {
"description": "Unfocused tab text colour",
"anyOf": [
{
"description": "Colour represented as RGB",
@@ -1071,6 +1080,7 @@
]
},
"width": {
"description": "Width of a stackbar tab",
"type": "integer",
"format": "int32"
}