mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Add Rust layouts
This commit is contained in:
parent
ab2b3ee13b
commit
a98777c11e
6 changed files with 1154 additions and 35 deletions
|
@ -283,11 +283,11 @@ function builtins.master_stack:layout(args)
|
|||
|
||||
if self.master_side == "left" or self.master_side == "right" then
|
||||
coord = master_rect.y
|
||||
len = master_rect.height
|
||||
len = master_rect.height // (master_slice_count + 1)
|
||||
axis = "horizontal"
|
||||
else
|
||||
coord = master_rect.x
|
||||
len = master_rect.width
|
||||
len = master_rect.width // (master_slice_count + 1)
|
||||
axis = "vertical"
|
||||
end
|
||||
|
||||
|
@ -680,23 +680,21 @@ function builtins.fair:layout(args)
|
|||
table.insert(geos, rect)
|
||||
elseif win_count == 2 then
|
||||
local len
|
||||
local coord
|
||||
if self.direction == "vertical" then
|
||||
len = rect.width
|
||||
coord = rect.x
|
||||
else
|
||||
len = rect.height
|
||||
coord = rect.y
|
||||
end
|
||||
-- Two windows is special cased to create a new line rather than increase to 2 in a line
|
||||
local rect1, rect2 = rect:split_at(self.direction, len // 2 - gaps // 2, gaps)
|
||||
local rect1, rect2 = rect:split_at(self.direction, coord + len // 2 - gaps // 2, gaps)
|
||||
if rect1 and rect2 then
|
||||
table.insert(geos, rect1)
|
||||
table.insert(geos, rect2)
|
||||
end
|
||||
else
|
||||
-- 3 / 1
|
||||
-- 7 / 2
|
||||
-- 13 / 3
|
||||
-- 21 / 4
|
||||
|
||||
local line_count = math.floor(math.sqrt(win_count) + 0.5)
|
||||
local wins_per_line = {}
|
||||
local max_per_line = line_count
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use pinnacle_api::layout::{
|
||||
CornerLayout, DwindleLayout, FairLayout, LayoutManager, MasterStackLayout, SpiralLayout,
|
||||
};
|
||||
use pinnacle_api::signal::WindowSignal;
|
||||
use pinnacle_api::xkbcommon::xkb::Keysym;
|
||||
use pinnacle_api::{
|
||||
|
@ -15,6 +18,7 @@ async fn main() {
|
|||
input,
|
||||
output,
|
||||
tag,
|
||||
layout,
|
||||
} = modules;
|
||||
|
||||
let mod_key = Mod::Ctrl;
|
||||
|
@ -78,6 +82,54 @@ async fn main() {
|
|||
// You can define window rules to get windows to open with desired properties.
|
||||
// See `pinnacle_api::window::rules` in the docs for more information.
|
||||
|
||||
// Layouts
|
||||
|
||||
let master_stack = Box::<MasterStackLayout>::default();
|
||||
let dwindle = Box::<DwindleLayout>::default();
|
||||
let spiral = Box::<SpiralLayout>::default();
|
||||
let corner = Box::<CornerLayout>::default();
|
||||
let fair = Box::<FairLayout>::default();
|
||||
|
||||
let layout_requester = layout.set_manager(layout.new_cycling_manager([
|
||||
master_stack as _,
|
||||
dwindle as _,
|
||||
spiral as _,
|
||||
corner as _,
|
||||
fair as _,
|
||||
]));
|
||||
|
||||
let mut layout_requester_clone = layout_requester.clone();
|
||||
|
||||
// `mod_key + space` cycles to the next layout
|
||||
input.keybind([mod_key], Keysym::space, move || {
|
||||
let Some(focused_op) = output.get_focused() else { return };
|
||||
let Some(first_active_tag) = focused_op
|
||||
.tags()
|
||||
.into_iter()
|
||||
.find(|tg| tg.active().unwrap_or(false))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
layout_requester.cycle_layout_forward(&first_active_tag);
|
||||
layout_requester.request_layout_on_output(&focused_op);
|
||||
});
|
||||
|
||||
// `mod_key + shift + space` cycles to the previous layout
|
||||
input.keybind([mod_key, Mod::Shift], Keysym::space, move || {
|
||||
let Some(focused_op) = output.get_focused() else { return };
|
||||
let Some(first_active_tag) = focused_op
|
||||
.tags()
|
||||
.into_iter()
|
||||
.find(|tg| tg.active().unwrap_or(false))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
layout_requester_clone.cycle_layout_backward(&first_active_tag);
|
||||
layout_requester_clone.request_layout_on_output(&focused_op);
|
||||
});
|
||||
|
||||
// Tags
|
||||
|
||||
let tag_names = ["1", "2", "3", "4", "5"];
|
||||
|
@ -92,30 +144,6 @@ async fn main() {
|
|||
|
||||
process.spawn_once([terminal]);
|
||||
|
||||
// Create a layout cycler to cycle through the given layouts
|
||||
let LayoutCycler {
|
||||
prev: layout_prev,
|
||||
next: layout_next,
|
||||
} = tag.new_layout_cycler([
|
||||
Layout::MasterStack,
|
||||
Layout::Dwindle,
|
||||
Layout::Spiral,
|
||||
Layout::CornerTopLeft,
|
||||
Layout::CornerTopRight,
|
||||
Layout::CornerBottomLeft,
|
||||
Layout::CornerBottomRight,
|
||||
]);
|
||||
|
||||
// `mod_key + space` cycles to the next layout
|
||||
input.keybind([mod_key], Keysym::space, move || {
|
||||
layout_next(None);
|
||||
});
|
||||
|
||||
// `mod_key + shift + space` cycles to the previous layout
|
||||
input.keybind([mod_key, Mod::Shift], Keysym::space, move || {
|
||||
layout_prev(None);
|
||||
});
|
||||
|
||||
for tag_name in tag_names {
|
||||
// `mod_key + 1-5` switches to tag "1" to "5"
|
||||
input.keybind([mod_key], tag_name, move || {
|
||||
|
|
995
api/rust/src/layout.rs
Normal file
995
api/rust/src/layout.rs
Normal file
|
@ -0,0 +1,995 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//! Layout management.
|
||||
//!
|
||||
//! TODO:
|
||||
|
||||
#![allow(missing_docs)] // TODO:
|
||||
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use pinnacle_api_defs::pinnacle::layout::v0alpha1::{
|
||||
layout_request::{Body, ExplicitLayout, Geometries},
|
||||
layout_service_client::LayoutServiceClient,
|
||||
LayoutRequest,
|
||||
};
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
|
||||
use tokio_stream::StreamExt;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::{
|
||||
block_on_tokio,
|
||||
output::OutputHandle,
|
||||
tag::TagHandle,
|
||||
util::{Axis, Geometry},
|
||||
window::WindowHandle,
|
||||
OUTPUT, TAG, WINDOW,
|
||||
};
|
||||
|
||||
/// A struct that allows you to add and remove tags and get [`TagHandle`]s.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Layout {
|
||||
layout_client: LayoutServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub(crate) fn new(channel: Channel) -> Self {
|
||||
Self {
|
||||
layout_client: LayoutServiceClient::new(channel.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_cycling_manager(
|
||||
&self,
|
||||
layouts: impl IntoIterator<Item = Box<dyn LayoutGenerator + Send>>,
|
||||
) -> CyclingLayoutManager {
|
||||
CyclingLayoutManager {
|
||||
layouts: layouts.into_iter().collect(),
|
||||
tag_indices: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_manager<M>(&self, manager: M) -> LayoutRequester<M>
|
||||
where
|
||||
M: LayoutManager + Send + 'static,
|
||||
{
|
||||
let (from_client, to_server) = unbounded_channel::<LayoutRequest>();
|
||||
let to_server_stream = tokio_stream::wrappers::UnboundedReceiverStream::new(to_server);
|
||||
let mut from_server = block_on_tokio(self.layout_client.clone().layout(to_server_stream))
|
||||
.expect("TODO")
|
||||
.into_inner();
|
||||
|
||||
let from_client_clone = from_client.clone();
|
||||
|
||||
let manager = Arc::new(tokio::sync::Mutex::new(manager));
|
||||
|
||||
let requester = LayoutRequester {
|
||||
sender: from_client_clone,
|
||||
manager: manager.clone(),
|
||||
};
|
||||
|
||||
let thing = async move {
|
||||
while let Some(Ok(response)) = from_server.next().await {
|
||||
let args = LayoutArgs {
|
||||
output: OUTPUT.get().unwrap().new_handle(response.output_name()),
|
||||
windows: response
|
||||
.window_ids
|
||||
.into_iter()
|
||||
.map(|id| WINDOW.get().unwrap().new_handle(id))
|
||||
.collect(),
|
||||
tags: response
|
||||
.tag_ids
|
||||
.into_iter()
|
||||
.map(|id| TAG.get().unwrap().new_handle(id))
|
||||
.collect(),
|
||||
output_width: response.output_width.unwrap_or_default(),
|
||||
output_height: response.output_height.unwrap_or_default(),
|
||||
};
|
||||
let geos = manager.lock().await.active_layout(&args).layout(&args);
|
||||
from_client
|
||||
.send(LayoutRequest {
|
||||
body: Some(Body::Geometries(Geometries {
|
||||
request_id: response.request_id,
|
||||
output_name: response.output_name,
|
||||
geometries: geos
|
||||
.into_iter()
|
||||
.map(|geo| pinnacle_api_defs::pinnacle::v0alpha1::Geometry {
|
||||
x: Some(geo.x),
|
||||
y: Some(geo.y),
|
||||
width: Some(geo.width as i32),
|
||||
height: Some(geo.height as i32),
|
||||
})
|
||||
.collect(),
|
||||
})),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
tokio::spawn(thing);
|
||||
requester
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LayoutArgs {
|
||||
pub output: OutputHandle,
|
||||
pub windows: Vec<WindowHandle>,
|
||||
pub tags: Vec<TagHandle>,
|
||||
pub output_width: u32,
|
||||
pub output_height: u32,
|
||||
}
|
||||
|
||||
pub trait LayoutManager {
|
||||
fn active_layout(&mut self, args: &LayoutArgs) -> &dyn LayoutGenerator;
|
||||
}
|
||||
|
||||
pub trait LayoutGenerator {
|
||||
fn layout(&self, args: &LayoutArgs) -> Vec<Geometry>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Gaps {
|
||||
Absolute(u32),
|
||||
Split { inner: u32, outer: u32 },
|
||||
}
|
||||
|
||||
pub struct CyclingLayoutManager {
|
||||
layouts: Vec<Box<dyn LayoutGenerator + Send>>,
|
||||
tag_indices: HashMap<u32, usize>,
|
||||
}
|
||||
|
||||
impl CyclingLayoutManager {
|
||||
pub fn new(
|
||||
layout: &Layout,
|
||||
layouts: impl IntoIterator<Item = Box<dyn LayoutGenerator + Send>>,
|
||||
) -> Self {
|
||||
layout.new_cycling_manager(layouts)
|
||||
}
|
||||
|
||||
pub fn cycle_layout_forward(&mut self, tag: &TagHandle) {
|
||||
let index = self.tag_indices.entry(tag.id).or_default();
|
||||
*index += 1;
|
||||
if *index >= self.layouts.len() {
|
||||
*index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle_layout_backward(&mut self, tag: &TagHandle) {
|
||||
let index = self.tag_indices.entry(tag.id).or_default();
|
||||
if let Some(i) = index.checked_sub(1) {
|
||||
*index = i;
|
||||
} else {
|
||||
*index = self.layouts.len().saturating_sub(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutManager for CyclingLayoutManager {
|
||||
fn active_layout(&mut self, args: &LayoutArgs) -> &dyn LayoutGenerator {
|
||||
let Some(first_tag) = args.tags.first() else {
|
||||
return &NoopLayout;
|
||||
};
|
||||
|
||||
self.layouts
|
||||
.get(*self.tag_indices.entry(first_tag.id).or_default())
|
||||
.expect("no layouts in manager")
|
||||
.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutRequester<T> {
|
||||
sender: UnboundedSender<LayoutRequest>,
|
||||
pub manager: Arc<tokio::sync::Mutex<T>>,
|
||||
}
|
||||
|
||||
impl<T> Clone for LayoutRequester<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sender: self.sender.clone(),
|
||||
manager: self.manager.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LayoutRequester<T> {
|
||||
pub fn request_layout(&self) {
|
||||
let output_name = OUTPUT.get().unwrap().get_focused().map(|op| op.name);
|
||||
self.sender
|
||||
.send(LayoutRequest {
|
||||
body: Some(Body::Layout(ExplicitLayout { output_name })),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn request_layout_on_output(&self, output: &OutputHandle) {
|
||||
self.sender
|
||||
.send(LayoutRequest {
|
||||
body: Some(Body::Layout(ExplicitLayout {
|
||||
output_name: Some(output.name.clone()),
|
||||
})),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutRequester<CyclingLayoutManager> {
|
||||
pub fn cycle_layout_forward(&self, tag: &TagHandle) {
|
||||
let mut lock = block_on_tokio(self.manager.lock());
|
||||
lock.cycle_layout_forward(tag);
|
||||
}
|
||||
|
||||
pub fn cycle_layout_backward(&mut self, tag: &TagHandle) {
|
||||
let mut lock = block_on_tokio(self.manager.lock());
|
||||
lock.cycle_layout_backward(tag);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoopLayout;
|
||||
|
||||
impl LayoutGenerator for NoopLayout {
|
||||
fn layout(&self, _args: &LayoutArgs) -> Vec<Geometry> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum MasterSide {
|
||||
Left,
|
||||
Right,
|
||||
Top,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct MasterStackLayout {
|
||||
pub gaps: Gaps,
|
||||
pub master_factor: f32,
|
||||
pub master_side: MasterSide,
|
||||
pub master_count: u32,
|
||||
}
|
||||
|
||||
impl Default for MasterStackLayout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gaps: Gaps::Absolute(8),
|
||||
master_factor: 0.5,
|
||||
master_side: MasterSide::Left,
|
||||
master_count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutGenerator for MasterStackLayout {
|
||||
fn layout(&self, args: &LayoutArgs) -> Vec<Geometry> {
|
||||
let win_count = args.windows.len() as u32;
|
||||
|
||||
if win_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let width = args.output_width;
|
||||
let height = args.output_height;
|
||||
|
||||
let mut geos = Vec::<Geometry>::new();
|
||||
|
||||
let (outer_gaps, inner_gaps) = match self.gaps {
|
||||
Gaps::Absolute(gaps) => (gaps, None),
|
||||
Gaps::Split { inner, outer } => (outer, Some(inner)),
|
||||
};
|
||||
|
||||
let rect = Geometry {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
.split_at(Axis::Horizontal, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Horizontal, (height - outer_gaps) as i32, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, (width - outer_gaps) as i32, outer_gaps)
|
||||
.0;
|
||||
|
||||
let master_factor = if win_count > self.master_count {
|
||||
self.master_factor.clamp(0.1, 0.9)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
let gaps = match inner_gaps {
|
||||
Some(_) => 0,
|
||||
None => outer_gaps,
|
||||
};
|
||||
|
||||
let (master_rect, mut stack_rect) = match self.master_side {
|
||||
MasterSide::Left => {
|
||||
let (rect1, rect2) = rect.split_at(
|
||||
Axis::Vertical,
|
||||
(width as f32 * master_factor).floor() as i32 - gaps as i32 / 2,
|
||||
gaps,
|
||||
);
|
||||
(Some(rect1), rect2)
|
||||
}
|
||||
MasterSide::Right => {
|
||||
let (rect2, rect1) = rect.split_at(
|
||||
Axis::Vertical,
|
||||
(width as f32 * master_factor).floor() as i32 - gaps as i32 / 2,
|
||||
gaps,
|
||||
);
|
||||
(rect1, Some(rect2))
|
||||
}
|
||||
MasterSide::Top => {
|
||||
let (rect1, rect2) = rect.split_at(
|
||||
Axis::Horizontal,
|
||||
(height as f32 * master_factor).floor() as i32 - gaps as i32 / 2,
|
||||
gaps,
|
||||
);
|
||||
(Some(rect1), rect2)
|
||||
}
|
||||
MasterSide::Bottom => {
|
||||
let (rect2, rect1) = rect.split_at(
|
||||
Axis::Horizontal,
|
||||
(height as f32 * master_factor).floor() as i32 - gaps as i32 / 2,
|
||||
gaps,
|
||||
);
|
||||
(rect1, Some(rect2))
|
||||
}
|
||||
};
|
||||
|
||||
let mut master_rect = master_rect.unwrap_or_else(|| stack_rect.take().unwrap());
|
||||
|
||||
let (master_count, stack_count) = if win_count > self.master_count {
|
||||
(self.master_count, Some(win_count - self.master_count))
|
||||
} else {
|
||||
(win_count, None)
|
||||
};
|
||||
|
||||
if master_count > 1 {
|
||||
let (coord, len, axis) = match self.master_side {
|
||||
MasterSide::Left | MasterSide::Right => (
|
||||
master_rect.y,
|
||||
master_rect.height as f32 / master_count as f32,
|
||||
Axis::Horizontal,
|
||||
),
|
||||
MasterSide::Top | MasterSide::Bottom => (
|
||||
master_rect.x,
|
||||
master_rect.width as f32 / master_count as f32,
|
||||
Axis::Vertical,
|
||||
),
|
||||
};
|
||||
|
||||
for i in 1..master_count {
|
||||
let slice_point = coord + (len * i as f32) as i32 - gaps as i32 / 2;
|
||||
let (to_push, rest) = master_rect.split_at(axis, slice_point, gaps);
|
||||
geos.push(to_push);
|
||||
if let Some(rest) = rest {
|
||||
master_rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geos.push(master_rect);
|
||||
|
||||
if let Some(stack_count) = stack_count {
|
||||
let mut stack_rect = stack_rect.unwrap();
|
||||
|
||||
if stack_count > 1 {
|
||||
let (coord, len, axis) = match self.master_side {
|
||||
MasterSide::Left | MasterSide::Right => (
|
||||
stack_rect.y,
|
||||
stack_rect.height as f32 / stack_count as f32,
|
||||
Axis::Horizontal,
|
||||
),
|
||||
MasterSide::Top | MasterSide::Bottom => (
|
||||
stack_rect.x,
|
||||
stack_rect.width as f32 / stack_count as f32,
|
||||
Axis::Vertical,
|
||||
),
|
||||
};
|
||||
|
||||
for i in 1..stack_count {
|
||||
let slice_point = coord + (len * i as f32) as i32 - gaps as i32 / 2;
|
||||
let (to_push, rest) = stack_rect.split_at(axis, slice_point, gaps);
|
||||
geos.push(to_push);
|
||||
if let Some(rest) = rest {
|
||||
stack_rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
geos.push(stack_rect);
|
||||
}
|
||||
|
||||
if let Some(inner_gaps) = inner_gaps {
|
||||
for geo in geos.iter_mut() {
|
||||
geo.x += inner_gaps as i32;
|
||||
geo.y += inner_gaps as i32;
|
||||
geo.width -= inner_gaps * 2;
|
||||
geo.height -= inner_gaps * 2;
|
||||
}
|
||||
}
|
||||
|
||||
geos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct DwindleLayout {
|
||||
pub gaps: Gaps,
|
||||
pub split_factors: HashMap<usize, f32>,
|
||||
}
|
||||
|
||||
impl Default for DwindleLayout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gaps: Gaps::Absolute(8),
|
||||
split_factors: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutGenerator for DwindleLayout {
|
||||
fn layout(&self, args: &LayoutArgs) -> Vec<Geometry> {
|
||||
let win_count = args.windows.len() as u32;
|
||||
|
||||
if win_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let width = args.output_width;
|
||||
let height = args.output_height;
|
||||
|
||||
let mut geos = Vec::<Geometry>::new();
|
||||
|
||||
let (outer_gaps, inner_gaps) = match self.gaps {
|
||||
Gaps::Absolute(gaps) => (gaps, None),
|
||||
Gaps::Split { inner, outer } => (outer, Some(inner)),
|
||||
};
|
||||
|
||||
let gaps = match inner_gaps {
|
||||
Some(_) => 0,
|
||||
None => outer_gaps,
|
||||
};
|
||||
|
||||
let mut rect = Geometry {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
.split_at(Axis::Horizontal, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Horizontal, (height - outer_gaps) as i32, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, (width - outer_gaps) as i32, outer_gaps)
|
||||
.0;
|
||||
|
||||
if win_count == 1 {
|
||||
geos.push(rect)
|
||||
} else {
|
||||
for i in 1..win_count {
|
||||
let factor = self
|
||||
.split_factors
|
||||
.get(&(i as usize))
|
||||
.copied()
|
||||
.unwrap_or(0.5)
|
||||
.clamp(0.1, 0.9);
|
||||
|
||||
let (axis, mut split_coord) = if i % 2 == 1 {
|
||||
(Axis::Vertical, rect.x + (rect.width as f32 * factor) as i32)
|
||||
} else {
|
||||
(
|
||||
Axis::Horizontal,
|
||||
rect.y + (rect.height as f32 * factor) as i32,
|
||||
)
|
||||
};
|
||||
split_coord -= gaps as i32 / 2;
|
||||
|
||||
let (to_push, rest) = rect.split_at(axis, split_coord, gaps);
|
||||
|
||||
geos.push(to_push);
|
||||
|
||||
if let Some(rest) = rest {
|
||||
rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
geos.push(rect)
|
||||
}
|
||||
|
||||
if let Some(inner_gaps) = inner_gaps {
|
||||
for geo in geos.iter_mut() {
|
||||
geo.x += inner_gaps as i32;
|
||||
geo.y += inner_gaps as i32;
|
||||
geo.width -= inner_gaps * 2;
|
||||
geo.height -= inner_gaps * 2;
|
||||
}
|
||||
}
|
||||
|
||||
geos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SpiralLayout {
|
||||
pub gaps: Gaps,
|
||||
pub split_factors: HashMap<usize, f32>,
|
||||
}
|
||||
|
||||
impl Default for SpiralLayout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gaps: Gaps::Absolute(8),
|
||||
split_factors: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutGenerator for SpiralLayout {
|
||||
fn layout(&self, args: &LayoutArgs) -> Vec<Geometry> {
|
||||
let win_count = args.windows.len() as u32;
|
||||
|
||||
if win_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let width = args.output_width;
|
||||
let height = args.output_height;
|
||||
|
||||
let mut geos = Vec::<Geometry>::new();
|
||||
|
||||
let (outer_gaps, inner_gaps) = match self.gaps {
|
||||
Gaps::Absolute(gaps) => (gaps, None),
|
||||
Gaps::Split { inner, outer } => (outer, Some(inner)),
|
||||
};
|
||||
|
||||
let gaps = match inner_gaps {
|
||||
Some(_) => 0,
|
||||
None => outer_gaps,
|
||||
};
|
||||
|
||||
let mut rect = Geometry {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
.split_at(Axis::Horizontal, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Horizontal, (height - outer_gaps) as i32, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, (width - outer_gaps) as i32, outer_gaps)
|
||||
.0;
|
||||
|
||||
if win_count == 1 {
|
||||
geos.push(rect)
|
||||
} else {
|
||||
for i in 1..win_count {
|
||||
let factor = self
|
||||
.split_factors
|
||||
.get(&(i as usize))
|
||||
.copied()
|
||||
.unwrap_or(0.5)
|
||||
.clamp(0.1, 0.9);
|
||||
|
||||
let (axis, mut split_coord) = if i % 2 == 1 {
|
||||
(Axis::Vertical, rect.x + (rect.width as f32 * factor) as i32)
|
||||
} else {
|
||||
(
|
||||
Axis::Horizontal,
|
||||
rect.y + (rect.height as f32 * factor) as i32,
|
||||
)
|
||||
};
|
||||
split_coord -= gaps as i32 / 2;
|
||||
|
||||
let (to_push, rest) = if let 1 | 2 = i % 4 {
|
||||
let (to_push, rest) = rect.split_at(axis, split_coord, gaps);
|
||||
(Some(to_push), rest)
|
||||
} else {
|
||||
let (rest, to_push) = rect.split_at(axis, split_coord, gaps);
|
||||
(to_push, Some(rest))
|
||||
};
|
||||
|
||||
if let Some(to_push) = to_push {
|
||||
geos.push(to_push);
|
||||
}
|
||||
|
||||
if let Some(rest) = rest {
|
||||
rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
geos.push(rect)
|
||||
}
|
||||
|
||||
if let Some(inner_gaps) = inner_gaps {
|
||||
for geo in geos.iter_mut() {
|
||||
geo.x += inner_gaps as i32;
|
||||
geo.y += inner_gaps as i32;
|
||||
geo.width -= inner_gaps * 2;
|
||||
geo.height -= inner_gaps * 2;
|
||||
}
|
||||
}
|
||||
|
||||
geos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum CornerLocation {
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct CornerLayout {
|
||||
pub gaps: Gaps,
|
||||
pub corner_width_factor: f32,
|
||||
pub corner_height_factor: f32,
|
||||
pub corner_loc: CornerLocation,
|
||||
}
|
||||
|
||||
impl Default for CornerLayout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gaps: Gaps::Absolute(8),
|
||||
corner_width_factor: 0.5,
|
||||
corner_height_factor: 0.5,
|
||||
corner_loc: CornerLocation::TopLeft,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutGenerator for CornerLayout {
|
||||
fn layout(&self, args: &LayoutArgs) -> Vec<Geometry> {
|
||||
let win_count = args.windows.len() as u32;
|
||||
|
||||
if win_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let width = args.output_width;
|
||||
let height = args.output_height;
|
||||
|
||||
let mut geos = Vec::<Geometry>::new();
|
||||
|
||||
let (outer_gaps, inner_gaps) = match self.gaps {
|
||||
Gaps::Absolute(gaps) => (gaps, None),
|
||||
Gaps::Split { inner, outer } => (outer, Some(inner)),
|
||||
};
|
||||
|
||||
let gaps = match inner_gaps {
|
||||
Some(_) => 0,
|
||||
None => outer_gaps,
|
||||
};
|
||||
|
||||
let rect = Geometry {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
.split_at(Axis::Horizontal, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Horizontal, (height - outer_gaps) as i32, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, (width - outer_gaps) as i32, outer_gaps)
|
||||
.0;
|
||||
|
||||
if win_count == 1 {
|
||||
geos.push(rect)
|
||||
} else {
|
||||
let (mut corner_rect, vert_stack_rect) = match self.corner_loc {
|
||||
CornerLocation::TopLeft | CornerLocation::BottomLeft => {
|
||||
let x_slice_point = rect.x
|
||||
+ (rect.width as f32 * self.corner_width_factor).round() as i32
|
||||
- gaps as i32 / 2;
|
||||
let (corner_rect, vert_stack_rect) =
|
||||
rect.split_at(Axis::Vertical, x_slice_point, gaps);
|
||||
(Some(corner_rect), vert_stack_rect)
|
||||
}
|
||||
CornerLocation::TopRight | CornerLocation::BottomRight => {
|
||||
let x_slice_point = rect.x
|
||||
+ (rect.width as f32 * (1.0 - self.corner_width_factor)).round() as i32
|
||||
- gaps as i32 / 2;
|
||||
let (vert_stack_rect, corner_rect) =
|
||||
rect.split_at(Axis::Vertical, x_slice_point, gaps);
|
||||
(corner_rect, Some(vert_stack_rect))
|
||||
}
|
||||
};
|
||||
|
||||
if win_count == 2 {
|
||||
geos.extend([corner_rect, vert_stack_rect].into_iter().flatten());
|
||||
} else {
|
||||
let horiz_stack_rect = match self.corner_loc {
|
||||
CornerLocation::TopLeft | CornerLocation::TopRight => {
|
||||
let y_slice_point = rect.y
|
||||
+ (rect.height as f32 * self.corner_height_factor).round() as i32
|
||||
- gaps as i32 / 2;
|
||||
|
||||
corner_rect.and_then(|corner| {
|
||||
let (corner, horiz) =
|
||||
corner.split_at(Axis::Horizontal, y_slice_point, gaps);
|
||||
corner_rect = Some(corner);
|
||||
horiz
|
||||
})
|
||||
}
|
||||
CornerLocation::BottomLeft | CornerLocation::BottomRight => {
|
||||
let y_slice_point = rect.y
|
||||
+ (rect.height as f32 * (1.0 - self.corner_height_factor)).round()
|
||||
as i32
|
||||
- gaps as i32 / 2;
|
||||
|
||||
corner_rect.map(|corner| {
|
||||
let (horiz, corner) =
|
||||
corner.split_at(Axis::Horizontal, y_slice_point, gaps);
|
||||
corner_rect = corner;
|
||||
horiz
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
if let (Some(mut horiz_stack_rect), Some(mut vert_stack_rect), Some(corner_rect)) =
|
||||
(horiz_stack_rect, vert_stack_rect, corner_rect)
|
||||
{
|
||||
geos.push(corner_rect);
|
||||
|
||||
let mut vert_geos = Vec::new();
|
||||
let mut horiz_geos = Vec::new();
|
||||
|
||||
let vert_stack_count = ((win_count - 1) as f32 / 2.0).ceil() as i32;
|
||||
let horiz_stack_count = ((win_count - 1) as f32 / 2.0).floor() as i32;
|
||||
|
||||
let vert_stack_y = vert_stack_rect.y;
|
||||
let vert_win_height = vert_stack_rect.height as f32 / vert_stack_count as f32;
|
||||
|
||||
for i in 1..vert_stack_count {
|
||||
let slice_point = vert_stack_y
|
||||
+ (vert_win_height * i as f32).round() as i32
|
||||
- gaps as i32 / 2;
|
||||
|
||||
let (to_push, rest) =
|
||||
vert_stack_rect.split_at(Axis::Horizontal, slice_point, gaps);
|
||||
|
||||
vert_geos.push(to_push);
|
||||
|
||||
if let Some(rest) = rest {
|
||||
vert_stack_rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vert_geos.push(vert_stack_rect);
|
||||
|
||||
let horiz_stack_x = horiz_stack_rect.x;
|
||||
let horiz_win_width = horiz_stack_rect.width as f32 / horiz_stack_count as f32;
|
||||
|
||||
for i in 1..horiz_stack_count {
|
||||
let slice_point = horiz_stack_x
|
||||
+ (horiz_win_width * i as f32).round() as i32
|
||||
- gaps as i32 / 2;
|
||||
|
||||
let (to_push, rest) =
|
||||
horiz_stack_rect.split_at(Axis::Vertical, slice_point, gaps);
|
||||
|
||||
horiz_geos.push(to_push);
|
||||
|
||||
if let Some(rest) = rest {
|
||||
horiz_stack_rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
horiz_geos.push(horiz_stack_rect);
|
||||
|
||||
for i in 0..(vert_geos.len() + horiz_geos.len()) {
|
||||
if i % 2 == 0 {
|
||||
geos.push(vert_geos[i / 2]);
|
||||
} else {
|
||||
geos.push(horiz_geos[i / 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inner_gaps) = inner_gaps {
|
||||
for geo in geos.iter_mut() {
|
||||
geo.x += inner_gaps as i32;
|
||||
geo.y += inner_gaps as i32;
|
||||
geo.width -= inner_gaps * 2;
|
||||
geo.height -= inner_gaps * 2;
|
||||
}
|
||||
}
|
||||
|
||||
geos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FairLayout {
|
||||
pub gaps: Gaps,
|
||||
pub axis: Axis,
|
||||
}
|
||||
|
||||
impl Default for FairLayout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gaps: Gaps::Absolute(8),
|
||||
axis: Axis::Vertical,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutGenerator for FairLayout {
|
||||
fn layout(&self, args: &LayoutArgs) -> Vec<Geometry> {
|
||||
let win_count = args.windows.len() as u32;
|
||||
|
||||
if win_count == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let width = args.output_width;
|
||||
let height = args.output_height;
|
||||
|
||||
let mut geos = Vec::<Geometry>::new();
|
||||
|
||||
let (outer_gaps, inner_gaps) = match self.gaps {
|
||||
Gaps::Absolute(gaps) => (gaps, None),
|
||||
Gaps::Split { inner, outer } => (outer, Some(inner)),
|
||||
};
|
||||
|
||||
let gaps = match inner_gaps {
|
||||
Some(_) => 0,
|
||||
None => outer_gaps,
|
||||
};
|
||||
|
||||
let mut rect = Geometry {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
.split_at(Axis::Horizontal, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Horizontal, (height - outer_gaps) as i32, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, 0, outer_gaps)
|
||||
.0
|
||||
.split_at(Axis::Vertical, (width - outer_gaps) as i32, outer_gaps)
|
||||
.0;
|
||||
|
||||
if win_count == 1 {
|
||||
geos.push(rect);
|
||||
} else if win_count == 2 {
|
||||
let len = match self.axis {
|
||||
Axis::Vertical => rect.width,
|
||||
Axis::Horizontal => rect.height,
|
||||
};
|
||||
|
||||
let coord = match self.axis {
|
||||
Axis::Vertical => rect.x,
|
||||
Axis::Horizontal => rect.y,
|
||||
};
|
||||
|
||||
let (rect1, rect2) =
|
||||
rect.split_at(self.axis, coord + len as i32 / 2 - gaps as i32 / 2, gaps);
|
||||
|
||||
geos.push(rect1);
|
||||
if let Some(rect2) = rect2 {
|
||||
geos.push(rect2);
|
||||
}
|
||||
} else {
|
||||
let line_count = (win_count as f32).sqrt().round() as u32;
|
||||
|
||||
let mut wins_per_line = Vec::new();
|
||||
|
||||
let max_per_line = if win_count > line_count * line_count {
|
||||
line_count + 1
|
||||
} else {
|
||||
line_count
|
||||
};
|
||||
|
||||
for i in 1..=win_count {
|
||||
let index = (i as f32 / max_per_line as f32).ceil() as usize - 1;
|
||||
if wins_per_line.get(index).is_none() {
|
||||
wins_per_line.push(0);
|
||||
}
|
||||
wins_per_line[index] += 1;
|
||||
}
|
||||
|
||||
assert_eq!(wins_per_line.len(), line_count as usize);
|
||||
|
||||
let mut line_rects = Vec::new();
|
||||
|
||||
let (coord, len, axis) = match self.axis {
|
||||
Axis::Horizontal => (
|
||||
rect.y,
|
||||
rect.height as f32 / line_count as f32,
|
||||
Axis::Horizontal,
|
||||
),
|
||||
Axis::Vertical => (
|
||||
rect.x,
|
||||
rect.width as f32 / line_count as f32,
|
||||
Axis::Vertical,
|
||||
),
|
||||
};
|
||||
|
||||
for i in 1..line_count {
|
||||
let slice_point = coord + (len * i as f32) as i32 - gaps as i32 / 2;
|
||||
let (to_push, rest) = rect.split_at(axis, slice_point, gaps);
|
||||
line_rects.push(to_push);
|
||||
if let Some(rest) = rest {
|
||||
rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
line_rects.push(rect);
|
||||
|
||||
for (i, mut line_rect) in line_rects.into_iter().enumerate() {
|
||||
let (coord, len, axis) = match self.axis {
|
||||
Axis::Vertical => (
|
||||
line_rect.y,
|
||||
line_rect.height as f32 / wins_per_line[i] as f32,
|
||||
Axis::Horizontal,
|
||||
),
|
||||
Axis::Horizontal => (
|
||||
line_rect.x,
|
||||
line_rect.width as f32 / wins_per_line[i] as f32,
|
||||
Axis::Vertical,
|
||||
),
|
||||
};
|
||||
|
||||
for j in 1..wins_per_line[i] {
|
||||
let slice_point = coord + (len * j as f32) as i32 - gaps as i32 / 2;
|
||||
let (to_push, rest) = line_rect.split_at(axis, slice_point, gaps);
|
||||
geos.push(to_push);
|
||||
if let Some(rest) = rest {
|
||||
line_rect = rest;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
geos.push(line_rect);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inner_gaps) = inner_gaps {
|
||||
for geo in geos.iter_mut() {
|
||||
geo.x += inner_gaps as i32;
|
||||
geo.y += inner_gaps as i32;
|
||||
geo.width -= inner_gaps * 2;
|
||||
geo.height -= inner_gaps * 2;
|
||||
}
|
||||
}
|
||||
|
||||
geos
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
#![deny(elided_lifetimes_in_paths)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! The Rust implementation of [Pinnacle](https://github.com/pinnacle-comp/pinnacle)'s
|
||||
|
@ -87,6 +88,7 @@ use futures::{
|
|||
Future, StreamExt,
|
||||
};
|
||||
use input::Input;
|
||||
use layout::Layout;
|
||||
use output::Output;
|
||||
use pinnacle::Pinnacle;
|
||||
use process::Process;
|
||||
|
@ -102,6 +104,7 @@ use tower::service_fn;
|
|||
use window::Window;
|
||||
|
||||
pub mod input;
|
||||
pub mod layout;
|
||||
pub mod output;
|
||||
pub mod pinnacle;
|
||||
pub mod process;
|
||||
|
@ -121,6 +124,7 @@ static INPUT: OnceLock<Input> = OnceLock::new();
|
|||
static OUTPUT: OnceLock<Output> = OnceLock::new();
|
||||
static TAG: OnceLock<Tag> = OnceLock::new();
|
||||
static SIGNAL: OnceLock<RwLock<SignalState>> = OnceLock::new();
|
||||
static LAYOUT: OnceLock<Layout> = OnceLock::new();
|
||||
|
||||
/// A struct containing static references to all of the configuration structs.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -137,6 +141,8 @@ pub struct ApiModules {
|
|||
pub output: &'static Output,
|
||||
/// The [`Tag`] struct
|
||||
pub tag: &'static Tag,
|
||||
/// The [`Layout`] struct
|
||||
pub layout: &'static Layout,
|
||||
}
|
||||
|
||||
/// Connects to Pinnacle and builds the configuration structs.
|
||||
|
@ -154,7 +160,7 @@ pub async fn connect(
|
|||
}))
|
||||
.await?;
|
||||
|
||||
let (fut_sender, fut_recv) = unbounded_channel::<BoxFuture<()>>();
|
||||
let (fut_sender, fut_recv) = unbounded_channel::<BoxFuture<'static, ()>>();
|
||||
|
||||
let pinnacle = PINNACLE.get_or_init(|| Pinnacle::new(channel.clone()));
|
||||
let process = PROCESS.get_or_init(|| Process::new(channel.clone(), fut_sender.clone()));
|
||||
|
@ -162,6 +168,7 @@ pub async fn connect(
|
|||
let input = INPUT.get_or_init(|| Input::new(channel.clone(), fut_sender.clone()));
|
||||
let tag = TAG.get_or_init(|| Tag::new(channel.clone()));
|
||||
let output = OUTPUT.get_or_init(|| Output::new(channel.clone()));
|
||||
let layout = LAYOUT.get_or_init(|| Layout::new(channel.clone()));
|
||||
|
||||
SIGNAL
|
||||
.set(RwLock::new(SignalState::new(
|
||||
|
@ -177,6 +184,7 @@ pub async fn connect(
|
|||
input,
|
||||
output,
|
||||
tag,
|
||||
layout,
|
||||
};
|
||||
|
||||
Ok((modules, fut_recv))
|
||||
|
@ -190,7 +198,7 @@ pub async fn connect(
|
|||
/// This function is inserted at the end of your config through the [`config`] macro.
|
||||
/// You should use the macro instead of this function directly.
|
||||
pub async fn listen(fut_recv: UnboundedReceiver<BoxFuture<'static, ()>>) {
|
||||
let mut future_set = FuturesUnordered::<BoxFuture<()>>::new();
|
||||
let mut future_set = FuturesUnordered::<BoxFuture<'static, ()>>::new();
|
||||
|
||||
let mut fut_recv = UnboundedReceiverStream::new(fut_recv);
|
||||
|
||||
|
|
|
@ -313,7 +313,6 @@ where
|
|||
}
|
||||
}
|
||||
_dc = dc_ping_recv_fuse => {
|
||||
println!("dc");
|
||||
control_sender.send(Req::from_control(StreamControl::Disconnect)).expect("send failed");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,97 @@ pub struct Geometry {
|
|||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||
pub enum Axis {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
impl Geometry {
|
||||
pub fn split_at(mut self, axis: Axis, at: i32, thickness: u32) -> (Geometry, Option<Geometry>) {
|
||||
match axis {
|
||||
Axis::Horizontal => {
|
||||
if at <= self.y {
|
||||
let diff = at - self.y + thickness as i32;
|
||||
if diff > 0 {
|
||||
self.y += diff;
|
||||
self.height = self.height.saturating_sub(diff as u32);
|
||||
}
|
||||
(self, None)
|
||||
} else if at >= self.y + self.height as i32 {
|
||||
(self, None)
|
||||
} else if at + thickness as i32 >= self.y + self.height as i32 {
|
||||
let diff = self.y + self.height as i32 - at;
|
||||
self.height = self.height.saturating_sub(diff as u32);
|
||||
(self, None)
|
||||
} else {
|
||||
let x = self.x;
|
||||
let top_y = self.y;
|
||||
let width = self.width;
|
||||
let top_height = at - self.y;
|
||||
|
||||
let bot_y = at + thickness as i32;
|
||||
let bot_height = self.y + self.height as i32 - at - thickness as i32;
|
||||
|
||||
let geo1 = Geometry {
|
||||
x,
|
||||
y: top_y,
|
||||
width,
|
||||
height: top_height as u32,
|
||||
};
|
||||
let geo2 = Geometry {
|
||||
x,
|
||||
y: bot_y,
|
||||
width,
|
||||
height: bot_height as u32,
|
||||
};
|
||||
|
||||
(geo1, Some(geo2))
|
||||
}
|
||||
}
|
||||
Axis::Vertical => {
|
||||
if at <= self.x {
|
||||
let diff = at - self.x + thickness as i32;
|
||||
if diff > 0 {
|
||||
self.x += diff;
|
||||
self.width = self.width.saturating_sub(diff as u32);
|
||||
}
|
||||
(self, None)
|
||||
} else if at >= self.x + self.width as i32 {
|
||||
(self, None)
|
||||
} else if at + thickness as i32 >= self.x + self.width as i32 {
|
||||
let diff = self.x + self.width as i32 - at;
|
||||
self.width = self.width.saturating_sub(diff as u32);
|
||||
(self, None)
|
||||
} else {
|
||||
let left_x = self.x;
|
||||
let y = self.y;
|
||||
let left_width = at - self.x;
|
||||
let height = self.height;
|
||||
|
||||
let right_x = at + thickness as i32;
|
||||
let right_width = self.x + self.width as i32 - at - thickness as i32;
|
||||
|
||||
let geo1 = Geometry {
|
||||
x: left_x,
|
||||
y,
|
||||
width: left_width as u32,
|
||||
height,
|
||||
};
|
||||
let geo2 = Geometry {
|
||||
x: right_x,
|
||||
y,
|
||||
width: right_width as u32,
|
||||
height,
|
||||
};
|
||||
|
||||
(geo1, Some(geo2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Batch a set of requests that will be sent ot the compositor all at once.
|
||||
///
|
||||
/// # Rationale
|
||||
|
|
Loading…
Reference in a new issue