Skip to content

Commit

Permalink
feat: add sample configs of new window effects; fix window border eff…
Browse files Browse the repository at this point in the history
…ect flickering (#752)
  • Loading branch information
lars-berger authored Oct 1, 2024
1 parent 4e5c662 commit 058bfa7
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 75 deletions.
33 changes: 26 additions & 7 deletions packages/wm/src/app_command.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{iter, path::PathBuf};

use anyhow::{bail, Context};
use clap::{error::KindFormatter, ArgAction, Args, Parser, ValueEnum};
use clap::{error::KindFormatter, Args, Parser, ValueEnum};
use serde::{Deserialize, Deserializer, Serialize};
use tracing::{warn, Level};
use uuid::Uuid;
Expand All @@ -26,8 +26,7 @@ use crate::{
windows::{
commands::{
ignore_window, move_window_in_direction, move_window_to_workspace,
resize_window, set_title_bar_visibility, set_window_size,
update_window_state,
resize_window, set_window_size, update_window_state,
},
traits::WindowGetters,
WindowState,
Expand Down Expand Up @@ -207,8 +206,8 @@ pub enum InvokeCommand {
SetMinimized,
SetTiling,
SetTitleBarVisibility {
#[clap(required = true, action = ArgAction::Set)]
visible: bool,
#[clap(required = true, value_enum)]
visibility: TitleBarVisibility,
},
ShellExec {
#[clap(required = true, trailing_var_arg = true)]
Expand Down Expand Up @@ -490,8 +489,20 @@ impl InvokeCommand {
_ => Ok(()),
}
}
InvokeCommand::SetTitleBarVisibility { visible } => {
set_title_bar_visibility(*visible, subject_container)
InvokeCommand::SetTitleBarVisibility { visibility } => {
match subject_container.as_window_container() {
Ok(window) => {
_ = window.native().set_title_bar_visibility(
if *visibility == TitleBarVisibility::Shown {
true
} else {
false
},
);
Ok(())
}
_ => Ok(()),
}
}
InvokeCommand::ShellExec { command } => {
shell_exec(&command.join(" "))
Expand Down Expand Up @@ -676,6 +687,14 @@ impl<'de> Deserialize<'de> for InvokeCommand {
}
}

#[derive(Clone, Debug, PartialEq, Serialize, ValueEnum)]
#[clap(rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum TitleBarVisibility {
Shown,
Hidden,
}

#[derive(Args, Clone, Debug, PartialEq, Serialize)]
#[group(required = true, multiple = true)]
pub struct InvokeAdjustBordersCommand {
Expand Down
81 changes: 52 additions & 29 deletions packages/wm/src/common/commands/platform_sync.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::time::Duration;

use anyhow::Context;
use tokio::task;
use tracing::warn;

use crate::{
Expand All @@ -7,7 +10,9 @@ use crate::{
traits::{CommonGetters, PositionGetters},
Container, WindowContainer,
},
user_config::{CursorJumpTrigger, UserConfig, WindowEffectConfig},
user_config::{
CornerStyle, CursorJumpTrigger, UserConfig, WindowEffectConfig,
},
windows::traits::WindowGetters,
wm_event::WmEvent,
wm_state::WmState,
Expand All @@ -31,34 +36,39 @@ pub fn platform_sync(
state.pending_sync.cursor_jump = false;
}

if state.pending_sync.focus_change {
sync_focus(focused_container.clone(), state)?;
state.pending_sync.focus_change = false;
}

if let Ok(window) = focused_container.as_window_container() {
apply_window_effects(window, true, config);
}
if state.pending_sync.focus_change
|| state.pending_sync.reset_window_effects
{
if let Ok(window) = focused_container.as_window_container() {
apply_window_effects(window, true, config);
}

// Get windows that should have the unfocused border applied to them.
// For the sake of performance, we only update the border of the
// previously focused window. If the `reset_window_effects` flag is
// passed, the unfocused border is applied to all unfocused windows.
let unfocused_windows = match state.pending_sync.reset_window_effects {
true => state.windows(),
false => recent_focused_container
.and_then(|container| container.as_window_container().ok())
// Get windows that should have the unfocused border applied to them.
// For the sake of performance, we only update the border of the
// previously focused window. If the `reset_window_effects` flag is
// passed, the unfocused border is applied to all unfocused windows.
let unfocused_windows =
match state.pending_sync.reset_window_effects {
true => state.windows(),
false => recent_focused_container
.and_then(|container| container.as_window_container().ok())
.into_iter()
.collect(),
}
.into_iter()
.collect(),
}
.into_iter()
.filter(|window| window.id() != focused_container.id());
.filter(|window| window.id() != focused_container.id());

for window in unfocused_windows {
apply_window_effects(window, false, config);
}

for window in unfocused_windows {
apply_window_effects(window, false, config);
state.pending_sync.reset_window_effects = false;
}

state.pending_sync.reset_window_effects = false;
if state.pending_sync.focus_change {
sync_focus(focused_container.clone(), state)?;
state.pending_sync.focus_change = false;
}

Ok(())
}
Expand Down Expand Up @@ -194,8 +204,8 @@ fn apply_window_effects(
apply_hide_title_bar_effect(&window, effect_config);
}

if window_effects.focused_window.corner.enabled
|| window_effects.other_windows.corner.enabled
if window_effects.focused_window.corner_style.enabled
|| window_effects.other_windows.corner_style.enabled
{
apply_corner_effect(&window, effect_config);
}
Expand All @@ -211,6 +221,16 @@ fn apply_border_effect(
};

_ = window.native().set_border_color(border_color);

let native = window.native().clone();
let border_color = border_color.cloned();

// Re-apply border color after a short delay to better handle
// windows that change it themselves.
task::spawn(async move {
tokio::time::sleep(Duration::from_millis(50)).await;
_ = native.set_border_color(border_color.as_ref());
});
}

fn apply_hide_title_bar_effect(
Expand All @@ -226,7 +246,10 @@ fn apply_corner_effect(
window: &WindowContainer,
effect_config: &WindowEffectConfig,
) {
_ = window
.native()
.set_corner_style(effect_config.corner.style.clone());
let corner_style = match effect_config.corner_style.enabled {
true => &effect_config.corner_style.style,
false => &CornerStyle::Default,
};

_ = window.native().set_corner_style(corner_style);
}
4 changes: 1 addition & 3 deletions packages/wm/src/common/events/handle_window_focused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ pub fn handle_window_focused(
config,
)?;

state.emit_event(WmEvent::FocusChanged {
focused_container: window.to_dto()?,
})
state.pending_sync.focus_change = true;
}

Ok(())
Expand Down
24 changes: 12 additions & 12 deletions packages/wm/src/common/platform/native_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,13 @@ impl NativeWindow {

pub fn set_corner_style(
&self,
corner_type: CornerStyle,
corner_style: &CornerStyle,
) -> anyhow::Result<()> {
let corner_preference = match corner_type {
CornerStyle::WindowsDefault => DWMWCP_DEFAULT,
let corner_preference = match corner_style {
CornerStyle::Default => DWMWCP_DEFAULT,
CornerStyle::Square => DWMWCP_DONOTROUND,
CornerStyle::Round => DWMWCP_ROUND,
CornerStyle::SmallRound => DWMWCP_ROUNDSMALL,
CornerStyle::Rounded => DWMWCP_ROUND,
CornerStyle::SmallRounded => DWMWCP_ROUNDSMALL,
};

unsafe {
Expand All @@ -356,15 +356,15 @@ impl NativeWindow {
&self,
visible: bool,
) -> anyhow::Result<()> {
unsafe {
let style = GetWindowLongPtrW(HWND(self.handle), GWL_STYLE);
let style = unsafe { GetWindowLongPtrW(HWND(self.handle), GWL_STYLE) };

let new_style = match visible {
true => style | (WS_DLGFRAME.0 as isize),
false => style & !(WS_DLGFRAME.0 as isize),
};
let new_style = match visible {
true => style | (WS_DLGFRAME.0 as isize),
false => style & !(WS_DLGFRAME.0 as isize),
};

if new_style != style {
if new_style != style {
unsafe {
SetWindowLongPtrW(HWND(self.handle), GWL_STYLE, new_style);
SetWindowPos(
HWND(self.handle),
Expand Down
12 changes: 6 additions & 6 deletions packages/wm/src/user_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ pub struct WindowEffectConfig {

/// Config for optionally changing the corner style.
#[serde(default)]
pub corner: CornerEffectConfig,
pub corner_style: CornerEffectConfig,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down Expand Up @@ -550,10 +550,10 @@ pub struct CornerEffectConfig {
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum CornerStyle {
WindowsDefault,
Default,
Square,
Round,
SmallRound,
Rounded,
SmallRounded,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
Expand Down Expand Up @@ -651,7 +651,7 @@ fn default_window_rule_on() -> Vec<WindowRuleEvent> {

impl Default for CornerStyle {
fn default() -> Self {
CornerStyle::WindowsDefault
CornerStyle::Default
}
}

Expand All @@ -665,7 +665,7 @@ impl Default for CornerEffectConfig {
fn default() -> Self {
CornerEffectConfig {
enabled: false,
style: CornerStyle::WindowsDefault,
style: CornerStyle::Default,
}
}
}
2 changes: 0 additions & 2 deletions packages/wm/src/windows/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ mod move_window_in_direction;
mod move_window_to_workspace;
mod resize_window;
mod run_window_rules;
mod set_title_bar_visibility;
mod set_window_size;
mod unmanage_window;
mod update_window_state;
Expand All @@ -15,7 +14,6 @@ pub use move_window_in_direction::*;
pub use move_window_to_workspace::*;
pub use resize_window::*;
pub use run_window_rules::*;
pub use set_title_bar_visibility::*;
pub use set_window_size::*;
pub use unmanage_window::*;
pub use update_window_state::*;
16 changes: 0 additions & 16 deletions packages/wm/src/windows/commands/set_title_bar_visibility.rs

This file was deleted.

17 changes: 17 additions & 0 deletions resources/assets/sample-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,28 @@ window_effects:
enabled: true
color: '#8dbcff'

# Remove the title bar from the window's frame.
# ** Exclusive to Windows 11 due to API limitations.
hide_title_bar:
enabled: false

# Change the corner style of the window's frame.
# ** Exclusive to Windows 11 due to API limitations.
corner_style:
enabled: false
# Allowed values: 'square', 'rounded', 'small_rounded'.
style: 'square'

# Visual effects to apply to non-focused windows.
other_windows:
border:
enabled: true
color: '#a1a1a1'
hide_title_bar:
enabled: false
corner_style:
enabled: false
style: 'square'

window_behavior:
# New windows are created in this state whenever possible.
Expand Down

0 comments on commit 058bfa7

Please sign in to comment.