Created
March 19, 2025 02:12
-
-
Save adrientetar/b039998a0752261d2ee7db9ade3e7c15 to your computer and use it in GitHub Desktop.
Custom title bar in Compose Multiplatform with JBR runtime. Updated for the new WindowDecorations API. known JBR 17 old version: b1207.30 new version: b1367.22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sun.misc.Unsafe | |
import java.awt.Frame | |
import java.lang.reflect.AccessibleObject | |
import java.lang.reflect.Method | |
/** | |
* Access custom window decoration functions in the JetBrains Runtime. | |
* | |
* Original code: <https://github.com/ButterCam/compose-jetbrains-theme/blob/main/expui/src/main/kotlin/io/kanro/compose/jetbrains/expui/util/CustomWindowDecorationAccessing.kt> | |
* Updated for new WindowDecorations JBR API: <https://android.googlesource.com/platform/external/jetbrains/JetBrainsRuntime/+/a8a5398a738fab3daa559874e779faad9c85e746%5E%21/#F24> | |
*/ | |
object WindowDecorations { | |
init { | |
UnsafeAccess.assignAccessibility( | |
UnsafeAccess.desktopModule, | |
listOf("java.awt") | |
) | |
} | |
private val windowDecorationsInstance: Any? = try { | |
val windowDecorations = Class.forName(windowDecorationsClassName) | |
val constructor = windowDecorations.declaredConstructors.first() | |
constructor.isAccessible = true | |
constructor.newInstance() | |
} catch (e: Exception) { | |
null | |
} | |
private val createCustomTitleBarMethod: Method? = | |
getMethod("createCustomTitleBar") | |
private val setHeightMethod: Method? = | |
getCustomTitleBarMethod("setHeight", Float::class.java) | |
private fun getMethod(name: String, vararg params: Class<*>): Method? { | |
return try { | |
val clazz = Class.forName(windowDecorationsClassName) | |
val method = clazz.getDeclaredMethod(name, *params) | |
method.isAccessible = true | |
method | |
} catch (e: Exception) { | |
null | |
} | |
} | |
private fun getCustomTitleBarMethod(name: String, vararg params: Class<*>): Method? { | |
return try { | |
val clazz = Class.forName(customTitleBarClassName) | |
val method = clazz.getDeclaredMethod(name, *params) | |
method.isAccessible = true | |
method | |
} catch (e: Exception) { | |
null | |
} | |
} | |
/** | |
* Disable the system title bar for the provided [frame] - only retain the window controls, | |
* centered around the provided [height]. | |
* | |
* @return `true` if the custom title bar was set successfully, `false` otherwise. | |
*/ | |
fun setCustomTitleBar(frame: Frame, height: Float): Boolean { | |
val windowDecorations = windowDecorationsInstance ?: return false | |
// Create custom title bar | |
val customTitleBar = run { | |
val method = createCustomTitleBarMethod ?: return false | |
method.invoke(windowDecorations) ?: return false | |
} | |
// Set the height | |
val setHeightMethod = setHeightMethod ?: return false | |
setHeightMethod.invoke(customTitleBar, height) | |
// Set custom title bar | |
val setCustomTitleBarMethod = getMethod( | |
"setCustomTitleBar", Frame::class.java, customTitleBar::class.java | |
) ?: return false | |
setCustomTitleBarMethod.invoke(windowDecorations, frame, customTitleBar) | |
return true | |
} | |
} | |
private const val windowDecorationsClassName = "java.awt.Window\$WindowDecorations" | |
private const val customTitleBarClassName = "java.awt.Window\$CustomTitleBar" | |
private object UnsafeAccess { | |
private val unsafe: Any? by lazy { | |
try { | |
val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe") | |
theUnsafe.isAccessible = true | |
theUnsafe.get(null) as Unsafe | |
} catch (e: Throwable) { | |
null | |
} | |
} | |
val desktopModule by lazy { | |
ModuleLayer.boot().findModule("java.desktop").get() | |
} | |
val ownerModule by lazy { | |
this.javaClass.module | |
} | |
private val isAccessibleFieldOffset: Long? by lazy { | |
try { | |
(unsafe as? Unsafe)?.objectFieldOffset(Parent::class.java.getDeclaredField("first")) | |
} catch (e: Throwable) { | |
null | |
} | |
} | |
private val implAddOpens by lazy { | |
try { | |
Module::class.java.getDeclaredMethod( | |
"implAddOpens", String::class.java, Module::class.java | |
).accessible() | |
} catch (e: Throwable) { | |
null | |
} | |
} | |
fun assignAccessibility(obj: AccessibleObject) { | |
try { | |
val theUnsafe = unsafe as? Unsafe ?: return | |
val offset = isAccessibleFieldOffset ?: return | |
theUnsafe.putBooleanVolatile(obj, offset, true) | |
} catch (e: Throwable) { | |
// ignore | |
} | |
} | |
fun assignAccessibility(module: Module, packages: List<String>) { | |
try { | |
packages.forEach { | |
implAddOpens?.invoke(module, it, ownerModule) | |
} | |
} catch (e: Throwable) { | |
// ignore | |
} | |
} | |
private class Parent { | |
var first = false | |
@Volatile | |
var second: Any? = null | |
} | |
} | |
private fun <T : AccessibleObject> T.accessible(): T { | |
return apply { | |
UnsafeAccess.assignAccessibility(this) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment