Some fun with `DialogWindow` :slightly_smiling_fac...
# compose-desktop
r
Some fun with
DialogWindow
🙂 I'd love to see more desktop-native capabilities in Compose Desktop, as well as non-material styled components -- might start to give some other desktop UI toolkits a run for their money if this stuff became easy to do
9
❤️ 8
m
Nice one! Can you share the code of this?
☝️ 4
a
what are we seeing in the video exactly? did u trigger the native context menu somehow?
r
This is all compose :)
Re: code, the logic is based around: 1. A custom PopupPositionProvider that takes a "preferred" anchor and alignment, and adjusts based on screen real-estate. Essentially, a Compose version of
[NSPopover showRelativeToRect:ofView:preferredEdge]
2. A custom Popup implementation that puts its content inside a borderless DialogWindow, and aligns the window based on the PopupPositionProvider 3. A lot of finessing focus, ensuring the dialog is focused when open and closes if it loses focus
a
This is gorgeous
z
@rob42 Hi. I'm trying to implement this but also making it popup using the cursor position as the origin. Would you be able to share more of the code for the Popup implementation?
r
@zt I would use the cursor position as the anchor, e.g.
Copy code
calculatePosition(
   anchorBounds = IntRect(offset = CURSOR_POS, size = IntSize(1,1)),
   windowSize = USABLE_SCREEN_SIZE,
   layoutDirection = LocalLayoutDirection.current,
   popupContentSize: CONTENT_SIZE,
)
In terms of wiring it all up, something like this:
Copy code
val state = rememberDialogState(size = DpSize.Unspecified)
    val density = LocalDensity.current
    val layoutDirection = LocalLayoutDirection.current

    // Currently this is "one shot", but could be adapted to use a listener on the Window if it was
    // necessary to get an updating value.
    val usableScreenBounds = remember(parentWindow) { DisplayUtil.getUsableScreenBounds(parentWindow) }

    remember(state.size, usableScreenBounds, anchorBoundsInScreen, popupPositionProvider) {
        // Wait for measurement before placing and making visible
        if (state.size == DpSize.Unspecified) return@remember

        state.position = with(density) {
            val origin = calculateBoundsRelativeToUsableScreen(
                usableScreenBounds = usableScreenBounds,
                anchorBoundsInScreen = anchorBoundsInScreen,
                contentSize = state.size.roundToPx(density),
                popupPositionProvider = popupPositionProvider,
                layoutDirection = layoutDirection
            ).translate(usableScreenBounds.topLeft).topLeft

            WindowPosition(
                x = origin.x.toDp(),
                y = origin.y.toDp(),
            )
        }
    }

    DialogWindow(state = state, visible = state.size.isSpecified, ... )
Copy code
private fun calculateBoundsRelativeToUsableScreen(
    usableScreenBounds: IntRect,
    anchorBoundsInScreen: IntRect,
    contentSize: IntSize,
    popupPositionProvider: PopupPositionProvider,
    layoutDirection: LayoutDirection,
) : IntRect {
    // Because PopoverPositionProvider takes a *size* for windowSize (really screen size, in our case),
    // coordinates need to be translated to/from a zero-origin rect before and after, to account for any top/left
    // insets in the usable screen bounds.
    val anchorBoundsRelativeToUsableScreen = anchorBoundsInScreen.translate(-usableScreenBounds.topLeft)

    val boundsRelativeToUsableScreen = popupPositionProvider.calculatePosition(
        anchorBounds = anchorBoundsRelativeToUsableScreen,
        windowSize = usableScreenBounds.size,
        layoutDirection = layoutDirection,
        popupContentSize = contentSize
    ).let { IntRect(it, contentSize) }

    return boundsRelativeToUsableScreen
}