Skip to content

Commit 287d07f

Browse files
[JEWEL-993] Add MacOS Scrollbar Detection to Standalone
1 parent dc813d3 commit 287d07f

File tree

30 files changed

+1110
-287
lines changed

30 files changed

+1110
-287
lines changed

platform/jewel/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import androidx.compose.ui.platform.debugInspectorInfo
1212
import androidx.compose.ui.unit.dp
1313
import com.jetbrains.JBR
1414
import org.jetbrains.jewel.foundation.theme.JewelTheme
15+
import org.jetbrains.jewel.intui.standalone.window.macos.MacPlatformServicesDefaultImpl
1516
import org.jetbrains.jewel.window.styling.TitleBarStyle
16-
import org.jetbrains.jewel.window.utils.macos.MacUtil
1717

1818
public fun Modifier.newFullscreenControls(newControls: Boolean = true): Modifier =
1919
this then
@@ -70,7 +70,7 @@ internal fun DecoratedWindowScope.TitleBarOnMacOs(
7070
"apple.awt.newFullScreenControls.background",
7171
"${style.colors.fullscreenControlButtonsBackground.toArgb()}",
7272
)
73-
MacUtil.updateColors(window)
73+
MacPlatformServicesDefaultImpl.updateColors(window)
7474
} else {
7575
System.clearProperty("apple.awt.newFullScreenControls")
7676
System.clearProperty("apple.awt.newFullScreenControls.background")
@@ -84,7 +84,7 @@ internal fun DecoratedWindowScope.TitleBarOnMacOs(
8484
style = style,
8585
applyTitleBar = { height, state ->
8686
if (state.isFullscreen) {
87-
MacUtil.updateFullScreenButtons(window)
87+
MacPlatformServicesDefaultImpl.updateFullScreenButtons(window)
8888
}
8989
titleBar.height = height.value
9090
JBR.getWindowDecorations().setCustomTitleBar(window, titleBar)

platform/jewel/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ import kotlin.math.max
4747
import org.jetbrains.jewel.foundation.theme.JewelTheme
4848
import org.jetbrains.jewel.foundation.theme.LocalContentColor
4949
import org.jetbrains.jewel.foundation.theme.OverrideDarkMode
50+
import org.jetbrains.jewel.intui.standalone.window.macos.MacPlatformServicesDefaultImpl
5051
import org.jetbrains.jewel.ui.component.styling.LocalDefaultDropdownStyle
5152
import org.jetbrains.jewel.ui.component.styling.LocalIconButtonStyle
5253
import org.jetbrains.jewel.ui.util.isDark
5354
import org.jetbrains.jewel.window.styling.TitleBarStyle
5455
import org.jetbrains.jewel.window.utils.DesktopPlatform
55-
import org.jetbrains.jewel.window.utils.macos.MacUtil
5656

5757
internal const val TITLE_BAR_COMPONENT_LAYOUT_ID_PREFIX = "__TITLE_BAR_"
5858

@@ -186,7 +186,7 @@ internal class TitleBarMeasurePolicy(
186186

187187
return layout(boxWidth, boxHeight) {
188188
if (state.isFullscreen) {
189-
MacUtil.updateFullScreenButtons(window)
189+
MacPlatformServicesDefaultImpl.updateFullScreenButtons(window)
190190
}
191191
val placeableGroups =
192192
measuredPlaceable.groupBy { (measurable, _) ->

platform/jewel/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt

Lines changed: 0 additions & 90 deletions
This file was deleted.

platform/jewel/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt

Lines changed: 0 additions & 95 deletions
This file was deleted.

platform/jewel/detekt.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ coroutines:
5858

5959
exceptions:
6060
active: true
61+
excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
6162
NotImplementedDeclaration:
6263
active: true
6364
ObjectExtendsThrowable:

platform/jewel/gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ ktor-client-java = { module = "io.ktor:ktor-client-java", version = "3.0.3" }
4949
metalava = { module = "com.android.tools.metalava:metalava", version.ref = "metalava" }
5050
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
5151
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
52+
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" }
53+
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test" }
5254

5355
# Plugin libraries for build-logic's convention plugins to use to resolve the types/tasks coming from these plugins
5456
detekt-gradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }

platform/jewel/int-ui/int-ui-standalone-tests/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ jvm_library(
4242
"@lib//:kotlin-test",
4343
"@lib//:junit5",
4444
"//platform/jewel/int-ui/int-ui-standalone:jewel-intUi-standalone",
45+
"//libraries/kotlinx/coroutines/core",
46+
"//libraries/kotlinx/coroutines/test",
47+
"//platform/jewel/ui",
48+
"//platform/jewel/ui:ui_test_lib",
4549
],
4650
runtime_deps = [":jewel-intUi-standalone-tests"]
4751
)

platform/jewel/int-ui/int-ui-standalone-tests/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ dependencies {
44
api(projects.intUi.intUiStandalone)
55
testImplementation(kotlin("test"))
66
testImplementation(libs.junit.jupiter)
7+
testImplementation(libs.coroutines.core)
8+
testImplementation(libs.coroutines.test)
79
testRuntimeOnly(libs.junit.platform.engine)
810
testRuntimeOnly(libs.junit.platform.launcher)
911
}

platform/jewel/int-ui/int-ui-standalone-tests/intellij.platform.jewel.intUi.standalone.tests.iml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,8 @@
2828
<orderEntry type="library" scope="TEST" name="kotlin-test" level="project" />
2929
<orderEntry type="library" scope="TEST" name="JUnit5" level="project" />
3030
<orderEntry type="module" module-name="intellij.platform.jewel.intUi.standalone" />
31+
<orderEntry type="module" module-name="intellij.libraries.kotlinx.coroutines.core" scope="TEST" />
32+
<orderEntry type="module" module-name="intellij.libraries.kotlinx.coroutines.test" scope="TEST" />
33+
<orderEntry type="module" module-name="intellij.platform.jewel.ui" scope="TEST" />
3134
</component>
3235
</module>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
package org.jetbrains.jewel.intui.standalone
3+
4+
import kotlin.test.assertEquals
5+
import org.jetbrains.jewel.intui.standalone.fakes.FakeMacPlatformServices
6+
import org.junit.jupiter.api.BeforeEach
7+
import org.junit.jupiter.api.Test
8+
import org.junit.jupiter.api.assertDoesNotThrow
9+
import org.junit.jupiter.api.condition.DisabledOnOs
10+
import org.junit.jupiter.api.condition.EnabledOnOs
11+
import org.junit.jupiter.api.condition.OS
12+
13+
internal class StandalonePlatformCursorControllerTest {
14+
private lateinit var fakePlatformServices: FakeMacPlatformServices
15+
private lateinit var controller: StandalonePlatformCursorController
16+
17+
@BeforeEach
18+
fun setup() {
19+
fakePlatformServices = FakeMacPlatformServices()
20+
controller = StandalonePlatformCursorController(fakePlatformServices)
21+
}
22+
23+
@Test
24+
@EnabledOnOs(OS.MAC)
25+
fun `verify hideCursorUntilMoved was called on macOS`() {
26+
controller.hideCursor()
27+
28+
assertEquals(1, fakePlatformServices.hideCursorCalls, "Should have called native hide exactly once")
29+
}
30+
31+
@Test
32+
@DisabledOnOs(OS.MAC)
33+
fun `verify hideCursorUntilMoved was not called on non-macOS`() {
34+
controller.hideCursor()
35+
36+
assertEquals(0, fakePlatformServices.hideCursorCalls, "Should not call native hide on non-macOS")
37+
}
38+
39+
@Test
40+
fun `hideCursor should not throw even if native service fails`() {
41+
val explodingFake =
42+
object : FakeMacPlatformServices() {
43+
override fun hideCursorUntilMoved() {
44+
@Suppress("") throw RuntimeException("Native library missing!")
45+
}
46+
}
47+
48+
val controller = StandalonePlatformCursorController(explodingFake)
49+
50+
assertDoesNotThrow { controller.hideCursor() }
51+
}
52+
}

0 commit comments

Comments
 (0)