JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.33k stars 1.11k forks source link

Support for native Windows 11 features? #1660

Open sjmueller opened 2 years ago

sjmueller commented 2 years ago

We've built our Android app with Jetpack Compose, and are considering Multiplatform for extending to Windows. With the release of Windows 11, there's now the opportunity to create more modern apps:

https://blogs.windows.com/windowsdeveloper/2021/10/04/developing-for-windows-11/

Specifically, this includes new materials like Acrylic and Mica that are more macOS-like. We're wondering: will Compose Multiplatform will be able to take advantage of these new features for Windows 11 app development, especially considering its use of skia for rendering?

sjmueller commented 2 years ago

Relevant discussion happening for electron+flutter https://github.com/electron/electron/issues/29937

ElisaMin commented 1 year ago

I think its not gonna support anymore lol

HLCaptain commented 1 year ago

I have found this neat lib, hope it fits your expectations: https://github.com/MayakaApps/ComposeWindowStyler

alexfacciorusso commented 9 months ago

Some libraries, like the one that was linked in the previous comment, have to misuse (e.g. access via introspection) some parts of the API that are not normally accessible.

Furthermore, the same library seems to not to work anymore on more recent versions of Compose Desktop, probably due to changes of the internal API.

Given frameworks like Flutter and Electron now indeed support transparency of the window background in order to allow users (either via third-party libraries or first-party framework features) to access the Windows API and enable Mica effects on Windows 11 or even Acrylic on previous ones, Compose Desktop not allowing it would be an unnecessary limitation.

If the Compose Desktop team does not intend to support this functionality on Windows, which is alas incredibly important for the visual identity in Windows 11 and mining a more native look on the system, I think at least we could try and make the needed APIs open or accessible in some way for those functionalities to be implemented finally by 3rd party libraries in safe ways.

As a conversation starter, following is a summary of what the ComposeWindowStyler library does and the workarounds to allow for alpha on Windows. All credits go to its owners and contributors.

It accesses the SkiaLayer which is in a private field in the ComposeWindowDelegate class, which is accessible via a protected field in ComposeWindow:

https://github.com/MayakaApps/ComposeWindowStyler/blob/4090e1ccf26457928a2d8d9080dd69799be05bb5/window-styler/src/jvmMain/kotlin/com/mayakapps/compose/windowstyler/TransparencyUtils.kt

The library also "hacks" the JFrame pane in the window to allow for transparency:

https://github.com/MayakaApps/ComposeWindowStyler/blob/4090e1ccf26457928a2d8d9080dd69799be05bb5/window-styler/src/jvmMain/kotlin/com/mayakapps/compose/windowstyler/HackedContentPane.kt

As a further context explanation, the library then uses JNA to access and call native functions in the libraries in Windows, which is fine as their headers are published from Microsoft itself.

As clever, those introspected accesses and wrapped JPane classes are still hacks which simply could (and should) be avoided in production apps, and IMHO would be perfectly feasible with a minimum effort from the team.

I believe if somebody expert in Swing in the Compose Desktop team or even some contributor could find or suggest a way of making the Skia layer safely accessible, and/or to enable the transparency without going through complicated routes, then this would be solved.

Just to conclude, I still believe the team should indeed give this a weight, as the transparent windows are the lynchpin of Windows 11 look and feel, and I will repeat myself, would be an unnecessary limitation for developers to have if there's nothing physical blocking these functionalities to be used (and the fact that there are indeed libraries that make that in a way or another working, suggest it is not a system/framework limitation).

Sorry for the long comment, hope to help. I would be happy to contribute to this functionality if can help.

alexfacciorusso commented 9 months ago

Yesterday I have found a way to retrieve the SkiaLayer without using reflection, via scanning the components recursively from the ComposeWindow:

private fun <T : JComponent> findComponent(
    container: Container,
    klass: Class<T>,
): T? {
    val componentSequence = container.components.asSequence()
    return componentSequence.filter { klass.isInstance(it) }.ifEmpty {
        componentSequence.filterIsInstance<Container>()
            .mapNotNull { findComponent(it, klass) }
    }.map { klass.cast(it) }.firstOrNull()
}

private inline fun <reified T : JComponent> Container.findComponent() = findComponent(this, T::class.java)
fun ComposeWindow.findSkiaLayer() = findComponent<SkiaLayer>()

If anybody from the compose team would check if this is the best way or there is any other more efficient way to do so, let me know. In the meantime, I might do a PR on the third party styler library so that it might start working again (I have just tested the code on my machine, it gets the SkiaLayer and it's able to perform the thing). It still obviously needs a hacked JPanel to render the transparent layer, but yeah it works.

This is still considered a hack, just not poking hidden fields. Everything I said in my previous post still stands, and it would be nice to get any kind of official support for at least part of this.

MSDarwish2000 commented 9 months ago

Thank you for your interest in ComposeWindowStyler library. For personal reasons, the library was abandoned for a while. Yet, I'm willing to improve it again when I get free time to do so after 6 weeks or so.

In addition to @alexfacciorusso's awesome method, the library still suffers on Windows 11 latest versions due to several issues. The first major issue is my own mistake due to an operator typo in setSystemBackdrop function. More issues need to be inspected carefully on different versions of Windows 10 and 11 as Microsoft seems to change how its backdrop APIs work.

For anyone looking for a fully-customizable layout with no hacks (or at least a recommended one if you'd like to say so), porting this beautiful project kalibetre/CustomDecoratedJFrame to Compose would be a great solution. I already had plans to do so in ComposeWindowStyler when I have free time. It would allow us to use undecorated and transparent window as allowed by Compose Multiplatform and then use a custom title bar that resembles the actual one. In addition to this, it would allow to fix the opaque box behind title issue on Windows 10, and allow for fully customizable titlebar. A method like this is described in Microsoft's Title bar customizations guide.

mahozad commented 9 months ago

Please also see the solution in this comment:

MSDarwish2000 commented 9 months ago

Please also see the solution in this comment:

I really appreciate your contribution. If no one builds a solution until I have free time, I'll study this valuable solution as well and try to implement the next version of the library in the best way possible.

alexfacciorusso commented 9 months ago

@MSDarwish2000 in reality, I was working on that already too, and I might already have something semi-working 🤣.

I might make a PR also about that feature in your library when I would have something more polished, if you don't mind?

Currently, my prototype is implemented in your library and the only thing that seems not being working yet is I can't press the system decoration buttons (close, maximise, minimise) and resizing, but it's a matter of telling the system something I'm not telling it already as the implementation is not finished yet.

For context, this video has an app made with Jetpack Compose and has Mica background (using MayakaApps/ComposeWindowStyler with my changes) and transparency, still keeping the system decoration (e.g. shadow, system buttons etc.), and using the Compose WindowDraggableArea for dragging.

https://github.com/JetBrains/compose-multiplatform/assets/4479527/c657e625-94ac-45c6-9491-515bc3dbe415

I'd prefer not polluting this thread about this as not directly related to the compose-multiplatform repository, I'll create another issue or PR in your repository where I'll document my status about that feature, but I feel might have some use to post here some of what is possible to do with some hours of tinkering, and it would be amazing seeing included in an official implementation in Compose Desktop.

MSDarwish2000 commented 9 months ago

@alexfacciorusso Amazing. Your sample looks nice.

Of course, I don't mind your contributions. Feel free to open an issue their to update us about your progress.

Daydreamer-riri commented 1 month ago

‌‌‌Is there any further progress on this side? I would like to know if there are plans to support mica natively in compose-multiplatform.