Update all dependencies

This commit is contained in:
Syer10
2023-12-01 19:21:07 -05:00
parent 30f43412d4
commit 2c088a3a7e
40 changed files with 385 additions and 431 deletions

View File

@@ -82,7 +82,7 @@ dependencies {
// Utility
implementation(libs.dateTime)
implementation(libs.immutableCollections)
implementation(libs.kds)
implementation(libs.korge.foundation)
// Localization
implementation(libs.moko.core)
@@ -90,7 +90,7 @@ dependencies {
// Testing
testImplementation(kotlin("test-junit"))
testImplementation(libs.compose.ui.test.junit4)
//testImplementation(libs.compose.ui.test.junit4)
testImplementation(libs.coroutines.test)
}

View File

@@ -50,14 +50,16 @@ subprojects {
if (name.contains("android", true)) {
jvmTarget = Config.androidJvmTarget.toString()
}
freeCompilerArgs += listOf("-Xexpect-actual-classes")
if (project.hasProperty("generateComposeCompilerMetrics")) {
freeCompilerArgs = freeCompilerArgs + listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
project.buildDir.absolutePath + "/compose_metrics",
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath
)
}
}
@@ -72,7 +74,7 @@ subprojects {
}
plugins.withType<com.android.build.gradle.BasePlugin> {
configure<com.android.build.gradle.BaseExtension> {
compileSdkVersion(33)
compileSdkVersion(34)
defaultConfig {
minSdk = 21
targetSdk = 31
@@ -126,9 +128,8 @@ subprojects {
}
}
plugins.withType<de.jensklingenberg.ktorfit.gradle.KtorfitGradleSubPlugin> {
plugins.withType<de.jensklingenberg.ktorfit.gradle.KtorfitGradlePlugin> {
configure<de.jensklingenberg.ktorfit.gradle.KtorfitGradleConfiguration> {
version = libs.versions.ktorfit.get()
logging = project.hasProperty("debugApp")
}
}
@@ -140,7 +141,7 @@ subprojects {
}
plugins.withType<ComposePlugin> {
configure<ComposeExtension> {
kotlinCompilerPlugin.set(libs.versions.composeCompiler.get())
// kotlinCompilerPlugin.set(libs.versions.composeCompiler.get())
}
}
afterEvaluate {

View File

@@ -12,5 +12,5 @@ dependencies {
implementation(gradleKotlinDsl())
implementation(gradleApi())
implementation(localGroovy())
implementation("de.undercouch:gradle-download-task:5.3.0")
implementation(libs.gradle.download.task)
}

View File

@@ -1,2 +1,11 @@
rootProject.name = "buildSrc"
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

View File

@@ -10,7 +10,7 @@ plugins {
}
kotlin {
android {
androidTarget {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
@@ -28,6 +28,21 @@ kotlin {
iosArm64()
iosSimulatorArm64()
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
applyHierarchyTemplate {
common {
group("jvm") {
withAndroidTarget()
withJvm()
}
group("ios") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
sourceSets {
all {
languageSettings {
@@ -53,7 +68,7 @@ kotlin {
api(libs.multiplatformSettings.coroutines)
api(libs.multiplatformSettings.serialization)
api(libs.dateTime)
api(libs.kds)
api(libs.korge.foundation)
api(compose("org.jetbrains.compose.ui:ui-text"))
}
}
@@ -64,53 +79,28 @@ kotlin {
}
}
val jvmMain by creating {
dependsOn(commonMain)
val jvmMain by getting {
dependencies {
api(kotlin("stdlib-jdk8"))
}
}
val jvmTest by creating {
dependsOn(commonTest)
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val desktopMain by getting {
dependsOn(jvmMain)
dependencies {
api(libs.appDirs)
}
}
val desktopTest by getting {
dependsOn(jvmTest)
}
val androidMain by getting {
dependsOn(jvmMain)
dependencies {
api(libs.compose.ui.text)
}
}
val androidUnitTest by getting {
dependsOn(jvmTest)
}
val iosMain by creating {
dependsOn(commonMain)
}
val iosTest by creating {
dependsOn(commonTest)
}
listOf(
"iosX64",
"iosArm64",
"iosSimulatorArm64",
).forEach {
getByName(it + "Main").dependsOn(iosMain)
getByName(it + "Test").dependsOn(iosTest)
}
}
}

View File

@@ -10,7 +10,7 @@ plugins {
}
kotlin {
android {
androidTarget {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
@@ -28,6 +28,21 @@ kotlin {
iosArm64()
iosSimulatorArm64()
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
applyHierarchyTemplate {
common {
group("jvm") {
withAndroidTarget()
withJvm()
}
group("ios") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
sourceSets {
all {
languageSettings {
@@ -58,47 +73,30 @@ kotlin {
}
}
val jvmMain by creating {
dependsOn(commonMain)
val jvmMain by getting {
dependencies {
api(kotlin("stdlib-jdk8"))
}
}
val jvmTest by creating {
dependsOn(commonTest)
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val desktopMain by getting {
dependsOn(jvmMain)
}
val desktopTest by getting {
dependsOn(jvmTest)
}
val androidMain by getting {
dependsOn(jvmMain)
}
val androidUnitTest by getting {
dependsOn(jvmTest)
}
val iosMain by creating {
dependsOn(commonMain)
val iosMain by getting {
}
val iosTest by creating {
dependsOn(commonTest)
}
listOf(
"iosX64",
"iosArm64",
"iosSimulatorArm64",
).forEach {
getByName(it + "Main").dependsOn(iosMain)
getByName(it + "Test").dependsOn(iosTest)
val iosTest by getting {
}
}
}

View File

@@ -87,7 +87,7 @@ dependencies {
// Utility
implementation(libs.dateTime)
implementation(libs.immutableCollections)
implementation(libs.kds)
implementation(libs.korge.foundation)
// Localization
implementation(libs.moko.core)

View File

@@ -11,7 +11,7 @@ plugins {
}
kotlin {
android {
androidTarget {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
@@ -29,6 +29,21 @@ kotlin {
iosArm64()
iosSimulatorArm64()
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
applyHierarchyTemplate {
common {
group("jvm") {
withAndroidTarget()
withJvm()
}
group("ios") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
sourceSets {
all {
languageSettings {
@@ -66,51 +81,34 @@ kotlin {
}
}
val jvmMain by creating {
dependsOn(commonMain)
val jvmMain by getting {
dependencies {
api(kotlin("stdlib-jdk8"))
api(libs.ktor.okHttp)
}
}
val jvmTest by creating {
dependsOn(commonTest)
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val desktopMain by getting {
dependsOn(jvmMain)
}
val desktopTest by getting {
dependsOn(jvmTest)
}
val androidMain by getting {
dependsOn(jvmMain)
}
val androidUnitTest by getting {
dependsOn(jvmTest)
}
val iosMain by creating {
dependsOn(commonMain)
val iosMain by getting {
dependencies {
api(libs.ktor.darwin)
}
}
val iosTest by creating {
dependsOn(commonTest)
}
listOf(
"iosX64",
"iosArm64",
"iosSimulatorArm64",
).forEach {
getByName(it + "Main").dependsOn(iosMain)
getByName(it + "Test").dependsOn(iosTest)
val iosTest by getting {
}
}
}

View File

@@ -21,6 +21,7 @@ import io.ktor.client.plugins.auth.providers.DigestAuthCredentials
import io.ktor.client.plugins.auth.providers.basic
import io.ktor.client.plugins.auth.providers.digest
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
@@ -49,6 +50,10 @@ fun httpClient(
expectSuccess = true
defaultRequest {
url(serverPreferences.serverUrl().get().toString())
}
engine {
proxy = when (serverPreferences.proxy().get()) {
Proxy.NO_PROXY -> null

View File

@@ -4,6 +4,5 @@ android.enableJetifier=false
android.useAndroidX=true
kotlin.mpp.stability.nowarn=true
kotlin.native.ignoreDisabledTargets=true
kotlin.native.cacheKind=none
org.jetbrains.compose.experimental.uikit.enabled=true
kotlin.mpp.androidSourceSetLayoutVersion=2

View File

@@ -1,82 +1,76 @@
[versions]
# Kotlin
kotlin = "1.8.22"
coroutines = "1.7.2"
kotlin = "1.9.21"
coroutines = "1.7.3"
# Serialization
json = "1.5.1"
json = "1.6.2"
# Compose
composeGradle = "1.4.1"
composeCompiler = "1.4.8"
composeAndroidRuntime = "1.4.3"
composeAndroidFoundation = "1.4.3"
composeAndroidUI = "1.4.3"
composeAndroidAnimation = "1.4.3"
composeAndroidMaterial = "1.4.3"
composeGradle = "1.5.11"
# Compose Libraries
voyager = "1.0.0-rc06"
voyager = "1.0.0-rc10"
accompanist = "0.30.1"
googleAccompanist = "0.30.1"
imageloader = "1.5.3"
materialDialogs = "0.9.3"
imageloader = "1.7.1"
materialDialogs = "0.9.4"
# Android
androidGradle = "8.0.2"
core = "1.9.0"
appCompat = "1.7.0-alpha02"
activityCompose = "1.7.2"
work = "2.8.1"
androidGradle = "8.1.4"
core = "1.12.0"
appCompat = "1.7.0-alpha03"
activityCompose = "1.8.1"
work = "2.9.0"
# Android Lifecycle
lifecycle = "2.6.1"
lifecycle = "2.6.2"
# Swing
darklaf = "3.0.2"
# Ksp
ksp = "1.8.22-1.0.11"
ksp = "1.9.21-1.0.15"
# Dependency Injection
kotlinInject = "0.6.1"
kotlinInject = "0.6.3"
# Network
ktor = "2.3.2"
ktorfit = "1.4.2"
ktorfitCompiler = "1.0.0"
ktor = "2.3.6"
ktorfit = "1.10.2"
# Logging
slf4j = "2.0.7"
log4j = "2.20.0"
slf4j = "2.0.9"
log4j = "2.22.0"
kmlogging = "1.3.0"
# Storage
okio = "3.3.0"
okio = "3.6.0"
appDirs = "1.2.1"
# Preferences
multiplatformSettings = "1.0.0-alpha01"
# Utility
desugarJdkLibs = "2.0.3"
aboutLibraries = "10.8.0"
dateTime = "0.4.0"
immutableCollections = "0.3.5"
kds = "4.0.7"
desugarJdkLibs = "2.0.4"
aboutLibraries = "10.9.2"
dateTime = "0.5.0"
immutableCollections = "0.3.6"
korge = "5.1.0"
gradleDownloadTask = "5.4.0"
# Localization
moko = "0.23.0"
# BuildConfigs
buildconfig = "4.1.1"
buildkonfig = "0.13.3"
buildconfig = "4.2.0"
buildkonfig = "0.15.1"
# Linter
kotlinter = "3.15.0"
kotlinter = "4.1.0"
# Version updates
versions = "0.47.0"
versions = "0.50.0"
# Optimizer
proguard = "7.3.2"
@@ -92,16 +86,6 @@ coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", ve
serialization-json-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "json" }
serialization-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "json" }
# Compose
compose-animation = { module = "androidx.compose.animation:animation", version.ref = "composeAndroidAnimation" }
compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "composeAndroidFoundation" }
compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "composeAndroidRuntime" }
compose-ui-core = { module = "androidx.compose.ui:ui", version.ref = "composeAndroidUI" }
compose-ui-util = { module = "androidx.compose.ui:ui-util", version.ref = "composeAndroidUI" }
compose-ui-text = { module = "androidx.compose.ui:ui-text", version.ref = "composeAndroidUI" }
compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "composeAndroidUI" }
compose-material-core = { module = "androidx.compose.material:material", version.ref = "composeAndroidMaterial" }
compose-material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "composeAndroidMaterial" }
# Compose UI
voyager-core = { module = "cafe.adriel.voyager:voyager-core", version.ref = "voyager" }
voyager-navigation = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
@@ -168,7 +152,7 @@ aboutLibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref
aboutLibraries-ui = { module = "com.mikepenz:aboutlibraries-compose", version.ref = "aboutLibraries" }
dateTime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "dateTime" }
immutableCollections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "immutableCollections" }
kds = { module = "com.soywiz.korlibs.kds:kds", version.ref = "kds" }
korge-foundation = { module = "com.soywiz.korge:korge-foundation", version.ref = "korge" }
# Localization
moko-core = { module = "dev.icerock.moko:resources", version.ref = "moko" }
@@ -177,6 +161,9 @@ moko-compose = { module = "dev.icerock.moko:resources-compose", version.ref = "m
# Optimizer
proguard = { module = "com.guardsquare:proguard-gradle", version.ref = "proguard" }
# Gradle
gradle-download-task = { module = "de.undercouch:gradle-download-task", version.ref = "gradleDownloadTask" }
[plugins]
# Kotlin
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
@@ -195,7 +182,7 @@ compose = { id = "org.jetbrains.compose", version.ref = "composeGradle" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
# Network
ktorfit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktorfitCompiler" }
ktorfit = { id = "de.jensklingenberg.ktorfit", version.ref = "ktorfit" }
# Localization
moko-gradle = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" }
@@ -215,11 +202,4 @@ aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "abo
[bundles]
compose-android = [
"compose-animation",
"compose-foundation",
"compose-runtime",
"compose-ui-core",
"compose-ui-util",
"compose-material-core",
"compose-material-icons"
]

Binary file not shown.

View File

@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

5
gradlew vendored
View File

@@ -130,10 +130,13 @@ 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.
if ! command -v java >/dev/null 2>&1
then
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
fi
# Increase the maximum file descriptors if we can.

View File

@@ -6,7 +6,7 @@ plugins {
}
kotlin {
android {
androidTarget {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
@@ -31,6 +31,8 @@ kotlin {
iosArm64(configure = configuration)
iosSimulatorArm64(configure = configuration)
applyDefaultHierarchyTemplate()
sourceSets {
val commonMain by getting {
dependencies {
@@ -44,6 +46,14 @@ kotlin {
implementation(kotlin("test-annotations-common"))
}
}
getByName("desktopMain") {
dependsOn(commonMain)
}
getByName("androidMain") {
dependsOn(commonMain)
}
}
}
@@ -65,7 +75,7 @@ android {
}
sourceSets.getByName("main") {
assets.srcDir(File(buildDir, "generated/moko/androidMain/assets"))
res.srcDir(File(buildDir, "generated/moko/androidMain/res"))
assets.srcDir(File(layout.buildDirectory.asFile.get(), "generated/moko/androidMain/assets"))
res.srcDir(File(layout.buildDirectory.asFile.get(), "generated/moko/androidMain/res"))
}
}

View File

@@ -1,9 +1,5 @@
import org.jetbrains.compose.compose
import org.jetbrains.compose.experimental.uikit.tasks.ExperimentalPackComposeApplicationForXCodeTask
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import java.io.File
import kotlin.reflect.full.declaredMemberProperties
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
@@ -17,28 +13,28 @@ plugins {
kotlin {
val configuration: KotlinNativeTarget.() -> Unit = {
binaries {
executable {
entryPoint = "ca.gosyer.jui.ios.main"
freeCompilerArgs = freeCompilerArgs + listOf(
"-linker-option", "-framework", "-linker-option", "Metal",
"-linker-option", "-framework", "-linker-option", "CoreText",
"-linker-option", "-framework", "-linker-option", "CoreGraphics"
)
// TODO: the current compose binary surprises LLVM, so disable checks for now.
freeCompilerArgs = freeCompilerArgs + "-Xdisable-phases=VerifyBitcode"
}
binaries.framework {
baseName = "ios"
isStatic = true
}
}
iosX64("uikitX64", configuration)
iosArm64("uikitArm64", configuration)
iosSimulatorArm64("uikitSimulatorArm64", configuration)
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
applyHierarchyTemplate {
common {
group("uikit") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
sourceSets {
val commonMain by getting
val commonTest by getting
val uikitMain by creating {
dependsOn(commonMain)
val uikitMain by getting {
dependencies {
implementation(projects.core)
implementation(projects.i18n)
@@ -100,7 +96,7 @@ kotlin {
// Utility
implementation(libs.dateTime)
implementation(libs.immutableCollections)
implementation(libs.kds)
implementation(libs.korge.foundation)
// Localization
implementation(libs.moko.core)
@@ -112,30 +108,7 @@ kotlin {
testImplementation(libs.coroutines.test)*/
}
}
val uikitTest by creating {
dependsOn(commonTest)
}
listOf(
"uikitX64",
"uikitArm64",
"uikitSimulatorArm64",
).forEach {
getByName(it + "Main").dependsOn(uikitMain)
getByName(it + "Test").dependsOn(uikitTest)
}
}
}
compose.experimental {
uikit.application {
bundleIdPrefix = "ca.gosyer.jui.app.ios"
projectName = "Tachidesk-JUI"
// ./gradlew :app:ios:iosDeployIPhone13Debug
deployConfigurations {
simulator("IPhone13") {
device = org.jetbrains.compose.experimental.dsl.IOSDevices.IPHONE_13
}
val uikitTest by getting {
}
}
}
@@ -151,92 +124,6 @@ dependencies {
}
}
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
// TODO: the current compose binary surprises LLVM, so disable checks for now.
freeCompilerArgs = freeCompilerArgs + "-Xdisable-phases=VerifyBitcode"
}
}
}
buildkonfig {
packageName = "ca.gosyer.jui.ios.build"
}
// todo: Remove when resolved: https://github.com/icerockdev/moko-resources/issues/372
// copy .bundle from all .klib to .kexe
tasks.withType<KotlinNativeLink>()
.configureEach {
val linkTask: KotlinNativeLink = this
val outputDir: File = this.outputFile.get().parentFile
@Suppress("ObjectLiteralToLambda") // lambda broke up-to-date
val action = object : Action<Task> {
override fun execute(t: Task) {
(linkTask.libraries + linkTask.sources)
.filter { library -> library.extension == "klib" }
.filter(File::exists)
.forEach { inputFile ->
val klibKonan = org.jetbrains.kotlin.konan.file.File(inputFile.path)
val klib = org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl(
klib = klibKonan,
component = "default"
)
val layout = klib.extractingToTemp
// extracting bundles
layout
.resourcesDir
.absolutePath
.let(::File)
.listFiles { file: File -> file.extension == "bundle" }
// copying bundles to app
?.forEach {
logger.info("${it.absolutePath} copying to $outputDir")
it.copyRecursively(
target = File(outputDir, it.name),
overwrite = true
)
}
}
}
}
doLast(action)
}
// copy .bundle from .kexe to .app
tasks.withType<ExperimentalPackComposeApplicationForXCodeTask>()
.configureEach {
val packTask: ExperimentalPackComposeApplicationForXCodeTask = this
val kclass = ExperimentalPackComposeApplicationForXCodeTask::class
val kotlinBinaryField =
kclass.declaredMemberProperties.single { it.name == "kotlinBinary" }
val destinationDirField =
kclass.declaredMemberProperties.single { it.name == "destinationDir" }
val executablePathField =
kclass.declaredMemberProperties.single { it.name == "executablePath" }
@Suppress("ObjectLiteralToLambda") // lambda broke up-to-date
val action = object : Action<Task> {
override fun execute(t: Task) {
val kotlinBinary: RegularFile =
(kotlinBinaryField.get(packTask) as RegularFileProperty).get()
val destinationDir: Directory =
(destinationDirField.get(packTask) as DirectoryProperty).get()
val executablePath: String =
(executablePathField.get(packTask) as Provider<String>).get()
val outputDir: File = File(destinationDir.asFile, executablePath).parentFile
val bundleSearchDir: File = kotlinBinary.asFile.parentFile
bundleSearchDir
.listFiles { file: File -> file.extension == "bundle" }
?.forEach { file ->
file.copyRecursively(File(outputDir, file.name), true)
}
}
}
doLast(action)
}

View File

@@ -11,7 +11,7 @@ plugins {
}
kotlin {
android {
androidTarget {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
@@ -29,6 +29,21 @@ kotlin {
iosArm64()
iosSimulatorArm64()
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
applyHierarchyTemplate {
common {
group("jvm") {
withAndroidTarget()
withJvm()
}
group("ios") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
sourceSets {
all {
languageSettings {
@@ -78,33 +93,28 @@ kotlin {
}
}
val jvmMain by creating {
dependsOn(commonMain)
val jvmMain by getting {
dependencies {
api(kotlin("stdlib-jdk8"))
api(compose.desktop.currentOs)
}
}
val jvmTest by creating {
dependsOn(commonTest)
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val desktopMain by getting {
dependsOn(jvmMain)
dependencies {
api(libs.coroutines.swing)
}
}
val desktopTest by getting {
dependsOn(jvmTest)
}
val androidMain by getting {
dependsOn(jvmMain)
dependencies {
api(libs.bundles.compose.android)
api(libs.androidx.core)
@@ -114,23 +124,11 @@ kotlin {
}
}
val androidUnitTest by getting {
dependsOn(jvmTest)
}
val iosMain by creating {
dependsOn(commonMain)
val iosMain by getting {
}
val iosTest by creating {
dependsOn(commonTest)
}
listOf(
"iosX64",
"iosArm64",
"iosSimulatorArm64",
).forEach {
getByName(it + "Main").dependsOn(iosMain)
getByName(it + "Test").dependsOn(iosTest)
val iosTest by getting {
}
}
}

View File

@@ -9,20 +9,22 @@ package ca.gosyer.jui.ui.base.image
import android.os.Build
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.Bitmap
import com.seiko.imageloader.BitmapConfig
import com.seiko.imageloader.cache.disk.DiskCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryKey
import com.seiko.imageloader.component.ComponentRegistryBuilder
import com.seiko.imageloader.component.setupDefaultComponents
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.OptionsBuilder
import com.seiko.imageloader.option.androidContext
import okio.Path.Companion.toOkioPath
actual fun OptionsBuilder.configure(contextWrapper: ContextWrapper) {
imageConfig = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
Options.ImageConfig.ARGB_8888
bitmapConfig = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
BitmapConfig.ARGB_8888
} else {
Options.ImageConfig.HARDWARE
BitmapConfig.HARDWARE
}
androidContext(contextWrapper)
}
@@ -42,5 +44,5 @@ actual fun DiskCacheBuilder.configure(
maxSizeBytes(1024 * 1024 * 150) // 150 MB
}
actual fun MemoryCacheBuilder.configure(contextWrapper: ContextWrapper) {
actual fun MemoryCacheBuilder<MemoryKey, Bitmap>.configure(contextWrapper: ContextWrapper) {
}

View File

@@ -35,10 +35,10 @@ fun getMaterialDialogProperties(
dismissOnClickOutside = dismissOnClickOutside,
securePolicy = securePolicy,
usePlatformDefaultWidth = usePlatformDefaultWidth,
position = position,
size = size,
title = title,
icon = icon,
resizable = resizable,
windowPosition = position,
windowSize = size,
windowTitle = title,
windowIcon = icon,
windowIsResizable = resizable,
)
}

View File

@@ -13,9 +13,11 @@ import ca.gosyer.jui.domain.server.service.ServerPreferences
import ca.gosyer.jui.domain.source.model.Source
import ca.gosyer.jui.ui.base.ImageCache
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.Bitmap
import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.cache.disk.DiskCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryKey
import com.seiko.imageloader.component.ComponentRegistryBuilder
import com.seiko.imageloader.component.fetcher.MokoResourceFetcher
import com.seiko.imageloader.component.keyer.Keyer
@@ -138,4 +140,4 @@ expect fun DiskCacheBuilder.configure(
cacheDir: String,
)
expect fun MemoryCacheBuilder.configure(contextWrapper: ContextWrapper)
expect fun MemoryCacheBuilder<MemoryKey, Bitmap>.configure(contextWrapper: ContextWrapper)

View File

@@ -27,7 +27,6 @@ import ca.gosyer.jui.ui.base.state.getStateFlow
import ca.gosyer.jui.ui.util.lang.CollatorComparator
import ca.gosyer.jui.uicore.vm.ContextWrapper
import ca.gosyer.jui.uicore.vm.ViewModel
import cafe.adriel.voyager.core.model.coroutineScope
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -198,7 +197,7 @@ class LibraryScreenViewModel
log.warn(it) { "Failed to get manga list from category ${category.name}" }
library.mangaMap.setError(category.id, it)
}
.launchIn(coroutineScope)
.launchIn(scope)
}
}
.catch {

View File

@@ -37,7 +37,7 @@ fun LibraryPager(
) {
if (categories.isEmpty()) return
HorizontalPager(categories.size, state = pagerState) {
HorizontalPager(state = pagerState) {
when (val library = getLibraryForPage(categories[it].id).value) {
CategoryState.Loading -> LoadingScreen()
is CategoryState.Failed -> ErrorScreen(library.e.message)

View File

@@ -12,7 +12,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
@@ -90,7 +89,9 @@ fun LibraryScreenContent(
}
BoxWithConstraints {
val pagerState = rememberPagerState(selectedCategoryIndex)
val pagerState = rememberPagerState(selectedCategoryIndex) {
(libraryState as? LibraryState.Loaded)?.categories?.size ?: 1
}
LaunchedEffect(pagerState.isScrollInProgress to pagerState.currentPage) {
if (!pagerState.isScrollInProgress && pagerState.currentPage != selectedCategoryIndex) {
onPageChanged(pagerState.currentPage)
@@ -240,11 +241,7 @@ fun WideLibraryScreenContent(
if (showingMenu) {
Box(
Modifier.fillMaxSize().pointerInput(Unit) {
forEachGesture {
detectTapGestures {
setShowingMenu(false)
}
}
detectTapGestures(onTap = { setShowingMenu(false) })
},
)
}
@@ -295,7 +292,7 @@ fun ThinLibraryScreenContent(
) {
val bottomSheetState = rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden,
confirmStateChange = {
confirmValueChange = {
when (it) {
ModalBottomSheetValue.Hidden -> setShowingSheet(false)
ModalBottomSheetValue.Expanded,

View File

@@ -49,7 +49,7 @@ fun LibrarySheet(
librarySort: @Composable () -> Unit,
libraryDisplay: @Composable () -> Unit,
) {
val pagerState = rememberPagerState()
val pagerState = rememberPagerState { LibrarySheetTabs.values().size }
val selectedPage = pagerState.currentPage
val scope = rememberCoroutineScope()
Column(Modifier.fillMaxSize()) {
@@ -72,7 +72,6 @@ fun LibrarySheet(
}
}
HorizontalPager(
pageCount = LibrarySheetTabs.values().size,
state = pagerState,
verticalAlignment = Alignment.Top,
) {

View File

@@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumedWindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
@@ -110,7 +110,11 @@ fun WideMainMenu(
}
withDisplayController(controller) {
val insets = WindowInsets.navigationBars.only(WindowInsetsSides.Start)
MainWindow(navigator, Modifier.padding(start = startPadding).windowInsetsPadding(insets).consumedWindowInsets(insets))
MainWindow(navigator,
Modifier.padding(start = startPadding)
.windowInsetsPadding(insets)
.consumeWindowInsets(insets)
)
}
}
}

View File

@@ -35,6 +35,7 @@ import ca.gosyer.jui.uicore.insets.statusBars
import ca.gosyer.jui.uicore.resources.stringResource
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.ui.compose.Libraries
import com.mikepenz.aboutlibraries.ui.compose.util.StableLibrary
import kotlinx.collections.immutable.toImmutableList
@Composable
@@ -58,10 +59,10 @@ fun LicensesContent() {
val state = rememberLazyListState()
val uriHandler = LocalUriHandler.current
Libraries(
libraries = remember(libs) { libs.libraries.toImmutableList() },
libraries = remember(libs) { libs.libraries.map { StableLibrary(it) }.toImmutableList() },
lazyListState = state,
onLibraryClick = {
it.website?.let(uriHandler::openUri)
it.library.website?.let(uriHandler::openUri)
},
contentPadding = WindowInsets.bottomNav.add(
WindowInsets.navigationBars.only(

View File

@@ -33,7 +33,6 @@ import ca.gosyer.jui.ui.base.chapter.ChapterDownloadState
import ca.gosyer.jui.ui.base.model.StableHolder
import ca.gosyer.jui.uicore.vm.ContextWrapper
import ca.gosyer.jui.uicore.vm.ViewModel
import cafe.adriel.voyager.core.model.coroutineScope
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -94,7 +93,7 @@ class MangaScreenViewModel
private val loadingManga = MutableStateFlow(true)
private val loadingChapters = MutableStateFlow(true)
val isLoading = combine(loadingManga, loadingChapters) { a, b -> a || b }
.stateIn(coroutineScope, SharingStarted.Eagerly, true)
.stateIn(scope, SharingStarted.Eagerly, true)
val categories = getCategories.asFlow(true)
.map { it.toImmutableList() }

View File

@@ -6,8 +6,8 @@
package ca.gosyer.jui.ui.reader
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory
import ca.gosyer.jui.ui.reader.loader.PagesState
import ca.gosyer.jui.ui.reader.loader.TachideskPageLoader
@@ -23,7 +23,7 @@ import org.lighthousegames.logging.logging
class ChapterLoader(
private val readerPreferences: ReaderPreferences,
private val getChapterPage: GetChapterPage,
private val http: Http,
private val chapterCache: DiskCache,
private val bitmapDecoderFactory: BitmapDecoderFactory,
) {
@@ -34,7 +34,7 @@ class ChapterLoader(
chapter.state = ReaderChapter.State.Loading
log.debug { "Loading pages for ${chapter.chapter.name}" }
val loader = TachideskPageLoader(chapter, readerPreferences, getChapterPage, chapterCache, bitmapDecoderFactory)
val loader = TachideskPageLoader(chapter, readerPreferences, http, chapterCache, bitmapDecoderFactory)
val pages = loader.getPages()

View File

@@ -22,6 +22,7 @@ import ca.gosyer.jui.domain.manga.model.MangaMeta
import ca.gosyer.jui.domain.reader.ReaderModeWatch
import ca.gosyer.jui.domain.reader.model.Direction
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.ui.base.ChapterCache
import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory
import ca.gosyer.jui.ui.base.model.StableHolder
@@ -82,6 +83,7 @@ class ReaderMenuViewModel
private val updateMangaMeta: UpdateMangaMeta,
private val updateChapterMeta: UpdateChapterMeta,
private val chapterCache: ChapterCache,
private val http: Http,
contextWrapper: ContextWrapper,
@Assisted private val params: Params,
) : ViewModel(contextWrapper) {
@@ -152,7 +154,7 @@ class ReaderMenuViewModel
private val loader = ChapterLoader(
readerPreferences = readerPreferences,
getChapterPage = getChapterPage,
http = http,
chapterCache = chapterCache,
bitmapDecoderFactory = BitmapDecoderFactory(contextWrapper),
)

View File

@@ -13,6 +13,7 @@ import ca.gosyer.jui.core.lang.PriorityChannel
import ca.gosyer.jui.core.lang.throwIfCancellation
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.ui.base.image.BitmapDecoderFactory
import ca.gosyer.jui.ui.base.model.StableHolder
import ca.gosyer.jui.ui.reader.model.ReaderChapter
@@ -23,10 +24,10 @@ import com.seiko.imageloader.asImageBitmap
import com.seiko.imageloader.cache.disk.DiskCache
import com.seiko.imageloader.component.decoder.DecodeResult
import com.seiko.imageloader.model.DataSource
import com.seiko.imageloader.model.ImageRequest
import com.seiko.imageloader.model.ImageResult
import com.seiko.imageloader.option.Options
import io.ktor.client.plugins.onDownload
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsChannel
import kotlinx.coroutines.CoroutineScope
@@ -39,6 +40,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
@@ -53,7 +55,7 @@ import org.lighthousegames.logging.logging
class TachideskPageLoader(
val chapter: ReaderChapter,
readerPreferences: ReaderPreferences,
private val getChapterPage: GetChapterPage,
private val http: Http,
private val chapterCache: DiskCache,
private val bitmapDecoderFactory: BitmapDecoderFactory,
) : PageLoader() {
@@ -109,11 +111,15 @@ class TachideskPageLoader(
private suspend fun fetchImage(page: ReaderPage) {
log.debug { "Loading page ${page.index}" }
getChapterPage.asFlow(chapter.chapter, page.index) {
flow {
val response = http.get("api/v1/manga/${chapter.chapter.mangaId}/chapter/${chapter.chapter.index}/page/${page.index}") {
onDownload { bytesSentTotal, contentLength ->
page.progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
}
}
emit(response)
}
.onEach {
putImageInCache(it, page)
page.bitmap.value = StableHolder { getImageFromCache(page) }
@@ -134,7 +140,7 @@ class TachideskPageLoader(
response: HttpResponse,
page: ReaderPage,
) {
val editor = chapterCache.edit(page.cacheKey)
val editor = chapterCache.openEditor(page.cacheKey)
?: throw Exception("Couldn't open cache")
try {
FileSystem.SYSTEM.write(editor.data) {
@@ -150,18 +156,17 @@ class TachideskPageLoader(
}
private suspend fun getImageFromCache(page: ReaderPage): ReaderPage.ImageDecodeState {
return chapterCache[page.cacheKey]?.use {
return chapterCache.openSnapshot(page.cacheKey)?.use {
it.source().use { source ->
val decoder = bitmapDecoderFactory.create(
ImageResult.Source(
ImageRequest(Any()),
ImageResult.OfSource(
source,
DataSource.Engine,
),
Options(),
)
if (decoder != null) {
runCatching { decoder.decode() as DecodeResult.Bitmap }
runCatching { decoder.decode() as DecodeResult.OfBitmap }
.mapCatching {
ReaderPage.ImageDecodeState.Success(
it.bitmap.asImageBitmap().also {

View File

@@ -86,6 +86,7 @@ class SettingsServerScreen : Screen {
expect class SettingsServerHostViewModel : ViewModel
@Composable
expect fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit
class SettingsServerViewModel

View File

@@ -12,7 +12,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.WindowInsets
@@ -228,11 +227,7 @@ private fun SourceWideScreenContent(
if (showingFilters && !isLatest) {
Box(
Modifier.fillMaxSize().pointerInput(loading) {
forEachGesture {
detectTapGestures {
setShowingFilters(false)
}
}
detectTapGestures(onTap = { setShowingFilters(false) })
},
)
}
@@ -287,7 +282,7 @@ private fun SourceThinScreenContent(
) {
val bottomSheetState = rememberModalBottomSheetState(
ModalBottomSheetValue.Hidden,
confirmStateChange = {
confirmValueChange = {
when (it) {
ModalBottomSheetValue.Hidden -> setShowingFilters(false)
ModalBottomSheetValue.Expanded,
@@ -364,11 +359,7 @@ private fun SourceThinScreenContent(
if (showingFilters && !isLatest) {
Box(
Modifier.fillMaxSize().pointerInput(loading) {
forEachGesture {
detectTapGestures {
setShowingFilters(false)
}
}
detectTapGestures(onTap = { setShowingFilters(false) })
},
)
}

View File

@@ -8,13 +8,35 @@ package ca.gosyer.jui.ui.base.components
import androidx.compose.foundation.ExperimentalFoundationApi
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.unit.DpOffset
@OptIn(ExperimentalFoundationApi::class)
actual typealias TooltipPlacement = androidx.compose.foundation.TooltipPlacement
actual typealias CursorPointImpl = androidx.compose.foundation.TooltipPlacement.CursorPoint
@OptIn(ExperimentalFoundationApi::class)
actual class CursorPointImpl actual constructor(
offset: DpOffset,
alignment: Alignment,
windowMargin: Dp,
) : TooltipPlacement by androidx.compose.foundation.TooltipPlacement.CursorPoint(
offset = offset,
alignment = alignment,
windowMargin = windowMargin
)
actual typealias ComponentRectImpl = androidx.compose.foundation.TooltipPlacement.ComponentRect
@OptIn(ExperimentalFoundationApi::class)
actual class ComponentRectImpl actual constructor(
anchor: Alignment,
alignment: Alignment,
offset: DpOffset,
) : TooltipPlacement by androidx.compose.foundation.TooltipPlacement.ComponentRect(
anchor = anchor,
alignment = alignment,
offset = offset
)
@OptIn(ExperimentalFoundationApi::class)
@Composable

View File

@@ -9,8 +9,10 @@ package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.core.io.userDataDir
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.Bitmap
import com.seiko.imageloader.cache.disk.DiskCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryKey
import com.seiko.imageloader.component.ComponentRegistryBuilder
import com.seiko.imageloader.component.setupDefaultComponents
import com.seiko.imageloader.option.OptionsBuilder
@@ -33,5 +35,5 @@ actual fun DiskCacheBuilder.configure(
maxSizeBytes(1024 * 1024 * 150) // 150 MB
}
actual fun MemoryCacheBuilder.configure(contextWrapper: ContextWrapper) {
actual fun MemoryCacheBuilder<MemoryKey, Bitmap>.configure(contextWrapper: ContextWrapper) {
}

View File

@@ -8,8 +8,10 @@ package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.Bitmap
import com.seiko.imageloader.cache.disk.DiskCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryKey
import com.seiko.imageloader.cache.memory.maxSizePercent
import com.seiko.imageloader.component.ComponentRegistryBuilder
import com.seiko.imageloader.component.setupDefaultComponents
@@ -47,6 +49,6 @@ private fun getCacheDir(): String {
)!!.path.orEmpty()
}
actual fun MemoryCacheBuilder.configure(contextWrapper: ContextWrapper) {
actual fun MemoryCacheBuilder<MemoryKey, Bitmap>.configure(contextWrapper: ContextWrapper) {
maxSizePercent(0.25)
}

View File

@@ -10,7 +10,7 @@ plugins {
}
kotlin {
android {
androidTarget {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
@@ -28,6 +28,21 @@ kotlin {
iosArm64()
iosSimulatorArm64()
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
applyHierarchyTemplate {
common {
group("jvm") {
withAndroidTarget()
withJvm()
}
group("ios") {
withIosX64()
withIosArm64()
withIosSimulatorArm64()
}
}
}
sourceSets {
all {
languageSettings {
@@ -63,29 +78,24 @@ kotlin {
}
}
val jvmMain by creating {
dependsOn(commonMain)
val jvmMain by getting {
dependencies {
api(kotlin("stdlib-jdk8"))
api(compose.desktop.currentOs)
}
}
val jvmTest by creating {
dependsOn(commonTest)
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val desktopMain by getting {
dependsOn(jvmMain)
}
val desktopTest by getting {
dependsOn(jvmTest)
}
val androidMain by getting {
dependsOn(jvmMain)
dependencies {
api(libs.bundles.compose.android)
api(libs.androidx.core)
@@ -93,23 +103,6 @@ kotlin {
}
}
val androidUnitTest by getting {
dependsOn(jvmTest)
}
val iosMain by creating {
dependsOn(commonMain)
}
val iosTest by creating {
dependsOn(commonTest)
}
listOf(
"iosX64",
"iosArm64",
"iosSimulatorArm64",
).forEach {
getByName(it + "Main").dependsOn(iosMain)
getByName(it + "Test").dependsOn(iosTest)
}
}
}

View File

@@ -35,9 +35,11 @@ import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQ
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import ca.gosyer.jui.uicore.components.LoadingScreen
import com.seiko.imageloader.ImageRequestState
import com.seiko.imageloader.model.ImageAction
import com.seiko.imageloader.model.ImageRequest
import com.seiko.imageloader.rememberAsyncImagePainter
import com.seiko.imageloader.rememberImageAction
import com.seiko.imageloader.rememberImageActionPainter
import com.seiko.imageloader.rememberImagePainter
import org.lighthousegames.logging.logging
private val log = logging()
@@ -84,32 +86,31 @@ fun ImageLoaderImage(
) {
key(data) {
val request = remember { ImageRequest(data) }
val painter = rememberAsyncImagePainter(
request,
contentScale = contentScale,
filterQuality = filterQuality,
)
if (animationSpec != null) {
val imageAction by rememberImageAction(request)
val progress = remember { mutableStateOf(-1F) }
val error = remember { mutableStateOf<Throwable?>(null) }
val state by derivedStateOf {
when (val state = painter.requestState) {
is ImageRequestState.Failure -> {
when (val action = imageAction) {
is ImageAction.Failure -> {
progress.value = 0.0F
error.value = state.error
error.value = action.error
ImageLoaderImageState.Failure
}
is ImageRequestState.Loading -> {
is ImageAction.Loading -> {
progress.value = 0.0F
ImageLoaderImageState.Loading
}
ImageRequestState.Success -> {
is ImageAction.Success -> {
progress.value = 1.0F
ImageLoaderImageState.Success
}
else -> {
ImageLoaderImageState.Loading
}
}
}
if (animationSpec != null) {
Crossfade(state, animationSpec = animationSpec, modifier = modifier) {
Box(Modifier.fillMaxSize(), contentAlignment) {
when (it) {
@@ -117,7 +118,10 @@ fun ImageLoaderImage(
onLoading(progress.value)
}
ImageLoaderImageState.Success -> Image(
painter = painter,
painter = rememberImageActionPainter(
imageAction,
filterQuality = filterQuality
),
contentDescription = contentDescription,
modifier = Modifier.fillMaxSize(),
alignment = alignment,
@@ -136,7 +140,7 @@ fun ImageLoaderImage(
} else {
Box(modifier, contentAlignment) {
Image(
painter = painter,
painter = rememberImagePainter(request, filterQuality = filterQuality),
contentDescription = contentDescription,
modifier = Modifier.fillMaxSize(),
alignment = alignment,

View File

@@ -6,9 +6,12 @@
package ca.gosyer.jui.uicore.pager
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout
import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion.CenterToCenter
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -38,6 +41,8 @@ import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMaxBy
import androidx.compose.ui.util.fastSumBy
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlin.math.abs
import kotlin.math.sign
@Composable
fun VerticalPager(
@@ -229,9 +234,7 @@ class PagerState(
// https://android.googlesource.com/platform/frameworks/support/+/refs/changes/78/2160778/35/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
private fun lazyListSnapLayoutInfoProvider(
lazyListState: LazyListState,
positionInLayout: (layoutSize: Float, itemSize: Float) -> Float = { layoutSize, itemSize ->
layoutSize / 2f - itemSize / 2f
},
positionInLayout: SnapPositionInLayout = CenterToCenter
) = object : SnapLayoutInfoProvider {
private val layoutInfo: LazyListLayoutInfo
get() = lazyListState.layoutInfo
@@ -239,13 +242,21 @@ private fun lazyListSnapLayoutInfoProvider(
// Single page snapping is the default
override fun Density.calculateApproachOffset(initialVelocity: Float): Float = 0f
override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
override fun Density.calculateSnappingOffset(currentVelocity: Float): Float {
var lowerBoundOffset = Float.NEGATIVE_INFINITY
var upperBoundOffset = Float.POSITIVE_INFINITY
layoutInfo.visibleItemsInfo.fastForEach { item ->
val offset =
calculateDistanceToDesiredSnapPosition(layoutInfo, item, positionInLayout)
calculateDistanceToDesiredSnapPosition(
mainAxisViewPortSize = layoutInfo.singleAxisViewportSize,
beforeContentPadding = layoutInfo.beforeContentPadding,
afterContentPadding = layoutInfo.afterContentPadding,
itemSize = item.size,
itemOffset = item.offset,
itemIndex = item.index,
snapPositionInLayout = positionInLayout
)
// Find item that is closest to the center
if (offset <= 0 && offset > lowerBoundOffset) {
@@ -258,7 +269,58 @@ private fun lazyListSnapLayoutInfoProvider(
}
}
return lowerBoundOffset.rangeTo(upperBoundOffset)
return calculateFinalOffset(
currentVelocity,
lowerBoundOffset,
upperBoundOffset
)
}
@OptIn(ExperimentalFoundationApi::class)
private fun Density.calculateDistanceToDesiredSnapPosition(
mainAxisViewPortSize: Int,
beforeContentPadding: Int,
afterContentPadding: Int,
itemSize: Int,
itemOffset: Int,
itemIndex: Int,
snapPositionInLayout: SnapPositionInLayout
): Float {
val containerSize = mainAxisViewPortSize - beforeContentPadding - afterContentPadding
val desiredDistance = with(snapPositionInLayout) {
position(containerSize, itemSize, itemIndex)
}.toFloat()
return itemOffset - desiredDistance
}
private fun calculateFinalOffset(velocity: Float, lowerBound: Float, upperBound: Float): Float {
fun Float.isValidDistance(): Boolean {
return this != Float.POSITIVE_INFINITY && this != Float.NEGATIVE_INFINITY
}
val finalDistance = when (sign(velocity)) {
0f -> {
if (abs(upperBound) <= abs(lowerBound)) {
upperBound
} else {
lowerBound
}
}
1f -> upperBound
-1f -> lowerBound
else -> 0f
}
return if (finalDistance.isValidDistance()) {
finalDistance
} else {
0f
}
}
override fun Density.calculateSnapStepSize(): Float =
@@ -277,20 +339,5 @@ private fun rememberLazyListSnapFlingBehavior(lazyListState: LazyListState): Fli
return rememberSnapFlingBehavior(snappingLayout)
}
private fun calculateDistanceToDesiredSnapPosition(
layoutInfo: LazyListLayoutInfo,
item: LazyListItemInfo,
positionInLayout: (layoutSize: Float, itemSize: Float) -> Float,
): Float {
val containerSize =
with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
val desiredDistance =
positionInLayout(containerSize.toFloat(), item.size.toFloat())
val itemCurrentPosition = item.offset
return itemCurrentPosition - desiredDistance
}
private val LazyListLayoutInfo.singleAxisViewportSize: Int
get() = if (orientation == Orientation.Vertical) viewportSize.height else viewportSize.width

View File

@@ -10,7 +10,7 @@ import ca.gosyer.jui.core.lang.launchUI
import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.uicore.prefs.PreferenceMutableStateFlow
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -20,7 +20,7 @@ import kotlinx.coroutines.launch
abstract class ViewModel(private val contextWrapper: ContextWrapper) : ScreenModel {
protected open val scope: CoroutineScope
get() = coroutineScope
get() = screenModelScope
fun <T> Preference<T>.asStateFlow() = PreferenceMutableStateFlow(this, scope)

View File

@@ -6,6 +6,7 @@
package ca.gosyer.jui.uicore.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.HoverInteraction
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -14,12 +15,14 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
@OptIn(ExperimentalFoundationApi::class, ExperimentalCoroutinesApi::class)
actual fun Modifier.buttonModifier(
onClick: () -> Unit,
onHintClick: () -> Unit,