Skip to content

Commit 9cb5a63

Browse files
committed
Let user pop current window to a new one
1 parent ceace71 commit 9cb5a63

File tree

5 files changed

+162
-81
lines changed

5 files changed

+162
-81
lines changed

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/core/ui/navigation/NavigationTransaction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ val LocalNavigationTransaction = staticCompositionLocalOf<NavigationTransaction>
1717
* This can be e.g. user clicking button multiple times.
1818
*/
1919
class NavigationTransaction(
20-
private val currentScreen: Screen,
20+
val currentScreen: Screen,
2121
private val navigator: Navigator,
2222
) {
2323
val canPop: Boolean get() = navigator.canPop
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package cz.frantisekmasa.wfrp_master.desktop
2+
3+
import androidx.compose.material.DrawerValue
4+
import androidx.compose.material.rememberDrawerState
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.CompositionLocalProvider
7+
import androidx.compose.runtime.Immutable
8+
import androidx.compose.runtime.remember
9+
import androidx.compose.runtime.rememberCoroutineScope
10+
import androidx.compose.ui.window.Window
11+
import cafe.adriel.voyager.core.screen.Screen
12+
import cafe.adriel.voyager.navigator.Navigator
13+
import cz.frantisekmasa.wfrp_master.common.core.ui.navigation.ProvideNavigationTransaction
14+
import cz.frantisekmasa.wfrp_master.common.core.ui.responsive.ScreenWithBreakpoints
15+
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.KeyboardDispatcher
16+
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.LocalKeyboardDispatcher
17+
import cz.frantisekmasa.wfrp_master.common.core.ui.theme.Theme
18+
import cz.frantisekmasa.wfrp_master.common.localization.FixedStrings
19+
import cz.frantisekmasa.wfrp_master.common.shell.DrawerShell
20+
import cz.frantisekmasa.wfrp_master.common.shell.SnackbarScaffold
21+
import kotlinx.coroutines.launch
22+
import java.util.UUID
23+
24+
@Immutable
25+
data class ApplicationWindowState(
26+
val initialScreen: Screen,
27+
val key: UUID,
28+
val isPrimary: Boolean,
29+
)
30+
31+
@Composable
32+
fun ApplicationWindow(
33+
initialScreen: Screen,
34+
onCloseRequest: () -> Unit,
35+
onNewWindowRequest: (initialScreen: Screen) -> Unit,
36+
) {
37+
val keyboardDispatcher = remember { KeyboardDispatcher() }
38+
39+
CompositionLocalProvider(
40+
LocalKeyboardDispatcher provides keyboardDispatcher,
41+
) {
42+
Window(
43+
title = FixedStrings.appName,
44+
onCloseRequest = onCloseRequest,
45+
onPreviewKeyEvent = {
46+
keyboardDispatcher.dispatch(it, beforeChildren = true)
47+
},
48+
onKeyEvent = {
49+
keyboardDispatcher.dispatch(it, beforeChildren = false)
50+
},
51+
) {
52+
Theme {
53+
SnackbarScaffold {
54+
Startup {
55+
ScreenWithBreakpoints {
56+
val drawerState = rememberDrawerState(DrawerValue.Closed)
57+
val coroutineScope = rememberCoroutineScope()
58+
59+
Navigator(
60+
screens = listOf(initialScreen),
61+
onBackPressed = {
62+
if (drawerState.isOpen) {
63+
coroutineScope.launch { drawerState.close() }
64+
return@Navigator false
65+
}
66+
67+
true
68+
}
69+
) { navigator ->
70+
DrawerShell(drawerState) {
71+
val screen = navigator.lastItem
72+
73+
navigator.saveableState("currentScreen") {
74+
ProvideNavigationTransaction(screen) {
75+
Shortcuts(
76+
onNewWindowRequest = onNewWindowRequest,
77+
)
78+
screen.Content()
79+
}
80+
}
81+
}
82+
}
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package cz.frantisekmasa.wfrp_master.desktop
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.ui.input.key.Key
5+
import androidx.compose.ui.input.key.KeyEventType
6+
import androidx.compose.ui.input.key.isAltPressed
7+
import androidx.compose.ui.input.key.isCtrlPressed
8+
import androidx.compose.ui.input.key.isShiftPressed
9+
import androidx.compose.ui.input.key.key
10+
import androidx.compose.ui.input.key.type
11+
import cafe.adriel.voyager.core.screen.Screen
12+
import cz.frantisekmasa.wfrp_master.common.core.ui.navigation.LocalNavigationTransaction
13+
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.KeyboardEffect
14+
15+
@Composable
16+
fun Shortcuts(onNewWindowRequest: (initialScreen: Screen) -> Unit) {
17+
val transaction = LocalNavigationTransaction.current
18+
19+
KeyboardEffect("global-shortcuts") {
20+
if (it.type != KeyEventType.KeyDown) {
21+
return@KeyboardEffect false
22+
}
23+
24+
if (it.isAltPressed && it.key == Key.DirectionLeft) {
25+
transaction.goBack(popLast = false)
26+
return@KeyboardEffect true
27+
}
28+
29+
if (it.isCtrlPressed && it.isShiftPressed && it.key == Key.T && transaction.canPop) {
30+
onNewWindowRequest(transaction.currentScreen)
31+
transaction.goBack()
32+
33+
return@KeyboardEffect true
34+
}
35+
36+
return@KeyboardEffect false
37+
}
38+
}

desktop/src/jvmMain/kotlin/cz/frantisekmasa/wfrp_master/desktop/Shortcuts.kt

Lines changed: 0 additions & 25 deletions
This file was deleted.

desktop/src/jvmMain/kotlin/cz/frantisekmasa/wfrp_master/desktop/WfrpMasterApplication.kt

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
11
package cz.frantisekmasa.wfrp_master.desktop
22

3-
import androidx.compose.material.DrawerValue
43
import androidx.compose.material.ExperimentalMaterialApi
5-
import androidx.compose.material.rememberDrawerState
64
import androidx.compose.runtime.CompositionLocalProvider
5+
import androidx.compose.runtime.key
6+
import androidx.compose.runtime.mutableStateListOf
77
import androidx.compose.runtime.rememberCoroutineScope
8-
import androidx.compose.ui.window.Window
8+
import androidx.compose.runtime.saveable.rememberSaveable
99
import androidx.compose.ui.window.application
10-
import cafe.adriel.voyager.navigator.Navigator
1110
import cz.frantisekmasa.wfrp_master.common.appModule
1211
import cz.frantisekmasa.wfrp_master.common.core.LocalStaticConfiguration
1312
import cz.frantisekmasa.wfrp_master.common.core.config.Platform
1413
import cz.frantisekmasa.wfrp_master.common.core.config.StaticConfiguration
1514
import cz.frantisekmasa.wfrp_master.common.core.shared.LocalFileChooserFactory
1615
import cz.frantisekmasa.wfrp_master.common.core.shared.LocalFileSaverFactory
1716
import cz.frantisekmasa.wfrp_master.common.core.shared.LocalUrlOpener
18-
import cz.frantisekmasa.wfrp_master.common.core.ui.navigation.ProvideNavigationTransaction
19-
import cz.frantisekmasa.wfrp_master.common.core.ui.responsive.ScreenWithBreakpoints
20-
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.KeyboardDispatcher
21-
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.LocalKeyboardDispatcher
22-
import cz.frantisekmasa.wfrp_master.common.core.ui.theme.Theme
23-
import cz.frantisekmasa.wfrp_master.common.localization.FixedStrings
2417
import cz.frantisekmasa.wfrp_master.common.partyList.PartyListScreen
25-
import cz.frantisekmasa.wfrp_master.common.shell.DrawerShell
26-
import cz.frantisekmasa.wfrp_master.common.shell.SnackbarScaffold
2718
import cz.frantisekmasa.wfrp_master.desktop.interop.DesktopUrlOpener
2819
import cz.frantisekmasa.wfrp_master.desktop.interop.NativeFileChooser
2920
import cz.frantisekmasa.wfrp_master.desktop.interop.NativeFileSaver
30-
import kotlinx.coroutines.launch
21+
import io.github.aakira.napier.Napier
3122
import org.kodein.di.compose.withDI
23+
import java.util.UUID
3224

3325
@ExperimentalMaterialApi
3426
object WfrpMasterApplication {
3527
@JvmStatic
3628
fun main(args: Array<String>) {
37-
val keyboardDispatcher = KeyboardDispatcher()
38-
3929
application {
4030
withDI(appModule) {
4131
val coroutineScope = rememberCoroutineScope()
@@ -49,49 +39,38 @@ object WfrpMasterApplication {
4939
version = "dev",
5040
platform = Platform.Desktop,
5141
),
52-
LocalKeyboardDispatcher provides keyboardDispatcher,
5342
) {
54-
Window(
55-
title = FixedStrings.appName,
56-
onCloseRequest = ::exitApplication,
57-
onPreviewKeyEvent = {
58-
keyboardDispatcher.dispatch(it, beforeChildren = true)
59-
},
60-
onKeyEvent = {
61-
keyboardDispatcher.dispatch(it, beforeChildren = false)
62-
},
63-
) {
64-
Theme {
65-
SnackbarScaffold {
66-
Startup {
67-
ScreenWithBreakpoints {
68-
val drawerState = rememberDrawerState(DrawerValue.Closed)
69-
70-
Navigator(
71-
screens = listOf(PartyListScreen),
72-
onBackPressed = {
73-
if (drawerState.isOpen) {
74-
coroutineScope.launch { drawerState.close() }
75-
return@Navigator false
76-
}
77-
78-
true
79-
}
80-
) { navigator ->
81-
DrawerShell(drawerState) {
82-
val screen = navigator.lastItem
43+
val windows = rememberSaveable {
44+
mutableStateListOf(
45+
ApplicationWindowState(
46+
initialScreen = PartyListScreen,
47+
key = UUID.randomUUID(),
48+
isPrimary = true,
49+
)
50+
)
51+
}
8352

84-
navigator.saveableState("currentScreen") {
85-
ProvideNavigationTransaction(screen) {
86-
Shortcuts()
87-
screen.Content()
88-
}
89-
}
90-
}
91-
}
53+
windows.forEach { window ->
54+
key(window.key) {
55+
ApplicationWindow(
56+
initialScreen = window.initialScreen,
57+
onNewWindowRequest = { initialScreen ->
58+
windows += ApplicationWindowState(
59+
initialScreen = initialScreen,
60+
key = UUID.randomUUID(),
61+
isPrimary = false,
62+
)
63+
},
64+
onCloseRequest = {
65+
if (window.isPrimary) {
66+
Napier.d("Requested closing of primary window, exiting the app")
67+
exitApplication()
68+
} else {
69+
Napier.d("Closing non-primary window")
70+
windows.removeIf { it.key == window.key }
9271
}
93-
}
94-
}
72+
},
73+
)
9574
}
9675
}
9776
}

0 commit comments

Comments
 (0)