Skip to content

macOS: traffic light positions reset after content view layout passes #1747

@jpawelczyk

Description

@jpawelczyk

Description

trafficLightPosition / with_traffic_light_inset positions are not maintained after AppKit layout passes triggered by content view replacement and webview content rendering. The traffic light buttons snap back to default macOS positions.

Root Cause

WryWebViewParent repositions traffic lights only in its drawRect: override (wry_web_view_parent.rs:42-47). drawRect: is a draw-phase method, not a layout-phase method. AppKit can reset button positions during layout passes that occur independently of (and sometimes after) the draw pass.

Specifically:

  1. setContentView: (at wkwebview/mod.rs:664) triggers a titlebar relayout that can reset button positions after set_traffic_light_inset already positioned them correctly at line 653.
  2. When webview content loads and triggers a content size change, AppKit performs another layout pass that resets button positions.
  3. If these layout passes don't trigger a subsequent drawRect: call, the buttons stay at default positions.

This is most visible in production builds where the bundled frontend loads synchronously from the app bundle, causing the content-triggered relayout to happen during initial display before any user interaction triggers a redraw.

Steps to Reproduce

  1. Create a Tauri v2 app with titleBarStyle: "Overlay" and trafficLightPosition: { "x": 16, "y": 22 }
  2. Build a production binary (tauri build)
  3. Launch the app
  4. Observe that traffic light buttons are at the default macOS position, not at (16, 22)

The issue is intermittent and timing-dependent -- it reproduces more reliably in production builds than dev builds (likely due to different content load timing).

Environment

  • wry: 0.54.2
  • tao: 0.34.5
  • tauri: 2.10.2
  • macOS: 15.4 (Sequoia)
  • Architecture: aarch64 (Apple Silicon)

Suggested Fix

Override viewDidLayout (or layout) on WryWebViewParent in addition to drawRect:, so that traffic light repositioning happens during the layout phase -- not just the draw phase. This would catch all layout-triggered resets regardless of whether a redraw follows.

Alternatively, register an observer for NSViewFrameDidChangeNotification on the titlebar container view to detect when AppKit resets button positions.

Current Workaround

We replicate inset_traffic_lights in our own code and trigger it from:

  • A frontend event emitted after React renders (catches initial layout reset)
  • Tauri's on_window_event for Resized/ThemeChanged/Focused (catches ongoing drift)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions