Browse Source

feat: add `BrowserWindow.isSnapped()` (#46226)

* feat: add BrowserWindow.isSnapped() on Windows

Co-authored-by: Shelley Vohr <[email protected]>

* docs: mark _Readonly_

Co-authored-by: Shelley Vohr <[email protected]>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <[email protected]>
trop[bot] 3 weeks ago
parent
commit
25e5055065

+ 11 - 0
docs/api/base-window.md

@@ -511,6 +511,10 @@ A `string` property that defines an alternative title provided only to
 accessibility tools such as screen readers. This string is not directly
 visible to users.
 
+#### `win.snapped` _Windows_ _Readonly_
+
+A `boolean` property that indicates whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241)
+
 ### Instance Methods
 
 Objects created with `new BaseWindow` have the following instance methods:
@@ -1264,6 +1268,13 @@ Sets whether the menu bar should be visible. If the menu bar is auto-hide, users
 
 Returns `boolean` - Whether the menu bar is visible.
 
+#### `win.isSnapped()` _Windows_
+
+Returns `boolean` - whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241)
+
+The window is snapped via buttons shown when the mouse is hovered over window
+maximize button, or by dragging it to the edges of the screen.
+
 #### `win.setVisibleOnAllWorkspaces(visible[, options])` _macOS_ _Linux_
 
 * `visible` boolean

+ 11 - 0
docs/api/browser-window.md

@@ -611,6 +611,10 @@ A `string` property that defines an alternative title provided only to
 accessibility tools such as screen readers. This string is not directly
 visible to users.
 
+#### `win.snapped` _Windows_ _Readonly_
+
+A `boolean` property that indicates whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241)
+
 ### Instance Methods
 
 Objects created with `new BrowserWindow` have the following instance methods:
@@ -1445,6 +1449,13 @@ Sets whether the menu bar should be visible. If the menu bar is auto-hide, users
 
 Returns `boolean` - Whether the menu bar is visible.
 
+#### `win.isSnapped()` _Windows_
+
+Returns `boolean` - whether the window is arranged via [Snap.](https://support.microsoft.com/en-us/windows/snap-your-windows-885a9b1e-a983-a3b1-16cd-c531795e6241)
+
+The window is snapped via buttons shown when the mouse is hovered over window
+maximize button, or by dragging it to the edges of the screen.
+
 #### `win.setVisibleOnAllWorkspaces(visible[, options])` _macOS_ _Linux_
 
 * `visible` boolean

+ 6 - 0
shell/browser/api/electron_api_base_window.cc

@@ -1091,6 +1091,10 @@ void BaseWindow::SetAppDetails(const gin_helper::Dictionary& options) {
                                   relaunch_command, relaunch_display_name,
                                   window_->GetAcceleratedWidget());
 }
+
+bool BaseWindow::IsSnapped() const {
+  return window_->IsSnapped();
+}
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
@@ -1335,6 +1339,8 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("setIcon", &BaseWindow::SetIcon)
 #endif
 #if BUILDFLAG(IS_WIN)
+      .SetMethod("isSnapped", &BaseWindow::IsSnapped)
+      .SetProperty("snapped", &BaseWindow::IsSnapped)
       .SetMethod("hookWindowMessage", &BaseWindow::HookWindowMessage)
       .SetMethod("isWindowMessageHooked", &BaseWindow::IsWindowMessageHooked)
       .SetMethod("unhookWindowMessage", &BaseWindow::UnhookWindowMessage)

+ 1 - 0
shell/browser/api/electron_api_base_window.h

@@ -252,6 +252,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
   bool SetThumbnailClip(const gfx::Rect& region);
   bool SetThumbnailToolTip(const std::string& tooltip);
   void SetAppDetails(const gin_helper::Dictionary& options);
+  bool IsSnapped() const;
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)

+ 4 - 0
shell/browser/native_window.cc

@@ -489,6 +489,10 @@ void NativeWindow::SetAspectRatio(double aspect_ratio,
   aspect_ratio_extraSize_ = extra_size;
 }
 
+bool NativeWindow::IsSnapped() const {
+  return false;
+}
+
 std::optional<gfx::Rect> NativeWindow::GetWindowControlsOverlayRect() {
   return overlay_rect_;
 }

+ 2 - 0
shell/browser/native_window.h

@@ -267,6 +267,8 @@ class NativeWindow : public base::SupportsUserData,
   virtual void SetMenuBarVisibility(bool visible) {}
   virtual bool IsMenuBarVisible() const;
 
+  virtual bool IsSnapped() const;
+
   // Set the aspect ratio when resizing window.
   [[nodiscard]] double aspect_ratio() const { return aspect_ratio_; }
   [[nodiscard]] gfx::Size aspect_ratio_extra_size() const {

+ 16 - 0
shell/browser/native_window_views.cc

@@ -1488,6 +1488,22 @@ bool NativeWindowViews::IsMenuBarVisible() const {
   return root_view_.is_menu_bar_visible();
 }
 
+bool NativeWindowViews::IsSnapped() const {
+#if BUILDFLAG(IS_WIN)
+  // IsWindowArranged() is not a part of any header file.
+  // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-iswindowarranged
+  using IsWindowArrangedFuncType = BOOL(WINAPI*)(HWND);
+  static const auto is_window_arranged_func =
+      reinterpret_cast<IsWindowArrangedFuncType>(
+          base::win::GetUser32FunctionPointer("IsWindowArranged"));
+  return is_window_arranged_func
+             ? is_window_arranged_func(GetAcceleratedWidget())
+             : false;
+#else
+  return false;
+#endif
+}
+
 void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
   NativeWindow::SetBackgroundMaterial(material);
 

+ 1 - 0
shell/browser/native_window_views.h

@@ -130,6 +130,7 @@ class NativeWindowViews : public NativeWindow,
   bool IsMenuBarAutoHide() const override;
   void SetMenuBarVisibility(bool visible) override;
   bool IsMenuBarVisible() const override;
+  bool IsSnapped() const override;
   void SetBackgroundMaterial(const std::string& type) override;
 
   void SetVisibleOnAllWorkspaces(bool visible,