mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-23 00:58:37 +02:00
feat(wm): initial commit
One week of blissful, in-the-zone coding, applying all of the lessons learnt from the development of yatta.
This commit is contained in:
34
komorebi-core/src/cycle_direction.rs
Normal file
34
komorebi-core/src/cycle_direction.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use clap::Clap;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[derive(Clap)]
|
||||
pub enum CycleDirection {
|
||||
Previous,
|
||||
Next,
|
||||
}
|
||||
|
||||
impl CycleDirection {
|
||||
pub fn next_idx(&self, idx: usize, len: usize) -> usize {
|
||||
match self {
|
||||
CycleDirection::Previous => {
|
||||
if idx == 0 {
|
||||
len - 1
|
||||
} else {
|
||||
idx - 1
|
||||
}
|
||||
}
|
||||
CycleDirection::Next => {
|
||||
if idx == len - 1 {
|
||||
0
|
||||
} else {
|
||||
idx + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
193
komorebi-core/src/layout.rs
Normal file
193
komorebi-core/src/layout.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use clap::Clap;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
use crate::Rect;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[derive(Clap)]
|
||||
pub enum Layout {
|
||||
BSP,
|
||||
Columns,
|
||||
Rows,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[derive(Clap)]
|
||||
pub enum LayoutFlip {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
HorizontalAndVertical,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn calculate(
|
||||
&self,
|
||||
area: &Rect,
|
||||
count: usize,
|
||||
container_padding: Option<i32>,
|
||||
layout_flip: Option<LayoutFlip>,
|
||||
) -> Vec<Rect> {
|
||||
let mut dimensions = match self {
|
||||
Layout::BSP => self.fibonacci(area, count, layout_flip),
|
||||
Layout::Columns => {
|
||||
let right = area.right / count as i32;
|
||||
let mut left = 0;
|
||||
|
||||
let mut layouts: Vec<Rect> = vec![];
|
||||
for _ in 0..count {
|
||||
layouts.push(Rect {
|
||||
left: area.left + left,
|
||||
top: area.top,
|
||||
right,
|
||||
bottom: area.bottom,
|
||||
});
|
||||
|
||||
left += right;
|
||||
}
|
||||
|
||||
layouts
|
||||
}
|
||||
Layout::Rows => {
|
||||
let bottom = area.bottom / count as i32;
|
||||
let mut top = 0;
|
||||
|
||||
let mut layouts: Vec<Rect> = vec![];
|
||||
for _ in 0..count {
|
||||
layouts.push(Rect {
|
||||
left: area.left,
|
||||
top: area.top + top,
|
||||
right: area.right,
|
||||
bottom,
|
||||
});
|
||||
|
||||
top += bottom;
|
||||
}
|
||||
|
||||
layouts
|
||||
}
|
||||
};
|
||||
|
||||
dimensions
|
||||
.iter_mut()
|
||||
.for_each(|l| l.add_padding(container_padding));
|
||||
|
||||
dimensions
|
||||
}
|
||||
|
||||
pub fn fibonacci(
|
||||
&self,
|
||||
area: &Rect,
|
||||
count: usize,
|
||||
layout_flip: Option<LayoutFlip>,
|
||||
) -> Vec<Rect> {
|
||||
let mut dimensions = vec![];
|
||||
|
||||
for _ in 0..count {
|
||||
dimensions.push(Rect::default())
|
||||
}
|
||||
|
||||
let mut left = area.left;
|
||||
let mut top = area.top;
|
||||
let mut bottom = area.bottom;
|
||||
let mut right = area.right;
|
||||
|
||||
for i in 0..count {
|
||||
if i % 2 != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let half_width = right / 2;
|
||||
let half_height = bottom / 2;
|
||||
|
||||
let (main_x, alt_x, new_y, alt_y);
|
||||
|
||||
match layout_flip {
|
||||
Some(flip) => match flip {
|
||||
LayoutFlip::Horizontal => {
|
||||
main_x = left + half_width;
|
||||
alt_x = left;
|
||||
|
||||
new_y = top + half_height;
|
||||
alt_y = top;
|
||||
}
|
||||
LayoutFlip::Vertical => {
|
||||
new_y = top;
|
||||
alt_y = top + half_height;
|
||||
|
||||
main_x = left;
|
||||
alt_x = left + half_width;
|
||||
}
|
||||
LayoutFlip::HorizontalAndVertical => {
|
||||
main_x = left + half_width;
|
||||
alt_x = left;
|
||||
new_y = top;
|
||||
alt_y = top + half_height;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
main_x = left;
|
||||
alt_x = left + half_width;
|
||||
new_y = top + half_height;
|
||||
alt_y = top;
|
||||
}
|
||||
}
|
||||
|
||||
match count - i {
|
||||
1 => {
|
||||
set_dimensions(&mut dimensions[i], left, top, right, bottom);
|
||||
}
|
||||
2 => {
|
||||
set_dimensions(&mut dimensions[i], main_x, top, half_width, bottom);
|
||||
set_dimensions(&mut dimensions[i + 1], alt_x, top, half_width, bottom);
|
||||
}
|
||||
_ => {
|
||||
set_dimensions(&mut dimensions[i], main_x, top, half_width, bottom);
|
||||
set_dimensions(
|
||||
&mut dimensions[i + 1],
|
||||
alt_x,
|
||||
alt_y,
|
||||
half_width,
|
||||
half_height,
|
||||
);
|
||||
|
||||
left = alt_x;
|
||||
top = new_y;
|
||||
right = half_width;
|
||||
bottom = half_height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dimensions
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn next(&mut self) {
|
||||
match self {
|
||||
Layout::BSP => *self = Layout::Columns,
|
||||
Layout::Columns => *self = Layout::Rows,
|
||||
Layout::Rows => *self = Layout::BSP,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn previous(&mut self) {
|
||||
match self {
|
||||
Layout::BSP => *self = Layout::Rows,
|
||||
Layout::Columns => *self = Layout::BSP,
|
||||
Layout::Rows => *self = Layout::Columns,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dimensions(rect: &mut Rect, left: i32, top: i32, right: i32, bottom: i32) {
|
||||
rect.bottom = bottom;
|
||||
rect.right = right;
|
||||
rect.left = left;
|
||||
rect.top = top;
|
||||
}
|
||||
105
komorebi-core/src/lib.rs
Normal file
105
komorebi-core/src/lib.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::Clap;
|
||||
use color_eyre::Result;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
pub use cycle_direction::CycleDirection;
|
||||
pub use layout::Layout;
|
||||
pub use layout::LayoutFlip;
|
||||
pub use operation_direction::OperationDirection;
|
||||
pub use rect::Rect;
|
||||
|
||||
pub mod cycle_direction;
|
||||
pub mod layout;
|
||||
pub mod operation_direction;
|
||||
pub mod rect;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Display)]
|
||||
pub enum SocketMessage {
|
||||
// Window / Container Commands
|
||||
FocusWindow(OperationDirection),
|
||||
MoveWindow(OperationDirection),
|
||||
StackWindow(OperationDirection),
|
||||
UnstackWindow,
|
||||
CycleStack(CycleDirection),
|
||||
MoveContainerToMonitorNumber(usize),
|
||||
MoveContainerToWorkspaceNumber(usize),
|
||||
Promote,
|
||||
ToggleFloat,
|
||||
ToggleMonocle,
|
||||
// Current Workspace Commands
|
||||
AdjustContainerPadding(Sizing, i32),
|
||||
AdjustWorkspacePadding(Sizing, i32),
|
||||
ChangeLayout(Layout),
|
||||
FlipLayout(LayoutFlip),
|
||||
// Monitor and Workspace Commands
|
||||
Stop,
|
||||
TogglePause,
|
||||
Retile,
|
||||
FocusMonitorNumber(usize),
|
||||
FocusWorkspaceNumber(usize),
|
||||
ContainerPadding(usize, usize, i32),
|
||||
WorkspacePadding(usize, usize, i32),
|
||||
WorkspaceName(usize, usize, String),
|
||||
SetLayout(usize, usize, Layout),
|
||||
// Configuration
|
||||
FloatClass(String),
|
||||
FloatExe(String),
|
||||
FloatTitle(String),
|
||||
// TODO: Add some state query commands
|
||||
}
|
||||
|
||||
impl SocketMessage {
|
||||
pub fn as_bytes(&self) -> Result<Vec<u8>> {
|
||||
Ok(serde_json::to_string(self)?.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||
Ok(serde_json::from_slice(bytes)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SocketMessage {
|
||||
type Err = serde_json::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[derive(Clap)]
|
||||
pub enum Sizing {
|
||||
Increase,
|
||||
Decrease,
|
||||
}
|
||||
|
||||
impl Sizing {
|
||||
pub fn adjust_by(&self, value: i32, adjustment: i32) -> i32 {
|
||||
match self {
|
||||
Sizing::Increase => value + adjustment,
|
||||
Sizing::Decrease => {
|
||||
if value > 0 && value - adjustment >= 0 {
|
||||
value - adjustment
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[derive(Clap)]
|
||||
pub enum ResizeEdge {
|
||||
Left,
|
||||
Top,
|
||||
Right,
|
||||
Bottom,
|
||||
}
|
||||
91
komorebi-core/src/operation_direction.rs
Normal file
91
komorebi-core/src/operation_direction.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use clap::Clap;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
use crate::Layout;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[derive(Clap)]
|
||||
pub enum OperationDirection {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
impl OperationDirection {
|
||||
pub fn can_resize(&self, layout: Layout, idx: usize, len: usize) -> bool {
|
||||
match layout {
|
||||
Layout::BSP => match self {
|
||||
Self::Left => len != 0 && idx != 0,
|
||||
Self::Up => len > 2 && idx != 0 && idx != 1,
|
||||
Self::Right => len > 1 && idx % 2 == 0 && idx != len - 1,
|
||||
Self::Down => len > 2 && idx != len - 1 && idx % 2 != 0,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self, layout: Layout, idx: usize, len: usize) -> bool {
|
||||
match self {
|
||||
OperationDirection::Up => match layout {
|
||||
Layout::BSP => len > 2 && idx != 0 && idx != 1,
|
||||
Layout::Columns => false,
|
||||
Layout::Rows => idx != 0,
|
||||
},
|
||||
OperationDirection::Down => match layout {
|
||||
Layout::BSP => len > 2 && idx != len - 1 && idx % 2 != 0,
|
||||
Layout::Columns => false,
|
||||
Layout::Rows => idx != len - 1,
|
||||
},
|
||||
OperationDirection::Left => match layout {
|
||||
Layout::BSP => len > 1 && idx != 0,
|
||||
Layout::Columns => idx != 0,
|
||||
Layout::Rows => false,
|
||||
},
|
||||
OperationDirection::Right => match layout {
|
||||
Layout::BSP => len > 1 && idx % 2 == 0,
|
||||
Layout::Columns => idx != len - 1,
|
||||
Layout::Rows => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_idx(&self, layout: Layout, idx: usize) -> usize {
|
||||
match self {
|
||||
OperationDirection::Up => match layout {
|
||||
Layout::BSP => {
|
||||
if idx % 2 == 0 {
|
||||
idx - 1
|
||||
} else {
|
||||
idx - 2
|
||||
}
|
||||
}
|
||||
Layout::Columns => unreachable!(),
|
||||
Layout::Rows => idx - 1,
|
||||
},
|
||||
OperationDirection::Down => match layout {
|
||||
Layout::BSP | Layout::Rows => idx + 1,
|
||||
Layout::Columns => unreachable!(),
|
||||
},
|
||||
OperationDirection::Left => match layout {
|
||||
Layout::BSP => {
|
||||
if idx % 2 == 0 {
|
||||
idx - 2
|
||||
} else {
|
||||
idx - 1
|
||||
}
|
||||
}
|
||||
Layout::Columns => idx - 1,
|
||||
Layout::Rows => unreachable!(),
|
||||
},
|
||||
OperationDirection::Right => match layout {
|
||||
Layout::BSP | Layout::Columns => idx + 1,
|
||||
Layout::Rows => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
49
komorebi-core/src/rect.rs
Normal file
49
komorebi-core/src/rect.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use bindings::Windows::Win32::Foundation::RECT;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Rect {
|
||||
pub left: i32,
|
||||
pub top: i32,
|
||||
pub right: i32,
|
||||
pub bottom: i32,
|
||||
}
|
||||
|
||||
impl Default for Rect {
|
||||
fn default() -> Self {
|
||||
Rect {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RECT> for Rect {
|
||||
fn from(rect: RECT) -> Self {
|
||||
Rect {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
right: rect.right - rect.left,
|
||||
bottom: rect.bottom - rect.top,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn add_padding(&mut self, padding: Option<i32>) {
|
||||
if let Some(padding) = padding {
|
||||
self.left += padding;
|
||||
self.top += padding;
|
||||
self.right -= padding * 2;
|
||||
self.bottom -= padding * 2;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, point: (i32, i32)) -> bool {
|
||||
point.0 >= self.left
|
||||
&& point.0 <= self.left + self.right
|
||||
&& point.1 >= self.top
|
||||
&& point.1 <= self.top + self.bottom
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user