-
Notifications
You must be signed in to change notification settings - Fork 871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blur-Behind / Glassy Windows implementation #568
Changes from 6 commits
2cb2826
03e7eb2
d0e7a2f
a0e9484
e7686aa
ea070f0
916eec2
79d1f94
ea119ff
bc53f54
4eb989c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
extern crate winit; | ||
|
||
fn main() { | ||
let mut events_loop = winit::EventsLoop::new(); | ||
|
||
let window = winit::WindowBuilder::new() | ||
.with_title("A blurry window!") | ||
.with_blur(true) | ||
.build(&events_loop) | ||
.unwrap(); | ||
|
||
#[cfg(target_os = "macos")] | ||
{ | ||
// On macOS the blur material is 'light' by default. | ||
// Let's change it to a dark theme! | ||
use winit::os::macos::{BlurMaterial, WindowExt}; | ||
window.set_blur_material(BlurMaterial::Dark); | ||
} | ||
|
||
events_loop.run_forever(|event| { | ||
match event { | ||
winit::Event::WindowEvent { | ||
event: winit::WindowEvent::CloseRequested, | ||
.. | ||
} => winit::ControlFlow::Break, | ||
winit::Event::WindowEvent { | ||
event: winit::WindowEvent::KeyboardInput { | ||
input: winit::KeyboardInput { | ||
virtual_keycode: Some(winit::VirtualKeyCode::Escape), | ||
.. | ||
}, | ||
.. | ||
}, | ||
.. | ||
} => winit::ControlFlow::Break, | ||
_ => winit::ControlFlow::Continue, | ||
} | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,9 @@ pub trait WindowExt { | |
/// | ||
/// The pointer will become invalid when the `Window` is destroyed. | ||
fn get_nsview(&self) -> *mut c_void; | ||
|
||
/// Just for testing purposes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should probably change that comment. |
||
fn set_blur_material(&self, material: BlurMaterial); | ||
} | ||
|
||
impl WindowExt for Window { | ||
|
@@ -28,6 +31,11 @@ impl WindowExt for Window { | |
fn get_nsview(&self) -> *mut c_void { | ||
self.window.get_nsview() | ||
} | ||
|
||
#[inline] | ||
fn set_blur_material(&self, material: BlurMaterial) { | ||
self.window.set_blur_material(material); | ||
} | ||
} | ||
|
||
/// Corresponds to `NSApplicationActivationPolicy`. | ||
|
@@ -157,3 +165,27 @@ impl MonitorIdExt for MonitorId { | |
self.inner.get_nsscreen().map(|s| s as *mut c_void) | ||
} | ||
} | ||
|
||
#[repr(i64)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these actually supposed to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's an I have added some valuable documentation in my new commit. |
||
// Applies to MacOS Mojave. | ||
pub enum BlurMaterial { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this enum is problematic. I created it based on the documentation Apple updated a while ago, based on the upcoming SDK version for macOS Mojave. Most of these variants are not supported on High Sierra, and will crash the application when attempted to be used. I'm unsure how to go about this. Should we remove the enum entirely and make the |
||
AppearanceBased = 0, // Deperecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this enum is public, the deprecation notices should be doc comments above the variant. |
||
Light = 1, // Deperecated | ||
Dark = 2, // Deprecated | ||
Titlebar = 3, | ||
Selection = 4, | ||
Menu = 5, | ||
Popover = 6, | ||
Sidebar = 7, | ||
MediumLight = 8, // Deprecated | ||
UltraDark = 9, // Deprecated | ||
HeaderView = 10, | ||
Sheet = 11, | ||
WindowBackground = 12, | ||
HudWindow = 13, | ||
FullScreenUi = 15, | ||
ToolTip = 17, | ||
ContentBackground = 18, | ||
UnderWindowBackground = 21, | ||
UnderPageBackground = 22, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,11 +13,12 @@ use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger}; | |
use objc::declare::ClassDecl; | ||
use objc::runtime::{Class, Object, Protocol, Sel, BOOL}; | ||
|
||
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId}; | ||
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId, WindowAttributes}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've been trying to keep imports sorted alphabetically, so |
||
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code}; | ||
use platform::platform::util; | ||
use platform::platform::ffi::*; | ||
use platform::platform::window::{get_window_id, IdRef}; | ||
use os::macos::BlurMaterial; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. likewise, Though, I might be more comfortable with you defining the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried defining I don't know if we even should keep the enum, see my other comments. |
||
|
||
struct ViewState { | ||
window: id, | ||
|
@@ -27,7 +28,7 @@ struct ViewState { | |
last_insert: Option<String>, | ||
} | ||
|
||
pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef { | ||
pub fn new_view(window: id, shared: Weak<Shared>, win_attribs: &WindowAttributes) -> IdRef { | ||
let state = ViewState { | ||
window, | ||
shared, | ||
|
@@ -38,8 +39,14 @@ pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef { | |
unsafe { | ||
// This is free'd in `dealloc` | ||
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void; | ||
let view: id = msg_send![VIEW_CLASS.0, alloc]; | ||
IdRef::new(msg_send![view, initWithWinit:state_ptr]) | ||
let view_class = if win_attribs.blur { VISUAL_EFFECT_VIEW_CLASS.0 } else { VIEW_CLASS.0 }; | ||
let view: id = msg_send![view_class, alloc]; | ||
msg_send![view, initWithWinit:state_ptr]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Proper Obj-C style dictates that we rebind |
||
if win_attribs.blur { | ||
msg_send![view, setMaterial: BlurMaterial::Light as i64]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to do |
||
msg_send![view, setBlendingMode: 0i64]; | ||
} | ||
IdRef::new(view) | ||
} | ||
} | ||
|
||
|
@@ -65,6 +72,18 @@ unsafe impl Sync for ViewClass {} | |
lazy_static! { | ||
static ref VIEW_CLASS: ViewClass = unsafe { | ||
let superclass = Class::get("NSView").unwrap(); | ||
let decl = common_view_decl(superclass); | ||
ViewClass(decl.register()) | ||
}; | ||
|
||
static ref VISUAL_EFFECT_VIEW_CLASS: ViewClass = unsafe { | ||
let superclass = Class::get("NSVisualEffectView").unwrap(); | ||
let decl = common_view_decl(superclass); | ||
ViewClass(decl.register()) | ||
}; | ||
} | ||
|
||
unsafe fn common_view_decl(superclass: &'static Class) -> ClassDecl { | ||
let mut decl = ClassDecl::new("WinitView", superclass).unwrap(); | ||
decl.add_method(sel!(dealloc), dealloc as extern fn(&Object, Sel)); | ||
decl.add_method( | ||
|
@@ -126,8 +145,7 @@ lazy_static! { | |
decl.add_ivar::<id>("markedText"); | ||
let protocol = Protocol::get("NSTextInputClient").unwrap(); | ||
decl.add_protocol(&protocol); | ||
ViewClass(decl.register()) | ||
}; | ||
decl | ||
} | ||
|
||
extern fn dealloc(this: &Object, _sel: Sel) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ use std::ops::Deref; | |
use std::os::raw::c_void; | ||
use std::sync::Weak; | ||
use std::cell::{Cell, RefCell}; | ||
use os::macos::BlurMaterial; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've been grouping imports into blocks, ordered:
With a newline between each block. (I know this isn't consistently applied throughout the repo yet, but we're getting there.) |
||
|
||
use cocoa; | ||
use cocoa::appkit::{ | ||
|
@@ -582,6 +583,12 @@ impl WindowExt for Window2 { | |
fn get_nsview(&self) -> *mut c_void { | ||
*self.view as *mut c_void | ||
} | ||
|
||
#[inline] | ||
fn set_blur_material(&self, material: BlurMaterial) { | ||
let view = self.get_nsview() as *mut objc::runtime::Object; | ||
unsafe { msg_send![view, setMaterial: material as i64]; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In every other usage, we call Additionally, with |
||
} | ||
} | ||
|
||
impl Window2 { | ||
|
@@ -618,7 +625,7 @@ impl Window2 { | |
return Err(OsError(format!("Couldn't create NSWindow"))); | ||
}, | ||
}; | ||
let view = match Window2::create_view(*window, Weak::clone(&shared)) { | ||
let view = match Window2::create_view(*window, Weak::clone(&shared), &win_attribs) { | ||
Some(view) => view, | ||
None => { | ||
let _: () = unsafe { msg_send![autoreleasepool, drain] }; | ||
|
@@ -843,9 +850,9 @@ impl Window2 { | |
} | ||
} | ||
|
||
fn create_view(window: id, shared: Weak<Shared>) -> Option<IdRef> { | ||
fn create_view(window: id, shared: Weak<Shared>, win_attribs: &WindowAttributes) -> Option<IdRef> { | ||
unsafe { | ||
let view = new_view(window, shared); | ||
let view = new_view(window, shared, win_attribs); | ||
view.non_nil().map(|view| { | ||
view.setWantsBestResolutionOpenGLSurface_(YES); | ||
window.setContentView_(*view); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,6 +96,15 @@ impl WindowBuilder { | |
#[inline] | ||
pub fn with_transparency(mut self, transparent: bool) -> WindowBuilder { | ||
self.window.transparent = transparent; | ||
self.window.blur = !transparent; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the intention with the negation here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Transparency and blur are mutually exclusive. The negation ensures that transprency and blur are always set to the opposite of each other, so that both can never be Writing this comment, I noticed that this obviously creates a bug. When for example |
||
self | ||
} | ||
|
||
/// Sets whether the background of the window should be blurred. | ||
#[inline] | ||
pub fn with_blur(mut self, blur: bool) -> WindowBuilder { | ||
self.window.blur = blur; | ||
self.window.transparent = !blur; | ||
self | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"blurry" has a pretty strong negative connotation. "blurred" sounds more neutral, but that has associations with input focus... "the window should have a blur effect" probably sounds the coolest, and then the second sentence should go on to explain things more.