diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index 38a83c50..8899700b 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -219,6 +219,8 @@ lazy_static! { static ref WINDOWS_BY_BAR_HWNDS: Arc>>> = Arc::new(Mutex::new(HashMap::new())); + + static ref FLOATING_WINDOW_TOGGLE_ASPECT_RATIO: Arc> = Arc::new(Mutex::new(AspectRatio::Predefined(PredefinedAspectRatio::Widescreen))); } pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10); diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index bff6faa2..f9e965f9 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -35,13 +35,16 @@ use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; use crate::workspace::Workspace; +use crate::AspectRatio; use crate::Axis; use crate::CrossBoundaryBehaviour; +use crate::PredefinedAspectRatio; use crate::DATA_DIR; use crate::DEFAULT_CONTAINER_PADDING; use crate::DEFAULT_WORKSPACE_PADDING; use crate::DISPLAY_INDEX_PREFERENCES; use crate::FLOATING_APPLICATIONS; +use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO; use crate::HIDING_BEHAVIOUR; use crate::IGNORE_IDENTIFIERS; use crate::LAYERED_WHITELIST; @@ -382,6 +385,9 @@ pub struct StaticConfig { /// HEAVILY DISCOURAGED: Identify applications for which komorebi should forcibly remove title bars #[serde(skip_serializing_if = "Option::is_none")] pub remove_titlebar_applications: Option>, + /// Aspect ratio to resize with when toggling floating mode for a window + #[serde(skip_serializing_if = "Option::is_none")] + pub floating_window_aspect_ratio: Option, } #[derive(Debug, Serialize, Deserialize, JsonSchema)] @@ -637,6 +643,7 @@ impl From<&WindowManager> for StaticConfig { slow_application_identifiers: Option::from(SLOW_APPLICATION_IDENTIFIERS.lock().clone()), bar_configurations: None, remove_titlebar_applications: Option::from(NO_TITLEBAR.lock().clone()), + floating_window_aspect_ratio: Option::from(*FLOATING_WINDOW_TOGGLE_ASPECT_RATIO.lock()), } } } @@ -644,6 +651,10 @@ impl From<&WindowManager> for StaticConfig { impl StaticConfig { #[allow(clippy::cognitive_complexity, clippy::too_many_lines)] fn apply_globals(&mut self) -> Result<()> { + *FLOATING_WINDOW_TOGGLE_ASPECT_RATIO.lock() = self + .floating_window_aspect_ratio + .unwrap_or(AspectRatio::Predefined(PredefinedAspectRatio::Standard)); + if let Some(monitor_index_preferences) = &self.monitor_index_preferences { let mut preferences = MONITOR_INDEX_PREFERENCES.lock(); preferences.clone_from(monitor_index_preferences); diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 32af415a..358da482 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -15,6 +15,7 @@ use crate::focus_manager; use crate::stackbar_manager; use crate::windows_api; use crate::AnimationStyle; +use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO; use crate::SLOW_APPLICATION_COMPENSATION_TIME; use crate::SLOW_APPLICATION_IDENTIFIERS; use std::collections::HashMap; @@ -296,6 +297,38 @@ impl RenderDispatcher for TransparencyRenderDispatcher { } } +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum AspectRatio { + /// A predefined aspect ratio + Predefined(PredefinedAspectRatio), + /// A custom W:H aspect ratio + Custom(i32, i32), +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub enum PredefinedAspectRatio { + /// 21:9 + Ultrawide, + /// 16:9 + Widescreen, + /// 4:3 + Standard, +} + +impl AspectRatio { + pub fn width_and_height(self) -> (i32, i32) { + match self { + AspectRatio::Predefined(predefined) => match predefined { + PredefinedAspectRatio::Ultrawide => (21, 9), + PredefinedAspectRatio::Widescreen => (16, 9), + PredefinedAspectRatio::Standard => (4, 3), + }, + AspectRatio::Custom(w, h) => (w, h), + } + } +} + impl Window { pub const fn hwnd(self) -> HWND { HWND(windows_api::as_ptr!(self.hwnd)) @@ -369,15 +402,21 @@ impl Window { } pub fn center(&mut self, work_area: &Rect) -> Result<()> { - let half_width = work_area.right / 2; - let half_weight = work_area.bottom / 2; + let (aspect_ratio_width, aspect_ratio_height) = FLOATING_WINDOW_TOGGLE_ASPECT_RATIO + .lock() + .width_and_height(); + let target_height = work_area.bottom / 2; + let target_width = (target_height * aspect_ratio_width) / aspect_ratio_height; + + let x = work_area.left + ((work_area.right - target_width) / 2); + let y = work_area.top + ((work_area.bottom - target_height) / 2); self.set_position( &Rect { - left: work_area.left + ((work_area.right - half_width) / 2), - top: work_area.top + ((work_area.bottom - half_weight) / 2), - right: half_width, - bottom: half_weight, + left: x, + top: y, + right: target_width, + bottom: target_height, }, true, ) @@ -928,12 +967,12 @@ fn window_is_eligible( } if (allow_wsl2_gui || allow_titlebar_removed || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE)) - && !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME) - // Get a lot of dupe events coming through that make the redrawing go crazy - // on FocusChange events if I don't filter out this one. But, if we are - // allowing a specific layered window on the whitelist (like Steam), it should - // pass this check - && (allow_layered || !ex_style.contains(ExtendedWindowStyle::LAYERED)) + && !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME) + // Get a lot of dupe events coming through that make the redrawing go crazy + // on FocusChange events if I don't filter out this one. But, if we are + // allowing a specific layered window on the whitelist (like Steam), it should + // pass this check + && (allow_layered || !ex_style.contains(ExtendedWindowStyle::LAYERED)) || managed_override { return true; diff --git a/schema.bar.json b/schema.bar.json index 718029a9..9599d49e 100644 --- a/schema.bar.json +++ b/schema.bar.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "KomobarConfig", - "description": "The `komorebi.bar.json` configuration file reference for `v0.1.33`", + "description": "The `komorebi.bar.json` configuration file reference for `v0.1.34`", "type": "object", "required": [ "left_widgets", @@ -1219,58 +1219,6 @@ "type": "number", "format": "float" }, - "horizontal_margin": { - "description": "Bar horizontal margin. Use one value for symmetric margin or use `[left, right]` to specify a different margin on each side (default: 0)", - "anyOf": [ - { - "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", - "type": "number", - "format": "float" - }, - { - "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", - "type": "array", - "items": [ - { - "type": "number", - "format": "float" - }, - { - "type": "number", - "format": "float" - } - ], - "maxItems": 2, - "minItems": 2 - } - ] - }, - "horizontal_padding": { - "description": "Bar horizontal padding. Use one value for symmetric padding or use `[left, right]` to specify a different padding on each side (default: 10)", - "anyOf": [ - { - "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", - "type": "number", - "format": "float" - }, - { - "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", - "type": "array", - "items": [ - { - "type": "number", - "format": "float" - }, - { - "type": "number", - "format": "float" - } - ], - "maxItems": 2, - "minItems": 2 - } - ] - }, "icon_scale": { "description": "Scale of the icons relative to the font_size [[1.0-2.0]]. (default: 1.4)", "type": "number", @@ -2141,6 +2089,93 @@ ] } }, + "margin": { + "description": "Bar margin. Use one value for all sides or use a grouped margin for horizontal and/or vertical definition which can each take a single value for a symmetric margin or two values for each side, i.e.: ```json \"margin\": { \"horizontal\": 10 } ``` or: ```json \"margin\": { \"vertical\": [top, bottom] } ``` You can also set individual margin on each side like this: ```json \"margin\": { \"top\": 10, \"bottom\": 10, \"left\": 10, \"right\": 10, } ``` By default, margin is set to 0 on all sides.", + "anyOf": [ + { + "type": "number", + "format": "float" + }, + { + "type": "object", + "required": [ + "bottom", + "left", + "right", + "top" + ], + "properties": { + "bottom": { + "type": "number", + "format": "float" + }, + "left": { + "type": "number", + "format": "float" + }, + "right": { + "type": "number", + "format": "float" + }, + "top": { + "type": "number", + "format": "float" + } + } + }, + { + "type": "object", + "properties": { + "horizontal": { + "anyOf": [ + { + "type": "number", + "format": "float" + }, + { + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, + "vertical": { + "anyOf": [ + { + "type": "number", + "format": "float" + }, + { + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + } + } + } + ] + }, "max_label_width": { "description": "Max label width before text truncation (default: 400.0)", "type": "number", @@ -2204,6 +2239,93 @@ } ] }, + "padding": { + "description": "Bar padding. Use one value for all sides or use a grouped padding for horizontal and/or vertical definition which can each take a single value for a symmetric padding or two values for each side, i.e.: ```json \"padding\": { \"horizontal\": 10 } ``` or: ```json \"padding\": { \"horizontal\": [left, right] } ``` You can also set individual padding on each side like this: ```json \"padding\": { \"top\": 10, \"bottom\": 10, \"left\": 10, \"right\": 10, } ``` By default, padding is set to 10 on all sides.", + "anyOf": [ + { + "type": "number", + "format": "float" + }, + { + "type": "object", + "required": [ + "bottom", + "left", + "right", + "top" + ], + "properties": { + "bottom": { + "type": "number", + "format": "float" + }, + "left": { + "type": "number", + "format": "float" + }, + "right": { + "type": "number", + "format": "float" + }, + "top": { + "type": "number", + "format": "float" + } + } + }, + { + "type": "object", + "properties": { + "horizontal": { + "anyOf": [ + { + "type": "number", + "format": "float" + }, + { + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, + "vertical": { + "anyOf": [ + { + "type": "number", + "format": "float" + }, + { + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + } + } + } + ] + }, "position": { "description": "Bar positioning options", "type": "object", @@ -3495,58 +3617,6 @@ "format": "uint8", "minimum": 0.0 }, - "vertical_margin": { - "description": "Bar vertical margin. Use one value for symmetric margin or use `[top, bottom]` to specify a different margin on each side (default: 0)", - "anyOf": [ - { - "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", - "type": "number", - "format": "float" - }, - { - "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", - "type": "array", - "items": [ - { - "type": "number", - "format": "float" - }, - { - "type": "number", - "format": "float" - } - ], - "maxItems": 2, - "minItems": 2 - } - ] - }, - "vertical_padding": { - "description": "Bar vertical padding. Use one value for symmetric padding or use `[top, bottom]` to specify a different padding on each side (default: 10)", - "anyOf": [ - { - "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", - "type": "number", - "format": "float" - }, - { - "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", - "type": "array", - "items": [ - { - "type": "number", - "format": "float" - }, - { - "type": "number", - "format": "float" - } - ], - "maxItems": 2, - "minItems": 2 - } - ] - }, "widget_spacing": { "description": "Spacing between widgets (default: 10.0)", "type": "number", diff --git a/schema.json b/schema.json index adecf401..c9de2b2f 100644 --- a/schema.json +++ b/schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "StaticConfig", - "description": "The `komorebi.json` static configuration file reference for `v0.1.33`", + "description": "The `komorebi.json` static configuration file reference for `v0.1.34`", "type": "object", "properties": { "animation": { @@ -642,6 +642,39 @@ ] } }, + "floating_window_aspect_ratio": { + "description": "Aspect ratio to resize with when toggling floating mode for a window", + "anyOf": [ + { + "description": "21:9", + "type": "null" + }, + { + "description": "16:9", + "type": "null" + }, + { + "description": "4:3", + "type": "null" + }, + { + "description": "A custom W:H aspect ratio", + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "integer", + "format": "int32" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, "focus_follows_mouse": { "description": "END OF LIFE FEATURE: Use https://github.com/LGUG2Z/masir instead", "oneOf": [