feat(desktop): add native Windows window controller

This commit is contained in:
2026-07-22 15:49:11 +02:00
parent 0ab076e5b2
commit 0e97c014a1
4 changed files with 1357 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,12 @@
package com.vnidrop.app
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.window.WindowPlacement
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.jetbrains.skiko.GraphicsApi
class DesktopWindowChromeTest {
@Test
@@ -34,4 +38,114 @@ class DesktopWindowChromeTest {
linuxWindowControlVisualState(isClose = false, isHovered = false, isPressed = false),
)
}
@Test
fun windowsHitTestingPreservesNativeResizeAndCaptionBehavior() {
val geometry = windowsGeometry()
assertEquals(WindowsHitTestResult.TopLeft, windowsHitTest(2, 2, geometry))
assertEquals(WindowsHitTestResult.Right, windowsHitTest(1_198, 300, geometry))
assertEquals(WindowsHitTestResult.Bottom, windowsHitTest(600, 798, geometry))
assertEquals(WindowsHitTestResult.Caption, windowsHitTest(600, 24, geometry))
assertEquals(WindowsHitTestResult.Client, windowsHitTest(600, 200, geometry))
}
@Test
fun windowsCaptionButtonsReturnNativeHitCodesForSnapLayouts() {
val geometry = windowsGeometry()
assertEquals(WindowsHitTestResult.MinimizeButton, windowsHitTest(1_075, 20, geometry))
assertEquals(WindowsHitTestResult.MaximizeButton, windowsHitTest(1_120, 20, geometry))
assertEquals(WindowsHitTestResult.CloseButton, windowsHitTest(1_175, 20, geometry))
}
@Test
fun nativeCaptionStripIsSplitIntoThreeAdjacentButtons() {
val bounds = splitWindowsCaptionButtonBounds(Rect(646f, 0f, 793f, 30f))
assertEquals(Rect(646f, 0f, 695f, 30f), bounds.minimize)
assertEquals(Rect(695f, 0f, 744f, 30f), bounds.maximize)
assertEquals(Rect(744f, 0f, 793f, 30f), bounds.close)
}
@Test
fun extendedDwmTitleBarMarginScalesWithWindowDpi() {
assertEquals(48, windowsTitleBarHeightPixels(96))
assertEquals(60, windowsTitleBarHeightPixels(120))
assertEquals(72, windowsTitleBarHeightPixels(144))
}
@Test
fun maximizedWindowsDoNotExposeResizeBorders() {
val geometry = windowsGeometry().copy(isMaximized = true)
assertEquals(WindowsHitTestResult.Caption, windowsHitTest(2, 2, geometry))
}
@Test
fun restoredWindowsPaintThroughTheResizeBorderWhileMaximizedWindowsStayInset() {
assertEquals(
WindowsFrameInsets(),
windowsContentInsets(
isMaximized = false,
horizontalResizeBorder = 8,
verticalResizeBorder = 8,
),
)
assertEquals(
WindowsFrameInsets(left = 8, top = 9, right = 8, bottom = 9),
windowsContentInsets(
isMaximized = true,
horizontalResizeBorder = 8,
verticalResizeBorder = 9,
),
)
}
@Test
fun nativeMouseCoordinatesPreserveNegativeMonitorPositions() {
val x = -320
val y = -48
val packed = ((y and 0xffff).toLong() shl 16) or (x and 0xffff).toLong()
assertEquals(WindowsScreenPoint(x, y), windowsScreenPoint(packed))
}
@Test
fun nativeBackdropRequiresAGpuRendererWithTransparentSwapChainSupport() {
assertTrue(GraphicsApi.DIRECT3D.supportsWindowsTransparentBackground())
assertTrue(GraphicsApi.OPENGL.supportsWindowsTransparentBackground())
assertFalse(GraphicsApi.UNKNOWN.supportsWindowsTransparentBackground())
assertFalse(GraphicsApi.SOFTWARE_FAST.supportsWindowsTransparentBackground())
assertFalse(GraphicsApi.SOFTWARE_COMPAT.supportsWindowsTransparentBackground())
}
@Test
fun captionActionRequiresReleaseOnTheOriginallyPressedButton() {
assertTrue(
shouldActivateWindowsCaptionButton(
WindowsCaptionButton.Maximize,
WindowsCaptionButton.Maximize,
),
)
assertFalse(
shouldActivateWindowsCaptionButton(
WindowsCaptionButton.Close,
WindowsCaptionButton.Maximize,
),
)
assertFalse(shouldActivateWindowsCaptionButton(WindowsCaptionButton.Close, null))
}
private fun windowsGeometry() = WindowsHitTestGeometry(
width = 1_200,
height = 800,
horizontalResizeBorder = 8,
verticalResizeBorder = 8,
isMaximized = false,
caption = Rect(0f, 0f, 1_200f, 48f),
minimizeButton = Rect(1_062f, 8f, 1_108f, 40f),
maximizeButton = Rect(1_108f, 8f, 1_154f, 40f),
closeButton = Rect(1_154f, 8f, 1_200f, 40f),
)
}