matrix
matrix

Reputation: 234

androidx.compose.ui.window.MenuBar cannot be found

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:

Screenshot

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

Answers (3)

Mark McClelland
Mark McClelland

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

Greg Harley
Greg Harley

Reputation: 463

Prefix MenuBar with FrameWindowScope, e.g. FrameWindowScope.MenuBar

Upvotes: 0

Kostas Drak
Kostas Drak

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

Related Questions