Browse Source

feat: programmatically modify traffic light positioning (#22533) (#22566)

* setter

* getter

* specs and docs

* fixup

Co-authored-by: Samuel Attard <[email protected]>

Co-authored-by: Samuel Attard <[email protected]>
Samuel Attard 5 years ago
parent
commit
539ca773de

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

@@ -1748,6 +1748,17 @@ will remove the vibrancy effect on the window.
 Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been
 deprecated and will be removed in an upcoming version of macOS.
 
+#### `win.setTrafficLightPosition(position)` _macOS_
+
+* `position` [Point](structures/point.md)
+
+Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden`.
+
+#### `win.getTrafficLightPosition()` _macOS_
+
+Returns `Point` - The current position for the traffic light buttons. Can only be used with `titleBarStyle`
+set to `hidden`.
+
 #### `win.setTouchBar(touchBar)` _macOS_ _Experimental_
 
 * `touchBar` TouchBar | null

+ 16 - 0
shell/browser/api/electron_api_top_level_window.cc

@@ -808,6 +808,16 @@ void TopLevelWindow::SetVibrancy(v8::Isolate* isolate,
   window_->SetVibrancy(type);
 }
 
+#if defined(OS_MACOSX)
+void TopLevelWindow::SetTrafficLightPosition(const gfx::Point& position) {
+  window_->SetTrafficLightPosition(position);
+}
+
+gfx::Point TopLevelWindow::GetTrafficLightPosition() const {
+  return window_->GetTrafficLightPosition();
+}
+#endif
+
 void TopLevelWindow::SetTouchBar(
     std::vector<gin_helper::PersistentDictionary> items) {
   window_->SetTouchBar(std::move(items));
@@ -1178,6 +1188,12 @@ void TopLevelWindow::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("setAutoHideCursor", &TopLevelWindow::SetAutoHideCursor)
 #endif
       .SetMethod("setVibrancy", &TopLevelWindow::SetVibrancy)
+#if defined(OS_MACOSX)
+      .SetMethod("setTrafficLightPosition",
+                 &TopLevelWindow::SetTrafficLightPosition)
+      .SetMethod("getTrafficLightPosition",
+                 &TopLevelWindow::GetTrafficLightPosition)
+#endif
       .SetMethod("_setTouchBarItems", &TopLevelWindow::SetTouchBar)
       .SetMethod("_refreshTouchBarItem", &TopLevelWindow::RefreshTouchBarItem)
       .SetMethod("_setEscapeTouchBarItem",

+ 6 - 0
shell/browser/api/electron_api_top_level_window.h

@@ -185,6 +185,12 @@ class TopLevelWindow : public gin_helper::TrackableObject<TopLevelWindow>,
   bool IsVisibleOnAllWorkspaces();
   void SetAutoHideCursor(bool auto_hide);
   virtual void SetVibrancy(v8::Isolate* isolate, v8::Local<v8::Value> value);
+
+#if defined(OS_MACOSX)
+  void SetTrafficLightPosition(const gfx::Point& position);
+  gfx::Point GetTrafficLightPosition() const;
+#endif
+
   void SetTouchBar(std::vector<gin_helper::PersistentDictionary> items);
   void RefreshTouchBarItem(const std::string& item_id);
   void SetEscapeTouchBarItem(gin_helper::PersistentDictionary item);

+ 6 - 0
shell/browser/native_window.h

@@ -194,6 +194,12 @@ class NativeWindow : public base::SupportsUserData,
   // Vibrancy API
   virtual void SetVibrancy(const std::string& type);
 
+  // Traffic Light API
+#if defined(OS_MACOSX)
+  virtual void SetTrafficLightPosition(const gfx::Point& position) = 0;
+  virtual gfx::Point GetTrafficLightPosition() const = 0;
+#endif
+
   // Touchbar API
   virtual void SetTouchBar(std::vector<gin_helper::PersistentDictionary> items);
   virtual void RefreshTouchBarItem(const std::string& item_id);

+ 2 - 0
shell/browser/native_window_mac.h

@@ -153,6 +153,8 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
   // Custom traffic light positioning
   void RepositionTrafficLights();
   void SetExitingFullScreen(bool flag);
+  void SetTrafficLightPosition(const gfx::Point& position) override;
+  gfx::Point GetTrafficLightPosition() const override;
   void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
 
   enum class TitleBarStyle {

+ 9 - 0
shell/browser/native_window_mac.mm

@@ -1552,6 +1552,15 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
     [effect_view setMaterial:vibrancyType];
 }
 
+void NativeWindowMac::SetTrafficLightPosition(const gfx::Point& position) {
+  traffic_light_position_ = position;
+  RepositionTrafficLights();
+}
+
+gfx::Point NativeWindowMac::GetTrafficLightPosition() const {
+  return traffic_light_position_;
+}
+
 void NativeWindowMac::SetTouchBar(
     std::vector<gin_helper::PersistentDictionary> items) {
   if (@available(macOS 10.12.2, *)) {

+ 30 - 5
spec-main/api-browser-window-spec.ts

@@ -194,7 +194,7 @@ describe('BrowserWindow module', () => {
       }).to.throw('Object has been destroyed')
     })
     it('should not crash when destroying windows with pending events', () => {
-      const focusListener = () => {}
+      const focusListener = () => { }
       app.on('browser-window-focus', focusListener)
       const windowCount = 3
       const windowOptions = {
@@ -265,7 +265,7 @@ describe('BrowserWindow module', () => {
               fs.readFile(filePath, (err, data) => {
                 if (err) return
                 if (parsedData.username === 'test' &&
-                    parsedData.file === data.toString()) {
+                  parsedData.file === data.toString()) {
                   res.end()
                 }
               })
@@ -1331,6 +1331,31 @@ describe('BrowserWindow module', () => {
     })
   })
 
+  ifdescribe(process.platform === 'darwin')('BrowserWindow.getTrafficLightPosition(pos)', () => {
+    afterEach(closeAllWindows)
+
+    it('gets the set traffic light position property', () => {
+      const pos = { x: 10, y: 10 }
+      const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos })
+      const currentPosition = w.getTrafficLightPosition()
+
+      expect(currentPosition).to.deep.equal(pos)
+    })
+  })
+
+  ifdescribe(process.platform === 'darwin')('BrowserWindow.setTrafficLightPosition(pos)', () => {
+    afterEach(closeAllWindows)
+
+    it('can set the traffic light position property', () => {
+      const pos = { x: 10, y: 10 }
+      const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos })
+      w.setTrafficLightPosition(pos)
+      const currentPosition = w.getTrafficLightPosition()
+
+      expect(currentPosition).to.deep.equal(pos)
+    })
+  })
+
   ifdescribe(process.platform === 'win32')('BrowserWindow.setAppDetails(options)', () => {
     afterEach(closeAllWindows)
 
@@ -1597,7 +1622,7 @@ describe('BrowserWindow module', () => {
     afterEach(closeAllWindows)
 
     describe('"preload" option', () => {
-      const doesNotLeakSpec = (name: string, webPrefs: {nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean}) => {
+      const doesNotLeakSpec = (name: string, webPrefs: { nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean }) => {
         it(name, async () => {
           const w = new BrowserWindow({
             webPreferences: {
@@ -1843,7 +1868,7 @@ describe('BrowserWindow module', () => {
     })
 
     describe('"sandbox" option', () => {
-      function waitForEvents<T> (emitter: {once: Function}, events: string[], callback: () => void) {
+      function waitForEvents<T> (emitter: { once: Function }, events: string[], callback: () => void) {
         let count = events.length
         for (const event of events) {
           emitter.once(event, () => {
@@ -2829,7 +2854,7 @@ describe('BrowserWindow module', () => {
             return
           }
           if (rect.height === contentHeight && rect.width === contentWidth &&
-              !gotInitialFullSizeFrame) {
+            !gotInitialFullSizeFrame) {
             // The initial frame is full-size, but we're looking for a call
             // with just the dirty-rect. The next frame should be a smaller
             // rect.