From 2e286619d571ca2834a02a9645a5dd98ef2d6bc3 Mon Sep 17 00:00:00 2001 From: Syer10 Date: Sun, 15 Aug 2021 12:40:07 -0400 Subject: [PATCH] Finish migration to the new window system --- build.gradle.kts | 9 +-- .../kotlin/ca/gosyer/ui/base/WindowDialog.kt | 4 +- .../base/components/BoxWithTooltipSurface.kt | 8 ++- .../ui/base/components/ComposeWindow.kt | 6 ++ .../ca/gosyer/ui/categories/CategoriesMenu.kt | 21 ++++--- .../ca/gosyer/ui/downloads/DownloadsMenu.kt | 9 ++- .../ca/gosyer/ui/extensions/ExtensionsMenu.kt | 13 ++++- .../ca/gosyer/ui/library/LibraryScreen.kt | 9 ++- src/main/kotlin/ca/gosyer/ui/main/main.kt | 2 + .../kotlin/ca/gosyer/ui/manga/ChapterItem.kt | 3 + .../kotlin/ca/gosyer/ui/manga/MangaMenu.kt | 9 ++- .../kotlin/ca/gosyer/ui/reader/ReaderMenu.kt | 7 ++- .../reader/navigation/NavigationClickable.kt | 5 +- .../ca/gosyer/ui/sources/SourcesMenu.kt | 57 +++++++++++-------- .../ui/sources/settings/SourceSettingsMenu.kt | 7 ++- .../ca/gosyer/util/compose/ContextMenu.kt | 30 ++++++---- .../kotlin/ca/gosyer/util/compose/Theme.kt | 51 +++++++++++------ 17 files changed, 166 insertions(+), 84 deletions(-) create mode 100644 src/main/kotlin/ca/gosyer/ui/base/components/ComposeWindow.kt diff --git a/build.gradle.kts b/build.gradle.kts index 6a2b831d..5e898f81 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,4 @@ +import Config.serverCode import Config.tachideskVersion import org.gradle.jvm.tasks.Jar import org.jetbrains.compose.compose @@ -7,10 +8,10 @@ import org.jmailen.gradle.kotlinter.tasks.FormatTask import org.jmailen.gradle.kotlinter.tasks.LintTask plugins { - kotlin("jvm") version "1.5.10" - kotlin("kapt") version "1.5.10" - kotlin("plugin.serialization") version "1.5.10" - id("org.jetbrains.compose") version "0.5.0-build245" + kotlin("jvm") version "1.5.21" + kotlin("kapt") version "1.5.21" + kotlin("plugin.serialization") version "1.5.21" + id("org.jetbrains.compose") version "1.0.0-alpha4-build310" id("de.fuerstenau.buildconfig") version "1.1.8" id("org.jmailen.kotlinter") version "3.4.5" id("com.github.ben-manes.versions") version "0.39.0" diff --git a/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt b/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt index b38cb15b..597ce0f2 100644 --- a/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt +++ b/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt @@ -89,6 +89,7 @@ fun WindowDialog( else -> false } }, + alwaysOnTop = forceFocus ) { CompositionLocalProvider( LocalResources provides resources @@ -153,7 +154,8 @@ fun WindowDialog( } else -> false } - } + }, + alwaysOnTop = forceFocus ) { CompositionLocalProvider( LocalResources provides resources diff --git a/src/main/kotlin/ca/gosyer/ui/base/components/BoxWithTooltipSurface.kt b/src/main/kotlin/ca/gosyer/ui/base/components/BoxWithTooltipSurface.kt index 3c4724ba..9c92ad1f 100644 --- a/src/main/kotlin/ca/gosyer/ui/base/components/BoxWithTooltipSurface.kt +++ b/src/main/kotlin/ca/gosyer/ui/base/components/BoxWithTooltipSurface.kt @@ -7,7 +7,7 @@ package ca.gosyer.ui.base.components import androidx.compose.foundation.BoxWithTooltip -import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.TooltipPlacement import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Surface import androidx.compose.runtime.Composable @@ -24,8 +24,10 @@ fun BoxWithTooltipSurface( contentAlignment: Alignment = Alignment.TopStart, propagateMinConstraints: Boolean = false, delay: Int = 500, - offset: DpOffset = DpOffset.Zero, - content: @Composable BoxScope.() -> Unit + offset: TooltipPlacement = TooltipPlacement.CursorPoint( + offset = DpOffset(0.dp, 16.dp) + ), + content: @Composable () -> Unit ) { BoxWithTooltip( { diff --git a/src/main/kotlin/ca/gosyer/ui/base/components/ComposeWindow.kt b/src/main/kotlin/ca/gosyer/ui/base/components/ComposeWindow.kt new file mode 100644 index 00000000..24811f09 --- /dev/null +++ b/src/main/kotlin/ca/gosyer/ui/base/components/ComposeWindow.kt @@ -0,0 +1,6 @@ +package ca.gosyer.ui.base.components + +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.awt.ComposeWindow + +val LocalComposeWindow = compositionLocalOf { throw RuntimeException("ComposeWindow not initialized") } diff --git a/src/main/kotlin/ca/gosyer/ui/categories/CategoriesMenu.kt b/src/main/kotlin/ca/gosyer/ui/categories/CategoriesMenu.kt index bd829b29..834230ee 100644 --- a/src/main/kotlin/ca/gosyer/ui/categories/CategoriesMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/categories/CategoriesMenu.kt @@ -6,7 +6,6 @@ package ca.gosyer.ui.categories -import androidx.compose.desktop.WindowEvents import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -36,9 +35,9 @@ import androidx.compose.material.icons.rounded.KeyboardArrowDown import androidx.compose.material.icons.rounded.KeyboardArrowUp import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -46,26 +45,32 @@ import ca.gosyer.BuildConfig import ca.gosyer.ui.base.resources.stringResource import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.util.compose.ThemedWindow +import ca.gosyer.util.lang.launchApplication import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import mu.KotlinLogging +@OptIn(DelicateCoroutinesApi::class) fun openCategoriesMenu(notifyFinished: (() -> Unit)? = null) { - val windowEvents = WindowEvents() - ThemedWindow("${BuildConfig.NAME} - Categories", events = windowEvents) { - CategoriesMenu(notifyFinished, windowEvents) + launchApplication { + ThemedWindow( + ::exitApplication, + title = "${BuildConfig.NAME} - Categories" + ) { + CategoriesMenu(notifyFinished) + } } } @OptIn(DelicateCoroutinesApi::class) @Composable -fun CategoriesMenu(notifyFinished: (() -> Unit)? = null, windowEvents: WindowEvents) { +fun CategoriesMenu(notifyFinished: (() -> Unit)? = null) { val vm = viewModel() val categories by vm.categories.collectAsState() - remember { - windowEvents.onClose = { + DisposableEffect(Unit) { + onDispose { val logger = KotlinLogging.logger {} val handler = CoroutineExceptionHandler { _, throwable -> logger.debug { throwable } diff --git a/src/main/kotlin/ca/gosyer/ui/downloads/DownloadsMenu.kt b/src/main/kotlin/ca/gosyer/ui/downloads/DownloadsMenu.kt index e8874da8..00bbcfb5 100644 --- a/src/main/kotlin/ca/gosyer/ui/downloads/DownloadsMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/downloads/DownloadsMenu.kt @@ -50,10 +50,15 @@ import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.resources.stringResource import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.util.compose.ThemedWindow +import ca.gosyer.util.lang.launchApplication +import kotlinx.coroutines.DelicateCoroutinesApi +@OptIn(DelicateCoroutinesApi::class) fun openDownloadsMenu() { - ThemedWindow(BuildConfig.NAME) { - DownloadsMenu() + launchApplication { + ThemedWindow(::exitApplication, title = BuildConfig.NAME) { + DownloadsMenu() + } } } diff --git a/src/main/kotlin/ca/gosyer/ui/extensions/ExtensionsMenu.kt b/src/main/kotlin/ca/gosyer/ui/extensions/ExtensionsMenu.kt index 8c9f244d..7a2359b8 100644 --- a/src/main/kotlin/ca/gosyer/ui/extensions/ExtensionsMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/extensions/ExtensionsMenu.kt @@ -42,9 +42,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.WindowSize +import androidx.compose.ui.window.rememberWindowState import ca.gosyer.BuildConfig import ca.gosyer.data.models.Extension import ca.gosyer.ui.base.WindowDialog @@ -56,12 +57,18 @@ import ca.gosyer.ui.base.resources.stringResource import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.util.compose.ThemedWindow import ca.gosyer.util.compose.persistentLazyListState +import ca.gosyer.util.lang.launchApplication +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import java.util.Locale +@OptIn(DelicateCoroutinesApi::class) fun openExtensionsMenu() { - ThemedWindow(BuildConfig.NAME, size = IntSize(550, 700)) { - ExtensionsMenu() + launchApplication { + val state = rememberWindowState(size = WindowSize(550.dp, 700.dp)) + ThemedWindow(::exitApplication, state, title = BuildConfig.NAME) { + ExtensionsMenu() + } } } diff --git a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt index 0421b687..06fa5166 100644 --- a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt +++ b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt @@ -32,12 +32,17 @@ import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.manga.openMangaMenu import ca.gosyer.util.compose.ThemedWindow +import ca.gosyer.util.lang.launchApplication import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.rememberPagerState +import kotlinx.coroutines.DelicateCoroutinesApi +@OptIn(DelicateCoroutinesApi::class) fun openLibraryMenu() { - ThemedWindow(BuildConfig.NAME) { - LibraryScreen() + launchApplication { + ThemedWindow(::exitApplication, title = BuildConfig.NAME) { + LibraryScreen() + } } } diff --git a/src/main/kotlin/ca/gosyer/ui/main/main.kt b/src/main/kotlin/ca/gosyer/ui/main/main.kt index ac70ce67..2194c88b 100644 --- a/src/main/kotlin/ca/gosyer/ui/main/main.kt +++ b/src/main/kotlin/ca/gosyer/ui/main/main.kt @@ -28,6 +28,7 @@ import ca.gosyer.data.ui.UiPreferences import ca.gosyer.data.ui.model.ThemeMode import ca.gosyer.ui.base.WindowDialog import ca.gosyer.ui.base.components.LoadingScreen +import ca.gosyer.ui.base.components.LocalComposeWindow import ca.gosyer.ui.base.prefs.asStateIn import ca.gosyer.ui.base.resources.LocalResources import ca.gosyer.ui.base.resources.stringResource @@ -142,6 +143,7 @@ suspend fun main() { ) { AppTheme { CompositionLocalProvider( + LocalComposeWindow provides window, LocalBackPressHandler provides backPressHandler, LocalResources provides resources ) { diff --git a/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt b/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt index d6a5bae6..a4ce4e1e 100644 --- a/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt +++ b/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp import ca.gosyer.data.download.model.DownloadChapter import ca.gosyer.data.download.model.DownloadState import ca.gosyer.ui.base.components.DropdownIconButton +import ca.gosyer.ui.base.components.LocalComposeWindow import ca.gosyer.ui.base.components.combinedMouseClickable import ca.gosyer.ui.base.resources.stringResource import ca.gosyer.util.compose.contextMenu @@ -71,6 +72,7 @@ fun ChapterItem( elevation = 1.dp, shape = RoundedCornerShape(4.dp) ) { + val window = LocalComposeWindow.current BoxWithConstraints( Modifier.combinedMouseClickable( onClick = { @@ -78,6 +80,7 @@ fun ChapterItem( }, onRightClick = { contextMenu( + window, it ) { menuItem("Toggle read") { toggleRead(chapter.index) } diff --git a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt index aca1fbe2..a3b6adfa 100644 --- a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt @@ -51,11 +51,16 @@ import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.main.Route import ca.gosyer.ui.reader.openReaderMenu import ca.gosyer.util.compose.ThemedWindow +import ca.gosyer.util.lang.launchApplication import com.github.zsoltk.compose.router.BackStack +import kotlinx.coroutines.DelicateCoroutinesApi +@OptIn(DelicateCoroutinesApi::class) fun openMangaMenu(mangaId: Long) { - ThemedWindow(BuildConfig.NAME) { - MangaMenu(mangaId) + launchApplication { + ThemedWindow(::exitApplication, title = BuildConfig.NAME) { + MangaMenu(mangaId) + } } } diff --git a/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt b/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt index 4b0e7cdb..e391447e 100644 --- a/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt @@ -35,7 +35,6 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowPlacement -import androidx.compose.ui.window.launchApplication import androidx.compose.ui.window.rememberWindowState import ca.gosyer.BuildConfig import ca.gosyer.common.di.AppScope @@ -47,6 +46,7 @@ import ca.gosyer.data.ui.UiPreferences import ca.gosyer.data.ui.model.WindowSettings import ca.gosyer.ui.base.components.ErrorScreen import ca.gosyer.ui.base.components.LoadingScreen +import ca.gosyer.ui.base.components.LocalComposeWindow import ca.gosyer.ui.base.components.mangaAspectRatio import ca.gosyer.ui.base.resources.LocalResources import ca.gosyer.ui.base.resources.stringResource @@ -62,8 +62,8 @@ import ca.gosyer.ui.reader.navigation.RightAndLeftNavigation import ca.gosyer.ui.reader.navigation.navigationClickable import ca.gosyer.ui.reader.viewer.ContinuousReader import ca.gosyer.ui.reader.viewer.PagerReader +import ca.gosyer.util.lang.launchApplication import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope @OptIn(DelicateCoroutinesApi::class) fun openReaderMenu(chapterIndex: Int, mangaId: Long) { @@ -77,7 +77,7 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) { val resources = AppScope.getInstance() - GlobalScope.launchApplication { + launchApplication { var shortcuts by remember { mutableStateOf(emptyMap Boolean)>()) } @@ -105,6 +105,7 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) { } ) { CompositionLocalProvider( + LocalComposeWindow provides window, LocalResources provides resources ) { AppTheme { diff --git a/src/main/kotlin/ca/gosyer/ui/reader/navigation/NavigationClickable.kt b/src/main/kotlin/ca/gosyer/ui/reader/navigation/NavigationClickable.kt index 22f97bd3..dd44f0f8 100644 --- a/src/main/kotlin/ca/gosyer/ui/reader/navigation/NavigationClickable.kt +++ b/src/main/kotlin/ca/gosyer/ui/reader/navigation/NavigationClickable.kt @@ -22,6 +22,8 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.debugInspectorInfo import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import ca.gosyer.ui.base.components.LocalComposeWindow import ca.gosyer.ui.reader.model.Navigation import java.awt.event.MouseEvent @@ -71,11 +73,12 @@ fun Modifier.navigationClickable( } ) { var lastEvent by remember { mutableStateOf(null) } + val window = LocalComposeWindow.current Modifier .clickable(interactionSource, null, enabled, onClickLabel, role) { val savedLastEvent = lastEvent ?: return@clickable val offset = savedLastEvent.let { IntOffset(it.x, it.y) } - // onClick(navigation.getAction(offset, IntSize(window.width, window.height))) + onClick(navigation.getAction(offset, IntSize(window.width, window.height))) } .pointerInput(interactionSource) { forEachGesture { diff --git a/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt b/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt index 3449b74c..d2b5f51f 100644 --- a/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt @@ -7,9 +7,11 @@ package ca.gosyer.ui.sources import androidx.compose.foundation.BoxWithTooltip +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size @@ -46,20 +48,25 @@ import ca.gosyer.ui.sources.components.SourceHomeScreen import ca.gosyer.ui.sources.components.SourceScreen import ca.gosyer.ui.sources.settings.openSourceSettingsMenu import ca.gosyer.util.compose.ThemedWindow +import ca.gosyer.util.lang.launchApplication import com.github.zsoltk.compose.savedinstancestate.Bundle import com.github.zsoltk.compose.savedinstancestate.BundleScope import com.github.zsoltk.compose.savedinstancestate.LocalSavedInstanceState +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +@OptIn(DelicateCoroutinesApi::class) fun openSourcesMenu() { - ThemedWindow(BuildConfig.NAME) { - CompositionLocalProvider( - LocalSavedInstanceState provides Bundle() - ) { - SourcesMenu( - ::openSourceSettingsMenu, - ::openMangaMenu - ) + launchApplication { + ThemedWindow(::exitApplication, title = BuildConfig.NAME) { + CompositionLocalProvider( + LocalSavedInstanceState provides Bundle() + ) { + SourcesMenu( + ::openSourceSettingsMenu, + ::openMangaMenu + ) + } } } } @@ -138,23 +145,25 @@ fun SourcesMenu(bundle: Bundle, onSourceSettingsClick: (Long) -> Unit, onMangaCl }, modifier = Modifier.size(64.dp) ) { - val modifier = Modifier - .combinedMouseClickable( - onClick = { - vm.selectTab(source) - }, - onMiddleClick = { - if (source != null) { - vm.closeTab(source) + Box(Modifier.fillMaxSize()) { + val modifier = Modifier + .combinedMouseClickable( + onClick = { + vm.selectTab(source) + }, + onMiddleClick = { + if (source != null) { + vm.closeTab(source) + } } - } - ) - .requiredSize(50.dp) - .align(Alignment.Center) - if (source != null) { - KtorImage(source.iconUrl(serverUrl), modifier = modifier) - } else { - Icon(Icons.Rounded.Home, stringResource("sources_home"), modifier = modifier) + ) + .requiredSize(50.dp) + .align(Alignment.Center) + if (source != null) { + KtorImage(source.iconUrl(serverUrl), modifier = modifier) + } else { + Icon(Icons.Rounded.Home, stringResource("sources_home"), modifier = modifier) + } } } } diff --git a/src/main/kotlin/ca/gosyer/ui/sources/settings/SourceSettingsMenu.kt b/src/main/kotlin/ca/gosyer/ui/sources/settings/SourceSettingsMenu.kt index effabbb7..74677ca0 100644 --- a/src/main/kotlin/ca/gosyer/ui/sources/settings/SourceSettingsMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/sources/settings/SourceSettingsMenu.kt @@ -36,12 +36,15 @@ import ca.gosyer.ui.sources.settings.model.SourceSettingsView.List import ca.gosyer.ui.sources.settings.model.SourceSettingsView.Switch import ca.gosyer.ui.sources.settings.model.SourceSettingsView.TwoState import ca.gosyer.util.compose.ThemedWindow +import ca.gosyer.util.lang.launchApplication import com.github.zsoltk.compose.router.BackStack import kotlinx.coroutines.flow.MutableStateFlow fun openSourceSettingsMenu(sourceId: Long) { - ThemedWindow(BuildConfig.NAME) { - SourceSettingsMenu(sourceId) + launchApplication { + ThemedWindow(::exitApplication, title = BuildConfig.NAME) { + SourceSettingsMenu(sourceId) + } } } diff --git a/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt b/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt index 54f23647..e648fc68 100644 --- a/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt +++ b/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt @@ -6,26 +6,29 @@ package ca.gosyer.util.compose -import androidx.compose.desktop.AppManager +import androidx.compose.ui.awt.ComposeWindow import androidx.compose.ui.unit.IntOffset import ca.gosyer.util.lang.launchUI import com.github.weisj.darklaf.listener.MouseClickListener import kotlinx.coroutines.DelicateCoroutinesApi +import java.awt.event.WindowEvent +import java.awt.event.WindowFocusListener import javax.swing.Icon import javax.swing.JMenuItem import javax.swing.JPopupMenu import javax.swing.JSeparator -class ContextMenu internal constructor() { +class ContextMenu internal constructor(private val window: ComposeWindow) { internal val items = mutableListOf Unit)?>>() @OptIn(DelicateCoroutinesApi::class) internal fun popupMenu() = JPopupMenu().apply { - val window = AppManager.focusedWindow var mouseListener: MouseClickListener? = null + var focusListener: WindowFocusListener? = null fun close() { isVisible = false - mouseListener?.let { window?.removeMouseListener(it) } + mouseListener?.let { window.removeMouseListener(it) } + focusListener?.let { window.removeWindowFocusListener(it) } } fun (() -> Unit)?.andClose() { launchUI { @@ -39,14 +42,17 @@ class ContextMenu internal constructor() { close() } } - window?.addMouseListener(mouseListener) - window?.events?.let { - val oldFocusLost = it.onFocusLost - it.onFocusLost = { - it.onFocusLost.andClose() - it.onFocusLost = oldFocusLost + window.addMouseListener(mouseListener) + + focusListener = object : WindowFocusListener { + override fun windowGainedFocus(e: WindowEvent?) {} + override fun windowLostFocus(e: WindowEvent?) { + launchUI { + close() + } } } + window.addWindowFocusListener(focusListener) items.forEach { (item, block) -> when (item) { @@ -68,6 +74,6 @@ class ContextMenu internal constructor() { } } -fun contextMenu(offset: IntOffset, contextMenu: ContextMenu.() -> Unit) { - ContextMenu().apply(contextMenu).popupMenu().show(null, offset.x, offset.y) +fun contextMenu(window: ComposeWindow, offset: IntOffset, contextMenu: ContextMenu.() -> Unit) { + ContextMenu(window).apply(contextMenu).popupMenu().show(null, offset.x, offset.y) } diff --git a/src/main/kotlin/ca/gosyer/util/compose/Theme.kt b/src/main/kotlin/ca/gosyer/util/compose/Theme.kt index ffaa435d..9b256870 100644 --- a/src/main/kotlin/ca/gosyer/util/compose/Theme.kt +++ b/src/main/kotlin/ca/gosyer/util/compose/Theme.kt @@ -6,34 +6,51 @@ package ca.gosyer.util.compose -import androidx.compose.desktop.Window -import androidx.compose.desktop.WindowEvents import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.window.v1.MenuBar +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.input.key.KeyEvent +import androidx.compose.ui.window.FrameWindowScope +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.WindowState +import androidx.compose.ui.window.rememberWindowState import ca.gosyer.common.di.AppScope import ca.gosyer.data.translation.XmlResourceBundle import ca.gosyer.ui.base.resources.LocalResources import ca.gosyer.ui.base.theme.AppTheme -import java.awt.image.BufferedImage +@Composable fun ThemedWindow( - title: String = "JetpackDesktopWindow", - size: IntSize = IntSize(800, 600), - location: IntOffset = IntOffset.Zero, - centered: Boolean = true, - icon: BufferedImage? = null, - menuBar: MenuBar? = null, + onCloseRequest: () -> Unit, + state: WindowState = rememberWindowState(), + visible: Boolean = true, + title: String = "Untitled", + icon: Painter? = null, undecorated: Boolean = false, resizable: Boolean = true, - events: WindowEvents = WindowEvents(), - onDismissRequest: (() -> Unit)? = null, - content: @Composable () -> Unit = { } + enabled: Boolean = true, + focusable: Boolean = true, + alwaysOnTop: Boolean = false, + onPreviewKeyEvent: (KeyEvent) -> Boolean = { false }, + onKeyEvent: (KeyEvent) -> Boolean = { false }, + content: @Composable FrameWindowScope.() -> Unit = { } ) { - val resources = AppScope.getInstance() - Window(title, size, location, centered, icon, menuBar, undecorated, resizable, events, onDismissRequest) { + val resources = remember { AppScope.getInstance() } + Window( + onCloseRequest, + state, + visible, + title, + icon, + undecorated, + resizable, + enabled, + focusable, + alwaysOnTop, + onPreviewKeyEvent, + onKeyEvent + ) { CompositionLocalProvider( LocalResources provides resources ) {