mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-15 06:13:36 +01:00
Compare commits
1 Commits
v0.1.37
...
feature/wo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
557ef98ee5 |
12
docs/cli/toggle-workspace-layer.md
Normal file
12
docs/cli/toggle-workspace-layer.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# toggle-workspace-layer
|
||||
|
||||
```
|
||||
Toggle between the Tiling and Floating layers on the focused workspace
|
||||
|
||||
Usage: komorebic.exe toggle-workspace-layer
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -369,12 +369,6 @@ impl Komobar {
|
||||
monitor_index,
|
||||
error,
|
||||
);
|
||||
} else {
|
||||
tracing::info!(
|
||||
"work area offset applied to monitor: {}\n, {:#?}",
|
||||
monitor_index,
|
||||
new_rect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,13 +623,8 @@ impl Komobar {
|
||||
pub fn position_bar(&self) {
|
||||
if let Some(hwnd) = self.hwnd {
|
||||
let window = komorebi_client::Window::from(hwnd);
|
||||
match window.set_position(&self.size_rect, false) {
|
||||
Ok(_) => {
|
||||
tracing::info!("updated bar position: {:#?}", &self.size_rect);
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{}", error.to_string())
|
||||
}
|
||||
if let Err(error) = window.set_position(&self.size_rect, false) {
|
||||
tracing::error!("{}", error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ use komorebi_client::Rect;
|
||||
use komorebi_client::SocketMessage;
|
||||
use komorebi_client::Window;
|
||||
use komorebi_client::Workspace;
|
||||
use komorebi_client::WorkspaceLayer;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -49,6 +50,8 @@ pub struct KomorebiConfig {
|
||||
pub workspaces: Option<KomorebiWorkspacesConfig>,
|
||||
/// Configure the Layout widget
|
||||
pub layout: Option<KomorebiLayoutConfig>,
|
||||
/// Configure the Workspace Layer widget
|
||||
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
|
||||
/// Configure the Focused Window widget
|
||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||
/// Configure the Configuration Switcher widget
|
||||
@@ -75,6 +78,12 @@ pub struct KomorebiLayoutConfig {
|
||||
pub display: Option<DisplayFormat>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiWorkspaceLayerConfig {
|
||||
/// Enable the Komorebi Workspace Layer widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiFocusedWindowConfig {
|
||||
/// Enable the Komorebi Focused Window widget
|
||||
@@ -127,6 +136,7 @@ impl From<&KomorebiConfig> for Komorebi {
|
||||
workspaces: value.workspaces,
|
||||
layout: value.layout.clone(),
|
||||
focused_window: value.focused_window,
|
||||
workspace_layer: value.workspace_layer,
|
||||
configuration_switcher,
|
||||
}
|
||||
}
|
||||
@@ -138,6 +148,7 @@ pub struct Komorebi {
|
||||
pub workspaces: Option<KomorebiWorkspacesConfig>,
|
||||
pub layout: Option<KomorebiLayoutConfig>,
|
||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
|
||||
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
||||
}
|
||||
|
||||
@@ -154,7 +165,7 @@ impl BarWidget for Komorebi {
|
||||
let format = workspaces.display.unwrap_or(DisplayFormat::Text);
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
for (i, (ws, container_information)) in
|
||||
for (i, (ws, container_information, _)) in
|
||||
komorebi_notification_state.workspaces.iter().enumerate()
|
||||
{
|
||||
if SelectableFrame::new(
|
||||
@@ -281,6 +292,42 @@ impl BarWidget for Komorebi {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(layer_config) = &self.workspace_layer {
|
||||
if layer_config.enable {
|
||||
let layer = komorebi_notification_state
|
||||
.workspaces
|
||||
.iter()
|
||||
.find(|o| komorebi_notification_state.selected_workspace.eq(&o.0))
|
||||
.map(|(_, _, layer)| layer);
|
||||
|
||||
if let Some(layer) = layer {
|
||||
let name = layer.to_string();
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(name).selectable(false)))
|
||||
.clicked()
|
||||
&& komorebi_client::send_batch([
|
||||
SocketMessage::MouseFollowsFocus(false),
|
||||
SocketMessage::ToggleWorkspaceLayer,
|
||||
SocketMessage::MouseFollowsFocus(
|
||||
komorebi_notification_state.mouse_follows_focus,
|
||||
),
|
||||
])
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send the following batch of messages to komorebi:\n\
|
||||
MouseFollowsFocus(false),
|
||||
ToggleWorkspaceLayer,
|
||||
MouseFollowsFocus({})",
|
||||
komorebi_notification_state.mouse_follows_focus,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(layout_config) = &self.layout {
|
||||
if layout_config.enable {
|
||||
let workspace_idx: Option<usize> = komorebi_notification_state
|
||||
@@ -476,7 +523,11 @@ fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KomorebiNotificationState {
|
||||
pub workspaces: Vec<(String, KomorebiNotificationStateContainerInformation)>,
|
||||
pub workspaces: Vec<(
|
||||
String,
|
||||
KomorebiNotificationStateContainerInformation,
|
||||
WorkspaceLayer,
|
||||
)>,
|
||||
pub selected_workspace: String,
|
||||
pub focused_container_information: KomorebiNotificationStateContainerInformation,
|
||||
pub layout: KomorebiLayout,
|
||||
@@ -592,6 +643,7 @@ impl KomorebiNotificationState {
|
||||
workspaces.push((
|
||||
ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)),
|
||||
ws.into(),
|
||||
ws.layer().to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ pub use komorebi::ring::Ring;
|
||||
pub use komorebi::window::Window;
|
||||
pub use komorebi::window_manager_event::WindowManagerEvent;
|
||||
pub use komorebi::workspace::Workspace;
|
||||
pub use komorebi::workspace::WorkspaceLayer;
|
||||
pub use komorebi::AnimationsConfig;
|
||||
pub use komorebi::AspectRatio;
|
||||
pub use komorebi::BorderColours;
|
||||
|
||||
@@ -149,6 +149,7 @@ pub enum SocketMessage {
|
||||
NamedWorkspaceLayoutCustomRule(String, usize, PathBuf),
|
||||
ClearWorkspaceLayoutRules(usize, usize),
|
||||
ClearNamedWorkspaceLayoutRules(String),
|
||||
ToggleWorkspaceLayer,
|
||||
// Configuration
|
||||
ReloadConfiguration,
|
||||
ReplaceConfiguration(PathBuf),
|
||||
|
||||
@@ -66,6 +66,7 @@ use crate::window_manager;
|
||||
use crate::window_manager::WindowManager;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent_listener;
|
||||
use crate::workspace::WorkspaceLayer;
|
||||
use crate::workspace::WorkspaceWindowLocation;
|
||||
use crate::GlobalState;
|
||||
use crate::Notification;
|
||||
@@ -291,13 +292,37 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
SocketMessage::FocusWindow(direction) => {
|
||||
self.focus_container_in_direction(direction)?;
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
match focused_workspace.layer() {
|
||||
WorkspaceLayer::Tiling => {
|
||||
self.focus_container_in_direction(direction)?;
|
||||
}
|
||||
WorkspaceLayer::Floating => {
|
||||
self.focus_floating_window_in_direction(direction)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::MoveWindow(direction) => {
|
||||
self.move_container_in_direction(direction)?;
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
match focused_workspace.layer() {
|
||||
WorkspaceLayer::Tiling => {
|
||||
self.move_container_in_direction(direction)?;
|
||||
}
|
||||
WorkspaceLayer::Floating => {
|
||||
self.move_floating_window_in_direction(direction)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::CycleFocusWindow(direction) => {
|
||||
self.focus_container_in_cycle_direction(direction)?;
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
match focused_workspace.layer() {
|
||||
WorkspaceLayer::Tiling => {
|
||||
self.focus_container_in_cycle_direction(direction)?;
|
||||
}
|
||||
WorkspaceLayer::Floating => {
|
||||
self.focus_floating_window_in_cycle_direction(direction)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::CycleMoveWindow(direction) => {
|
||||
self.move_container_in_cycle_direction(direction)?;
|
||||
@@ -1020,6 +1045,29 @@ impl WindowManager {
|
||||
self.focus_workspace(workspace_idx)?;
|
||||
}
|
||||
}
|
||||
SocketMessage::ToggleWorkspaceLayer => {
|
||||
let mouse_follows_focus = self.mouse_follows_focus;
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
match workspace.layer() {
|
||||
WorkspaceLayer::Tiling => {
|
||||
workspace.set_layer(WorkspaceLayer::Floating);
|
||||
|
||||
if let Some(first) = workspace.floating_windows().first() {
|
||||
first.focus(mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
WorkspaceLayer::Floating => {
|
||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
||||
|
||||
if let Some(container) = workspace.focused_container() {
|
||||
if let Some(window) = container.focused_window() {
|
||||
window.focus(mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
SocketMessage::Stop => {
|
||||
self.stop(false)?;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ use crate::core::WindowContainerBehaviour;
|
||||
use crate::core::WindowManagementBehaviour;
|
||||
|
||||
use crate::border_manager;
|
||||
use crate::border_manager::BORDER_OFFSET;
|
||||
use crate::border_manager::BORDER_WIDTH;
|
||||
use crate::border_manager::STYLE;
|
||||
use crate::config_generation::WorkspaceMatchingRule;
|
||||
use crate::container::Container;
|
||||
@@ -75,6 +77,7 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent_listener;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::workspace::WorkspaceLayer;
|
||||
use crate::BorderColours;
|
||||
use crate::Colour;
|
||||
use crate::CrossBoundaryBehaviour;
|
||||
@@ -333,6 +336,7 @@ impl From<&WindowManager> for State {
|
||||
.window_container_behaviour_rules
|
||||
.clone(),
|
||||
float_override: workspace.float_override,
|
||||
layer: workspace.layer,
|
||||
workspace_config: None,
|
||||
})
|
||||
.collect::<VecDeque<_>>();
|
||||
@@ -1392,86 +1396,168 @@ impl WindowManager {
|
||||
delta: i32,
|
||||
update: bool,
|
||||
) -> Result<()> {
|
||||
let work_area = self.focused_monitor_work_area()?;
|
||||
let mouse_follows_focus = self.mouse_follows_focus;
|
||||
let mut focused_monitor_work_area = self.focused_monitor_work_area()?;
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
match workspace.layout() {
|
||||
Layout::Default(layout) => {
|
||||
tracing::info!("resizing window");
|
||||
let len = NonZeroUsize::new(workspace.containers().len())
|
||||
.ok_or_else(|| anyhow!("there must be at least one container"))?;
|
||||
let focused_idx = workspace.focused_container_idx();
|
||||
let focused_idx_resize = workspace
|
||||
.resize_dimensions()
|
||||
.get(focused_idx)
|
||||
.ok_or_else(|| anyhow!("there is no resize adjustment for this container"))?;
|
||||
match workspace.layer() {
|
||||
WorkspaceLayer::Floating => {
|
||||
let workspace = self.focused_workspace()?;
|
||||
let focused_hwnd = WindowsApi::foreground_window()?;
|
||||
|
||||
if direction
|
||||
.destination(
|
||||
workspace.layout().as_boxed_direction().as_ref(),
|
||||
workspace.layout_flip(),
|
||||
focused_idx,
|
||||
len,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let unaltered = layout.calculate(
|
||||
&work_area,
|
||||
len,
|
||||
workspace.container_padding(),
|
||||
workspace.layout_flip(),
|
||||
&[],
|
||||
);
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
focused_monitor_work_area.left += border_offset;
|
||||
focused_monitor_work_area.left += border_width;
|
||||
focused_monitor_work_area.top += border_offset;
|
||||
focused_monitor_work_area.top += border_width;
|
||||
focused_monitor_work_area.right -= border_offset;
|
||||
focused_monitor_work_area.right -= border_width;
|
||||
focused_monitor_work_area.bottom -= border_offset;
|
||||
focused_monitor_work_area.bottom -= border_width;
|
||||
|
||||
let mut direction = direction;
|
||||
|
||||
// We only ever want to operate on the unflipped Rect positions when resizing, then we
|
||||
// can flip them however they need to be flipped once the resizing has been done
|
||||
if let Some(flip) = workspace.layout_flip() {
|
||||
match flip {
|
||||
Axis::Horizontal => {
|
||||
if matches!(direction, OperationDirection::Left)
|
||||
|| matches!(direction, OperationDirection::Right)
|
||||
{
|
||||
direction = direction.opposite();
|
||||
for window in workspace.floating_windows().iter() {
|
||||
if window.hwnd == focused_hwnd {
|
||||
let mut rect = WindowsApi::window_rect(window.hwnd)?;
|
||||
match (direction, sizing) {
|
||||
(OperationDirection::Left, Sizing::Increase) => {
|
||||
if rect.left - delta < focused_monitor_work_area.left {
|
||||
rect.left = focused_monitor_work_area.left;
|
||||
} else {
|
||||
rect.left -= delta;
|
||||
}
|
||||
}
|
||||
Axis::Vertical => {
|
||||
if matches!(direction, OperationDirection::Up)
|
||||
|| matches!(direction, OperationDirection::Down)
|
||||
(OperationDirection::Left, Sizing::Decrease) => {
|
||||
rect.left += delta;
|
||||
}
|
||||
(OperationDirection::Right, Sizing::Increase) => {
|
||||
if rect.left + rect.right + delta * 2
|
||||
> focused_monitor_work_area.right
|
||||
{
|
||||
direction = direction.opposite();
|
||||
rect.right = focused_monitor_work_area.right - rect.left;
|
||||
} else {
|
||||
rect.right += delta * 2;
|
||||
}
|
||||
}
|
||||
Axis::HorizontalAndVertical => direction = direction.opposite(),
|
||||
(OperationDirection::Right, Sizing::Decrease) => {
|
||||
rect.right -= delta * 2;
|
||||
}
|
||||
(OperationDirection::Up, Sizing::Increase) => {
|
||||
if rect.top - delta < focused_monitor_work_area.top {
|
||||
rect.top = focused_monitor_work_area.top;
|
||||
} else {
|
||||
rect.top -= delta;
|
||||
}
|
||||
}
|
||||
(OperationDirection::Up, Sizing::Decrease) => {
|
||||
rect.top += delta;
|
||||
}
|
||||
(OperationDirection::Down, Sizing::Increase) => {
|
||||
if rect.top + rect.bottom + delta * 2
|
||||
> focused_monitor_work_area.bottom
|
||||
{
|
||||
rect.bottom = focused_monitor_work_area.bottom - rect.top;
|
||||
} else {
|
||||
rect.bottom += delta * 2;
|
||||
}
|
||||
}
|
||||
(OperationDirection::Down, Sizing::Decrease) => {
|
||||
rect.bottom -= delta * 2;
|
||||
}
|
||||
}
|
||||
|
||||
WindowsApi::position_window(window.hwnd, &rect, false)?;
|
||||
if mouse_follows_focus {
|
||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
let resize = layout.resize(
|
||||
unaltered
|
||||
.get(focused_idx)
|
||||
.ok_or_else(|| anyhow!("there is no last layout"))?,
|
||||
focused_idx_resize,
|
||||
direction,
|
||||
sizing,
|
||||
delta,
|
||||
);
|
||||
|
||||
workspace.resize_dimensions_mut()[focused_idx] = resize;
|
||||
|
||||
return if update {
|
||||
self.update_focused_workspace(false, false)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
|
||||
tracing::warn!("cannot resize container in this direction");
|
||||
}
|
||||
Layout::Custom(_) => {
|
||||
tracing::warn!("containers cannot be resized when using custom layouts");
|
||||
WorkspaceLayer::Tiling => {
|
||||
match workspace.layout() {
|
||||
Layout::Default(layout) => {
|
||||
tracing::info!("resizing window");
|
||||
let len = NonZeroUsize::new(workspace.containers().len())
|
||||
.ok_or_else(|| anyhow!("there must be at least one container"))?;
|
||||
let focused_idx = workspace.focused_container_idx();
|
||||
let focused_idx_resize = workspace
|
||||
.resize_dimensions()
|
||||
.get(focused_idx)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("there is no resize adjustment for this container")
|
||||
})?;
|
||||
|
||||
if direction
|
||||
.destination(
|
||||
workspace.layout().as_boxed_direction().as_ref(),
|
||||
workspace.layout_flip(),
|
||||
focused_idx,
|
||||
len,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
let unaltered = layout.calculate(
|
||||
&focused_monitor_work_area,
|
||||
len,
|
||||
workspace.container_padding(),
|
||||
workspace.layout_flip(),
|
||||
&[],
|
||||
);
|
||||
|
||||
let mut direction = direction;
|
||||
|
||||
// We only ever want to operate on the unflipped Rect positions when resizing, then we
|
||||
// can flip them however they need to be flipped once the resizing has been done
|
||||
if let Some(flip) = workspace.layout_flip() {
|
||||
match flip {
|
||||
Axis::Horizontal => {
|
||||
if matches!(direction, OperationDirection::Left)
|
||||
|| matches!(direction, OperationDirection::Right)
|
||||
{
|
||||
direction = direction.opposite();
|
||||
}
|
||||
}
|
||||
Axis::Vertical => {
|
||||
if matches!(direction, OperationDirection::Up)
|
||||
|| matches!(direction, OperationDirection::Down)
|
||||
{
|
||||
direction = direction.opposite();
|
||||
}
|
||||
}
|
||||
Axis::HorizontalAndVertical => direction = direction.opposite(),
|
||||
}
|
||||
}
|
||||
|
||||
let resize = layout.resize(
|
||||
unaltered
|
||||
.get(focused_idx)
|
||||
.ok_or_else(|| anyhow!("there is no last layout"))?,
|
||||
focused_idx_resize,
|
||||
direction,
|
||||
sizing,
|
||||
delta,
|
||||
);
|
||||
|
||||
workspace.resize_dimensions_mut()[focused_idx] = resize;
|
||||
|
||||
return if update {
|
||||
self.update_focused_workspace(false, false)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
|
||||
tracing::warn!("cannot resize container in this direction");
|
||||
}
|
||||
Layout::Custom(_) => {
|
||||
tracing::warn!("containers cannot be resized when using custom layouts");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1860,6 +1946,56 @@ impl WindowManager {
|
||||
self.update_focused_workspace(mouse_follows_focus, true)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_floating_window_in_direction(
|
||||
&mut self,
|
||||
direction: OperationDirection,
|
||||
) -> Result<()> {
|
||||
let mouse_follows_focus = self.mouse_follows_focus;
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
|
||||
let mut target_idx = None;
|
||||
let len = focused_workspace.floating_windows().len();
|
||||
|
||||
if len > 1 {
|
||||
let focused_hwnd = WindowsApi::foreground_window()?;
|
||||
for (idx, window) in focused_workspace.floating_windows().iter().enumerate() {
|
||||
if window.hwnd == focused_hwnd {
|
||||
match direction {
|
||||
OperationDirection::Left => {}
|
||||
OperationDirection::Right => {}
|
||||
OperationDirection::Up => {
|
||||
if idx == len - 1 {
|
||||
target_idx = Some(0)
|
||||
} else {
|
||||
target_idx = Some(idx + 1)
|
||||
}
|
||||
}
|
||||
OperationDirection::Down => {
|
||||
if idx == 0 {
|
||||
target_idx = Some(len - 1)
|
||||
} else {
|
||||
target_idx = Some(idx - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if target_idx.is_none() {
|
||||
target_idx = Some(0);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(idx) = target_idx {
|
||||
if let Some(window) = focused_workspace.floating_windows().get(idx) {
|
||||
window.focus(mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> {
|
||||
self.handle_unmanaged_window_behaviour()?;
|
||||
@@ -2005,6 +2141,75 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn move_floating_window_in_direction(
|
||||
&mut self,
|
||||
direction: OperationDirection,
|
||||
) -> Result<()> {
|
||||
let mouse_follows_focus = self.mouse_follows_focus;
|
||||
|
||||
let mut focused_monitor_work_area = self.focused_monitor_work_area()?;
|
||||
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
|
||||
focused_monitor_work_area.left += border_offset;
|
||||
focused_monitor_work_area.left += border_width;
|
||||
focused_monitor_work_area.top += border_offset;
|
||||
focused_monitor_work_area.top += border_width;
|
||||
focused_monitor_work_area.right -= border_offset;
|
||||
focused_monitor_work_area.right -= border_width;
|
||||
focused_monitor_work_area.bottom -= border_offset;
|
||||
focused_monitor_work_area.bottom -= border_width;
|
||||
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
let delta = self.resize_delta;
|
||||
|
||||
let focused_hwnd = WindowsApi::foreground_window()?;
|
||||
for window in focused_workspace.floating_windows().iter() {
|
||||
if window.hwnd == focused_hwnd {
|
||||
let mut rect = WindowsApi::window_rect(window.hwnd)?;
|
||||
match direction {
|
||||
OperationDirection::Left => {
|
||||
if rect.left - delta < focused_monitor_work_area.left {
|
||||
rect.left = focused_monitor_work_area.left;
|
||||
} else {
|
||||
rect.left -= delta;
|
||||
}
|
||||
}
|
||||
OperationDirection::Right => {
|
||||
if rect.left + delta + rect.right > focused_monitor_work_area.right {
|
||||
rect.left = focused_monitor_work_area.right - rect.right;
|
||||
} else {
|
||||
rect.left += delta;
|
||||
}
|
||||
}
|
||||
OperationDirection::Up => {
|
||||
if rect.top - delta < focused_monitor_work_area.top {
|
||||
rect.top = focused_monitor_work_area.top;
|
||||
} else {
|
||||
rect.top -= delta;
|
||||
}
|
||||
}
|
||||
OperationDirection::Down => {
|
||||
if rect.top + delta + rect.bottom > focused_monitor_work_area.bottom {
|
||||
rect.top = focused_monitor_work_area.bottom - rect.bottom;
|
||||
} else {
|
||||
rect.top += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowsApi::position_window(window.hwnd, &rect, false)?;
|
||||
if mouse_follows_focus {
|
||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn move_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> {
|
||||
self.handle_unmanaged_window_behaviour()?;
|
||||
@@ -2181,6 +2386,54 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_floating_window_in_cycle_direction(
|
||||
&mut self,
|
||||
direction: CycleDirection,
|
||||
) -> Result<()> {
|
||||
let mouse_follows_focus = self.mouse_follows_focus;
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
|
||||
let mut target_idx = None;
|
||||
let len = focused_workspace.floating_windows().len();
|
||||
|
||||
if len > 1 {
|
||||
let focused_hwnd = WindowsApi::foreground_window()?;
|
||||
for (idx, window) in focused_workspace.floating_windows().iter().enumerate() {
|
||||
if window.hwnd == focused_hwnd {
|
||||
match direction {
|
||||
CycleDirection::Previous => {
|
||||
if idx == 0 {
|
||||
target_idx = Some(len - 1)
|
||||
} else {
|
||||
target_idx = Some(idx - 1)
|
||||
}
|
||||
}
|
||||
CycleDirection::Next => {
|
||||
if idx == len - 1 {
|
||||
target_idx = Some(0)
|
||||
} else {
|
||||
target_idx = Some(idx - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if target_idx.is_none() {
|
||||
target_idx = Some(0);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(idx) = target_idx {
|
||||
if let Some(window) = focused_workspace.floating_windows().get(idx) {
|
||||
window.focus(mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> {
|
||||
self.handle_unmanaged_window_behaviour()?;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
@@ -92,11 +94,29 @@ pub struct Workspace {
|
||||
pub window_container_behaviour_rules: Option<Vec<(usize, WindowContainerBehaviour)>>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub float_override: Option<bool>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub layer: WorkspaceLayer,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
pub workspace_config: Option<WorkspaceConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
pub enum WorkspaceLayer {
|
||||
#[default]
|
||||
Tiling,
|
||||
Floating,
|
||||
}
|
||||
|
||||
impl Display for WorkspaceLayer {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WorkspaceLayer::Tiling => write!(f, "Tiling"),
|
||||
WorkspaceLayer::Floating => write!(f, "Floating"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_ring_elements!(Workspace, Container);
|
||||
|
||||
impl Default for Workspace {
|
||||
@@ -122,6 +142,7 @@ impl Default for Workspace {
|
||||
window_container_behaviour_rules: None,
|
||||
float_override: None,
|
||||
workspace_config: None,
|
||||
layer: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1269,6 +1269,8 @@ enum SubCommand {
|
||||
/// mode, for the currently focused workspace. If there was no override value set for the
|
||||
/// workspace previously it takes the opposite of the global value.
|
||||
ToggleWorkspaceFloatOverride,
|
||||
/// Toggle between the Tiling and Floating layers on the focused workspace
|
||||
ToggleWorkspaceLayer,
|
||||
/// Toggle window tiling on the focused workspace
|
||||
TogglePause,
|
||||
/// Toggle window tiling on the focused workspace
|
||||
@@ -2854,6 +2856,9 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
SubCommand::ToggleWorkspaceFloatOverride => {
|
||||
send_message(&SocketMessage::ToggleWorkspaceFloatOverride)?;
|
||||
}
|
||||
SubCommand::ToggleWorkspaceLayer => {
|
||||
send_message(&SocketMessage::ToggleWorkspaceLayer)?;
|
||||
}
|
||||
SubCommand::WindowHidingBehaviour(arg) => {
|
||||
send_message(&SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour))?;
|
||||
}
|
||||
|
||||
@@ -177,6 +177,7 @@ nav:
|
||||
- cli/toggle-float-override.md
|
||||
- cli/toggle-workspace-window-container-behaviour.md
|
||||
- cli/toggle-workspace-float-override.md
|
||||
- cli/toggle-workspace-layer.md
|
||||
- cli/toggle-pause.md
|
||||
- cli/toggle-tiling.md
|
||||
- cli/toggle-float.md
|
||||
|
||||
@@ -504,6 +504,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspace_layer": {
|
||||
"description": "Configure the Workspace Layer widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Workspace Layer widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Configure the Workspaces widget",
|
||||
"type": "object",
|
||||
@@ -1811,6 +1824,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspace_layer": {
|
||||
"description": "Configure the Workspace Layer widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Workspace Layer widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Configure the Workspaces widget",
|
||||
"type": "object",
|
||||
@@ -3051,6 +3077,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspace_layer": {
|
||||
"description": "Configure the Workspace Layer widget",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"description": "Enable the Komorebi Workspace Layer widget",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Configure the Workspaces widget",
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user