Finish migration to the new window system

This commit is contained in:
Syer10
2021-08-15 12:40:07 -04:00
parent ad4544ff5d
commit 2e286619d5
17 changed files with 166 additions and 84 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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(
{

View File

@@ -0,0 +1,6 @@
package ca.gosyer.ui.base.components
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.awt.ComposeWindow
val LocalComposeWindow = compositionLocalOf<ComposeWindow> { throw RuntimeException("ComposeWindow not initialized") }

View File

@@ -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<CategoriesMenuViewModel>()
val categories by vm.categories.collectAsState()
remember {
windowEvents.onClose = {
DisposableEffect(Unit) {
onDispose {
val logger = KotlinLogging.logger {}
val handler = CoroutineExceptionHandler { _, throwable ->
logger.debug { throwable }

View File

@@ -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()
}
}
}

View File

@@ -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()
}
}
}

View File

@@ -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()
}
}
}

View File

@@ -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
) {

View File

@@ -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) }

View File

@@ -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)
}
}
}

View File

@@ -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<XmlResourceBundle>()
GlobalScope.launchApplication {
launchApplication {
var shortcuts by remember {
mutableStateOf(emptyMap<Key, ((KeyEvent) -> Boolean)>())
}
@@ -105,6 +105,7 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
}
) {
CompositionLocalProvider(
LocalComposeWindow provides window,
LocalResources provides resources
) {
AppTheme {

View File

@@ -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<MouseEvent?>(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 {

View File

@@ -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)
}
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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<Pair<Any, (() -> 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)
}

View File

@@ -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<XmlResourceBundle>()
Window(title, size, location, centered, icon, menuBar, undecorated, resizable, events, onDismissRequest) {
val resources = remember { AppScope.getInstance<XmlResourceBundle>() }
Window(
onCloseRequest,
state,
visible,
title,
icon,
undecorated,
resizable,
enabled,
focusable,
alwaysOnTop,
onPreviewKeyEvent,
onKeyEvent
) {
CompositionLocalProvider(
LocalResources provides resources
) {