@ -0,0 +1,10 @@
|
||||
*.iml |
||||
.gradle |
||||
/local.properties |
||||
/.idea/ |
||||
/.run |
||||
.DS_Store |
||||
build/ |
||||
/captures |
||||
.externalNativeBuild |
||||
.cxx |
@ -0,0 +1,25 @@
|
||||
# Widget gallery |
||||
|
||||
This example is derived from |
||||
[ComposeCookBook](https://github.com/Gurupreet/ComposeCookBook) project |
||||
by Gurupreet Singh ([@Gurupreet](https://github.com/Gurupreet)) |
||||
published under [MIT license](third_party/ComposeCookBook_LICENSE.txt). |
||||
|
||||
An example of Compose application for Desktop and Android platforms, |
||||
demonstrating how to use various Material widgets. |
||||
|
||||
### Running desktop application |
||||
``` |
||||
./gradlew :desktop:run |
||||
``` |
||||
|
||||
### Building native desktop distribution |
||||
``` |
||||
./gradlew :desktop:package |
||||
# outputs are written to desktop/build/compose/binaries |
||||
``` |
||||
|
||||
### Installing Android application on device/emulator |
||||
``` |
||||
./gradlew installDebug |
||||
``` |
@ -0,0 +1,25 @@
|
||||
plugins { |
||||
id("com.android.application") |
||||
kotlin("android") |
||||
id("org.jetbrains.compose") |
||||
} |
||||
|
||||
android { |
||||
compileSdkVersion(30) |
||||
|
||||
defaultConfig { |
||||
minSdkVersion(26) |
||||
targetSdkVersion(30) |
||||
versionCode = 1 |
||||
versionName = "1.0" |
||||
} |
||||
|
||||
compileOptions { |
||||
sourceCompatibility = JavaVersion.VERSION_1_8 |
||||
targetCompatibility = JavaVersion.VERSION_1_8 |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
implementation(project(":common")) |
||||
} |
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="org.jetbrains.compose.demo.widgets"> |
||||
|
||||
<application |
||||
android:allowBackup="true" |
||||
android:icon="@mipmap/ic_launcher" |
||||
android:label="@string/app_name" |
||||
android:roundIcon="@mipmap/ic_launcher_round" |
||||
android:supportsRtl="true" |
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"> |
||||
<activity |
||||
android:name="MainActivity" |
||||
android:label="@string/app_name"> |
||||
<intent-filter> |
||||
<action android:name="android.intent.action.MAIN" /> |
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
</activity> |
||||
</application> |
||||
|
||||
</manifest> |
@ -0,0 +1,15 @@
|
||||
package org.jetbrains.compose.demo.widgets |
||||
|
||||
import android.os.Bundle |
||||
import androidx.appcompat.app.AppCompatActivity |
||||
import androidx.compose.ui.platform.setContent |
||||
import org.jetbrains.compose.demo.widgets.ui.MainView |
||||
|
||||
class MainActivity : AppCompatActivity() { |
||||
override fun onCreate(savedInstanceState: Bundle?) { |
||||
super.onCreate(savedInstanceState) |
||||
setContent { |
||||
MainView() |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="108dp" |
||||
android:height="108dp" |
||||
android:viewportWidth="108" |
||||
android:viewportHeight="108"> |
||||
<path |
||||
android:fillColor="#3DDC84" |
||||
android:pathData="M0,0h108v108h-108z" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M9,0L9,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,0L19,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M29,0L29,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M39,0L39,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M49,0L49,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M59,0L59,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M69,0L69,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M79,0L79,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M89,0L89,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M99,0L99,108" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,9L108,9" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,19L108,19" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,29L108,29" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,39L108,39" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,49L108,49" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,59L108,59" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,69L108,69" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,79L108,79" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,89L108,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M0,99L108,99" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,29L89,29" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,39L89,39" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,49L89,49" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,59L89,59" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,69L89,69" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M19,79L89,79" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M29,19L29,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M39,19L39,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M49,19L49,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M59,19L59,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M69,19L69,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
<path |
||||
android:fillColor="#00000000" |
||||
android:pathData="M79,19L79,89" |
||||
android:strokeWidth="0.8" |
||||
android:strokeColor="#33FFFFFF" /> |
||||
</vector> |
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:aapt="http://schemas.android.com/aapt" |
||||
android:width="108dp" |
||||
android:height="108dp" |
||||
android:viewportWidth="108" |
||||
android:viewportHeight="108"> |
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> |
||||
<aapt:attr name="android:fillColor"> |
||||
<gradient |
||||
android:endX="85.84757" |
||||
android:endY="92.4963" |
||||
android:startX="42.9492" |
||||
android:startY="49.59793" |
||||
android:type="linear"> |
||||
<item |
||||
android:color="#44000000" |
||||
android:offset="0.0" /> |
||||
<item |
||||
android:color="#00000000" |
||||
android:offset="1.0" /> |
||||
</gradient> |
||||
</aapt:attr> |
||||
</path> |
||||
<path |
||||
android:fillColor="#FFFFFF" |
||||
android:fillType="nonZero" |
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" |
||||
android:strokeWidth="1" |
||||
android:strokeColor="#00000000" /> |
||||
</vector> |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<background android:drawable="@drawable/ic_launcher_background" /> |
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" /> |
||||
</adaptive-icon> |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<background android:drawable="@drawable/ic_launcher_background" /> |
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" /> |
||||
</adaptive-icon> |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools"> |
||||
<!-- <!– Base application theme. –>--> |
||||
<!-- <style name="Theme.ComposeCookBook" parent="Theme.MaterialComponents.DayNight.DarkActionBar">--> |
||||
<!-- <!– Primary brand color. –>--> |
||||
<!-- <item name="colorPrimary">@color/green_200</item>--> |
||||
<!-- <item name="colorPrimaryVariant">@color/green_700</item>--> |
||||
<!-- <item name="colorOnPrimary">@color/black</item>--> |
||||
<!-- <!– Secondary brand color. –>--> |
||||
<!-- <item name="colorSecondary">@color/teal_200</item>--> |
||||
<!-- <item name="colorSecondaryVariant">@color/teal_200</item>--> |
||||
<!-- <item name="colorOnSecondary">@color/black</item>--> |
||||
<!-- <!– Status bar color. –>--> |
||||
<!-- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>--> |
||||
<!-- <!– Customize your theme here. –>--> |
||||
<!-- </style>--> |
||||
</resources> |
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<color name="green_200">#ffa5d6a7</color> |
||||
<color name="green_500">#ff4caf50</color> |
||||
<color name="green_700">#ff388e3c</color> |
||||
<color name="teal_200">#FF03DAC5</color> |
||||
<color name="teal_700">#ff80deea</color> |
||||
<color name="purple">#FF833AB4</color> |
||||
<color name="black">#FF000000</color> |
||||
<color name="white">#FFFFFFFF</color> |
||||
</resources> |
@ -0,0 +1,3 @@
|
||||
<resources> |
||||
<string name="app_name">ComposeWidgets</string> |
||||
</resources> |
@ -0,0 +1,26 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools"> |
||||
<!-- <!– Base application theme. –>--> |
||||
<!-- <style name="Theme.ComposeCookBook" parent="Theme.MaterialComponents.DayNight.DarkActionBar">--> |
||||
<!-- <!– Primary brand color. –>--> |
||||
<!-- <item name="colorPrimary">@color/green_500</item>--> |
||||
<!-- <item name="colorPrimaryVariant">@color/green_700</item>--> |
||||
<!-- <item name="colorOnPrimary">@color/white</item>--> |
||||
<!-- <!– Secondary brand color. –>--> |
||||
<!-- <item name="colorSecondary">@color/teal_200</item>--> |
||||
<!-- <item name="colorSecondaryVariant">@color/teal_700</item>--> |
||||
<!-- <item name="colorOnSecondary">@color/black</item>--> |
||||
<!-- <!– Status bar color. –>--> |
||||
<!-- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>--> |
||||
<!-- <!– Customize your theme here. –>--> |
||||
<!-- <item name="android:windowContentTransitions">true</item>--> |
||||
<!-- </style>--> |
||||
|
||||
<!-- <style name="Theme.ComposeCookBook.NoActionBar">--> |
||||
<!-- <item name="windowActionBar">false</item>--> |
||||
<!-- <item name="windowNoTitle">true</item>--> |
||||
<!-- </style>--> |
||||
|
||||
<!-- <style name="Theme.ComposeCookBook.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />--> |
||||
|
||||
<!-- <style name="Theme.ComposeCookBook.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />--> |
||||
</resources> |
@ -0,0 +1,24 @@
|
||||
buildscript { |
||||
repositories { |
||||
// TODO: remove after new build is published |
||||
mavenLocal() |
||||
google() |
||||
jcenter() |
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") |
||||
} |
||||
|
||||
dependencies { |
||||
// __LATEST_COMPOSE_RELEASE_VERSION__ |
||||
classpath("org.jetbrains.compose:compose-gradle-plugin:0.3.0-build135") |
||||
classpath("com.android.tools.build:gradle:4.0.1") |
||||
classpath(kotlin("gradle-plugin", version = "1.4.21")) |
||||
} |
||||
} |
||||
|
||||
allprojects { |
||||
repositories { |
||||
google() |
||||
jcenter() |
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") |
||||
} |
||||
} |
@ -0,0 +1,60 @@
|
||||
import org.jetbrains.compose.compose |
||||
|
||||
plugins { |
||||
id("com.android.library") |
||||
kotlin("multiplatform") |
||||
id("org.jetbrains.compose") |
||||
} |
||||
|
||||
kotlin { |
||||
android() |
||||
jvm("desktop") |
||||
|
||||
sourceSets { |
||||
named("commonMain") { |
||||
dependencies { |
||||
api(compose.runtime) |
||||
api(compose.foundation) |
||||
api(compose.material) |
||||
api(compose.materialIconsExtended) |
||||
} |
||||
} |
||||
named("androidMain") { |
||||
kotlin.srcDirs("src/jvmMain/kotlin") |
||||
dependencies { |
||||
api("androidx.appcompat:appcompat:1.1.0") |
||||
api("androidx.core:core-ktx:1.3.1") |
||||
} |
||||
} |
||||
named("desktopMain") { |
||||
kotlin.srcDirs("src/jvmMain/kotlin") |
||||
resources.srcDirs("src/commonMain/resources") |
||||
dependencies { |
||||
api(compose.desktop.common) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
android { |
||||
compileSdkVersion(30) |
||||
|
||||
defaultConfig { |
||||
minSdkVersion(21) |
||||
targetSdkVersion(30) |
||||
versionCode = 1 |
||||
versionName = "1.0" |
||||
} |
||||
|
||||
compileOptions { |
||||
sourceCompatibility = JavaVersion.VERSION_1_8 |
||||
targetCompatibility = JavaVersion.VERSION_1_8 |
||||
} |
||||
|
||||
sourceSets { |
||||
named("main") { |
||||
manifest.srcFile("src/androidMain/AndroidManifest.xml") |
||||
res.srcDirs("src/androidMain/res", "src/commonMain/resources") |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest package="org.jetbrains.compose.demo.widgets"/> |
@ -0,0 +1,12 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.geometry.Offset |
||||
|
||||
actual fun Modifier.pointerMoveFilter( |
||||
onEnter: () -> Boolean, |
||||
onExit: () -> Boolean, |
||||
onMove: (Offset) -> Boolean |
||||
): Modifier = this |
||||
|
||||
actual fun Modifier.cursorForHorizontalResize() = this |
@ -0,0 +1,27 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.graphics.ImageBitmap |
||||
import androidx.compose.ui.graphics.vector.ImageVector |
||||
import org.jetbrains.compose.demo.widgets.R |
||||
|
||||
@Composable |
||||
actual fun imageResource(res: String): ImageBitmap { |
||||
val id = drawableId(res) |
||||
return androidx.compose.ui.res.imageResource(id) |
||||
} |
||||
|
||||
@Composable |
||||
actual fun vectorResource(res: String): ImageVector { |
||||
val id = drawableId(res) |
||||
return androidx.compose.ui.res.vectorResource(id) |
||||
} |
||||
|
||||
// TODO: improve resource loading |
||||
private fun drawableId(res: String): Int { |
||||
val imageName = res.substringAfterLast("/").substringBeforeLast(".") |
||||
val drawableClass = R.drawable::class.java |
||||
val field = drawableClass.getDeclaredField(imageName) |
||||
val idValue = field.get(drawableClass) as Integer |
||||
return idValue.toInt() |
||||
} |
@ -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 |
@ -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() |
@ -0,0 +1,14 @@
|
||||
package org.jetbrains.compose.demo.widgets.data |
||||
|
||||
import org.jetbrains.compose.demo.widgets.data.model.Item |
||||
import org.jetbrains.compose.demo.widgets.data.model.Tweet |
||||
import org.jetbrains.compose.demo.widgets.platform.Res |
||||
|
||||
object DemoDataProvider { |
||||
val item = Item( |
||||
1, |
||||
"Awesome List Item", |
||||
"Very awesome list item has very awesome subtitle. This is bit long", |
||||
Res.drawable.food6 |
||||
) |
||||
} |
@ -0,0 +1,9 @@
|
||||
package org.jetbrains.compose.demo.widgets.data.model |
||||
|
||||
data class Item( |
||||
val id: Int, |
||||
val title: String, |
||||
val subtitle: String, |
||||
val imageId: String, |
||||
val source: String = "demo source" |
||||
) |
@ -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 |
||||
) |
@ -0,0 +1,12 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.geometry.Offset |
||||
|
||||
expect fun Modifier.pointerMoveFilter( |
||||
onEnter: () -> Boolean = { true }, |
||||
onExit: () -> Boolean = { true }, |
||||
onMove: (Offset) -> Boolean = { true } |
||||
): Modifier |
||||
|
||||
expect fun Modifier.cursorForHorizontalResize(): Modifier |
@ -0,0 +1,21 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
object Res { |
||||
object drawable { |
||||
val food6 = "drawable-nodpi/food6.jpg" |
||||
val p1 = "drawable-nodpi/p1.jpeg" |
||||
val p2 = "drawable-nodpi/p2.jpeg" |
||||
val p3 = "drawable-nodpi/p3.jpeg" |
||||
val p6 = "drawable-nodpi/p6.jpeg" |
||||
|
||||
val ic_instagram = "drawable/ic_instagram.xml" |
||||
val ic_send = "drawable/ic_send.xml" |
||||
val ic_twitter = "drawable/ic_twitter.xml" |
||||
} |
||||
|
||||
object string { |
||||
val spotify_nav_home = "Home" |
||||
val spotify_nav_search = "Search" |
||||
val spotify_nav_library = "Your Library" |
||||
} |
||||
} |
@ -0,0 +1,12 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.graphics.ImageBitmap |
||||
import androidx.compose.ui.graphics.vector.ImageVector |
||||
|
||||
@Composable |
||||
expect fun imageResource(res: String): ImageBitmap |
||||
|
||||
@Composable |
||||
expect fun vectorResource(res: String): ImageVector |
||||
|
@ -0,0 +1,21 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.foundation.ScrollState |
||||
import androidx.compose.foundation.lazy.LazyListState |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.Dp |
||||
|
||||
@Composable |
||||
expect fun VerticalScrollbar( |
||||
modifier: Modifier, |
||||
scrollState: ScrollState |
||||
) |
||||
|
||||
@Composable |
||||
expect fun VerticalScrollbar( |
||||
modifier: Modifier, |
||||
scrollState: LazyListState, |
||||
itemCount: Int, |
||||
averageItemSize: Dp |
||||
) |
@ -0,0 +1,3 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
expect fun isSystemInDarkTheme(): Boolean |
@ -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) |
@ -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) |
||||
) |
@ -0,0 +1,136 @@
|
||||
package org.jetbrains.compose.demo.widgets.theme |
||||
|
||||
import androidx.compose.material.MaterialTheme |
||||
import androidx.compose.material.darkColors |
||||
import androidx.compose.material.lightColors |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.graphics.Color |
||||
import org.jetbrains.compose.demo.widgets.platform.isSystemInDarkTheme |
||||
import org.jetbrains.compose.demo.widgets.theme.ColorPallet.* |
||||
|
||||
// dark palettes |
||||
private val DarkGreenColorPalette = darkColors( |
||||
primary = green200, |
||||
primaryVariant = green700, |
||||
secondary = teal200, |
||||
background = Color.Black, |
||||
surface = Color.Black, |
||||
onPrimary = Color.Black, |
||||
onSecondary = Color.White, |
||||
onBackground = Color.White, |
||||
onSurface = Color.White, |
||||
error = Color.Red, |
||||
) |
||||
|
||||
private val DarkPurpleColorPalette = darkColors( |
||||
primary = purple200, |
||||
primaryVariant = purple700, |
||||
secondary = teal200, |
||||
background = Color.Black, |
||||
surface = Color.Black, |
||||
onPrimary = Color.Black, |
||||
onSecondary = Color.White, |
||||
onBackground = Color.White, |
||||
onSurface = Color.White, |
||||
error = Color.Red, |
||||
) |
||||
|
||||
private val DarkBlueColorPalette = darkColors( |
||||
primary = blue200, |
||||
primaryVariant = blue700, |
||||
secondary = teal200, |
||||
background = Color.Black, |
||||
surface = Color.Black, |
||||
onPrimary = Color.Black, |
||||
onSecondary = Color.White, |
||||
onBackground = Color.White, |
||||
onSurface = Color.White, |
||||
error = Color.Red, |
||||
) |
||||
|
||||
private val DarkOrangeColorPalette = darkColors( |
||||
primary = orange200, |
||||
primaryVariant = orange700, |
||||
secondary = teal200, |
||||
background = Color.Black, |
||||
surface = Color.Black, |
||||
onPrimary = Color.Black, |
||||
onSecondary = Color.White, |
||||
onBackground = Color.White, |
||||
onSurface = Color.White, |
||||
error = Color.Red, |
||||
) |
||||
|
||||
// Light pallets |
||||
private val LightGreenColorPalette = lightColors( |
||||
primary = green500, |
||||
primaryVariant = green700, |
||||
secondary = teal200, |
||||
background = Color.White, |
||||
surface = Color.White, |
||||
onPrimary = Color.White, |
||||
onSecondary = Color.Black, |
||||
onBackground = Color.Black, |
||||
onSurface = Color.Black |
||||
) |
||||
|
||||
private val LightPurpleColorPalette = lightColors( |
||||
primary = purple, |
||||
primaryVariant = purple700, |
||||
secondary = teal200, |
||||
background = Color.White, |
||||
surface = Color.White, |
||||
onPrimary = Color.White, |
||||
onSecondary = Color.Black, |
||||
onBackground = Color.Black, |
||||
onSurface = Color.Black |
||||
) |
||||
|
||||
private val LightBlueColorPalette = lightColors( |
||||
primary = blue500, |
||||
primaryVariant = blue700, |
||||
secondary = teal200, |
||||
background = Color.White, |
||||
surface = Color.White, |
||||
onPrimary = Color.White, |
||||
onSecondary = Color.Black, |
||||
onBackground = Color.Black, |
||||
onSurface = Color.Black |
||||
) |
||||
|
||||
private val LightOrangeColorPalette = lightColors( |
||||
primary = orange500, |
||||
primaryVariant = orange700, |
||||
secondary = teal200, |
||||
background = Color.White, |
||||
surface = Color.White, |
||||
onPrimary = Color.White, |
||||
onSecondary = Color.Black, |
||||
onBackground = Color.Black, |
||||
onSurface = Color.Black |
||||
) |
||||
|
||||
enum class ColorPallet { |
||||
PURPLE, GREEN, ORANGE, BLUE |
||||
} |
||||
|
||||
@Composable |
||||
fun WidgetGalleryTheme( |
||||
darkTheme: Boolean = isSystemInDarkTheme(), |
||||
colorPallet: ColorPallet = GREEN, |
||||
content: @Composable() () -> Unit, |
||||
) { |
||||
val colors = when (colorPallet) { |
||||
GREEN -> if (darkTheme) DarkGreenColorPalette else LightGreenColorPalette |
||||
PURPLE -> if (darkTheme) DarkPurpleColorPalette else LightPurpleColorPalette |
||||
ORANGE -> if (darkTheme) DarkOrangeColorPalette else LightOrangeColorPalette |
||||
BLUE -> if (darkTheme) DarkBlueColorPalette else LightBlueColorPalette |
||||
} |
||||
|
||||
MaterialTheme( |
||||
colors = colors, |
||||
typography = typography, |
||||
shapes = shapes, |
||||
content = content |
||||
) |
||||
} |
@ -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 |
||||
), |
||||
) |
@ -0,0 +1,154 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui |
||||
|
||||
import androidx.compose.animation.animate |
||||
import androidx.compose.animation.core.Spring |
||||
import androidx.compose.animation.core.SpringSpec |
||||
import androidx.compose.foundation.clickable |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.foundation.lazy.LazyColumnFor |
||||
import androidx.compose.foundation.lazy.rememberLazyListState |
||||
import androidx.compose.material.* |
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.runtime.savedinstancestate.savedInstanceState |
||||
import androidx.compose.ui.Alignment |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.draw.clipToBounds |
||||
import androidx.compose.ui.platform.AmbientDensity |
||||
import androidx.compose.ui.selection.DisableSelection |
||||
import androidx.compose.ui.text.style.TextOverflow |
||||
import androidx.compose.ui.unit.* |
||||
import org.jetbrains.compose.demo.widgets.platform.VerticalScrollbar |
||||
import org.jetbrains.compose.demo.widgets.platform.pointerMoveFilter |
||||
import org.jetbrains.compose.demo.widgets.theme.WidgetGalleryTheme |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.PanelState |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.ResizablePanel |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.VerticalSplittable |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.withoutWidthConstraints |
||||
import kotlin.ranges.coerceAtLeast |
||||
|
||||
@Composable |
||||
fun MainView() { |
||||
DisableSelection { |
||||
WidgetGalleryTheme() { |
||||
WidgetsPanel() |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
fun WidgetsPanel() { |
||||
val widgetsTypeState = savedInstanceState { WidgetsType.sortedValues.first() } |
||||
val panelState = remember { PanelState() } |
||||
|
||||
val animatedSize = if (panelState.splitter.isResizing) { |
||||
if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize |
||||
} else { |
||||
animate( |
||||
if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize, |
||||
SpringSpec(stiffness = Spring.StiffnessLow) |
||||
) |
||||
} |
||||
|
||||
VerticalSplittable( |
||||
Modifier.fillMaxSize(), |
||||
panelState.splitter, |
||||
onResize = { |
||||
panelState.expandedSize = |
||||
(panelState.expandedSize + it).coerceAtLeast(panelState.expandedSizeMin) |
||||
} |
||||
) { |
||||
ResizablePanel( |
||||
Modifier.width(animatedSize).fillMaxHeight(), |
||||
title = "Widgets", |
||||
state = panelState |
||||
) { |
||||
WidgetsListView(widgetsTypeState) |
||||
} |
||||
|
||||
Box { |
||||
Column { |
||||
WidgetsView( |
||||
widgetsTypeState, |
||||
modifier = Modifier.weight(1f) |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
private fun WidgetsListView(widgetsTypeState: MutableState<WidgetsType>) { |
||||
Box { |
||||
with(AmbientDensity.current) { |
||||
val scrollState = rememberLazyListState() |
||||
|
||||
val fontSize = 14.sp |
||||
val lineHeight = fontSize.toDp() * 1.5f |
||||
|
||||
val items = WidgetsType.sortedValues |
||||
LazyColumnFor( |
||||
items, |
||||
modifier = Modifier.fillMaxSize().withoutWidthConstraints(), |
||||
state = scrollState, |
||||
itemContent = { WidgetsListItemViewImpl(it, widgetsTypeState, fontSize, lineHeight) } |
||||
) |
||||
|
||||
VerticalScrollbar( |
||||
Modifier.align(Alignment.CenterEnd), |
||||
scrollState, |
||||
items.size, |
||||
lineHeight |
||||
) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
@Composable |
||||
private fun WidgetsListItemViewImpl( |
||||
widgetsType: WidgetsType, |
||||
widgetsTypeState: MutableState<WidgetsType>, |
||||
fontSize: TextUnit, |
||||
height: Dp |
||||
) { |
||||
val isCurrent = widgetsTypeState.value == widgetsType |
||||
|
||||
Row( |
||||
modifier = Modifier |
||||
.wrapContentHeight() |
||||
.clickable { widgetsTypeState.value = widgetsType } |
||||
.height(height) |
||||
.padding(start = 16.dp) |
||||
) { |
||||
var inFocus by remember { mutableStateOf(false) } |
||||
val textColor = AmbientContentColor.current.let { |
||||
when { |
||||
isCurrent -> it |
||||
inFocus -> it.copy(alpha = 0.6f) |
||||
else -> it.copy(alpha = 0.4f) |
||||
} |
||||
} |
||||
|
||||
Text( |
||||
text = widgetsType.readableName, |
||||
color = textColor, |
||||
modifier = Modifier |
||||
.align(Alignment.CenterVertically) |
||||
.clipToBounds() |
||||
.pointerMoveFilter( |
||||
onEnter = { |
||||
inFocus = true |
||||
true |
||||
}, |
||||
onExit = { |
||||
inFocus = false |
||||
true |
||||
} |
||||
), |
||||
softWrap = true, |
||||
fontSize = fontSize, |
||||
overflow = TextOverflow.Ellipsis, |
||||
maxLines = 1 |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui |
||||
|
||||
import androidx.compose.foundation.ScrollableColumn |
||||
import androidx.compose.foundation.layout.Column |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.MutableState |
||||
import androidx.compose.ui.Modifier |
||||
import org.jetbrains.compose.demo.widgets.ui.screens.* |
||||
|
||||
@Composable |
||||
fun WidgetsView( |
||||
widgetsTypeState: MutableState<WidgetsType>, |
||||
modifier: Modifier |
||||
) { |
||||
ScrollableColumn(modifier = modifier) { |
||||
Column { |
||||
@Suppress("UNUSED_VARIABLE") |
||||
val exhaustive = when (widgetsTypeState.value) { |
||||
WidgetsType.APP_BARS -> AppBars() |
||||
WidgetsType.BUTTONS -> Buttons() |
||||
WidgetsType.CHIPS -> Chips() |
||||
WidgetsType.LOADERS -> Loaders() |
||||
WidgetsType.SNACK_BARS -> SnackBars() |
||||
WidgetsType.TEXT_VIEWS -> TextViews() |
||||
WidgetsType.TEXT_INPUTS -> TextInputs() |
||||
WidgetsType.TOGGLES -> Toggles() |
||||
WidgetsType.UI_CARDS -> UICards() |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,30 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui |
||||
|
||||
enum class WidgetsType(private val customTitle: String? = null) { |
||||
APP_BARS, |
||||
BUTTONS, |
||||
CHIPS, |
||||
LOADERS, |
||||
SNACK_BARS, |
||||
TEXT_VIEWS, |
||||
TEXT_INPUTS, |
||||
TOGGLES, |
||||
UI_CARDS("UI Cards"); |
||||
|
||||
val readableName: String by lazy { |
||||
name.split("_") |
||||
.map { it.toLowerCase() } |
||||
.mapIndexed { i, it -> |
||||
if (i == 0) it.capitalize() else it |
||||
}.joinToString(" ") |
||||
} |
||||
|
||||
val title: String |
||||
get() = customTitle ?: readableName |
||||
|
||||
companion object { |
||||
val sortedValues: List<WidgetsType> by lazy { |
||||
values().sortedBy { it.name } |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,160 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.Image |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.foundation.shape.CircleShape |
||||
import androidx.compose.material.* |
||||
import androidx.compose.material.icons.Icons |
||||
import androidx.compose.material.icons.filled.ArrowBack |
||||
import androidx.compose.material.icons.filled.MoreHoriz |
||||
import androidx.compose.material.icons.filled.StarBorder |
||||
import androidx.compose.material.icons.outlined.* |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.draw.clip |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.platform.Res |
||||
import org.jetbrains.compose.demo.widgets.platform.imageResource |
||||
import org.jetbrains.compose.demo.widgets.platform.vectorResource |
||||
import org.jetbrains.compose.demo.widgets.theme.twitterColor |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.TitleText |
||||
|
||||
@Composable |
||||
fun AppBars() { |
||||
TopAppBarsDemo() |
||||
BottomAppBarDemo() |
||||
NavigationBarDemo() |
||||
} |
||||
|
||||
@Composable |
||||
private fun TopAppBarsDemo() { |
||||
SubtitleText(subtitle = "Top App bar") |
||||
|
||||
TopAppBar( |
||||
title = { Text(text = "Home") }, |
||||
elevation = 8.dp, |
||||
navigationIcon = { |
||||
IconButton(onClick = {}) { |
||||
Icon(Icons.Default.ArrowBack) |
||||
} |
||||
} |
||||
) |
||||
|
||||
Spacer(modifier = Modifier.height(8.dp)) |
||||
|
||||
TopAppBar( |
||||
title = { Text(text = "Instagram") }, |
||||
backgroundColor = MaterialTheme.colors.surface, |
||||
contentColor = MaterialTheme.colors.onSurface, |
||||
elevation = 8.dp, |
||||
navigationIcon = { |
||||
IconButton(onClick = {}) { |
||||
Icon(vectorResource(Res.drawable.ic_instagram)) |
||||
} |
||||
}, |
||||
actions = { |
||||
IconButton(onClick = {}) { |
||||
Icon(vectorResource(Res.drawable.ic_send)) |
||||
} |
||||
} |
||||
) |
||||
|
||||
Spacer(modifier = Modifier.height(8.dp)) |
||||
|
||||
TopAppBar( |
||||
title = { |
||||
Icon( |
||||
vectorResource(Res.drawable.ic_twitter), |
||||
tint = twitterColor, |
||||
modifier = Modifier.fillMaxWidth() |
||||
) |
||||
}, |
||||
backgroundColor = MaterialTheme.colors.surface, |
||||
contentColor = MaterialTheme.colors.onSurface, |
||||
elevation = 8.dp, |
||||
navigationIcon = { |
||||
Image( |
||||
imageResource(Res.drawable.p6), |
||||
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp) |
||||
.preferredSize(32.dp).clip(CircleShape) |
||||
) |
||||
}, |
||||
actions = { |
||||
Icon( |
||||
Icons.Default.StarBorder, |
||||
modifier = Modifier.padding(horizontal = 8.dp) |
||||
) |
||||
} |
||||
) |
||||
Spacer(modifier = Modifier.height(8.dp)) |
||||
} |
||||
|
||||
@Composable |
||||
private fun BottomAppBarDemo() { |
||||
Spacer(modifier = Modifier.height(16.dp)) |
||||
SubtitleText("Bottom app bars: Note bottom app bar support FAB cutouts when used with scafolds see demoUI crypto app") |
||||
|
||||
BottomAppBar( |
||||
cutoutShape = CircleShape |
||||
) { |
||||
IconButton(onClick = {}) { |
||||
Icon(Icons.Default.MoreHoriz) |
||||
} |
||||
TitleText(title = "Bottom App Bar") |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
private fun NavigationBarDemo() { |
||||
Spacer(modifier = Modifier.height(16.dp)) |
||||
SubtitleText(subtitle = "Bottom Navigation Bars") |
||||
val navItemState = remember { mutableStateOf(NavType.HOME) } |
||||
BottomNavigation(backgroundColor = MaterialTheme.colors.surface) { |
||||
BottomNavigationItem( |
||||
icon = { Icon(Icons.Outlined.Home) }, |
||||
selected = navItemState.value == NavType.HOME, |
||||
onClick = { navItemState.value = NavType.HOME }, |
||||
label = { Text(text = Res.string.spotify_nav_home) }, |
||||
) |
||||
BottomNavigationItem( |
||||
icon = { Icon(Icons.Outlined.Search) }, |
||||
selected = navItemState.value == NavType.SEARCH, |
||||
onClick = { navItemState.value = NavType.SEARCH }, |
||||
label = { Text(text = Res.string.spotify_nav_search) } |
||||
) |
||||
BottomNavigationItem( |
||||
icon = { Icon(Icons.Outlined.LibraryMusic) }, |
||||
selected = navItemState.value == NavType.LIBRARY, |
||||
onClick = { navItemState.value = NavType.LIBRARY }, |
||||
label = { Text(text = Res.string.spotify_nav_library) } |
||||
) |
||||
} |
||||
|
||||
Spacer(modifier = Modifier.height(16.dp)) |
||||
|
||||
BottomNavigation { |
||||
BottomNavigationItem( |
||||
icon = { Icon(Icons.Outlined.ReadMore) }, |
||||
selected = navItemState.value == NavType.HOME, |
||||
onClick = { navItemState.value = NavType.HOME }, |
||||
) |
||||
BottomNavigationItem( |
||||
icon = { Icon(Icons.Outlined.Search) }, |
||||
selected = navItemState.value == NavType.SEARCH, |
||||
onClick = { navItemState.value = NavType.SEARCH }, |
||||
) |
||||
BottomNavigationItem( |
||||
icon = { Icon(Icons.Outlined.CleanHands) }, |
||||
selected = navItemState.value == NavType.LIBRARY, |
||||
onClick = { navItemState.value = NavType.LIBRARY }, |
||||
) |
||||
} |
||||
} |
||||
|
||||
private enum class NavType { |
||||
HOME, SEARCH, LIBRARY |
||||
} |
@ -0,0 +1,109 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.background |
||||
import androidx.compose.foundation.clickable |
||||
import androidx.compose.foundation.layout.Column |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.foundation.shape.RoundedCornerShape |
||||
import androidx.compose.material.* |
||||
import androidx.compose.material.icons.Icons |
||||
import androidx.compose.material.icons.filled.FavoriteBorder |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.draw.clip |
||||
import androidx.compose.ui.graphics.Color |
||||
import androidx.compose.ui.graphics.HorizontalGradient |
||||
import androidx.compose.ui.graphics.VerticalGradient |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.theme.purple |
||||
import org.jetbrains.compose.demo.widgets.theme.purple200 |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
|
||||
@Composable |
||||
fun Buttons() { |
||||
Column { |
||||
Button(onClick = {}, modifier = Modifier.padding(8.dp)) { |
||||
Text(text = "Main Button") |
||||
} |
||||
TextButton(onClick = {}, modifier = Modifier.padding(8.dp)) { |
||||
Text(text = "Text Button") |
||||
} |
||||
TextButton(onClick = {}, modifier = Modifier.padding(8.dp), enabled = false) { |
||||
Text(text = "Text Disabled") |
||||
} |
||||
Button(onClick = {}, modifier = Modifier.padding(8.dp), enabled = false) { |
||||
Text(text = "Disabled") |
||||
} |
||||
Button( |
||||
onClick = {}, |
||||
modifier = Modifier.padding(8.dp), |
||||
elevation = ButtonConstants.defaultElevation() |
||||
) { |
||||
Text(text = "Flat") |
||||
} |
||||
Button( |
||||
onClick = {}, |
||||
modifier = Modifier.padding(8.dp), |
||||
shape = RoundedCornerShape(12.dp) |
||||
) { |
||||
Text(text = "Rounded") |
||||
} |
||||
OutlinedButton(onClick = {}, modifier = Modifier.padding(8.dp)) { |
||||
Text(text = "Outline") |
||||
} |
||||
Button(onClick = {}, modifier = Modifier.padding(8.dp)) { |
||||
Row { |
||||
Icon(Icons.Default.FavoriteBorder, modifier = Modifier.padding(end = 4.dp)) |
||||
Text(text = "Icon Button") |
||||
} |
||||
} |
||||
Button(onClick = {}, modifier = Modifier.padding(8.dp)) { |
||||
Text(text = "Icon Button") |
||||
Icon(Icons.Default.FavoriteBorder, modifier = Modifier.padding(start = 4.dp)) |
||||
} |
||||
//custom background buttons |
||||
val outlineButtonColor = ButtonConstants.defaultOutlinedButtonColors( |
||||
contentColor = purple200, |
||||
) |
||||
val mainButtonColor = ButtonConstants.defaultButtonColors( |
||||
backgroundColor = purple, |
||||
contentColor = MaterialTheme.colors.surface |
||||
) |
||||
OutlinedButton( |
||||
colors = outlineButtonColor, |
||||
onClick = {}, |
||||
modifier = Modifier.padding(8.dp) |
||||
) { |
||||
Text(text = "Outline colors") |
||||
} |
||||
Button(colors = mainButtonColor, onClick = {}, modifier = Modifier.padding(8.dp)) { |
||||
Text(text = "Custom colors") |
||||
} |
||||
|
||||
val horizontalGradient = HorizontalGradient( |
||||
colors = listOf(MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant), |
||||
0f, |
||||
250f |
||||
) |
||||
val verticalGradient = VerticalGradient( |
||||
colors = listOf(MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant), |
||||
startY = 0f, |
||||
endY = 100f |
||||
) |
||||
Text( |
||||
text = "Horizontal gradient", |
||||
style = typography.body2.copy(color = Color.White), |
||||
modifier = Modifier.padding(12.dp).clickable(onClick = {}) |
||||
.clip(RoundedCornerShape(4.dp)) |
||||
.background(brush = horizontalGradient).padding(12.dp) |
||||
) |
||||
Text( |
||||
text = "Vertical gradient", |
||||
style = typography.body1.copy(color = Color.White), |
||||
modifier = Modifier.padding(12.dp).clickable(onClick = {}) |
||||
.clip(RoundedCornerShape(4.dp)) |
||||
.background(brush = verticalGradient).padding(12.dp) |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,131 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.BorderStroke |
||||
import androidx.compose.foundation.Image |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.shape.CircleShape |
||||
import androidx.compose.foundation.shape.RoundedCornerShape |
||||
import androidx.compose.material.Button |
||||
import androidx.compose.material.MaterialTheme |
||||
import androidx.compose.material.Surface |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.draw.clip |
||||
import androidx.compose.ui.graphics.Color |
||||
import androidx.compose.ui.text.style.TextAlign |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.platform.Res |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText |
||||
import org.jetbrains.compose.demo.widgets.platform.imageResource |
||||
|
||||
@Composable |
||||
fun Chips() { |
||||
// There is no in-built chips but you can make yours like below |
||||
SubtitleText(subtitle = "Custom chips with surface") |
||||
Column(modifier = Modifier.padding(8.dp)) { |
||||
YoutubeChip(selected = true, text = "Chip", modifier = Modifier.padding(horizontal = 8.dp)) |
||||
Spacer(modifier = Modifier.padding(8.dp)) |
||||
YoutubeChip( |
||||
selected = false, |
||||
text = "Inactive", |
||||
modifier = Modifier.padding(horizontal = 8.dp) |
||||
) |
||||
Spacer(modifier = Modifier.padding(8.dp)) |
||||
CustomImageChip(text = "custom", imageId = Res.drawable.p2, selected = true) |
||||
Spacer(modifier = Modifier.padding(8.dp)) |
||||
CustomImageChip(text = "custom2", imageId = Res.drawable.p6, selected = false) |
||||
} |
||||
SubtitleText(subtitle = "Buttons with circle clipping.") |
||||
Column(modifier = Modifier.padding(8.dp)) { |
||||
Button( |
||||
onClick = {}, |
||||
modifier = Modifier.padding(8.dp).clip(CircleShape) |
||||
) { |
||||
Text(text = "Chip button") |
||||
} |
||||
Button( |
||||
onClick = {}, |
||||
enabled = false, |
||||
modifier = Modifier.padding(8.dp).clip(CircleShape) |
||||
) { |
||||
Text(text = "Disabled chip") |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
//Inspired from jetcaster sample. I hope compose can add simple Chip UI element that can |
||||
// support images or icons with multiple states. |
||||
@Composable |
||||
private fun CustomImageChip( |
||||
text: String, |
||||
imageId: String, |
||||
selected: Boolean, |
||||
modifier: Modifier = Modifier |
||||
) { |
||||
Surface( |
||||
color = when { |
||||
selected -> MaterialTheme.colors.primary |
||||
else -> Color.Transparent |
||||
}, |
||||
contentColor = when { |
||||
selected -> MaterialTheme.colors.onPrimary |
||||
else -> Color.LightGray |
||||
}, |
||||
shape = RoundedCornerShape(16.dp), |
||||
border = BorderStroke( |
||||
width = 1.dp, |
||||
color = when { |
||||
selected -> MaterialTheme.colors.primary |
||||
else -> Color.LightGray |
||||
} |
||||
), |
||||
modifier = modifier |
||||
) { |
||||
Row(modifier = Modifier) { |
||||
Image( |
||||
imageResource(imageId), |
||||
modifier = Modifier.padding(8.dp).preferredSize(20.dp).clip(CircleShape) |
||||
) |
||||
Text( |
||||
text = text, |
||||
style = MaterialTheme.typography.body2, |
||||
modifier = Modifier.padding(end = 8.dp, top = 8.dp, bottom = 8.dp) |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
private fun YoutubeChip(selected: Boolean, text: String, modifier: Modifier = Modifier) { |
||||
Surface( |
||||
color = when { |
||||
selected -> MaterialTheme.colors.onSurface |
||||
else -> Color.Transparent |
||||
}, |
||||
contentColor = when { |
||||
selected -> MaterialTheme.colors.onPrimary |
||||
else -> Color.LightGray |
||||
}, |
||||
shape = CircleShape, |
||||
border = BorderStroke( |
||||
width = 1.dp, |
||||
color = when { |
||||
selected -> MaterialTheme.colors.primary |
||||
else -> Color.LightGray |
||||
} |
||||
), |
||||
modifier = modifier |
||||
) { |
||||
Text( |
||||
text = text, |
||||
textAlign = TextAlign.Center, |
||||
style = MaterialTheme.typography.body2, |
||||
modifier = Modifier.padding(8.dp) |
||||
) |
||||
|
||||
} |
||||
} |
@ -0,0 +1,41 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.layout.Column |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.layout.fillMaxWidth |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.material.CircularProgressIndicator |
||||
import androidx.compose.material.LinearProgressIndicator |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Alignment |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.dp |
||||
|
||||
@Composable |
||||
fun Loaders() { |
||||
AlignedColumn { |
||||
CircularProgressIndicator() |
||||
} |
||||
|
||||
AlignedColumn { |
||||
CircularProgressIndicator(strokeWidth = 8.dp) |
||||
} |
||||
|
||||
AlignedColumn { |
||||
LinearProgressIndicator() |
||||
} |
||||
|
||||
AlignedColumn { |
||||
LinearProgressIndicator() |
||||
Text(text = "Loading with text...", modifier = Modifier.padding(8.dp)) |
||||
} |
||||
} |
||||
@Composable |
||||
private fun AlignedColumn(content: @Composable () -> Unit) { |
||||
Column( |
||||
modifier = Modifier.fillMaxWidth().padding(16.dp) |
||||
) { |
||||
content() |
||||
} |
||||
} |
@ -0,0 +1,38 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.material.Snackbar |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.material.TextButton |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
|
||||
@Composable |
||||
fun SnackBars() { |
||||
Snackbar(modifier = Modifier.padding(4.dp)) { |
||||
Text(text = "This is a basic snackbar") |
||||
} |
||||
Snackbar( |
||||
modifier = Modifier.padding(4.dp), |
||||
action = { |
||||
TextButton(onClick = {}) { |
||||
Text(text = "Remove") |
||||
} |
||||
} |
||||
) { |
||||
Text(text = "This is a basic Snackbar with action item") |
||||
} |
||||
Snackbar( |
||||
modifier = Modifier.padding(4.dp), |
||||
actionOnNewLine = true, |
||||
action = { |
||||
TextButton(onClick = {}) { |
||||
Text(text = "Remove") |
||||
} |
||||
} |
||||
) { |
||||
Text(text = "Snackbar with action item below text") |
||||
} |
||||
} |
@ -0,0 +1,87 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.foundation.text.KeyboardOptions |
||||
import androidx.compose.material.Icon |
||||
import androidx.compose.material.OutlinedTextField |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.material.TextField |
||||
import androidx.compose.material.icons.Icons |
||||
import androidx.compose.material.icons.filled.Edit |
||||
import androidx.compose.material.icons.filled.Email |
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.text.input.KeyboardType |
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation |
||||
import androidx.compose.ui.text.input.TextFieldValue |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
|
||||
@Composable |
||||
fun TextInputs() { |
||||
var text by remember { mutableStateOf(TextFieldValue("")) } |
||||
|
||||
// TODO Explore CoreTextField |
||||
// CoreTextField( |
||||
// value = text, |
||||
// onValueChange = { newValue -> text = newValue }, |
||||
// modifier = Modifier.padding(8.dp).preferredSize(0.dp), |
||||
// cursorColor = Color.Magenta |
||||
// ) |
||||
TextField( |
||||
value = text, |
||||
onValueChange = { newValue -> text = newValue }, |
||||
modifier = Modifier.padding(8.dp).fillMaxWidth(), |
||||
label = { Text("label") }, |
||||
placeholder = { Text("placeholder") } |
||||
) |
||||
|
||||
OutlinedTextField( |
||||
value = text, |
||||
modifier = Modifier.padding(8.dp).fillMaxWidth(), |
||||
label = { Text(text = "Password") }, |
||||
placeholder = { Text(text = "12334444") }, |
||||
visualTransformation = PasswordVisualTransformation(), |
||||
onValueChange = { |
||||
text = it |
||||
}, |
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password) |
||||
) |
||||
|
||||
OutlinedTextField( |
||||
value = text, |
||||
leadingIcon = { Icon(Icons.Default.Email) }, |
||||
modifier = Modifier.padding(8.dp).fillMaxWidth(), |
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), |
||||
label = { Text(text = "Email address") }, |
||||
placeholder = { Text(text = "Your email") }, |
||||
onValueChange = { |
||||
text = it |
||||
} |
||||
) |
||||
OutlinedTextField( |
||||
value = text, |
||||
leadingIcon = { Icon(Icons.Default.Email) }, |
||||
trailingIcon = { Icon(Icons.Default.Edit) }, |
||||
modifier = Modifier.padding(8.dp).fillMaxWidth(), |
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), |
||||
label = { Text(text = "Email address") }, |
||||
placeholder = { Text(text = "Your email") }, |
||||
onValueChange = { |
||||
text = it |
||||
} |
||||
) |
||||
|
||||
var numberText by remember { mutableStateOf(TextFieldValue("")) } |
||||
OutlinedTextField(value = numberText, |
||||
modifier = Modifier.padding(8.dp).fillMaxWidth(), |
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), |
||||
label = { Text(text = "Phone number") }, |
||||
placeholder = { Text(text = "88888888") }, |
||||
onValueChange = { |
||||
numberText = it |
||||
} |
||||
) |
||||
} |
@ -0,0 +1,97 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.layout.Column |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.text.font.FontWeight |
||||
import androidx.compose.ui.text.style.TextDecoration |
||||
import androidx.compose.ui.text.style.TextOverflow |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
import org.jetbrains.compose.demo.widgets.ui.utils.SubtitleText |
||||
|
||||
@Composable |
||||
fun TextViews() { |
||||
Column { |
||||
val textModifier = Modifier.padding(horizontal = 8.dp) |
||||
|
||||
SubtitleText(subtitle = "Font weights") |
||||
Text(text = "Plain", modifier = textModifier) |
||||
Text( |
||||
text = "Medium Bold", |
||||
style = typography.body1.copy(fontWeight = FontWeight.Medium), |
||||
modifier = textModifier |
||||
) |
||||
Text( |
||||
text = "Bold", |
||||
style = typography.body1.copy(fontWeight = FontWeight.Bold), |
||||
modifier = textModifier |
||||
) |
||||
Text( |
||||
text = "Extra Bold", |
||||
style = typography.body1.copy(fontWeight = FontWeight.Bold), |
||||
modifier = textModifier |
||||
) |
||||
|
||||
SubtitleText(subtitle = "Text decorations") |
||||
Text(text = "Default", modifier = textModifier) |
||||
Text( |
||||
text = "Underline", |
||||
textDecoration = TextDecoration.Underline, |
||||
modifier = textModifier |
||||
) |
||||
Text( |
||||
text = "LineThrough", |
||||
textDecoration = TextDecoration.LineThrough, |
||||
modifier = textModifier |
||||
) |
||||
Text( |
||||
text = "UnderlineLineThrough", |
||||
textDecoration = TextDecoration.combine( |
||||
listOf( |
||||
TextDecoration.Underline, |
||||
TextDecoration.LineThrough |
||||
) |
||||
), |
||||
modifier = textModifier |
||||
) |
||||
|
||||
SubtitleText(subtitle = "Overflow") |
||||
Text( |
||||
text = "Ellipsis: This text is supposed to ellipsis with max 1 line allowed for this", |
||||
overflow = TextOverflow.Ellipsis, |
||||
modifier = textModifier, |
||||
maxLines = 1 |
||||
) |
||||
Text( |
||||
text = "Clip: This text is supposed to clip with max 1 line allowed for this", |
||||
overflow = TextOverflow.Clip, |
||||
modifier = textModifier, |
||||
maxLines = 1 |
||||
) |
||||
} |
||||
|
||||
/* TODO: https://github.com/JetBrains/compose-jb/issues/106 |
||||
SubtitleText(subtitle = "font family dynamic") |
||||
Row { |
||||
Text(text = "Default", modifier = textModifier) |
||||
Text( |
||||
text = "Cursive", |
||||
style = typography.body1.copy(fontFamily = FontFamily.Cursive), |
||||
modifier = textModifier |
||||
) |
||||
Text( |
||||
text = "SansSerif", |
||||
style = typography.body1.copy(fontFamily = FontFamily.SansSerif), |
||||
modifier = textModifier |
||||
) |
||||
Text( |
||||
text = "Monospace", |
||||
style = typography.body1.copy(fontFamily = FontFamily.Monospace), |
||||
modifier = textModifier |
||||
) |
||||
} */ |
||||
} |
@ -0,0 +1,83 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.clickable |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.material.* |
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
|
||||
@Composable |
||||
fun Toggles() { |
||||
Column { |
||||
var checked by remember { mutableStateOf(true) } |
||||
Checkbox( |
||||
checked = checked, |
||||
modifier = Modifier.padding(8.dp), |
||||
onCheckedChange = { checked = !checked } |
||||
) |
||||
|
||||
var switched by remember { mutableStateOf(true) } |
||||
Switch( |
||||
checked = switched, |
||||
colors = SwitchConstants.defaultColors(checkedThumbColor = MaterialTheme.colors.primary), |
||||
modifier = Modifier.padding(8.dp), |
||||
onCheckedChange = { switched = it } |
||||
) |
||||
|
||||
AlignedColumn { |
||||
var selected by remember { mutableStateOf("Kotlin") } |
||||
|
||||
Row { |
||||
RadioButton(selected = selected == "Kotlin", onClick = { selected = "Kotlin" }) |
||||
Text( |
||||
text = "Kotlin", |
||||
modifier = Modifier.clickable(onClick = { selected = "Kotlin" }).padding(start = 4.dp) |
||||
) |
||||
} |
||||
//Spacer(modifier = Modifier.size(4.dp)) |
||||
Row { |
||||
RadioButton(selected = selected == "Java", onClick = { selected = "Java" }) |
||||
Text( |
||||
text = "Java", |
||||
modifier = Modifier.clickable(onClick = { selected = "Java" }).padding(start = 4.dp) |
||||
) |
||||
} |
||||
//Spacer(modifier = Modifier.size(4.dp)) |
||||
Row { |
||||
RadioButton(selected = selected == "Swift", onClick = { selected = "Swift" }) |
||||
Text( |
||||
text = "Swift", |
||||
modifier = Modifier.clickable(onClick = { selected = "Swift" }).padding(start = 4.dp) |
||||
) |
||||
} |
||||
} |
||||
|
||||
var sliderState by remember { mutableStateOf(0f) } |
||||
Slider(value = sliderState, modifier = Modifier.fillMaxWidth().padding(8.dp), |
||||
onValueChange = { newValue -> |
||||
sliderState = newValue |
||||
} |
||||
) |
||||
|
||||
var sliderState2 by remember { mutableStateOf(20f) } |
||||
Slider(value = sliderState2, modifier = Modifier.fillMaxWidth().padding(8.dp), |
||||
valueRange = 0f..100f, |
||||
steps = 5, |
||||
thumbColor = MaterialTheme.colors.secondary, |
||||
onValueChange = { newValue -> |
||||
sliderState2 = newValue |
||||
} |
||||
) |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
private fun AlignedColumn(content: @Composable () -> Unit) { |
||||
Column( |
||||
modifier = Modifier.fillMaxWidth().padding(16.dp) |
||||
) { |
||||
content() |
||||
} |
||||
} |
@ -0,0 +1,100 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.screens |
||||
|
||||
import androidx.compose.foundation.Image |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.foundation.shape.RoundedCornerShape |
||||
import androidx.compose.material.* |
||||
import androidx.compose.material.icons.Icons |
||||
import androidx.compose.material.icons.filled.ShoppingCart |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.data.DemoDataProvider |
||||
import org.jetbrains.compose.demo.widgets.platform.Res |
||||
import org.jetbrains.compose.demo.widgets.platform.imageResource |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
|
||||
@Composable |
||||
fun UICards() { |
||||
val item = remember { DemoDataProvider.item } |
||||
|
||||
Text( |
||||
text = "Inbuilt box as container for any Clipping/Alignment controls", |
||||
style = typography.subtitle1, |
||||
modifier = Modifier.padding(8.dp) |
||||
) |
||||
Card( |
||||
modifier = Modifier.padding(8.dp).fillMaxWidth(), |
||||
backgroundColor = MaterialTheme.colors.primary, |
||||
shape = RoundedCornerShape(topLeft = 16.dp, bottomRight = 16.dp) |
||||
) { |
||||
Column { |
||||
Text( |
||||
text = item.title, |
||||
modifier = Modifier.padding(8.dp), |
||||
color = MaterialTheme.colors.onPrimary |
||||
) |
||||
Text( |
||||
text = item.subtitle, |
||||
modifier = Modifier.padding(8.dp), |
||||
color = MaterialTheme.colors.onPrimary |
||||
) |
||||
} |
||||
} |
||||
Divider() |
||||
|
||||
Text(text = "Inbuilt Card", style = typography.subtitle1, modifier = Modifier.padding(8.dp)) |
||||
Card( |
||||
modifier = Modifier.padding(16.dp).fillMaxWidth(), |
||||
shape = RoundedCornerShape(4.dp), |
||||
elevation = 4.dp |
||||
) { |
||||
Row { |
||||
Image( |
||||
imageResource(Res.drawable.p3), |
||||
modifier = Modifier.preferredSize(60.dp) |
||||
) |
||||
Text(text = item.title, modifier = Modifier.padding(16.dp)) |
||||
} |
||||
} |
||||
Divider() |
||||
|
||||
Text( |
||||
text = "In-built ListItems", |
||||
style = typography.subtitle1, |
||||
modifier = Modifier.padding(8.dp) |
||||
) |
||||
ListItem(text = { Text(item.title) }, secondaryText = { Text(item.subtitle) }) |
||||
Divider(modifier = Modifier.padding(4.dp)) |
||||
ListItem( |
||||
text = { Text(item.title) }, |
||||
secondaryText = { Text(item.subtitle) }, |
||||
singleLineSecondaryText = false |
||||
) |
||||
Divider(modifier = Modifier.padding(4.dp)) |
||||
ListItem(text = { Text(item.title) }, secondaryText = { Text(item.subtitle) }, icon = { |
||||
Image( |
||||
imageResource(Res.drawable.p3) |
||||
) |
||||
}) |
||||
Divider(modifier = Modifier.padding(4.dp)) |
||||
//I am not sure why this is not going multiline for secondaryText... |
||||
ListItem( |
||||
text = { Text(item.title) }, |
||||
secondaryText = { Text(item.subtitle) }, |
||||
icon = { Image(imageResource(Res.drawable.p1)) }, |
||||
overlineText = { Text("Overline text") }, |
||||
singleLineSecondaryText = false |
||||
) |
||||
Divider() |
||||
ListItem( |
||||
text = { Text(item.title) }, |
||||
secondaryText = { Text(item.subtitle) }, |
||||
icon = { Image(imageResource(Res.drawable.p2)) }, |
||||
trailing = { Icon(Icons.Default.ShoppingCart) }, |
||||
singleLineSecondaryText = false |
||||
) |
||||
Divider() |
||||
|
||||
} |
@ -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) |
||||
} |
||||
} |
@ -0,0 +1,80 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.utils |
||||
|
||||
import androidx.compose.animation.animate |
||||
import androidx.compose.animation.core.Spring |
||||
import androidx.compose.animation.core.SpringSpec |
||||
import androidx.compose.foundation.background |
||||
import androidx.compose.foundation.clickable |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.material.AmbientContentColor |
||||
import androidx.compose.material.Icon |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.material.icons.Icons |
||||
import androidx.compose.material.icons.filled.ArrowBack |
||||
import androidx.compose.material.icons.filled.ArrowForward |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.draw.clipToBounds |
||||
import androidx.compose.ui.graphics.Color |
||||
import androidx.compose.ui.graphics.graphicsLayer |
||||
import androidx.compose.ui.unit.dp |
||||
import androidx.compose.ui.unit.sp |
||||
|
||||
class PanelState { |
||||
val collapsedSize = 24.dp |
||||
var expandedSize by mutableStateOf(110.dp) |
||||
val expandedSizeMin = 90.dp |
||||
var isExpanded by mutableStateOf(true) |
||||
val splitter = SplitterState() |
||||
} |
||||
|
||||
@Composable |
||||
fun ResizablePanel( |
||||
modifier: Modifier, |
||||
state: PanelState, |
||||
title: String, |
||||
content: @Composable () -> Unit, |
||||
) { |
||||
val alpha = animate(if (state.isExpanded) 1f else 0f, SpringSpec(stiffness = Spring.StiffnessLow),) |
||||
|
||||
Box(modifier) { |
||||
Column { |
||||
Row(Modifier |
||||
.height(32.dp) |
||||
.padding(6.dp) |
||||
.clickable { state.isExpanded = !state.isExpanded } |
||||
) { |
||||
Text( |
||||
text = if (state.isExpanded) title else "", |
||||
modifier = Modifier.fillMaxWidth().clipToBounds(), |
||||
fontSize = 14.sp |
||||
) |
||||
|
||||
Icon( |
||||
if (state.isExpanded) Icons.Default.ArrowBack else Icons.Default.ArrowForward, |
||||
tint = AmbientContentColor.current, |
||||
modifier = Modifier |
||||
.size(24.dp) |
||||
.padding(end = 8.dp) |
||||
.padding(bottom = 4.dp) |
||||
) |
||||
} |
||||
|
||||
if (state.isExpanded) { |
||||
Box( |
||||
Modifier |
||||
.fillMaxWidth() |
||||
.height(1.dp) |
||||
.background(Color.Gray) |
||||
) |
||||
|
||||
Column(Modifier.fillMaxSize().padding(top = 4.dp).graphicsLayer(alpha = alpha)) { |
||||
content() |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,23 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.utils |
||||
|
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.dp |
||||
import androidx.compose.ui.unit.sp |
||||
import org.jetbrains.compose.demo.widgets.theme.typography |
||||
|
||||
@Composable |
||||
fun SubtitleText(subtitle: String, modifier: Modifier = Modifier) { |
||||
Text(text = subtitle, style = typography.subtitle2, modifier = modifier.padding(8.dp)) |
||||
} |
||||
|
||||
@Composable |
||||
fun TitleText(title: String, modifier: Modifier = Modifier) { |
||||
Text( |
||||
text = title, |
||||
style = typography.h6.copy(fontSize = 14.sp), |
||||
modifier = Modifier.padding(8.dp) |
||||
) |
||||
} |
@ -0,0 +1,90 @@
|
||||
package org.jetbrains.compose.demo.widgets.ui.utils |
||||
|
||||
import androidx.compose.foundation.background |
||||
import androidx.compose.foundation.gestures.draggable |
||||
import androidx.compose.foundation.layout.Box |
||||
import androidx.compose.foundation.layout.fillMaxHeight |
||||
import androidx.compose.foundation.layout.width |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.gesture.scrollorientationlocking.Orientation |
||||
import androidx.compose.ui.graphics.Color |
||||
import androidx.compose.ui.layout.Layout |
||||
import androidx.compose.ui.unit.Constraints |
||||
import androidx.compose.ui.unit.Dp |
||||
import androidx.compose.ui.unit.dp |
||||
import org.jetbrains.compose.demo.widgets.platform.cursorForHorizontalResize |
||||
|
||||
class SplitterState { |
||||
var isResizing by mutableStateOf(false) |
||||
var isResizeEnabled by mutableStateOf(true) |
||||
} |
||||
|
||||
@Composable |
||||
fun VerticalSplittable( |
||||
modifier: Modifier, |
||||
splitterState: SplitterState, |
||||
onResize: (delta: Dp) -> Unit, |
||||
children: @Composable () -> Unit |
||||
) = Layout({ |
||||
children() |
||||
VerticalSplitter(splitterState, onResize) |
||||
}, modifier, measureBlock = { measurables, constraints -> |
||||
require(measurables.size == 3) |
||||
|
||||
val firstPlaceable = measurables[0].measure(constraints.copy(minWidth = 0)) |
||||
val secondWidth = constraints.maxWidth - firstPlaceable.width |
||||
val secondPlaceable = measurables[1].measure( |
||||
Constraints( |
||||
minWidth = secondWidth, |
||||
maxWidth = secondWidth, |
||||
minHeight = constraints.maxHeight, |
||||
maxHeight = constraints.maxHeight |
||||
) |
||||
) |
||||
val splitterPlaceable = measurables[2].measure(constraints) |
||||
layout(constraints.maxWidth, constraints.maxHeight) { |
||||
firstPlaceable.place(0, 0) |
||||
secondPlaceable.place(firstPlaceable.width, 0) |
||||
splitterPlaceable.place(firstPlaceable.width, 0) |
||||
} |
||||
}) |
||||
|
||||
|
||||
|
||||
@Composable |
||||
fun VerticalSplitter( |
||||
splitterState: SplitterState, |
||||
onResize: (delta: Dp) -> Unit, |
||||
color: Color = Color.DarkGray |
||||
) = Box { |
||||
Box( |
||||
Modifier |
||||
.width(8.dp) |
||||
.fillMaxHeight() |
||||
.run { |
||||
if (splitterState.isResizeEnabled) { |
||||
this.draggable( |
||||
Orientation.Horizontal, |
||||
startDragImmediately = true, |
||||
onDragStarted = { splitterState.isResizing = true }, |
||||
onDragStopped = { splitterState.isResizing = false } |
||||
) { |
||||
onResize(it.toDp()) |
||||
}.cursorForHorizontalResize() |
||||
} else { |
||||
this |
||||
} |
||||
} |
||||
) |
||||
|
||||
Box( |
||||
Modifier |
||||
.width(1.dp) |
||||
.fillMaxHeight() |
||||
.background(color) |
||||
) |
||||
} |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 5.7 KiB |
@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="20dp" |
||||
android:height="20dp" |
||||
android:viewportWidth="512" |
||||
android:viewportHeight="512"> |
||||
<path |
||||
android:fillColor="#FF000000" |
||||
android:pathData="M352,0H160C71.648,0 0,71.648 0,160v192c0,88.352 71.648,160 160,160h192c88.352,0 160,-71.648 160,-160V160C512,71.648 440.352,0 352,0zM464,352c0,61.76 -50.24,112 -112,112H160c-61.76,0 -112,-50.24 -112,-112V160C48,98.24 98.24,48 160,48h192c61.76,0 112,50.24 112,112V352z" /> |
||||
<path |
||||
android:fillColor="#FF000000" |
||||
android:pathData="M256,128c-70.688,0 -128,57.312 -128,128s57.312,128 128,128s128,-57.312 128,-128S326.688,128 256,128zM256,336c-44.096,0 -80,-35.904 -80,-80c0,-44.128 35.904,-80 80,-80s80,35.872 80,80C336,300.096 300.096,336 256,336z" /> |
||||
<path |
||||
android:fillColor="#FF000000" |
||||
android:pathData="M393.6,118.4m-17.056,0a17.056,17.056 0,1 1,34.112 0a17.056,17.056 0,1 1,-34.112 0" /> |
||||
</vector> |
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="20dp" |
||||
android:height="20dp" |
||||
android:viewportWidth="512.001" |
||||
android:viewportHeight="512.001"> |
||||
<path |
||||
android:fillColor="#FF000000" |
||||
android:pathData="M507.608,4.395c-4.243,-4.244 -10.609,-5.549 -16.177,-3.321L9.43,193.872c-5.515,2.206 -9.208,7.458 -9.42,13.395c-0.211,5.936 3.101,11.437 8.445,14.029l190.068,92.181l92.182,190.068c2.514,5.184 7.764,8.455 13.493,8.455c0.178,0 0.357,-0.003 0.536,-0.01c5.935,-0.211 11.189,-3.904 13.394,-9.419l192.8,-481.998C513.156,15.001 511.851,8.638 507.608,4.395zM52.094,209.118L434.72,56.069L206.691,284.096L52.094,209.118zM302.883,459.907l-74.979,-154.599l228.03,-228.027L302.883,459.907z" /> |
||||
</vector> |
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:width="20dp" |
||||
android:height="20dp" |
||||
android:viewportWidth="512" |
||||
android:viewportHeight="512"> |
||||
<path |
||||
android:fillColor="#000000" |
||||
android:pathData="M459.37,151.716c0.325,4.548 0.325,9.097 0.325,13.645 0,138.72 -105.583,298.558 -298.558,298.558 -59.452,0 -114.68,-17.219 -161.137,-47.106 8.447,0.974 16.568,1.299 25.34,1.299 49.055,0 94.213,-16.568 130.274,-44.832 -46.132,-0.975 -84.792,-31.188 -98.112,-72.772 6.498,0.974 12.995,1.624 19.818,1.624 9.421,0 18.843,-1.3 27.614,-3.573 -48.081,-9.747 -84.143,-51.98 -84.143,-102.985v-1.299c13.969,7.797 30.214,12.67 47.431,13.319 -28.264,-18.843 -46.781,-51.005 -46.781,-87.391 0,-19.492 5.197,-37.36 14.294,-52.954 51.655,63.675 129.3,105.258 216.365,109.807 -1.624,-7.797 -2.599,-15.918 -2.599,-24.04 0,-57.828 46.782,-104.934 104.934,-104.934 30.213,0 57.502,12.67 76.67,33.137 23.715,-4.548 46.456,-13.32 66.599,-25.34 -7.798,24.366 -24.366,44.833 -46.132,57.827 21.117,-2.273 41.584,-8.122 60.426,-16.243 -14.292,20.791 -32.161,39.308 -52.628,54.253z" /> |
||||
</vector> |
@ -0,0 +1,33 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.desktop.AppWindowAmbient |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.composed |
||||
import androidx.compose.ui.geometry.Offset |
||||
import androidx.compose.ui.input.pointer.pointerMoveFilter |
||||
import java.awt.Cursor |
||||
|
||||
actual fun Modifier.pointerMoveFilter( |
||||
onEnter: () -> Boolean, |
||||
onExit: () -> Boolean, |
||||
onMove: (Offset) -> Boolean |
||||
): Modifier = this.pointerMoveFilter(onEnter = onEnter, onExit = onExit, onMove = onMove) |
||||
|
||||
actual fun Modifier.cursorForHorizontalResize(): Modifier = composed { |
||||
var isHover by remember { mutableStateOf(false) } |
||||
|
||||
if (isHover) { |
||||
AppWindowAmbient.current!!.window.cursor = Cursor(Cursor.E_RESIZE_CURSOR) |
||||
} else { |
||||
AppWindowAmbient.current!!.window.cursor = Cursor.getDefaultCursor() |
||||
} |
||||
|
||||
pointerMoveFilter( |
||||
onEnter = { isHover = true; true }, |
||||
onExit = { isHover = false; true } |
||||
) |
||||
} |
@ -0,0 +1,14 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.graphics.vector.ImageVector |
||||
import androidx.compose.ui.res.vectorXmlResource |
||||
import androidx.compose.ui.res.imageResource as bitmapImage |
||||
|
||||
@Composable |
||||
actual fun imageResource(res: String) = |
||||
bitmapImage(res) |
||||
|
||||
@Composable |
||||
actual fun vectorResource(res: String): ImageVector = |
||||
vectorXmlResource(res) |
@ -0,0 +1,30 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi |
||||
import androidx.compose.foundation.ScrollState |
||||
import androidx.compose.foundation.lazy.LazyListState |
||||
import androidx.compose.foundation.rememberScrollbarAdapter |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.unit.Dp |
||||
|
||||
@Composable |
||||
actual fun VerticalScrollbar( |
||||
modifier: Modifier, |
||||
scrollState: ScrollState |
||||
) = androidx.compose.foundation.VerticalScrollbar( |
||||
rememberScrollbarAdapter(scrollState), |
||||
modifier |
||||
) |
||||
|
||||
@OptIn(ExperimentalFoundationApi::class) |
||||
@Composable |
||||
actual fun VerticalScrollbar( |
||||
modifier: Modifier, |
||||
scrollState: LazyListState, |
||||
itemCount: Int, |
||||
averageItemSize: Dp |
||||
) = androidx.compose.foundation.VerticalScrollbar( |
||||
rememberScrollbarAdapter(scrollState, itemCount, averageItemSize), |
||||
modifier |
||||
) |
@ -0,0 +1,4 @@
|
||||
package org.jetbrains.compose.demo.widgets.platform |
||||
|
||||
// TODO: https://github.com/JetBrains/compose-jb/issues/169 |
||||
actual fun isSystemInDarkTheme(): Boolean = false |
@ -0,0 +1,39 @@
|
||||
import org.jetbrains.compose.compose |
||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat |
||||
|
||||
plugins { |
||||
kotlin("multiplatform") // kotlin("jvm") doesn't work well in IDEA/AndroidStudio (https://github.com/JetBrains/compose-jb/issues/22) |
||||
id("org.jetbrains.compose") |
||||
} |
||||
|
||||
kotlin { |
||||
jvm { |
||||
withJava() |
||||
} |
||||
|
||||
sourceSets { |
||||
named("jvmMain") { |
||||
dependencies { |
||||
implementation(compose.desktop.currentOs) |
||||
implementation(project(":common")) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
compose.desktop { |
||||
application { |
||||
mainClass = "org.jetbrains.compose.demo.widgets.MainKt" |
||||
|
||||
nativeDistributions { |
||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) |
||||
packageName = "ComposeWidgetsGallery" |
||||
|
||||
windows { |
||||
menu = true |
||||
// see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html |
||||
upgradeUuid = "a61b72be-1b0c-4de5-9607-791c17687428" |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,30 @@
|
||||
package org.jetbrains.compose.demo.widgets |
||||
|
||||
import androidx.compose.desktop.Window |
||||
import androidx.compose.foundation.layout.ExperimentalLayout |
||||
import androidx.compose.ui.unit.IntSize |
||||
import org.jetbrains.compose.demo.widgets.ui.MainView |
||||
import java.awt.Dimension |
||||
import java.awt.Toolkit |
||||
import javax.swing.SwingUtilities.invokeLater |
||||
|
||||
@OptIn(ExperimentalLayout::class) |
||||
fun main() { |
||||
invokeLater { |
||||
Window( |
||||
title = "Widgets Gallery", |
||||
size = getPreferredWindowSize(600, 800), |
||||
) { |
||||
MainView() |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun getPreferredWindowSize(desiredWidth: Int, desiredHeight: Int): IntSize { |
||||
val screenSize: Dimension = Toolkit.getDefaultToolkit().screenSize |
||||
val preferredWidth: Int = (screenSize.width * 0.8f).toInt() |
||||
val preferredHeight: Int = (screenSize.height * 0.8f).toInt() |
||||
val width: Int = if (desiredWidth < preferredWidth) desiredWidth else preferredWidth |
||||
val height: Int = if (desiredHeight < preferredHeight) desiredHeight else preferredHeight |
||||
return IntSize(width, height) |
||||
} |
@ -0,0 +1,21 @@
|
||||
# Project-wide Gradle settings. |
||||
# IDE (e.g. Android Studio) users: |
||||
# Gradle settings configured through the IDE *will override* |
||||
# any settings specified in this file. |
||||
# For more details on how to configure your build environment visit |
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html |
||||
# Specifies the JVM arguments used for the daemon process. |
||||
# The setting is particularly useful for tweaking memory settings. |
||||
org.gradle.jvmargs=-Xmx2048m |
||||
# When configured, Gradle will run in incubating parallel mode. |
||||
# This option should only be used with decoupled projects. More details, visit |
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
||||
# org.gradle.parallel=true |
||||
# AndroidX package structure to make it clearer which packages are bundled with the |
||||
# Android operating system, and which are packaged with your app"s APK |
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn |
||||
android.useAndroidX=true |
||||
# Automatically convert third-party libraries to use AndroidX |
||||
android.enableJetifier=true |
||||
# Kotlin code style for this project: "official" or "obsolete": |
||||
kotlin.code.style=official |
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME |
||||
distributionPath=wrapper/dists |
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip |
||||
zipStoreBase=GRADLE_USER_HOME |
||||
zipStorePath=wrapper/dists |
@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env sh |
||||
|
||||
# |
||||
# Copyright 2015 the original author or authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
# |
||||
|
||||
############################################################################## |
||||
## |
||||
## Gradle start up script for UN*X |
||||
## |
||||
############################################################################## |
||||
|
||||
# Attempt to set APP_HOME |
||||
# Resolve links: $0 may be a link |
||||
PRG="$0" |
||||
# Need this for relative symlinks. |
||||
while [ -h "$PRG" ] ; do |
||||
ls=`ls -ld "$PRG"` |
||||
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
if expr "$link" : '/.*' > /dev/null; then |
||||
PRG="$link" |
||||
else |
||||
PRG=`dirname "$PRG"`"/$link" |
||||
fi |
||||
done |
||||
SAVED="`pwd`" |
||||
cd "`dirname \"$PRG\"`/" >/dev/null |
||||
APP_HOME="`pwd -P`" |
||||
cd "$SAVED" >/dev/null |
||||
|
||||
APP_NAME="Gradle" |
||||
APP_BASE_NAME=`basename "$0"` |
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' |
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value. |
||||
MAX_FD="maximum" |
||||
|
||||
warn () { |
||||
echo "$*" |
||||
} |
||||
|
||||
die () { |
||||
echo |
||||
echo "$*" |
||||
echo |
||||
exit 1 |
||||
} |
||||
|
||||
# OS specific support (must be 'true' or 'false'). |
||||
cygwin=false |
||||
msys=false |
||||
darwin=false |
||||
nonstop=false |
||||
case "`uname`" in |
||||
CYGWIN* ) |
||||
cygwin=true |
||||
;; |
||||
Darwin* ) |
||||
darwin=true |
||||
;; |
||||
MINGW* ) |
||||
msys=true |
||||
;; |
||||
NONSTOP* ) |
||||
nonstop=true |
||||
;; |
||||
esac |
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
||||
|
||||
# Determine the Java command to use to start the JVM. |
||||
if [ -n "$JAVA_HOME" ] ; then |
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
# IBM's JDK on AIX uses strange locations for the executables |
||||
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
else |
||||
JAVACMD="$JAVA_HOME/bin/java" |
||||
fi |
||||
if [ ! -x "$JAVACMD" ] ; then |
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the |
||||
location of your Java installation." |
||||
fi |
||||
else |
||||
JAVACMD="java" |
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the |
||||
location of your Java installation." |
||||
fi |
||||
|
||||
# Increase the maximum file descriptors if we can. |
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then |
||||
MAX_FD_LIMIT=`ulimit -H -n` |
||||
if [ $? -eq 0 ] ; then |
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
||||
MAX_FD="$MAX_FD_LIMIT" |
||||
fi |
||||
ulimit -n $MAX_FD |
||||
if [ $? -ne 0 ] ; then |
||||
warn "Could not set maximum file descriptor limit: $MAX_FD" |
||||
fi |
||||
else |
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
||||
fi |
||||
fi |
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock |
||||
if $darwin; then |
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
||||
fi |
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java |
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then |
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
||||
JAVACMD=`cygpath --unix "$JAVACMD"` |
||||
|
||||
# We build the pattern for arguments to be converted via cygpath |
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
||||
SEP="" |
||||
for dir in $ROOTDIRSRAW ; do |
||||
ROOTDIRS="$ROOTDIRS$SEP$dir" |
||||
SEP="|" |
||||
done |
||||
OURCYGPATTERN="(^($ROOTDIRS))" |
||||
# Add a user-defined pattern to the cygpath arguments |
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
||||
fi |
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
||||
i=0 |
||||
for arg in "$@" ; do |
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
||||
else |
||||
eval `echo args$i`="\"$arg\"" |
||||
fi |
||||
i=`expr $i + 1` |
||||
done |
||||
case $i in |
||||
0) set -- ;; |
||||
1) set -- "$args0" ;; |
||||
2) set -- "$args0" "$args1" ;; |
||||
3) set -- "$args0" "$args1" "$args2" ;; |
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
||||
esac |
||||
fi |
||||
|
||||
# Escape application args |
||||
save () { |
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done |
||||
echo " " |
||||
} |
||||
APP_ARGS=`save "$@"` |
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules |
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" |
||||
|
||||
exec "$JAVACMD" "$@" |
@ -0,0 +1,100 @@
|
||||
@rem |
||||
@rem Copyright 2015 the original author or authors. |
||||
@rem |
||||
@rem Licensed under the Apache License, Version 2.0 (the "License"); |
||||
@rem you may not use this file except in compliance with the License. |
||||
@rem You may obtain a copy of the License at |
||||
@rem |
||||
@rem https://www.apache.org/licenses/LICENSE-2.0 |
||||
@rem |
||||
@rem Unless required by applicable law or agreed to in writing, software |
||||
@rem distributed under the License is distributed on an "AS IS" BASIS, |
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
@rem See the License for the specific language governing permissions and |
||||
@rem limitations under the License. |
||||
@rem |
||||
|
||||
@if "%DEBUG%" == "" @echo off |
||||
@rem ########################################################################## |
||||
@rem |
||||
@rem Gradle startup script for Windows |
||||
@rem |
||||
@rem ########################################################################## |
||||
|
||||
@rem Set local scope for the variables with windows NT shell |
||||
if "%OS%"=="Windows_NT" setlocal |
||||
|
||||
set DIRNAME=%~dp0 |
||||
if "%DIRNAME%" == "" set DIRNAME=. |
||||
set APP_BASE_NAME=%~n0 |
||||
set APP_HOME=%DIRNAME% |
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" |
||||
|
||||
@rem Find java.exe |
||||
if defined JAVA_HOME goto findJavaFromJavaHome |
||||
|
||||
set JAVA_EXE=java.exe |
||||
%JAVA_EXE% -version >NUL 2>&1 |
||||
if "%ERRORLEVEL%" == "0" goto init |
||||
|
||||
echo. |
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
echo. |
||||
echo Please set the JAVA_HOME variable in your environment to match the |
||||
echo location of your Java installation. |
||||
|
||||
goto fail |
||||
|
||||
:findJavaFromJavaHome |
||||
set JAVA_HOME=%JAVA_HOME:"=% |
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
||||
|
||||
if exist "%JAVA_EXE%" goto init |
||||
|
||||
echo. |
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
||||
echo. |
||||
echo Please set the JAVA_HOME variable in your environment to match the |
||||
echo location of your Java installation. |
||||
|
||||
goto fail |
||||
|
||||
:init |
||||
@rem Get command-line arguments, handling Windows variants |
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args |
||||
|
||||
:win9xME_args |
||||
@rem Slurp the command line arguments. |
||||
set CMD_LINE_ARGS= |
||||
set _SKIP=2 |
||||
|
||||
:win9xME_args_slurp |
||||
if "x%~1" == "x" goto execute |
||||
|
||||
set CMD_LINE_ARGS=%* |
||||
|
||||
:execute |
||||
@rem Setup the command line |
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
||||
|
||||
@rem Execute Gradle |
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% |
||||
|
||||
:end |
||||
@rem End local scope for the variables with windows NT shell |
||||
if "%ERRORLEVEL%"=="0" goto mainEnd |
||||
|
||||
:fail |
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
||||
rem the _cmd.exe /c_ return code! |
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 |
||||
exit /b 1 |
||||
|
||||
:mainEnd |
||||
if "%OS%"=="Windows_NT" endlocal |
||||
|
||||
:omega |
@ -0,0 +1 @@
|
||||
include(":common", ":android", ":desktop") |
@ -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. |