From 2a554cb1385d20f0907e039ef03af748ac952840 Mon Sep 17 00:00:00 2001 From: evgenyzinoviev Date: Mon, 18 Jan 2016 23:46:35 +0100 Subject: [PATCH] added or/and improved closable, minimizable, movable, resizable features for windows on os x and windows --- atom/browser/api/atom_api_window.cc | 30 +++++++ atom/browser/api/atom_api_window.h | 6 ++ atom/browser/native_window.cc | 8 ++ atom/browser/native_window.h | 6 ++ atom/browser/native_window_mac.h | 7 ++ atom/browser/native_window_mac.mm | 70 ++++++++++++++- atom/browser/native_window_views.cc | 110 ++++++++++++++++++++---- atom/browser/native_window_views.h | 9 ++ atom/browser/native_window_views_win.cc | 9 ++ atom/common/options_switches.cc | 34 ++++---- atom/common/options_switches.h | 2 + 11 files changed, 255 insertions(+), 36 deletions(-) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 09d88edb9b..5ff9cabc90 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -408,6 +408,30 @@ bool Window::IsResizable() { return window_->IsResizable(); } +void Window::SetMovable(bool movable) { + window_->SetMovable(movable); +} + +bool Window::IsMovable() { + return window_->IsMovable(); +} + +void Window::SetMinimizable(bool minimizable) { + window_->SetMinimizable(minimizable); +} + +bool Window::IsMinimizable() { + return window_->IsMinimizable(); +} + +void Window::SetClosable(bool closable) { + window_->SetClosable(closable); +} + +bool Window::IsClosable() { + return window_->IsClosable(); +} + void Window::SetAlwaysOnTop(bool top) { window_->SetAlwaysOnTop(top); } @@ -659,6 +683,12 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("getMaximumSize", &Window::GetMaximumSize) .SetMethod("setResizable", &Window::SetResizable) .SetMethod("isResizable", &Window::IsResizable) + .SetMethod("setMovable", &Window::SetMovable) + .SetMethod("isMovable", &Window::IsMovable) + .SetMethod("setMinimizable", &Window::SetMinimizable) + .SetMethod("isMinimizable", &Window::IsMinimizable) + .SetMethod("setClosable", &Window::SetClosable) + .SetMethod("isClosable", &Window::IsClosable) .SetMethod("setAlwaysOnTop", &Window::SetAlwaysOnTop) .SetMethod("isAlwaysOnTop", &Window::IsAlwaysOnTop) .SetMethod("center", &Window::Center) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index d8e25965d8..ae10fa951d 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -106,6 +106,12 @@ class Window : public mate::TrackableObject, std::vector GetMaximumSize(); void SetResizable(bool resizable); bool IsResizable(); + void SetMovable(bool movable); + bool IsMovable(); + void SetMinimizable(bool minimizable); + bool IsMinimizable(); + void SetClosable(bool closable); + bool IsClosable(); void SetAlwaysOnTop(bool top); bool IsAlwaysOnTop(); void Center(); diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 4df4b49169..1f330a261c 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -120,6 +120,14 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) { if (options.Get(options::kResizable, &resizable)) { SetResizable(resizable); } + bool minimizable; + if (options.Get(options::kMinimizable, &minimizable)) { + SetMinimizable(minimizable); + } + bool closable; + if (options.Get(options::kClosable, &closable)) { + SetClosable(closable); + } #endif bool top; if (options.Get(options::kAlwaysOnTop, &top) && top) { diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 0ccf3dfbc2..d379db2b49 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -125,6 +125,12 @@ class NativeWindow : public base::SupportsUserData, virtual gfx::Size GetMaximumSize(); virtual void SetResizable(bool resizable) = 0; virtual bool IsResizable() = 0; + virtual void SetMovable(bool movable) = 0; + virtual bool IsMovable() = 0; + virtual void SetMinimizable(bool minimizable) = 0; + virtual bool IsMinimizable() = 0; + virtual void SetClosable(bool closable) = 0; + virtual bool IsClosable() = 0; virtual void SetAlwaysOnTop(bool top) = 0; virtual bool IsAlwaysOnTop() = 0; virtual void Center() = 0; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index ef3ccb1502..45c974c079 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -48,6 +48,12 @@ class NativeWindowMac : public NativeWindow { const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; bool IsResizable() override; + void SetMovable(bool movable) override; + bool IsMovable() override; + void SetMinimizable(bool minimizable) override; + bool IsMinimizable() override; + void SetClosable(bool closable) override; + bool IsClosable() override; void SetAlwaysOnTop(bool top) override; bool IsAlwaysOnTop() override; void Center() override; @@ -100,6 +106,7 @@ class NativeWindowMac : public NativeWindow { gfx::Size WindowSizeToContentSize(const gfx::Size& size) override; void UpdateDraggableRegions( const std::vector& regions) override; + void FixZoomButton(); void InstallView(); void UninstallView(); diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index f128f3b2cb..1c91c5efa5 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -369,6 +369,12 @@ NativeWindowMac::NativeWindowMac( bool resizable = true; options.Get(options::kResizable, &resizable); + bool minimizable = true; + options.Get(options::kMinimizable, &minimizable); + + bool closable = true; + options.Get(options::kClosable, &closable); + // New title bar styles are available in Yosemite or newer std::string titleBarStyle; if (base::mac::IsOSYosemiteOrLater()) @@ -385,8 +391,13 @@ NativeWindowMac::NativeWindowMac( useStandardWindow = false; } - NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask; + NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask; + if (minimizable) { + styleMask |= NSMiniaturizableWindowMask; + } + if (!closable) { + styleMask &= ~NSClosableWindowMask; + } if (!useStandardWindow || transparent() || !has_frame()) { styleMask |= NSTexturedBackgroundWindowMask; } @@ -487,6 +498,11 @@ NativeWindowMac::NativeWindowMac( [window_ setCollectionBehavior:collectionBehavior]; } + // Disable zoom button if window is not resizable + if (!resizable) { + [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO]; + } + NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; @@ -635,8 +651,10 @@ void NativeWindowMac::SetResizable(bool resizable) { [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:YES]; [window_ setStyleMask:[window_ styleMask] | NSResizableWindowMask]; } else { - [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO]; + // If we disable the button before changing the styleMask, button is not + // disabled. Looks like a bug in Cocoa (OS X 10.10.5) [window_ setStyleMask:[window_ styleMask] & (~NSResizableWindowMask)]; + [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO]; } } @@ -644,10 +662,56 @@ bool NativeWindowMac::IsResizable() { return [window_ styleMask] & NSResizableWindowMask; } +void NativeWindowMac::SetMovable(bool movable) { + [window_ setMovable:movable]; +} + +bool NativeWindowMac::IsMovable() { + return [window_ isMovable]; +} + +void NativeWindowMac::SetMinimizable(bool minimizable) { + if (minimizable) { + [window_ setStyleMask:[window_ styleMask] | NSMiniaturizableWindowMask]; + } else { + [window_ setStyleMask:[window_ styleMask] & (~NSMiniaturizableWindowMask)]; + } + FixZoomButton(); +} + +bool NativeWindowMac::IsMinimizable() { + return [window_ styleMask] & NSMiniaturizableWindowMask; +} + +void NativeWindowMac::SetClosable(bool closable) { + if (closable) { + [window_ setStyleMask:[window_ styleMask] | NSClosableWindowMask]; + } else { + [window_ setStyleMask:[window_ styleMask] & (~NSClosableWindowMask)]; + } + FixZoomButton(); +} + +bool NativeWindowMac::IsClosable() { + return [window_ styleMask] & NSClosableWindowMask; +} + void NativeWindowMac::SetAlwaysOnTop(bool top) { [window_ setLevel:(top ? NSFloatingWindowLevel : NSNormalWindowLevel)]; } +void NativeWindowMac::FixZoomButton() { + // If fullscreen has not been disabled via `fullscreen: false` (i.e. when + // collectionBehavior has NSWindowCollectionBehaviorFullScreenPrimary mask), + // zoom button is reset to it's default (enabled) state when window's + // styleMask has been changed. So if the button was disabled, we have to + // disable it again. I think it's a bug in Cocoa. + if ([window_ collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary + && !([window_ styleMask] & NSResizableWindowMask)) { + [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO]; + } +} + bool NativeWindowMac::IsAlwaysOnTop() { return [window_ level] == NSFloatingWindowLevel; } diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index ad08604235..25e45ea6f1 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -126,7 +126,10 @@ NativeWindowViews::NativeWindowViews( menu_bar_alt_pressed_(false), keyboard_event_handler_(new views::UnhandledKeyboardEventHandler), use_content_size_(false), - resizable_(true) { + resizable_(true), + maximizable_(true), + movable_(true), + minimizable_(true) { options.Get(options::kTitle, &title_); options.Get(options::kAutoHideMenuBar, &menu_bar_autohide_); @@ -134,6 +137,8 @@ NativeWindowViews::NativeWindowViews( // On Windows we rely on the CanResize() to indicate whether window can be // resized, and it should be set before window is created. options.Get(options::kResizable, &resizable_); + options.Get(options::kMovable, &movable_); + options.Get(options::kMinimizable, &minimizable_); #endif if (enable_larger_than_screen()) @@ -182,7 +187,9 @@ NativeWindowViews::NativeWindowViews( window_->Init(params); bool fullscreen = false; - options.Get(options::kFullscreen, &fullscreen); + if (options.Get(options::kFullscreen, &fullscreen) && !fullscreen) { + maximizable_ = false; + } #if defined(USE_X11) // Start monitoring window states. @@ -236,22 +243,27 @@ NativeWindowViews::NativeWindowViews( last_normal_size_ = gfx::Size(widget_size_); - if (!has_frame()) { - // Set Window style so that we get a minimize and maximize animation when - // frameless. - DWORD frame_style = WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | - WS_CAPTION; - // We should not show a frame for transparent window. - if (transparent()) - frame_style &= ~(WS_THICKFRAME | WS_CAPTION); - ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, frame_style); + DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE); + style |= WS_THICKFRAME | WS_CAPTION | WS_MINIMIZEBOX; + if (!maximizable_) { + style &= (~WS_MAXIMIZEBOX); + } else { + style |= WS_MAXIMIZEBOX; } if (transparent()) { - // Transparent window on Windows has to have WS_EX_COMPOSITED style. - LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); + DWORD ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); ex_style |= WS_EX_COMPOSITED; ::SetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE, ex_style); + + if (!has_frame()) { + // We should not show a frame for transparent window. + style &= ~(WS_THICKFRAME | WS_CAPTION); + } + } + + if (!transparent() || !has_frame()) { + ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style); } #endif @@ -427,10 +439,12 @@ void NativeWindowViews::SetResizable(bool resizable) { // WS_THICKFRAME => Resize handle if (!transparent()) { DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE); - if (resizable) - style |= WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME; - else - style = (style & ~(WS_MAXIMIZEBOX | WS_THICKFRAME)) | WS_MINIMIZEBOX; + if (resizable) { + style |= WS_THICKFRAME; + if (maximizable_) style |= WS_MAXIMIZEBOX; + } else { + style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); + } ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style); } #elif defined(USE_X11) @@ -456,6 +470,64 @@ bool NativeWindowViews::IsResizable() { return resizable_; } +void NativeWindowViews::SetMovable(bool movable) { + movable_ = movable; +} + +bool NativeWindowViews::IsMovable() { + return movable_; +} + +void NativeWindowViews::SetMinimizable(bool minimizable) { +#if defined(OS_WIN) + if (!transparent()) { + DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE); + if (minimizable) + style |= WS_MINIMIZEBOX; + else + style &= (~WS_MINIMIZEBOX); + ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style); + } +#endif + + minimizable_ = minimizable; +} + +bool NativeWindowViews::IsMinimizable() { +#if defined(OS_WIN) + return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_MINIMIZEBOX; +#elif defined(USE_X11) + return true; +#endif +} + +void NativeWindowViews::SetClosable(bool closable) { +#if defined(OS_WIN) + HMENU menu = GetSystemMenu(GetAcceleratedWidget(), false); + if (closable) { + EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_ENABLED); + } else { + EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + } +#endif +} + +bool NativeWindowViews::IsClosable() { +#if defined(OS_WIN) + HMENU menu = GetSystemMenu(GetAcceleratedWidget(), false); + MENUITEMINFO info; + memset(&info, 0, sizeof(info)); + info.cbSize = sizeof(info); + info.fMask = MIIM_STATE; + if (!GetMenuItemInfo(menu, SC_CLOSE, false, &info)) { + return false; + } + return !(info.fState & MFS_DISABLED); +#elif defined(USE_X11) + return true; +#endif +} + void NativeWindowViews::SetAlwaysOnTop(bool top) { window_->SetAlwaysOnTop(top); } @@ -717,7 +789,11 @@ bool NativeWindowViews::CanMaximize() const { } bool NativeWindowViews::CanMinimize() const { +#ifdef (OS_WIN) + return minimizable_; +#elif defined(USE_X11) return true; +#endif } base::string16 NativeWindowViews::GetWindowTitle() const { diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 6592242210..d3117e5b2f 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -68,6 +68,12 @@ class NativeWindowViews : public NativeWindow, const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; bool IsResizable() override; + void SetMovable(bool movable) override; + bool IsMovable() override; + void SetMinimizable(bool minimizable) override; + bool IsMinimizable() override; + void SetClosable(bool closable) override; + bool IsClosable() override; void SetAlwaysOnTop(bool top) override; bool IsAlwaysOnTop() override; void Center() override; @@ -197,6 +203,9 @@ class NativeWindowViews : public NativeWindow, bool use_content_size_; bool resizable_; + bool maximizable_; + bool movable_; + bool minimizable_; std::string title_; gfx::Size widget_size_; diff --git a/atom/browser/native_window_views_win.cc b/atom/browser/native_window_views_win.cc index 513b33bcb8..53228d5fdf 100644 --- a/atom/browser/native_window_views_win.cc +++ b/atom/browser/native_window_views_win.cc @@ -89,10 +89,19 @@ bool NativeWindowViews::PreHandleMSG( if (HIWORD(w_param) == THBN_CLICKED) return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param)); return false; + case WM_SIZE: // Handle window state change. HandleSizeEvent(w_param, l_param); return false; + + case WM_MOVING: { + if (!movable_) { + ::GetWindowRect(GetAcceleratedWidget(), (LPRECT)l_param); + } + return false; + } + default: return false; } diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 477fe6755f..e14a63ea92 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -8,22 +8,24 @@ namespace atom { namespace options { -const char kTitle[] = "title"; -const char kIcon[] = "icon"; -const char kFrame[] = "frame"; -const char kShow[] = "show"; -const char kCenter[] = "center"; -const char kX[] = "x"; -const char kY[] = "y"; -const char kWidth[] = "width"; -const char kHeight[] = "height"; -const char kMinWidth[] = "minWidth"; -const char kMinHeight[] = "minHeight"; -const char kMaxWidth[] = "maxWidth"; -const char kMaxHeight[] = "maxHeight"; -const char kResizable[] = "resizable"; -const char kMovable[] = "movable"; -const char kFullscreen[] = "fullscreen"; +const char kTitle[] = "title"; +const char kIcon[] = "icon"; +const char kFrame[] = "frame"; +const char kShow[] = "show"; +const char kCenter[] = "center"; +const char kX[] = "x"; +const char kY[] = "y"; +const char kWidth[] = "width"; +const char kHeight[] = "height"; +const char kMinWidth[] = "minWidth"; +const char kMinHeight[] = "minHeight"; +const char kMaxWidth[] = "maxWidth"; +const char kMaxHeight[] = "maxHeight"; +const char kResizable[] = "resizable"; +const char kMovable[] = "movable"; +const char kMinimizable[] = "minimizable"; +const char kClosable[] = "closable"; +const char kFullscreen[] = "fullscreen"; // Whether the window should show in taskbar. const char kSkipTaskbar[] = "skipTaskbar"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 78de53b825..f7b68f218b 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -24,6 +24,8 @@ extern const char kMaxWidth[]; extern const char kMaxHeight[]; extern const char kResizable[]; extern const char kMovable[]; +extern const char kMinimizable[]; +extern const char kClosable[]; extern const char kFullscreen[]; extern const char kSkipTaskbar[]; extern const char kKiosk[];