diff --git a/examples/intellij-plugin-with-experimental-shared-base/.gitignore b/examples/intellij-plugin-with-experimental-shared-base/.gitignore new file mode 100644 index 0000000000..65ca50d416 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/.gitignore @@ -0,0 +1,3 @@ +build/ +.gradle/ +.idea/ \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/README.md b/examples/intellij-plugin-with-experimental-shared-base/README.md new file mode 100644 index 0000000000..e8d43a50bc --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/README.md @@ -0,0 +1,19 @@ +## Example Compose Multiplatform based plugin for IntelliJ Idea. + +A plugin, demonstrating an Intellij plugin, showing a dialog window written with Compose. + +The only difference from [examples/intellij-plugin](../intellij-plugin) is that +this version does not bundle Compose runtime, which makes the plugin smaller +and allows sharing Compose runtime between multiple plugins +(Compose class files and native libraries are not loaded by each plugin). + +### Usage + +1. Start test IDE: + * Choose the `runIde` task in the Gradle panel in Intellij; + * Or run the following command in terminal: `./gradlew runIde`; +2. Create a new project or open any existing; +3. Select `Show Compose Demo...` from the `Tools` menu. + +![screen1](../intellij-plugin/screenshots/toolsshow.png) +![screen2](../intellij-plugin/screenshots/screenshot.png) diff --git a/examples/intellij-plugin-with-experimental-shared-base/build.gradle.kts b/examples/intellij-plugin-with-experimental-shared-base/build.gradle.kts new file mode 100644 index 0000000000..e0f916a336 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/build.gradle.kts @@ -0,0 +1,37 @@ +import org.jetbrains.compose.compose + +plugins { + id("org.jetbrains.intellij") version "1.3.0" + java + kotlin("jvm") + id("org.jetbrains.compose") + id("idea") +} + +group = "org.jetbrains.compose.intellij.platform" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() + google() + maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") } +} + +dependencies { + // runtime dependency is provided by org.jetbrains.compose.intellij.platform + compileOnly(compose.desktop.currentOs) + + testImplementation("junit", "junit", "4.12") +} + +// See https://github.com/JetBrains/gradle-intellij-plugin/ +intellij { + version.set("2021.3") + plugins.set( + listOf("org.jetbrains.compose.intellij.platform:0.1.0") + ) +} + +tasks.withType { + kotlinOptions.jvmTarget = "11" +} diff --git a/examples/intellij-plugin-with-experimental-shared-base/gradle.properties b/examples/intellij-plugin-with-experimental-shared-base/gradle.properties new file mode 100644 index 0000000000..4218dc8395 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/gradle.properties @@ -0,0 +1,5 @@ +kotlin.stdlib.default.dependency=false +kotlin.code.style=official +kotlin.version=1.6.10 +compose.version=1.0.1 + diff --git a/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.jar b/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..62d4c05355 Binary files /dev/null and b/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.properties b/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..2e6e5897b5 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/intellij-plugin-with-experimental-shared-base/gradlew b/examples/intellij-plugin-with-experimental-shared-base/gradlew new file mode 100755 index 0000000000..4f906e0c81 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$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 "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/examples/intellij-plugin-with-experimental-shared-base/gradlew.bat b/examples/intellij-plugin-with-experimental-shared-base/gradlew.bat new file mode 100644 index 0000000000..107acd32c4 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/gradlew.bat @@ -0,0 +1,89 @@ +@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%" == "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%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/intellij-plugin-with-experimental-shared-base/settings.gradle.kts b/examples/intellij-plugin-with-experimental-shared-base/settings.gradle.kts new file mode 100644 index 0000000000..dd1ac3675a --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/settings.gradle.kts @@ -0,0 +1,8 @@ +rootProject.name = "ComposeDemoPlugin" + +pluginManagement { + plugins { + kotlin("jvm").version(extra["kotlin.version"] as String) + id("org.jetbrains.compose").version(extra["compose.version"] as String) + } +} diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/ComposeDemoAction.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/ComposeDemoAction.kt new file mode 100755 index 0000000000..581c8cbe91 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/ComposeDemoAction.kt @@ -0,0 +1,70 @@ +package com.jetbrains.compose + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Surface +import androidx.compose.ui.Modifier +import androidx.compose.ui.awt.ComposePanel +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.MeasurePolicy +import androidx.compose.ui.layout.onGloballyPositioned +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAwareAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.jetbrains.compose.theme.WidgetTheme +import com.jetbrains.compose.widgets.Buttons +import com.jetbrains.compose.widgets.LazyScrollable +import com.jetbrains.compose.widgets.Loaders +import com.jetbrains.compose.widgets.TextInputs +import com.jetbrains.compose.widgets.Toggles +import java.awt.Dimension +import javax.swing.JComponent +import javax.swing.SwingUtilities + + +/** + * @author Konstantin Bulenkov + */ +class ComposeDemoAction : DumbAwareAction() { + override fun actionPerformed(e: AnActionEvent) { + DemoDialog(e.project).show() + } + + class DemoDialog(project: Project?) : DialogWrapper(project) { + init { + title = "Demo" + init() + } + + override fun createCenterPanel(): JComponent { + return ComposePanel().apply { + setBounds(0, 0, 800, 600) + setContent { + WidgetTheme(darkTheme = true) { + Surface(modifier = Modifier.fillMaxSize()) { + Row { + Column( + modifier = Modifier.fillMaxHeight().weight(1f) + ) { + Buttons() + Loaders() + TextInputs() + Toggles() + } + Box( + modifier = Modifier.fillMaxHeight().weight(1f) + ) { + LazyScrollable() + } + } + } + } + } + } + } + } +} diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Color.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Color.kt new file mode 100644 index 0000000000..9ed4581b85 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Color.kt @@ -0,0 +1,9 @@ +package com.jetbrains.compose.theme + +import androidx.compose.ui.graphics.Color + +val green200 = Color(0xffa5d6a7) +val green500 = Color(0xff4caf50) +val green700 = Color(0xff388e3c) + +val teal200 = Color(0xff80deea) diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Shape.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Shape.kt new file mode 100644 index 0000000000..3ac8795fc7 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Shape.kt @@ -0,0 +1,11 @@ +package com.jetbrains.compose.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +val shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) +) \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Theme.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Theme.kt new file mode 100644 index 0000000000..78a3ec5181 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Theme.kt @@ -0,0 +1,46 @@ +package com.jetbrains.compose.theme + +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.jetbrains.compose.theme.intellij.SwingColor + +private val DarkGreenColorPalette = darkColors( + primary = green200, + primaryVariant = green700, + secondary = teal200, + onPrimary = Color.Black, + onSecondary = Color.White, + error = Color.Red, +) + +private val LightGreenColorPalette = lightColors( + primary = green500, + primaryVariant = green700, + secondary = teal200, + onPrimary = Color.White, + onSurface = Color.Black +) + +@Composable +fun WidgetTheme( + darkTheme: Boolean = false, + content: @Composable() () -> Unit, +) { + val colors = if (darkTheme) DarkGreenColorPalette else LightGreenColorPalette + val swingColor = SwingColor() + + MaterialTheme( + colors = colors.copy( + background = swingColor.background, + onBackground = swingColor.onBackground, + surface = swingColor.background, + onSurface = swingColor.onBackground, + ), + typography = typography, + shapes = shapes, + content = content + ) +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Type.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Type.kt new file mode 100644 index 0000000000..4bc429afa8 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/Type.kt @@ -0,0 +1,43 @@ +package com.jetbrains.compose.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +val typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ), + body2 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 14.sp + ), + button = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + fontSize = 14.sp + ), + caption = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + ), + subtitle1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + color = Color.Gray + ), + subtitle2 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = Color.Gray + ), +) \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/SwingColor.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/SwingColor.kt new file mode 100644 index 0000000000..0aac1aaf50 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/SwingColor.kt @@ -0,0 +1,61 @@ +package com.jetbrains.compose.theme.intellij + +import androidx.compose.runtime.* +import androidx.compose.ui.graphics.Color +import com.intellij.ide.ui.LafManagerListener +import com.intellij.openapi.application.ApplicationManager +import javax.swing.UIManager +import java.awt.Color as AWTColor + +interface SwingColor { + val background: Color + val onBackground: Color +} + +@Composable +fun SwingColor(): SwingColor { + val swingColor = remember { SwingColorImpl() } + + val messageBus = remember { + ApplicationManager.getApplication().messageBus.connect() + } + + remember(messageBus) { + messageBus.subscribe( + LafManagerListener.TOPIC, + ThemeChangeListener(swingColor::updateCurrentColors) + ) + } + + DisposableEffect(messageBus) { + onDispose { + messageBus.disconnect() + } + } + + return swingColor +} + +private class SwingColorImpl : SwingColor { + private val _backgroundState: MutableState = mutableStateOf(getBackgroundColor) + private val _onBackgroundState: MutableState = mutableStateOf(getOnBackgroundColor) + + override val background: Color get() = _backgroundState.value + override val onBackground: Color get() = _onBackgroundState.value + + private val getBackgroundColor get() = getColor(BACKGROUND_KEY) + private val getOnBackgroundColor get() = getColor(ON_BACKGROUND_KEY) + + fun updateCurrentColors() { + _backgroundState.value = getBackgroundColor + _onBackgroundState.value = getOnBackgroundColor + } + + private val AWTColor.asComposeColor: Color get() = Color(red, green, blue, alpha) + private fun getColor(key: String): Color = UIManager.getColor(key).asComposeColor + + companion object { + private const val BACKGROUND_KEY = "Panel.background" + private const val ON_BACKGROUND_KEY = "Panel.foreground" + } +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/ThemeChangeListener.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/ThemeChangeListener.kt new file mode 100644 index 0000000000..95a8324114 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/theme/intellij/ThemeChangeListener.kt @@ -0,0 +1,13 @@ +package com.jetbrains.compose.theme.intellij + +import com.intellij.ide.ui.LafManager +import com.intellij.ide.ui.LafManagerListener + +internal class ThemeChangeListener( + val updateColors: () -> Unit +) : LafManagerListener { + override fun lookAndFeelChanged(source: LafManager) { + updateColors() + } +} + diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Buttons.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Buttons.kt new file mode 100755 index 0000000000..89fc8c2c11 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Buttons.kt @@ -0,0 +1,57 @@ +package com.jetbrains.compose.widgets + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.material.TextButton +import androidx.compose.material.OutlinedButton +import androidx.compose.material.Text +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.FavoriteBorder +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun Buttons() { + Row { + val btnEnabled = remember { mutableStateOf(true) } + Button( + onClick = { btnEnabled.value = !btnEnabled.value}, + modifier = Modifier.padding(8.dp), + enabled = btnEnabled.value + ) { + Icon( + imageVector = Icons.Default.FavoriteBorder, + contentDescription = "FavoriteBorder", + modifier = Modifier.padding(end = 4.dp) + ) + Text(text = "Button") + } + val btnTextEnabled = remember { mutableStateOf(true) } + TextButton( + onClick = { btnTextEnabled.value = !btnTextEnabled.value }, + modifier = Modifier.padding(8.dp), + enabled = btnTextEnabled.value + ) { + Text(text = "Text Button") + } + OutlinedButton( + onClick = { + btnEnabled.value = true + btnTextEnabled.value = true + }, + modifier = Modifier.padding(8.dp) + ) { + Icon( + imageVector = Icons.Default.Refresh, + contentDescription = "Refresh", + modifier = Modifier.padding(0.dp) + ) + } + } +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/LazyScrollable.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/LazyScrollable.kt new file mode 100755 index 0000000000..e5bba421a0 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/LazyScrollable.kt @@ -0,0 +1,71 @@ +package com.jetbrains.compose.widgets + +import androidx.compose.desktop.DesktopTheme +import androidx.compose.foundation.background +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.rememberScrollbarAdapter +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.VerticalScrollbar +import androidx.compose.material.Text +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun LazyScrollable() { + MaterialTheme { + DesktopTheme { + Box( + modifier = Modifier.fillMaxSize() + .padding(10.dp) + ) { + + val state = rememberLazyListState() + val itemCount = 100 + + LazyColumn(Modifier.fillMaxSize().padding(end = 12.dp), state) { + items(itemCount) { x -> + TextBox("Item in ScrollableColumn #$x") + Spacer(modifier = Modifier.height(5.dp)) + } + } + VerticalScrollbar( + modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(), + adapter = rememberScrollbarAdapter( + scrollState = state + ) + ) + } + } + } +} + +@Composable +private fun TextBox(text: String = "Item") { + Surface( + color = Color(135, 135, 135, 40), + shape = RoundedCornerShape(4.dp) + ) { + Box( + modifier = Modifier.height(32.dp) + .fillMaxWidth() + .padding(start = 10.dp), + contentAlignment = Alignment.CenterStart + ) { + Text(text = text) + } + } +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Loaders.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Loaders.kt new file mode 100755 index 0000000000..a18252459b --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Loaders.kt @@ -0,0 +1,39 @@ +package com.jetbrains.compose.widgets + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun Loaders() { + Row( + modifier = Modifier.fillMaxWidth().padding(16.dp) + ) { + Box( + modifier = Modifier.height(30.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator( + modifier = Modifier.size(20.dp, 20.dp), + strokeWidth = 4.dp + ) + } + Box( + modifier = Modifier + .height(30.dp) + .padding(start = 8.dp), + contentAlignment = Alignment.Center + ) { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } + } +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/TextInputs.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/TextInputs.kt new file mode 100755 index 0000000000..5ca7a9aad6 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/TextInputs.kt @@ -0,0 +1,53 @@ +package com.jetbrains.compose.widgets + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.material.OutlinedTextField +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp + +@Composable +fun TextInputs() { + Column( + modifier = Modifier.fillMaxWidth().padding(16.dp) + ) { + var name by remember { mutableStateOf(TextFieldValue("")) } + var password by remember { mutableStateOf(TextFieldValue("")) } + + TextField( + value = name, + onValueChange = { newValue -> name = newValue }, + modifier = Modifier.padding(8.dp).fillMaxWidth(), + textStyle = TextStyle(fontFamily = FontFamily.SansSerif), + label = { Text("Account:") }, + placeholder = { Text("account name") } + ) + + OutlinedTextField( + value = password, + modifier = Modifier.padding(8.dp).fillMaxWidth(), + label = { Text(text = "Password:") }, + placeholder = { Text(text = "your password") }, + textStyle = TextStyle(fontFamily = FontFamily.SansSerif), + visualTransformation = PasswordVisualTransformation(), + onValueChange = { + password = it + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password) + ) + } +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Toggles.kt b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Toggles.kt new file mode 100755 index 0000000000..b4a7febac2 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/kotlin/com/jetbrains/compose/widgets/Toggles.kt @@ -0,0 +1,90 @@ +package com.jetbrains.compose.widgets + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Checkbox +import androidx.compose.material.MaterialTheme +import androidx.compose.material.RadioButton +import androidx.compose.material.Slider +import androidx.compose.material.Switch +import androidx.compose.material.SwitchDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun Toggles() { + Column { + Row { + Column( + modifier = Modifier.padding(16.dp) + ) { + var checked by remember { mutableStateOf(true) } + Checkbox( + checked = checked, + modifier = Modifier.padding(8.dp), + onCheckedChange = { checked = !checked } + ) + var switched by remember { mutableStateOf(true) } + Switch( + checked = switched, + colors = SwitchDefaults.colors(checkedThumbColor = MaterialTheme.colors.primary), + modifier = Modifier.padding(8.dp), + onCheckedChange = { switched = it } + ) + } + Column( + modifier = Modifier.padding(16.dp) + ) { + var selected by remember { mutableStateOf("Kotlin") } + Row(verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected = selected == "Kotlin", onClick = { selected = "Kotlin" }) + Text( + text = "Kotlin", + modifier = Modifier.clickable(onClick = { selected = "Kotlin" }).padding(start = 4.dp) + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected = selected == "Java", onClick = { selected = "Java" }) + Text( + text = "Java", + modifier = Modifier.clickable(onClick = { selected = "Java" }).padding(start = 4.dp) + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected = selected == "Swift", onClick = { selected = "Swift" }) + Text( + text = "Swift", + modifier = Modifier.clickable(onClick = { selected = "Swift" }).padding(start = 4.dp) + ) + } + } + } + + var sliderState by remember { mutableStateOf(0f) } + Slider(value = sliderState, modifier = Modifier.fillMaxWidth().padding(8.dp), + onValueChange = { newValue -> + sliderState = newValue + } + ) + + var sliderState2 by remember { mutableStateOf(20f) } + Slider(value = sliderState2, modifier = Modifier.fillMaxWidth().padding(8.dp), + valueRange = 0f..100f, + steps = 5, + onValueChange = { newValue -> + sliderState2 = newValue + } + ) + } +} \ No newline at end of file diff --git a/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/META-INF/plugin.xml b/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/META-INF/plugin.xml new file mode 100644 index 0000000000..55a1038563 --- /dev/null +++ b/examples/intellij-plugin-with-experimental-shared-base/src/main/resources/META-INF/plugin.xml @@ -0,0 +1,22 @@ + + com.jetbrains.ComposeDemoPlugin + Jetpack Compose for Desktop Demo + JetBrains + + + + + com.intellij.modules.platform + org.jetbrains.compose.intellij.platform + + + + + + + + \ No newline at end of file diff --git a/examples/intellij-plugin/README.md b/examples/intellij-plugin/README.md index cd22890e35..1b510009c5 100644 --- a/examples/intellij-plugin/README.md +++ b/examples/intellij-plugin/README.md @@ -2,7 +2,7 @@ To run the example: -1. from directory `compose-jb/examples/intelliJPlugin` run command in terminal: `./gradlew runIde` +1. from directory `compose-jb/examples/intellij-plugin` run command in terminal: `./gradlew runIde` 2. create project or open any existing 3. from menu `Tools` select `Show Compose Demo...` diff --git a/tooling/compose-intellij-platform/README.md b/tooling/compose-intellij-platform/README.md new file mode 100644 index 0000000000..d30bdf6d0c --- /dev/null +++ b/tooling/compose-intellij-platform/README.md @@ -0,0 +1,60 @@ +## Compose for IDE Plugin Development + +This is an experimental plugin, enabling Intellij plugin development +with Compose. +For now, it only provides Compose runtime classes and native libraries, +so they can be reused by different plugins. + +### Usage + +The following steps assume [gradle-intellij-plugin](https://github.com/JetBrains/gradle-intellij-plugin) is used +for Intellij plugin development: +1. Add Compose as `compileOnly` dependency to your plugin's dependencies. +2. Add the platform plugin's ID to `intellij` block in a Gradle script and to a +`plugin.xml`. +3. Add `kotlin.stdlib.default.dependency=false` to `gradle.properties`. +4. Use `runIde` Gradle task to run a test IDE. + +``` +// build.gradle.kts + +import org.jetbrains.compose.compose + +plugins { + id("org.jetbrains.intellij") version "1.3.0" + id("org.jetbrains.kotlin.jvm") version "1.6.10" + id("org.jetbrains.compose") version "1.0.1" +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(compose.desktop.currentOs) +} + +// See https://github.com/JetBrains/gradle-intellij-plugin/ +intellij { + pluginName.set("Example plugin name") + version.set("2021.3") + plugins.set(listOf("org.jetbrains.compose.intellij.platform:0.1.0")) +} +``` + +``` + + + com.jetbrains.ComposeDemoPlugin + Jetpack Compose for Desktop Demo + Demo Vendor + + ... + + com.intellij.modules.platform + org.jetbrains.compose.intellij.platform + +``` + +A complete example can be found at +[examples/intellij-plugin-with-experimental-shared-base](../../examples/intellij-plugin-with-experimental-shared-base). \ No newline at end of file