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:
LGUG2Z
2021-07-29 16:18:06 -07:00
commit 61cee458a1
32 changed files with 5593 additions and 0 deletions

View 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
View 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
View 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,
}

View 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
View 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
}
}