All Downloads are FREE. Search and download functionalities are using the official Maven repository.

desktopMain.com.multiplatform.webview.web.WebView.desktop.kt Maven / Gradle / Ivy

package com.multiplatform.webview.web

import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import com.multiplatform.webview.jsbridge.WebViewJsBridge
import compose_webview_multiplatform.webview.generated.resources.Res
import dev.datlag.kcef.KCEF
import dev.datlag.kcef.KCEFBrowser
import dev.datlag.kcef.KCEFClient
import org.cef.browser.CefRendering
import org.cef.browser.CefRequestContext
import org.jetbrains.compose.resources.ExperimentalResourceApi

/**
 * Desktop WebView implementation.
 */
@Composable
actual fun ActualWebView(
    state: WebViewState,
    modifier: Modifier,
    captureBackPresses: Boolean,
    navigator: WebViewNavigator,
    webViewJsBridge: WebViewJsBridge?,
    onCreated: (NativeWebView) -> Unit,
    onDispose: (NativeWebView) -> Unit,
    factory: (WebViewFactoryParam) -> NativeWebView,
) {
    DesktopWebView(
        state,
        modifier,
        navigator,
        webViewJsBridge,
        onCreated = onCreated,
        onDispose = onDispose,
        factory = factory,
    )
}

/** Desktop WebView factory parameters: web view state, client, and possible file content. */
actual class WebViewFactoryParam(
    val state: WebViewState,
    val client: KCEFClient,
    val fileContent: String,
) {
    inline val webSettings get() = state.webSettings
    inline val rendering: CefRendering get() =
        if (webSettings.desktopWebSettings.offScreenRendering) {
            CefRendering.OFFSCREEN
        } else {
            CefRendering.DEFAULT
        }
    inline val transparent: Boolean get() = webSettings.desktopWebSettings.transparent
    val requestContext: CefRequestContext get() = createModifiedRequestContext(webSettings)
}

/** Default WebView factory for Desktop. */
actual fun defaultWebViewFactory(param: WebViewFactoryParam): NativeWebView =
    when (val content = param.state.content) {
        is WebContent.Url ->
            param.client.createBrowser(
                content.url,
                param.rendering,
                param.transparent,
                param.requestContext,
            )
        is WebContent.Data ->
            param.client.createBrowserWithHtml(
                content.data,
                content.baseUrl ?: KCEFBrowser.BLANK_URI,
                param.rendering,
                param.transparent,
            )
        is WebContent.File ->
            param.client.createBrowserWithHtml(
                param.fileContent,
                KCEFBrowser.BLANK_URI,
                param.rendering,
                param.transparent,
            )
        else ->
            param.client.createBrowser(
                KCEFBrowser.BLANK_URI,
                param.rendering,
                param.transparent,
                param.requestContext,
            )
    }

/**
 * Desktop WebView implementation.
 */
@OptIn(ExperimentalResourceApi::class)
@Composable
fun DesktopWebView(
    state: WebViewState,
    modifier: Modifier,
    navigator: WebViewNavigator,
    webViewJsBridge: WebViewJsBridge?,
    onCreated: (NativeWebView) -> Unit,
    onDispose: (NativeWebView) -> Unit,
    factory: (WebViewFactoryParam) -> NativeWebView,
) {
    val currentOnDispose by rememberUpdatedState(onDispose)
    val client =
        remember(state.webSettings.desktopWebSettings.disablePopupWindows) {
            KCEF.newClientOrNullBlocking()?.also {
                if (state.webSettings.desktopWebSettings.disablePopupWindows) {
                    it.addLifeSpanHandler(DisablePopupWindowsLifeSpanHandler())
                } else {
                    if (it.getLifeSpanHandler() is DisablePopupWindowsLifeSpanHandler) {
                        it.removeLifeSpanHandler()
                    }
                }
            }
        }
    val scope = rememberCoroutineScope()
    val fileContent by produceState("", state.content) {
        value =
            if (state.content is WebContent.File) {
                val res = Res.readBytes("assets/${(state.content as WebContent.File).fileName}")
                res.decodeToString().trimIndent()
            } else {
                ""
            }
    }

    val browser: KCEFBrowser? =
        remember(client, state.webSettings, fileContent) {
            client?.let { factory(WebViewFactoryParam(state, client, fileContent)) }
        }
    val desktopWebView: DesktopWebView? =
        remember(browser) {
            browser?.let { DesktopWebView(browser, scope, webViewJsBridge) }
        }

    browser?.let {
        SwingPanel(
            factory = {
                onCreated(it)
                state.webView = desktopWebView
                webViewJsBridge?.webView = desktopWebView
                browser.apply {
                    addDisplayHandler(state)
                    addLoadListener(state, navigator)
                    addRequestHandler(state, navigator)
                }
                browser.uiComponent
            },
            modifier = modifier,
        )
    }

    DisposableEffect(Unit) {
        onDispose {
            client?.dispose()
            browser?.let { currentOnDispose(it) }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy