diff --git a/examples/README.md b/examples/README.md index 9897c6a551d..72e5ae0f1d4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,5 +8,4 @@ | [Todoapp Lite](todoapp-lite) | A simple todo app fully based on Compose | Android, iOS, Desktop | | [Nav Cupcake](nav_cupcake) | Multiscreen app to demonstrate the use of Compose Navigation | Android, iOS, Desktop | | [Issues tracker](issues) | GitHub issue tracker with an adaptive UI and ktor-client | Android, Desktop | -| [Notepad](notepad) | Notepad, using the Composable Window API | Desktop | | [HTML based samples](html/README.md) | Examples written with Compose HTML Library | diff --git a/examples/notepad/.gitignore b/examples/notepad/.gitignore deleted file mode 100644 index ba8435b9c5c..00000000000 --- a/examples/notepad/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -build/ -/captures -.externalNativeBuild -.cxx \ No newline at end of file diff --git a/examples/notepad/.run/desktop.run.xml b/examples/notepad/.run/desktop.run.xml deleted file mode 100644 index 4f432247113..00000000000 --- a/examples/notepad/.run/desktop.run.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - true - - - \ No newline at end of file diff --git a/examples/notepad/README.md b/examples/notepad/README.md deleted file mode 100644 index 15b106a9f58..00000000000 --- a/examples/notepad/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Notepad example for desktop written in Compose for Desktop library, using Composable Window API - -### Running desktop application -* To run, launch command: `./gradlew run` -* Or choose **desktop** configuration in IDE and run it. - ![desktop-run-configuration.png](screenshots/desktop-run-configuration.png) - -### Building native desktop distribution -``` -./gradlew packageDistributionForCurrentOS -# outputs are written to build/compose/binaries -``` -![Desktop](screenshots/notepad.gif) \ No newline at end of file diff --git a/examples/notepad/build.gradle.kts b/examples/notepad/build.gradle.kts deleted file mode 100644 index 258ba6c619e..00000000000 --- a/examples/notepad/build.gradle.kts +++ /dev/null @@ -1,37 +0,0 @@ -import org.jetbrains.compose.desktop.application.dsl.TargetFormat - -plugins { - kotlin("jvm") - kotlin("plugin.compose") - id("org.jetbrains.compose") -} - -repositories { - google() - mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") -} - -dependencies { - implementation(compose.desktop.currentOs) - implementation(compose.materialIconsExtended) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.8.0") -} - -compose.desktop { - application { - mainClass = "MainKt" - - nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) - packageName = "Notepad" - packageVersion = "1.0.0" - - windows { - menu = true - // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html - upgradeUuid = "61DAB35E-17CB-43B0-81D5-B30E1C0830FA" - } - } - } -} diff --git a/examples/notepad/gradle.properties b/examples/notepad/gradle.properties deleted file mode 100644 index fd34d88378e..00000000000 --- a/examples/notepad/gradle.properties +++ /dev/null @@ -1,6 +0,0 @@ -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -kotlin.code.style=official -kotlin.version=2.1.20 -compose.version=1.8.2 -org.gradle.configuration-cache=true -org.gradle.caching=true diff --git a/examples/notepad/gradle/wrapper/gradle-wrapper.jar b/examples/notepad/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f09..00000000000 Binary files a/examples/notepad/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/examples/notepad/gradle/wrapper/gradle-wrapper.properties b/examples/notepad/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 17655d0ef2b..00000000000 --- a/examples/notepad/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/examples/notepad/gradlew b/examples/notepad/gradlew deleted file mode 100755 index a69d9cb6c20..00000000000 --- a/examples/notepad/gradlew +++ /dev/null @@ -1,240 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/examples/notepad/gradlew.bat b/examples/notepad/gradlew.bat deleted file mode 100644 index f127cfd49d4..00000000000 --- a/examples/notepad/gradlew.bat +++ /dev/null @@ -1,91 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/examples/notepad/screenshots/desktop-run-configuration.png b/examples/notepad/screenshots/desktop-run-configuration.png deleted file mode 100644 index 3688407c6f7..00000000000 Binary files a/examples/notepad/screenshots/desktop-run-configuration.png and /dev/null differ diff --git a/examples/notepad/screenshots/notepad.gif b/examples/notepad/screenshots/notepad.gif deleted file mode 100644 index f289318857a..00000000000 Binary files a/examples/notepad/screenshots/notepad.gif and /dev/null differ diff --git a/examples/notepad/settings.gradle.kts b/examples/notepad/settings.gradle.kts deleted file mode 100644 index 377c453a535..00000000000 --- a/examples/notepad/settings.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") - } - - plugins { - kotlin("jvm").version(extra["kotlin.version"] as String) - kotlin("plugin.compose").version(extra["kotlin.version"] as String) - id("org.jetbrains.compose").version(extra["compose.version"] as String) - } -} diff --git a/examples/notepad/src/main/kotlin/NotepadApplication.kt b/examples/notepad/src/main/kotlin/NotepadApplication.kt deleted file mode 100644 index 1d29567ff38..00000000000 --- a/examples/notepad/src/main/kotlin/NotepadApplication.kt +++ /dev/null @@ -1,42 +0,0 @@ -import androidx.compose.runtime.Composable -import androidx.compose.runtime.key -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.window.ApplicationScope -import androidx.compose.ui.window.MenuScope -import androidx.compose.ui.window.Tray -import common.LocalAppResources -import kotlinx.coroutines.launch -import window.NotepadWindow - -@Composable -fun ApplicationScope.NotepadApplication(state: NotepadApplicationState) { - if (state.settings.isTrayEnabled && state.windows.isNotEmpty()) { - ApplicationTray(state) - } - - for (window in state.windows) { - key(window) { - NotepadWindow(window) - } - } -} - -@Composable -private fun ApplicationScope.ApplicationTray(state: NotepadApplicationState) { - Tray( - LocalAppResources.current.icon, - state = state.tray, - tooltip = "Notepad", - menu = { ApplicationMenu(state) } - ) -} - -@Composable -private fun MenuScope.ApplicationMenu(state: NotepadApplicationState) { - val scope = rememberCoroutineScope() - fun exit() = scope.launch { state.exit() } - - Item("New", onClick = state::newWindow) - Separator() - Item("Exit", onClick = { exit() }) -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/NotepadApplicationState.kt b/examples/notepad/src/main/kotlin/NotepadApplicationState.kt deleted file mode 100644 index b8a1c29d345..00000000000 --- a/examples/notepad/src/main/kotlin/NotepadApplicationState.kt +++ /dev/null @@ -1,45 +0,0 @@ -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember -import androidx.compose.ui.window.Notification -import androidx.compose.ui.window.TrayState -import common.Settings -import window.NotepadWindowState - -@Composable -fun rememberApplicationState() = remember { - NotepadApplicationState().apply { - newWindow() - } -} - -class NotepadApplicationState { - val settings = Settings() - val tray = TrayState() - - private val _windows = mutableStateListOf() - val windows: List get() = _windows - - fun newWindow() { - _windows.add( - NotepadWindowState( - application = this, - path = null, - exit = _windows::remove - ) - ) - } - - fun sendNotification(notification: Notification) { - tray.sendNotification(notification) - } - - suspend fun exit() { - val windowsCopy = windows.reversed() - for (window in windowsCopy) { - if (!window.exit()) { - break - } - } - } -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/common/AppResources.kt b/examples/notepad/src/main/kotlin/common/AppResources.kt deleted file mode 100644 index 865e53e0403..00000000000 --- a/examples/notepad/src/main/kotlin/common/AppResources.kt +++ /dev/null @@ -1,39 +0,0 @@ -package common - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Description -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.RenderVectorGroup -import androidx.compose.ui.graphics.vector.VectorPainter -import androidx.compose.ui.graphics.vector.rememberVectorPainter - -val LocalAppResources = staticCompositionLocalOf { - error("LocalNotepadResources isn't provided") -} - -@Composable -fun rememberAppResources(): AppResources { - val icon = rememberVectorPainter(Icons.Default.Description, tintColor = Color(0xFF2CA4E1)) - return remember { AppResources(icon) } -} - -class AppResources(val icon: VectorPainter) - -@Composable -fun rememberVectorPainter(image: ImageVector, tintColor: Color) = - rememberVectorPainter( - defaultWidth = image.defaultWidth, - defaultHeight = image.defaultHeight, - viewportWidth = image.viewportWidth, - viewportHeight = image.viewportHeight, - name = image.name, - tintColor = tintColor, - tintBlendMode = image.tintBlendMode, - autoMirror = false, - content = { _, _ -> RenderVectorGroup(group = image.root) } - ) diff --git a/examples/notepad/src/main/kotlin/common/Settings.kt b/examples/notepad/src/main/kotlin/common/Settings.kt deleted file mode 100644 index 4576f060d11..00000000000 --- a/examples/notepad/src/main/kotlin/common/Settings.kt +++ /dev/null @@ -1,14 +0,0 @@ -package common - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue - -class Settings { - var isTrayEnabled by mutableStateOf(true) - private set - - fun toggleTray() { - isTrayEnabled = !isTrayEnabled - } -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/main.kt b/examples/notepad/src/main/kotlin/main.kt deleted file mode 100644 index c39a9258b0d..00000000000 --- a/examples/notepad/src/main/kotlin/main.kt +++ /dev/null @@ -1,10 +0,0 @@ -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.window.application -import common.LocalAppResources -import common.rememberAppResources - -fun main() = application { - CompositionLocalProvider(LocalAppResources provides rememberAppResources()) { - NotepadApplication(rememberApplicationState()) - } -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/util/Compose.kt b/examples/notepad/src/main/kotlin/util/Compose.kt deleted file mode 100644 index 61eda384546..00000000000 --- a/examples/notepad/src/main/kotlin/util/Compose.kt +++ /dev/null @@ -1,59 +0,0 @@ -package util - -import androidx.compose.runtime.Applier -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Composition -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.MonotonicFrameClock -import androidx.compose.runtime.withRunningRecomposer -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.LayoutDirection -import kotlinx.coroutines.withContext -import kotlinx.coroutines.yield - -/** - * Helper function that allows to use Composable functions that return value in non-composable scope - */ -@Suppress("UNCHECKED_CAST") -suspend fun compose(content: @Composable () -> T): T { - var result: Any? = Unit - withContext(YieldFrameClock) { - withRunningRecomposer { recomposer -> - val composition = Composition(UnitApplier(), recomposer) - composition.setContent { - val density = Density(1f) - val layoutDirection = LayoutDirection.Ltr - CompositionLocalProvider( - LocalDensity provides density, - LocalLayoutDirection provides layoutDirection, - ) { - result = content() - } - } - } - } - return result as T -} - -private object YieldFrameClock : MonotonicFrameClock { - override suspend fun withFrameNanos( - onFrame: (frameTimeNanos: Long) -> R - ): R { - yield() - return onFrame(System.nanoTime()) - } -} - -private class UnitApplier : Applier { - override val current: Unit = Unit - override fun down(node: Unit) = Unit - override fun up() = Unit - override fun insertTopDown(index: Int, instance: Unit) = Unit - override fun insertBottomUp(index: Int, instance: Unit) = Unit - override fun remove(index: Int, count: Int) = Unit - override fun move(from: Int, to: Int, count: Int) = Unit - override fun clear() = Unit - override fun onEndChanges() = Unit -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/util/Dialogs.kt b/examples/notepad/src/main/kotlin/util/Dialogs.kt deleted file mode 100644 index 884556c54fe..00000000000 --- a/examples/notepad/src/main/kotlin/util/Dialogs.kt +++ /dev/null @@ -1,72 +0,0 @@ -package util - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.AwtWindow -import androidx.compose.ui.window.FrameWindowScope -import androidx.compose.ui.window.WindowScope -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.swing.Swing -import java.awt.FileDialog -import java.io.File -import java.nio.file.Path -import javax.swing.JOptionPane - -@Composable -fun FrameWindowScope.FileDialog( - title: String, - isLoad: Boolean, - onResult: (result: Path?) -> Unit -) = AwtWindow( - create = { - object : FileDialog(window, "Choose a file", if (isLoad) LOAD else SAVE) { - override fun setVisible(value: Boolean) { - super.setVisible(value) - if (value) { - if (file != null) { - onResult(File(directory).resolve(file).toPath()) - } else { - onResult(null) - } - } - } - }.apply { - this.title = title - } - }, - dispose = FileDialog::dispose -) - -@OptIn(DelicateCoroutinesApi::class) -@Composable -fun WindowScope.YesNoCancelDialog( - title: String, - message: String, - onResult: (result: AlertDialogResult) -> Unit -) { - DisposableEffect(Unit) { - val job = GlobalScope.launch(Dispatchers.Swing) { - val resultInt = JOptionPane.showConfirmDialog( - window, message, title, JOptionPane.YES_NO_CANCEL_OPTION - ) - val result = when (resultInt) { - JOptionPane.YES_OPTION -> AlertDialogResult.Yes - JOptionPane.NO_OPTION -> AlertDialogResult.No - else -> AlertDialogResult.Cancel - } - onResult(result) - } - - onDispose { - job.cancel() - } - } -} - -enum class AlertDialogResult { - Yes, No, Cancel -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/window/NotepadWindow.kt b/examples/notepad/src/main/kotlin/window/NotepadWindow.kt deleted file mode 100644 index e600ec24b6b..00000000000 --- a/examples/notepad/src/main/kotlin/window/NotepadWindow.kt +++ /dev/null @@ -1,121 +0,0 @@ -package window - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.window.* -import common.LocalAppResources -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch -import util.FileDialog -import util.YesNoCancelDialog - -@Composable -fun NotepadWindow(state: NotepadWindowState) { - val scope = rememberCoroutineScope() - - fun exit() = scope.launch { state.exit() } - - Window( - state = state.window, - title = titleOf(state), - icon = LocalAppResources.current.icon, - onCloseRequest = { exit() } - ) { - LaunchedEffect(Unit) { state.run() } - - WindowNotifications(state) - WindowMenuBar(state) - - // TextField isn't efficient for big text files, we use it for simplicity - BasicTextField( - state.text, - state::text::set, - enabled = state.isInit, - modifier = Modifier.fillMaxSize() - ) - - if (state.openDialog.isAwaiting) { - FileDialog( - title = "Notepad", - isLoad = true, - onResult = { - state.openDialog.onResult(it) - } - ) - } - - if (state.saveDialog.isAwaiting) { - FileDialog( - title = "Notepad", - isLoad = false, - onResult = { state.saveDialog.onResult(it) } - ) - } - - if (state.exitDialog.isAwaiting) { - YesNoCancelDialog( - title = "Notepad", - message = "Save changes?", - onResult = { state.exitDialog.onResult(it) } - ) - } - } -} - -private fun titleOf(state: NotepadWindowState): String { - val changeMark = if (state.isChanged) "*" else "" - val filePath = state.path ?: "Untitled" - return "$changeMark$filePath - Notepad" -} - -@Composable -private fun WindowNotifications(state: NotepadWindowState) { - // Usually we take into account something like LocalLocale.current here - fun NotepadWindowNotification.format() = when (this) { - is NotepadWindowNotification.SaveSuccess -> Notification( - "File is saved", path.toString(), Notification.Type.Info - ) - is NotepadWindowNotification.SaveError -> Notification( - "File isn't saved", path.toString(), Notification.Type.Error - ) - } - - LaunchedEffect(Unit) { - state.notifications.collect { - state.sendNotification(it.format()) - } - } -} - -@Composable -private fun FrameWindowScope.WindowMenuBar(state: NotepadWindowState) = MenuBar { - val scope = rememberCoroutineScope() - - fun save() = scope.launch { state.save() } - fun open() = scope.launch { state.open() } - fun exit() = scope.launch { state.exit() } - - Menu("File") { - Item("New window", onClick = state::newWindow) - Item("Open...", onClick = { open() }) - Item("Save", onClick = { save() }, enabled = state.isChanged || state.path == null) - Separator() - Item("Exit", onClick = { exit() }) - } - - Menu("Settings") { - Item( - if (state.settings.isTrayEnabled) "Hide tray" else "Show tray", - onClick = state.settings::toggleTray - ) - Item( - if (state.window.placement == WindowPlacement.Fullscreen) "Exit fullscreen" else "Enter fullscreen", - onClick = state::toggleFullscreen - ) - } -} \ No newline at end of file diff --git a/examples/notepad/src/main/kotlin/window/NotepadWindowState.kt b/examples/notepad/src/main/kotlin/window/NotepadWindowState.kt deleted file mode 100644 index 74f7f725e81..00000000000 --- a/examples/notepad/src/main/kotlin/window/NotepadWindowState.kt +++ /dev/null @@ -1,200 +0,0 @@ -package window - -import NotepadApplicationState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.window.Notification -import androidx.compose.ui.window.WindowPlacement -import androidx.compose.ui.window.WindowState -import common.Settings -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.receiveAsFlow -import util.AlertDialogResult -import java.nio.file.Path - -class NotepadWindowState( - private val application: NotepadApplicationState, - path: Path?, - private val exit: (NotepadWindowState) -> Unit -) { - val settings: Settings get() = application.settings - - val window = WindowState() - - var path by mutableStateOf(path) - private set - - var isChanged by mutableStateOf(false) - private set - - val openDialog = DialogState() - val saveDialog = DialogState() - val exitDialog = DialogState() - - private var _notifications = Channel(0) - val notifications: Flow get() = _notifications.receiveAsFlow() - - private var _text by mutableStateOf("") - - var text: String - get() = _text - set(value) { - check(isInit) - _text = value - isChanged = true - } - - var isInit by mutableStateOf(false) - private set - - fun toggleFullscreen() { - window.placement = if (window.placement == WindowPlacement.Fullscreen) { - WindowPlacement.Floating - } else { - WindowPlacement.Fullscreen - } - } - - suspend fun run() { - if (path != null) { - open(path!!) - } else { - initNew() - } - } - - private suspend fun open(path: Path) { - isInit = false - isChanged = false - this.path = path - try { - _text = path.readTextAsync() - isInit = true - } catch (e: Exception) { - e.printStackTrace() - text = "Cannot read $path" - } - } - - private fun initNew() { - _text = "" - isInit = true - isChanged = false - } - - fun newWindow() { - application.newWindow() - } - - suspend fun open() { - if (askToSave()) { - val path = openDialog.awaitResult() - if (path != null) { - open(path) - } - } - } - - suspend fun save(): Boolean { - check(isInit) - if (path == null) { - val path = saveDialog.awaitResult() - if (path != null) { - save(path) - return true - } - } else { - save(path!!) - return true - } - return false - } - - private var saveJob: Job? = null - - private suspend fun save(path: Path) { - isChanged = false - this.path = path - - saveJob?.cancel() - saveJob = path.launchSaving(text) - - try { - saveJob?.join() - _notifications.trySend(NotepadWindowNotification.SaveSuccess(path)) - } catch (e: Exception) { - isChanged = true - e.printStackTrace() - _notifications.trySend(NotepadWindowNotification.SaveError(path)) - } - } - - suspend fun exit(): Boolean { - return if (askToSave()) { - exit(this) - true - } else { - false - } - } - - private suspend fun askToSave(): Boolean { - if (isChanged) { - when (exitDialog.awaitResult()) { - AlertDialogResult.Yes -> { - if (save()) { - return true - } - } - AlertDialogResult.No -> { - return true - } - AlertDialogResult.Cancel -> return false - } - } else { - return true - } - - return false - } - - fun sendNotification(notification: Notification) { - application.sendNotification(notification) - } -} - -@OptIn(DelicateCoroutinesApi::class) -private fun Path.launchSaving(text: String) = GlobalScope.launch { - writeTextAsync(text) -} - -private suspend fun Path.writeTextAsync(text: String) = withContext(Dispatchers.IO) { - toFile().writeText(text) -} - -private suspend fun Path.readTextAsync() = withContext(Dispatchers.IO) { - toFile().readText() -} - -sealed class NotepadWindowNotification { - class SaveSuccess(val path: Path) : NotepadWindowNotification() - class SaveError(val path: Path) : NotepadWindowNotification() -} - -class DialogState { - private var onResult: CompletableDeferred? by mutableStateOf(null) - - val isAwaiting get() = onResult != null - - suspend fun awaitResult(): T { - onResult = CompletableDeferred() - val result = onResult!!.await() - onResult = null - return result - } - - fun onResult(result: T) = onResult!!.complete(result) -} \ No newline at end of file diff --git a/examples/validateExamples.sh b/examples/validateExamples.sh index 6571905176f..7a044d6dc62 100755 --- a/examples/validateExamples.sh +++ b/examples/validateExamples.sh @@ -23,7 +23,6 @@ runGradle chat packageDistributionForCurrentOS runGradle codeviewer packageDistributionForCurrentOS runGradle imageviewer packageDistributionForCurrentOS runGradle issues packageDistributionForCurrentOS -runGradle notepad packageDistributionForCurrentOS runGradle todoapp-lite packageDistributionForCurrentOS runGradle graphics-2d packageDistributionForCurrentOS runGradle jetsnack packageDistributionForCurrentOS