[bug] sp-picker not selectable on secondary monitor (includes repro code!)

, ,

Writing a UXP photoshop plugin.

Started with just an panel, populated by a single sp-picker.

  • Works completely fine on primary monitor.
  • When the panel is moved to the secondary monitor, it is no longer selectable via mouse.
  • I am able to use tab navigation to reach the dropdown and open it with arrow keys.

Has anybody else encountered this? The native Photoshop dropdown selectors don’t seem to have this problem.. Only the ones created via the plugin tools

is there a different way to create dropdowns?

[edit] full repro steps, sample plugin/code, and video are posted below !

Switched to webview for the UI instead and don’t have any issues with that

sp-picker-bug-repro.zip (16.8 KB)

Files

There is no build step and no dependencies.

File Purpose
sp-picker-repro.ccx pre-built plugin
package.json script runner
package.sh Builds a .ccx for loading without the debugger
src/manifest.json Panel + command entrypoints, manifest v5
src/index.html The picker, the metrics readout, and the divider
src/index.css Panel styling, theme-aware per Photoshop’s four themes
src/plugin/plugin.js Entrypoint registration, DOM instrumentation, logging
src/plugin/controls.js The control group below the divider

Recordings

gif highlight

photoshop plugin sp-select bug

full video

Bug Information

Minimal reproduction:

  • on a secondary display, an sp-picker’s menu never appears and cannot be used.
  • the sp-picker itself takes focus and shows its focus outline, but the menu is never painted.

The panel instruments the DOM at the moment the menu should be visible. Every property we can read is identical on the working and failing displays: the menu is in the document, computed-visible, and correctly sized. Only the painting differs.

Observed in:

  • Photoshop 27.9.1
  • UXP manifest v5
  • Windows 11

Repro Steps

Load this plugin with the UXP Developer Tool (src/manifest.json), or run ./package.sh and install the resulting .ccx. It reproduces with and without the debugger attached.

  1. With the panel on the primary display, click the picker. The menu opens below the control and works normally.
  2. Drag the panel to a secondary display.
  3. Click the picker again.

Expected: the menu opens below the picker, as on the primary display.

Actual: no menu appears, and no option can be selected. The picker takes focus and draws its focus outline, so the control looks active but is unusable.

Dragging back to the primary display restores it immediately, with no reload and no re-initialisation.

An unexplained workaround

Clicking into a text field, focus only and no keystroke, makes the picker start working on the secondary display.

The text field does not have to be in the plugin. Focusing a text input in one of Photoshop’s own panels, such as the Swatches search box, has the same effect. So this is not about the plugin’s DOM, Spectrum, or the panel’s own focus state.

The control group in the panel includes both text controls so the in-plugin version is reproducible.

A related observation, possibly the same underlying gap:

  • before clicking the panel at all, Tab triggers Photoshop’s own show/hide-panels shortcut
  • before clicking into a text field, Tab does nothing inside the panel
  • after clicking into a text box, Tab and the sp-picker begin working as expected

What the debug logs show

Clicking the picker logs the menu’s root, overlay host, computed visibility, and rect. These are byte-identical on both displays:

menu rect: x=1 y=5 w=286 h=166
menu root: document
menu host: (none — still inline)
menu vis : display=block visibility=visible opacity=1

Read directly, that says the menu is laid out at a normal size, it lives in the plugin’s own document, it was never reparented into a popup or overlay surface, and CSS considers it fully visible.

The DOM believes the menu is visible and correctly placed. It is not drawn.

What this rules out

Each of these was a working hypothesis, and each is contradicted by the readings above:

  • Off-screen positioning. The menu’s rect is the same on both displays and sits within the panel. Nothing is measured off-screen.
  • Overlay mispositioning. There is no overlay. closest("[popover], sp-overlay, sp-popover, dialog") returns nothing, and the menu is inline.
  • CSS hiding it. display=block visibility=visible opacity=1.
  • A layout failure. The menu has a real size, 286×166, on both displays.
  • Display scaling. Ruled out separately, see below.
  • The UXP debugger. Reproduces when loaded from a packaged .ccx with no debugger attached.

Ruled out: display scaling

The first hypothesis was a DPI/scale mismatch between displays, on the strength of Adobe’s own known issue:

window.devicePixelRatio … always returns 1. This is incorrect … window.devicePixelRatio may not always match an element’s pixel ratio if the element is visible on a screen with a different pixel ratio from the primary screen.

This was not applicable to the sp-picker bug. It predicts no failure when both displays share a scale factor, and I tested exactly that case:

  • One physical panel, split 50/50 across two HDMI inputs. Windows sees two displays, but they are the same monitor, with identical resolution, DPI, and scale factor by construction.
  • The picker works on the half Windows designates primary.
  • The picker fails on the half Windows designates secondary.

With scaling held constant, the bug still tracks the primary/secondary designation exactly.

I can’t reproduce this in PS 27.9.1 on macOS 26.6.1, so it may be Windows-specific, though I haven’t been able to test there.

If it only started appearing with 27.9 and later, it’s almost certainly related to the new Drover UXP UI introduced in that release.

Appreciate you looking into it!

I think another important detail might be.. when I open Photoshop, it’s opening to the Primary monitor by default, and I have to drag the window over to the Secondary monitor.

And after you give keyboard to any text input field, then the bug won’t show up again until restarting Photoshop.


I did come up with a programmatic workaround.

I’m able to render this:
(disclaimer: the following is psuedocode, may not run as-is)

<div class='dropdown'>

  <div id='select-overlay'>
    // height, width equal to the sp-picker
  </div>
  
  <div id='select-text-wrapper'>
    // width: 0%, height: 0%, overflow: hidden;
    // sp-textfield is not visible on-screen but 
    // is still programmatically focusable/clickable

    // there is no way to give sp-textfield a transparent background..
    // any attempt to results in a black rectangle. So, another hacky fix.
    
    <sp-textfield id='select-text' />
    // visibility: hidden WILL NOT WORK because 
    // it prevents clicks and focus from registering correctly
  </div>

  <sp-picker id='picker'>
    <sp-menu slot='options'>
      // ... sp-menu-items
    </sp-menu>
  </sp-picker>
</div>
const selectOverlay = document.querySelector('select-overlay')
const selectText = document.querySelector('select-text')
const selectPicker = document.querySelector('picker')

selectOverlay.addEventListener('click', (e) => {
  selectText.click()
  // pass click to the invisible sp-textfield element
})

selectText.addEventListener('click', (e) => {
  // after sp-textfield receives click, we can pass focus+click onto sp-picker
  // this results in sp-picker being rendered correctly
  selectPicker.focus()
  selectPicker.click()
})

It’s a bit round-a-bout, but it gets the sp-picker to work consistently.