feat: support compose multiplatform
1
samples/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
1
samples/androidApp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
45
samples/androidApp/build.gradle.kts
Normal file
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.kotlin.multiplatform)
|
||||
autowire(libs.plugins.android.application)
|
||||
autowire(libs.plugins.jetbrains.compose)
|
||||
}
|
||||
|
||||
group = property.project.groupName
|
||||
|
||||
kotlin {
|
||||
androidTarget()
|
||||
jvmToolchain(17)
|
||||
sourceSets {
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
implementation(projects.samples.shared)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = property.project.groupName
|
||||
compileSdk = property.project.android.compileSdk
|
||||
|
||||
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||
|
||||
defaultConfig {
|
||||
applicationId = property.project.groupName
|
||||
minSdk = property.project.android.minSdk
|
||||
targetSdk = property.project.android.targetSdk
|
||||
versionName = property.project.samples.androidApp.versionName
|
||||
versionCode = property.project.samples.androidApp.versionCode
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
21
samples/androidApp/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.kts.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
23
samples/androidApp/src/androidMain/AndroidManifest.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<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.PageCurl">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.PageCurl">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
BIN
samples/androidApp/src/androidMain/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,26 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
setContent { App(Modifier.safeDrawingPadding()) }
|
||||
val callback = onBackPressedDispatcher.addCallback(enabled = false) {
|
||||
isEnabled = false
|
||||
PageStateHandler.navToMain()
|
||||
}
|
||||
PageStateHandler.onLeftMain { callback.isEnabled = true }
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group
|
||||
android:scaleX="2.475"
|
||||
android:scaleY="2.475"
|
||||
android:translateX="34.2"
|
||||
android:translateY="34.2">
|
||||
<path
|
||||
android:fillColor="#FFF281"
|
||||
android:pathData="M4,0.375H12C14.002,0.375 15.625,1.9981 15.625,4.0001V4.0052V4.0103V4.0154V4.0205V4.0256V4.0307V4.0358V4.0409V4.046V4.0511V4.0561V4.0612V4.0663V4.0714V4.0765V4.0816V4.0867V4.0917V4.0968V4.1019V4.107V4.1121V4.1171V4.1222V4.1273V4.1324V4.1374V4.1425V4.1476V4.1526V4.1577V4.1628V4.1678V4.1729V4.178V4.183V4.1881V4.1932V4.1982V4.2033V4.2083V4.2134V4.2184V4.2235V4.2285V4.2336V4.2386V4.2437V4.2487V4.2538V4.2588V4.2639V4.2689V4.274V4.279V4.284V4.2891V4.2941V4.2991V4.3042V4.3092V4.3142V4.3193V4.3243V4.3293V4.3343V4.3394V4.3444V4.3494V4.3544V4.3594V4.3644V4.3695V4.3745V4.3795V4.3845V4.3895V4.3945V4.3995V4.4045V4.4095V4.4145V4.4195V4.4245V4.4295V4.4345V4.4395V4.4445V4.4495V4.4545V4.4595V4.4644V4.4694V4.4744V4.4794V4.4844V4.4893V4.4943V4.4993V4.5043V4.5092V4.5142V4.5192V4.5241V4.5291V4.5341V4.539V4.544V4.5489V4.5539V4.5588V4.5638V4.5687V4.5737V4.5786V4.5836V4.5885V4.5934V4.5984V4.6033V4.6083V4.6132V4.6181V4.623V4.628V4.6329V4.6378V4.6427V4.6476V4.6526V4.6575V4.6624V4.6673V4.6722V4.6771V4.682V4.6869V4.6918V4.6967V4.7016V4.7065V4.7114V4.7163V4.7211V4.726V4.7309V4.7358V4.7407V4.7456V4.7504V4.7553V4.7602V4.765V4.7699V4.7748V4.7796V4.7845V4.7893V4.7942V4.799V4.8039V4.8087V4.8136V4.8184V4.8232V4.8281V4.8329V4.8377V4.8426V4.8474V4.8522V4.8571V4.8619V4.8667V4.8715V4.8763V4.8811V4.8859V4.8907V4.8955V4.9004V4.9051V4.9099V4.9147V4.9195V4.9243V4.9291V4.9339V4.9387V4.9434V4.9482V4.953V4.9578V4.9625V4.9673V4.9721V4.9768V4.9816V4.9863V4.9911V4.9958V5.0006V5.0053V5.01V5.0148V5.0195V5.0243V5.029V5.0337V5.0384V5.0432V5.0479V5.0526V5.0573V5.062V5.0667V5.0714V5.0761V5.0808V5.0855V5.0902V5.0949V5.0996V5.1043V5.109V5.1136V5.1183V5.123V5.1277V5.1323V5.137V5.1417V5.1463V5.151V5.1556V5.1603V5.1649V5.1696V5.1742V5.1788V5.1835V5.1881V5.1927V5.1974V5.202V5.2066V5.2112V5.2158V5.2204V5.225V5.2296V5.2342V5.2388V5.2434V5.248V5.2526V5.2572V5.2618V5.2663V5.2709V5.2755V5.2801V5.2846V5.2892V5.2937V5.2983V5.3029V5.3074V5.312V5.3165V5.321V5.3256V5.3301V5.3346V5.3391V5.3437V5.3482V5.3527V5.3572V5.3617V5.3662V5.3707V5.3752V5.3797V5.3842V5.3887V5.3932V5.3977V5.4021V5.4066V5.4111V5.4155V5.42V5.4245V5.4289V5.4334V5.4378V5.4423V5.4467V5.4511V5.4556V5.46V5.4644V5.4689V5.4733V5.4777V5.4821V5.4865V5.4909V5.4953V5.4997V5.5041V5.5085V5.5129V5.5173V5.5216V5.526V5.5304V5.5348V5.5391V5.5435V5.5478V5.5522V5.5565V5.5609V5.5652V5.5696V5.5739V5.5782V5.5826V5.5869V5.5912V5.5955V5.5998V5.6041V5.6084V5.6127V5.617V5.6213V5.6256V5.6299V5.6341V5.6384V5.6427V5.6469V5.6512V5.6555V5.6597V5.664V5.6682V5.6725V5.6767V5.6809V5.6852V5.6894V5.6936V5.6978V5.702V5.7062V5.7104V5.7146V5.7188V5.723V5.7272V5.7314V5.7356V5.7397V5.7439V5.7481V5.7523V5.7564V5.7606V5.7647V5.7689V5.773V5.7771V5.7813V5.7854V5.7895V5.7936V5.7977V5.8019V5.806V5.8101V5.8142V5.8183V5.8223V5.8264V5.8305V5.8346V5.8387V5.8427V5.8468V5.8508V5.8549V5.8589V5.863V5.867V5.871V5.8751V5.8791V5.8831V5.8871V5.8911V5.8952V5.8992V5.9032V5.9071V5.9111V5.9151V5.9191V5.9231V5.927V5.931V5.935V5.9389V5.9429V5.9468V5.9508V5.9547V5.9586V5.9626V5.9665V5.9704V5.9743V5.9782V5.9821V5.986V5.9899V5.9938V5.9977V6.0016V6.0054V6.0093V6.0132V6.017V6.0209V6.0247V6.0286V6.0324V6.0363V6.0401V6.0439V6.0477V6.0515V6.0554V6.0592V6.063V6.0668V6.0705V6.0743V6.0781V6.0819V6.0857V6.0894V6.0932V6.0969V6.1007V6.1044V6.1082V6.1119V6.1156V6.1194V6.1231V6.1268V6.1305V6.1342V6.1379V6.1416V6.1453V6.149V6.1526V6.1563V6.16V6.1636V6.1673V6.1709V6.1746V6.1782V6.1819V6.1855V6.1891V6.1927V6.1964V6.2V6.2036V6.2072V6.2108V6.2144V6.2179V6.2215V6.2251V6.2287V6.2322V6.2358V6.2393V6.2428V6.2464V6.2499V6.2534V6.257V6.2605V6.264V6.2675V6.271V6.2745V6.278V6.2815V6.2849V6.2884V6.2919V6.2954C15.625,6.6481 15.4851,6.9846 15.2348,7.2348L7.2348,15.2348C6.985,15.4847 6.6462,15.625 6.2929,15.625H4C1.998,15.625 0.375,14.002 0.375,12V4C0.375,1.998 1.998,0.375 4,0.375Z"
|
||||
android:strokeWidth="0.75"
|
||||
android:strokeColor="#000000" />
|
||||
<path
|
||||
android:fillColor="#FFF9C2"
|
||||
android:pathData="M10,6.375H15.5902C15.5151,6.7764 15.3206,7.1491 15.0277,7.4419L11.7348,10.7348L7.4419,15.0277C7.1491,15.3206 6.7764,15.5151 6.375,15.5902V10C6.375,7.998 7.998,6.375 10,6.375Z"
|
||||
android:strokeWidth="0.75"
|
||||
android:strokeColor="#000000" />
|
||||
<path
|
||||
android:fillColor="#6DD3FF"
|
||||
android:pathData="M7.4057,15.1246L15.1251,7.4053V11.4979C15.1251,13.4994 13.503,15.1221 11.5016,15.1229L7.4057,15.1246Z"
|
||||
android:strokeWidth="0.75"
|
||||
android:strokeColor="#000000" />
|
||||
</group>
|
||||
</vector>
|
BIN
samples/androidApp/src/androidMain/res/drawable/img_sit.jpg
Normal file
After Width: | Height: | Size: 286 KiB |
BIN
samples/androidApp/src/androidMain/res/drawable/img_sleep.jpg
Normal file
After Width: | Height: | Size: 607 KiB |
@@ -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="@color/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="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 12 KiB |
4
samples/androidApp/src/androidMain/res/values/colors.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">PageCurl</string>
|
||||
</resources>
|
7
samples/androidApp/src/androidMain/res/values/themes.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.PageCurl" parent="android:Theme.Material.Light.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
1
samples/desktopApp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
29
samples/desktopApp/build.gradle.kts
Normal file
@@ -0,0 +1,29 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.kotlin.multiplatform)
|
||||
autowire(libs.plugins.jetbrains.compose)
|
||||
}
|
||||
|
||||
group = property.project.groupName
|
||||
|
||||
kotlin {
|
||||
jvm("desktop")
|
||||
jvmToolchain(17)
|
||||
sourceSets {
|
||||
val desktopMain by getting {
|
||||
dependencies {
|
||||
implementation(projects.samples.shared)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
compose.desktop {
|
||||
application {
|
||||
mainClass = "${property.project.groupName}.MainKt"
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.application
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
|
||||
fun main() = application {
|
||||
Window(
|
||||
onCloseRequest = ::exitApplication,
|
||||
title = "PageCurl Demo",
|
||||
state = rememberWindowState(width = 450.dp, height = 750.dp)
|
||||
) { App() }
|
||||
}
|
6
samples/iosApp/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## Xcode 8 and earlier
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
3
samples/iosApp/Configuration/Config.xcconfig
Normal file
@@ -0,0 +1,3 @@
|
||||
TEAM_ID=
|
||||
BUNDLE_ID=com.myapplication.MyApplication
|
||||
APP_NAME=PageCurl
|
400
samples/iosApp/iosApp.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,400 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
|
||||
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
|
||||
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
|
||||
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
||||
7555FF7B242A565900829871 /* PageCurl.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PageCurl.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
F85CB1118929364A9C6EFABC /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
058557D7273AAEEB004C7B11 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
42799AB246E5F90AF97AA0EF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7555FF72242A565900829871 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AB1DB47929225F7C00F7AF9C /* Configuration */,
|
||||
7555FF7D242A565900829871 /* iosApp */,
|
||||
7555FF7C242A565900829871 /* Products */,
|
||||
42799AB246E5F90AF97AA0EF /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7555FF7C242A565900829871 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7555FF7B242A565900829871 /* PageCurl.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7555FF7D242A565900829871 /* iosApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
058557BA273AAA24004C7B11 /* Assets.xcassets */,
|
||||
7555FF82242A565900829871 /* ContentView.swift */,
|
||||
7555FF8C242A565B00829871 /* Info.plist */,
|
||||
2152FB032600AC8F00CF470E /* iOSApp.swift */,
|
||||
058557D7273AAEEB004C7B11 /* Preview Content */,
|
||||
);
|
||||
path = iosApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AB1DB47929225F7C00F7AF9C /* Configuration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AB3632DC29227652001CCB65 /* Config.xcconfig */,
|
||||
);
|
||||
path = Configuration;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
7555FF7A242A565900829871 /* iosApp */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
|
||||
buildPhases = (
|
||||
05D91A912A5EF49C00F138EB /* Compile Kotlin */,
|
||||
7555FF77242A565900829871 /* Sources */,
|
||||
7555FF79242A565900829871 /* Resources */,
|
||||
F85CB1118929364A9C6EFABC /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = iosApp;
|
||||
productName = iosApp;
|
||||
productReference = 7555FF7B242A565900829871 /* PageCurl.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
7555FF73242A565900829871 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1130;
|
||||
LastUpgradeCheck = 1130;
|
||||
ORGANIZATIONNAME = orgName;
|
||||
TargetAttributes = {
|
||||
7555FF7A242A565900829871 = {
|
||||
CreatedOnToolsVersion = 11.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 7555FF72242A565900829871;
|
||||
productRefGroup = 7555FF7C242A565900829871 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
7555FF7A242A565900829871 /* iosApp */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
7555FF79242A565900829871 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
|
||||
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
05D91A912A5EF49C00F138EB /* Compile Kotlin */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Compile Kotlin";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "cd \"$(dirname \"$(dirname \"$SRCROOT\")\")\"\n./gradlew :samples:shared:embedAndSignAppleFrameworkForXcode\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
7555FF77242A565900829871 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
|
||||
7555FF83242A565900829871 /* ContentView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
7555FFA3242A565B00829871 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7555FFA4242A565B00829871 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
7555FFA6242A565B00829871 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = "${TEAM_ID}";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)\n",
|
||||
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-framework",
|
||||
"shared\n$(inherited)",
|
||||
"-framework",
|
||||
"shared\n",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
|
||||
PRODUCT_NAME = "${APP_NAME}";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7555FFA7242A565B00829871 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = "${TEAM_ID}";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)\n",
|
||||
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n",
|
||||
);
|
||||
INFOPLIST_FILE = iosApp/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-framework",
|
||||
"shared\n$(inherited)",
|
||||
"-framework",
|
||||
"shared\n",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
|
||||
PRODUCT_NAME = "${APP_NAME}";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7555FFA3242A565B00829871 /* Debug */,
|
||||
7555FFA4242A565B00829871 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7555FFA6242A565B00829871 /* Debug */,
|
||||
7555FFA7242A565B00829871 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 7555FF73242A565900829871 /* Project object */;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "app-icon-512.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 17 KiB |
6
samples/iosApp/iosApp/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
19
samples/iosApp/iosApp/ContentView.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import shared
|
||||
|
||||
struct ComposeView: UIViewControllerRepresentable {
|
||||
func makeUIViewController(context: Context) -> UIViewController {
|
||||
let viewController = Main_iosKt.createUIViewController()
|
||||
return viewController
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
ComposeView()
|
||||
.ignoresSafeArea(.all, edges: .bottom) // Compose has own keyboard handler
|
||||
}
|
||||
}
|
50
samples/iosApp/iosApp/Info.plist
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
10
samples/iosApp/iosApp/iOSApp.swift
Normal file
@@ -0,0 +1,10 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct iOSApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
1
samples/shared/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
87
samples/shared/build.gradle.kts
Normal file
@@ -0,0 +1,87 @@
|
||||
plugins {
|
||||
autowire(libs.plugins.kotlin.multiplatform)
|
||||
autowire(libs.plugins.android.library)
|
||||
autowire(libs.plugins.jetbrains.compose)
|
||||
}
|
||||
|
||||
group = property.project.groupName
|
||||
|
||||
kotlin {
|
||||
androidTarget()
|
||||
jvm("desktop")
|
||||
listOf(
|
||||
iosX64(),
|
||||
iosArm64(),
|
||||
iosSimulatorArm64()
|
||||
).forEach { iosTarget ->
|
||||
iosTarget.binaries.framework {
|
||||
baseName = "shared"
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
jvmToolchain(17)
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings {
|
||||
optIn("androidx.compose.material3.ExperimentalMaterial3Api")
|
||||
optIn("eu.wewox.pagecurl.ExperimentalPageCurlApi")
|
||||
}
|
||||
}
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material3)
|
||||
implementation(app.cash.paging.paging.common)
|
||||
implementation(app.cash.paging.paging.compose.common)
|
||||
implementation(projects.pagecurl)
|
||||
}
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependencies {
|
||||
api(compose.foundation)
|
||||
api(androidx.core.core.ktx)
|
||||
api(androidx.appcompat.appcompat)
|
||||
api(androidx.activity.activity)
|
||||
api(androidx.activity.activity.compose)
|
||||
}
|
||||
}
|
||||
val desktopMain by getting {
|
||||
dependencies {
|
||||
api(compose.desktop.currentOs)
|
||||
api(compose.runtime)
|
||||
api(compose.foundation)
|
||||
api(compose.material3)
|
||||
}
|
||||
}
|
||||
val iosX64Main by getting
|
||||
val iosArm64Main by getting
|
||||
val iosSimulatorArm64Main by getting
|
||||
val iosMain by creating {
|
||||
dependsOn(commonMain)
|
||||
iosX64Main.dependsOn(this)
|
||||
iosArm64Main.dependsOn(this)
|
||||
iosSimulatorArm64Main.dependsOn(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = property.project.groupName
|
||||
compileSdk = property.project.android.compileSdk
|
||||
|
||||
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||
|
||||
defaultConfig {
|
||||
minSdk = property.project.android.minSdk
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
internal actual fun BackablePageScreen(content: @Composable () -> Unit) {
|
||||
content()
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.wewox.pagecurl.screens.BackPagePageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.InteractionConfigInPageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.PagingPageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.SettingsPageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.ShadowInPageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.SimplePageCurlScreen
|
||||
import eu.wewox.pagecurl.screens.StateInPageCurlScreen
|
||||
import eu.wewox.pagecurl.ui.theme.PageCurlTheme
|
||||
|
||||
/**
|
||||
* Main view for demo application.
|
||||
* Contains simple "Crossfade" based navigation to various examples.
|
||||
*/
|
||||
@Composable
|
||||
fun App(modifier: Modifier = Modifier) {
|
||||
PageCurlTheme {
|
||||
var example by rememberSaveable { mutableStateOf<Example?>(null) }
|
||||
PageStateHandler.callNavToMain = { example = null }
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
Crossfade(targetState = example, modifier, label = "Crossfade") { selected ->
|
||||
when (selected) {
|
||||
null -> RootScreen(onExampleClick = { PageStateHandler.onLeftMain?.invoke(); example = it })
|
||||
Example.SimplePageCurl -> BackablePageScreen { SimplePageCurlScreen() }
|
||||
Example.PagingPageCurl -> BackablePageScreen { PagingPageCurlScreen() }
|
||||
Example.SettingsPageCurl -> BackablePageScreen { SettingsPageCurlScreen() }
|
||||
Example.StateInPageCurl -> BackablePageScreen { StateInPageCurlScreen() }
|
||||
Example.InteractionConfigInPageCurl -> BackablePageScreen { InteractionConfigInPageCurlScreen() }
|
||||
Example.ShadowPageCurl -> BackablePageScreen { ShadowInPageCurlScreen() }
|
||||
Example.BackPagePageCurl -> BackablePageScreen { BackPagePageCurlScreen() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object PageStateHandler {
|
||||
|
||||
internal var onLeftMain: (() -> Unit)? = null
|
||||
internal var callNavToMain: (() -> Unit)? = null
|
||||
|
||||
fun onLeftMain(onLeftMain: () -> Unit) {
|
||||
this.onLeftMain = onLeftMain
|
||||
}
|
||||
|
||||
fun navToMain() {
|
||||
callNavToMain?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal expect fun BackablePageScreen(content: @Composable () -> Unit)
|
@@ -0,0 +1,41 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
/**
|
||||
* Enumeration of available demo examples.
|
||||
*
|
||||
* @param label Example name.
|
||||
* @param description Brief description.
|
||||
*/
|
||||
enum class Example(
|
||||
val label: String,
|
||||
val description: String,
|
||||
) {
|
||||
SimplePageCurl(
|
||||
"Simple Page Curl",
|
||||
"Basic PageCurl usage"
|
||||
),
|
||||
PagingPageCurl(
|
||||
"PageCurl with lazy paging",
|
||||
"Example how component could be used with paging implementation"
|
||||
),
|
||||
SettingsPageCurl(
|
||||
"Page Curl With Settings",
|
||||
"Showcases how individual interactions can be toggled on / off"
|
||||
),
|
||||
StateInPageCurl(
|
||||
"Page Curl With State Management",
|
||||
"Example how state can be used to change current page (snap / animate)"
|
||||
),
|
||||
InteractionConfigInPageCurl(
|
||||
"Interactions Configurations In Page Curl",
|
||||
"Example interactions (drag / tap) can be customized"
|
||||
),
|
||||
ShadowPageCurl(
|
||||
"Shadow Configuration in Page Curl",
|
||||
"Example how to customize shadow of the page"
|
||||
),
|
||||
BackPagePageCurl(
|
||||
"Back-Page Configuration in Page Curl",
|
||||
"Example how to customize the back-page (the back of the page user see during the drag or animation)"
|
||||
),
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
@file:Suppress("MaxLineLength", "UndocumentedPublicProperty")
|
||||
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
/**
|
||||
* The model for data for the page.
|
||||
*
|
||||
* @property title The title to show on the page.
|
||||
* @property message The message to show on the page.
|
||||
*/
|
||||
data class HowToPageData(
|
||||
val title: String,
|
||||
val message: String,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val simpleHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Welcome \uD83D\uDC4B",
|
||||
"This is a simple demo of the PageCurl. Swipe to the left to turn the page.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Forward & backward",
|
||||
"Nice, now try another direction to go backward.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Taps",
|
||||
"You may also just tap in the right half of the screen to go forward and tap on the left one to go backward.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
|
||||
val interactionHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Interaction example",
|
||||
"This example demonstrates how drag & tap gestures can be toggled on or off. By default all gestures are allowed.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Custom tap",
|
||||
"This example has a custom tap configured to show a settings popup. Try it and tap somewhere near the center of the page.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"Try to disable forward / backward drags and / or forward / backward taps.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
|
||||
val stateHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"State example",
|
||||
"This example demonstrates how state object can be used to change current page.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Custom tap",
|
||||
"This example has a custom tap configured to show a settings row below. Try it and tap somewhere near the center of the page. Tap on the PageCurl to zoom back in.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"Use buttons to go to the first / last page, or try to snap / animate forward or backward. The .snapTo() method changes the current page immediately, but .next() and .prev() methods changes the current page with a default animation. This animation could be customized, see DefaultNext and DefaultPrev in library sources.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
|
||||
val interactionSettingsHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Another interaction example",
|
||||
"This example demonstrates how drag & tap gestures zones can be configured. By default it is right half to go forward and left half to go backward.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Custom tap",
|
||||
"This example has a custom tap configured to show a settings row below. Try it and tap somewhere near the center of the page. Tap on the PageCurl to zoom back in.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"Try to change the slider value and see how it changes the gesture zones. For example if you set 0.25f on the tap gesture this means, that the first 25% of the width will be dedicated for backward tap, and other 75% will be used for forward tap.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End region in drag",
|
||||
"Keep in mind, that drag gestures have 'end' region (where gesture should be ended to complete a drag), so if you set 0f for drag gesture, the only forward gesture will be allowed, but it could not be completed. You can modify the 'end' region as needed for your use-case.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
|
||||
val shadowHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Shadow configuration",
|
||||
"This example demonstrates how shadow can be configured.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Custom tap",
|
||||
"This example has a custom tap configured to show a settings row below. Try it and tap somewhere near the center of the page. Tap on the PageCurl to zoom back in.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"Try to change different sliders. Settings should be self-descriptive. You may also change the shadow color (not present in the example).",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
|
||||
val backPageHowToPages = listOf(
|
||||
HowToPageData(
|
||||
"Back-page configuration",
|
||||
"This example demonstrates how back-page can be configured.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Custom tap",
|
||||
"This example has a custom tap configured to show a settings row below. Try it and tap somewhere near the center of the page. Tap on the PageCurl to zoom back in.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"The alpha slider configures how much content is visible on the back page. Bigger value means that page is more see-through.",
|
||||
),
|
||||
HowToPageData(
|
||||
"Settings",
|
||||
"The color value defines a color of the back-page. It is more visible is the alpha value is low.",
|
||||
),
|
||||
HowToPageData(
|
||||
"End",
|
||||
"That is the last page, you cannot go further \uD83D\uDE09",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.wewox.pagecurl.components.TopBar
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
|
||||
@Composable
|
||||
internal fun RootScreen(onExampleClick: (Example) -> Unit) {
|
||||
Scaffold(
|
||||
topBar = { TopBar("PageCurl Demo") }
|
||||
) { padding ->
|
||||
LazyColumn(Modifier.padding(padding)) {
|
||||
items(Example.entries) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onExampleClick(it) }
|
||||
.padding(SpacingMedium)
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = it.label,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = it.description,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowRight,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
|
||||
/**
|
||||
* The simple page to use for demo purposes.
|
||||
*
|
||||
* @param index The index of the page to show a page number in the bottom.
|
||||
* @param page The page data to show.
|
||||
* @param modifier The modifier for this composable.
|
||||
*/
|
||||
@Composable
|
||||
fun HowToPage(
|
||||
index: Int,
|
||||
page: HowToPageData,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(SpacingMedium, Alignment.CenterVertically),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(SpacingLarge)
|
||||
) {
|
||||
Text(
|
||||
text = page.title,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Text(
|
||||
text = page.message,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = index.toString(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.background(MaterialTheme.colorScheme.onBackground, RoundedCornerShape(topStartPercent = 100))
|
||||
.padding(SpacingMedium)
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
|
||||
@Composable
|
||||
internal fun SettingsPopup(
|
||||
config: PageCurlConfig,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
Popup(
|
||||
alignment = Alignment.Center,
|
||||
properties = PopupProperties(focusable = true),
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
elevation = CardDefaults.cardElevation(8.dp),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.width(IntrinsicSize.Max)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
val switchRowModifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp)
|
||||
SwitchRow(
|
||||
text = "Forward drag enabled",
|
||||
enabled = config.dragForwardEnabled,
|
||||
onChanged = { config.dragForwardEnabled = it },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
SwitchRow(
|
||||
text = "Backward drag enabled",
|
||||
enabled = config.dragBackwardEnabled,
|
||||
onChanged = { config.dragBackwardEnabled = it },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
SwitchRow(
|
||||
text = "Forward tap enabled",
|
||||
enabled = config.tapForwardEnabled,
|
||||
onChanged = { config.tapForwardEnabled = it },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
SwitchRow(
|
||||
text = "Backward tap enabled",
|
||||
enabled = config.tapBackwardEnabled,
|
||||
onChanged = { config.tapBackwardEnabled = it },
|
||||
modifier = switchRowModifier
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwitchRow(
|
||||
text: String,
|
||||
enabled: Boolean,
|
||||
onChanged: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(text = text)
|
||||
Switch(
|
||||
checked = enabled,
|
||||
onCheckedChange = onChanged
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/**
|
||||
* The reusable component for top bar.
|
||||
*
|
||||
* @param title The text to show in top bar.
|
||||
* @param modifier The modifier instance for the root composable.
|
||||
*/
|
||||
@Composable
|
||||
fun TopBar(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
if (onBackClick != null) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Back button"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(Color.Transparent),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
package eu.wewox.pagecurl.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.expandIn
|
||||
import androidx.compose.animation.shrinkOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
|
||||
/**
|
||||
* Layout which could be zoomed out and zoomed in to show / hide the [bottom] bar.
|
||||
*
|
||||
* @param zoomOut True when layout is zoomed out.
|
||||
* @param config The [PageCurlConfig] to turn off interactions in the page curl.
|
||||
* @param bottom The content of the bottom bar.
|
||||
* @param modifier The modifier for this composable.
|
||||
* @param pageCurl The content where PageCurl should be placed.
|
||||
*/
|
||||
@Composable
|
||||
fun ZoomOutLayout(
|
||||
zoomOut: Boolean,
|
||||
config: PageCurlConfig,
|
||||
bottom: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
pageCurl: @Composable () -> Unit,
|
||||
) {
|
||||
// Disable all state interactions when PageCurl is zoomed out
|
||||
LaunchedEffect(zoomOut, config) {
|
||||
with(config) {
|
||||
dragForwardEnabled = !zoomOut
|
||||
dragBackwardEnabled = !zoomOut
|
||||
tapForwardEnabled = !zoomOut
|
||||
tapBackwardEnabled = !zoomOut
|
||||
}
|
||||
}
|
||||
|
||||
ZoomOutLayout(
|
||||
zoomOut = zoomOut,
|
||||
bottom = bottom,
|
||||
modifier = modifier,
|
||||
) {
|
||||
// Animate radius and elevation with the same value, because why not :)
|
||||
val cornersAndElevation by animateDpAsState(if (zoomOut) 16.dp else 0.dp)
|
||||
|
||||
if (cornersAndElevation != 0.dp) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(cornersAndElevation),
|
||||
elevation = CardDefaults.cardElevation(cornersAndElevation),
|
||||
content = { pageCurl() },
|
||||
)
|
||||
} else {
|
||||
pageCurl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ZoomOutLayout(
|
||||
zoomOut: Boolean,
|
||||
bottom: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Layout(
|
||||
modifier = modifier,
|
||||
content = {
|
||||
content()
|
||||
Box {
|
||||
AnimatedVisibility(
|
||||
visible = zoomOut,
|
||||
enter = expandIn(expandFrom = Alignment.Center, initialSize = { IntSize(it.width, 0) }),
|
||||
exit = shrinkOut(shrinkTowards = Alignment.Center, targetSize = { IntSize(it.width, 0) })
|
||||
) {
|
||||
bottom()
|
||||
}
|
||||
}
|
||||
},
|
||||
measurePolicy = { measurables, constraints ->
|
||||
val (contentMeasurable, bottomMeasurable) = measurables
|
||||
|
||||
val bottomPlaceable = bottomMeasurable.measure(constraints)
|
||||
val contentPlaceable = contentMeasurable.measure(constraints)
|
||||
|
||||
layout(constraints.maxWidth, constraints.maxHeight) {
|
||||
bottomPlaceable.place(x = 0, y = constraints.maxHeight - bottomPlaceable.height)
|
||||
|
||||
contentPlaceable.placeWithLayer(0, 0) {
|
||||
val height = constraints.maxHeight - 2 * bottomPlaceable.height
|
||||
val scale = height / contentPlaceable.height.toFloat()
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.ZoomOutLayout
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
import eu.wewox.pagecurl.ui.SpacingSmall
|
||||
|
||||
/**
|
||||
* Back-Page Configuration in Page Curl.
|
||||
* Example how to customize the back-page (the back of the page user see during the drag or animation).
|
||||
*/
|
||||
@Composable
|
||||
fun BackPagePageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.backPageHowToPages }
|
||||
var zoomOut by remember { mutableStateOf(false) }
|
||||
val state = rememberPageCurlState()
|
||||
val config = rememberPageCurlConfig(
|
||||
onCustomTap = { size, position ->
|
||||
// When PageCurl is zoomed out then zoom back in
|
||||
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
||||
if (zoomOut) {
|
||||
zoomOut = false
|
||||
true
|
||||
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
zoomOut = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
ZoomOutLayout(
|
||||
zoomOut = zoomOut,
|
||||
config = config,
|
||||
bottom = { SettingsRow(config) },
|
||||
) {
|
||||
PageCurl(
|
||||
count = pages.size,
|
||||
state = state,
|
||||
config = config,
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRow(
|
||||
config: PageCurlConfig,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = SpacingLarge)
|
||||
) {
|
||||
Text(
|
||||
text = "Alpha",
|
||||
modifier = Modifier.padding(horizontal = SpacingLarge)
|
||||
)
|
||||
Slider(
|
||||
value = config.backPageContentAlpha,
|
||||
onValueChange = {
|
||||
config.backPageContentAlpha = it
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = SpacingLarge)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Color",
|
||||
modifier = Modifier.padding(horizontal = SpacingLarge)
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(SpacingMedium),
|
||||
modifier = Modifier
|
||||
.padding(top = SpacingSmall)
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.padding(horizontal = SpacingLarge)
|
||||
) {
|
||||
backPageColors.forEach { color ->
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.size(64.dp)
|
||||
.border(2.dp, color, CircleShape)
|
||||
.background(color.copy(alpha = 0.8f), CircleShape)
|
||||
.clip(CircleShape)
|
||||
.clickable {
|
||||
config.backPageColor = color
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val backPageColors: List<Color> = listOf(
|
||||
Color(0xFFF9CEEE),
|
||||
Color(0xFF68A7AD),
|
||||
Color(0xFFE5CB9F),
|
||||
Color(0xFFAC7D88),
|
||||
Color(0xFF9ADCFF),
|
||||
Color(0xFFFFF89A),
|
||||
Color(0xFFCDB699),
|
||||
Color(0xFFA267AC),
|
||||
)
|
@@ -0,0 +1,166 @@
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.ZoomOutLayout
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
import eu.wewox.pagecurl.ui.SpacingSmall
|
||||
|
||||
/**
|
||||
* Interactions Configurations In Page Curl.
|
||||
* Example interactions (drag / tap) can be customized.
|
||||
*/
|
||||
@Composable
|
||||
fun InteractionConfigInPageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.interactionSettingsHowToPages }
|
||||
var zoomOut by remember { mutableStateOf(false) }
|
||||
val state = rememberPageCurlState()
|
||||
val config = rememberPageCurlConfig(
|
||||
onCustomTap = { size, position ->
|
||||
// When PageCurl is zoomed out then zoom back in
|
||||
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
||||
if (zoomOut) {
|
||||
zoomOut = false
|
||||
true
|
||||
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
zoomOut = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
ZoomOutLayout(
|
||||
zoomOut = zoomOut,
|
||||
config = config,
|
||||
bottom = { SettingsRow(config) },
|
||||
) {
|
||||
PageCurl(
|
||||
count = pages.size,
|
||||
state = state,
|
||||
config = config,
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRow(
|
||||
config: PageCurlConfig,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var selectedOption by remember { mutableStateOf(InteractionOption.Drag) }
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(SpacingSmall),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(SpacingLarge)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectableGroup()
|
||||
) {
|
||||
InteractionOption.values().forEach { option ->
|
||||
Row(
|
||||
Modifier
|
||||
.selectable(
|
||||
selected = selectedOption == option,
|
||||
onClick = { selectedOption = option },
|
||||
role = Role.RadioButton
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = selectedOption == option,
|
||||
onClick = null
|
||||
)
|
||||
Text(
|
||||
text = option.name,
|
||||
modifier = Modifier.padding(start = SpacingMedium)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsRowSlider(
|
||||
selectedOption = selectedOption,
|
||||
config = config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRowSlider(
|
||||
selectedOption: InteractionOption,
|
||||
config: PageCurlConfig,
|
||||
) {
|
||||
when (selectedOption) {
|
||||
InteractionOption.Drag -> {
|
||||
Slider(
|
||||
value = config.dragForwardInteraction.start.left,
|
||||
onValueChange = {
|
||||
config.dragForwardInteraction = PageCurlConfig.DragInteraction(
|
||||
Rect(it, 0.0f, 1.0f, 1.0f),
|
||||
Rect(0.0f, 0.0f, it, 1.0f)
|
||||
)
|
||||
config.dragBackwardInteraction = PageCurlConfig.DragInteraction(
|
||||
Rect(0.0f, 0.0f, it, 1.0f),
|
||||
Rect(it, 0.0f, 1.0f, 1.0f),
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
InteractionOption.Tap -> {
|
||||
Slider(
|
||||
value = config.tapForwardInteraction.target.left,
|
||||
onValueChange = {
|
||||
config.tapForwardInteraction = PageCurlConfig.TapInteraction(
|
||||
Rect(it, 0.0f, 1.0f, 1.0f),
|
||||
)
|
||||
config.tapBackwardInteraction = PageCurlConfig.TapInteraction(
|
||||
Rect(0.0f, 0.0f, it, 1.0f),
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class InteractionOption { Drag, Tap }
|
@@ -0,0 +1,123 @@
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.paging.LoadState.Loading
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import app.cash.paging.compose.collectAsLazyPagingItems
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* PageCurl with lazy paging.
|
||||
* Example how component could be used with paging implementation.
|
||||
*/
|
||||
@Composable
|
||||
fun PagingPageCurlScreen() {
|
||||
val startPage = 4 // in range 0..9
|
||||
val startPageOffset = 5 // in range 0..9
|
||||
|
||||
val items = rememberPager(startPage).collectAsLazyPagingItems()
|
||||
|
||||
val loading = with(items.loadState) { refresh is Loading || append is Loading || prepend is Loading }
|
||||
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
if (items.loadState.refresh is Loading) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
} else {
|
||||
PageCurl(
|
||||
count = items.itemCount,
|
||||
key = { index -> items.peek(index).hashCode() },
|
||||
state = rememberPageCurlState(initialCurrent = startPageOffset),
|
||||
) { index ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
) {
|
||||
Text(items[index]?.content.orEmpty())
|
||||
Text(if (loading) "Loading..." else "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberPager(startPage: Int): Flow<PagingData<Item>> =
|
||||
remember {
|
||||
Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 10,
|
||||
prefetchDistance = 3,
|
||||
),
|
||||
pagingSourceFactory = { ItemPagingSource(startPage, BackendService()) }
|
||||
).flow
|
||||
}
|
||||
|
||||
private class Item(val content: String)
|
||||
|
||||
private class Response(val items: List<Item>, val prePageNumber: Int, val nextPageNumber: Int)
|
||||
|
||||
private class BackendService {
|
||||
|
||||
suspend fun searchItems(page: Int): Response {
|
||||
delay(1_000L)
|
||||
return Response(
|
||||
items = List(10) { Item("Content #${page * 10 + it}") },
|
||||
prePageNumber = page - 1,
|
||||
nextPageNumber = page + 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ItemPagingSource(
|
||||
private val startPage: Int,
|
||||
private val backend: BackendService
|
||||
) : PagingSource<Int, Item>() {
|
||||
|
||||
@Suppress("TooGenericExceptionCaught") // It is only for demo purpose
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> =
|
||||
try {
|
||||
val nextPageNumber = params.key ?: startPage
|
||||
val response = backend.searchItems(nextPageNumber)
|
||||
LoadResult.Page(
|
||||
data = response.items,
|
||||
prevKey = response.prePageNumber.takeIf { it >= 0 },
|
||||
nextKey = response.nextPageNumber.takeIf { it < 10 }
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
LoadResult.Error(e)
|
||||
}
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, Item>): Int? =
|
||||
state.anchorPosition?.let { anchorPosition ->
|
||||
val anchorPage = state.closestPageToPosition(anchorPosition)
|
||||
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.SettingsPopup
|
||||
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
|
||||
/**
|
||||
* Page Curl With Settings.
|
||||
* Showcases how individual interactions can be toggled on / off.
|
||||
*/
|
||||
@Composable
|
||||
fun SettingsPageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.interactionHowToPages }
|
||||
|
||||
var showPopup by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val state = rememberPageCurlState()
|
||||
val config = rememberPageCurlConfig(
|
||||
onCustomTap = { size, position ->
|
||||
// Detect tap somewhere in the center with 64 radius and show popup
|
||||
if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
showPopup = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
PageCurl(
|
||||
count = pages.size,
|
||||
state = state,
|
||||
config = config,
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
|
||||
if (showPopup) {
|
||||
SettingsPopup(
|
||||
config = config,
|
||||
onDismiss = {
|
||||
showPopup = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,112 @@
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.ZoomOutLayout
|
||||
import eu.wewox.pagecurl.config.PageCurlConfig
|
||||
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
|
||||
/**
|
||||
* Shadow Configuration in Page Curl.
|
||||
* Example how to customize shadow of the page.
|
||||
*/
|
||||
@Composable
|
||||
fun ShadowInPageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.shadowHowToPages }
|
||||
var zoomOut by remember { mutableStateOf(false) }
|
||||
val state = rememberPageCurlState()
|
||||
val config = rememberPageCurlConfig(
|
||||
onCustomTap = { size, position ->
|
||||
// When PageCurl is zoomed out then zoom back in
|
||||
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
||||
if (zoomOut) {
|
||||
zoomOut = false
|
||||
true
|
||||
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
zoomOut = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
ZoomOutLayout(
|
||||
zoomOut = zoomOut,
|
||||
config = config,
|
||||
bottom = { SettingsRow(config) },
|
||||
) {
|
||||
PageCurl(
|
||||
count = pages.size,
|
||||
state = state,
|
||||
config = config,
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRow(
|
||||
config: PageCurlConfig,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(SpacingLarge)
|
||||
) {
|
||||
Text(text = "Alpha")
|
||||
Slider(
|
||||
value = config.shadowAlpha,
|
||||
onValueChange = {
|
||||
config.shadowAlpha = it
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Text(text = "Radius")
|
||||
Slider(
|
||||
value = config.shadowRadius.value,
|
||||
onValueChange = {
|
||||
config.shadowRadius = it.dp
|
||||
},
|
||||
valueRange = 0f..32f,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Text(text = "Horizontal offset")
|
||||
Slider(
|
||||
value = config.shadowOffset.x.value,
|
||||
onValueChange = {
|
||||
config.shadowOffset = DpOffset(it.dp, 0.dp)
|
||||
},
|
||||
valueRange = -20f..20f,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
|
||||
/**
|
||||
* Simple Page Curl.
|
||||
* Basic PageCurl usage.
|
||||
*/
|
||||
@Composable
|
||||
fun SimplePageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.simpleHowToPages }
|
||||
|
||||
PageCurl(count = pages.size) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,129 @@
|
||||
package eu.wewox.pagecurl.screens
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import eu.wewox.pagecurl.HowToPageData
|
||||
import eu.wewox.pagecurl.components.HowToPage
|
||||
import eu.wewox.pagecurl.components.ZoomOutLayout
|
||||
import eu.wewox.pagecurl.config.rememberPageCurlConfig
|
||||
import eu.wewox.pagecurl.page.PageCurl
|
||||
import eu.wewox.pagecurl.page.PageCurlState
|
||||
import eu.wewox.pagecurl.page.rememberPageCurlState
|
||||
import eu.wewox.pagecurl.ui.SpacingLarge
|
||||
import eu.wewox.pagecurl.ui.SpacingMedium
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Page Curl With State Management.
|
||||
* Example how state can be used to change current page (snap / animate).
|
||||
*/
|
||||
@Composable
|
||||
fun StateInPageCurlScreen() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val pages = remember { HowToPageData.stateHowToPages }
|
||||
var zoomOut by remember { mutableStateOf(false) }
|
||||
val state = rememberPageCurlState()
|
||||
val config = rememberPageCurlConfig(
|
||||
onCustomTap = { size, position ->
|
||||
// When PageCurl is zoomed out then zoom back in
|
||||
// Else detect tap somewhere in the center with 64 radius and zoom out a PageCurl
|
||||
if (zoomOut) {
|
||||
zoomOut = false
|
||||
true
|
||||
} else if ((position - size.center.toOffset()).getDistance() < 64.dp.toPx()) {
|
||||
zoomOut = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
ZoomOutLayout(
|
||||
zoomOut = zoomOut,
|
||||
config = config,
|
||||
bottom = { SettingsRow(pages.size, state) },
|
||||
) {
|
||||
PageCurl(
|
||||
count = pages.size,
|
||||
state = state,
|
||||
config = config,
|
||||
) { index ->
|
||||
HowToPage(index, pages[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRow(
|
||||
max: Int,
|
||||
state: PageCurlState,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(SpacingMedium, Alignment.CenterHorizontally),
|
||||
modifier = modifier
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.fillMaxWidth()
|
||||
.padding(SpacingLarge)
|
||||
) {
|
||||
SettingsRowButton("Snap to first") {
|
||||
state.snapTo(0)
|
||||
}
|
||||
|
||||
SettingsRowButton("Snap to last") {
|
||||
state.snapTo(max)
|
||||
}
|
||||
|
||||
SettingsRowButton("Snap forward") {
|
||||
state.snapTo(state.current + 1)
|
||||
}
|
||||
|
||||
SettingsRowButton("Snap backward") {
|
||||
state.snapTo(state.current - 1)
|
||||
}
|
||||
|
||||
SettingsRowButton("Animate forward") {
|
||||
state.next()
|
||||
}
|
||||
|
||||
SettingsRowButton("Animate backward") {
|
||||
state.prev()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsRowButton(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: suspend () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
Button(
|
||||
onClick = { scope.launch { onClick() } },
|
||||
modifier = modifier
|
||||
) {
|
||||
Text(text = text)
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package eu.wewox.pagecurl.ui
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* The small spacing.
|
||||
*/
|
||||
val SpacingSmall = 8.dp
|
||||
|
||||
/**
|
||||
* The medium spacing.
|
||||
*/
|
||||
val SpacingMedium = 16.dp
|
||||
|
||||
/**
|
||||
* The large spacing.
|
||||
*/
|
||||
val SpacingLarge = 32.dp
|
@@ -0,0 +1,8 @@
|
||||
@file:Suppress("UndocumentedPublicProperty")
|
||||
|
||||
package eu.wewox.pagecurl.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val LightBlue = Color(0xFF6DD3FF)
|
||||
val LightYellow = Color(0xFFFFF281)
|
@@ -0,0 +1,61 @@
|
||||
package eu.wewox.pagecurl.ui.theme
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = LightBlue,
|
||||
secondary = LightBlue,
|
||||
tertiary = LightYellow,
|
||||
|
||||
onPrimary = Color.Black,
|
||||
onSecondary = Color.Black,
|
||||
onTertiary = Color.Black,
|
||||
|
||||
surface = Color.Black,
|
||||
background = Color.Black,
|
||||
|
||||
onSurface = Color.White,
|
||||
onBackground = Color.White,
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = LightBlue,
|
||||
secondary = LightBlue,
|
||||
tertiary = LightYellow,
|
||||
|
||||
onPrimary = Color.Black,
|
||||
onSecondary = Color.Black,
|
||||
onTertiary = Color.Black,
|
||||
|
||||
surface = Color.White,
|
||||
background = Color.White,
|
||||
|
||||
onSurface = Color.Black,
|
||||
onBackground = Color.Black,
|
||||
)
|
||||
|
||||
/**
|
||||
* The theme to use for demo application.
|
||||
*/
|
||||
@Composable
|
||||
fun PageCurlTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography(),
|
||||
content = content
|
||||
)
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.wewox.pagecurl.components.TopBar
|
||||
|
||||
@Composable
|
||||
internal actual fun BackablePageScreen(content: @Composable () -> Unit) {
|
||||
Box {
|
||||
content()
|
||||
TopBar(title = "", onBackClick = { PageStateHandler.navToMain() })
|
||||
}
|
||||
}
|
6
samples/shared/src/iosMain/kotlin/Main.ios.kt
Normal file
@@ -0,0 +1,6 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
import androidx.compose.ui.window.ComposeUIViewController
|
||||
import eu.wewox.pagecurl.App
|
||||
|
||||
fun createUIViewController() = ComposeUIViewController { App() }
|
@@ -0,0 +1,13 @@
|
||||
package eu.wewox.pagecurl
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.wewox.pagecurl.components.TopBar
|
||||
|
||||
@Composable
|
||||
internal actual fun BackablePageScreen(content: @Composable () -> Unit) {
|
||||
Box {
|
||||
content()
|
||||
TopBar(title = "", onBackClick = { PageStateHandler.navToMain() })
|
||||
}
|
||||
}
|