Skip to content

Commit

Permalink
Merge pull request #7 from DATL4G/main
Browse files Browse the repository at this point in the history
Use CEF Browser on Desktop
  • Loading branch information
KevinnZou authored Sep 24, 2023
2 parents 0418730 + a02976f commit 8552706
Show file tree
Hide file tree
Showing 15 changed files with 730 additions and 171 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ iosApp/iosApp.xcodeproj/*
webview/webview.podspec
/convention-plugins/
keySec.gpg
desktopApp/jcef-bundle
39 changes: 16 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Thus I created a fork of it and used it as the base for this library. If you jus

The iOS implementation of this library relies on [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview).

The Desktop implementation of this library relies on [JavaFX WebView](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebView.html).
The Desktop implementation of this library relies on [JavaFX WebView](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/web/WebView.html) for version <= 1.2.0.


## Basic Usage
Expand All @@ -35,61 +35,53 @@ This will display a WebView in your Compose layout that shows the URL provided.
## WebView State
This library provides a *WebViewState* class as a state holder to hold the state for the WebView.
```kotlin
public class WebViewState(webContent: WebContent) {
public var lastLoadedUrl: String? by mutableStateOf(null)
class WebViewState(webContent: WebContent) {
var lastLoadedUrl: String? by mutableStateOf(null)
internal set

/**
* The content being loaded by the WebView
*/
public var content: WebContent by mutableStateOf(webContent)
var content: WebContent by mutableStateOf(webContent)

/**
* Whether the WebView is currently [LoadingState.Loading] data in its main frame (along with
* progress) or the data loading has [LoadingState.Finished]. See [LoadingState]
*/
public var loadingState: LoadingState by mutableStateOf(LoadingState.Initializing)
var loadingState: LoadingState by mutableStateOf(LoadingState.Initializing)
internal set

/**
* Whether the webview is currently loading data in its main frame
*/
public val isLoading: Boolean
get() = loadingState !is Finished
val isLoading: Boolean
get() = loadingState !is LoadingState.Finished

/**
* The title received from the loaded content of the current page
*/
public var pageTitle: String? by mutableStateOf(null)
internal set

/**
* the favicon received from the loaded content of the current page
*/
public var pageIcon: Bitmap? by mutableStateOf(null)
var pageTitle: String? by mutableStateOf(null)
internal set

/**
* A list for errors captured in the last load. Reset when a new page is loaded.
* Errors could be from any resource (iframe, image, etc.), not just for the main page.
* For more fine grained control use the OnError callback of the WebView.
*/
public val errorsForCurrentRequest: SnapshotStateList<WebViewError> = mutableStateListOf()
val errorsForCurrentRequest: SnapshotStateList<WebViewError> = mutableStateListOf()

/**
* The saved view state from when the view was destroyed last. To restore state,
* use the navigator and only call loadUrl if the bundle is null.
* See WebViewSaveStateSample.
* Custom Settings for WebView.
*/
public var viewState: Bundle? = null
internal set
val webSettings: WebSettings by mutableStateOf(WebSettings())

// We need access to this in the state saver. An internal DisposableEffect or AndroidView
// onDestroy is called after the state saver and so can't be used.
internal var webView by mutableStateOf<WebView?>(null)
internal var webView by mutableStateOf<IWebView?>(null)
}
```
It can be created using the *rememberWebViewState* function, which can be remembered across Compositions.

```kotlin
val state = rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")

Expand All @@ -101,7 +93,7 @@ val state = rememberWebViewState("https://github.com/KevinnZou/compose-webview-m
* Note that these headers are used for all subsequent requests of the WebView.
*/
@Composable
public fun rememberWebViewState(
fun rememberWebViewState(
url: String,
additionalHttpHeaders: Map<String, String> = emptyMap()
): WebViewState =
Expand Down Expand Up @@ -200,11 +192,12 @@ class WebViewNavigator(private val coroutineScope: CoroutineScope) {
}
```
It can be created using the *rememberWebViewNavigator* function, which can be remembered across Compositions.

```kotlin
val navigator = rememberWebViewNavigator()

@Composable
public fun rememberWebViewNavigator(
fun rememberWebViewNavigator(
coroutineScope: CoroutineScope = rememberCoroutineScope()
): WebViewNavigator = remember(coroutineScope) { WebViewNavigator(coroutineScope) }
```
Expand Down
8 changes: 8 additions & 0 deletions desktopApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ compose.desktop {
}
}
}

afterEvaluate {
tasks.withType<JavaExec> {
jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
jvmArgs("--add-opens", "java.desktop/sun.lwawt=ALL-UNNAMED")
jvmArgs("--add-opens", "java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
}
}
26 changes: 25 additions & 1 deletion desktopApp/src/jvmMain/kotlin/main.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
import androidx.compose.material.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.multiplatform.webview.MainWebView
import com.multiplatform.webview.web.Cef
import java.io.File

fun main() = application {
Window(onCloseRequest = ::exitApplication) {
MainWebView()
var restartRequired by remember { mutableStateOf(false) }

LaunchedEffect(Unit) {
Cef.init(builder = {
installDir = File("jcef-bundle")
}, onError = {
it.printStackTrace()
}) {
restartRequired = true
}
}

if (restartRequired) {
Text(text = "Restart required.")
} else {
MainWebView()
}
}
}
21 changes: 2 additions & 19 deletions webview/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
@file:Suppress("UNUSED_VARIABLE", "OPT_IN_USAGE")

import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode

plugins {
kotlin("multiplatform")
id("com.android.library")
id("org.jetbrains.compose")
// id("convention.publication")
}

val os = org.gradle.internal.os.OperatingSystem.current()

val platform = when {
os.isWindows -> "win"
os.isMacOsX -> "mac"
else -> "linux"
}

val jdkVersion = "17"

kotlin {
// explicitApi = ExplicitApiMode.Strict

Expand Down Expand Up @@ -72,13 +60,8 @@ kotlin {
val desktopMain by getting {
dependencies {
implementation(compose.desktop.common)
implementation("org.openjfx:javafx-base:$jdkVersion:${platform}")
implementation("org.openjfx:javafx-graphics:$jdkVersion:${platform}")
implementation("org.openjfx:javafx-controls:$jdkVersion:${platform}")
implementation("org.openjfx:javafx-media:$jdkVersion:${platform}")
implementation("org.openjfx:javafx-web:$jdkVersion:${platform}")
implementation("org.openjfx:javafx-swing:$jdkVersion:${platform}")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.7.2")
implementation("me.friwi:jcefmaven:110.0.25.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.2")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ internal fun WebViewApp() {
@Composable
internal fun WebViewSample() {
MaterialTheme {
val webViewState = rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")
val webViewState =
rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")
webViewState.webSettings.apply {
isJavaScriptEnabled = true
androidWebSettings.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal fun BasicWebViewWithHTMLSample() {
<title>Compose WebView Multiplatform</title>
<style>
body {
background-color: #e0e8f0;
background-color: e0e8f0;
display: flex;
justify-content: center;
align-items: center;
Expand All @@ -38,7 +38,7 @@ internal fun BasicWebViewWithHTMLSample() {
}
h1, h2 {
text-align: center;
color: white;
color: ffffff;
}
</style>
</head>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ sealed class PlatformWebSettings {
var safeBrowsingEnabled: Boolean = true
) : PlatformWebSettings()

data object DesktopWebSettings : PlatformWebSettings()
data class DesktopWebSettings(
var offScreenRendering: Boolean = false,
var transparent: Boolean = false,
) : PlatformWebSettings()

data object IOSWebSettings : PlatformWebSettings()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class WebSettings {
/**
* Desktop platform specific settings
*/
val desktopWebSettings = PlatformWebSettings.DesktopWebSettings
val desktopWebSettings = PlatformWebSettings.DesktopWebSettings()

/**
* iOS platform specific settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@ interface IWebView {
*/
fun stopLoading()

/**
* Evaluates the given JavaScript in the context of the currently displayed page.
* and returns the result of the evaluation.
* Note: The callback will not be called from desktop platform because it is not supported by CEF currently.
*/
fun evaluateJavaScript(script: String, callback: ((String) -> Unit)? = null)
}
Loading

0 comments on commit 8552706

Please sign in to comment.