Reputation: 234
I am trying to use the androidx.compose.ui.window.MenuBar
from Jetbrain Tutorial but the problem is that when I try to use it in my project it does not show up.
Here is the problem:
It only offers me to import from those 2 locations but when I use the import it does not complain or resolve the error.
build.gradle.kts
import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.6.10"
id("org.jetbrains.compose") version "1.1.0"
}
group = "me.whate"
version = "1.0"
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
dependencies {
implementation(compose.desktop.currentOs)
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "11"
}
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "Test"
packageVersion = "1.0.0"
}
}
}
I have tried to invalidate caches, etc but no success so far.
Anybody has any suggestion?
Upvotes: 5
Views: 1087
Reputation: 5288
The MenuBar()
Composable is provided by FrameWindowScope
, and if this
is not a FrameWindowScope
, you won't be able to access the function, regardless of whether it has been imported. In the tutorial, MenuBar()
is called within a Window
content block, something like this:
Window(onCloseRequest = ::exitApplication) {
MenuBar { ... }
}
In this case, at the point where MenuBar()
is called, this
is an instance of FrameWindowScope
. You can see this if you look at the last lines of the declaration of the Window()
function, where the content parameter is defined:
@Composable
fun Window(
onCloseRequest: () -> Unit,
state: WindowState = rememberWindowState(),
visible: Boolean = true,
title: String = "Untitled",
icon: Painter? = null,
undecorated: Boolean = false,
transparent: Boolean = false,
resizable: Boolean = true,
enabled: Boolean = true,
focusable: Boolean = true,
alwaysOnTop: Boolean = false,
onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
onKeyEvent: (KeyEvent) -> Boolean = { false },
content: @Composable FrameWindowScope.() -> Unit
) {
The easy answer to your question is to keep your call to MenuBar()
inside the curly braces of your Window
content block. This is a natural approach, as it keeps the code that's dependent on FrameWindowScope
where a developer would expect to find it.
However, if you really want to call MenuBar()
from within your App()
Composable, you could actually pass the instance of FrameWindowScope
into the function, and then call MenuBar()
on that instance, like this:
@OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App(this, this@application)
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun App(windowScope: FrameWindowScope, applicationScope: ApplicationScope) {
windowScope.MenuBar {
Menu("File", mnemonic = 'F') {
val nextWindowState = rememberWindowState()
Item(
"Exit",
onClick = { applicationScope.exitApplication() },
shortcut = KeyShortcut(
Key.X, ctrl = false
)
)
}
}
...
}
While I don't think it's considered a best practice to pass scope like this, as it tends to make your Composables harder to reuse and to preview, I hope it helps illustrate some of the "magic" of how these scoped Composable function calls are made available in some places and not others. The key is understanding that within a Composable's content block, this
is a very specific type of scope.
Upvotes: 2
Reputation: 463
Prefix MenuBar with FrameWindowScope, e.g. FrameWindowScope.MenuBar
Upvotes: 0
Reputation: 3260
You have to modify it like this:
@OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
var text by remember { mutableStateOf("Hello, World!") }
DesktopMaterialTheme {
MenuBar {
// your menubar code here
}
Button(onClick = {
text = "Hello, Desktop!"
}) {
Text(text)
}
}
}
}
and completely remove the App() function
Upvotes: 3