New Window and DialogWindow APIs#2938
Conversation
5f80465 to
44d9b6c
Compare
| internal fun Dimension.toDpSize() = DpSize(width.dp, height.dp) | ||
| internal fun Point.toDpOffset() = DpOffset(x.dp, y.dp) | ||
| internal fun Rectangle.toDpRect() = DpRect( |
There was a problem hiding this comment.
Any reason to change it? Currently, it's aligned with the similar API across other platforms
There was a problem hiding this comment.
To me, Object.asSomething has the connotation of casting, or wrapping an object, like in Arrays.asList().
Object.toSomething(), on the other hand, has the connotation of converting, and creating a completely independent new object. Like in Array.toList()
There was a problem hiding this comment.
Currently, it's aligned with the similar API across other platforms
If you really want to change this, please unify the naming across all platform-helpers (not only desktop) in a separate PR
There was a problem hiding this comment.
Ok, reverted the naming here.
| */ | ||
| @ExperimentalComposeUiApi | ||
| @Immutable | ||
| class DpInsets( |
There was a problem hiding this comment.
We must not introduce such API one more time as desktop only. Please reuse the existing one
There was a problem hiding this comment.
The one in ios is internal; this one is public (but experimental).
We could change ios to use this one, but I wouldn't say it's a "must". There are plenty of places where we have internal copies of code because we don't want to create a dependency.
Up to the ios team...
There was a problem hiding this comment.
It's public and in skikoMain
There was a problem hiding this comment.
@InternalComposeUiApi is about using only inside our library. If you need really user-faced API - it should be existing foundation/commonMain one
There was a problem hiding this comment.
PlatformInsets is not the same as DpInsets.
What I thought you meant was this:
There was a problem hiding this comment.
DpInsets is currently internal to iosMain as convenience so that we don’t introduce an API that isn’t used anywhere else unless it’s actually needed.
Personally, I’m fine with having DpInsets as a dp-based convenience type alongside PlatformInsets, which is px-based and also internal API. I don’t see this as duplication, as long as DpInsets remains internal. If moved to skikoMain, then we can remove the one in iosMain.
If this new API is being used for window/platform insets, then I think we should use the existing WindowInsets / PlatformInsets APIs.
If we decide to introduce a public dp-based insets API, then I think it should live in common, at the same level as something like DpRect. But I am not sure this is the case.
As for PlatformInsets it is a px-based API. Its purpose is to represent the platform backing to WindowInsets, which are also px-based. The developer can then choose the PlatformInsets behavior they need for their particular case, for example dynamic getters, packed values, introduce new ones, etc.
There was a problem hiding this comment.
I moved DpInsets to skikoMain. Not sure what else should be done here.
There was a problem hiding this comment.
WindowInsets is related to DpInsets mostly just by name, and WindowRulers is even less related.
These are types for a concrete purpose, not generic geometry types like, for example, DpRect.
So I see no reason to forcibly use WindowInsets instead of a simple holder of 4 named Dp values. Also, WindowInsets is in foundation, while this new API is in ui.
There was a problem hiding this comment.
If we have an issue with DpInsets, there is an option to remove it and keep only availableBounds public - insets can be derived from it.
I am also fine with keeping the current DpInsets public.
Another option is to rename them to DpScreenInsets
There was a problem hiding this comment.
I don't see anything wrong with DpInsets.
7abe766 to
d0f6910
Compare
48f8bc8 to
fb106d0
Compare
| /** | ||
| * The list of screens on which the window can be placed. | ||
| */ | ||
| val screens: List<Screen> = devices.map { Screen(it) } |
There was a problem hiding this comment.
Shouldn't it be state-backed? How to be notified about a new screen appearing?
There was a problem hiding this comment.
This is part of WindowScreenProviderScope, which is a very short-lived object. It's created for, and only exists, during the call to WindowScreenProvider.getScreen().
Also, there's no AWT API to monitor screens...
There was a problem hiding this comment.
Let's add an explicit note about the fact that it's not state backed and you won't be notified about the change
There was a problem hiding this comment.
There's no need to add a note here because the API shape is such that it's not possible to be notified of such changes even if it were technically possible. WindowScreenProviderScope is a temporary scope object with which WindowScreenProvider is invoked once.
92a59f1 to
f23f001
Compare
f23f001 to
146a6be
Compare
Igor Demin (igordmn)
left a comment
There was a problem hiding this comment.
Approve for the current public API.
I also looked at the implementation, but not deeply.
5502d13 to
7619a90
Compare
… and `SwingWindow`
…e and position separately
…owBoundsProvider`.
…ate to avoid accidentally using it. Add size and position properties to WindowState and DialogState.
7619a90 to
7b2bda6
Compare
| import kotlinx.coroutines.isActive | ||
| import kotlinx.coroutines.launch | ||
|
|
||
| // TODO(demin): fix mouse hover after opening a dialog. |
There was a problem hiding this comment.
Let's add a link to YT task
There was a problem hiding this comment.
This is not a new TODO, it's just copied from the (v1) SwingDialog; likewise for all the others in this PR.
Igor can create a YT ticket when he gets back, if necessary.
There was a problem hiding this comment.
ComponentUpdater.desktop.kt
There was a problem hiding this comment.
This is not a new file.
There was a problem hiding this comment.
ComposeAccessible.desktop.kt
There was a problem hiding this comment.
Not a new file either.
There was a problem hiding this comment.
Screen.desktop.kt
|
|
||
|
|
||
| /** | ||
| * Represents a user's screen. |
There was a problem hiding this comment.
user's? Maybe is it better to re-word that in terms of system/environment/etc?
There was a problem hiding this comment.
Changed to "Represents a screen (a graphical device on which windows can be rendered)."
|
|
||
| @Test | ||
| fun `set window min intrinsic width`() = runApplicationTest { | ||
| assumeTrue(!isLinux) // Flaky on our CI |
There was a problem hiding this comment.
YT issue?
There was a problem hiding this comment.
Not a new problem, and hardly deserves its own ticket, IMO.
|
|
||
| @Test | ||
| fun `showing a window should measure content specified size`() = runApplicationTest { | ||
| // TODO fix on Linux https://github.com/JetBrains/compose-multiplatform/issues/1297 |
There was a problem hiding this comment.
There was a problem hiding this comment.
Yes, but it's not a new issue; it's a copy paste from WindowTest.
There was a problem hiding this comment.
MeasurableRootContent.skiko.kt
There was a problem hiding this comment.
DpInsets.skiko.kt
There was a problem hiding this comment.
If it's just data structure (not window-insets or so), probably it should be part of ui-unit module
There was a problem hiding this comment.
Pull request overview
This WIP PR introduces a new experimental desktop windowing API under androidx.compose.ui.window.v2, adding screen-aware window state, bounds/size providers, intrinsic sizing, and updated save/restore behavior. It fits into the desktop Compose window stack by plumbing measurable content and geometry state from scene/container layers up through AWT wrappers and the public window/dialog APIs.
Changes:
- Adds new experimental
WindowState/DialogStatev2 APIs, geometry providers, screen abstractions, and publicWindow/DialogWindowcomposables. - Exposes measurable root content and new geometry/inset helpers so window sizing can depend on intrinsic content size before first show.
- Expands desktop test coverage for the v2 APIs and splits low-level
ComposeWindow/ComposeDialogfocus-clearing tests into dedicated files.
Reviewed changes
Copilot reviewed 49 out of 50 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/unit/Geometry.skiko.kt |
Adds shared dp geometry helpers used by new window sizing/state code. |
compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/unit/DpInsets.kt |
Introduces public experimental DpInsets and related operators. |
compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/scene/ComposeScene.skiko.kt |
Trivial formatting/no-op around unconstrained size helper. |
compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/layout/MeasurableRootContent.kt |
Promotes measurable root content interface to experimental public API. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowTypingLocationTest.kt |
Normalizes package for existing desktop typing-location tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowTypeTest.kt |
Normalizes package for existing desktop type tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowTestUtils.kt |
Normalizes package for shared legacy window test helpers. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowTest.kt |
Cleans imports and removes low-level ComposeWindow focus-clearing tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowStateTest.kt |
Normalizes package/imports for legacy window state tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowParameterTest.kt |
Normalizes package/imports for legacy parameter tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/WindowInputEventTest.kt |
Normalizes package/imports for legacy input-event tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/v2/WindowV2Test.kt |
Adds broad behavioral coverage for v2 window composition and lifecycle. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/v2/WindowV2StateTest.kt |
Adds extensive state/bounds/sizing tests for v2 windows. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/v2/WindowTestUtils.kt |
Adds v2-specific intrinsic sizing and content-size test helpers. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/v2/DialogWindowV2Test.kt |
Adds broad behavioral coverage for v2 dialogs. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/v2/DialogWindowV2StateTest.kt |
Adds extensive state/bounds/sizing tests for v2 dialogs. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/TestUtils.kt |
Adds helper for launching v2 window tests in existing test harness. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DialogWindowTest.kt |
Cleans imports and removes low-level ComposeDialog focus-clearing tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/ComposeWindowTest.kt |
New dedicated low-level ComposeWindow focus-clearing tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/ComposeDialogTest.kt |
New dedicated low-level ComposeDialog focus-clearing tests. |
compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/BaseWindowTextFieldTest.kt |
Normalizes package/imports for shared text-field window tests. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/WindowState.desktop.kt |
Minor import cleanup in legacy window state implementation. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/WindowPosition.desktop.kt |
Adds deprecation suppression to legacy aligned position factory. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/WindowPlacement.desktop.kt.kt |
Minor documentation wording fixes for legacy placement enum. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/WindowLocationTracker.desktop.kt |
Refactors cascade placement to be screen-aware and reusable by v2. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Window.desktop.kt |
Exposes single-window scope helper for reuse and updates TODO comment. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v2/WindowState.desktop.kt |
Adds new experimental v2 window state and save/restore API. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v2/WindowGeometryProviders.kt |
Adds v2 screen/bounds/position/size provider model and intrinsic sizing logic. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v2/Window.desktop.kt |
Adds new experimental v2 public window composable and single-window entry point. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v2/Screen.kt |
Adds screen abstraction used by v2 screen-selection APIs. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v2/DialogWindow.desktop.kt |
Adds new experimental v2 public dialog composable. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/v2/DialogState.desktop.kt |
Adds new experimental v2 dialog state and save/restore API. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/LayoutConfiguration.desktop.kt |
Renames desktop geometry conversion helpers. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Geometry.desktop.kt |
Adds desktop geometry/inset conversion helpers used by v2 code. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/util/Windows.desktop.kt |
Extracts screen-alignment location helper for reuse. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/util/ComponentUpdater.kt |
Small generic/doc cleanup in shared updater utility. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.desktop.kt |
Exposes measurable scene content and renames geometry helpers. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/scene/ComposeContainer.desktop.kt |
Exposes measurable content from desktop compose container. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/PlatformWindowContext.desktop.kt |
Renames desktop geometry helpers and related imports. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/AwtDragAndDropManager.desktop.kt |
Renames desktop point-to-dp helper usage. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/a11y/ComposeAccessible.kt |
Renames desktop point-to-dp helper usage in accessibility layer. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/v2/SwingWindow.desktop.kt |
Adds AWT-backed v2 window implementation and state synchronization. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/v2/SwingDialog.desktop.kt |
Adds AWT-backed v2 dialog implementation and state synchronization. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/Utils.desktop.kt |
Adds rectangle conversion helpers used by v2 bounds handling. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/SwingWindow.desktop.kt |
Ensures first render validates before immediate draw. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/SwingDialog.desktop.kt |
Ensures first render validates before immediate draw. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeWindowPanel.desktop.kt |
Exposes measurable content from ComposeWindowPanel. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeWindow.desktop.kt |
Exposes measurable content from public ComposeWindow. |
compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeDialog.desktop.kt |
Exposes measurable content from public ComposeDialog. |
compose/ui/ui/api/ui.klib.api |
Partially updates API dump for newly exposed/public types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| val env = GraphicsEnvironment.getLocalGraphicsEnvironment() | ||
| val devices = env.screenDevices | ||
| val actualDefaultDevice = defaultDevice | ||
| ?: devices.firstOrNull { it.iDstring === lastActiveConfig?.device?.iDstring } |
| fun requestPosition(positionProvider: WindowPositionProvider) { | ||
| boundsRequests.trySend( | ||
| WindowBoundsProvider( | ||
| positionProvider = positionProvider, | ||
| ) | ||
| ) |
| fun requestSize(sizeProvider: WindowSizeProvider) { | ||
| boundsRequests.trySend( | ||
| WindowBoundsProvider( | ||
| sizeProvider = sizeProvider, | ||
| ) | ||
| ) |
2522696 to
91a3569
Compare
WindowState API
This PR introduces a new WindowState API, and corresponding composable functions.
The main improvements relative to the current API:
DpSize.Unspecified).DpSize.Unspecifiedwith afillMaxSize()top-level modifier would cause the window to fill the screen. Requested in CMP-2923 Add the ability to "pack" a window while allowing its content to match the window sizeWindowStatesaving/restoring (see CMP-1535 Revisit WindowState/DialogState API).Quick-start
The new APIs reside in
androidx.compose.ui.window.v2.rememberWindowState(initialScreenProvider=...)orWindowState.requestScreento specify the screen on which the window may be placed. The actual screen (which may change over time) is observable viaWindowState.screenId.rememberWindowState(initialBoundsProvider=...)orWindowState.requestBoundsto change the bounds of the window. The actual bounds (which may be different, and which will change over time) are observable viaWindowState.bounds.DialogWindowhas a similar API, also inandroidx.compose.ui.window.v2.Fixes https://youtrack.jetbrains.com/issue/CMP-1535
Fixes https://youtrack.jetbrains.com/issue/CMP-9260
Fixes https://youtrack.jetbrains.com/issue/CMP-2923
Fixes https://youtrack.jetbrains.com/issue/CMP-1873
Testing
The new API is tested via all the tests of the previous one, plus some additional tests.
This should be tested by QA
Release Notes
Features - Desktop
WindowStateAPI.