mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
feat(desktop): add native Windows window controller
This commit is contained in:
@@ -25,6 +25,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
implementation(libs.filekit.dialogs)
|
implementation(libs.filekit.dialogs)
|
||||||
|
implementation(libs.jna.platform)
|
||||||
implementation(libs.kotlinx.coroutinesSwing)
|
implementation(libs.kotlinx.coroutinesSwing)
|
||||||
|
|
||||||
implementation(libs.compose.uiToolingPreview)
|
implementation(libs.compose.uiToolingPreview)
|
||||||
|
|||||||
1240
desktopApp/src/main/kotlin/com/vnidrop/app/WindowsWindowProcedure.kt
Normal file
1240
desktopApp/src/main/kotlin/com/vnidrop/app/WindowsWindowProcedure.kt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,12 @@
|
|||||||
package com.vnidrop.app
|
package com.vnidrop.app
|
||||||
|
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
import androidx.compose.ui.window.WindowPlacement
|
import androidx.compose.ui.window.WindowPlacement
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
import org.jetbrains.skiko.GraphicsApi
|
||||||
|
|
||||||
class DesktopWindowChromeTest {
|
class DesktopWindowChromeTest {
|
||||||
@Test
|
@Test
|
||||||
@@ -34,4 +38,114 @@ class DesktopWindowChromeTest {
|
|||||||
linuxWindowControlVisualState(isClose = false, isHovered = false, isPressed = false),
|
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),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ androidx-testExt = "1.3.0"
|
|||||||
composeMultiplatform = "1.11.1"
|
composeMultiplatform = "1.11.1"
|
||||||
filekit = "0.14.2"
|
filekit = "0.14.2"
|
||||||
gobley = "0.3.7"
|
gobley = "0.3.7"
|
||||||
|
jna = "5.19.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kotlin = "2.4.0"
|
kotlin = "2.4.0"
|
||||||
kotlinx-coroutines = "1.11.0"
|
kotlinx-coroutines = "1.11.0"
|
||||||
@@ -42,6 +43,7 @@ compose-uiTest = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "c
|
|||||||
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
|
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
|
||||||
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
|
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
|
||||||
filekit-dialogs = { module = "io.github.vinceglb:filekit-dialogs", version.ref = "filekit" }
|
filekit-dialogs = { module = "io.github.vinceglb:filekit-dialogs", version.ref = "filekit" }
|
||||||
|
jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna" }
|
||||||
kotlinx-coroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
kotlinx-coroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
||||||
kotlinx-coroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
|
kotlinx-coroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
|
||||||
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
|
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
|
||||||
|
|||||||
Reference in New Issue
Block a user