From 4cb2aebd3d0366e635e0e063577f82aabbf2efea Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sat, 2 Nov 2024 00:45:55 -0300 Subject: [PATCH 01/18] refactor: improve platform identify --- .../com/neoutils/neoregex/Main.desktop.kt | 2 -- .../core/common/platform/Platform.android.kt | 5 +---- .../neoregex/core/common/platform/Platform.kt | 18 ++++++++++++------ .../core/common/platform/Platform.desktop.kt | 11 ++++++++--- .../core/common/util/UiMode.desktop.kt | 8 ++++---- .../core/common/platform/Platform.web.kt | 3 +-- .../core/sharedui/component/DefaultHeader.kt | 12 ++++++------ .../core/sharedui/component/NeoWindow.kt | 4 +--- 8 files changed, 33 insertions(+), 30 deletions(-) diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index 5dcc3276..8a1f49f1 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -20,7 +20,6 @@ package com.neoutils.neoregex import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon @@ -30,7 +29,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.unit.dp import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.application import com.neoutils.neoregex.core.common.util.UiMode diff --git a/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.android.kt b/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.android.kt index 0ba769c7..06614d80 100644 --- a/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.android.kt +++ b/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.android.kt @@ -18,7 +18,4 @@ package com.neoutils.neoregex.core.common.platform -import com.neoutils.neoregex.core.common.platform.Platform - -actual val Platform.Companion.Current: Platform - get() = Platform.ANDROID \ No newline at end of file +actual val platform: Platform = Platform.Android \ No newline at end of file diff --git a/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.kt b/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.kt index 520729c2..19446db1 100644 --- a/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.kt +++ b/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.kt @@ -18,12 +18,18 @@ package com.neoutils.neoregex.core.common.platform -enum class Platform { - DESKTOP, - ANDROID, - WEB; +sealed class Platform { - companion object + sealed class Desktop : Platform() { + data object Windows : Desktop() + data object Linux : Desktop() + data object MacOS : Desktop() + } + + data object Android : Platform() + + // Experimental + data object Web : Platform() } -expect val Platform.Companion.Current: Platform \ No newline at end of file +expect val platform: Platform \ No newline at end of file diff --git a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.desktop.kt b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.desktop.kt index 78ed80a8..c3f0e873 100644 --- a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.desktop.kt +++ b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.desktop.kt @@ -18,7 +18,12 @@ package com.neoutils.neoregex.core.common.platform -import com.neoutils.neoregex.core.common.platform.Platform +import org.jetbrains.skiko.OS +import org.jetbrains.skiko.hostOs -actual val Platform.Companion.Current - get() = Platform.DESKTOP \ No newline at end of file +actual val platform: Platform = when (hostOs) { + OS.Linux -> Platform.Desktop.Linux + OS.Windows -> Platform.Desktop.Windows + OS.MacOS -> Platform.Desktop.MacOS + else -> error("Unsupported platform ${hostOs.name}") +} diff --git a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt index 41531ec9..b9c9db00 100644 --- a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt +++ b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt @@ -18,17 +18,17 @@ package com.neoutils.neoregex.core.common.util +import com.neoutils.neoregex.core.common.platform.Platform +import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.UiMode.DARK import com.neoutils.neoregex.core.common.util.UiMode.LIGHT -import org.jetbrains.skiko.OS import org.jetbrains.skiko.SystemTheme import org.jetbrains.skiko.currentSystemTheme -import org.jetbrains.skiko.hostOs fun UiMode.Companion.resolve(): UiMode { - return when (hostOs) { - OS.Linux -> { + return when (platform) { + Platform.Desktop.Linux -> { XDGDesktopPortal().use { it.getTheme() } diff --git a/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.web.kt b/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.web.kt index 9a4c3d1a..ac771a5a 100644 --- a/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.web.kt +++ b/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/platform/Platform.web.kt @@ -18,5 +18,4 @@ package com.neoutils.neoregex.core.common.platform -actual val Platform.Companion.Current: Platform - get() = Platform.WEB \ No newline at end of file +actual val platform: Platform = Platform.Web \ No newline at end of file diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt index 1b9b6928..64dca957 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt @@ -44,6 +44,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.WindowScope import com.jetbrains.JBR +import com.neoutils.neoregex.core.common.platform.Platform +import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.DragHandler import com.neoutils.neoregex.core.common.util.UiMode import com.neoutils.neoregex.core.common.util.isDark @@ -53,8 +55,6 @@ import com.neoutils.neoregex.core.sharedui.remember.CompleteWindowState import com.neoutils.neoregex.core.sharedui.remember.WindowFocus import com.neoutils.neoregex.core.sharedui.remember.rememberCompleteWindowState import com.neoutils.neoregex.core.sharedui.remember.rememberWindowFocus -import org.jetbrains.skiko.OS -import org.jetbrains.skiko.hostOs import java.awt.Frame import java.awt.event.MouseEvent import java.awt.event.WindowEvent @@ -66,7 +66,7 @@ fun FrameWindowScope.DefaultHeader( title: String, modifier: Modifier = Modifier, options: @Composable RowScope.() -> Unit = {}, - uiMode: UiMode = UiMode.resolve(), + uiMode: UiMode = remember { UiMode.resolve() }, ) { val focus = rememberWindowFocus() @@ -168,9 +168,9 @@ fun FrameWindowScope.DefaultHeader( Row( modifier = Modifier.align( - alignment = when (hostOs) { - OS.Linux, OS.Windows -> Alignment.CenterStart - OS.MacOS -> Alignment.CenterEnd + alignment = when (platform) { + is Platform.Desktop.MacOS -> Alignment.CenterEnd + is Platform.Desktop.Linux -> Alignment.CenterStart else -> error("Invalid") } ) diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt index 3c2d355e..89ccf3e0 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt @@ -47,9 +47,7 @@ fun ApplicationScope.NeoWindow( title: String = stringResource(Res.string.app_name), undecorated: Boolean = JBR.windowDecorations == null, header: @Composable FrameWindowScope.() -> Unit = { - DefaultHeader( - title = title, - ) + DefaultHeader(title = title) }, content: @Composable FrameWindowScope.() -> Unit ) { From 2f46255b720a3ad8f28ea78913553b00a414aba3 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sat, 2 Nov 2024 15:01:07 -0300 Subject: [PATCH 02/18] refactor(desktop): extract header content --- application/build.gradle.kts | 5 +- .../com/neoutils/neoregex/Main.desktop.kt | 70 ++++++++++++------- .../core/sharedui/component/DefaultHeader.kt | 20 +----- .../core/sharedui/component/NeoWindow.kt | 11 ++- 4 files changed, 59 insertions(+), 47 deletions(-) diff --git a/application/build.gradle.kts b/application/build.gradle.kts index 42927910..c0c18007 100644 --- a/application/build.gradle.kts +++ b/application/build.gradle.kts @@ -16,7 +16,10 @@ * along with this program. If not, see . */ -import extension.* +import extension.catalog +import extension.config +import extension.distribution +import extension.name plugins { alias(libs.plugins.neoutils.neoregex.android) diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index 8a1f49f1..156e9c0d 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -19,18 +19,23 @@ package com.neoutils.neoregex import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.application +import com.neoutils.neoregex.core.common.platform.Platform +import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.UiMode import com.neoutils.neoregex.core.common.util.isDark import com.neoutils.neoregex.core.common.util.resolve @@ -63,31 +68,44 @@ fun main() = application { private fun FrameWindowScope.TitleBar( title: String = stringResource(Res.string.app_name), uiMode: UiMode = remember { UiMode.resolve() } -) { - DefaultHeader( - title = title, - options = { - val uriHandler = LocalUriHandler.current +) = DefaultHeader { - Icon( - painter = painterResource(Res.drawable.github), - contentDescription = null, - tint = when (uiMode) { - UiMode.LIGHT -> colorScheme.onSurface - UiMode.DARK -> colorScheme.onSurface - }, - modifier = Modifier - .clip(CircleShape) - .clickable( - onClick = { - uriHandler.openUri( - uri = "https://github.com/NeoUtils/NeoRegex" - ) - } - ) - .padding(dimensions.medium) - .aspectRatio(ratio = 1f) - ) - } + Text( + text = title, + modifier = Modifier.align( + Alignment.Center + ) ) -} + + Row( + modifier = Modifier.align( + alignment = when (platform) { + is Platform.Desktop.MacOS -> Alignment.CenterEnd + is Platform.Desktop.Linux -> Alignment.CenterStart + else -> error("Invalid") + } + ) + ) { + val uriHandler = LocalUriHandler.current + + Icon( + painter = painterResource(Res.drawable.github), + contentDescription = null, + tint = when (uiMode) { + UiMode.LIGHT -> colorScheme.onSurface + UiMode.DARK -> colorScheme.onSurface + }, + modifier = Modifier + .clip(CircleShape) + .clickable( + onClick = { + uriHandler.openUri( + uri = "https://github.com/NeoUtils/NeoRegex" + ) + } + ) + .padding(dimensions.medium) + .aspectRatio(ratio = 1f) + ) + } +} \ No newline at end of file diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt index 64dca957..7b6c65c2 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt @@ -28,7 +28,6 @@ import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -44,8 +43,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.WindowScope import com.jetbrains.JBR -import com.neoutils.neoregex.core.common.platform.Platform -import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.DragHandler import com.neoutils.neoregex.core.common.util.UiMode import com.neoutils.neoregex.core.common.util.isDark @@ -63,10 +60,9 @@ private val DefaultHeaderHeight = 40.dp @Composable fun FrameWindowScope.DefaultHeader( - title: String, modifier: Modifier = Modifier, - options: @Composable RowScope.() -> Unit = {}, uiMode: UiMode = remember { UiMode.resolve() }, + content: @Composable BoxScope.() -> Unit = {}, ) { val focus = rememberWindowFocus() @@ -148,7 +144,6 @@ fun FrameWindowScope.DefaultHeader( ) } } - ) { Box( modifier = Modifier @@ -156,7 +151,6 @@ fun FrameWindowScope.DefaultHeader( .fillMaxWidth(), contentAlignment = Alignment.Center ) { - Text(text = title) if (customTitleBar == null) { Buttons( @@ -166,17 +160,7 @@ fun FrameWindowScope.DefaultHeader( ) } - Row( - modifier = Modifier.align( - alignment = when (platform) { - is Platform.Desktop.MacOS -> Alignment.CenterEnd - is Platform.Desktop.Linux -> Alignment.CenterStart - else -> error("Invalid") - } - ) - ) { - options() - } + content() } } } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt index 89ccf3e0..c18f457d 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt @@ -20,10 +20,10 @@ package com.neoutils.neoregex.core.sharedui.component import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.height import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -47,7 +47,14 @@ fun ApplicationScope.NeoWindow( title: String = stringResource(Res.string.app_name), undecorated: Boolean = JBR.windowDecorations == null, header: @Composable FrameWindowScope.() -> Unit = { - DefaultHeader(title = title) + DefaultHeader { + Text( + text = title, + modifier = Modifier.align( + Alignment.Center + ) + ) + } }, content: @Composable FrameWindowScope.() -> Unit ) { From f609492ab3b7af77d3977a86f3ce1a6e6a5eff5f Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sat, 2 Nov 2024 15:30:30 -0300 Subject: [PATCH 03/18] feat(desktop): align options to right (WIP) --- .../com/neoutils/neoregex/Main.desktop.kt | 15 ++++-------- .../core/designsystem/theme/Dimensions.kt | 2 +- .../core/sharedui/component/DefaultHeader.kt | 24 ++++++++++++++----- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index 156e9c0d..4688bdd5 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -34,8 +34,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.application -import com.neoutils.neoregex.core.common.platform.Platform -import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.UiMode import com.neoutils.neoregex.core.common.util.isDark import com.neoutils.neoregex.core.common.util.resolve @@ -68,7 +66,7 @@ fun main() = application { private fun FrameWindowScope.TitleBar( title: String = stringResource(Res.string.app_name), uiMode: UiMode = remember { UiMode.resolve() } -) = DefaultHeader { +) = DefaultHeader { padding -> Text( text = title, @@ -78,13 +76,10 @@ private fun FrameWindowScope.TitleBar( ) Row( - modifier = Modifier.align( - alignment = when (platform) { - is Platform.Desktop.MacOS -> Alignment.CenterEnd - is Platform.Desktop.Linux -> Alignment.CenterStart - else -> error("Invalid") - } - ) + modifier = Modifier + .padding(padding) + .padding(horizontal = dimensions.medium) + .align(Alignment.CenterEnd) ) { val uriHandler = LocalUriHandler.current diff --git a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt index b4684a3d..fc2af6f3 100644 --- a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt +++ b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt @@ -27,8 +27,8 @@ val LocalDimensions = compositionLocalOf { error("Dimensions not def data class Dimensions( val micro: Dp = 2.dp, val tiny: Dp = 4.dp, - val small: Dp = 8.dp, val medium: Dp = 6.dp, + val small: Dp = 8.dp, val default: Dp = 16.dp, val large: Dp = 24.dp, val huge: Dp = 32.dp, diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt index 7b6c65c2..a61fd65b 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt @@ -30,6 +30,7 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -62,7 +63,7 @@ private val DefaultHeaderHeight = 40.dp fun FrameWindowScope.DefaultHeader( modifier: Modifier = Modifier, uiMode: UiMode = remember { UiMode.resolve() }, - content: @Composable BoxScope.() -> Unit = {}, + content: @Composable BoxScope.(padding: PaddingValues) -> Unit = {}, ) { val focus = rememberWindowFocus() @@ -148,19 +149,30 @@ fun FrameWindowScope.DefaultHeader( Box( modifier = Modifier .padding(dimensions.medium) - .fillMaxWidth(), + .fillMaxSize(), contentAlignment = Alignment.Center ) { if (customTitleBar == null) { + + val width = remember { mutableStateOf(0.dp) } + Buttons( - modifier = Modifier.align( - Alignment.CenterEnd + modifier = Modifier + .align(Alignment.CenterEnd) + .onSizeChanged { + width.value = it.width.dp + } + ) + + content( + PaddingValues( + end = width.value + dimensions.medium, ) ) + } else { + // TODO: implement } - - content() } } } From 3d073f7c7af97f503976d54730ee724c4e3a22bb Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sat, 2 Nov 2024 19:10:33 -0300 Subject: [PATCH 04/18] feat(desktop): adjust options to system buttons --- .../com/neoutils/neoregex/Main.desktop.kt | 4 +-- .../core/designsystem/theme/Dimensions.kt | 1 + .../core/sharedui/component/DefaultHeader.kt | 11 +++++-- .../core/sharedui/component/NeoWindow.kt | 14 ++++----- .../core/sharedui/extension/Modifier.kt | 30 ------------------- 5 files changed, 18 insertions(+), 42 deletions(-) delete mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/Modifier.kt diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index 4688bdd5..acbd85bb 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -54,7 +54,7 @@ fun main() = application { NeoTheme(uiMode.isDark) { NeoWindow( header = { - TitleBar() + Header() } ) { App() @@ -63,7 +63,7 @@ fun main() = application { } @Composable -private fun FrameWindowScope.TitleBar( +private fun FrameWindowScope.Header( title: String = stringResource(Res.string.app_name), uiMode: UiMode = remember { UiMode.resolve() } ) = DefaultHeader { padding -> diff --git a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt index fc2af6f3..38d8adaa 100644 --- a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt +++ b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Dimensions.kt @@ -29,6 +29,7 @@ data class Dimensions( val tiny: Dp = 4.dp, val medium: Dp = 6.dp, val small: Dp = 8.dp, + val short: Dp = 12.dp, val default: Dp = 16.dp, val large: Dp = 24.dp, val huge: Dp = 32.dp, diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt index a61fd65b..f097e336 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt @@ -167,11 +167,18 @@ fun FrameWindowScope.DefaultHeader( content( PaddingValues( - end = width.value + dimensions.medium, + end = width.value + dimensions.short, ) ) } else { - // TODO: implement + content( + density.run { + PaddingValues( + start = customTitleBar.leftInset.toDp(), + end = customTitleBar.rightInset.toDp() + ) + } + ) } } } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt index c18f457d..61dbc42e 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt @@ -18,6 +18,7 @@ package com.neoutils.neoregex.core.sharedui.component +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column import androidx.compose.material3.HorizontalDivider @@ -27,7 +28,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.dp import androidx.compose.ui.window.* @@ -35,7 +35,6 @@ import com.jetbrains.JBR import com.neoutils.neoregex.core.resources.Res import com.neoutils.neoregex.core.resources.app_name import com.neoutils.neoregex.core.resources.flavicon -import com.neoutils.neoregex.core.sharedui.extension.applyIf import com.neoutils.neoregex.core.sharedui.remember.CompleteWindowState import com.neoutils.neoregex.core.sharedui.remember.rememberCompleteWindowState import org.jetbrains.compose.resources.painterResource @@ -46,6 +45,7 @@ fun ApplicationScope.NeoWindow( icon: Painter = painterResource(Res.drawable.flavicon), title: String = stringResource(Res.string.app_name), undecorated: Boolean = JBR.windowDecorations == null, + border: BorderStroke = BorderStroke(1.dp, colorScheme.outline), header: @Composable FrameWindowScope.() -> Unit = { DefaultHeader { Text( @@ -79,12 +79,10 @@ fun ApplicationScope.NeoWindow( modifier = when (completeWindowState) { CompleteWindowState.FLOATING, CompleteWindowState.PINNED -> { - Modifier.applyIf(undecorated) { - border( - width = 1.dp, - color = colorScheme.outline, - shape = RectangleShape, - ) + if (undecorated) { + Modifier.border(border) + } else { + Modifier } } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/Modifier.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/Modifier.kt deleted file mode 100644 index e7b74021..00000000 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/Modifier.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * NeoRegex. - * - * Copyright (C) 2024 Irineu A. Silva. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.neoutils.neoregex.core.sharedui.extension - -import androidx.compose.ui.Modifier - -inline fun Modifier.applyIf( - mustApply: Boolean, - apply: Modifier. () -> Modifier -): Modifier { - return if (mustApply) { - apply() - } else this -} From 6435bd22e0312879b634ea4c92260f39fda36c47 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sat, 2 Nov 2024 21:02:57 -0300 Subject: [PATCH 05/18] doc(desktop, metainfo): inaccurate information --- application/installation/com.neoutils.NeoRegex.metainfo.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/installation/com.neoutils.NeoRegex.metainfo.xml b/application/installation/com.neoutils.NeoRegex.metainfo.xml index c310ee2f..9c40248d 100644 --- a/application/installation/com.neoutils.NeoRegex.metainfo.xml +++ b/application/installation/com.neoutils.NeoRegex.metainfo.xml @@ -10,7 +10,8 @@

- Visualize the functioning of your regular expressions in real-time with a useful and intuitive tool. With support for capture groups, undo, and redo. + Visualize the functioning of your regular expressions in real-time with a useful and intuitive tool. With + support for capture groups, undo, and redo.

@@ -50,10 +51,9 @@

Improvements in user experience.

    -
  • Small improvements across the application.
  • Customized window for a personalized experience.
  • -
  • Enhanced touchscreen support for better usability on touch devices.
  • Improved theme detection.
  • +
  • Small improvements.
From ca23c5d467fede0859f6b92f0dd52c9fdcf1ae22 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sun, 3 Nov 2024 02:07:45 -0300 Subject: [PATCH 06/18] refactor(desktop, NeoWindow): receives exception handler per parameter --- .idea/deploymentTargetSelector.xml | 10 ++++ .../com/neoutils/neoregex/Main.desktop.kt | 3 + .../core/sharedui/component/NeoWindow.kt | 57 +++++++++++-------- 3 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 .idea/deploymentTargetSelector.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..a6ec6492 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index acbd85bb..c374c2fd 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +@file:OptIn(ExperimentalComposeUiApi::class) + package com.neoutils.neoregex import androidx.compose.foundation.clickable @@ -29,6 +31,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt index 61dbc42e..39356d52 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt @@ -26,7 +26,9 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.dp @@ -40,12 +42,14 @@ import com.neoutils.neoregex.core.sharedui.remember.rememberCompleteWindowState import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource +@OptIn(ExperimentalComposeUiApi::class) @Composable fun ApplicationScope.NeoWindow( icon: Painter = painterResource(Res.drawable.flavicon), title: String = stringResource(Res.string.app_name), undecorated: Boolean = JBR.windowDecorations == null, border: BorderStroke = BorderStroke(1.dp, colorScheme.outline), + exceptionHandlerFactory: WindowExceptionHandlerFactory = DefaultWindowExceptionHandlerFactory, header: @Composable FrameWindowScope.() -> Unit = { DefaultHeader { Text( @@ -64,38 +68,43 @@ fun ApplicationScope.NeoWindow( ), ) - Window( - icon = icon, - title = title, - undecorated = undecorated, - onCloseRequest = ::exitApplication, - state = windowState + CompositionLocalProvider( + LocalWindowExceptionHandlerFactory + .provides(exceptionHandlerFactory) ) { + Window( + icon = icon, + title = title, + undecorated = undecorated, + onCloseRequest = ::exitApplication, + state = windowState + ) { - val completeWindowState = rememberCompleteWindowState() + val completeWindowState = rememberCompleteWindowState() - Surface( - color = colorScheme.background, - modifier = when (completeWindowState) { - CompleteWindowState.FLOATING, - CompleteWindowState.PINNED -> { - if (undecorated) { - Modifier.border(border) - } else { - Modifier + Surface( + color = colorScheme.background, + modifier = when (completeWindowState) { + CompleteWindowState.FLOATING, + CompleteWindowState.PINNED -> { + if (undecorated) { + Modifier.border(border) + } else { + Modifier + } } - } - else -> Modifier - } - ) { - Column { + else -> Modifier + } + ) { + Column { - header() + header() - HorizontalDivider() + HorizontalDivider() - content() + content() + } } } } From 997b39506ae35e9499d55f95addb4b402a0b55b1 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Wed, 6 Nov 2024 18:36:54 -0300 Subject: [PATCH 07/18] feat(matcher): show regex performance --- .idea/deploymentTargetSelector.xml | 10 +++ .../core/designsystem/theme/FontSizes.kt | 1 + .../composeResources/values/strings.xml | 5 ++ .../neoregex/core/sharedui/model/Match.kt | 2 +- .../neoregex/feature/matcher/MatcherScreen.kt | 84 ++++++++++++------- .../feature/matcher/MatcherViewModel.kt | 25 ++++-- .../feature/matcher/state/MatcherUiState.kt | 13 ++- 7 files changed, 102 insertions(+), 38 deletions(-) create mode 100644 .idea/deploymentTargetSelector.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 00000000..a6ec6492 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/FontSizes.kt b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/FontSizes.kt index 2b56a4e1..0639ded8 100644 --- a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/FontSizes.kt +++ b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/FontSizes.kt @@ -30,4 +30,5 @@ data class FontSizes( val medium: TextUnit = 16.sp, val default: TextUnit = 14.sp, val small: TextUnit = 12.sp, + val tiny: TextUnit = 10.sp, ) \ No newline at end of file diff --git a/core/resources/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml index de0b0229..ec873da4 100644 --- a/core/resources/src/commonMain/composeResources/values/strings.xml +++ b/core/resources/src/commonMain/composeResources/values/strings.xml @@ -19,4 +19,9 @@ NeoRegex Enter regular expression + + + %1$s match (%2$s) + %1$s matches (%2$s) + \ No newline at end of file diff --git a/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/model/Match.kt b/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/model/Match.kt index 3b11a83c..ceb5a1f3 100644 --- a/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/model/Match.kt +++ b/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/model/Match.kt @@ -22,5 +22,5 @@ data class Match( val number: Int, val text: String, val range: IntRange, - val groups: List = emptyList() + val groups: List = emptyList(), ) \ No newline at end of file diff --git a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt index 29b917e3..2d972a47 100644 --- a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt +++ b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt @@ -25,13 +25,10 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.* import androidx.compose.material3.MaterialTheme.colorScheme -import androidx.compose.material3.Surface -import androidx.compose.material3.VerticalDivider +import androidx.compose.material3.MaterialTheme.typography import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -46,23 +43,26 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.screen.Screen +import com.neoutils.neoregex.core.common.platform.Platform +import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.Command import com.neoutils.neoregex.core.designsystem.textfield.NeoTextField import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.fontSizes +import com.neoutils.neoregex.core.resources.* import com.neoutils.neoregex.core.sharedui.component.TextEditor import com.neoutils.neoregex.feature.matcher.action.MatcherAction import com.neoutils.neoregex.feature.matcher.extension.onLongHold import com.neoutils.neoregex.feature.matcher.extension.toTextState import com.neoutils.neoregex.feature.matcher.model.Target import com.neoutils.neoregex.feature.matcher.state.MatcherUiState +import com.neoutils.neoregex.feature.matcher.state.duration import com.neoutils.neoregex.feature.matcher.state.error import com.neoutils.neoregex.feature.matcher.state.matches -import com.neoutils.neoregex.core.resources.Res -import com.neoutils.neoregex.core.resources.ic_redo_24 -import com.neoutils.neoregex.core.resources.ic_undo_24 -import com.neoutils.neoregex.core.resources.insert_regex_hint import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.pluralStringResource import org.jetbrains.compose.resources.stringResource +import kotlin.time.DurationUnit class MatcherScreen : Screen { @@ -77,27 +77,28 @@ class MatcherScreen : Screen { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - TextEditor( - value = uiState.text, - onValueChange = { - viewModel.onAction( - MatcherAction.Input.UpdateText(it.toTextState()) - ) - }, - onFocusChange = { - if (it.isFocused) { + Box(modifier = Modifier.weight(weight = 1f)) { + + TextEditor( + value = uiState.text, + onValueChange = { viewModel.onAction( - MatcherAction.TargetChange(Target.TEXT) + MatcherAction.Input.UpdateText(it.toTextState()) ) - } - }, - textStyle = TextStyle( - letterSpacing = 1.sp, - fontSize = 16.sp, - ), - modifier = Modifier - .weight(weight = 1f) - .onPreviewKeyEvent { + }, + onFocusChange = { + if (it.isFocused) { + viewModel.onAction( + MatcherAction.TargetChange(Target.TEXT) + ) + } + }, + textStyle = TextStyle( + letterSpacing = 1.sp, + fontSize = 16.sp, + ), + matches = uiState.matchResult.matches, + modifier = Modifier.onPreviewKeyEvent { when (Command.from(it)) { Command.UNDO -> { viewModel.onAction( @@ -116,8 +117,31 @@ class MatcherScreen : Screen { else -> false } }, - matches = uiState.matchResult.matches, - ) + ) + + if (platform != Platform.Web) { + Text( + text = pluralStringResource( + Res.plurals.match_result_infos, + uiState.matchResult.matches.size, + uiState.matchResult.matches.size, + uiState.matchResult.duration.toString( + unit = DurationUnit.MILLISECONDS, + decimals = 3 + ) + ), + fontSize = fontSizes.tiny, + style = typography.labelSmall, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(4.dp) + .background( + color = colorScheme.surfaceVariant, + shape = RoundedCornerShape(dimensions.tiny) + ).padding(2.dp) + ) + } + } Footer( uiState = uiState, diff --git a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt index 64cf14d5..35c8b622 100644 --- a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt +++ b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt @@ -30,6 +30,8 @@ import com.neoutils.neoregex.feature.matcher.model.TextState import com.neoutils.neoregex.feature.matcher.state.MatcherUiState import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* +import kotlin.time.Duration +import kotlin.time.measureTime @OptIn(FlowPreview::class) class MatcherViewModel : ScreenModel { @@ -51,27 +53,40 @@ class MatcherViewModel : ScreenModel { inputs[Target.REGEX].map { it.text } ) { text, pattern -> + if (pattern.isEmpty()) { + return@combine MatcherUiState.MatchResult.Success() + } + val regex = try { Regex(pattern) - } catch (exception: Throwable) { + } catch (t: Throwable) { return@combine MatcherUiState.MatchResult.Failure( - error = exception.message ?: "Invalid regex pattern" + error = t.message ?: "Invalid regex pattern" ) } + val duration: Duration + val result: Sequence + + // TODO: don't support web target + duration = measureTime { + result = regex.findAll(text) + } + MatcherUiState.MatchResult.Success( matches = buildList { - regex.findAll(text).forEachIndexed { index, match -> + result.forEachIndexed { index, match -> add( Match( text = match.value, range = match.range, groups = match.groupValues.drop(n = 1), - number = index.inc() + number = index.inc(), ) ) } - } + }, + duration = duration ) } diff --git a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/state/MatcherUiState.kt b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/state/MatcherUiState.kt index ceb588d6..04a4e2d4 100644 --- a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/state/MatcherUiState.kt +++ b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/state/MatcherUiState.kt @@ -21,13 +21,14 @@ package com.neoutils.neoregex.feature.matcher.state import androidx.compose.ui.text.input.TextFieldValue import com.neoutils.neoregex.core.sharedui.model.Match import com.neoutils.neoregex.feature.matcher.model.Target +import kotlin.time.Duration data class MatcherUiState( val target: Target? = null, val text: TextFieldValue = TextFieldValue(), val regex: TextFieldValue = TextFieldValue(), val history: History = History(), - val matchResult: MatchResult = MatchResult.Success(listOf()), + val matchResult: MatchResult = MatchResult.Success(), ) { data class History( val canUndo: Boolean = false, @@ -35,8 +36,10 @@ data class MatcherUiState( ) sealed class MatchResult { + data class Success( - val matches: List + val matches: List = listOf(), + val duration: Duration = Duration.ZERO ) : MatchResult() data class Failure( @@ -56,3 +59,9 @@ val MatcherUiState.MatchResult.error is MatcherUiState.MatchResult.Failure -> error is MatcherUiState.MatchResult.Success -> "" } + +val MatcherUiState.MatchResult.duration + get() = when (this) { + is MatcherUiState.MatchResult.Failure -> Duration.ZERO + is MatcherUiState.MatchResult.Success -> duration + } \ No newline at end of file From 520937569453739daa46a76d1c82113354e2db30 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Wed, 6 Nov 2024 18:43:00 -0300 Subject: [PATCH 08/18] fix(matcher): wrong font attributes --- .../core/designsystem/theme/Typhography.kt | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt index 7ff613c3..c1627532 100644 --- a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt +++ b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt @@ -36,105 +36,75 @@ internal fun NeoTypography( fontFamily = fontFamily, fontWeight = FontWeight.Bold, fontSize = 57.sp, - lineHeight = 64.sp, - letterSpacing = 0.sp ), displayMedium = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Bold, fontSize = 45.sp, - lineHeight = 52.sp, - letterSpacing = 0.sp ), displaySmall = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Bold, fontSize = 36.sp, - lineHeight = 44.sp, - letterSpacing = 0.sp ), headlineLarge = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 32.sp, - lineHeight = 40.sp, - letterSpacing = 0.sp ), headlineMedium = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 28.sp, - lineHeight = 36.sp, - letterSpacing = 0.sp ), headlineSmall = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 24.sp, - lineHeight = 32.sp, - letterSpacing = 0.sp ), titleLarge = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.SemiBold, fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp ), titleMedium = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Medium, fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.1.sp ), titleSmall = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Medium, fontSize = 14.sp, - lineHeight = 20.sp, - letterSpacing = 0.1.sp ), bodyLarge = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 16.sp, - lineHeight = 25.sp, - letterSpacing = 0.5.sp ), bodyMedium = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 14.sp, - lineHeight = 20.sp, - letterSpacing = 0.25.sp ), bodySmall = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp, - lineHeight = 16.sp, - letterSpacing = 0.4.sp ), labelLarge = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Medium, fontSize = 14.sp, - lineHeight = 20.sp, - letterSpacing = 0.1.sp ), labelMedium = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Medium, fontSize = 12.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp ), labelSmall = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Medium, fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp ) ) \ No newline at end of file From 34f68b1a1147bb7c353c364a354242f721f43cd9 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Wed, 6 Nov 2024 18:52:50 -0300 Subject: [PATCH 09/18] fix(matcher): don't process regex unnecessarily --- .../com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt index 35c8b622..62fbbb02 100644 --- a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt +++ b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherViewModel.kt @@ -49,8 +49,8 @@ class MatcherViewModel : ScreenModel { ) private val matchResult = combine( - inputs[Target.TEXT].map { it.text }, - inputs[Target.REGEX].map { it.text } + inputs[Target.TEXT].map { it.text }.distinctUntilChanged(), + inputs[Target.REGEX].map { it.text }.distinctUntilChanged() ) { text, pattern -> if (pattern.isEmpty()) { From c92142c8586010ea89ae751a1a11e9513fbe8507 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Sun, 3 Nov 2024 03:32:16 -0300 Subject: [PATCH 10/18] feat(desktop): create custom error window #55 --- .../com/neoutils/neoregex/Main.android.kt | 11 +- .../com/neoutils/neoregex/Main.desktop.kt | 43 +++---- .../kotlin/com/neoutils/neoregex/Main.web.kt | 3 +- ...iMode.android.kt => ColorTheme.android.kt} | 25 ++-- .../common/util/{UiMode.kt => ColorTheme.kt} | 14 ++- ...iMode.desktop.kt => ColorTheme.desktop.kt} | 31 ++--- .../core/common/util/XDGDesktopPortal.kt | 8 +- .../core/common/util/ColorTheme.web.kt | 33 +++++ core/design-system/build.gradle.kts | 1 + .../neoregex/core/designsystem/theme/Theme.kt | 34 +++-- .../core/designsystem/theme/Typhography.kt | 4 +- .../composeResources/values/strings.xml | 6 + .../core/sharedui/component/DefaultHeader.kt | 13 +- .../sharedui/component/FatalErrorWindow.kt | 116 ++++++++++++++++++ .../core/sharedui/component/NeoWindow.kt | 5 +- .../core/sharedui/component/ReportScreen.kt | 99 +++++++++++++++ .../core/sharedui/component/StackTraceBox.kt | 90 ++++++++++++++ .../core/sharedui/extension/WindowState.kt | 36 ++++++ .../core/sharedui/theme/NeoErrorTheme.kt | 52 ++++++++ .../util/NeoWindowExceptionHandlerFactory.kt | 54 ++++++++ .../neoregex/core/sharedui/util/Padding.kt | 37 ++++++ 21 files changed, 640 insertions(+), 75 deletions(-) rename core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/{UiMode.android.kt => ColorTheme.android.kt} (66%) rename core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/{UiMode.kt => ColorTheme.kt} (76%) rename core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/{UiMode.desktop.kt => ColorTheme.desktop.kt} (61%) create mode 100644 core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/StackTraceBox.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt diff --git a/application/src/androidMain/kotlin/com/neoutils/neoregex/Main.android.kt b/application/src/androidMain/kotlin/com/neoutils/neoregex/Main.android.kt index 7e3dc00d..757f6569 100644 --- a/application/src/androidMain/kotlin/com/neoutils/neoregex/Main.android.kt +++ b/application/src/androidMain/kotlin/com/neoutils/neoregex/Main.android.kt @@ -26,8 +26,8 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.ui.Modifier -import com.neoutils.neoregex.core.common.util.UiMode -import com.neoutils.neoregex.core.common.util.resolve +import com.neoutils.neoregex.core.common.util.ColorTheme +import com.neoutils.neoregex.core.common.util.colorTheme import com.neoutils.neoregex.core.designsystem.theme.NeoTheme class MainActivity : ComponentActivity() { @@ -38,6 +38,7 @@ class MainActivity : ComponentActivity() { setupSystemBars() setContent { + NeoTheme { App(Modifier.safeDrawingPadding()) } @@ -46,14 +47,14 @@ class MainActivity : ComponentActivity() { private fun setupSystemBars() { - val style = when (UiMode.resolve(context = this)) { - UiMode.DARK -> { + val style = when (colorTheme) { + ColorTheme.DARK -> { SystemBarStyle.dark( Color.BLACK, ) } - UiMode.LIGHT -> { + ColorTheme.LIGHT -> { SystemBarStyle.light( Color.WHITE, Color.BLACK, diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index c374c2fd..97dcd74f 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -29,38 +29,39 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.window.FrameWindowScope -import androidx.compose.ui.window.application -import com.neoutils.neoregex.core.common.util.UiMode -import com.neoutils.neoregex.core.common.util.isDark -import com.neoutils.neoregex.core.common.util.resolve +import androidx.compose.ui.window.launchApplication +import com.neoutils.neoregex.core.common.util.ColorTheme +import com.neoutils.neoregex.core.common.util.rememberColorTheme import com.neoutils.neoregex.core.designsystem.theme.NeoTheme import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions import com.neoutils.neoregex.core.resources.Res import com.neoutils.neoregex.core.resources.app_name import com.neoutils.neoregex.core.resources.github -import com.neoutils.neoregex.core.sharedui.component.DefaultHeader +import com.neoutils.neoregex.core.sharedui.component.NeoHeader import com.neoutils.neoregex.core.sharedui.component.NeoWindow +import kotlinx.coroutines.CoroutineScope import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource +import org.jetbrains.skiko.MainUIDispatcher -fun main() = application { - - val uiMode = remember { UiMode.resolve() } - - NeoTheme(uiMode.isDark) { - NeoWindow( - header = { - Header() +fun main() { + with(CoroutineScope(MainUIDispatcher)) { + launchApplication { + NeoTheme { + NeoWindow( + header = { + Header() + } + ) { + App() + } } - ) { - App() } } } @@ -68,8 +69,8 @@ fun main() = application { @Composable private fun FrameWindowScope.Header( title: String = stringResource(Res.string.app_name), - uiMode: UiMode = remember { UiMode.resolve() } -) = DefaultHeader { padding -> + colorTheme: ColorTheme = rememberColorTheme() +) = NeoHeader { padding -> Text( text = title, @@ -89,9 +90,9 @@ private fun FrameWindowScope.Header( Icon( painter = painterResource(Res.drawable.github), contentDescription = null, - tint = when (uiMode) { - UiMode.LIGHT -> colorScheme.onSurface - UiMode.DARK -> colorScheme.onSurface + tint = when (colorTheme) { + ColorTheme.LIGHT -> colorScheme.onSurface + ColorTheme.DARK -> colorScheme.onSurface }, modifier = Modifier .clip(CircleShape) diff --git a/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt b/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt index 3b94f017..1594dbe9 100644 --- a/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt +++ b/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.window.CanvasBasedWindow import com.neoutils.neoregex.core.common.extension.toCss import com.neoutils.neoregex.core.common.util.SizeManager +import com.neoutils.neoregex.core.common.util.ColorTheme import com.neoutils.neoregex.core.designsystem.theme.NeoTheme import kotlinx.browser.document import kotlinx.coroutines.flow.first @@ -44,7 +45,7 @@ fun main() { sizeManager.changes.first() } ) { - NeoTheme { + NeoTheme(ColorTheme) { body.style.backgroundColor = colorScheme.background.toCss() diff --git a/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.android.kt b/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt similarity index 66% rename from core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.android.kt rename to core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt index 98cec81f..23904d65 100644 --- a/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.android.kt +++ b/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt @@ -1,7 +1,7 @@ /* * NeoRegex. * - * Copyright (C) 2024 Irineu A. Silva. + * Copyright (C) 2024 . * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,17 +21,26 @@ package com.neoutils.neoregex.core.common.util import android.content.Context import android.content.res.Configuration.UI_MODE_NIGHT_MASK import android.content.res.Configuration.UI_MODE_NIGHT_YES -import com.neoutils.neoregex.core.common.util.UiMode +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable -fun UiMode.Companion.resolve(context: Context): UiMode { +@Composable +actual fun rememberColorTheme(): ColorTheme { - return when (context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK) { + return if (isSystemInDarkTheme()) { + ColorTheme.DARK + } else { + ColorTheme.LIGHT + } +} + +val Context.colorTheme + get() = when (resources.configuration.uiMode and UI_MODE_NIGHT_MASK) { UI_MODE_NIGHT_YES -> { - UiMode.DARK + ColorTheme.DARK } else -> { - UiMode.LIGHT + ColorTheme.LIGHT } - } -} + } \ No newline at end of file diff --git a/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.kt b/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.kt similarity index 76% rename from core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.kt rename to core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.kt index 3349e2de..dc45a3f3 100644 --- a/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.kt +++ b/core/common/src/commonMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.kt @@ -18,12 +18,18 @@ package com.neoutils.neoregex.core.common.util -enum class UiMode { +import androidx.compose.runtime.Composable + +enum class ColorTheme { LIGHT, DARK; - companion object + val isLight: Boolean + get() = this == LIGHT + + val isDark: Boolean + get() = this == DARK } -val UiMode.isDark: Boolean - get() = this == UiMode.DARK +@Composable +expect fun rememberColorTheme(): ColorTheme \ No newline at end of file diff --git a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt similarity index 61% rename from core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt rename to core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt index b9c9db00..276f86f4 100644 --- a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/UiMode.desktop.kt +++ b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt @@ -1,7 +1,7 @@ /* * NeoRegex. * - * Copyright (C) 2024 Irineu A. Silva. + * Copyright (C) 2024 . * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,26 +18,29 @@ package com.neoutils.neoregex.core.common.util +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import com.neoutils.neoregex.core.common.platform.Platform import com.neoutils.neoregex.core.common.platform.platform -import com.neoutils.neoregex.core.common.util.UiMode.DARK -import com.neoutils.neoregex.core.common.util.UiMode.LIGHT import org.jetbrains.skiko.SystemTheme import org.jetbrains.skiko.currentSystemTheme -fun UiMode.Companion.resolve(): UiMode { +@Composable +actual fun rememberColorTheme(): ColorTheme { - return when (platform) { - Platform.Desktop.Linux -> { - XDGDesktopPortal().use { - it.getTheme() + return remember { + when (platform) { + Platform.Desktop.Linux -> { + XDGDesktopPortal().use { + it.getTheme() + } } - } - else -> when (currentSystemTheme) { - SystemTheme.LIGHT -> LIGHT - SystemTheme.DARK -> DARK - SystemTheme.UNKNOWN -> LIGHT + else -> when (currentSystemTheme) { + SystemTheme.LIGHT -> ColorTheme.LIGHT + SystemTheme.DARK -> ColorTheme.DARK + SystemTheme.UNKNOWN -> ColorTheme.LIGHT + } } } -} +} \ No newline at end of file diff --git a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/XDGDesktopPortal.kt b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/XDGDesktopPortal.kt index 0e638983..e0087872 100644 --- a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/XDGDesktopPortal.kt +++ b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/XDGDesktopPortal.kt @@ -31,7 +31,7 @@ class XDGDesktopPortal( .build() ) : AutoCloseable { - fun getTheme(): UiMode { + fun getTheme(): ColorTheme { val settings = connection.getRemoteObject( BUS, @@ -45,9 +45,9 @@ class XDGDesktopPortal( ) return when (theme.value.value) { - Theme.LIGHT.value -> UiMode.LIGHT - Theme.DARK.value -> UiMode.DARK - else -> UiMode.LIGHT + Theme.LIGHT.value -> ColorTheme.LIGHT + Theme.DARK.value -> ColorTheme.DARK + else -> ColorTheme.LIGHT } } diff --git a/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt b/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt new file mode 100644 index 00000000..d1c1641c --- /dev/null +++ b/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt @@ -0,0 +1,33 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.common.util + +import androidx.compose.runtime.Composable +import org.jetbrains.skiko.SystemTheme +import org.jetbrains.skiko.currentSystemTheme + +@Composable +actual fun rememberColorTheme(): ColorTheme { + + return when (currentSystemTheme) { + SystemTheme.LIGHT -> ColorTheme.LIGHT + SystemTheme.DARK -> ColorTheme.DARK + SystemTheme.UNKNOWN -> ColorTheme.LIGHT + } +} \ No newline at end of file diff --git a/core/design-system/build.gradle.kts b/core/design-system/build.gradle.kts index cedbc197..d8438b49 100644 --- a/core/design-system/build.gradle.kts +++ b/core/design-system/build.gradle.kts @@ -31,6 +31,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.core.resources) + implementation(projects.core.common) } } } diff --git a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Theme.kt b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Theme.kt index 26e0bda9..26394932 100644 --- a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Theme.kt +++ b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Theme.kt @@ -18,7 +18,6 @@ package com.neoutils.neoregex.core.designsystem.theme -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.* import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.runtime.Composable @@ -26,6 +25,12 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import com.neoutils.neoregex.core.common.util.ColorTheme +import com.neoutils.neoregex.core.common.util.rememberColorTheme +import com.neoutils.neoregex.core.resources.Res +import com.neoutils.neoregex.core.resources.roboto_mono +import org.jetbrains.compose.resources.Font private val LightColors = lightColorScheme( primary = Blue700, @@ -63,18 +68,33 @@ private val DarkColors = darkColorScheme( @Composable fun NeoTheme( - darkMode: Boolean = isSystemInDarkTheme(), + colorTheme: ColorTheme = rememberColorTheme(), + content: @Composable () -> Unit +) { + NeoBaseTheme( + colorScheme = when (colorTheme) { + ColorTheme.LIGHT -> LightColors + ColorTheme.DARK -> DarkColors + }, + fontSizes = FontSizes(), + dimensions = Dimensions(), + typography = NeoTypography( + fontFamily = FontFamily(Font(Res.font.roboto_mono)) + ), + content = content + ) +} + +@Composable +fun NeoBaseTheme( + colorScheme: ColorScheme, fontSizes: FontSizes = FontSizes(), dimensions: Dimensions = Dimensions(), typography: Typography = NeoTypography(), content: @Composable () -> Unit ) { MaterialTheme( - colorScheme = if (darkMode) { - DarkColors - } else { - LightColors - }, + colorScheme = colorScheme, typography = typography, ) { CompositionLocalProvider( diff --git a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt index 7ff613c3..925a050c 100644 --- a/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt +++ b/core/design-system/src/commonMain/kotlin/com/neoutils/neoregex/core/designsystem/theme/Typhography.kt @@ -29,8 +29,8 @@ import com.neoutils.neoregex.core.resources.roboto_mono import org.jetbrains.compose.resources.Font @Composable -internal fun NeoTypography( - fontFamily: FontFamily = FontFamily(Font(Res.font.roboto_mono)) +fun NeoTypography( + fontFamily: FontFamily = FontFamily.Default ) = Typography( displayLarge = TextStyle( fontFamily = fontFamily, diff --git a/core/resources/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml index de0b0229..3a30aa0d 100644 --- a/core/resources/src/commonMain/composeResources/values/strings.xml +++ b/core/resources/src/commonMain/composeResources/values/strings.xml @@ -19,4 +19,10 @@ NeoRegex Enter regular expression + Oops! Something went wrong. + We'd love to fix this! If possible, please share what happened. + Copy error + Report error + Stack trace + Fatal error: %1$s \ No newline at end of file diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt index f097e336..ebd5b0e4 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/DefaultHeader.kt @@ -45,9 +45,8 @@ import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.WindowScope import com.jetbrains.JBR import com.neoutils.neoregex.core.common.util.DragHandler -import com.neoutils.neoregex.core.common.util.UiMode -import com.neoutils.neoregex.core.common.util.isDark -import com.neoutils.neoregex.core.common.util.resolve +import com.neoutils.neoregex.core.common.util.ColorTheme +import com.neoutils.neoregex.core.common.util.rememberColorTheme import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions import com.neoutils.neoregex.core.sharedui.remember.CompleteWindowState import com.neoutils.neoregex.core.sharedui.remember.WindowFocus @@ -60,9 +59,9 @@ import java.awt.event.WindowEvent private val DefaultHeaderHeight = 40.dp @Composable -fun FrameWindowScope.DefaultHeader( +fun FrameWindowScope.NeoHeader( modifier: Modifier = Modifier, - uiMode: UiMode = remember { UiMode.resolve() }, + colorTheme: ColorTheme = rememberColorTheme(), content: @Composable BoxScope.(padding: PaddingValues) -> Unit = {}, ) { @@ -78,9 +77,9 @@ fun FrameWindowScope.DefaultHeader( val density = LocalDensity.current - LaunchedEffect(window, uiMode) { + LaunchedEffect(window, colorTheme) { customTitleBar?.height = density.run { DefaultHeaderHeight.toPx() } - customTitleBar?.putProperty("controls.dark", uiMode.isDark) + customTitleBar?.putProperty("controls.dark", colorTheme.isDark) JBR.windowDecorations?.setCustomTitleBar(window, customTitleBar) } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt new file mode 100644 index 00000000..77dc4601 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt @@ -0,0 +1,116 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.ApplicationScope +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.WindowPosition +import androidx.compose.ui.window.rememberWindowState +import com.neoutils.neoregex.core.resources.Res +import com.neoutils.neoregex.core.resources.fatal_error_title +import com.neoutils.neoregex.core.resources.report_error +import com.neoutils.neoregex.core.resources.stack_trace +import com.neoutils.neoregex.core.sharedui.extension.updateSize +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource + +@Composable +fun ApplicationScope.FatalErrorWindow( + throwable: Throwable +) { + val state = rememberWindowState( + position = WindowPosition.Aligned(Alignment.Center), + size = DpSize(400.dp, 320.dp), + ) + + Window( + onCloseRequest = ::exitApplication, + title = stringResource( + Res.string.fatal_error_title, + throwable::class.java.name + ), + state = state, + resizable = false, + ) { + + Column { + + var current by remember { mutableStateOf(Tab.REPORT_ERROR) } + + LaunchedEffect(current) { + when (current) { + Tab.REPORT_ERROR -> { + state.updateSize(DpSize(400.dp, 320.dp)) + } + + Tab.STACK_TRACE -> { + state.updateSize(DpSize(800.dp, 600.dp)) + } + } + } + + val selectedTabIndex = remember(current) { Tab.entries.indexOf(current) } + + TabRow( + selectedTabIndex = selectedTabIndex, + ) { + Tab.entries.forEach { + Tab( + text = { Text(stringResource(it.title)) }, + selected = current == it, + onClick = { + current = it + } + ) + } + } + + when (current) { + Tab.REPORT_ERROR -> { + ReportScreen( + throwable = throwable, + modifier = Modifier.fillMaxSize() + ) + } + + Tab.STACK_TRACE -> { + StackTraceBox( + throwable = throwable, + modifier = Modifier.fillMaxSize() + ) + } + } + } + } +} + +enum class Tab(val title: StringResource) { + REPORT_ERROR(title = Res.string.report_error), + STACK_TRACE(title = Res.string.stack_trace); +} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt index 39356d52..6feb3435 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt @@ -39,6 +39,7 @@ import com.neoutils.neoregex.core.resources.app_name import com.neoutils.neoregex.core.resources.flavicon import com.neoutils.neoregex.core.sharedui.remember.CompleteWindowState import com.neoutils.neoregex.core.sharedui.remember.rememberCompleteWindowState +import com.neoutils.neoregex.core.sharedui.util.NeoWindowExceptionHandlerFactory import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource @@ -49,9 +50,9 @@ fun ApplicationScope.NeoWindow( title: String = stringResource(Res.string.app_name), undecorated: Boolean = JBR.windowDecorations == null, border: BorderStroke = BorderStroke(1.dp, colorScheme.outline), - exceptionHandlerFactory: WindowExceptionHandlerFactory = DefaultWindowExceptionHandlerFactory, + exceptionHandlerFactory: WindowExceptionHandlerFactory = NeoWindowExceptionHandlerFactory, header: @Composable FrameWindowScope.() -> Unit = { - DefaultHeader { + NeoHeader { Text( text = title, modifier = Modifier.align( diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt new file mode 100644 index 00000000..262b7a25 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt @@ -0,0 +1,99 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ErrorOutline +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions +import com.neoutils.neoregex.core.resources.* +import org.jetbrains.compose.resources.stringResource + +@Composable +fun ReportScreen( + throwable: Throwable, + modifier: Modifier = Modifier +) = Column( + modifier = modifier.padding(dimensions.default), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy( + space = dimensions.small, + alignment = Alignment.Top + ) +) { + + Icon( + Icons.Rounded.ErrorOutline, + contentDescription = null, + modifier = Modifier.size(40.dp), + tint = MaterialTheme.colorScheme.error + ) + + Text( + text = stringResource(Res.string.error_title), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium + ) + + Text( + text = stringResource(Res.string.error_message), + textAlign = TextAlign.Center, + ) + + Spacer(Modifier.weight(weight = 1f)) + + val uriHandler = LocalUriHandler.current + val clipboard = LocalClipboardManager.current + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround + ) { + Button( + onClick = { + clipboard.setText( + AnnotatedString(throwable.stackTraceToString()) + ) + }, + ) { + Text(stringResource(Res.string.copy_error)) + } + + Button( + onClick = { + val title = "Crash:%20${throwable::class.java.name}" + uriHandler.openUri("https://github.com/NeoUtils/NeoRegex/issues/new?title=$title") + }, + ) { + Text(stringResource(Res.string.report_error)) + } + } +} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/StackTraceBox.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/StackTraceBox.kt new file mode 100644 index 00000000..c46d42d9 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/StackTraceBox.kt @@ -0,0 +1,90 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme +import com.neoutils.neoregex.core.sharedui.util.Padding + +@Composable +fun StackTraceBox( + throwable: Throwable, + modifier: Modifier = Modifier +) = Box( + modifier = modifier, +) { + + val stackTrace = remember(throwable) { + throwable.stackTraceToString().trim() + } + + val density = LocalDensity.current + + val verticalScrollState = rememberScrollState() + + val horizontalScrollState = rememberScrollState() + + var padding by remember { mutableStateOf(Padding()) } + + SelectionContainer { + Text( + text = stackTrace, + color = MaterialTheme.colorScheme.error, + modifier = Modifier + .padding(padding.values) + .horizontalScroll(horizontalScrollState) + .verticalScroll(verticalScrollState) + .padding(NeoTheme.dimensions.small) + ) + } + + VerticalScrollbar( + adapter = rememberScrollbarAdapter(verticalScrollState), + modifier = Modifier + .align(Alignment.TopEnd) + .padding(bottom = padding.bottom) + .onSizeChanged { + padding = padding.copy( + end = with(density) { it.width.toDp() }, + ) + } + ) + + HorizontalScrollbar( + adapter = rememberScrollbarAdapter(horizontalScrollState), + modifier = Modifier + .align(Alignment.BottomStart) + .padding(end = padding.end) + .onSizeChanged { + padding = padding.copy( + bottom = with(density) { it.height.toDp() }, + ) + } + ) +} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt new file mode 100644 index 00000000..62169487 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt @@ -0,0 +1,36 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.extension + +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.window.WindowPosition +import androidx.compose.ui.window.WindowState + +fun WindowState.updateSize(newSize: DpSize) { + + val diff = size - newSize + + val newPosition = WindowPosition.Absolute( + x = position.x + diff.width / 2, + y = position.y + diff.height / 2 + ) + + size = newSize + position = newPosition +} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt new file mode 100644 index 00000000..4bc5bd67 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt @@ -0,0 +1,52 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.theme + +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.neoutils.neoregex.core.designsystem.theme.* + +private val LightColors = lightColorScheme( + primary = Gray800, + onPrimary = Color.White, + background = Color.White, + onBackground = Color.Black, + surface = Color.White, + onSurface = Color.Black, + surfaceVariant = Gray200, + onSurfaceVariant = Color.Black, + surfaceTint = Color.White, + surfaceContainer = Gray100, + surfaceBright = Gray300, + secondary = Blue100, + secondaryContainer = Gray300, + onSecondaryContainer = Color.Black, + error = Red700, +) + +@Composable +fun NeoErrorTheme( + content: @Composable () -> Unit +) { + NeoBaseTheme( + colorScheme = LightColors, + content = content + ) +} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt new file mode 100644 index 00000000..86b68399 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt @@ -0,0 +1,54 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.util + +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.* +import com.neoutils.neoregex.core.sharedui.component.FatalErrorWindow +import com.neoutils.neoregex.core.sharedui.theme.NeoErrorTheme +import kotlinx.coroutines.CoroutineScope +import org.jetbrains.skiko.MainUIDispatcher +import java.awt.event.WindowEvent +import javax.swing.SwingUtilities +import java.awt.Window as AwtWindows + +private val mainScope = CoroutineScope(MainUIDispatcher) + +@OptIn(ExperimentalComposeUiApi::class) +object NeoWindowExceptionHandlerFactory : WindowExceptionHandlerFactory { + + override fun exceptionHandler(window: AwtWindows) = WindowExceptionHandler { throwable -> + + SwingUtilities.invokeLater { + mainScope.launchApplication { + NeoErrorTheme { + FatalErrorWindow(throwable) + } + } + + window.dispatchEvent( + WindowEvent( + window, WindowEvent.WINDOW_CLOSING + ) + ) + } + + throw throwable + } +} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt new file mode 100644 index 00000000..5459e258 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt @@ -0,0 +1,37 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu . Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.util + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +data class Padding( + val start: Dp = 0.dp, + val top: Dp = 0.dp, + val end: Dp = 0.dp, + val bottom: Dp = 0.dp +) { + val values = PaddingValues( + top = top, + start = start, + end = end, + bottom = bottom, + ) +} \ No newline at end of file From 0778f1f45af3b8ea219ebec62f9155185f9a94d8 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Wed, 6 Nov 2024 19:54:25 -0300 Subject: [PATCH 11/18] feat(desktop): improve copy feedback and remove resize --- .../composeResources/values/strings.xml | 1 + .../sharedui/component/FatalErrorWindow.kt | 36 ++--- .../core/sharedui/component/NeoWindow.kt | 4 +- .../core/sharedui/component/ReportScreen.kt | 142 ++++++++++++------ .../core/sharedui/extension/WindowState.kt | 36 ----- .../core/sharedui/theme/NeoErrorTheme.kt | 1 + ... NeoRegexWindowExceptionHandlerFactory.kt} | 17 ++- 7 files changed, 116 insertions(+), 121 deletions(-) delete mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt rename core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/{NeoWindowExceptionHandlerFactory.kt => NeoRegexWindowExceptionHandlerFactory.kt} (76%) diff --git a/core/resources/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml index 3a30aa0d..f385dc1d 100644 --- a/core/resources/src/commonMain/composeResources/values/strings.xml +++ b/core/resources/src/commonMain/composeResources/values/strings.xml @@ -22,6 +22,7 @@ Oops! Something went wrong. We'd love to fix this! If possible, please share what happened. Copy error + Copied! Report error Stack trace Fatal error: %1$s diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt index 77dc4601..577bbbf4 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt @@ -28,15 +28,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.ApplicationScope -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.WindowPosition -import androidx.compose.ui.window.rememberWindowState +import androidx.compose.ui.window.* import com.neoutils.neoregex.core.resources.Res import com.neoutils.neoregex.core.resources.fatal_error_title import com.neoutils.neoregex.core.resources.report_error import com.neoutils.neoregex.core.resources.stack_trace -import com.neoutils.neoregex.core.sharedui.extension.updateSize import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource @@ -44,48 +40,34 @@ import org.jetbrains.compose.resources.stringResource fun ApplicationScope.FatalErrorWindow( throwable: Throwable ) { - val state = rememberWindowState( + val state = rememberDialogState( position = WindowPosition.Aligned(Alignment.Center), - size = DpSize(400.dp, 320.dp), + size = DpSize(500.dp, 400.dp) ) - Window( + DialogWindow( onCloseRequest = ::exitApplication, title = stringResource( Res.string.fatal_error_title, throwable::class.java.name ), state = state, - resizable = false, ) { - - Column { + Column(Modifier.fillMaxSize()) { var current by remember { mutableStateOf(Tab.REPORT_ERROR) } - LaunchedEffect(current) { - when (current) { - Tab.REPORT_ERROR -> { - state.updateSize(DpSize(400.dp, 320.dp)) - } - - Tab.STACK_TRACE -> { - state.updateSize(DpSize(800.dp, 600.dp)) - } - } - } - val selectedTabIndex = remember(current) { Tab.entries.indexOf(current) } TabRow( selectedTabIndex = selectedTabIndex, ) { - Tab.entries.forEach { + Tab.entries.forEach { tab -> Tab( - text = { Text(stringResource(it.title)) }, - selected = current == it, + text = { Text(stringResource(tab.title)) }, + selected = current == tab, onClick = { - current = it + current = tab } ) } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt index 6feb3435..ba74bed4 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/NeoWindow.kt @@ -39,7 +39,7 @@ import com.neoutils.neoregex.core.resources.app_name import com.neoutils.neoregex.core.resources.flavicon import com.neoutils.neoregex.core.sharedui.remember.CompleteWindowState import com.neoutils.neoregex.core.sharedui.remember.rememberCompleteWindowState -import com.neoutils.neoregex.core.sharedui.util.NeoWindowExceptionHandlerFactory +import com.neoutils.neoregex.core.sharedui.util.NeoRegexWindowExceptionHandlerFactory import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource @@ -50,7 +50,7 @@ fun ApplicationScope.NeoWindow( title: String = stringResource(Res.string.app_name), undecorated: Boolean = JBR.windowDecorations == null, border: BorderStroke = BorderStroke(1.dp, colorScheme.outline), - exceptionHandlerFactory: WindowExceptionHandlerFactory = NeoWindowExceptionHandlerFactory, + exceptionHandlerFactory: WindowExceptionHandlerFactory = NeoRegexWindowExceptionHandlerFactory, header: @Composable FrameWindowScope.() -> Unit = { NeoHeader { Text( diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt index 262b7a25..a965c99f 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt @@ -18,14 +18,15 @@ package com.neoutils.neoregex.core.sharedui.component +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ErrorOutline -import androidx.compose.material3.Button -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager @@ -35,65 +36,108 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions import com.neoutils.neoregex.core.resources.* +import kotlinx.coroutines.delay import org.jetbrains.compose.resources.stringResource @Composable fun ReportScreen( throwable: Throwable, modifier: Modifier = Modifier -) = Column( - modifier = modifier.padding(dimensions.default), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy( - space = dimensions.small, - alignment = Alignment.Top - ) -) { - - Icon( - Icons.Rounded.ErrorOutline, - contentDescription = null, - modifier = Modifier.size(40.dp), - tint = MaterialTheme.colorScheme.error - ) +) = Scaffold( + modifier = modifier, + bottomBar = { + BottomButtons( + throwable = throwable, + modifier = Modifier + .padding(bottom = dimensions.default) + .fillMaxWidth() + ) + } +) { contentPadding -> + Column( + modifier = Modifier + .padding(contentPadding) + .padding(dimensions.default) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy( + space = dimensions.small, + alignment = Alignment.CenterVertically + ) + ) { + Icon( + Icons.Rounded.ErrorOutline, + contentDescription = null, + modifier = Modifier.size(40.dp), + tint = MaterialTheme.colorScheme.error + ) - Text( - text = stringResource(Res.string.error_title), - textAlign = TextAlign.Center, - style = MaterialTheme.typography.titleMedium - ) + Text( + text = stringResource(Res.string.error_title), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleMedium + ) - Text( - text = stringResource(Res.string.error_message), - textAlign = TextAlign.Center, - ) + Text( + text = stringResource(Res.string.error_message), + textAlign = TextAlign.Center, + ) + } +} - Spacer(Modifier.weight(weight = 1f)) +@Composable +private fun BottomButtons( + throwable: Throwable, + modifier: Modifier = Modifier +) = Row( + modifier = modifier, + horizontalArrangement = Arrangement.SpaceAround +) { val uriHandler = LocalUriHandler.current val clipboard = LocalClipboardManager.current - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceAround - ) { - Button( - onClick = { - clipboard.setText( - AnnotatedString(throwable.stackTraceToString()) - ) - }, - ) { - Text(stringResource(Res.string.copy_error)) + var copied by remember { mutableStateOf(false) } + + LaunchedEffect(copied) { + if (copied) { + delay(timeMillis = 1_000) + copied = false } + } - Button( - onClick = { - val title = "Crash:%20${throwable::class.java.name}" - uriHandler.openUri("https://github.com/NeoUtils/NeoRegex/issues/new?title=$title") - }, - ) { - Text(stringResource(Res.string.report_error)) + Button( + onClick = { + clipboard.setText( + AnnotatedString( + throwable.stackTraceToString() + ) + ) + copied = true + }, + modifier = Modifier.width(120.dp) + ) { + AnimatedContent( + targetState = copied, + transitionSpec = { + fadeIn() togetherWith fadeOut() + } + ) { copied -> + if (copied) { + Text(stringResource(Res.string.copied)) + } else { + Text(stringResource(Res.string.copy_error)) + } } } + + Button( + onClick = { + uriHandler.openUri( + uri = "https://github.com/NeoUtils/NeoRegex/issues/new?template=bug_report.md" + ) + }, + ) { + Text(stringResource(Res.string.report_error)) + } } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt deleted file mode 100644 index 62169487..00000000 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/extension/WindowState.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * NeoRegex. - * - * Copyright (C) 2024 Irineu A. Silva. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.neoutils.neoregex.core.sharedui.extension - -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.window.WindowPosition -import androidx.compose.ui.window.WindowState - -fun WindowState.updateSize(newSize: DpSize) { - - val diff = size - newSize - - val newPosition = WindowPosition.Absolute( - x = position.x + diff.width / 2, - y = position.y + diff.height / 2 - ) - - size = newSize - position = newPosition -} diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt index 4bc5bd67..ffc69238 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/theme/NeoErrorTheme.kt @@ -35,6 +35,7 @@ private val LightColors = lightColorScheme( surfaceTint = Color.White, surfaceContainer = Gray100, surfaceBright = Gray300, + inverseSurface = Gray800, secondary = Blue100, secondaryContainer = Gray300, onSecondaryContainer = Color.Black, diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoRegexWindowExceptionHandlerFactory.kt similarity index 76% rename from core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt rename to core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoRegexWindowExceptionHandlerFactory.kt index 86b68399..2a265b68 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoWindowExceptionHandlerFactory.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/NeoRegexWindowExceptionHandlerFactory.kt @@ -19,7 +19,9 @@ package com.neoutils.neoregex.core.sharedui.util import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.* +import androidx.compose.ui.window.WindowExceptionHandler +import androidx.compose.ui.window.WindowExceptionHandlerFactory +import androidx.compose.ui.window.launchApplication import com.neoutils.neoregex.core.sharedui.component.FatalErrorWindow import com.neoutils.neoregex.core.sharedui.theme.NeoErrorTheme import kotlinx.coroutines.CoroutineScope @@ -28,17 +30,18 @@ import java.awt.event.WindowEvent import javax.swing.SwingUtilities import java.awt.Window as AwtWindows -private val mainScope = CoroutineScope(MainUIDispatcher) - @OptIn(ExperimentalComposeUiApi::class) -object NeoWindowExceptionHandlerFactory : WindowExceptionHandlerFactory { +object NeoRegexWindowExceptionHandlerFactory : WindowExceptionHandlerFactory { override fun exceptionHandler(window: AwtWindows) = WindowExceptionHandler { throwable -> SwingUtilities.invokeLater { - mainScope.launchApplication { - NeoErrorTheme { - FatalErrorWindow(throwable) + + with(CoroutineScope(MainUIDispatcher)) { + launchApplication { + NeoErrorTheme { + FatalErrorWindow(throwable) + } } } From ec1ebe3b12d9f9472c9ac92bced3e74f4d65aa90 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Thu, 7 Nov 2024 21:35:42 -0300 Subject: [PATCH 12/18] refactor(desktop): small improvements --- .../com/neoutils/neoregex/Main.desktop.kt | 2 +- .../kotlin/com/neoutils/neoregex/Main.web.kt | 2 +- .../core/common/util/ColorTheme.android.kt | 4 ++-- .../core/common/util/ColorTheme.desktop.kt | 4 ++-- .../core/common/util/ColorTheme.web.kt | 4 ++-- .../composeResources/values/strings.xml | 19 +++++++++++-------- .../sharedui/component/FatalErrorWindow.kt | 14 +++++++++----- .../core/sharedui/component/ReportScreen.kt | 11 ++++++----- .../neoregex/core/sharedui/util/Padding.kt | 2 +- .../neoregex/feature/matcher/MatcherScreen.kt | 4 ++-- 10 files changed, 37 insertions(+), 29 deletions(-) diff --git a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt index 97dcd74f..4bf1350b 100644 --- a/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt +++ b/application/src/desktopMain/kotlin/com/neoutils/neoregex/Main.desktop.kt @@ -107,4 +107,4 @@ private fun FrameWindowScope.Header( .aspectRatio(ratio = 1f) ) } -} \ No newline at end of file +} diff --git a/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt b/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt index 1594dbe9..ea4bd449 100644 --- a/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt +++ b/application/src/webMain/kotlin/com/neoutils/neoregex/Main.web.kt @@ -45,7 +45,7 @@ fun main() { sizeManager.changes.first() } ) { - NeoTheme(ColorTheme) { + NeoTheme { body.style.backgroundColor = colorScheme.background.toCss() diff --git a/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt b/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt index 23904d65..615a0b1b 100644 --- a/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt +++ b/core/common/src/androidMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.android.kt @@ -1,7 +1,7 @@ /* * NeoRegex. * - * Copyright (C) 2024 . + * Copyright (C) 2024 Irineu A. Silva. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,4 +43,4 @@ val Context.colorTheme else -> { ColorTheme.LIGHT } - } \ No newline at end of file + } diff --git a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt index 276f86f4..8304fd77 100644 --- a/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt +++ b/core/common/src/desktopMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.desktop.kt @@ -1,7 +1,7 @@ /* * NeoRegex. * - * Copyright (C) 2024 . + * Copyright (C) 2024 Irineu A. Silva. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,4 +43,4 @@ actual fun rememberColorTheme(): ColorTheme { } } } -} \ No newline at end of file +} diff --git a/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt b/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt index d1c1641c..a41cef72 100644 --- a/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt +++ b/core/common/src/webMain/kotlin/com/neoutils/neoregex/core/common/util/ColorTheme.web.kt @@ -1,7 +1,7 @@ /* * NeoRegex. * - * Copyright (C) 2024 . + * Copyright (C) 2024 Irineu A. Silva. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,4 +30,4 @@ actual fun rememberColorTheme(): ColorTheme { SystemTheme.DARK -> ColorTheme.DARK SystemTheme.UNKNOWN -> ColorTheme.LIGHT } -} \ No newline at end of file +} diff --git a/core/resources/src/commonMain/composeResources/values/strings.xml b/core/resources/src/commonMain/composeResources/values/strings.xml index f385dc1d..e356ff7d 100644 --- a/core/resources/src/commonMain/composeResources/values/strings.xml +++ b/core/resources/src/commonMain/composeResources/values/strings.xml @@ -18,12 +18,15 @@ NeoRegex - Enter regular expression - Oops! Something went wrong. - We'd love to fix this! If possible, please share what happened. - Copy error - Copied! - Report error - Stack trace + + Enter regular expression + Fatal error: %1$s - \ No newline at end of file + Oops! Something went wrong. + We'd love to fix this! If possible, please share what happened. + Copy error + Copied! + Report error + Report error + Stack trace + diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt index 577bbbf4..2878598e 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/FatalErrorWindow.kt @@ -20,19 +20,22 @@ package com.neoutils.neoregex.core.sharedui.component import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.twotone.Error import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.window.* import com.neoutils.neoregex.core.resources.Res +import com.neoutils.neoregex.core.resources.fatal_error_report_tab +import com.neoutils.neoregex.core.resources.fatal_error_stack_trace_tab import com.neoutils.neoregex.core.resources.fatal_error_title -import com.neoutils.neoregex.core.resources.report_error -import com.neoutils.neoregex.core.resources.stack_trace import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource @@ -47,11 +50,12 @@ fun ApplicationScope.FatalErrorWindow( DialogWindow( onCloseRequest = ::exitApplication, + state = state, title = stringResource( Res.string.fatal_error_title, throwable::class.java.name ), - state = state, + icon = rememberVectorPainter(Icons.TwoTone.Error) ) { Column(Modifier.fillMaxSize()) { @@ -93,6 +97,6 @@ fun ApplicationScope.FatalErrorWindow( } enum class Tab(val title: StringResource) { - REPORT_ERROR(title = Res.string.report_error), - STACK_TRACE(title = Res.string.stack_trace); + REPORT_ERROR(title = Res.string.fatal_error_report_tab), + STACK_TRACE(title = Res.string.fatal_error_stack_trace_tab); } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt index a965c99f..612d95a2 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/ReportScreen.kt @@ -73,13 +73,13 @@ fun ReportScreen( ) Text( - text = stringResource(Res.string.error_title), + text = stringResource(Res.string.fatal_error_subtitle), textAlign = TextAlign.Center, style = MaterialTheme.typography.titleMedium ) Text( - text = stringResource(Res.string.error_message), + text = stringResource(Res.string.fatal_error_message), textAlign = TextAlign.Center, ) } @@ -115,6 +115,7 @@ private fun BottomButtons( ) copied = true }, + enabled = !copied, modifier = Modifier.width(120.dp) ) { AnimatedContent( @@ -124,9 +125,9 @@ private fun BottomButtons( } ) { copied -> if (copied) { - Text(stringResource(Res.string.copied)) + Text(stringResource(Res.string.fatal_error_copied_label)) } else { - Text(stringResource(Res.string.copy_error)) + Text(stringResource(Res.string.fatal_error_copy_btn)) } } } @@ -138,6 +139,6 @@ private fun BottomButtons( ) }, ) { - Text(stringResource(Res.string.report_error)) + Text(stringResource(Res.string.fatal_error_report_btn)) } } diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt index 5459e258..a342aa91 100644 --- a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/util/Padding.kt @@ -34,4 +34,4 @@ data class Padding( end = end, bottom = bottom, ) -} \ No newline at end of file +} diff --git a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt index 29b917e3..eed1fca6 100644 --- a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt +++ b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt @@ -60,7 +60,7 @@ import com.neoutils.neoregex.feature.matcher.state.matches import com.neoutils.neoregex.core.resources.Res import com.neoutils.neoregex.core.resources.ic_redo_24 import com.neoutils.neoregex.core.resources.ic_undo_24 -import com.neoutils.neoregex.core.resources.insert_regex_hint +import com.neoutils.neoregex.core.resources.matcher_footer_insert_regex_hint import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource @@ -178,7 +178,7 @@ class MatcherScreen : Screen { else -> false } }, - hint = stringResource(Res.string.insert_regex_hint), + hint = stringResource(Res.string.matcher_footer_insert_regex_hint), error = uiState.matchResult.error ) From d26df0ce8a3ebeaf9dc373721d2735d4a8b1ec7c Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Fri, 8 Nov 2024 10:32:58 -0300 Subject: [PATCH 13/18] refactor(desktop & android): extract match infos --- .../sharedui/component/MatchResult.android.kt | 64 +++++++++++++++++++ .../core/sharedui/component/MatchesResult.kt | 31 +++++++++ .../sharedui/component/MatchResult.desktop.kt | 63 ++++++++++++++++++ .../sharedui/component/MatchResult.web.kt | 33 ++++++++++ .../neoregex/feature/matcher/MatcherScreen.kt | 33 ++-------- 5 files changed, 196 insertions(+), 28 deletions(-) create mode 100644 core/shared-ui/src/androidMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.android.kt create mode 100644 core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchesResult.kt create mode 100644 core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.desktop.kt create mode 100644 core/shared-ui/src/webMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.web.kt diff --git a/core/shared-ui/src/androidMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.android.kt b/core/shared-ui/src/androidMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.android.kt new file mode 100644 index 00000000..8a387408 --- /dev/null +++ b/core/shared-ui/src/androidMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.android.kt @@ -0,0 +1,64 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.fontSizes +import com.neoutils.neoregex.core.resources.Res +import com.neoutils.neoregex.core.resources.match_result_infos +import org.jetbrains.compose.resources.pluralStringResource +import kotlin.time.Duration +import kotlin.time.DurationUnit + +@Composable +actual fun BoxScope.MatchesResult( + duration: Duration, + matches: Int, + modifier: Modifier +) = Text( + text = pluralStringResource( + Res.plurals.match_result_infos, + matches, matches, + duration.toString( + unit = DurationUnit.MILLISECONDS, + decimals = 3 + ) + ), + fontSize = fontSizes.tiny, + style = typography.labelSmall, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(4.dp) + .background( + color = colorScheme.surfaceVariant, + shape = RoundedCornerShape(dimensions.tiny) + ) + .padding(2.dp) +) \ No newline at end of file diff --git a/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchesResult.kt b/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchesResult.kt new file mode 100644 index 00000000..0da65b23 --- /dev/null +++ b/core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchesResult.kt @@ -0,0 +1,31 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import kotlin.time.Duration + +@Composable +expect fun BoxScope.MatchesResult( + duration: Duration, + matches: Int, + modifier: Modifier = Modifier +) \ No newline at end of file diff --git a/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.desktop.kt b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.desktop.kt new file mode 100644 index 00000000..31f99657 --- /dev/null +++ b/core/shared-ui/src/desktopMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.desktop.kt @@ -0,0 +1,63 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions +import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.fontSizes +import com.neoutils.neoregex.core.resources.Res +import com.neoutils.neoregex.core.resources.match_result_infos +import org.jetbrains.compose.resources.pluralStringResource +import kotlin.time.Duration +import kotlin.time.DurationUnit + +@Composable +actual fun BoxScope.MatchesResult( + duration: Duration, + matches: Int, + modifier: Modifier +) = Text( + text = pluralStringResource( + Res.plurals.match_result_infos, + matches, matches, + duration.toString( + unit = DurationUnit.MILLISECONDS, + decimals = 3 + ) + ), + fontSize = fontSizes.tiny, + style = typography.labelSmall, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(4.dp) + .background( + color = colorScheme.surfaceVariant, + shape = RoundedCornerShape(dimensions.tiny) + ).padding(2.dp) +) \ No newline at end of file diff --git a/core/shared-ui/src/webMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.web.kt b/core/shared-ui/src/webMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.web.kt new file mode 100644 index 00000000..9047d1a0 --- /dev/null +++ b/core/shared-ui/src/webMain/kotlin/com/neoutils/neoregex/core/sharedui/component/MatchResult.web.kt @@ -0,0 +1,33 @@ +/* + * NeoRegex. + * + * Copyright (C) 2024 Irineu A. Silva. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.neoutils.neoregex.core.sharedui.component + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import kotlin.time.Duration + +@Composable +actual fun BoxScope.MatchesResult( + duration: Duration, + matches: Int, + modifier: Modifier +) { + // TODO: don't support +} \ No newline at end of file diff --git a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt index 2d972a47..e59a7d5d 100644 --- a/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt +++ b/feature/matcher/src/commonMain/kotlin/com/neoutils/neoregex/feature/matcher/MatcherScreen.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.material3.MaterialTheme.colorScheme -import androidx.compose.material3.MaterialTheme.typography import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -43,13 +42,11 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.screen.Screen -import com.neoutils.neoregex.core.common.platform.Platform -import com.neoutils.neoregex.core.common.platform.platform import com.neoutils.neoregex.core.common.util.Command import com.neoutils.neoregex.core.designsystem.textfield.NeoTextField import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.dimensions -import com.neoutils.neoregex.core.designsystem.theme.NeoTheme.fontSizes import com.neoutils.neoregex.core.resources.* +import com.neoutils.neoregex.core.sharedui.component.MatchesResult import com.neoutils.neoregex.core.sharedui.component.TextEditor import com.neoutils.neoregex.feature.matcher.action.MatcherAction import com.neoutils.neoregex.feature.matcher.extension.onLongHold @@ -60,9 +57,7 @@ import com.neoutils.neoregex.feature.matcher.state.duration import com.neoutils.neoregex.feature.matcher.state.error import com.neoutils.neoregex.feature.matcher.state.matches import org.jetbrains.compose.resources.painterResource -import org.jetbrains.compose.resources.pluralStringResource import org.jetbrains.compose.resources.stringResource -import kotlin.time.DurationUnit class MatcherScreen : Screen { @@ -119,28 +114,10 @@ class MatcherScreen : Screen { }, ) - if (platform != Platform.Web) { - Text( - text = pluralStringResource( - Res.plurals.match_result_infos, - uiState.matchResult.matches.size, - uiState.matchResult.matches.size, - uiState.matchResult.duration.toString( - unit = DurationUnit.MILLISECONDS, - decimals = 3 - ) - ), - fontSize = fontSizes.tiny, - style = typography.labelSmall, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(4.dp) - .background( - color = colorScheme.surfaceVariant, - shape = RoundedCornerShape(dimensions.tiny) - ).padding(2.dp) - ) - } + MatchesResult( + duration = uiState.matchResult.duration, + matches = uiState.matchResult.matches.size, + ) } Footer( From fc1dfac6191672fede8ec7474343438e6183b547 Mon Sep 17 00:00:00 2001 From: "Irineu A. Silva" Date: Fri, 8 Nov 2024 12:07:25 -0300 Subject: [PATCH 14/18] feat(desktop, match-infos): drag and drop support --- .idea/inspectionProfiles/Project_Default.xml | 24 +++ .../sharedui/component/MatchResult.android.kt | 2 +- .../{MatchesResult.kt => MatchesInfos.kt} | 2 +- .../sharedui/component/MatchResult.desktop.kt | 165 +++++++++++++++--- .../sharedui/component/MatchResult.web.kt | 2 +- .../neoregex/feature/matcher/MatcherScreen.kt | 4 +- 6 files changed, 171 insertions(+), 28 deletions(-) rename core/shared-ui/src/commonMain/kotlin/com/neoutils/neoregex/core/sharedui/component/{MatchesResult.kt => MatchesInfos.kt} (96%) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 44ca2d9b..b67486ec 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,30 @@