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
16.34k stars 1.18k forks source link

how to hide scrollbar for compose-desktop #2912

Closed wangzhigang1112 closed 1 year ago

wangzhigang1112 commented 1 year ago

how to hide scrollbar

hfeky commented 1 year ago

You can do something like this:

var isScrollbarShown by remember { mutableStateOf<Boolean>(false) }
if (isScrollbarShown) {
    VerticalScrollbar(...)
}

and control its visibility by changing the value of the mutable state.

wangzhigang1112 commented 1 year ago

I use javafx lib for compose, I need the ability of webview。

When the height of the webpage content exceeds the height of the window, the vertical scroll bar of the window is displayed, and I don't need it to be displayed.

my build.gradle.kts: `import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins { kotlin("multiplatform") id("org.openjfx.javafxplugin") version "0.0.13" id("org.jetbrains.compose") }

group = "com.epoint.app" version = "1.0-SNAPSHOT"

repositories { maven("https://jitpack.io")

google()
mavenCentral()

}

kotlin { jvm { jvmToolchain(11) withJava() } sourceSets { val jvmMain by getting { dependencies { implementation(compose.desktop.currentOs) //本地sqlite据库 implementation("org.xerial:sqlite-jdbc:3.34.0") //kotlin 网络请求库 implementation("io.ktor:ktor-client-core:2.2.3") implementation("io.ktor:ktor-client-cio:2.2.3") // implementation("io.ktor:ktor-client-apache:2.2.3") implementation("io.ktor:ktor-client-content-negotiation:2.2.3") implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.3") implementation("io.ktor:ktor-serialization-kotlinx-xml:2.2.3") implementation("io.ktor:ktor-serialization-kotlinx-cbor:2.2.3") implementation("io.ktor:ktor-serialization-kotlinx-protobuf:2.2.3") implementation("io.ktor:ktor-client-logging:2.2.3") //阿里json库 implementation("com.alibaba:fastjson:1.2.83") //日志库 implementation("ch.qos.logback:logback-classic:1.2.11")

            implementation(files("src/libs/kmm.jar"))
        }
    }
    val jvmTest by getting
}

}

compose.desktop { application { mainClass = "MainKt" nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = "desktop" packageVersion = "1.0.0" } } }

//https://www.likecs.com/ask-7701835.html javafx { version = "20" modules = listOf("javafx.controls", "javafx.swing", "javafx.web", "javafx.graphics") }`

my compose code `@Composable fun EpointWebView(composeWindow: ComposeWindow, url: String) { val jfxPanel = remember { JFXPanel() } var jsObject = remember<JSObject?> { null }

Box(modifier = Modifier.fillMaxSize().background(Color.White)) {
    lateinit var engine: WebEngine
    Button(onClick = {
        Platform.runLater {
            engine.history.go(-1)
        }

    }) {
        Text("上一页")
    }

    ComposeJFXPanel(
        composeWindow = composeWindow,
        jfxPanel = jfxPanel,
        onCreate = {
            Platform.runLater {
                val root = WebView()
                // 设置WebView自适应宽度和高度
                println("w=" + composeWindow.size.width.toDouble())
                println("h=" + composeWindow.size.height.toDouble())
                root.setPrefWidth(composeWindow.size.width.toDouble());
                root.setPrefHeight(composeWindow.size.height.toDouble());
                val userAgent =
                    "Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_6 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0"
                root.getEngine().setUserAgent(userAgent)
                engine = root.engine
                val scene = Scene(root)

                engine.loadWorker.stateProperty().addListener { _, _, newState ->
                    println("newState=" + newState)
                    if (newState === Worker.State.SUCCEEDED) {
                        jsObject = root.engine.executeScript("window") as JSObject
                        // execute other javascript / setup js callbacks fields etc..
                    }
                }
                engine.loadWorker.exceptionProperty().addListener { _, _, newError ->
                    println("page load error : $newError")
                }
                jfxPanel.scene = scene
                val file = File(url)
                val loadFileStr = file.toURI().toURL().toString()
                println("loadFileStr=" + loadFileStr)
                engine.load(loadFileStr) // can be a html document from resources ..
                engine.setOnError { error -> println("onError : $error") }
            }
        }, onDestroy = {
            Platform.runLater {
                jsObject?.let { jsObj ->
                    // clean up code for more complex implementations i.e. removing javascript callbacks etc..
                }
            }
        })

}

}

@Composable fun ComposeJFXPanel( composeWindow: ComposeWindow, jfxPanel: JFXPanel, onCreate: () -> Unit, onDestroy: () -> Unit = {} ) { val jPanel = remember { JPanel() } val density = LocalDensity.current.density Layout( content = {}, modifier = Modifier.onGloballyPositioned { childCoordinates -> val coordinates = childCoordinates.parentCoordinates!! val location = coordinates.localToWindow(Offset.Zero).round() val size = coordinates.size jPanel.setBounds( (location.x / density).toInt(), (location.y / density).toInt() + 44, (size.width / density).toInt(), (size.height / density).toInt() - 44 ) jPanel.validate() jPanel.repaint() }, measurePolicy = { , -> layout(0, 0) {} })

DisposableEffect(jPanel) {
    composeWindow.add(jPanel)
    jPanel.layout = BorderLayout(0, 0)
    jPanel.add(jfxPanel)
    onCreate()
    onDispose {
        onDestroy()
        composeWindow.remove(jPanel)
    }

}

}`

malliaridis commented 1 year ago

@wangzhigang1112 I looked into your issue. The scrollbar is coming from the JavaFX WebView and is not related to Compose. Compose does not add any scroll behavior for boxes without a modifier by default.

However, to solve your issue, you have multiple options here:

  1. Add custom CSS that hides the scrollbar with style attribute overflow-y: hidden;
  2. Directly access and override the styles in your state property listener

For option 1: Create a new resource file with the style override:

/* E.g. in src/jvmMain/resource/no-scrollbars.css */
body {
    overflow-x: hidden; /* Hide horizontal bar */
    overflow-y: hidden; /* Hide vertical bar */
    overflow: hidden; /* or hide both */
}

And then load the CSS file when initializing the WebView:

// In EpointWebView Composable

// ...
engine = root.engine
val scene = Scene(root)

root.engine.userStyleSheetLocation = this.javaClass.getResource("/no-scrollbars.css")?.toExternalForm()

// ...

For option 2:

// In EpointWebView Composable

// ...

engine.loadWorker.stateProperty().addListener { _, _, newState ->
  println("newState=" + newState)
  if (newState === Worker.State.SUCCEEDED) {
    jsObject = root.engine.executeScript("window") as JSObject

    engine.executeScript("document.body.style.overflow = 'hidden';") // <-- Override style here directly
    // execute other javascript / setup js callbacks fields etc..
  }
}
// ...

Solutions are inspired from https://stackoverflow.com/questions/11206942/how-to-hide-scrollbars-in-the-javafx-webview.

okushnikov commented 3 months ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.