Skip to content

Commit

Permalink
feat(macos): display border for opaque backgrounds (neovide#2351)
Browse files Browse the repository at this point in the history
* feat(macos): display border for opaque backgrounds

* remove redundant  call

* feat(macos): add `g:neovide_show_border` option

* fix(macos): `g:neovide_background_color` runtime changes

* docs: `g:neovide_show_border`

* set background on window creation

* fix: macos border and shadow quirks with tradeoffs

* edit comments

* split `set_background` into legacy and non-legacy functions

* docs: improve `g:neovide_show_border` description

* add `set_background` docstring

* make `handle_background` and `handle_legacy_background` private

* fix incorrect comment explanation

* refactor: move `set_window_blurred` into `update_background`

* improve readability of `handle_window_settings_changed`

* revert adding `MacosWindowFeature#handle_window_settings_changed`

* readd `MacosWindowFeature#handle_settings_changed`
  • Loading branch information
emonadeo authored Feb 23, 2024
1 parent 71e9fa4 commit 86c2170
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 88 deletions.
126 changes: 104 additions & 22 deletions src/window/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
settings::SETTINGS,
};

use super::WindowSettings;
use super::{WindowSettings, WindowSettingsChanged};

declare_class!(
// A view to simulate the double-click-to-zoom effect for `--frame transparency`.
Expand Down Expand Up @@ -66,25 +66,6 @@ impl MacosWindowFeature {
_ => panic!("Not an appkit window."),
};

if let Ok(color) = &SETTINGS
.get::<WindowSettings>()
.background_color
.parse::<Color>()
{
error_msg!(concat!(
"neovide_background_color has now been deprecated. ",
"Use neovide_transparency instead if you want to get a transparent window titlebar. ",
"Please check https://neovide.dev/configuration.html#background-color-deprecated-currently-macos-only for more information.",
));

unsafe {
let [red, green, blue, alpha] = color.to_array();
let ns_background =
NSColor::colorWithSRGBRed_green_blue_alpha(red, green, blue, alpha);
ns_window.setBackgroundColor(Some(&ns_background));
}
};

let mut extra_titlebar_height_in_pixel: u32 = 0;

let frame = SETTINGS.get::<CmdLineSettings>().frame;
Expand Down Expand Up @@ -117,12 +98,16 @@ impl MacosWindowFeature {

let is_fullscreen = unsafe { ns_window.styleMask() } & NSWindowStyleMaskFullScreen != 0;

MacosWindowFeature {
let macos_window_feature = MacosWindowFeature {
ns_window,
titlebar_click_handler,
extra_titlebar_height_in_pixel,
is_fullscreen,
}
};

macos_window_feature.update_background(window, true);

macos_window_feature
}

// Used to calculate the value of TITLEBAR_HEIGHT, aka, titlebar height in dpi-independent length.
Expand Down Expand Up @@ -174,4 +159,101 @@ impl MacosWindowFeature {
self.extra_titlebar_height_in_pixel
}
}

/// Print a deprecation warning for `neovide_background_color`
pub fn display_deprecation_warning(&self) {
error_msg!(concat!(
"neovide_background_color has now been deprecated. ",
"Use neovide_transparency instead if you want to get a transparent window titlebar. ",
"Please check https://neovide.dev/configuration.html#background-color-deprecated-currently-macos-only for more information.",
));
}

#[deprecated(
since = "0.12.2",
note = "This function will be removed in the future."
)]
fn update_ns_background_legacy(
&self,
color: Color,
show_border: bool,
ignore_deprecation_warning: bool,
) {
if !ignore_deprecation_warning {
self.display_deprecation_warning();
}
let [red, green, blue, alpha] = color.to_array();
unsafe {
let opaque = alpha >= 1.0;
let ns_background = NSColor::colorWithSRGBRed_green_blue_alpha(red, green, blue, alpha);
self.ns_window.setBackgroundColor(Some(&ns_background));
// If the shadow is enabled and the background color is not transparent, the window will have a grey border
// Workaround: Disable shadow when `show_border` is false
self.ns_window.setHasShadow(opaque && show_border);
// Setting the window to opaque upon creation shows a permanent subtle grey border on the top edge of the window
self.ns_window.setOpaque(opaque && show_border);
self.ns_window.invalidateShadow();
}
}

fn update_ns_background(&self, transparency: f32, show_border: bool) {
unsafe {
let opaque = transparency >= 1.0;
// Setting the background color to `NSColor::windowBackgroundColor()`
// makes the background opaque and draws a grey border around the window
let ns_background = match opaque && show_border {
true => NSColor::windowBackgroundColor(),
false => NSColor::clearColor(),
};
self.ns_window.setBackgroundColor(Some(&ns_background));
self.ns_window.setHasShadow(opaque);
// Setting the window to opaque upon creation shows a permanent subtle grey border on the top edge of the window
self.ns_window.setOpaque(opaque && show_border);
self.ns_window.invalidateShadow();
}
}

/// Update background color, opacity, shadow and blur of a window.
fn update_background(&self, window: &Window, ignore_deprecation_warning: bool) {
let WindowSettings {
background_color,
show_border,
transparency,
window_blurred,
..
} = SETTINGS.get::<WindowSettings>();
match background_color.parse::<Color>() {
Ok(color) => {
self.update_ns_background_legacy(color, show_border, ignore_deprecation_warning)
}
_ => self.update_ns_background(transparency, show_border),
}
let opaque = transparency >= 1.0;
window.set_blur(window_blurred && !opaque);
}

pub fn handle_settings_changed(&self, window: &Window, changed_setting: WindowSettingsChanged) {
match changed_setting {
WindowSettingsChanged::BackgroundColor(background_color) => {
log::info!("background_color changed to {}", background_color);
self.update_background(window, false);
}
#[cfg(target_os = "macos")]
WindowSettingsChanged::ShowBorder(show_border) => {
log::info!("show_border changed to {}", show_border);
self.update_background(window, true);
}
#[cfg(target_os = "macos")]
WindowSettingsChanged::Transparency(transparency) => {
log::info!("transparency changed to {}", transparency);
self.update_background(window, true);
}
#[cfg(target_os = "macos")]
WindowSettingsChanged::WindowBlurred(window_blurred) => {
log::info!("window_blurred changed to {}", window_blurred);
self.update_background(window, true);
}
_ => {}
}
}
}
49 changes: 0 additions & 49 deletions src/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ mod window_wrapper;
#[cfg(target_os = "macos")]
mod macos;

#[cfg(target_os = "macos")]
use cocoa::base::id;

#[cfg(target_os = "linux")]
use std::env;

Expand All @@ -23,18 +20,9 @@ use winit::{
window::{Icon, Theme, WindowBuilder},
};

#[cfg(target_os = "macos")]
use winit::window::Window;

#[cfg(target_os = "macos")]
use winit::platform::macos::WindowBuilderExtMacOS;

#[cfg(target_os = "macos")]
use objc::{msg_send, sel, sel_impl};

#[cfg(target_os = "macos")]
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};

#[cfg(target_os = "linux")]
use winit::platform::wayland::WindowBuilderExtWayland;
#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -133,36 +121,6 @@ pub fn create_event_loop() -> EventLoop<UserEvent> {
.expect("Failed to create winit event loop")
}

/// Set the window blurred or not.
#[cfg(target_os = "macos")]
pub fn set_window_blurred(window: &Window, opacity: f32) {
let window_blurred = SETTINGS.get::<WindowSettings>().window_blurred;
let opaque = opacity >= 1.0;

window.set_blur(window_blurred && !opaque);
}

/// Force macOS to clear shadow of transparent windows.
#[cfg(target_os = "macos")]
pub fn invalidate_shadow(window: &Window) {
use cocoa::base::NO;
use cocoa::base::YES;

let window_transparency = &SETTINGS.get::<WindowSettings>().transparency;
let opaque = *window_transparency >= 1.0;

let raw_window = match window.raw_window_handle() {
#[cfg(target_os = "macos")]
RawWindowHandle::AppKit(handle) => handle.ns_window as id,
_ => return,
};

let value = if opaque { YES } else { NO };
unsafe {
let _: id = msg_send![raw_window, setHasShadow: value];
}
}

pub fn create_window(
event_loop: &EventLoop<UserEvent>,
initial_window_size: &WindowSize,
Expand Down Expand Up @@ -212,7 +170,6 @@ pub fn create_window(
Frame::Full => winit_window_builder,
Frame::None => winit_window_builder.with_decorations(false),
Frame::Buttonless => winit_window_builder
.with_transparent(true)
.with_title_hidden(title_hidden)
.with_titlebar_buttons_hidden(true)
.with_titlebar_transparent(true)
Expand Down Expand Up @@ -274,12 +231,6 @@ pub fn create_window(
Some(())
});

#[cfg(target_os = "macos")]
set_window_blurred(window, SETTINGS.get::<WindowSettings>().transparency);

#[cfg(target_os = "macos")]
invalidate_shadow(window);

gl_window
}

Expand Down
2 changes: 2 additions & 0 deletions src/window/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct WindowSettings {
pub input_macos_alt_is_meta: bool,
pub input_ime: bool,
pub unlink_border_highlights: bool,
pub show_border: bool,

#[option = "mousemoveevent"]
pub mouse_move_event: bool,
Expand Down Expand Up @@ -63,6 +64,7 @@ impl Default for WindowSettings {
observed_lines: None,
observed_columns: None,
unlink_border_highlights: true,
show_border: false,
}
}
}
22 changes: 5 additions & 17 deletions src/window/window_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ use crate::{
CmdLineSettings,
};

#[cfg(target_os = "macos")]
use crate::window::{invalidate_shadow, set_window_blurred};

#[cfg(target_os = "macos")]
use super::macos::MacosWindowFeature;

Expand Down Expand Up @@ -79,7 +76,7 @@ pub struct WinitWindowWrapper {
theme: Option<Theme>,
pub vsync: VSync,
#[cfg(target_os = "macos")]
macos_feature: MacosWindowFeature,
pub macos_feature: MacosWindowFeature,
}

impl WinitWindowWrapper {
Expand Down Expand Up @@ -235,20 +232,11 @@ impl WinitWindowWrapper {
self.set_ime(ime_enabled);
}
}
#[cfg(target_os = "macos")]
WindowSettingsChanged::Transparency(opacity) => {
log::info!("transparency changed to {}", opacity);
invalidate_shadow(self.windowed_context.window());
set_window_blurred(self.windowed_context.window(), opacity);
}
#[cfg(target_os = "macos")]
WindowSettingsChanged::WindowBlurred(window_blurred) => {
log::info!("window_blurred changed to {}", window_blurred);
let transparency = SETTINGS.get::<WindowSettings>().transparency;
set_window_blurred(self.windowed_context.window(), transparency);
}
_ => {}
}
};
#[cfg(target_os = "macos")]
self.macos_feature
.handle_settings_changed(self.windowed_context.window(), changed_setting);
}

pub fn handle_title_changed(&mut self, new_title: String) {
Expand Down
18 changes: 18 additions & 0 deletions website/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,24 @@ Lua:
vim.g.neovide_transparency = 0.8
```

#### Show Border (Currently macOS only)

VimScript:

```vim
let g:neovide_show_border = v:true
```

Lua:

```lua
vim.g.neovide_show_border = true
```

Draw a grey border around opaque windows only.

Default: `false`

![Transparency](assets/Transparency.png)

Setting `g:neovide_transparency` to a value between 0.0 and 1.0 will set the opacity of the window
Expand Down

0 comments on commit 86c2170

Please sign in to comment.