Browse Source

feat: allow setting window shape (#13789) (backport 2-0-x) (#13989)

This binds Widget::SetShape, an API that already exists in Chromium (for
Windows and Linux). It's a more reliable method of having some parts of
your window be "click-through" than the current `setIgnoreMouseEvents`
API, which messes around with the `WS_EX_LAYERED` window style on
Windows, causing strange bugs and incompatibility with hardware
acceleration.
Jeremy Apthorp 6 years ago
parent
commit
8683ba33c1

+ 11 - 0
atom/browser/api/atom_api_window.cc

@@ -644,6 +644,16 @@ double Window::GetOpacity() {
   return window_->GetOpacity();
 }
 
+void Window::SetShape(const std::vector<gfx::Rect>& rects) {
+#if !defined(OS_MACOSX)
+  auto native_window_views = static_cast<NativeWindowViews*>(window_.get());
+  auto shape = base::MakeUnique<SkRegion>();
+  for (const auto& rect : rects)
+    shape->op(gfx::RectToSkIRect(rect), SkRegion::kUnion_Op);
+  native_window_views->widget()->SetShape(std::move(shape));
+#endif  // !defined(OS_MACOSX)
+}
+
 void Window::FocusOnWebView() {
   window_->FocusOnWebView();
 }
@@ -1084,6 +1094,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("hasShadow", &Window::HasShadow)
       .SetMethod("setOpacity", &Window::SetOpacity)
       .SetMethod("getOpacity", &Window::GetOpacity)
+      .SetMethod("setShape", &Window::SetShape)
       .SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename)
       .SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename)
       .SetMethod("setDocumentEdited", &Window::SetDocumentEdited)

+ 1 - 0
atom/browser/api/atom_api_window.h

@@ -164,6 +164,7 @@ class Window : public mate::TrackableObject<Window>,
   bool HasShadow();
   void SetOpacity(const double opacity);
   double GetOpacity();
+  void SetShape(const std::vector<gfx::Rect>& rects);
   void FocusOnWebView();
   void BlurWebView();
   bool IsWebViewFocused();

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

@@ -1249,6 +1249,17 @@ Sets the opacity of the window. On Linux does nothing.
 
 Returns `Number` - between 0.0 (fully transparent) and 1.0 (fully opaque)
 
+#### `win.setShape(rects)` _Windows_ _Linux_ _Experimental_
+
+* `rects` [Rectangle[]](structures/rectangle.md) - Sets a shape on the window.
+  Passing an empty list reverts the window to being rectangular.
+
+Setting a window shape determines the area within the window where the system
+permits drawing and user interaction. Outside of the given region, no pixels
+will be drawn and no mouse events will be registered. Mouse events outside of
+the region will not be received by that window, but will fall through to
+whatever is behind the window.
+
 #### `win.setThumbarButtons(buttons)` _Windows_
 
 * `buttons` [ThumbarButton[]](structures/thumbar-button.md)

+ 11 - 0
spec/api-browser-window-spec.js

@@ -903,6 +903,17 @@ describe('BrowserWindow module', () => {
     })
   })
 
+  describe('BrowserWindow.setShape(rects)', () => {
+    it('allows setting shape', () => {
+      assert.doesNotThrow(() => {
+        w.setShape([])
+        w.setShape([{x: 0, y: 0, width: 100, height: 100}])
+        w.setShape([{x: 0, y: 0, width: 100, height: 100}, {x: 0, y: 200, width: 1000, height: 100}])
+        w.setShape([])
+      })
+    })
+  })
+
   describe('"useContentSize" option', () => {
     it('make window created with content size when used', () => {
       w.destroy()