mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-11 14:40:25 +01:00
feat(wm): allow scrolling layout with >1 monitors
This commit takes some inspiration from a trick I implemented in the komorebi for Mac codebase to hide windows that are not within the horizontal bounds of the work space area when the scrolling layer is active. Doing this ensures that such windows will not spill over into adjacent monitors (or at least, not for very long). There are some additional changes required in komorebi for Windows to try and make sure this plays nicely with the animations feature, which is not present in komorebi for Mac.
This commit is contained in:
@@ -802,7 +802,7 @@ 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) {
|
||||
match window.set_position(&self.size_rect, false, false) {
|
||||
Ok(_) => {
|
||||
tracing::info!("updated bar position");
|
||||
}
|
||||
|
||||
@@ -85,6 +85,15 @@ impl Rect {
|
||||
&& point.1 <= self.top + self.bottom
|
||||
}
|
||||
|
||||
pub fn contains_within_horizontal_bounds(&self, other: &Rect) -> bool {
|
||||
let left_corner_is_within_bounds =
|
||||
other.left >= self.left && other.left < self.left + self.right;
|
||||
let right_corner_is_within_bounds = other.left + other.right >= self.left
|
||||
&& other.left + other.right < self.left + self.right;
|
||||
|
||||
left_corner_is_within_bounds || right_corner_is_within_bounds
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn scale(&self, system_dpi: i32, rect_dpi: i32) -> Rect {
|
||||
Rect {
|
||||
|
||||
@@ -355,7 +355,7 @@ impl Stackbar {
|
||||
// tile
|
||||
if index != focused_window_idx
|
||||
&& let Err(err) =
|
||||
window.set_position(&focused_window_rect, false)
|
||||
window.set_position(&focused_window_rect, false, false)
|
||||
{
|
||||
tracing::error!(
|
||||
"stackbar WM_LBUTTONDOWN repositioning error: hwnd {} ({})",
|
||||
|
||||
@@ -1361,8 +1361,6 @@ impl StaticConfig {
|
||||
workspace_matching_rules.clear();
|
||||
drop(workspace_matching_rules);
|
||||
|
||||
let monitor_count = wm.monitors().len();
|
||||
|
||||
let offset = wm.work_area_offset;
|
||||
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
||||
let preferred_config_idx = {
|
||||
@@ -1411,15 +1409,6 @@ impl StaticConfig {
|
||||
monitor.update_workspaces_globals(offset);
|
||||
for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() {
|
||||
if let Some(workspace_config) = monitor_config.workspaces.get_mut(j) {
|
||||
if monitor_count > 1
|
||||
&& matches!(workspace_config.layout, Some(DefaultLayout::Scrolling))
|
||||
{
|
||||
tracing::warn!(
|
||||
"scrolling layout is only supported for a single monitor; falling back to columns layout"
|
||||
);
|
||||
workspace_config.layout = Some(DefaultLayout::Columns);
|
||||
}
|
||||
|
||||
ws.load_static_config(workspace_config)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +165,7 @@ struct MovementRenderDispatcher {
|
||||
target_rect: Rect,
|
||||
top: bool,
|
||||
style: AnimationStyle,
|
||||
end_with_hide: bool,
|
||||
}
|
||||
|
||||
impl MovementRenderDispatcher {
|
||||
@@ -176,6 +177,7 @@ impl MovementRenderDispatcher {
|
||||
target_rect: Rect,
|
||||
top: bool,
|
||||
style: AnimationStyle,
|
||||
end_with_hide: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
hwnd,
|
||||
@@ -183,6 +185,7 @@ impl MovementRenderDispatcher {
|
||||
target_rect,
|
||||
top,
|
||||
style,
|
||||
end_with_hide,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,6 +196,11 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
||||
}
|
||||
|
||||
fn pre_render(&self) -> eyre::Result<()> {
|
||||
let window = Window::from(self.hwnd);
|
||||
if window.is_cloaked().unwrap_or(true) {
|
||||
window.restore()
|
||||
}
|
||||
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
stackbar_manager::send_notification();
|
||||
|
||||
@@ -213,7 +221,13 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
||||
fn post_render(&self) -> eyre::Result<()> {
|
||||
// we don't add the async_window_pos flag here because animations
|
||||
// are always run on a separate thread
|
||||
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top, false)?;
|
||||
if self.end_with_hide {
|
||||
let window = Window::from(self.hwnd);
|
||||
window.hide();
|
||||
} else {
|
||||
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top, false)?;
|
||||
}
|
||||
|
||||
if ANIMATION_MANAGER
|
||||
.lock()
|
||||
.count_in_progress(MovementRenderDispatcher::PREFIX)
|
||||
@@ -383,7 +397,7 @@ impl Window {
|
||||
let anim_count = ANIMATION_MANAGER
|
||||
.lock()
|
||||
.count_in_progress(MovementRenderDispatcher::PREFIX);
|
||||
self.set_position(&new_rect, true)?;
|
||||
self.set_position(&new_rect, true, false)?;
|
||||
let hwnd = self.hwnd;
|
||||
// Wait for the animation to finish before maximizing the window again, otherwise
|
||||
// we would be maximizing the window on the current monitor anyway
|
||||
@@ -402,11 +416,11 @@ impl Window {
|
||||
windows_api::WindowsApi::maximize_window(hwnd);
|
||||
});
|
||||
} else {
|
||||
self.set_position(&new_rect, true)?;
|
||||
self.set_position(&new_rect, true, false)?;
|
||||
windows_api::WindowsApi::maximize_window(self.hwnd);
|
||||
}
|
||||
} else {
|
||||
self.set_position(&new_rect, true)?;
|
||||
self.set_position(&new_rect, true, false)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -436,10 +450,11 @@ impl Window {
|
||||
bottom: target_height,
|
||||
},
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_position(&self, layout: &Rect, top: bool) -> eyre::Result<()> {
|
||||
pub fn set_position(&self, layout: &Rect, top: bool, end_with_hide: bool) -> eyre::Result<()> {
|
||||
let window_rect = WindowsApi::window_rect(self.hwnd)?;
|
||||
|
||||
if window_rect.eq(layout) {
|
||||
@@ -463,11 +478,24 @@ impl Window {
|
||||
.get(&MovementRenderDispatcher::PREFIX)
|
||||
.unwrap_or(&ANIMATION_STYLE_GLOBAL.lock());
|
||||
|
||||
let render_dispatcher =
|
||||
MovementRenderDispatcher::new(self.hwnd, window_rect, *layout, top, style);
|
||||
let render_dispatcher = MovementRenderDispatcher::new(
|
||||
self.hwnd,
|
||||
window_rect,
|
||||
*layout,
|
||||
top,
|
||||
style,
|
||||
end_with_hide,
|
||||
);
|
||||
|
||||
AnimationEngine::animate(render_dispatcher, duration)
|
||||
} else if end_with_hide {
|
||||
self.hide();
|
||||
Ok(())
|
||||
} else {
|
||||
if self.is_cloaked().unwrap_or(true) {
|
||||
self.restore()
|
||||
}
|
||||
|
||||
WindowsApi::position_window(self.hwnd, layout, top, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3088,16 +3088,8 @@ impl WindowManager {
|
||||
pub fn change_workspace_layout_default(&mut self, layout: DefaultLayout) -> eyre::Result<()> {
|
||||
tracing::info!("changing layout");
|
||||
|
||||
let monitor_count = self.monitors().len();
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
if monitor_count > 1 && matches!(layout, DefaultLayout::Scrolling) {
|
||||
tracing::warn!(
|
||||
"scrolling layout is only supported for a single monitor; not changing layout"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match &workspace.layout {
|
||||
Layout::Default(_) => {}
|
||||
Layout::Custom(layout) => {
|
||||
|
||||
@@ -531,7 +531,7 @@ impl Workspace {
|
||||
adjusted_work_area.add_padding(container_padding);
|
||||
adjusted_work_area.add_padding(border_offset);
|
||||
adjusted_work_area.add_padding(border_width);
|
||||
window.set_position(&adjusted_work_area, true)?;
|
||||
window.set_position(&adjusted_work_area, true, false)?;
|
||||
};
|
||||
} else if let Some(window) = &mut self.maximized_window {
|
||||
window.maximize();
|
||||
@@ -553,6 +553,8 @@ impl Workspace {
|
||||
let no_titlebar = NO_TITLEBAR.lock().clone();
|
||||
let regex_identifiers = REGEX_IDENTIFIERS.lock().clone();
|
||||
|
||||
let is_scrolling = matches!(self.layout, Layout::Default(DefaultLayout::Scrolling));
|
||||
|
||||
let containers = self.containers_mut();
|
||||
|
||||
for (i, container) in containers.iter_mut().enumerate() {
|
||||
@@ -597,7 +599,13 @@ impl Workspace {
|
||||
WindowsApi::restore_window(window.hwnd);
|
||||
}
|
||||
}
|
||||
window.set_position(layout, false)?;
|
||||
|
||||
window.set_position(
|
||||
layout,
|
||||
false,
|
||||
is_scrolling
|
||||
&& !work_area.contains_within_horizontal_bounds(layout),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1581,6 +1589,14 @@ impl Workspace {
|
||||
tracing::info!("focusing container");
|
||||
|
||||
self.containers.focus(idx);
|
||||
|
||||
if matches!(self.layout, Layout::Default(DefaultLayout::Scrolling))
|
||||
&& let Some(container) = self.focused_container()
|
||||
&& let Some(window) = container.focused_window()
|
||||
&& window.is_cloaked().unwrap_or(true)
|
||||
{
|
||||
window.restore();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_containers(&mut self, i: usize, j: usize) {
|
||||
|
||||
Reference in New Issue
Block a user