mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-15 14:23:26 +01:00
Compare commits
1 Commits
master
...
feature/st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b575cba9f4 |
@@ -26,13 +26,29 @@ impl From<RECT> for Rect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Rect> for RECT {
|
||||||
|
fn from(rect: Rect) -> Self {
|
||||||
|
Self {
|
||||||
|
left: rect.left,
|
||||||
|
top: rect.top,
|
||||||
|
right: rect.right,
|
||||||
|
bottom: rect.bottom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
/// decrease the size of self by the padding amount.
|
/// decrease the size of self by the padding amount.
|
||||||
pub fn add_padding(&mut self, padding: i32) {
|
pub fn add_padding<T>(&mut self, padding: T)
|
||||||
self.left += padding;
|
where
|
||||||
self.top += padding;
|
T: Into<Option<i32>>,
|
||||||
self.right -= padding * 2;
|
{
|
||||||
self.bottom -= padding * 2;
|
if let Some(padding) = padding.into() {
|
||||||
|
self.left += padding;
|
||||||
|
self.top += padding;
|
||||||
|
self.right -= padding * 2;
|
||||||
|
self.bottom -= padding * 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// increase the size of self by the margin amount.
|
/// increase the size of self by the margin amount.
|
||||||
@@ -43,6 +59,14 @@ impl Rect {
|
|||||||
self.bottom += margin * 2;
|
self.bottom += margin * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn left_padding(&mut self, padding: i32) {
|
||||||
|
self.left += padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right_padding(&mut self, padding: i32) {
|
||||||
|
self.right -= padding;
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn contains_point(&self, point: (i32, i32)) -> bool {
|
pub const fn contains_point(&self, point: (i32, i32)) -> bool {
|
||||||
point.0 >= self.left
|
point.0 >= self.left
|
||||||
|
|||||||
@@ -7,13 +7,18 @@ use serde::Deserialize;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
|
use crate::stackbar::Stackbar;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
|
use crate::StackbarMode;
|
||||||
|
use crate::STACKBAR_MODE;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
#[getset(get = "pub")]
|
#[getset(get = "pub")]
|
||||||
id: String,
|
id: String,
|
||||||
windows: Ring<Window>,
|
windows: Ring<Window>,
|
||||||
|
#[getset(get = "pub", get_mut = "pub")]
|
||||||
|
stackbar: Option<Stackbar>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ring_elements!(Container, Window);
|
impl_ring_elements!(Container, Window);
|
||||||
@@ -23,6 +28,10 @@ impl Default for Container {
|
|||||||
Self {
|
Self {
|
||||||
id: nanoid!(),
|
id: nanoid!(),
|
||||||
windows: Ring::default(),
|
windows: Ring::default(),
|
||||||
|
stackbar: match *STACKBAR_MODE.lock() {
|
||||||
|
StackbarMode::Always => Stackbar::create().ok(),
|
||||||
|
StackbarMode::Never | StackbarMode::OnStack => None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +43,38 @@ impl PartialEq for Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
|
pub fn hide(&self, omit: Option<isize>) {
|
||||||
|
if let Some(stackbar) = self.stackbar() {
|
||||||
|
stackbar.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
for window in self.windows().iter().rev() {
|
||||||
|
let mut should_hide = omit.is_none();
|
||||||
|
|
||||||
|
if !should_hide {
|
||||||
|
if let Some(omit) = omit {
|
||||||
|
if omit != window.hwnd {
|
||||||
|
should_hide = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_hide {
|
||||||
|
window.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore(&self) {
|
||||||
|
if let Some(stackbar) = self.stackbar() {
|
||||||
|
stackbar.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(window) = self.focused_window() {
|
||||||
|
window.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_focused_window(&mut self) {
|
pub fn load_focused_window(&mut self) {
|
||||||
let focused_idx = self.focused_window_idx();
|
let focused_idx = self.focused_window_idx();
|
||||||
for (i, window) in self.windows_mut().iter_mut().enumerate() {
|
for (i, window) in self.windows_mut().iter_mut().enumerate() {
|
||||||
@@ -81,6 +122,10 @@ impl Container {
|
|||||||
pub fn remove_window_by_idx(&mut self, idx: usize) -> Option<Window> {
|
pub fn remove_window_by_idx(&mut self, idx: usize) -> Option<Window> {
|
||||||
let window = self.windows_mut().remove(idx);
|
let window = self.windows_mut().remove(idx);
|
||||||
|
|
||||||
|
if matches!(*STACKBAR_MODE.lock(), StackbarMode::OnStack) && self.windows().len() <= 1 {
|
||||||
|
self.stackbar = None;
|
||||||
|
}
|
||||||
|
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
self.focus_window(idx - 1);
|
self.focus_window(idx - 1);
|
||||||
};
|
};
|
||||||
@@ -95,6 +140,14 @@ impl Container {
|
|||||||
|
|
||||||
pub fn add_window(&mut self, window: Window) {
|
pub fn add_window(&mut self, window: Window) {
|
||||||
self.windows_mut().push_back(window);
|
self.windows_mut().push_back(window);
|
||||||
|
|
||||||
|
if matches!(*STACKBAR_MODE.lock(), StackbarMode::OnStack)
|
||||||
|
&& self.windows().len() > 1
|
||||||
|
&& self.stackbar.is_none()
|
||||||
|
{
|
||||||
|
self.stackbar = Stackbar::create().ok();
|
||||||
|
}
|
||||||
|
|
||||||
self.focus_window(self.windows().len() - 1);
|
self.focus_window(self.windows().len() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub mod process_command;
|
|||||||
pub mod process_event;
|
pub mod process_event;
|
||||||
pub mod process_movement;
|
pub mod process_movement;
|
||||||
pub mod set_window_position;
|
pub mod set_window_position;
|
||||||
|
pub mod stackbar;
|
||||||
pub mod static_config;
|
pub mod static_config;
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
@@ -23,6 +24,7 @@ pub mod workspace;
|
|||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
@@ -38,6 +40,7 @@ use std::sync::Arc;
|
|||||||
pub use hidden::*;
|
pub use hidden::*;
|
||||||
pub use process_command::*;
|
pub use process_command::*;
|
||||||
pub use process_event::*;
|
pub use process_event::*;
|
||||||
|
pub use stackbar::*;
|
||||||
pub use static_config::*;
|
pub use static_config::*;
|
||||||
pub use window_manager::*;
|
pub use window_manager::*;
|
||||||
pub use window_manager_event::*;
|
pub use window_manager_event::*;
|
||||||
@@ -198,6 +201,11 @@ lazy_static! {
|
|||||||
// Use app-specific titlebar removal options where possible
|
// Use app-specific titlebar removal options where possible
|
||||||
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
||||||
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
||||||
|
|
||||||
|
static ref STACKBAR_MODE: Arc<Mutex<StackbarMode >> = Arc::new(Mutex::new(StackbarMode::Never));
|
||||||
|
static ref WINDOWS_BY_BAR_HWNDS: Arc<Mutex<HashMap<isize, VecDeque<isize>>>> =
|
||||||
|
Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10);
|
pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10);
|
||||||
@@ -222,6 +230,12 @@ pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
|||||||
|
|
||||||
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||||
|
|
||||||
|
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
|
||||||
|
pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947); // gray text
|
||||||
|
pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray
|
||||||
|
pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40);
|
||||||
|
pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
||||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ impl Monitor {
|
|||||||
if i == focused_idx {
|
if i == focused_idx {
|
||||||
workspace.restore(mouse_follows_focus)?;
|
workspace.restore(mouse_follows_focus)?;
|
||||||
} else {
|
} else {
|
||||||
workspace.hide();
|
workspace.hide(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ impl WindowManager {
|
|||||||
SocketMessage::UnstackWindow => self.remove_window_from_container()?,
|
SocketMessage::UnstackWindow => self.remove_window_from_container()?,
|
||||||
SocketMessage::CycleStack(direction) => {
|
SocketMessage::CycleStack(direction) => {
|
||||||
self.cycle_container_window_in_direction(direction)?;
|
self.cycle_container_window_in_direction(direction)?;
|
||||||
|
self.focused_window()?.focus(self.mouse_follows_focus)?;
|
||||||
}
|
}
|
||||||
SocketMessage::ForceFocus => {
|
SocketMessage::ForceFocus => {
|
||||||
let focused_window = self.focused_window()?;
|
let focused_window = self.focused_window()?;
|
||||||
|
|||||||
@@ -518,6 +518,9 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WindowManagerEvent::ForceUpdate(_) => {
|
||||||
|
self.update_focused_workspace(false)?;
|
||||||
|
}
|
||||||
WindowManagerEvent::DisplayChange(..)
|
WindowManagerEvent::DisplayChange(..)
|
||||||
| WindowManagerEvent::MouseCapture(..)
|
| WindowManagerEvent::MouseCapture(..)
|
||||||
| WindowManagerEvent::Cloak(..)
|
| WindowManagerEvent::Cloak(..)
|
||||||
|
|||||||
294
komorebi/src/stackbar.rs
Normal file
294
komorebi/src/stackbar.rs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use windows::core::PCWSTR;
|
||||||
|
use windows::Win32::Foundation::COLORREF;
|
||||||
|
use windows::Win32::Foundation::HWND;
|
||||||
|
use windows::Win32::Foundation::LPARAM;
|
||||||
|
use windows::Win32::Foundation::LRESULT;
|
||||||
|
use windows::Win32::Foundation::WPARAM;
|
||||||
|
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
|
||||||
|
use windows::Win32::Graphics::Gdi::CreatePen;
|
||||||
|
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||||
|
use windows::Win32::Graphics::Gdi::DrawTextW;
|
||||||
|
use windows::Win32::Graphics::Gdi::GetDC;
|
||||||
|
use windows::Win32::Graphics::Gdi::ReleaseDC;
|
||||||
|
use windows::Win32::Graphics::Gdi::SelectObject;
|
||||||
|
use windows::Win32::Graphics::Gdi::SetBkColor;
|
||||||
|
use windows::Win32::Graphics::Gdi::SetTextColor;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_CENTER;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_SINGLELINE;
|
||||||
|
use windows::Win32::Graphics::Gdi::DT_VCENTER;
|
||||||
|
use windows::Win32::Graphics::Gdi::FONT_QUALITY;
|
||||||
|
use windows::Win32::Graphics::Gdi::FW_BOLD;
|
||||||
|
use windows::Win32::Graphics::Gdi::LOGFONTW;
|
||||||
|
use windows::Win32::Graphics::Gdi::PROOF_QUALITY;
|
||||||
|
use windows::Win32::Graphics::Gdi::PS_SOLID;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::SW_SHOW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
|
||||||
|
|
||||||
|
use komorebi_core::Rect;
|
||||||
|
|
||||||
|
use crate::window::Window;
|
||||||
|
use crate::windows_api::WindowsApi;
|
||||||
|
use crate::winevent::WinEvent;
|
||||||
|
use crate::winevent_listener;
|
||||||
|
use crate::WindowManagerEvent;
|
||||||
|
use crate::DEFAULT_CONTAINER_PADDING;
|
||||||
|
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
|
use crate::STACKBAR_TAB_HEIGHT;
|
||||||
|
use crate::STACKBAR_TAB_WIDTH;
|
||||||
|
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::TRANSPARENCY_COLOUR;
|
||||||
|
use crate::WINDOWS_BY_BAR_HWNDS;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct Stackbar {
|
||||||
|
pub(crate) hwnd: isize,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub is_cloned: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Stackbar {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.is_cloned {
|
||||||
|
let _ = WindowsApi::close_window(self.hwnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Stackbar {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
hwnd: self.hwnd,
|
||||||
|
is_cloned: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Stackbar {
|
||||||
|
unsafe extern "system" fn window_proc(
|
||||||
|
hwnd: HWND,
|
||||||
|
msg: u32,
|
||||||
|
w_param: WPARAM,
|
||||||
|
l_param: LPARAM,
|
||||||
|
) -> LRESULT {
|
||||||
|
match msg {
|
||||||
|
WM_LBUTTONDOWN => {
|
||||||
|
let win_hwnds_by_topbar = WINDOWS_BY_BAR_HWNDS.lock();
|
||||||
|
if let Some(win_hwnds) = win_hwnds_by_topbar.get(&hwnd.0) {
|
||||||
|
let x = l_param.0 as i32 & 0xFFFF;
|
||||||
|
let y = (l_param.0 as i32 >> 16) & 0xFFFF;
|
||||||
|
|
||||||
|
let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst);
|
||||||
|
let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
||||||
|
let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
for (index, win_hwnd) in win_hwnds.iter().enumerate() {
|
||||||
|
let left = gap + (index as i32 * (width + gap));
|
||||||
|
let right = left + width;
|
||||||
|
let top = 0;
|
||||||
|
let bottom = height;
|
||||||
|
|
||||||
|
if x >= left && x <= right && y >= top && y <= bottom {
|
||||||
|
let window = Window { hwnd: *win_hwnd };
|
||||||
|
let event_sender = winevent_listener::event_tx();
|
||||||
|
let _ = event_sender.send(WindowManagerEvent::FocusChange(
|
||||||
|
WinEvent::ObjectFocus,
|
||||||
|
window,
|
||||||
|
));
|
||||||
|
let _ = event_sender.send(WindowManagerEvent::ForceUpdate(window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WINDOWS_BY_BAR_HWNDS.force_unlock();
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
WM_DESTROY => {
|
||||||
|
PostQuitMessage(0);
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
_ => DefWindowProcW(hwnd, msg, w_param, l_param),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn hwnd(&self) -> HWND {
|
||||||
|
HWND(self.hwnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create() -> Result<Stackbar> {
|
||||||
|
let name: Vec<u16> = "komorebi_stackbar\0".encode_utf16().collect();
|
||||||
|
let class_name = PCWSTR(name.as_ptr());
|
||||||
|
|
||||||
|
let h_module = WindowsApi::module_handle_w()?;
|
||||||
|
|
||||||
|
let wnd_class = WNDCLASSW {
|
||||||
|
style: CS_HREDRAW | CS_VREDRAW,
|
||||||
|
lpfnWndProc: Some(Self::window_proc),
|
||||||
|
hInstance: h_module.into(),
|
||||||
|
lpszClassName: class_name,
|
||||||
|
hbrBackground: WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
RegisterClassW(&wnd_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (hwnd_sender, hwnd_receiver) = crossbeam_channel::bounded::<HWND>(1);
|
||||||
|
|
||||||
|
let name_cl = name.clone();
|
||||||
|
std::thread::spawn(move || -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let hwnd = CreateWindowExW(
|
||||||
|
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
|
||||||
|
PCWSTR(name_cl.as_ptr()),
|
||||||
|
PCWSTR(name_cl.as_ptr()),
|
||||||
|
WS_POPUP | WS_VISIBLE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
h_module,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY)?;
|
||||||
|
hwnd_sender.send(hwnd)?;
|
||||||
|
|
||||||
|
let mut msg = MSG::default();
|
||||||
|
while GetMessageW(&mut msg, hwnd, 0, 0).into() {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
std::thread::sleep(Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
hwnd: hwnd_receiver.recv()?.0,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> {
|
||||||
|
WindowsApi::position_window(self.hwnd(), layout, top)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_position_from_container_layout(&self, layout: &Rect) -> Rect {
|
||||||
|
Rect {
|
||||||
|
bottom: STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst),
|
||||||
|
..*layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self, windows: &VecDeque<Window>, focused_hwnd: isize) -> Result<()> {
|
||||||
|
let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst);
|
||||||
|
let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
||||||
|
let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
||||||
|
let background = STACKBAR_TAB_BACKGROUND_COLOUR.load(Ordering::SeqCst);
|
||||||
|
let focused_text_colour = STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst);
|
||||||
|
let unfocused_text_colour = STACKBAR_UNFOCUSED_TEXT_COLOUR.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let hdc = GetDC(self.hwnd());
|
||||||
|
|
||||||
|
let hpen = CreatePen(PS_SOLID, 0, COLORREF(background));
|
||||||
|
let hbrush = CreateSolidBrush(COLORREF(background));
|
||||||
|
|
||||||
|
SelectObject(hdc, hpen);
|
||||||
|
SelectObject(hdc, hbrush);
|
||||||
|
SetBkColor(hdc, COLORREF(background));
|
||||||
|
|
||||||
|
let hfont = CreateFontIndirectW(&LOGFONTW {
|
||||||
|
lfWeight: FW_BOLD.0 as i32,
|
||||||
|
lfQuality: FONT_QUALITY(PROOF_QUALITY.0),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectObject(hdc, hfont);
|
||||||
|
|
||||||
|
for (i, window) in windows.iter().enumerate() {
|
||||||
|
if window.hwnd == focused_hwnd {
|
||||||
|
SetTextColor(hdc, COLORREF(focused_text_colour));
|
||||||
|
|
||||||
|
window.focus(false)?;
|
||||||
|
} else {
|
||||||
|
SetTextColor(hdc, COLORREF(unfocused_text_colour));
|
||||||
|
}
|
||||||
|
|
||||||
|
let left = gap + (i as i32 * (width + gap));
|
||||||
|
let mut tab_box = Rect {
|
||||||
|
top: 0,
|
||||||
|
left,
|
||||||
|
right: left + width,
|
||||||
|
bottom: height,
|
||||||
|
};
|
||||||
|
|
||||||
|
WindowsApi::round_rect(hdc, &tab_box, 8);
|
||||||
|
|
||||||
|
let exe = window.exe()?;
|
||||||
|
let exe_trimmed = exe.trim_end_matches(".exe");
|
||||||
|
let mut tab_title: Vec<u16> = exe_trimmed.encode_utf16().collect();
|
||||||
|
|
||||||
|
tab_box.left_padding(10);
|
||||||
|
tab_box.right_padding(10);
|
||||||
|
|
||||||
|
DrawTextW(
|
||||||
|
hdc,
|
||||||
|
&mut tab_title,
|
||||||
|
&mut tab_box.into(),
|
||||||
|
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseDC(self.hwnd(), hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut windows_hwdns: VecDeque<isize> = VecDeque::new();
|
||||||
|
for window in windows {
|
||||||
|
windows_hwdns.push_back(window.hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
WINDOWS_BY_BAR_HWNDS.lock().insert(self.hwnd, windows_hwdns);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide(&self) {
|
||||||
|
WindowsApi::hide_window(self.hwnd())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore(&self) {
|
||||||
|
WindowsApi::show_window(self.hwnd(), SW_SHOW)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,12 @@ use crate::MANAGE_IDENTIFIERS;
|
|||||||
use crate::MONITOR_INDEX_PREFERENCES;
|
use crate::MONITOR_INDEX_PREFERENCES;
|
||||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||||
use crate::REGEX_IDENTIFIERS;
|
use crate::REGEX_IDENTIFIERS;
|
||||||
|
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::STACKBAR_MODE;
|
||||||
|
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
|
use crate::STACKBAR_TAB_HEIGHT;
|
||||||
|
use crate::STACKBAR_TAB_WIDTH;
|
||||||
|
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
use crate::WORKSPACE_RULES;
|
use crate::WORKSPACE_RULES;
|
||||||
|
|
||||||
@@ -293,6 +299,30 @@ pub struct StaticConfig {
|
|||||||
/// Set display index preferences
|
/// Set display index preferences
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub display_index_preferences: Option<HashMap<usize, String>>,
|
pub display_index_preferences: Option<HashMap<usize, String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub stackbar: Option<StackbarConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub enum StackbarMode {
|
||||||
|
Always,
|
||||||
|
Never,
|
||||||
|
OnStack,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct TabsConfig {
|
||||||
|
width: Option<i32>,
|
||||||
|
focused_text: Option<Colour>,
|
||||||
|
unfocused_text: Option<Colour>,
|
||||||
|
background: Option<Colour>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct StackbarConfig {
|
||||||
|
height: Option<i32>,
|
||||||
|
mode: Option<StackbarMode>,
|
||||||
|
tabs: Option<TabsConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&WindowManager> for StaticConfig {
|
impl From<&WindowManager> for StaticConfig {
|
||||||
@@ -398,6 +428,7 @@ impl From<&WindowManager> for StaticConfig {
|
|||||||
object_name_change_applications: None,
|
object_name_change_applications: None,
|
||||||
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
|
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
|
||||||
display_index_preferences: Option::from(DISPLAY_INDEX_PREFERENCES.lock().clone()),
|
display_index_preferences: Option::from(DISPLAY_INDEX_PREFERENCES.lock().clone()),
|
||||||
|
stackbar: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,6 +521,33 @@ impl StaticConfig {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(stackbar) = &self.stackbar {
|
||||||
|
if let Some(height) = &stackbar.height {
|
||||||
|
STACKBAR_TAB_HEIGHT.store(*height, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
if let Some(mode) = &stackbar.mode {
|
||||||
|
let mut stackbar_mode = STACKBAR_MODE.lock();
|
||||||
|
*stackbar_mode = *mode;
|
||||||
|
}
|
||||||
|
if let Some(tabs) = &stackbar.tabs {
|
||||||
|
if let Some(background) = &tabs.background {
|
||||||
|
STACKBAR_TAB_BACKGROUND_COLOUR.store((*background).into(), Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(colour) = &tabs.focused_text {
|
||||||
|
STACKBAR_FOCUSED_TEXT_COLOUR.store((*colour).into(), Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(colour) = &tabs.unfocused_text {
|
||||||
|
STACKBAR_UNFOCUSED_TEXT_COLOUR.store((*colour).into(), Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(width) = &tabs.width {
|
||||||
|
STACKBAR_TAB_WIDTH.store(*width, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(path) = &self.app_specific_configuration_path {
|
if let Some(path) = &self.app_specific_configuration_path {
|
||||||
let path = resolve_home_path(path)?;
|
let path = resolve_home_path(path)?;
|
||||||
let content = std::fs::read_to_string(path)?;
|
let content = std::fs::read_to_string(path)?;
|
||||||
|
|||||||
@@ -841,16 +841,16 @@ impl WindowManager {
|
|||||||
if !follow_focus && self.focused_container_mut().is_ok() {
|
if !follow_focus && self.focused_container_mut().is_ok() {
|
||||||
// and we have a stack with >1 windows
|
// and we have a stack with >1 windows
|
||||||
if self.focused_container_mut()?.windows().len() > 1
|
if self.focused_container_mut()?.windows().len() > 1
|
||||||
// and we don't have a maxed window
|
// and we don't have a maxed window
|
||||||
&& self.focused_workspace()?.maximized_window().is_none()
|
&& self.focused_workspace()?.maximized_window().is_none()
|
||||||
// and we don't have a monocle container
|
// and we don't have a monocle container
|
||||||
&& self.focused_workspace()?.monocle_container().is_none()
|
&& self.focused_workspace()?.monocle_container().is_none()
|
||||||
{
|
{
|
||||||
if let Ok(window) = self.focused_window_mut() {
|
if let Ok(window) = self.focused_window_mut() {
|
||||||
window.focus(self.mouse_follows_focus)?;
|
window.focus(self.mouse_follows_focus)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// This is to correctly restore and focus when switching to a workspace which
|
// This is to correctly restore and focus when switching to a workspace which
|
||||||
// contains a managed maximized window
|
// contains a managed maximized window
|
||||||
@@ -1197,7 +1197,21 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.focused_window_mut()?.focus(self.mouse_follows_focus)?;
|
// When switching workspaces and landing focus on a window that is not stack, but a stack
|
||||||
|
// exists, and there is a stackbar visible, when changing focus to that container stack,
|
||||||
|
// the focused text colour will not be applied until the stack has been cycled at least once
|
||||||
|
//
|
||||||
|
// With this piece of code, we check if we have changed focus to a container stack with
|
||||||
|
// a stackbar, and if we have, we run a quick update to make sure the focused text colour
|
||||||
|
// has been applied
|
||||||
|
let focused_window = self.focused_window_mut()?;
|
||||||
|
let focused_window_hwnd = focused_window.hwnd;
|
||||||
|
focused_window.focus(self.mouse_follows_focus)?;
|
||||||
|
|
||||||
|
let focused_container = self.focused_container()?;
|
||||||
|
if let Some(stackbar) = focused_container.stackbar() {
|
||||||
|
stackbar.update(focused_container.windows(), focused_window_hwnd)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ pub enum WindowManagerEvent {
|
|||||||
Unmanage(Window),
|
Unmanage(Window),
|
||||||
Raise(Window),
|
Raise(Window),
|
||||||
DisplayChange(Window),
|
DisplayChange(Window),
|
||||||
|
ForceUpdate(Window),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for WindowManagerEvent {
|
impl Display for WindowManagerEvent {
|
||||||
@@ -78,6 +79,9 @@ impl Display for WindowManagerEvent {
|
|||||||
Self::DisplayChange(window) => {
|
Self::DisplayChange(window) => {
|
||||||
write!(f, "DisplayChange (Window: {window})")
|
write!(f, "DisplayChange (Window: {window})")
|
||||||
}
|
}
|
||||||
|
Self::ForceUpdate(window) => {
|
||||||
|
write!(f, "ForceUpdate (Window: {window})")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +102,8 @@ impl WindowManagerEvent {
|
|||||||
| Self::Raise(window)
|
| Self::Raise(window)
|
||||||
| Self::Manage(window)
|
| Self::Manage(window)
|
||||||
| Self::DisplayChange(window)
|
| Self::DisplayChange(window)
|
||||||
| Self::Unmanage(window) => window,
|
| Self::Unmanage(window)
|
||||||
|
| Self::ForceUpdate(window) => window,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
|||||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||||
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
|
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
|
||||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||||
|
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||||
|
use windows::Win32::Graphics::Gdi::RoundRect;
|
||||||
use windows::Win32::Graphics::Gdi::DISPLAY_DEVICEW;
|
use windows::Win32::Graphics::Gdi::DISPLAY_DEVICEW;
|
||||||
use windows::Win32::Graphics::Gdi::HBRUSH;
|
use windows::Win32::Graphics::Gdi::HBRUSH;
|
||||||
use windows::Win32::Graphics::Gdi::HDC;
|
use windows::Win32::Graphics::Gdi::HDC;
|
||||||
@@ -66,6 +68,7 @@ use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
|
|||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
|
||||||
|
use windows::Win32::UI::Input::KeyboardAndMouse::VK_LBUTTON;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
|
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
||||||
@@ -418,7 +421,7 @@ impl WindowsApi {
|
|||||||
.process()
|
.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
pub fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
||||||
// BOOL is returned but does not signify whether or not the operation was succesful
|
// BOOL is returned but does not signify whether or not the operation was succesful
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||||
unsafe { ShowWindow(hwnd, command) };
|
unsafe { ShowWindow(hwnd, command) };
|
||||||
@@ -527,6 +530,24 @@ impl WindowsApi {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn round_rect(hdc: HDC, rect: &Rect, border_radius: i32) {
|
||||||
|
unsafe {
|
||||||
|
RoundRect(
|
||||||
|
hdc,
|
||||||
|
rect.left,
|
||||||
|
rect.top,
|
||||||
|
rect.right,
|
||||||
|
rect.bottom,
|
||||||
|
border_radius,
|
||||||
|
border_radius,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn rectangle(hdc: HDC, rect: &Rect) {
|
||||||
|
unsafe {
|
||||||
|
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
|
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
|
||||||
unsafe { SetCursorPos(x, y) }.process()
|
unsafe { SetCursorPos(x, y) }.process()
|
||||||
}
|
}
|
||||||
@@ -964,6 +985,13 @@ impl WindowsApi {
|
|||||||
actual != 0
|
actual != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lbutton_is_pressed() -> bool {
|
||||||
|
let state = unsafe { GetKeyState(i32::from(VK_LBUTTON.0)) };
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
let actual = (state as u16) & 0x8000;
|
||||||
|
actual != 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn left_click() -> u32 {
|
pub fn left_click() -> u32 {
|
||||||
let inputs = [
|
let inputs = [
|
||||||
INPUT {
|
INPUT {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use crate::DEFAULT_WORKSPACE_PADDING;
|
|||||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||||
use crate::NO_TITLEBAR;
|
use crate::NO_TITLEBAR;
|
||||||
use crate::REMOVE_TITLEBARS;
|
use crate::REMOVE_TITLEBARS;
|
||||||
|
use crate::STACKBAR_TAB_HEIGHT;
|
||||||
|
|
||||||
#[allow(clippy::struct_field_names)]
|
#[allow(clippy::struct_field_names)]
|
||||||
#[derive(
|
#[derive(
|
||||||
@@ -143,55 +144,66 @@ impl Workspace {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide(&mut self) {
|
pub fn hide(&mut self, omit: Option<isize>) {
|
||||||
for container in self.containers_mut() {
|
for window in self.floating_windows_mut().iter_mut().rev() {
|
||||||
for window in container.windows_mut() {
|
let mut should_hide = omit.is_none();
|
||||||
|
|
||||||
|
if !should_hide {
|
||||||
|
if let Some(omit) = omit {
|
||||||
|
if omit != window.hwnd {
|
||||||
|
should_hide = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_hide {
|
||||||
window.hide();
|
window.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for container in self.containers_mut() {
|
||||||
|
container.hide(omit)
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(window) = self.maximized_window() {
|
if let Some(window) = self.maximized_window() {
|
||||||
window.hide();
|
window.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(container) = self.monocle_container_mut() {
|
if let Some(container) = self.monocle_container_mut() {
|
||||||
for window in container.windows_mut() {
|
container.hide(omit)
|
||||||
window.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for window in self.floating_windows() {
|
|
||||||
window.hide();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&mut self, mouse_follows_focus: bool) -> Result<()> {
|
pub fn restore(&mut self, mouse_follows_focus: bool) -> Result<()> {
|
||||||
let idx = self.focused_container_idx();
|
let idx = self.focused_container_idx();
|
||||||
let mut to_focus = None;
|
let mut to_focus = None;
|
||||||
|
|
||||||
for (i, container) in self.containers_mut().iter_mut().enumerate() {
|
for (i, container) in self.containers_mut().iter_mut().enumerate() {
|
||||||
if let Some(window) = container.focused_window_mut() {
|
if let Some(window) = container.focused_window_mut() {
|
||||||
window.restore();
|
|
||||||
|
|
||||||
if idx == i {
|
if idx == i {
|
||||||
to_focus = Option::from(*window);
|
to_focus = Option::from(*window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(window) = self.maximized_window() {
|
for container in self.containers_mut() {
|
||||||
window.maximize();
|
container.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(container) = self.monocle_container_mut() {
|
if let Some(container) = self.monocle_container_mut() {
|
||||||
for window in container.windows_mut() {
|
container.restore();
|
||||||
window.restore();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for window in self.floating_windows() {
|
for window in self.floating_windows() {
|
||||||
window.restore();
|
window.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(container) = self.focused_container_mut() {
|
||||||
|
container.focus_window(container.focused_window_idx());
|
||||||
|
}
|
||||||
|
|
||||||
// Do this here to make sure that an error doesn't stop the restoration of other windows
|
// Do this here to make sure that an error doesn't stop the restoration of other windows
|
||||||
// Maximised windows should always be drawn at the top of the Z order
|
// Maximised windows should always be drawn at the top of the Z order
|
||||||
if let Some(window) = to_focus {
|
if let Some(window) = to_focus {
|
||||||
@@ -276,9 +288,23 @@ impl Workspace {
|
|||||||
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
|
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
|
||||||
let no_titlebar = NO_TITLEBAR.lock().clone();
|
let no_titlebar = NO_TITLEBAR.lock().clone();
|
||||||
|
|
||||||
let windows = self.visible_windows_mut();
|
let focused_hwnd = self
|
||||||
for (i, window) in windows.into_iter().enumerate() {
|
.focused_container()
|
||||||
if let (Some(window), Some(layout)) = (window, layouts.get(i)) {
|
.ok_or_else(|| anyhow!("couldn't find a focused container"))?
|
||||||
|
.focused_window()
|
||||||
|
.ok_or_else(|| anyhow!("couldn't find a focused window"))?
|
||||||
|
.hwnd;
|
||||||
|
|
||||||
|
let container_padding = self.container_padding().unwrap_or(0);
|
||||||
|
let containers = self.containers_mut();
|
||||||
|
|
||||||
|
for (i, container) in containers.iter_mut().enumerate() {
|
||||||
|
let container_windows = container.windows().clone();
|
||||||
|
let container_topbar = container.stackbar().clone();
|
||||||
|
|
||||||
|
if let (Some(window), Some(layout)) =
|
||||||
|
(container.focused_window_mut(), layouts.get(i))
|
||||||
|
{
|
||||||
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
|
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
|
||||||
window.remove_title_bar()?;
|
window.remove_title_bar()?;
|
||||||
} else if no_titlebar.contains(&window.exe()?) {
|
} else if no_titlebar.contains(&window.exe()?) {
|
||||||
@@ -300,6 +326,20 @@ impl Workspace {
|
|||||||
rect.add_padding(width);
|
rect.add_padding(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(stackbar) = container_topbar {
|
||||||
|
stackbar.set_position(
|
||||||
|
&stackbar.get_position_from_container_layout(layout),
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
stackbar.update(&container_windows, focused_hwnd)?;
|
||||||
|
let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
|
||||||
|
let total_height = tab_height + container_padding;
|
||||||
|
|
||||||
|
rect.top += total_height;
|
||||||
|
rect.bottom -= total_height;
|
||||||
|
}
|
||||||
|
|
||||||
window.set_position(&rect, false)?;
|
window.set_position(&rect, false)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -375,7 +415,18 @@ impl Workspace {
|
|||||||
.idx_for_window(hwnd)
|
.idx_for_window(hwnd)
|
||||||
.ok_or_else(|| anyhow!("there is no window"))?;
|
.ok_or_else(|| anyhow!("there is no window"))?;
|
||||||
|
|
||||||
|
let mut should_load = false;
|
||||||
|
|
||||||
|
if container.focused_window_idx() != window_idx {
|
||||||
|
should_load = true
|
||||||
|
}
|
||||||
|
|
||||||
container.focus_window(window_idx);
|
container.focus_window(window_idx);
|
||||||
|
|
||||||
|
if should_load {
|
||||||
|
container.load_focused_window();
|
||||||
|
}
|
||||||
|
|
||||||
self.focus_container(container_idx);
|
self.focus_container(container_idx);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user