diff --git a/examples/widgetsgallery/.gitignore b/examples/widgetsgallery/.gitignore
new file mode 100644
index 0000000000..88816f6122
--- /dev/null
+++ b/examples/widgetsgallery/.gitignore
@@ -0,0 +1,10 @@
+*.iml
+.gradle
+/local.properties
+/.idea/
+/.run
+.DS_Store
+build/
+/captures
+.externalNativeBuild
+.cxx
\ No newline at end of file
diff --git a/examples/widgetsgallery/README.md b/examples/widgetsgallery/README.md
new file mode 100644
index 0000000000..985bfb2854
--- /dev/null
+++ b/examples/widgetsgallery/README.md
@@ -0,0 +1,25 @@
+# Widget gallery
+
+This example is derived from
+[ComposeCookBook](https://github.com/Gurupreet/ComposeCookBook) project
+by Gurupreet Singh ([@Gurupreet](https://github.com/Gurupreet))
+published under [MIT license](third_party/ComposeCookBook_LICENSE.txt).
+
+An example of Compose application for Desktop and Android platforms,
+demonstrating how to use various Material widgets.
+
+### Running desktop application
+```
+./gradlew :desktop:run
+```
+
+### Building native desktop distribution
+```
+./gradlew :desktop:package
+# outputs are written to desktop/build/compose/binaries
+```
+
+### Installing Android application on device/emulator
+```
+./gradlew installDebug
+```
diff --git a/examples/widgetsgallery/android/build.gradle.kts b/examples/widgetsgallery/android/build.gradle.kts
new file mode 100644
index 0000000000..20040c60e6
--- /dev/null
+++ b/examples/widgetsgallery/android/build.gradle.kts
@@ -0,0 +1,25 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("org.jetbrains.compose")
+}
+
+android {
+ compileSdkVersion(30)
+
+ defaultConfig {
+ minSdkVersion(26)
+ targetSdkVersion(30)
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation(project(":common"))
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/AndroidManifest.xml b/examples/widgetsgallery/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..bbfbcf26cc
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/java/org/jetbrains/compose/demo/widgets/MainActivity.kt b/examples/widgetsgallery/android/src/main/java/org/jetbrains/compose/demo/widgets/MainActivity.kt
new file mode 100644
index 0000000000..e2e221a68e
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/java/org/jetbrains/compose/demo/widgets/MainActivity.kt
@@ -0,0 +1,15 @@
+package org.jetbrains.compose.demo.widgets
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.ui.platform.setContent
+import org.jetbrains.compose.demo.widgets.ui.MainView
+
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MainView()
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/drawable-v24/ic_launcher_background.xml b/examples/widgetsgallery/android/src/main/res/drawable-v24/ic_launcher_background.xml
new file mode 100644
index 0000000000..07d5da9cbf
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/drawable-v24/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/widgetsgallery/android/src/main/res/drawable-v24/ic_launcher_foreground.xml b/examples/widgetsgallery/android/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..2b068d1146
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/widgetsgallery/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..eca70cfe52
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/widgetsgallery/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..eca70cfe52
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/widgetsgallery/android/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..a571e60098
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-hdpi/ic_launcher_round.png b/examples/widgetsgallery/android/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..61da551c55
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/widgetsgallery/android/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..c41dd28531
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-mdpi/ic_launcher_round.png b/examples/widgetsgallery/android/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..db5080a752
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/widgetsgallery/android/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..6dba46dab1
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/examples/widgetsgallery/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..da31a871c8
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/widgetsgallery/android/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..15ac681720
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/examples/widgetsgallery/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..b216f2d313
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/widgetsgallery/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..f25a419744
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/widgetsgallery/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..e96783ccce
Binary files /dev/null and b/examples/widgetsgallery/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/examples/widgetsgallery/android/src/main/res/values-night/themes.xml b/examples/widgetsgallery/android/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000000..710bfabb30
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/values/colors.xml b/examples/widgetsgallery/android/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..6040e1f218
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+
+
+ #ffa5d6a7
+ #ff4caf50
+ #ff388e3c
+ #FF03DAC5
+ #ff80deea
+ #FF833AB4
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/values/strings.xml b/examples/widgetsgallery/android/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..49dfce059f
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ ComposeWidgets
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/android/src/main/res/values/themes.xml b/examples/widgetsgallery/android/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..50dcf6d3d9
--- /dev/null
+++ b/examples/widgetsgallery/android/src/main/res/values/themes.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/build.gradle.kts b/examples/widgetsgallery/build.gradle.kts
new file mode 100644
index 0000000000..946ae9688d
--- /dev/null
+++ b/examples/widgetsgallery/build.gradle.kts
@@ -0,0 +1,24 @@
+buildscript {
+ repositories {
+ // TODO: remove after new build is published
+ mavenLocal()
+ google()
+ jcenter()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+ }
+
+ dependencies {
+ // __LATEST_COMPOSE_RELEASE_VERSION__
+ classpath("org.jetbrains.compose:compose-gradle-plugin:0.3.0-build135")
+ classpath("com.android.tools.build:gradle:4.0.1")
+ classpath(kotlin("gradle-plugin", version = "1.4.21"))
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+ }
+}
diff --git a/examples/widgetsgallery/common/build.gradle.kts b/examples/widgetsgallery/common/build.gradle.kts
new file mode 100644
index 0000000000..f14bbb300b
--- /dev/null
+++ b/examples/widgetsgallery/common/build.gradle.kts
@@ -0,0 +1,60 @@
+import org.jetbrains.compose.compose
+
+plugins {
+ id("com.android.library")
+ kotlin("multiplatform")
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ android()
+ jvm("desktop")
+
+ sourceSets {
+ named("commonMain") {
+ dependencies {
+ api(compose.runtime)
+ api(compose.foundation)
+ api(compose.material)
+ api(compose.materialIconsExtended)
+ }
+ }
+ named("androidMain") {
+ kotlin.srcDirs("src/jvmMain/kotlin")
+ dependencies {
+ api("androidx.appcompat:appcompat:1.1.0")
+ api("androidx.core:core-ktx:1.3.1")
+ }
+ }
+ named("desktopMain") {
+ kotlin.srcDirs("src/jvmMain/kotlin")
+ resources.srcDirs("src/commonMain/resources")
+ dependencies {
+ api(compose.desktop.common)
+ }
+ }
+ }
+}
+
+android {
+ compileSdkVersion(30)
+
+ defaultConfig {
+ minSdkVersion(21)
+ targetSdkVersion(30)
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+
+ sourceSets {
+ named("main") {
+ manifest.srcFile("src/androidMain/AndroidManifest.xml")
+ res.srcDirs("src/androidMain/res", "src/commonMain/resources")
+ }
+ }
+}
diff --git a/examples/widgetsgallery/common/src/androidMain/AndroidManifest.xml b/examples/widgetsgallery/common/src/androidMain/AndroidManifest.xml
new file mode 100644
index 0000000000..35be3787c7
--- /dev/null
+++ b/examples/widgetsgallery/common/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..30eed99b90
--- /dev/null
+++ b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+
+actual fun Modifier.pointerMoveFilter(
+ onEnter: () -> Boolean,
+ onExit: () -> Boolean,
+ onMove: (Offset) -> Boolean
+): Modifier = this
+
+actual fun Modifier.cursorForHorizontalResize() = this
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..16967b720e
--- /dev/null
+++ b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,27 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.vector.ImageVector
+import org.jetbrains.compose.demo.widgets.R
+
+@Composable
+actual fun imageResource(res: String): ImageBitmap {
+ val id = drawableId(res)
+ return androidx.compose.ui.res.imageResource(id)
+}
+
+@Composable
+actual fun vectorResource(res: String): ImageVector {
+ val id = drawableId(res)
+ return androidx.compose.ui.res.vectorResource(id)
+}
+
+// TODO: improve resource loading
+private fun drawableId(res: String): Int {
+ val imageName = res.substringAfterLast("/").substringBeforeLast(".")
+ val drawableClass = R.drawable::class.java
+ val field = drawableClass.getDeclaredField(imageName)
+ val idValue = field.get(drawableClass) as Integer
+ return idValue.toInt()
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..7ec4cd59c7
--- /dev/null
+++ b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
@@ -0,0 +1,21 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+
+@Composable
+actual fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: ScrollState
+) = Unit
+
+@Composable
+actual fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: LazyListState,
+ itemCount: Int,
+ averageItemSize: Dp
+) = Unit
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..be73331ce2
--- /dev/null
+++ b/examples/widgetsgallery/common/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
@@ -0,0 +1,8 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.foundation.isSystemInDarkTheme as androidSystemIsInDarkTheme
+
+@Composable
+actual fun isSystemInDarkTheme(): Boolean =
+ androidSystemIsInDarkTheme()
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/DemoDataProvider.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/DemoDataProvider.kt
new file mode 100644
index 0000000000..74369433cc
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/DemoDataProvider.kt
@@ -0,0 +1,14 @@
+package org.jetbrains.compose.demo.widgets.data
+
+import org.jetbrains.compose.demo.widgets.data.model.Item
+import org.jetbrains.compose.demo.widgets.data.model.Tweet
+import org.jetbrains.compose.demo.widgets.platform.Res
+
+object DemoDataProvider {
+ val item = Item(
+ 1,
+ "Awesome List Item",
+ "Very awesome list item has very awesome subtitle. This is bit long",
+ Res.drawable.food6
+ )
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Item.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Item.kt
new file mode 100644
index 0000000000..bb879b4ac4
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Item.kt
@@ -0,0 +1,9 @@
+package org.jetbrains.compose.demo.widgets.data.model
+
+data class Item(
+ val id: Int,
+ val title: String,
+ val subtitle: String,
+ val imageId: String,
+ val source: String = "demo source"
+)
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Tweet.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Tweet.kt
new file mode 100644
index 0000000000..dd9df15318
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Tweet.kt
@@ -0,0 +1,15 @@
+package org.jetbrains.compose.demo.widgets.data.model
+
+data class Tweet(
+ val id: Int,
+ val text: String,
+ val author: String,
+ val handle: String,
+ val time: String,
+ val authorImageId: String,
+ val likesCount: Int,
+ val commentsCount: Int,
+ val retweetCount: Int,
+ val source: String,
+ val tweetImageId: String? = null
+)
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..1d20d024a2
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+
+expect fun Modifier.pointerMoveFilter(
+ onEnter: () -> Boolean = { true },
+ onExit: () -> Boolean = { true },
+ onMove: (Offset) -> Boolean = { true }
+): Modifier
+
+expect fun Modifier.cursorForHorizontalResize(): Modifier
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Res.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Res.kt
new file mode 100644
index 0000000000..404e83a410
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Res.kt
@@ -0,0 +1,21 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+object Res {
+ object drawable {
+ val food6 = "drawable-nodpi/food6.jpg"
+ val p1 = "drawable-nodpi/p1.jpeg"
+ val p2 = "drawable-nodpi/p2.jpeg"
+ val p3 = "drawable-nodpi/p3.jpeg"
+ val p6 = "drawable-nodpi/p6.jpeg"
+
+ val ic_instagram = "drawable/ic_instagram.xml"
+ val ic_send = "drawable/ic_send.xml"
+ val ic_twitter = "drawable/ic_twitter.xml"
+ }
+
+ object string {
+ val spotify_nav_home = "Home"
+ val spotify_nav_search = "Search"
+ val spotify_nav_library = "Your Library"
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..f9ecf511b0
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.vector.ImageVector
+
+@Composable
+expect fun imageResource(res: String): ImageBitmap
+
+@Composable
+expect fun vectorResource(res: String): ImageVector
+
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..d7adfa7349
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
@@ -0,0 +1,21 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+
+@Composable
+expect fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: ScrollState
+)
+
+@Composable
+expect fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: LazyListState,
+ itemCount: Int,
+ averageItemSize: Dp
+)
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..3050d9f245
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
@@ -0,0 +1,3 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+expect fun isSystemInDarkTheme(): Boolean
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Color.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Color.kt
new file mode 100644
index 0000000000..af32b07b1d
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Color.kt
@@ -0,0 +1,23 @@
+package org.jetbrains.compose.demo.widgets.theme
+
+import androidx.compose.ui.graphics.Color
+
+val green200 = Color(0xffa5d6a7)
+val green500 = Color(0xff4caf50)
+val green700 = Color(0xff388e3c)
+
+val blue500 = Color(0xFF3F51B5)
+val blue200 = Color(0xFF9FA8DA)
+val blue700 = Color(0xFF303F9F)
+
+val purple200 = Color(0xFFB39DDB)
+val purple = Color(0xFF833AB4)
+val purple700 = Color(0xFF512DA8)
+
+val orange200 = Color(0xFFff7961)
+val orange500 = Color(0xFFf44336)
+val orange700 = Color(0xFFba000d)
+
+
+val teal200 = Color(0xff80deea)
+val twitterColor = Color(0xFF1DA1F2)
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Shape.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Shape.kt
new file mode 100644
index 0000000000..764221a766
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Shape.kt
@@ -0,0 +1,11 @@
+package org.jetbrains.compose.demo.widgets.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/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Theme.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Theme.kt
new file mode 100644
index 0000000000..64ec9f8344
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Theme.kt
@@ -0,0 +1,136 @@
+package org.jetbrains.compose.demo.widgets.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 org.jetbrains.compose.demo.widgets.platform.isSystemInDarkTheme
+import org.jetbrains.compose.demo.widgets.theme.ColorPallet.*
+
+// dark palettes
+private val DarkGreenColorPalette = darkColors(
+ primary = green200,
+ primaryVariant = green700,
+ secondary = teal200,
+ background = Color.Black,
+ surface = Color.Black,
+ onPrimary = Color.Black,
+ onSecondary = Color.White,
+ onBackground = Color.White,
+ onSurface = Color.White,
+ error = Color.Red,
+)
+
+private val DarkPurpleColorPalette = darkColors(
+ primary = purple200,
+ primaryVariant = purple700,
+ secondary = teal200,
+ background = Color.Black,
+ surface = Color.Black,
+ onPrimary = Color.Black,
+ onSecondary = Color.White,
+ onBackground = Color.White,
+ onSurface = Color.White,
+ error = Color.Red,
+)
+
+private val DarkBlueColorPalette = darkColors(
+ primary = blue200,
+ primaryVariant = blue700,
+ secondary = teal200,
+ background = Color.Black,
+ surface = Color.Black,
+ onPrimary = Color.Black,
+ onSecondary = Color.White,
+ onBackground = Color.White,
+ onSurface = Color.White,
+ error = Color.Red,
+)
+
+private val DarkOrangeColorPalette = darkColors(
+ primary = orange200,
+ primaryVariant = orange700,
+ secondary = teal200,
+ background = Color.Black,
+ surface = Color.Black,
+ onPrimary = Color.Black,
+ onSecondary = Color.White,
+ onBackground = Color.White,
+ onSurface = Color.White,
+ error = Color.Red,
+)
+
+// Light pallets
+private val LightGreenColorPalette = lightColors(
+ primary = green500,
+ primaryVariant = green700,
+ secondary = teal200,
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black
+)
+
+private val LightPurpleColorPalette = lightColors(
+ primary = purple,
+ primaryVariant = purple700,
+ secondary = teal200,
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black
+)
+
+private val LightBlueColorPalette = lightColors(
+ primary = blue500,
+ primaryVariant = blue700,
+ secondary = teal200,
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black
+)
+
+private val LightOrangeColorPalette = lightColors(
+ primary = orange500,
+ primaryVariant = orange700,
+ secondary = teal200,
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black
+)
+
+enum class ColorPallet {
+ PURPLE, GREEN, ORANGE, BLUE
+}
+
+@Composable
+fun WidgetGalleryTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ colorPallet: ColorPallet = GREEN,
+ content: @Composable() () -> Unit,
+) {
+ val colors = when (colorPallet) {
+ GREEN -> if (darkTheme) DarkGreenColorPalette else LightGreenColorPalette
+ PURPLE -> if (darkTheme) DarkPurpleColorPalette else LightPurpleColorPalette
+ ORANGE -> if (darkTheme) DarkOrangeColorPalette else LightOrangeColorPalette
+ BLUE -> if (darkTheme) DarkBlueColorPalette else LightBlueColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = typography,
+ shapes = shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Type.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Type.kt
new file mode 100644
index 0000000000..bda3b064fa
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Type.kt
@@ -0,0 +1,43 @@
+package org.jetbrains.compose.demo.widgets.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/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/MainView.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/MainView.kt
new file mode 100644
index 0000000000..e2f96b33e6
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/MainView.kt
@@ -0,0 +1,154 @@
+package org.jetbrains.compose.demo.widgets.ui
+
+import androidx.compose.animation.animate
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumnFor
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.*
+import androidx.compose.runtime.*
+import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.platform.AmbientDensity
+import androidx.compose.ui.selection.DisableSelection
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.*
+import org.jetbrains.compose.demo.widgets.platform.VerticalScrollbar
+import org.jetbrains.compose.demo.widgets.platform.pointerMoveFilter
+import org.jetbrains.compose.demo.widgets.theme.WidgetGalleryTheme
+import org.jetbrains.compose.demo.widgets.ui.utils.PanelState
+import org.jetbrains.compose.demo.widgets.ui.utils.ResizablePanel
+import org.jetbrains.compose.demo.widgets.ui.utils.VerticalSplittable
+import org.jetbrains.compose.demo.widgets.ui.utils.withoutWidthConstraints
+import kotlin.ranges.coerceAtLeast
+
+@Composable
+fun MainView() {
+ DisableSelection {
+ WidgetGalleryTheme() {
+ WidgetsPanel()
+ }
+ }
+}
+
+@Composable
+fun WidgetsPanel() {
+ val widgetsTypeState = savedInstanceState { WidgetsType.sortedValues.first() }
+ val panelState = remember { PanelState() }
+
+ val animatedSize = if (panelState.splitter.isResizing) {
+ if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize
+ } else {
+ animate(
+ if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize,
+ SpringSpec(stiffness = Spring.StiffnessLow)
+ )
+ }
+
+ VerticalSplittable(
+ Modifier.fillMaxSize(),
+ panelState.splitter,
+ onResize = {
+ panelState.expandedSize =
+ (panelState.expandedSize + it).coerceAtLeast(panelState.expandedSizeMin)
+ }
+ ) {
+ ResizablePanel(
+ Modifier.width(animatedSize).fillMaxHeight(),
+ title = "Widgets",
+ state = panelState
+ ) {
+ WidgetsListView(widgetsTypeState)
+ }
+
+ Box {
+ Column {
+ WidgetsView(
+ widgetsTypeState,
+ modifier = Modifier.weight(1f)
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun WidgetsListView(widgetsTypeState: MutableState) {
+ Box {
+ with(AmbientDensity.current) {
+ val scrollState = rememberLazyListState()
+
+ val fontSize = 14.sp
+ val lineHeight = fontSize.toDp() * 1.5f
+
+ val items = WidgetsType.sortedValues
+ LazyColumnFor(
+ items,
+ modifier = Modifier.fillMaxSize().withoutWidthConstraints(),
+ state = scrollState,
+ itemContent = { WidgetsListItemViewImpl(it, widgetsTypeState, fontSize, lineHeight) }
+ )
+
+ VerticalScrollbar(
+ Modifier.align(Alignment.CenterEnd),
+ scrollState,
+ items.size,
+ lineHeight
+ )
+ }
+ }
+
+}
+
+@Composable
+private fun WidgetsListItemViewImpl(
+ widgetsType: WidgetsType,
+ widgetsTypeState: MutableState,
+ fontSize: TextUnit,
+ height: Dp
+) {
+ val isCurrent = widgetsTypeState.value == widgetsType
+
+ Row(
+ modifier = Modifier
+ .wrapContentHeight()
+ .clickable { widgetsTypeState.value = widgetsType }
+ .height(height)
+ .padding(start = 16.dp)
+ ) {
+ var inFocus by remember { mutableStateOf(false) }
+ val textColor = AmbientContentColor.current.let {
+ when {
+ isCurrent -> it
+ inFocus -> it.copy(alpha = 0.6f)
+ else -> it.copy(alpha = 0.4f)
+ }
+ }
+
+ Text(
+ text = widgetsType.readableName,
+ color = textColor,
+ modifier = Modifier
+ .align(Alignment.CenterVertically)
+ .clipToBounds()
+ .pointerMoveFilter(
+ onEnter = {
+ inFocus = true
+ true
+ },
+ onExit = {
+ inFocus = false
+ true
+ }
+ ),
+ softWrap = true,
+ fontSize = fontSize,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1
+ )
+ }
+}
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetView.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetView.kt
new file mode 100644
index 0000000000..45de67c6cf
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetView.kt
@@ -0,0 +1,31 @@
+package org.jetbrains.compose.demo.widgets.ui
+
+import androidx.compose.foundation.ScrollableColumn
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.ui.Modifier
+import org.jetbrains.compose.demo.widgets.ui.screens.*
+
+@Composable
+fun WidgetsView(
+ widgetsTypeState: MutableState,
+ modifier: Modifier
+) {
+ ScrollableColumn(modifier = modifier) {
+ Column {
+ @Suppress("UNUSED_VARIABLE")
+ val exhaustive = when (widgetsTypeState.value) {
+ WidgetsType.APP_BARS -> AppBars()
+ WidgetsType.BUTTONS -> Buttons()
+ WidgetsType.CHIPS -> Chips()
+ WidgetsType.LOADERS -> Loaders()
+ WidgetsType.SNACK_BARS -> SnackBars()
+ WidgetsType.TEXT_VIEWS -> TextViews()
+ WidgetsType.TEXT_INPUTS -> TextInputs()
+ WidgetsType.TOGGLES -> Toggles()
+ WidgetsType.UI_CARDS -> UICards()
+ }
+ }
+ }
+}
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetsType.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetsType.kt
new file mode 100644
index 0000000000..d2c32e396d
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetsType.kt
@@ -0,0 +1,30 @@
+package org.jetbrains.compose.demo.widgets.ui
+
+enum class WidgetsType(private val customTitle: String? = null) {
+ APP_BARS,
+ BUTTONS,
+ CHIPS,
+ LOADERS,
+ SNACK_BARS,
+ TEXT_VIEWS,
+ TEXT_INPUTS,
+ TOGGLES,
+ UI_CARDS("UI Cards");
+
+ val readableName: String by lazy {
+ name.split("_")
+ .map { it.toLowerCase() }
+ .mapIndexed { i, it ->
+ if (i == 0) it.capitalize() else it
+ }.joinToString(" ")
+ }
+
+ val title: String
+ get() = customTitle ?: readableName
+
+ companion object {
+ val sortedValues: List by lazy {
+ values().sortedBy { it.name }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/AppBars.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/AppBars.kt
new file mode 100644
index 0000000000..a979c7e327
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/AppBars.kt
@@ -0,0 +1,160 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.filled.MoreHoriz
+import androidx.compose.material.icons.filled.StarBorder
+import androidx.compose.material.icons.outlined.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.platform.Res
+import org.jetbrains.compose.demo.widgets.platform.imageResource
+import org.jetbrains.compose.demo.widgets.platform.vectorResource
+import org.jetbrains.compose.demo.widgets.theme.twitterColor
+import org.jetbrains.compose.demo.widgets.theme.typography
+import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText
+import org.jetbrains.compose.demo.widgets.ui.utils.TitleText
+
+@Composable
+fun AppBars() {
+ TopAppBarsDemo()
+ BottomAppBarDemo()
+ NavigationBarDemo()
+}
+
+@Composable
+private fun TopAppBarsDemo() {
+ SubtitleText(subtitle = "Top App bar")
+
+ TopAppBar(
+ title = { Text(text = "Home") },
+ elevation = 8.dp,
+ navigationIcon = {
+ IconButton(onClick = {}) {
+ Icon(Icons.Default.ArrowBack)
+ }
+ }
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ TopAppBar(
+ title = { Text(text = "Instagram") },
+ backgroundColor = MaterialTheme.colors.surface,
+ contentColor = MaterialTheme.colors.onSurface,
+ elevation = 8.dp,
+ navigationIcon = {
+ IconButton(onClick = {}) {
+ Icon(vectorResource(Res.drawable.ic_instagram))
+ }
+ },
+ actions = {
+ IconButton(onClick = {}) {
+ Icon(vectorResource(Res.drawable.ic_send))
+ }
+ }
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ TopAppBar(
+ title = {
+ Icon(
+ vectorResource(Res.drawable.ic_twitter),
+ tint = twitterColor,
+ modifier = Modifier.fillMaxWidth()
+ )
+ },
+ backgroundColor = MaterialTheme.colors.surface,
+ contentColor = MaterialTheme.colors.onSurface,
+ elevation = 8.dp,
+ navigationIcon = {
+ Image(
+ imageResource(Res.drawable.p6),
+ modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
+ .preferredSize(32.dp).clip(CircleShape)
+ )
+ },
+ actions = {
+ Icon(
+ Icons.Default.StarBorder,
+ modifier = Modifier.padding(horizontal = 8.dp)
+ )
+ }
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+}
+
+@Composable
+private fun BottomAppBarDemo() {
+ Spacer(modifier = Modifier.height(16.dp))
+ SubtitleText("Bottom app bars: Note bottom app bar support FAB cutouts when used with scafolds see demoUI crypto app")
+
+ BottomAppBar(
+ cutoutShape = CircleShape
+ ) {
+ IconButton(onClick = {}) {
+ Icon(Icons.Default.MoreHoriz)
+ }
+ TitleText(title = "Bottom App Bar")
+ }
+}
+
+@Composable
+private fun NavigationBarDemo() {
+ Spacer(modifier = Modifier.height(16.dp))
+ SubtitleText(subtitle = "Bottom Navigation Bars")
+ val navItemState = remember { mutableStateOf(NavType.HOME) }
+ BottomNavigation(backgroundColor = MaterialTheme.colors.surface) {
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.Home) },
+ selected = navItemState.value == NavType.HOME,
+ onClick = { navItemState.value = NavType.HOME },
+ label = { Text(text = Res.string.spotify_nav_home) },
+ )
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.Search) },
+ selected = navItemState.value == NavType.SEARCH,
+ onClick = { navItemState.value = NavType.SEARCH },
+ label = { Text(text = Res.string.spotify_nav_search) }
+ )
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.LibraryMusic) },
+ selected = navItemState.value == NavType.LIBRARY,
+ onClick = { navItemState.value = NavType.LIBRARY },
+ label = { Text(text = Res.string.spotify_nav_library) }
+ )
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ BottomNavigation {
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.ReadMore) },
+ selected = navItemState.value == NavType.HOME,
+ onClick = { navItemState.value = NavType.HOME },
+ )
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.Search) },
+ selected = navItemState.value == NavType.SEARCH,
+ onClick = { navItemState.value = NavType.SEARCH },
+ )
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.CleanHands) },
+ selected = navItemState.value == NavType.LIBRARY,
+ onClick = { navItemState.value = NavType.LIBRARY },
+ )
+ }
+}
+
+private enum class NavType {
+ HOME, SEARCH, LIBRARY
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Buttons.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Buttons.kt
new file mode 100644
index 0000000000..3c59a82108
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Buttons.kt
@@ -0,0 +1,109 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.FavoriteBorder
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.HorizontalGradient
+import androidx.compose.ui.graphics.VerticalGradient
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.theme.purple
+import org.jetbrains.compose.demo.widgets.theme.purple200
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@Composable
+fun Buttons() {
+ Column {
+ Button(onClick = {}, modifier = Modifier.padding(8.dp)) {
+ Text(text = "Main Button")
+ }
+ TextButton(onClick = {}, modifier = Modifier.padding(8.dp)) {
+ Text(text = "Text Button")
+ }
+ TextButton(onClick = {}, modifier = Modifier.padding(8.dp), enabled = false) {
+ Text(text = "Text Disabled")
+ }
+ Button(onClick = {}, modifier = Modifier.padding(8.dp), enabled = false) {
+ Text(text = "Disabled")
+ }
+ Button(
+ onClick = {},
+ modifier = Modifier.padding(8.dp),
+ elevation = ButtonConstants.defaultElevation()
+ ) {
+ Text(text = "Flat")
+ }
+ Button(
+ onClick = {},
+ modifier = Modifier.padding(8.dp),
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Text(text = "Rounded")
+ }
+ OutlinedButton(onClick = {}, modifier = Modifier.padding(8.dp)) {
+ Text(text = "Outline")
+ }
+ Button(onClick = {}, modifier = Modifier.padding(8.dp)) {
+ Row {
+ Icon(Icons.Default.FavoriteBorder, modifier = Modifier.padding(end = 4.dp))
+ Text(text = "Icon Button")
+ }
+ }
+ Button(onClick = {}, modifier = Modifier.padding(8.dp)) {
+ Text(text = "Icon Button")
+ Icon(Icons.Default.FavoriteBorder, modifier = Modifier.padding(start = 4.dp))
+ }
+ //custom background buttons
+ val outlineButtonColor = ButtonConstants.defaultOutlinedButtonColors(
+ contentColor = purple200,
+ )
+ val mainButtonColor = ButtonConstants.defaultButtonColors(
+ backgroundColor = purple,
+ contentColor = MaterialTheme.colors.surface
+ )
+ OutlinedButton(
+ colors = outlineButtonColor,
+ onClick = {},
+ modifier = Modifier.padding(8.dp)
+ ) {
+ Text(text = "Outline colors")
+ }
+ Button(colors = mainButtonColor, onClick = {}, modifier = Modifier.padding(8.dp)) {
+ Text(text = "Custom colors")
+ }
+
+ val horizontalGradient = HorizontalGradient(
+ colors = listOf(MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant),
+ 0f,
+ 250f
+ )
+ val verticalGradient = VerticalGradient(
+ colors = listOf(MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant),
+ startY = 0f,
+ endY = 100f
+ )
+ Text(
+ text = "Horizontal gradient",
+ style = typography.body2.copy(color = Color.White),
+ modifier = Modifier.padding(12.dp).clickable(onClick = {})
+ .clip(RoundedCornerShape(4.dp))
+ .background(brush = horizontalGradient).padding(12.dp)
+ )
+ Text(
+ text = "Vertical gradient",
+ style = typography.body1.copy(color = Color.White),
+ modifier = Modifier.padding(12.dp).clickable(onClick = {})
+ .clip(RoundedCornerShape(4.dp))
+ .background(brush = verticalGradient).padding(12.dp)
+ )
+ }
+}
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Chips.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Chips.kt
new file mode 100644
index 0000000000..98166ab23e
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Chips.kt
@@ -0,0 +1,131 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.platform.Res
+import org.jetbrains.compose.demo.widgets.theme.typography
+import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText
+import org.jetbrains.compose.demo.widgets.platform.imageResource
+
+@Composable
+fun Chips() {
+ // There is no in-built chips but you can make yours like below
+ SubtitleText(subtitle = "Custom chips with surface")
+ Column(modifier = Modifier.padding(8.dp)) {
+ YoutubeChip(selected = true, text = "Chip", modifier = Modifier.padding(horizontal = 8.dp))
+ Spacer(modifier = Modifier.padding(8.dp))
+ YoutubeChip(
+ selected = false,
+ text = "Inactive",
+ modifier = Modifier.padding(horizontal = 8.dp)
+ )
+ Spacer(modifier = Modifier.padding(8.dp))
+ CustomImageChip(text = "custom", imageId = Res.drawable.p2, selected = true)
+ Spacer(modifier = Modifier.padding(8.dp))
+ CustomImageChip(text = "custom2", imageId = Res.drawable.p6, selected = false)
+ }
+ SubtitleText(subtitle = "Buttons with circle clipping.")
+ Column(modifier = Modifier.padding(8.dp)) {
+ Button(
+ onClick = {},
+ modifier = Modifier.padding(8.dp).clip(CircleShape)
+ ) {
+ Text(text = "Chip button")
+ }
+ Button(
+ onClick = {},
+ enabled = false,
+ modifier = Modifier.padding(8.dp).clip(CircleShape)
+ ) {
+ Text(text = "Disabled chip")
+ }
+ }
+}
+
+
+//Inspired from jetcaster sample. I hope compose can add simple Chip UI element that can
+// support images or icons with multiple states.
+@Composable
+private fun CustomImageChip(
+ text: String,
+ imageId: String,
+ selected: Boolean,
+ modifier: Modifier = Modifier
+) {
+ Surface(
+ color = when {
+ selected -> MaterialTheme.colors.primary
+ else -> Color.Transparent
+ },
+ contentColor = when {
+ selected -> MaterialTheme.colors.onPrimary
+ else -> Color.LightGray
+ },
+ shape = RoundedCornerShape(16.dp),
+ border = BorderStroke(
+ width = 1.dp,
+ color = when {
+ selected -> MaterialTheme.colors.primary
+ else -> Color.LightGray
+ }
+ ),
+ modifier = modifier
+ ) {
+ Row(modifier = Modifier) {
+ Image(
+ imageResource(imageId),
+ modifier = Modifier.padding(8.dp).preferredSize(20.dp).clip(CircleShape)
+ )
+ Text(
+ text = text,
+ style = MaterialTheme.typography.body2,
+ modifier = Modifier.padding(end = 8.dp, top = 8.dp, bottom = 8.dp)
+ )
+ }
+ }
+}
+
+@Composable
+private fun YoutubeChip(selected: Boolean, text: String, modifier: Modifier = Modifier) {
+ Surface(
+ color = when {
+ selected -> MaterialTheme.colors.onSurface
+ else -> Color.Transparent
+ },
+ contentColor = when {
+ selected -> MaterialTheme.colors.onPrimary
+ else -> Color.LightGray
+ },
+ shape = CircleShape,
+ border = BorderStroke(
+ width = 1.dp,
+ color = when {
+ selected -> MaterialTheme.colors.primary
+ else -> Color.LightGray
+ }
+ ),
+ modifier = modifier
+ ) {
+ Text(
+ text = text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.body2,
+ modifier = Modifier.padding(8.dp)
+ )
+
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Loaders.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Loaders.kt
new file mode 100644
index 0000000000..df19865d62
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Loaders.kt
@@ -0,0 +1,41 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+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.material.CircularProgressIndicator
+import androidx.compose.material.LinearProgressIndicator
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun Loaders() {
+ AlignedColumn {
+ CircularProgressIndicator()
+ }
+
+ AlignedColumn {
+ CircularProgressIndicator(strokeWidth = 8.dp)
+ }
+
+ AlignedColumn {
+ LinearProgressIndicator()
+ }
+
+ AlignedColumn {
+ LinearProgressIndicator()
+ Text(text = "Loading with text...", modifier = Modifier.padding(8.dp))
+ }
+}
+@Composable
+private fun AlignedColumn(content: @Composable () -> Unit) {
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(16.dp)
+ ) {
+ content()
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/SnackBars.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/SnackBars.kt
new file mode 100644
index 0000000000..78946984a7
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/SnackBars.kt
@@ -0,0 +1,38 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Snackbar
+import androidx.compose.material.Text
+import androidx.compose.material.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@Composable
+fun SnackBars() {
+ Snackbar(modifier = Modifier.padding(4.dp)) {
+ Text(text = "This is a basic snackbar")
+ }
+ Snackbar(
+ modifier = Modifier.padding(4.dp),
+ action = {
+ TextButton(onClick = {}) {
+ Text(text = "Remove")
+ }
+ }
+ ) {
+ Text(text = "This is a basic Snackbar with action item")
+ }
+ Snackbar(
+ modifier = Modifier.padding(4.dp),
+ actionOnNewLine = true,
+ action = {
+ TextButton(onClick = {}) {
+ Text(text = "Remove")
+ }
+ }
+ ) {
+ Text(text = "Snackbar with action item below text")
+ }
+}
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/TextInputs.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/TextInputs.kt
new file mode 100644
index 0000000000..6995a64257
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/TextInputs.kt
@@ -0,0 +1,87 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.Icon
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material.icons.filled.Email
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@Composable
+fun TextInputs() {
+ var text by remember { mutableStateOf(TextFieldValue("")) }
+
+ // TODO Explore CoreTextField
+// CoreTextField(
+// value = text,
+// onValueChange = { newValue -> text = newValue },
+// modifier = Modifier.padding(8.dp).preferredSize(0.dp),
+// cursorColor = Color.Magenta
+// )
+ TextField(
+ value = text,
+ onValueChange = { newValue -> text = newValue },
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ label = { Text("label") },
+ placeholder = { Text("placeholder") }
+ )
+
+ OutlinedTextField(
+ value = text,
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ label = { Text(text = "Password") },
+ placeholder = { Text(text = "12334444") },
+ visualTransformation = PasswordVisualTransformation(),
+ onValueChange = {
+ text = it
+ },
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
+ )
+
+ OutlinedTextField(
+ value = text,
+ leadingIcon = { Icon(Icons.Default.Email) },
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
+ label = { Text(text = "Email address") },
+ placeholder = { Text(text = "Your email") },
+ onValueChange = {
+ text = it
+ }
+ )
+ OutlinedTextField(
+ value = text,
+ leadingIcon = { Icon(Icons.Default.Email) },
+ trailingIcon = { Icon(Icons.Default.Edit) },
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
+ label = { Text(text = "Email address") },
+ placeholder = { Text(text = "Your email") },
+ onValueChange = {
+ text = it
+ }
+ )
+
+ var numberText by remember { mutableStateOf(TextFieldValue("")) }
+ OutlinedTextField(value = numberText,
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+ label = { Text(text = "Phone number") },
+ placeholder = { Text(text = "88888888") },
+ onValueChange = {
+ numberText = it
+ }
+ )
+}
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Texts.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Texts.kt
new file mode 100644
index 0000000000..c14f8f9545
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Texts.kt
@@ -0,0 +1,97 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.theme.typography
+import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText
+
+@Composable
+fun TextViews() {
+ Column {
+ val textModifier = Modifier.padding(horizontal = 8.dp)
+
+ SubtitleText(subtitle = "Font weights")
+ Text(text = "Plain", modifier = textModifier)
+ Text(
+ text = "Medium Bold",
+ style = typography.body1.copy(fontWeight = FontWeight.Medium),
+ modifier = textModifier
+ )
+ Text(
+ text = "Bold",
+ style = typography.body1.copy(fontWeight = FontWeight.Bold),
+ modifier = textModifier
+ )
+ Text(
+ text = "Extra Bold",
+ style = typography.body1.copy(fontWeight = FontWeight.Bold),
+ modifier = textModifier
+ )
+
+ SubtitleText(subtitle = "Text decorations")
+ Text(text = "Default", modifier = textModifier)
+ Text(
+ text = "Underline",
+ textDecoration = TextDecoration.Underline,
+ modifier = textModifier
+ )
+ Text(
+ text = "LineThrough",
+ textDecoration = TextDecoration.LineThrough,
+ modifier = textModifier
+ )
+ Text(
+ text = "UnderlineLineThrough",
+ textDecoration = TextDecoration.combine(
+ listOf(
+ TextDecoration.Underline,
+ TextDecoration.LineThrough
+ )
+ ),
+ modifier = textModifier
+ )
+
+ SubtitleText(subtitle = "Overflow")
+ Text(
+ text = "Ellipsis: This text is supposed to ellipsis with max 1 line allowed for this",
+ overflow = TextOverflow.Ellipsis,
+ modifier = textModifier,
+ maxLines = 1
+ )
+ Text(
+ text = "Clip: This text is supposed to clip with max 1 line allowed for this",
+ overflow = TextOverflow.Clip,
+ modifier = textModifier,
+ maxLines = 1
+ )
+ }
+
+ /* TODO: https://github.com/JetBrains/compose-jb/issues/106
+ SubtitleText(subtitle = "font family dynamic")
+ Row {
+ Text(text = "Default", modifier = textModifier)
+ Text(
+ text = "Cursive",
+ style = typography.body1.copy(fontFamily = FontFamily.Cursive),
+ modifier = textModifier
+ )
+ Text(
+ text = "SansSerif",
+ style = typography.body1.copy(fontFamily = FontFamily.SansSerif),
+ modifier = textModifier
+ )
+ Text(
+ text = "Monospace",
+ style = typography.body1.copy(fontFamily = FontFamily.Monospace),
+ modifier = textModifier
+ )
+ } */
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Toggles.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Toggles.kt
new file mode 100644
index 0000000000..3fe981de23
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Toggles.kt
@@ -0,0 +1,83 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@Composable
+fun Toggles() {
+ Column {
+ 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 = SwitchConstants.defaultColors(checkedThumbColor = MaterialTheme.colors.primary),
+ modifier = Modifier.padding(8.dp),
+ onCheckedChange = { switched = it }
+ )
+
+ AlignedColumn {
+ var selected by remember { mutableStateOf("Kotlin") }
+
+ Row {
+ RadioButton(selected = selected == "Kotlin", onClick = { selected = "Kotlin" })
+ Text(
+ text = "Kotlin",
+ modifier = Modifier.clickable(onClick = { selected = "Kotlin" }).padding(start = 4.dp)
+ )
+ }
+ //Spacer(modifier = Modifier.size(4.dp))
+ Row {
+ RadioButton(selected = selected == "Java", onClick = { selected = "Java" })
+ Text(
+ text = "Java",
+ modifier = Modifier.clickable(onClick = { selected = "Java" }).padding(start = 4.dp)
+ )
+ }
+ //Spacer(modifier = Modifier.size(4.dp))
+ Row {
+ 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,
+ thumbColor = MaterialTheme.colors.secondary,
+ onValueChange = { newValue ->
+ sliderState2 = newValue
+ }
+ )
+ }
+}
+
+@Composable
+private fun AlignedColumn(content: @Composable () -> Unit) {
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(16.dp)
+ ) {
+ content()
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/UICards.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/UICards.kt
new file mode 100644
index 0000000000..bf11733fff
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/UICards.kt
@@ -0,0 +1,100 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ShoppingCart
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.data.DemoDataProvider
+import org.jetbrains.compose.demo.widgets.platform.Res
+import org.jetbrains.compose.demo.widgets.platform.imageResource
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@Composable
+fun UICards() {
+ val item = remember { DemoDataProvider.item }
+
+ Text(
+ text = "Inbuilt box as container for any Clipping/Alignment controls",
+ style = typography.subtitle1,
+ modifier = Modifier.padding(8.dp)
+ )
+ Card(
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ backgroundColor = MaterialTheme.colors.primary,
+ shape = RoundedCornerShape(topLeft = 16.dp, bottomRight = 16.dp)
+ ) {
+ Column {
+ Text(
+ text = item.title,
+ modifier = Modifier.padding(8.dp),
+ color = MaterialTheme.colors.onPrimary
+ )
+ Text(
+ text = item.subtitle,
+ modifier = Modifier.padding(8.dp),
+ color = MaterialTheme.colors.onPrimary
+ )
+ }
+ }
+ Divider()
+
+ Text(text = "Inbuilt Card", style = typography.subtitle1, modifier = Modifier.padding(8.dp))
+ Card(
+ modifier = Modifier.padding(16.dp).fillMaxWidth(),
+ shape = RoundedCornerShape(4.dp),
+ elevation = 4.dp
+ ) {
+ Row {
+ Image(
+ imageResource(Res.drawable.p3),
+ modifier = Modifier.preferredSize(60.dp)
+ )
+ Text(text = item.title, modifier = Modifier.padding(16.dp))
+ }
+ }
+ Divider()
+
+ Text(
+ text = "In-built ListItems",
+ style = typography.subtitle1,
+ modifier = Modifier.padding(8.dp)
+ )
+ ListItem(text = { Text(item.title) }, secondaryText = { Text(item.subtitle) })
+ Divider(modifier = Modifier.padding(4.dp))
+ ListItem(
+ text = { Text(item.title) },
+ secondaryText = { Text(item.subtitle) },
+ singleLineSecondaryText = false
+ )
+ Divider(modifier = Modifier.padding(4.dp))
+ ListItem(text = { Text(item.title) }, secondaryText = { Text(item.subtitle) }, icon = {
+ Image(
+ imageResource(Res.drawable.p3)
+ )
+ })
+ Divider(modifier = Modifier.padding(4.dp))
+ //I am not sure why this is not going multiline for secondaryText...
+ ListItem(
+ text = { Text(item.title) },
+ secondaryText = { Text(item.subtitle) },
+ icon = { Image(imageResource(Res.drawable.p1)) },
+ overlineText = { Text("Overline text") },
+ singleLineSecondaryText = false
+ )
+ Divider()
+ ListItem(
+ text = { Text(item.title) },
+ secondaryText = { Text(item.subtitle) },
+ icon = { Image(imageResource(Res.drawable.p2)) },
+ trailing = { Icon(Icons.Default.ShoppingCart) },
+ singleLineSecondaryText = false
+ )
+ Divider()
+
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/LayoutModifiers.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/LayoutModifiers.kt
new file mode 100644
index 0000000000..228604e27d
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/LayoutModifiers.kt
@@ -0,0 +1,11 @@
+package org.jetbrains.compose.demo.widgets.ui.utils
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
+
+fun Modifier.withoutWidthConstraints() = layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints.copy(maxWidth = Int.MAX_VALUE))
+ layout(constraints.maxWidth, placeable.height) {
+ placeable.place(0, 0)
+ }
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/ResizablePanel.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/ResizablePanel.kt
new file mode 100644
index 0000000000..42818d8e38
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/ResizablePanel.kt
@@ -0,0 +1,80 @@
+package org.jetbrains.compose.demo.widgets.ui.utils
+
+import androidx.compose.animation.animate
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.AmbientContentColor
+import androidx.compose.material.Icon
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material.icons.filled.ArrowForward
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+class PanelState {
+ val collapsedSize = 24.dp
+ var expandedSize by mutableStateOf(110.dp)
+ val expandedSizeMin = 90.dp
+ var isExpanded by mutableStateOf(true)
+ val splitter = SplitterState()
+}
+
+@Composable
+fun ResizablePanel(
+ modifier: Modifier,
+ state: PanelState,
+ title: String,
+ content: @Composable () -> Unit,
+) {
+ val alpha = animate(if (state.isExpanded) 1f else 0f, SpringSpec(stiffness = Spring.StiffnessLow),)
+
+ Box(modifier) {
+ Column {
+ Row(Modifier
+ .height(32.dp)
+ .padding(6.dp)
+ .clickable { state.isExpanded = !state.isExpanded }
+ ) {
+ Text(
+ text = if (state.isExpanded) title else "",
+ modifier = Modifier.fillMaxWidth().clipToBounds(),
+ fontSize = 14.sp
+ )
+
+ Icon(
+ if (state.isExpanded) Icons.Default.ArrowBack else Icons.Default.ArrowForward,
+ tint = AmbientContentColor.current,
+ modifier = Modifier
+ .size(24.dp)
+ .padding(end = 8.dp)
+ .padding(bottom = 4.dp)
+ )
+ }
+
+ if (state.isExpanded) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(1.dp)
+ .background(Color.Gray)
+ )
+
+ Column(Modifier.fillMaxSize().padding(top = 4.dp).graphicsLayer(alpha = alpha)) {
+ content()
+ }
+ }
+ }
+ }
+}
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/Text.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/Text.kt
new file mode 100644
index 0000000000..81254ab169
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/Text.kt
@@ -0,0 +1,23 @@
+package org.jetbrains.compose.demo.widgets.ui.utils
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@Composable
+fun SubtitleText(subtitle: String, modifier: Modifier = Modifier) {
+ Text(text = subtitle, style = typography.subtitle2, modifier = modifier.padding(8.dp))
+}
+
+@Composable
+fun TitleText(title: String, modifier: Modifier = Modifier) {
+ Text(
+ text = title,
+ style = typography.h6.copy(fontSize = 14.sp),
+ modifier = Modifier.padding(8.dp)
+ )
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/VerticalSplittable.kt b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/VerticalSplittable.kt
new file mode 100644
index 0000000000..90a97e4ec7
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/VerticalSplittable.kt
@@ -0,0 +1,90 @@
+package org.jetbrains.compose.demo.widgets.ui.utils
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.draggable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import org.jetbrains.compose.demo.widgets.platform.cursorForHorizontalResize
+
+class SplitterState {
+ var isResizing by mutableStateOf(false)
+ var isResizeEnabled by mutableStateOf(true)
+}
+
+@Composable
+fun VerticalSplittable(
+ modifier: Modifier,
+ splitterState: SplitterState,
+ onResize: (delta: Dp) -> Unit,
+ children: @Composable () -> Unit
+) = Layout({
+ children()
+ VerticalSplitter(splitterState, onResize)
+}, modifier, measureBlock = { measurables, constraints ->
+ require(measurables.size == 3)
+
+ val firstPlaceable = measurables[0].measure(constraints.copy(minWidth = 0))
+ val secondWidth = constraints.maxWidth - firstPlaceable.width
+ val secondPlaceable = measurables[1].measure(
+ Constraints(
+ minWidth = secondWidth,
+ maxWidth = secondWidth,
+ minHeight = constraints.maxHeight,
+ maxHeight = constraints.maxHeight
+ )
+ )
+ val splitterPlaceable = measurables[2].measure(constraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ firstPlaceable.place(0, 0)
+ secondPlaceable.place(firstPlaceable.width, 0)
+ splitterPlaceable.place(firstPlaceable.width, 0)
+ }
+})
+
+
+
+@Composable
+fun VerticalSplitter(
+ splitterState: SplitterState,
+ onResize: (delta: Dp) -> Unit,
+ color: Color = Color.DarkGray
+) = Box {
+ Box(
+ Modifier
+ .width(8.dp)
+ .fillMaxHeight()
+ .run {
+ if (splitterState.isResizeEnabled) {
+ this.draggable(
+ Orientation.Horizontal,
+ startDragImmediately = true,
+ onDragStarted = { splitterState.isResizing = true },
+ onDragStopped = { splitterState.isResizing = false }
+ ) {
+ onResize(it.toDp())
+ }.cursorForHorizontalResize()
+ } else {
+ this
+ }
+ }
+ )
+
+ Box(
+ Modifier
+ .width(1.dp)
+ .fillMaxHeight()
+ .background(color)
+ )
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/food6.jpg b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/food6.jpg
new file mode 100644
index 0000000000..2fdf68f5e1
Binary files /dev/null and b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/food6.jpg differ
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p1.jpeg b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p1.jpeg
new file mode 100644
index 0000000000..51e7bf4c7c
Binary files /dev/null and b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p1.jpeg differ
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p2.jpeg b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p2.jpeg
new file mode 100644
index 0000000000..6ac860ec0c
Binary files /dev/null and b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p2.jpeg differ
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p3.jpeg b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p3.jpeg
new file mode 100644
index 0000000000..24e4a48089
Binary files /dev/null and b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p3.jpeg differ
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p6.jpeg b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p6.jpeg
new file mode 100644
index 0000000000..a8d79560ff
Binary files /dev/null and b/examples/widgetsgallery/common/src/commonMain/resources/drawable-nodpi/p6.jpeg differ
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_instagram.xml b/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_instagram.xml
new file mode 100644
index 0000000000..abd196bb92
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_instagram.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_send.xml b/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_send.xml
new file mode 100644
index 0000000000..6ec2e846e1
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_send.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_twitter.xml b/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_twitter.xml
new file mode 100644
index 0000000000..ed54306c20
--- /dev/null
+++ b/examples/widgetsgallery/common/src/commonMain/resources/drawable/ic_twitter.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..21f9b94dbd
--- /dev/null
+++ b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,33 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.desktop.AppWindowAmbient
+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.composed
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.pointerMoveFilter
+import java.awt.Cursor
+
+actual fun Modifier.pointerMoveFilter(
+ onEnter: () -> Boolean,
+ onExit: () -> Boolean,
+ onMove: (Offset) -> Boolean
+): Modifier = this.pointerMoveFilter(onEnter = onEnter, onExit = onExit, onMove = onMove)
+
+actual fun Modifier.cursorForHorizontalResize(): Modifier = composed {
+ var isHover by remember { mutableStateOf(false) }
+
+ if (isHover) {
+ AppWindowAmbient.current!!.window.cursor = Cursor(Cursor.E_RESIZE_CURSOR)
+ } else {
+ AppWindowAmbient.current!!.window.cursor = Cursor.getDefaultCursor()
+ }
+
+ pointerMoveFilter(
+ onEnter = { isHover = true; true },
+ onExit = { isHover = false; true }
+ )
+}
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..7f1c30a862
--- /dev/null
+++ b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,14 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorXmlResource
+import androidx.compose.ui.res.imageResource as bitmapImage
+
+@Composable
+actual fun imageResource(res: String) =
+ bitmapImage(res)
+
+@Composable
+actual fun vectorResource(res: String): ImageVector =
+ vectorXmlResource(res)
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..6801a2ead9
--- /dev/null
+++ b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
@@ -0,0 +1,30 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.rememberScrollbarAdapter
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+
+@Composable
+actual fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: ScrollState
+) = androidx.compose.foundation.VerticalScrollbar(
+ rememberScrollbarAdapter(scrollState),
+ modifier
+)
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+actual fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: LazyListState,
+ itemCount: Int,
+ averageItemSize: Dp
+) = androidx.compose.foundation.VerticalScrollbar(
+ rememberScrollbarAdapter(scrollState, itemCount, averageItemSize),
+ modifier
+)
\ No newline at end of file
diff --git a/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..1e8abcd7af
--- /dev/null
+++ b/examples/widgetsgallery/common/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
@@ -0,0 +1,4 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+// TODO: https://github.com/JetBrains/compose-jb/issues/169
+actual fun isSystemInDarkTheme(): Boolean = false
\ No newline at end of file
diff --git a/examples/widgetsgallery/desktop/build.gradle.kts b/examples/widgetsgallery/desktop/build.gradle.kts
new file mode 100644
index 0000000000..0f2b000565
--- /dev/null
+++ b/examples/widgetsgallery/desktop/build.gradle.kts
@@ -0,0 +1,39 @@
+import org.jetbrains.compose.compose
+import org.jetbrains.compose.desktop.application.dsl.TargetFormat
+
+plugins {
+ kotlin("multiplatform") // kotlin("jvm") doesn't work well in IDEA/AndroidStudio (https://github.com/JetBrains/compose-jb/issues/22)
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ jvm {
+ withJava()
+ }
+
+ sourceSets {
+ named("jvmMain") {
+ dependencies {
+ implementation(compose.desktop.currentOs)
+ implementation(project(":common"))
+ }
+ }
+ }
+}
+
+compose.desktop {
+ application {
+ mainClass = "org.jetbrains.compose.demo.widgets.MainKt"
+
+ nativeDistributions {
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
+ packageName = "ComposeWidgetsGallery"
+
+ windows {
+ menu = true
+ // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html
+ upgradeUuid = "a61b72be-1b0c-4de5-9607-791c17687428"
+ }
+ }
+ }
+}
diff --git a/examples/widgetsgallery/desktop/src/jvmMain/kotlin/org/jetbrains/compose/demo/widgets/main.kt b/examples/widgetsgallery/desktop/src/jvmMain/kotlin/org/jetbrains/compose/demo/widgets/main.kt
new file mode 100644
index 0000000000..9c382b78be
--- /dev/null
+++ b/examples/widgetsgallery/desktop/src/jvmMain/kotlin/org/jetbrains/compose/demo/widgets/main.kt
@@ -0,0 +1,30 @@
+package org.jetbrains.compose.demo.widgets
+
+import androidx.compose.desktop.Window
+import androidx.compose.foundation.layout.ExperimentalLayout
+import androidx.compose.ui.unit.IntSize
+import org.jetbrains.compose.demo.widgets.ui.MainView
+import java.awt.Dimension
+import java.awt.Toolkit
+import javax.swing.SwingUtilities.invokeLater
+
+@OptIn(ExperimentalLayout::class)
+fun main() {
+ invokeLater {
+ Window(
+ title = "Widgets Gallery",
+ size = getPreferredWindowSize(600, 800),
+ ) {
+ MainView()
+ }
+ }
+}
+
+private fun getPreferredWindowSize(desiredWidth: Int, desiredHeight: Int): IntSize {
+ val screenSize: Dimension = Toolkit.getDefaultToolkit().screenSize
+ val preferredWidth: Int = (screenSize.width * 0.8f).toInt()
+ val preferredHeight: Int = (screenSize.height * 0.8f).toInt()
+ val width: Int = if (desiredWidth < preferredWidth) desiredWidth else preferredWidth
+ val height: Int = if (desiredHeight < preferredHeight) desiredHeight else preferredHeight
+ return IntSize(width, height)
+}
diff --git a/examples/widgetsgallery/gradle.properties b/examples/widgetsgallery/gradle.properties
new file mode 100644
index 0000000000..4d15d015f8
--- /dev/null
+++ b/examples/widgetsgallery/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/examples/widgetsgallery/gradle/wrapper/gradle-wrapper.jar b/examples/widgetsgallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..62d4c05355
Binary files /dev/null and b/examples/widgetsgallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/widgetsgallery/gradle/wrapper/gradle-wrapper.properties b/examples/widgetsgallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..ac33e9944a
--- /dev/null
+++ b/examples/widgetsgallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/examples/widgetsgallery/gradlew b/examples/widgetsgallery/gradlew
new file mode 100755
index 0000000000..2fe81a7d95
--- /dev/null
+++ b/examples/widgetsgallery/gradlew
@@ -0,0 +1,183 @@
+#!/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/widgetsgallery/gradlew.bat b/examples/widgetsgallery/gradlew.bat
new file mode 100644
index 0000000000..9618d8d960
--- /dev/null
+++ b/examples/widgetsgallery/gradlew.bat
@@ -0,0 +1,100 @@
+@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 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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+: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 %CMD_LINE_ARGS%
+
+: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/widgetsgallery/settings.gradle.kts b/examples/widgetsgallery/settings.gradle.kts
new file mode 100644
index 0000000000..9a0d554e9b
--- /dev/null
+++ b/examples/widgetsgallery/settings.gradle.kts
@@ -0,0 +1 @@
+include(":common", ":android", ":desktop")
diff --git a/examples/widgetsgallery/third_party/ComposeCookBook_LICENSE.txt b/examples/widgetsgallery/third_party/ComposeCookBook_LICENSE.txt
new file mode 100644
index 0000000000..928f38ee9d
--- /dev/null
+++ b/examples/widgetsgallery/third_party/ComposeCookBook_LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Gurupreet Singh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file