Skip to content

Commit

Permalink
feat: options to disable individual window controls, closes #116 (#574)
Browse files Browse the repository at this point in the history
* feat(window): implement `minimizable` and `closable` for macOS

* feat(window): examples for `minimizable` and `closable`

* fix(window): stub `minimizable` and `resizable` for other platforms

* lint: run cargo fmt

* feat(window): implement `closable` for Linux

* refactor: rename parameter

* feat(window): implement `maximizable` for macOS

* feat(window): implement `minimizable` for Windows

* fix: edit bitflag for minimizable and move line

# Conflicts:
#	src/platform_impl/windows/window_state.rs

* fix(window): initial `minimzable` value on Windows

* feat(window): implement `maximizable` for Windows

* docs(window): correct platform-specific notes

* feat(window): implement `closable` for Windows

* fix(window): missing `mut`

* chore: add changefile

* docs(window): correct platform-specific notes

* docs(window): improve docs for window control toggles

* refactor (examples): move window control examples into window_debug.rs

This reverts commit ddb9f2b.

* refactor(window): move `set_closable` impl on Windows to `WindowFlags::apply_diff`

* chore: cargo fmt

* chore: update changefile

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* chore: remove println

* fix with_closable on Windows

* Update src/platform_impl/linux/window.rs

* Update src/platform_impl/linux/window.rs

* Update src/window.rs

* update flags

Co-authored-by: David A Klein <david@kleincyber.com>
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
  • Loading branch information
3 people committed Oct 11, 2022
1 parent b84e9fb commit a50fd86
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changes/window-controls-disable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": patch
---

Add APIs for disabling the individual window controls on desktop platforms, `Window::set_closable`, `Window::is_closable`, `WindowBuilder::with_closable`, `Window::set_minimizable`, `Window::is_minimizable`, `WindowBuilder::with_minimizable`, `Window::set_maximizable`, `Window::is_maximizable`, `WindowBuilder::with_maximizable`. See the docs for platform-specific notes, especially regarding Linux.
29 changes: 19 additions & 10 deletions examples/window_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ fn main() {
eprintln!("debugging keys:");
eprintln!(" (E) Enter exclusive fullscreen");
eprintln!(" (F) Toggle borderless fullscreen");
eprintln!(" (P) Toggle borderless fullscreen on system's preffered monitor");
eprintln!(" (M) Toggle minimized");
eprintln!(" (Q) Quit event loop");
eprintln!(" (P) Toggle borderless fullscreen on system's preferred monitor");
eprintln!(" (V) Toggle visibility");
eprintln!(" (X) Toggle maximized");
eprintln!(" (T) Toggle always on top");
eprintln!(" (B) Toggle always on bottom");
eprintln!(" (C) Toggle content protection");
eprintln!(" (M) Toggle minimized");
eprintln!(" (X) Toggle maximized");
eprintln!(" (Q) Quit event loop");
eprintln!(" (Shift + M) Toggle minimizable");
eprintln!(" (Shift + X) Toggle maximizable");
eprintln!(" (Shift + Q) Toggle closable");

let mut always_on_bottom = false;
let mut always_on_top = false;
Expand Down Expand Up @@ -139,19 +142,25 @@ fn main() {
content_protection = !content_protection;
window.set_content_protection(content_protection);
}
"M" => {
let minimizable = !window.is_minimizable();
window.set_minimizable(minimizable);
}
"X" => {
let maximizable = !window.is_maximizable();
window.set_maximizable(maximizable);
}
"Q" => {
let closable = !window.is_closable();
window.set_closable(closable);
}
_ => (),
},
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
..
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Focused(focused),
..
} => {
dbg!(focused, window.is_focused());
}
_ => (),
}
});
Expand Down
35 changes: 32 additions & 3 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,21 @@ impl Window {
false
}

pub fn set_resizable(&self, _resizeable: bool) {}
pub fn set_resizable(&self, _resizeable: bool) {
warn!("`Window::set_resizable` is ignored on Android")
}

pub fn set_minimizable(&self, _minimizable: bool) {
warn!("`Window::set_minimizable` is ignored on Android")
}

pub fn set_maximizable(&self, _maximizable: bool) {
warn!("`Window::set_maximizable` is ignored on Android")
}

pub fn set_closable(&self, _closable: bool) {
warn!("`Window::set_closable` is ignored on Android")
}

pub fn set_minimized(&self, _minimized: bool) {}

Expand All @@ -622,12 +636,27 @@ impl Window {
}

pub fn is_visible(&self) -> bool {
log::warn!("`Window::is_visible` is ignored on android");
log::warn!("`Window::is_visible` is ignored on Android");
false
}

pub fn is_resizable(&self) -> bool {
warn!("`Window::is_resizable` is ignored on android");
warn!("`Window::is_resizable` is ignored on Android");
false
}

pub fn is_minimizable(&self) -> bool {
warn!("`Window::is_minimizable` is ignored on Android");
false
}

pub fn is_maximizable(&self) -> bool {
warn!("`Window::is_maximizable` is ignored on Android");
false
}

pub fn is_closable(&self) -> bool {
warn!("`Window::is_closable` is ignored on Android");
false
}

Expand Down
27 changes: 27 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ impl Inner {
warn!("`Window::set_resizable` is ignored on iOS")
}

pub fn set_minimizable(&self, _minimizable: bool) {
warn!("`Window::set_minimizable` is ignored on iOS")
}

pub fn set_maximizable(&self, _maximizable: bool) {
warn!("`Window::set_maximizable` is ignored on iOS")
}

pub fn set_closable(&self, _closable: bool) {
warn!("`Window::set_closable` is ignored on iOS")
}

pub fn scale_factor(&self) -> f64 {
unsafe {
let hidpi: CGFloat = msg_send![self.view, contentScaleFactor];
Expand Down Expand Up @@ -240,6 +252,21 @@ impl Inner {
false
}

pub fn is_minimizable(&self) -> bool {
warn!("`Window::is_minimizable` is ignored on iOS");
false
}

pub fn is_maximizable(&self) -> bool {
warn!("`Window::is_maximizable` is ignored on iOS");
false
}

pub fn is_closable(&self) -> bool {
warn!("`Window::is_closable` is ignored on iOS");
false
}

pub fn is_decorated(&self) -> bool {
warn!("`Window::is_decorated` is ignored on iOS");
false
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ impl<T: 'static> EventLoop<T> {
window.present_with_time(gdk_sys::GDK_CURRENT_TIME as _);
}
WindowRequest::Resizable(resizable) => window.set_resizable(resizable),
WindowRequest::Closable(closable) => window.set_deletable(closable),
WindowRequest::Minimized(minimized) => {
if minimized {
window.iconify();
Expand Down
27 changes: 27 additions & 0 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl Window {
}

window.set_resizable(attributes.resizable);
window.set_deletable(attributes.closable);

// Set Min/Max Size
let geom_mask = (if attributes.min_inner_size.is_some() {
Expand Down Expand Up @@ -498,6 +499,19 @@ impl Window {
}
}

pub fn set_minimizable(&self, _minimizable: bool) {}

pub fn set_maximizable(&self, _maximizable: bool) {}

pub fn set_closable(&self, closable: bool) {
if let Err(e) = self
.window_requests_tx
.send((self.window_id, WindowRequest::Closable(closable)))
{
log::warn!("Fail to send closable request: {}", e);
}
}

pub fn set_minimized(&self, minimized: bool) {
if let Err(e) = self
.window_requests_tx
Expand Down Expand Up @@ -528,6 +542,18 @@ impl Window {
self.window.is_resizable()
}

pub fn is_minimizable(&self) -> bool {
true
}

pub fn is_maximizable(&self) -> bool {
true
}

pub fn is_closable(&self) -> bool {
self.window.is_deletable()
}

pub fn is_decorated(&self) -> bool {
self.window.is_decorated()
}
Expand Down Expand Up @@ -779,6 +805,7 @@ pub enum WindowRequest {
Visible(bool),
Focus,
Resizable(bool),
Closable(bool),
Minimized(bool),
Maximized(bool),
DragWindow,
Expand Down
69 changes: 69 additions & 0 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ fn create_window(
masks &= !NSWindowStyleMask::NSResizableWindowMask;
}

if !attrs.minimizable {
masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
}

if !attrs.closable {
masks &= !NSWindowStyleMask::NSClosableWindowMask;
}

if pl_attrs.fullsize_content_view {
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
}
Expand Down Expand Up @@ -247,6 +255,11 @@ fn create_window(
];
}

if !attrs.maximizable {
let button = ns_window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
let _: () = msg_send![button, setEnabled: NO];
}

if let Some(increments) = pl_attrs.resize_increments {
let (x, y) = (increments.width, increments.height);
if x >= 1.0 && y >= 1.0 {
Expand Down Expand Up @@ -705,6 +718,38 @@ impl UnownedWindow {
} // Otherwise, we don't change the mask until we exit fullscreen.
}

#[inline]
pub fn set_minimizable(&self, minimizable: bool) {
let mut mask = unsafe { self.ns_window.styleMask() };
if minimizable {
mask |= NSWindowStyleMask::NSMiniaturizableWindowMask;
} else {
mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
}
self.set_style_mask_async(mask);
}

#[inline]
pub fn set_maximizable(&self, maximizable: bool) {
unsafe {
let button = self
.ns_window
.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
let _: () = msg_send![button, setEnabled: maximizable];
}
}

#[inline]
pub fn set_closable(&self, closable: bool) {
let mut mask = unsafe { self.ns_window.styleMask() };
if closable {
mask |= NSWindowStyleMask::NSClosableWindowMask;
} else {
mask &= !NSWindowStyleMask::NSClosableWindowMask;
}
self.set_style_mask_async(mask);
}

pub fn set_cursor_icon(&self, cursor: CursorIcon) {
let cursor = util::Cursor::from(cursor);
if let Some(cursor_access) = self.cursor_state.upgrade() {
Expand Down Expand Up @@ -898,6 +943,30 @@ impl UnownedWindow {
is_resizable == YES
}

#[inline]
pub fn is_minimizable(&self) -> bool {
let is_minimizable: BOOL = unsafe { msg_send![*self.ns_window, isMiniaturizable] };
is_minimizable == YES
}

#[inline]
pub fn is_maximizable(&self) -> bool {
let is_maximizable: BOOL;
unsafe {
let button = self
.ns_window
.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
is_maximizable = msg_send![button, isEnabled];
}
is_maximizable == YES
}

#[inline]
pub fn is_closable(&self) -> bool {
let is_closable: BOOL = unsafe { msg_send![*self.ns_window, hasCloseBox] };
is_closable == YES
}

#[inline]
pub fn is_decorated(&self) -> bool {
self.decorations.load(Ordering::Acquire)
Expand Down

0 comments on commit a50fd86

Please sign in to comment.