diff --git a/experimental/examples/widgets-gallery/.gitignore b/experimental/examples/widgets-gallery/.gitignore
new file mode 100644
index 0000000000..6ce3e8de8e
--- /dev/null
+++ b/experimental/examples/widgets-gallery/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/
+.DS_Store
+build/
+/captures
+.externalNativeBuild
+.cxx
+iosApp/Podfile.lock
+iosApp/Pods/*
+iosApp/WidgetsGallery.xcworkspace/*
+iosApp/WidgetsGallery.xcodeproj/*
+!iosApp/WidgetsGallery.xcodeproj/project.pbxproj
+shared/shared.podspec
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/.run/desktopApp.run.xml b/experimental/examples/widgets-gallery/.run/desktopApp.run.xml
new file mode 100644
index 0000000000..7af7e15f4b
--- /dev/null
+++ b/experimental/examples/widgets-gallery/.run/desktopApp.run.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/.run/iosApp.run.xml b/experimental/examples/widgets-gallery/.run/iosApp.run.xml
new file mode 100644
index 0000000000..9a44c1ee71
--- /dev/null
+++ b/experimental/examples/widgets-gallery/.run/iosApp.run.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/.run/iosApp_.run.xml b/experimental/examples/widgets-gallery/.run/iosApp_.run.xml
new file mode 100644
index 0000000000..8c18b8b909
--- /dev/null
+++ b/experimental/examples/widgets-gallery/.run/iosApp_.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/README.md b/experimental/examples/widgets-gallery/README.md
new file mode 100644
index 0000000000..9f9cda5fd9
--- /dev/null
+++ b/experimental/examples/widgets-gallery/README.md
@@ -0,0 +1,32 @@
+# Widgets 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, Android and iOS platforms,
+demonstrating how to use various Material widgets.
+
+## How to run
+
+Choose a run configuration for an appropriate target in IDE and run it.
+
+![run-configurations.png](run-configurations.png)
+
+To run on iOS device, please correct `iosApp/Configuration/TeamId.xcconfig` with your Apple Team ID.
+Alternatively, you may setup signing within XCode opening `iosApp/WidgetsGallery.xcworkspace` and then
+using "Signing & Capabilities" tab of `WidgetsGallery` target.
+
+Then choose **iosApp** configuration in IDE and run it
+(may also be referred as `WidgetsGallery` in the Run Configurations or `iosApp_` for Android studio).
+
+## Run on desktop via Gradle
+
+`./gradlew desktopApp:run`
+
+### Building native desktop distribution
+```
+./gradlew :desktop:packageDistributionForCurrentOS
+# outputs are written to desktop/build/compose/binaries
+```
diff --git a/experimental/examples/widgets-gallery/androidApp/build.gradle.kts b/experimental/examples/widgets-gallery/androidApp/build.gradle.kts
new file mode 100644
index 0000000000..ca1d24e891
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/build.gradle.kts
@@ -0,0 +1,33 @@
+plugins {
+ kotlin("multiplatform")
+ id("com.android.application")
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ android()
+ sourceSets {
+ val androidMain by getting {
+ dependencies {
+ implementation(project(":shared"))
+ implementation("androidx.appcompat:appcompat:1.5.1")
+ implementation("androidx.activity:activity-compose:1.6.1")
+ }
+ }
+ }
+}
+
+android {
+ compileSdk = 33
+ defaultConfig {
+ applicationId = "org.jetbrains.FallingBalls"
+ minSdk = 24
+ targetSdk = 33
+ versionCode = 1
+ versionName = "1.0"
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+}
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/AndroidManifest.xml b/experimental/examples/widgets-gallery/androidApp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..fc44b2f2bc
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/kotlin/org/jetbrains/compose/demo/widgets/MainActivity.kt b/experimental/examples/widgets-gallery/androidApp/src/main/kotlin/org/jetbrains/compose/demo/widgets/MainActivity.kt
new file mode 100644
index 0000000000..38f20fde9e
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/kotlin/org/jetbrains/compose/demo/widgets/MainActivity.kt
@@ -0,0 +1,15 @@
+package org.jetbrains.compose.demo.widgets
+
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import MainView
+
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MainView()
+ }
+ }
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/drawable-v24/ic_launcher_background.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/drawable-v24/ic_launcher_background.xml
new file mode 100644
index 0000000000..07d5da9cbf
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/drawable-v24/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..2b068d1146
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..eca70cfe52
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..eca70cfe52
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..a571e60098
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..61da551c55
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..c41dd28531
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..db5080a752
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..6dba46dab1
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..da31a871c8
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..15ac681720
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..b216f2d313
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..f25a419744
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..e96783ccce
Binary files /dev/null and b/experimental/examples/widgets-gallery/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/values-night/themes.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000000..710bfabb30
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/values/colors.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..6040e1f218
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/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/experimental/examples/widgets-gallery/androidApp/src/main/res/values/strings.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..49dfce059f
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ ComposeWidgets
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/androidApp/src/main/res/values/themes.xml b/experimental/examples/widgets-gallery/androidApp/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..50dcf6d3d9
--- /dev/null
+++ b/experimental/examples/widgets-gallery/androidApp/src/main/res/values/themes.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/build.gradle.kts b/experimental/examples/widgets-gallery/build.gradle.kts
new file mode 100644
index 0000000000..caa18ecb7a
--- /dev/null
+++ b/experimental/examples/widgets-gallery/build.gradle.kts
@@ -0,0 +1,22 @@
+buildscript {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ mavenLocal()
+ }
+ dependencies {
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
+ classpath("com.android.tools.build:gradle:7.3.1")
+ classpath("org.jetbrains.compose:compose-gradle-plugin:1.2.1")
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ mavenLocal()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+ }
+}
diff --git a/experimental/examples/widgets-gallery/desktopApp/build.gradle.kts b/experimental/examples/widgets-gallery/desktopApp/build.gradle.kts
new file mode 100644
index 0000000000..11fbaa2a69
--- /dev/null
+++ b/experimental/examples/widgets-gallery/desktopApp/build.gradle.kts
@@ -0,0 +1,40 @@
+import org.jetbrains.compose.desktop.application.dsl.TargetFormat
+
+plugins {
+ kotlin("multiplatform")
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ jvm {}
+ sourceSets {
+ val jvmMain by getting {
+ dependencies {
+ implementation(compose.desktop.currentOs)
+ implementation(project(":shared"))
+ }
+ }
+ }
+}
+
+compose.desktop {
+ application {
+ mainClass = "org.jetbrains.compose.demo.widgets.MainKt"
+
+ nativeDistributions {
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
+ packageName = "ComposeWidgetsGallery"
+ packageVersion = "1.0.0"
+
+ windows {
+ menu = true
+ // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html
+ upgradeUuid = "a61b72be-1b0c-4de5-9607-791c17687428"
+ }
+
+ macOS {
+ bundleID = "org.jetbrains.compose.demo.widgets"
+ }
+ }
+ }
+}
diff --git a/experimental/examples/widgets-gallery/desktopApp/src/jvmMain/kotlin/org/jetbrains/compose/demo/widgets/main.kt b/experimental/examples/widgets-gallery/desktopApp/src/jvmMain/kotlin/org/jetbrains/compose/demo/widgets/main.kt
new file mode 100644
index 0000000000..7d0d6ae801
--- /dev/null
+++ b/experimental/examples/widgets-gallery/desktopApp/src/jvmMain/kotlin/org/jetbrains/compose/demo/widgets/main.kt
@@ -0,0 +1,13 @@
+package org.jetbrains.compose.demo.widgets
+
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.WindowState
+import androidx.compose.ui.window.singleWindowApplication
+import MainView
+
+fun main() = singleWindowApplication(
+ title = "Widgets Gallery", state = WindowState(size = DpSize(800.dp, 800.dp))
+) {
+ MainView()
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/gradle.properties b/experimental/examples/widgets-gallery/gradle.properties
new file mode 100644
index 0000000000..00c8bf1343
--- /dev/null
+++ b/experimental/examples/widgets-gallery/gradle.properties
@@ -0,0 +1,14 @@
+kotlin.code.style=official
+xcodeproj=./iosApp
+kotlin.native.cocoapods.generate.wrapper=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx3g
+org.jetbrains.compose.experimental.jscanvas.enabled=true
+org.jetbrains.compose.experimental.macos.enabled=true
+org.jetbrains.compose.experimental.uikit.enabled=true
+kotlin.native.cacheKind=none
+kotlin.native.useEmbeddableCompilerJar=true
+kotlin.native.enableDependencyPropagation=false
+kotlin.mpp.enableGranularSourceSetsMetadata=true
+# Enable kotlin/native experimental memory model
+kotlin.native.binary.memoryModel=experimental
diff --git a/experimental/examples/widgets-gallery/gradle/wrapper/gradle-wrapper.jar b/experimental/examples/widgets-gallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..249e5832f0
Binary files /dev/null and b/experimental/examples/widgets-gallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/experimental/examples/widgets-gallery/gradle/wrapper/gradle-wrapper.properties b/experimental/examples/widgets-gallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..ae04661ee7
--- /dev/null
+++ b/experimental/examples/widgets-gallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/experimental/examples/widgets-gallery/gradlew b/experimental/examples/widgets-gallery/gradlew
new file mode 100755
index 0000000000..a69d9cb6c2
--- /dev/null
+++ b/experimental/examples/widgets-gallery/gradlew
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/experimental/examples/widgets-gallery/gradlew.bat b/experimental/examples/widgets-gallery/gradlew.bat
new file mode 100644
index 0000000000..53a6b238d4
--- /dev/null
+++ b/experimental/examples/widgets-gallery/gradlew.bat
@@ -0,0 +1,91 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/experimental/examples/widgets-gallery/iosApp/Configuration/TeamId.xcconfig b/experimental/examples/widgets-gallery/iosApp/Configuration/TeamId.xcconfig
new file mode 100644
index 0000000000..bf06eb27e9
--- /dev/null
+++ b/experimental/examples/widgets-gallery/iosApp/Configuration/TeamId.xcconfig
@@ -0,0 +1 @@
+TEAM_ID=
diff --git a/experimental/examples/widgets-gallery/iosApp/Podfile b/experimental/examples/widgets-gallery/iosApp/Podfile
new file mode 100644
index 0000000000..7ac5a5cc38
--- /dev/null
+++ b/experimental/examples/widgets-gallery/iosApp/Podfile
@@ -0,0 +1,5 @@
+target 'WidgetsGallery' do
+ use_frameworks!
+ platform :ios, '14.1'
+ pod 'shared', :path => '../shared'
+end
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/iosApp/WidgetsGallery.xcodeproj/project.pbxproj b/experimental/examples/widgets-gallery/iosApp/WidgetsGallery.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..703b0cdc04
--- /dev/null
+++ b/experimental/examples/widgets-gallery/iosApp/WidgetsGallery.xcodeproj/project.pbxproj
@@ -0,0 +1,380 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 2152FB042600AC8F00CF470E /* iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iosApp.swift */; };
+ C1FC908188C4E8695729CB06 /* Pods_WidgetsGallery.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DE96E47030356CE6AD9794A /* Pods_WidgetsGallery.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 1EB65E27D2C0F884D0A1A133 /* Pods-WidgetsGallery.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsGallery.debug.xcconfig"; path = "Target Support Files/Pods-WidgetsGallery/Pods-WidgetsGallery.debug.xcconfig"; sourceTree = ""; };
+ 2152FB032600AC8F00CF470E /* iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosApp.swift; sourceTree = ""; };
+ 3D7A606AB0AD7636269BD9D0 /* Pods-WidgetsGallery.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsGallery.release.xcconfig"; path = "Target Support Files/Pods-WidgetsGallery/Pods-WidgetsGallery.release.xcconfig"; sourceTree = ""; };
+ 7555FF7B242A565900829871 /* WidgetsGallery.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WidgetsGallery.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 8DE96E47030356CE6AD9794A /* Pods_WidgetsGallery.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WidgetsGallery.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ AB3632DC29227652001CCB65 /* TeamId.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = TeamId.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 9964867F0862B4D9FB6ABFC7 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C1FC908188C4E8695729CB06 /* Pods_WidgetsGallery.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 7555FF72242A565900829871 = {
+ isa = PBXGroup;
+ children = (
+ AB1DB47929225F7C00F7AF9C /* Configuration */,
+ 7555FF7D242A565900829871 /* iosApp */,
+ 7555FF7C242A565900829871 /* Products */,
+ E1DAFBE8E1CFC0878361EF0E /* Pods */,
+ B62309C7396AD7BF607A63B2 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 7555FF7C242A565900829871 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7555FF7B242A565900829871 /* WidgetsGallery.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 7555FF7D242A565900829871 /* iosApp */ = {
+ isa = PBXGroup;
+ children = (
+ 7555FF8C242A565B00829871 /* Info.plist */,
+ 2152FB032600AC8F00CF470E /* iosApp.swift */,
+ );
+ path = iosApp;
+ sourceTree = "";
+ };
+ AB1DB47929225F7C00F7AF9C /* Configuration */ = {
+ isa = PBXGroup;
+ children = (
+ AB3632DC29227652001CCB65 /* TeamId.xcconfig */,
+ );
+ path = Configuration;
+ sourceTree = "";
+ };
+ B62309C7396AD7BF607A63B2 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 8DE96E47030356CE6AD9794A /* Pods_WidgetsGallery.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ E1DAFBE8E1CFC0878361EF0E /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 1EB65E27D2C0F884D0A1A133 /* Pods-WidgetsGallery.debug.xcconfig */,
+ 3D7A606AB0AD7636269BD9D0 /* Pods-WidgetsGallery.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 7555FF7A242A565900829871 /* WidgetsGallery */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "WidgetsGallery" */;
+ buildPhases = (
+ E8D673591E7196AEA2EA10E2 /* [CP] Check Pods Manifest.lock */,
+ 7555FF77242A565900829871 /* Sources */,
+ 7555FF79242A565900829871 /* Resources */,
+ 9964867F0862B4D9FB6ABFC7 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = WidgetsGallery;
+ productName = iosApp;
+ productReference = 7555FF7B242A565900829871 /* WidgetsGallery.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 7555FF73242A565900829871 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1130;
+ LastUpgradeCheck = 1130;
+ ORGANIZATIONNAME = org.jetbrains;
+ TargetAttributes = {
+ 7555FF7A242A565900829871 = {
+ CreatedOnToolsVersion = 11.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "WidgetsGallery" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 7555FF72242A565900829871;
+ productRefGroup = 7555FF7C242A565900829871 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 7555FF7A242A565900829871 /* WidgetsGallery */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 7555FF79242A565900829871 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ E8D673591E7196AEA2EA10E2 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-WidgetsGallery-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 7555FF77242A565900829871 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2152FB042600AC8F00CF470E /* iosApp.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 7555FFA3242A565B00829871 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = AB3632DC29227652001CCB65 /* TeamId.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 7555FFA4242A565B00829871 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = AB3632DC29227652001CCB65 /* TeamId.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 7555FFA6242A565B00829871 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 1EB65E27D2C0F884D0A1A133 /* Pods-WidgetsGallery.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = "${TEAM_ID}";
+ ENABLE_PREVIEWS = YES;
+ INFOPLIST_FILE = iosApp/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.WidgetsGallery${TEAM_ID}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 7555FFA7242A565B00829871 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 3D7A606AB0AD7636269BD9D0 /* Pods-WidgetsGallery.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = "${TEAM_ID}";
+ ENABLE_PREVIEWS = YES;
+ INFOPLIST_FILE = iosApp/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.WidgetsGallery${TEAM_ID}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 7555FF76242A565900829871 /* Build configuration list for PBXProject "WidgetsGallery" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7555FFA3242A565B00829871 /* Debug */,
+ 7555FFA4242A565B00829871 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "WidgetsGallery" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7555FFA6242A565B00829871 /* Debug */,
+ 7555FFA7242A565B00829871 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 7555FF73242A565900829871 /* Project object */;
+}
diff --git a/experimental/examples/widgets-gallery/iosApp/iosApp/Info.plist b/experimental/examples/widgets-gallery/iosApp/iosApp/Info.plist
new file mode 100644
index 0000000000..9a269f5eaa
--- /dev/null
+++ b/experimental/examples/widgets-gallery/iosApp/iosApp/Info.plist
@@ -0,0 +1,48 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+
+ UILaunchScreen
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/experimental/examples/widgets-gallery/iosApp/iosApp/iosApp.swift b/experimental/examples/widgets-gallery/iosApp/iosApp/iosApp.swift
new file mode 100644
index 0000000000..b42016a6fc
--- /dev/null
+++ b/experimental/examples/widgets-gallery/iosApp/iosApp/iosApp.swift
@@ -0,0 +1,15 @@
+import UIKit
+import shared
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+ var window: UIWindow?
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ window = UIWindow(frame: UIScreen.main.bounds)
+ let mainViewController = Main_iosKt.MainViewController()
+ window?.rootViewController = mainViewController
+ window?.makeKeyAndVisible()
+ return true
+ }
+}
diff --git a/experimental/examples/widgets-gallery/run-configurations.png b/experimental/examples/widgets-gallery/run-configurations.png
new file mode 100644
index 0000000000..0840602f9b
Binary files /dev/null and b/experimental/examples/widgets-gallery/run-configurations.png differ
diff --git a/experimental/examples/widgets-gallery/settings.gradle.kts b/experimental/examples/widgets-gallery/settings.gradle.kts
new file mode 100644
index 0000000000..384af83852
--- /dev/null
+++ b/experimental/examples/widgets-gallery/settings.gradle.kts
@@ -0,0 +1,14 @@
+pluginManagement {
+ repositories {
+ google()
+ jcenter()
+ gradlePluginPortal()
+ mavenCentral()
+ }
+
+}
+rootProject.name = "widgets-gallery"
+
+include(":androidApp")
+include(":shared")
+include(":desktopApp")
diff --git a/experimental/examples/widgets-gallery/shared/build.gradle.kts b/experimental/examples/widgets-gallery/shared/build.gradle.kts
new file mode 100644
index 0000000000..c2aa57fc28
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/build.gradle.kts
@@ -0,0 +1,72 @@
+plugins {
+ kotlin("multiplatform")
+ kotlin("native.cocoapods")
+ id("com.android.library")
+ id("org.jetbrains.compose")
+}
+
+version = "1.0-SNAPSHOT"
+
+kotlin {
+ android()
+
+ jvm("desktop")
+
+ ios()
+ iosSimulatorArm64()
+
+ cocoapods {
+ summary = "Shared code for the sample"
+ homepage = "https://github.com/JetBrains/compose-jb"
+ ios.deploymentTarget = "14.1"
+ podfile = project.file("../iosApp/Podfile")
+ framework {
+ baseName = "shared"
+ }
+ }
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(compose.runtime)
+ implementation(compose.foundation)
+ implementation(compose.material)
+ implementation(compose.materialIconsExtended)
+ }
+ }
+ val androidMain by getting {
+ dependencies {
+ implementation("androidx.appcompat:appcompat:1.5.1")
+ implementation("androidx.core:core-ktx:1.8.0")
+ }
+ }
+ val iosMain by getting
+ val iosTest by getting
+ val iosSimulatorArm64Main by getting {
+ dependsOn(iosMain)
+ }
+ val iosSimulatorArm64Test by getting {
+ dependsOn(iosTest)
+ }
+
+ val desktopMain by getting {
+ dependencies {
+ implementation(compose.desktop.common)
+ }
+ }
+ }
+}
+
+android {
+ compileSdk = 33
+ sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
+ sourceSets["main"].res.srcDirs("src/androidMain/res", "src/commonMain/resources")
+ defaultConfig {
+ minSdk = 24
+ targetSdk = 33
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+}
diff --git a/experimental/examples/widgets-gallery/shared/src/androidMain/AndroidManifest.xml b/experimental/examples/widgets-gallery/shared/src/androidMain/AndroidManifest.xml
new file mode 100644
index 0000000000..3c65a79fe0
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/main.android.kt b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/main.android.kt
new file mode 100644
index 0000000000..370156e9fa
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/main.android.kt
@@ -0,0 +1,5 @@
+import androidx.compose.runtime.Composable
+import org.jetbrains.compose.demo.widgets.ui.MainView
+
+@Composable
+fun MainView() = MainView()
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..a49921fe64
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,6 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+
+actual fun Modifier.cursorForHorizontalResize() = this
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..552d284f9a
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,18 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.painter.Painter
+
+@Composable
+actual fun painterResource(res: String): Painter {
+ val id = drawableId(res)
+ return androidx.compose.ui.res.painterResource(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)
+ return field.get(drawableClass) as Int
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..7ec4cd59c7
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/experimental/examples/widgets-gallery/shared/src/androidMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..be73331ce2
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/DemoDataProvider.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/DemoDataProvider.kt
new file mode 100644
index 0000000000..65fd232eb0
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/DemoDataProvider.kt
@@ -0,0 +1,13 @@
+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",
+ )
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Item.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Item.kt
new file mode 100644
index 0000000000..03b8aee24b
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Item.kt
@@ -0,0 +1,8 @@
+package org.jetbrains.compose.demo.widgets.data.model
+
+data class Item(
+ val id: Int,
+ val title: String,
+ val subtitle: String,
+ val source: String = "demo source"
+)
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Tweet.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/data/model/Tweet.kt
new file mode 100644
index 0000000000..dd9df15318
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..c6b3b3af40
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,6 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+
+expect fun Modifier.cursorForHorizontalResize(): Modifier
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Res.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Res.kt
new file mode 100644
index 0000000000..10f57c7cd7
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Res.kt
@@ -0,0 +1,20 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+object Res {
+ object drawable {
+ 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..08e9d3b672
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,7 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.painter.Painter
+
+@Composable
+internal expect fun painterResource(res: String): Painter
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..972ceb7ed0
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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
+internal expect fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: ScrollState
+)
+
+@Composable
+internal expect fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: LazyListState,
+ itemCount: Int,
+ averageItemSize: Dp
+)
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..2a257d2964
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
@@ -0,0 +1,3 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+internal expect fun isSystemInDarkTheme(): Boolean
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Color.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Color.kt
new file mode 100644
index 0000000000..af32b07b1d
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Shape.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Shape.kt
new file mode 100644
index 0000000000..764221a766
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Theme.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Theme.kt
new file mode 100644
index 0000000000..04cca73b9d
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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
+internal 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Type.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/theme/Type.kt
new file mode 100644
index 0000000000..bda3b064fa
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/MainView.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/MainView.kt
new file mode 100644
index 0000000000..3827867189
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/MainView.kt
@@ -0,0 +1,160 @@
+package org.jetbrains.compose.demo.widgets.ui
+
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.LocalContentColor
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import org.jetbrains.compose.demo.widgets.platform.VerticalScrollbar
+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
+
+@Composable
+internal fun MainView() {
+ WidgetGalleryTheme {
+ Surface {
+ WidgetsPanel()
+ }
+ }
+}
+
+@Composable
+internal fun WidgetsPanel() {
+ val widgetsTypeState = rememberSaveable { mutableStateOf(WidgetsType.sortedValues.first()) }
+ val panelState = remember { PanelState() }
+
+ val animatedSize = if (panelState.splitter.isResizing) {
+ if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize
+ } else {
+ animateDpAsState(
+ if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize,
+ SpringSpec(stiffness = Spring.StiffnessLow)
+ ).value
+ }
+
+ 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(LocalDensity.current) {
+ val scrollState = rememberLazyListState()
+
+ val fontSize = 14.sp
+ val lineHeight = fontSize.toDp() * 1.5f
+
+ val sortedItems = WidgetsType.sortedValues
+ LazyColumn(
+ modifier = Modifier.fillMaxSize().withoutWidthConstraints(),
+ state = scrollState
+ ) {
+ items(sortedItems) {
+ WidgetsListItemViewImpl(it, widgetsTypeState, fontSize, lineHeight)
+ }
+ }
+
+ VerticalScrollbar(
+ Modifier.align(Alignment.CenterEnd),
+ scrollState,
+ sortedItems.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 }
+ .semantics {
+ set(SemanticsProperties.Role, Role.Button)
+ }
+ .height(height)
+ .padding(start = 16.dp)
+ ) {
+ val inFocusInteractionSource = remember { MutableInteractionSource() }
+ val inFocus by inFocusInteractionSource.collectIsHoveredAsState()
+ val textColor = LocalContentColor.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()
+ .hoverable(inFocusInteractionSource),
+ softWrap = true,
+ fontSize = fontSize,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1
+ )
+ }
+}
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetView.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetView.kt
new file mode 100644
index 0000000000..8cd6df8d9e
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetView.kt
@@ -0,0 +1,30 @@
+package org.jetbrains.compose.demo.widgets.ui
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.ui.Modifier
+import org.jetbrains.compose.demo.widgets.ui.screens.*
+
+@Composable
+internal fun WidgetsView(
+ widgetsTypeState: MutableState,
+ modifier: Modifier
+) {
+ Column(modifier = modifier.verticalScroll(state = rememberScrollState())) {
+ @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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetsType.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetsType.kt
new file mode 100644
index 0000000000..58035dc9e5
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/WidgetsType.kt
@@ -0,0 +1,32 @@
+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.lowercase() }
+ .mapIndexed { i, it ->
+ if (i == 0) it.replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase() else it.toString()
+ } 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/AppBars.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/AppBars.kt
new file mode 100644
index 0000000000..0a4c48d0bf
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/AppBars.kt
@@ -0,0 +1,161 @@
+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.painterResource
+import org.jetbrains.compose.demo.widgets.theme.twitterColor
+import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText
+import org.jetbrains.compose.demo.widgets.ui.utils.TitleText
+
+@Composable
+internal 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, contentDescription = "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(painterResource(Res.drawable.ic_instagram), contentDescription = "Instagram")
+ }
+ },
+ actions = {
+ IconButton(onClick = {}) {
+ Icon(painterResource(Res.drawable.ic_send), contentDescription = "Send")
+ }
+ }
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ TopAppBar(
+ title = {
+ Icon(
+ painterResource(Res.drawable.ic_twitter),
+ contentDescription = "Twitter",
+ tint = twitterColor,
+ modifier = Modifier.fillMaxWidth()
+ )
+ },
+ backgroundColor = MaterialTheme.colors.surface,
+ contentColor = MaterialTheme.colors.onSurface,
+ elevation = 8.dp,
+ navigationIcon = {
+ Image(
+ painterResource(Res.drawable.p6),
+ contentDescription = "",
+ modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
+ .requiredSize(32.dp).clip(CircleShape)
+ )
+ },
+ actions = {
+ Icon(
+ Icons.Default.StarBorder,
+ contentDescription = "",
+ 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, contentDescription = "")
+ }
+ 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, contentDescription = "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, contentDescription = "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, contentDescription = "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, contentDescription = "ReadMore") },
+ selected = navItemState.value == NavType.HOME,
+ onClick = { navItemState.value = NavType.HOME },
+ )
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.Search, contentDescription = "Search") },
+ selected = navItemState.value == NavType.SEARCH,
+ onClick = { navItemState.value = NavType.SEARCH },
+ )
+ BottomNavigationItem(
+ icon = { Icon(Icons.Outlined.CleanHands, contentDescription = "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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Buttons.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Buttons.kt
new file mode 100644
index 0000000000..1cd45d8d85
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Buttons.kt
@@ -0,0 +1,108 @@
+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.Brush
+import androidx.compose.ui.graphics.Color
+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
+internal 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 = ButtonDefaults.elevation()
+ ) {
+ 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, contentDescription = null, 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, contentDescription = null, modifier = Modifier.padding(start = 4.dp))
+ }
+ //custom background buttons
+ val outlineButtonColor = ButtonDefaults.outlinedButtonColors(
+ contentColor = purple200,
+ )
+ val mainButtonColor = ButtonDefaults.buttonColors(
+ 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 = Brush.horizontalGradient(
+ colors = listOf(MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant),
+ 0f,
+ 250f
+ )
+ val verticalGradient = Brush.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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Chips.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Chips.kt
new file mode 100644
index 0000000000..b4d1f6980a
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Chips.kt
@@ -0,0 +1,130 @@
+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.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.platform.painterResource
+import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText
+
+@Composable
+internal 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(
+ painterResource(imageId),
+ contentDescription = null,
+ modifier = Modifier.padding(8.dp).requiredSize(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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Loaders.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Loaders.kt
new file mode 100644
index 0000000000..2fce3bd346
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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
+internal 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/SnackBars.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/SnackBars.kt
new file mode 100644
index 0000000000..2fc777e63c
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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
+internal 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/TextInputs.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/TextInputs.kt
new file mode 100644
index 0000000000..13abef7cdc
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/TextInputs.kt
@@ -0,0 +1,86 @@
+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
+
+@Composable
+internal 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, contentDescription = "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, contentDescription = "Email") },
+ trailingIcon = { Icon(Icons.Default.Edit, contentDescription = "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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Texts.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Texts.kt
new file mode 100644
index 0000000000..2d34d9521e
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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
+internal 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Toggles.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Toggles.kt
new file mode 100644
index 0000000000..5bf8b06913
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/Toggles.kt
@@ -0,0 +1,71 @@
+package org.jetbrains.compose.demo.widgets.ui.screens
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+internal 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 = SwitchDefaults.colors(checkedThumbColor = MaterialTheme.colors.primary),
+ modifier = Modifier.padding(8.dp),
+ onCheckedChange = { switched = it }
+ )
+
+ AlignedColumn {
+ var selected by remember { mutableStateOf("Kotlin") }
+ for (lang in arrayOf("Kotlin", "Java", "Swift")) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(selected = selected == lang, onClick = { selected = lang })
+ Text(
+ text = lang,
+ modifier = Modifier.clickable(onClick = { selected = lang }).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,
+ colors = SliderDefaults.colors(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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/UICards.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/UICards.kt
new file mode 100644
index 0000000000..a274f08f43
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/screens/UICards.kt
@@ -0,0 +1,103 @@
+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.painterResource
+import org.jetbrains.compose.demo.widgets.theme.typography
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+internal 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(topStart = 16.dp, bottomEnd = 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(
+ painterResource(Res.drawable.p3),
+ contentDescription = null,
+ modifier = Modifier.requiredSize(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(
+ painterResource(Res.drawable.p3),
+ contentDescription = null
+ )
+ })
+ 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(painterResource(Res.drawable.p1), contentDescription = null) },
+ overlineText = { Text("Overline text") },
+ singleLineSecondaryText = false
+ )
+ Divider()
+ ListItem(
+ text = { Text(item.title) },
+ secondaryText = { Text(item.subtitle) },
+ icon = { Image(painterResource(Res.drawable.p2), contentDescription = null) },
+ trailing = { Icon(Icons.Default.ShoppingCart, contentDescription = null) },
+ singleLineSecondaryText = false
+ )
+ Divider()
+
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/LayoutModifiers.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/LayoutModifiers.kt
new file mode 100644
index 0000000000..228604e27d
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/ResizablePanel.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/ResizablePanel.kt
new file mode 100644
index 0000000000..35009ed6ee
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/ResizablePanel.kt
@@ -0,0 +1,91 @@
+package org.jetbrains.compose.demo.widgets.ui.utils
+
+import androidx.compose.animation.core.*
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Icon
+import androidx.compose.material.LocalContentColor
+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.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+class PanelState {
+ val collapsedSize = 40.dp
+ var expandedSize by mutableStateOf(110.dp)
+ val expandedSizeMin = 120.dp
+ var isExpanded by mutableStateOf(true)
+ val splitter = SplitterState()
+}
+
+@Composable
+internal fun ResizablePanel(
+ modifier: Modifier,
+ state: PanelState,
+ title: String,
+ content: @Composable () -> Unit,
+) {
+ val alpha = animateFloatAsState(
+ if (state.isExpanded) 1f else 0f,
+ SpringSpec(stiffness = Spring.StiffnessLow),
+ ).value
+
+ Box(modifier) {
+ Column {
+ Row(Modifier
+ .height(32.dp)
+ .padding(6.dp)
+ .semantics(mergeDescendants = false) {
+ val text = if (state.isExpanded) "Collapse" else "Expand"
+ set(SemanticsProperties.Text, listOf(
+ AnnotatedString("$text $title panel")
+ ))
+ set(SemanticsProperties.Role, Role.Button)
+ }
+ .clickable { state.isExpanded = !state.isExpanded }
+ ) {
+ Icon(
+ if (state.isExpanded) Icons.Default.ArrowBack else Icons.Default.ArrowForward,
+ contentDescription = if (state.isExpanded) "Collapse" else "Expand",
+ tint = LocalContentColor.current,
+ modifier = Modifier
+ .size(24.dp)
+ .padding(start = 2.dp, end = 2.dp, bottom = 2.dp)
+ )
+ Text(
+ text = if (state.isExpanded) title else "",
+ modifier = Modifier.fillMaxWidth().clipToBounds(),
+ fontSize = 14.sp
+ )
+ }
+
+ 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/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/Text.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/Text.kt
new file mode 100644
index 0000000000..5c4e02cc43
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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
+internal fun SubtitleText(subtitle: String, modifier: Modifier = Modifier) {
+ Text(text = subtitle, style = typography.subtitle2, modifier = modifier.padding(8.dp))
+}
+
+@Composable
+internal fun TitleText(title: String) {
+ Text(
+ text = title,
+ style = typography.h6.copy(fontSize = 14.sp),
+ modifier = Modifier.padding(8.dp)
+ )
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/VerticalSplittable.kt b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/VerticalSplittable.kt
new file mode 100644
index 0000000000..e966f52880
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/kotlin/org/jetbrains/compose/demo/widgets/ui/utils/VerticalSplittable.kt
@@ -0,0 +1,96 @@
+package org.jetbrains.compose.demo.widgets.ui.utils
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.draggable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberDraggableState
+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.graphics.Color
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.LocalDensity
+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
+internal fun VerticalSplittable(
+ modifier: Modifier,
+ splitterState: SplitterState,
+ onResize: (delta: Dp) -> Unit,
+ children: @Composable () -> Unit
+) = Layout({
+ children()
+ VerticalSplitter(splitterState, onResize)
+}, modifier, measurePolicy = { 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
+internal fun VerticalSplitter(
+ splitterState: SplitterState,
+ onResize: (delta: Dp) -> Unit,
+ color: Color = Color.DarkGray
+) = Box {
+ val density = LocalDensity.current
+ Box(
+ Modifier
+ .width(8.dp)
+ .fillMaxHeight()
+ .run {
+ if (splitterState.isResizeEnabled) {
+ this.draggable(
+ state = rememberDraggableState {
+ with(density) {
+ onResize(it.toDp())
+ }
+ },
+ orientation = Orientation.Horizontal,
+ startDragImmediately = true,
+ onDragStarted = { splitterState.isResizing = true },
+ onDragStopped = { splitterState.isResizing = false }
+ ).cursorForHorizontalResize()
+ } else {
+ this
+ }
+ }
+ )
+
+ Box(
+ Modifier
+ .width(1.dp)
+ .fillMaxHeight()
+ .background(color)
+ )
+}
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p1.jpeg b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p1.jpeg
new file mode 100644
index 0000000000..51e7bf4c7c
Binary files /dev/null and b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p1.jpeg differ
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p2.jpeg b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p2.jpeg
new file mode 100644
index 0000000000..6ac860ec0c
Binary files /dev/null and b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p2.jpeg differ
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p3.jpeg b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p3.jpeg
new file mode 100644
index 0000000000..24e4a48089
Binary files /dev/null and b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p3.jpeg differ
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p6.jpeg b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p6.jpeg
new file mode 100644
index 0000000000..a8d79560ff
Binary files /dev/null and b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable-nodpi/p6.jpeg differ
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_instagram.xml b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_instagram.xml
new file mode 100644
index 0000000000..abd196bb92
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_instagram.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_send.xml b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_send.xml
new file mode 100644
index 0000000000..6ec2e846e1
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_send.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_twitter.xml b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_twitter.xml
new file mode 100644
index 0000000000..ed54306c20
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/commonMain/resources/drawable/ic_twitter.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/main.desktop.kt b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/main.desktop.kt
new file mode 100644
index 0000000000..370156e9fa
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/main.desktop.kt
@@ -0,0 +1,5 @@
+import androidx.compose.runtime.Composable
+import org.jetbrains.compose.demo.widgets.ui.MainView
+
+@Composable
+fun MainView() = MainView()
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..4fa07e3eb5
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerIcon
+import androidx.compose.ui.input.pointer.pointerHoverIcon
+import androidx.compose.ui.input.pointer.pointerMoveFilter
+import java.awt.Cursor
+
+actual fun Modifier.cursorForHorizontalResize(): Modifier =
+ this.pointerHoverIcon(PointerIcon(Cursor(Cursor.E_RESIZE_CURSOR)))
diff --git a/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..ea49a5a059
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,7 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.painter.Painter
+
+@Composable
+actual fun painterResource(res: String): Painter = androidx.compose.ui.res.painterResource(res)
diff --git a/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..b4ba358b2f
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/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),
+ modifier
+)
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..c479cb8cf0
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/desktopMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
@@ -0,0 +1,5 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import org.jetbrains.skiko.SystemTheme
+
+actual fun isSystemInDarkTheme(): Boolean = org.jetbrains.skiko.currentSystemTheme == SystemTheme.DARK
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/main.ios.kt b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/main.ios.kt
new file mode 100644
index 0000000000..c64bf93623
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/main.ios.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
+ */
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.height
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Application
+import org.jetbrains.compose.demo.widgets.ui.MainView
+import platform.UIKit.UIViewController
+
+fun MainViewController() : UIViewController =
+ Application("Widgets Gallery") {
+ Column {
+ // To skip upper part of screen.
+ Box(
+ modifier = Modifier
+ .height(30.dp)
+ )
+ MainView()
+ }
+ }
diff --git a/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
new file mode 100644
index 0000000000..a49921fe64
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Mouse.kt
@@ -0,0 +1,6 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+
+actual fun Modifier.cursorForHorizontalResize() = this
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
new file mode 100644
index 0000000000..f23355510b
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Resources.kt
@@ -0,0 +1,19 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.painter.Painter
+
+@Composable
+internal actual fun painterResource(res: String): Painter =
+ // TODO: use resource API
+ object : Painter() {
+ override val intrinsicSize: Size
+ get() = Size(16f, 16f)
+
+ override fun DrawScope.onDraw() {
+ drawRect(color = Color.Red)
+ }
+ }
diff --git a/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/Scrollbar.kt
new file mode 100644
index 0000000000..ce20b90c19
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/iosMain/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
+internal actual fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: ScrollState
+) = Unit
+
+@Composable
+internal actual fun VerticalScrollbar(
+ modifier: Modifier,
+ scrollState: LazyListState,
+ itemCount: Int,
+ averageItemSize: Dp
+) = Unit
\ No newline at end of file
diff --git a/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
new file mode 100644
index 0000000000..aff87819fb
--- /dev/null
+++ b/experimental/examples/widgets-gallery/shared/src/iosMain/kotlin/org/jetbrains/compose/demo/widgets/platform/System.kt
@@ -0,0 +1,7 @@
+package org.jetbrains.compose.demo.widgets.platform
+
+import androidx.compose.runtime.Composable
+import org.jetbrains.skiko.SystemTheme
+
+@Composable
+internal actual fun isSystemInDarkTheme(): Boolean = org.jetbrains.skiko.currentSystemTheme == SystemTheme.DARK
diff --git a/experimental/examples/widgets-gallery/third_party/ComposeCookBook_LICENSE.txt b/experimental/examples/widgets-gallery/third_party/ComposeCookBook_LICENSE.txt
new file mode 100644
index 0000000000..928f38ee9d
--- /dev/null
+++ b/experimental/examples/widgets-gallery/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