mirror of
https://github.com/BetterAndroid/FlexiUI.git
synced 2025-09-09 12:04:11 +08:00
Initial commit
This commit is contained in:
1
flexiui-core/.gitignore
vendored
Normal file
1
flexiui-core/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
71
flexiui-core/build.gradle.kts
Normal file
71
flexiui-core/build.gradle.kts
Normal file
@@ -0,0 +1,71 @@
|
||||
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 = projects.flexiuiCore.name
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
jvmToolchain(17)
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings {
|
||||
optIn("org.jetbrains.compose.resources.ExperimentalResourceApi")
|
||||
}
|
||||
}
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(compose.runtime)
|
||||
implementation(compose.foundation)
|
||||
// TODO: We need to remove this and replace with "material-ripple"
|
||||
implementation(compose.material)
|
||||
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
|
||||
implementation(compose.components.resources)
|
||||
}
|
||||
}
|
||||
val androidMain by getting
|
||||
val desktopMain by getting
|
||||
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
|
||||
}
|
2
flexiui-core/src/androidMain/AndroidManifest.xml
Normal file
2
flexiui-core/src/androidMain/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest />
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/8.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
internal actual fun FlexiThemeContent(content: @Composable () -> Unit) {
|
||||
content()
|
||||
}
|
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
// TODO: Dynamic colors support
|
||||
|
||||
@Stable
|
||||
data class Colors(
|
||||
var backgroundPrimary: Color,
|
||||
var backgroundSecondary: Color,
|
||||
var foregroundPrimary: Color,
|
||||
var foregroundSecondary: Color,
|
||||
var themePrimary: Color,
|
||||
var themeSecondary: Color,
|
||||
var themeTertiary: Color,
|
||||
var textPrimary: Color,
|
||||
var textSecondary: Color,
|
||||
var isLight: Boolean
|
||||
)
|
||||
|
||||
private val DefaultLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFF5F5F5),
|
||||
backgroundSecondary = Color(0xFFEDEDED),
|
||||
foregroundPrimary = Color(0xFFFFFFFF),
|
||||
foregroundSecondary = Color(0xFFF5F5F5),
|
||||
themePrimary = Color(0xFF777777),
|
||||
themeSecondary = Color(0xA6777777),
|
||||
themeTertiary = Color(0x27777777),
|
||||
textPrimary = Color(0xFF323B42),
|
||||
textSecondary = Color(0xFF777777),
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val DefaultDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF2D2D2D),
|
||||
backgroundSecondary = Color(0xFF484848),
|
||||
foregroundPrimary = Color(0xFF474747),
|
||||
foregroundSecondary = Color(0xFF646464),
|
||||
themePrimary = Color(0xFF888888),
|
||||
themeSecondary = Color(0xA6888888),
|
||||
themeTertiary = Color(0x40888888),
|
||||
textPrimary = Color(0xFFE3E3E3),
|
||||
textSecondary = Color(0xFFBBBBBB),
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val DefaultBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF1B1B1B),
|
||||
foregroundPrimary = Color(0xFF1A1A1A),
|
||||
foregroundSecondary = Color(0xFF373737),
|
||||
themePrimary = Color(0xFF5B5B5B),
|
||||
themeSecondary = Color(0xA65B5B5B),
|
||||
themeTertiary = Color(0x455B5B5B),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val RedLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFFBEEEC),
|
||||
backgroundSecondary = Color(0xFFEDE0DE),
|
||||
foregroundPrimary = Color(0xFFFFFBFF),
|
||||
foregroundSecondary = Color(0xFFFBEEEC),
|
||||
themePrimary = Color(0xFFFF5545),
|
||||
themeSecondary = Color(0xA6FF8A7B),
|
||||
themeTertiary = Color(0xFFF9DCD8),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val PinkLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFFBEEEE),
|
||||
backgroundSecondary = Color(0xFFECE0E0),
|
||||
foregroundPrimary = Color(0xFFFFFBFF),
|
||||
foregroundSecondary = Color(0xFFFBEEEE),
|
||||
themePrimary = Color(0xFFFF4E7C),
|
||||
themeSecondary = Color(0xA6FF869D),
|
||||
themeTertiary = Color(0xFFF7DCDF),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val PurpleLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFF5EFF4),
|
||||
backgroundSecondary = Color(0xFFE6E1E6),
|
||||
foregroundPrimary = Color(0xFFFFFBFF),
|
||||
foregroundSecondary = Color(0xFFF5EFF4),
|
||||
themePrimary = Color(0xFFA476FF),
|
||||
themeSecondary = Color(0xA6BB99FF),
|
||||
themeTertiary = Color(0xFFE8DFEE),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val OrangeLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFFAEFE7),
|
||||
backgroundSecondary = Color(0xFFEBE0D9),
|
||||
foregroundPrimary = Color(0xFFFFFBFF),
|
||||
foregroundSecondary = Color(0xFFFAEFE7),
|
||||
themePrimary = Color(0xFFD27C00),
|
||||
themeSecondary = Color(0xA6F89300),
|
||||
themeTertiary = Color(0xFFF5DECC),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val YellowLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFF8EFE7),
|
||||
backgroundSecondary = Color(0xFFE9E1D9),
|
||||
foregroundPrimary = Color(0xFFFFFBFF),
|
||||
foregroundSecondary = Color(0xFFF8EFE7),
|
||||
themePrimary = Color(0xFFBA8800),
|
||||
themeSecondary = Color(0xA6DCA100),
|
||||
themeTertiary = Color(0xFFF0E0CA),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val GreenLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFEFF1ED),
|
||||
backgroundSecondary = Color(0xFFE1E3DF),
|
||||
foregroundPrimary = Color(0xFFFBFDF8),
|
||||
foregroundSecondary = Color(0xFFEFF1ED),
|
||||
themePrimary = Color(0xFF5B9E7A),
|
||||
themeSecondary = Color(0xA676B993),
|
||||
themeTertiary = Color(0xFFE1E3DF),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val BlueLightColors = Colors(
|
||||
backgroundPrimary = Color(0xFFF0F0F3),
|
||||
backgroundSecondary = Color(0xFFE2E2E5),
|
||||
foregroundPrimary = Color(0xFFFCFCFF),
|
||||
foregroundSecondary = Color(0xFFF0F0F3),
|
||||
themePrimary = Color(0xFF0099DF),
|
||||
themeSecondary = Color(0xA633B4FF),
|
||||
themeTertiary = Color(0xFFDBE3ED),
|
||||
textPrimary = DefaultLightColors.textPrimary,
|
||||
textSecondary = DefaultLightColors.textSecondary,
|
||||
isLight = true
|
||||
)
|
||||
|
||||
private val RedDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF271816),
|
||||
backgroundSecondary = Color(0xFF3D2C2A),
|
||||
foregroundPrimary = Color(0xFF3D2C2A),
|
||||
foregroundSecondary = Color(0xFF554240),
|
||||
themePrimary = Color(0xFFB9856D),
|
||||
themeSecondary = Color(0xA69B6B54),
|
||||
themeTertiary = Color(0xFF554240),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val PinkDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF26181A),
|
||||
backgroundSecondary = Color(0xFF3D2C2E),
|
||||
foregroundPrimary = Color(0xFF3D2C2E),
|
||||
foregroundSecondary = Color(0xFF544244),
|
||||
themePrimary = Color(0xFFBA837B),
|
||||
themeSecondary = Color(0xA69D6962),
|
||||
themeTertiary = Color(0xFF544244),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val PurpleDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF1E1A24),
|
||||
backgroundSecondary = Color(0xFF332E3A),
|
||||
foregroundPrimary = Color(0xFF332E3A),
|
||||
foregroundSecondary = Color(0xFF494550),
|
||||
themePrimary = Color(0xFF9F88AD),
|
||||
themeSecondary = Color(0xA6846E91),
|
||||
themeTertiary = Color(0xFF494550),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val OrangeDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF25190E),
|
||||
backgroundSecondary = Color(0xFF3B2E22),
|
||||
foregroundPrimary = Color(0xFF3B2E22),
|
||||
foregroundSecondary = Color(0xFF534437),
|
||||
themePrimary = Color(0xFFAE8B5D),
|
||||
themeSecondary = Color(0xA6917045),
|
||||
themeTertiary = Color(0xFF534437),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val YellowDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF221B0D),
|
||||
backgroundSecondary = Color(0xFF382F20),
|
||||
foregroundPrimary = Color(0xFF382F20),
|
||||
foregroundSecondary = Color(0xFF4F4535),
|
||||
themePrimary = Color(0xFFA18F5C),
|
||||
themeSecondary = Color(0xA6857544),
|
||||
themeTertiary = Color(0xFF4F4535),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val GreenDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF191C1A),
|
||||
backgroundSecondary = Color(0xFF2E312E),
|
||||
foregroundPrimary = Color(0xFF2E312E),
|
||||
foregroundSecondary = Color(0xFF444844),
|
||||
themePrimary = Color(0xFF7F9687),
|
||||
themeSecondary = Color(0xA6657B6D),
|
||||
themeTertiary = Color(0xFF444844),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val BlueDarkColors = Colors(
|
||||
backgroundPrimary = Color(0xFF141C23),
|
||||
backgroundSecondary = Color(0xFF293139),
|
||||
foregroundPrimary = Color(0xFF293139),
|
||||
foregroundSecondary = Color(0xFF3F484F),
|
||||
themePrimary = Color(0xFF8091B1),
|
||||
themeSecondary = Color(0xA6657795),
|
||||
themeTertiary = Color(0xFF3F484F),
|
||||
textPrimary = DefaultDarkColors.textPrimary,
|
||||
textSecondary = DefaultDarkColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val RedBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF271816),
|
||||
foregroundSecondary = Color(0xFF3D2C2A),
|
||||
themePrimary = Color(0xFFB9856D),
|
||||
themeSecondary = Color(0xA69B6B54),
|
||||
themeTertiary = Color(0xFF3D2C2A),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val PinkBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF26181A),
|
||||
foregroundSecondary = Color(0xFF3D2C2E),
|
||||
themePrimary = Color(0xFFBA837B),
|
||||
themeSecondary = Color(0xA69D6962),
|
||||
themeTertiary = Color(0xFF3D2C2E),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val PurpleBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF1E1A24),
|
||||
foregroundSecondary = Color(0xFF332E3A),
|
||||
themePrimary = Color(0xFF9F88AD),
|
||||
themeSecondary = Color(0xA6846E91),
|
||||
themeTertiary = Color(0xFF332E3A),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val OrangeBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF25190E),
|
||||
foregroundSecondary = Color(0xFF3B2E22),
|
||||
themePrimary = Color(0xFFAE8B5D),
|
||||
themeSecondary = Color(0xA6917045),
|
||||
themeTertiary = Color(0xFF3B2E22),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val YellowBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF221B0D),
|
||||
foregroundSecondary = Color(0xFF382F20),
|
||||
themePrimary = Color(0xFFA18F5C),
|
||||
themeSecondary = Color(0xA6857544),
|
||||
themeTertiary = Color(0xFF382F20),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val GreenBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF191C1A),
|
||||
foregroundSecondary = Color(0xFF2E312E),
|
||||
themePrimary = Color(0xFF7F9687),
|
||||
themeSecondary = Color(0xA6657B6D),
|
||||
themeTertiary = Color(0xFF2E312E),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
private val BlueBlackColors = Colors(
|
||||
backgroundPrimary = Color(0xFF000000),
|
||||
backgroundSecondary = Color(0xFF000000),
|
||||
foregroundPrimary = Color(0xFF141C23),
|
||||
foregroundSecondary = Color(0xFF293139),
|
||||
themePrimary = Color(0xFF8091B1),
|
||||
themeSecondary = Color(0xA6657795),
|
||||
themeTertiary = Color(0xFF293139),
|
||||
textPrimary = DefaultBlackColors.textPrimary,
|
||||
textSecondary = DefaultBlackColors.textSecondary,
|
||||
isLight = false
|
||||
)
|
||||
|
||||
fun defaultColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) DefaultBlackColors else DefaultDarkColors
|
||||
else -> DefaultLightColors
|
||||
}
|
||||
|
||||
fun redColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) RedBlackColors else RedDarkColors
|
||||
else -> RedLightColors
|
||||
}
|
||||
|
||||
fun pinkColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) PinkBlackColors else PinkDarkColors
|
||||
else -> PinkLightColors
|
||||
}
|
||||
|
||||
fun purpleColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) PurpleBlackColors else PurpleDarkColors
|
||||
else -> PurpleLightColors
|
||||
}
|
||||
|
||||
fun orangeColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) OrangeBlackColors else OrangeDarkColors
|
||||
else -> OrangeLightColors
|
||||
}
|
||||
|
||||
fun yellowColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) YellowBlackColors else YellowDarkColors
|
||||
else -> YellowLightColors
|
||||
}
|
||||
|
||||
fun greenColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) GreenBlackColors else GreenDarkColors
|
||||
else -> GreenLightColors
|
||||
}
|
||||
|
||||
fun blueColors(darkMode: Boolean = false, blackDarkMode: Boolean = false) = when {
|
||||
darkMode -> if (blackDarkMode) BlueBlackColors else BlueDarkColors
|
||||
else -> BlueLightColors
|
||||
}
|
||||
|
||||
internal val LocalColors = staticCompositionLocalOf { DefaultLightColors }
|
||||
|
||||
@Stable
|
||||
val Color.Companion.Translucent get() = Color(0x80000000)
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
|
||||
@Composable
|
||||
fun FlexiTheme(
|
||||
colors: Colors = FlexiTheme.colors,
|
||||
shapes: Shapes = FlexiTheme.shapes,
|
||||
typography: Typography = FlexiTheme.typography,
|
||||
sizes: Sizes = FlexiTheme.sizes,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalColors provides colors,
|
||||
LocalShapes provides shapes,
|
||||
LocalTypography provides typography,
|
||||
LocalSizes provides sizes
|
||||
) { FlexiThemeContent(content) }
|
||||
}
|
||||
|
||||
object FlexiTheme {
|
||||
val colors: Colors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalColors.current
|
||||
val shapes: Shapes
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalShapes.current
|
||||
val typography: Typography
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalTypography.current
|
||||
val sizes: Sizes
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalSizes.current
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal expect fun FlexiThemeContent(content: @Composable () -> Unit)
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.foundation.shape.CornerBasedShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Immutable
|
||||
data class Shapes(
|
||||
val primary: CornerBasedShape,
|
||||
val secondary: CornerBasedShape,
|
||||
val tertiary: CornerBasedShape
|
||||
)
|
||||
|
||||
internal val LocalShapes = staticCompositionLocalOf { DefaultShapes }
|
||||
|
||||
internal val DefaultShapes = Shapes(
|
||||
primary = RoundedCornerShape(15.dp),
|
||||
secondary = RoundedCornerShape(10.dp),
|
||||
tertiary = RoundedCornerShape(50.dp)
|
||||
)
|
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
// TODO: Some sizes will modify in the future
|
||||
|
||||
@Immutable
|
||||
data class Sizes(
|
||||
val spacingPrimary: Dp,
|
||||
val spacingSecondary: Dp,
|
||||
val spacingTertiary: Dp,
|
||||
val iconSizePrimary: Dp,
|
||||
val iconSizeSecondary: Dp,
|
||||
val iconSizeTertiary: Dp,
|
||||
val zoomSizePrimary: Dp,
|
||||
val zoomSizeSecondary: Dp,
|
||||
val zoomSizeTertiary: Dp,
|
||||
val borderSizePrimary: Dp,
|
||||
val borderSizeSecondary: Dp,
|
||||
val borderSizeTertiary: Dp
|
||||
)
|
||||
|
||||
internal val LocalSizes = staticCompositionLocalOf { DefaultSizes }
|
||||
|
||||
internal val DefaultSizes = Sizes(
|
||||
spacingPrimary = 15.dp,
|
||||
spacingSecondary = 10.dp,
|
||||
spacingTertiary = 5.dp,
|
||||
iconSizePrimary = 27.dp,
|
||||
iconSizeSecondary = 25.dp,
|
||||
iconSizeTertiary = 20.dp,
|
||||
zoomSizePrimary = 15.dp,
|
||||
zoomSizeSecondary = 10.dp,
|
||||
zoomSizeTertiary = 8.dp,
|
||||
borderSizePrimary = 2.dp,
|
||||
borderSizeSecondary = 1.dp,
|
||||
borderSizeTertiary = 0.dp
|
||||
)
|
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.em
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Immutable
|
||||
data class Typography(
|
||||
val titlePrimary: TextStyle,
|
||||
val titleSecondary: TextStyle,
|
||||
val subtitle: TextStyle,
|
||||
val primary: TextStyle,
|
||||
val secondary: TextStyle
|
||||
)
|
||||
|
||||
internal val LocalTypography = staticCompositionLocalOf { DefaultTypography }
|
||||
|
||||
internal val DefaultTypography = Typography(
|
||||
titlePrimary = TextStyle(
|
||||
fontSize = 25.sp,
|
||||
lineHeight = 1.5.em
|
||||
),
|
||||
titleSecondary = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
lineHeight = 1.2.em
|
||||
),
|
||||
subtitle = TextStyle(
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 1.em
|
||||
),
|
||||
primary = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 1.2.em
|
||||
),
|
||||
secondary = TextStyle(
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 1.em
|
||||
)
|
||||
)
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.highcapable.flexiui.DefaultShapes
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.utils.borderOrNot
|
||||
import com.highcapable.flexiui.utils.orElse
|
||||
|
||||
@Composable
|
||||
fun AreaBox(
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = AreaBox.padding,
|
||||
topPadding: Dp = Dp.Unspecified,
|
||||
startPadding: Dp = Dp.Unspecified,
|
||||
bottomPadding: Dp = Dp.Unspecified,
|
||||
endPadding: Dp = Dp.Unspecified,
|
||||
shape: Shape = AreaBox.shape,
|
||||
border: BorderStroke = AreaBox.border,
|
||||
color: Color = AreaBox.color,
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
propagateMinConstraints: Boolean = false,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalInAreaBox provides true,
|
||||
LocalAreaBoxShape provides shape
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.box(padding, topPadding, startPadding, bottomPadding, endPadding, shape, border, color),
|
||||
contentAlignment = contentAlignment,
|
||||
propagateMinConstraints = propagateMinConstraints,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AreaRow(
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = AreaBox.padding,
|
||||
topPadding: Dp = Dp.Unspecified,
|
||||
startPadding: Dp = Dp.Unspecified,
|
||||
bottomPadding: Dp = Dp.Unspecified,
|
||||
endPadding: Dp = Dp.Unspecified,
|
||||
shape: Shape = AreaBox.shape,
|
||||
border: BorderStroke = AreaBox.border,
|
||||
color: Color = AreaBox.color,
|
||||
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
|
||||
verticalAlignment: Alignment.Vertical = Alignment.Top,
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalInAreaBox provides true,
|
||||
LocalAreaBoxShape provides shape
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.box(padding, topPadding, startPadding, bottomPadding, endPadding, shape, border, color),
|
||||
horizontalArrangement = horizontalArrangement,
|
||||
verticalAlignment = verticalAlignment,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AreaColumn(
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = AreaBox.padding,
|
||||
topPadding: Dp = Dp.Unspecified,
|
||||
startPadding: Dp = Dp.Unspecified,
|
||||
bottomPadding: Dp = Dp.Unspecified,
|
||||
endPadding: Dp = Dp.Unspecified,
|
||||
shape: Shape = AreaBox.shape,
|
||||
border: BorderStroke = AreaBox.border,
|
||||
color: Color = AreaBox.color,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalInAreaBox provides true,
|
||||
LocalAreaBoxShape provides shape
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.box(padding, topPadding, startPadding, bottomPadding, endPadding, shape, border, color),
|
||||
verticalArrangement = verticalArrangement,
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.box(
|
||||
padding: Dp,
|
||||
topPadding: Dp,
|
||||
startPadding: Dp,
|
||||
bottomPadding: Dp,
|
||||
endPadding: Dp,
|
||||
shape: Shape,
|
||||
border: BorderStroke,
|
||||
color: Color
|
||||
) = clip(shape = shape)
|
||||
.background(color = color, shape = shape)
|
||||
.borderOrNot(border, shape)
|
||||
.padding(
|
||||
top = topPadding.orElse() ?: padding,
|
||||
start = startPadding.orElse() ?: padding,
|
||||
bottom = bottomPadding.orElse() ?: padding,
|
||||
end = endPadding.orElse() ?: padding
|
||||
)
|
||||
|
||||
object AreaBox {
|
||||
val padding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAreaBoxPadding()
|
||||
val shape: Shape
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAreaBoxShape()
|
||||
val border: BorderStroke
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAreaBoxBorder()
|
||||
val color: Color
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultAreaBoxColor()
|
||||
}
|
||||
|
||||
internal val LocalInAreaBox = compositionLocalOf { false }
|
||||
|
||||
internal val LocalAreaBoxShape = compositionLocalOf { DefaultAreaBoxShape }
|
||||
|
||||
internal val DefaultAreaBoxShape: Shape = DefaultShapes.primary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxPadding() = LocalSizes.current.spacingPrimary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxShape() = LocalShapes.current.primary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultAreaBoxColor() = LocalColors.current.foregroundPrimary
|
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.utils.borderOrNot
|
||||
import com.highcapable.flexiui.utils.orElse
|
||||
|
||||
@Immutable
|
||||
data class ButtonColors(
|
||||
val fillColor: Color,
|
||||
val contentColor: Color,
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun Button(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = Dp.Unspecified,
|
||||
topPadding: Dp = Button.topPadding,
|
||||
startPadding: Dp = Button.startPadding,
|
||||
bottomPadding: Dp = Button.bottomPadding,
|
||||
endPadding: Dp = Button.endPadding,
|
||||
shape: Shape = Button.shape,
|
||||
border: BorderStroke = Button.border,
|
||||
colors: ButtonColors = Button.colors,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
header: @Composable () -> Unit = {},
|
||||
footer: @Composable () -> Unit = {},
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
var sModifier = modifier.clip(shape = shape)
|
||||
sModifier = if (enabled) sModifier.clickable(
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
role = Role.Button,
|
||||
indication = rememberRipple(color = colors.fillColor),
|
||||
interactionSource = interactionSource
|
||||
) else sModifier.alpha(0.5f)
|
||||
sModifier = sModifier.background(color = colors.backgroundColor, shape = shape)
|
||||
sModifier = sModifier.borderOrNot(border = border, shape = shape)
|
||||
val localTextStyle = LocalTextStyle.current.copy(color = colors.contentColor)
|
||||
val localProgressIndicatorColors = LocalProgressIndicatorColors.current.copy(
|
||||
foregroundColor = colors.contentColor,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
Box(modifier = sModifier) {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides localTextStyle,
|
||||
LocalProgressIndicatorColors provides localProgressIndicatorColors
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(
|
||||
top = topPadding.orElse() ?: padding,
|
||||
start = startPadding.orElse() ?: padding,
|
||||
bottom = bottomPadding.orElse() ?: padding,
|
||||
end = endPadding.orElse() ?: padding
|
||||
)
|
||||
) {
|
||||
header()
|
||||
content()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IconButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
fillColor: Color = Button.colors.fillColor,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.clickable(
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
role = Role.Button,
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(bounded = false, color = fillColor)
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) { content() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IconToggleButton(
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
fillColor: Color = Button.colors.fillColor,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.toggleable(
|
||||
value = checked,
|
||||
onValueChange = onCheckedChange,
|
||||
enabled = enabled,
|
||||
role = Role.Checkbox,
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(bounded = false, color = fillColor)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) { content() }
|
||||
}
|
||||
|
||||
object Button {
|
||||
val topPadding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defalutButtonPaddings()[0]
|
||||
val startPadding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defalutButtonPaddings()[1]
|
||||
val bottomPadding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defalutButtonPaddings()[2]
|
||||
val endPadding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defalutButtonPaddings()[3]
|
||||
val shape: Shape
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = when (LocalInAreaBox.current) {
|
||||
true -> LocalAreaBoxShape.current
|
||||
else -> defaultButtonShape()
|
||||
}
|
||||
val border: BorderStroke
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultButtonBorder()
|
||||
val colors: ButtonColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = when (LocalInAreaBox.current) {
|
||||
true -> defaultButtonInBoxColors()
|
||||
else -> defaultButtonOutBoxColors()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defalutButtonPaddings() = arrayOf(
|
||||
LocalSizes.current.spacingSecondary,
|
||||
LocalSizes.current.spacingPrimary,
|
||||
LocalSizes.current.spacingSecondary,
|
||||
LocalSizes.current.spacingPrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonBorder() = BorderStroke(LocalSizes.current.borderSizeTertiary, LocalColors.current.textPrimary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonShape() = LocalShapes.current.tertiary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonInBoxColors() = ButtonColors(
|
||||
fillColor = LocalColors.current.themeSecondary,
|
||||
contentColor = LocalColors.current.textPrimary,
|
||||
backgroundColor = LocalColors.current.foregroundSecondary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultButtonOutBoxColors() = ButtonColors(
|
||||
fillColor = LocalColors.current.foregroundSecondary,
|
||||
contentColor = Color.White,
|
||||
backgroundColor = LocalColors.current.themePrimary
|
||||
)
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/8.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.animation.core.CubicBezierEasing
|
||||
import androidx.compose.animation.core.Easing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.VectorConverter
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.animateValue
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.keyframes
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.progressSemantics
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.utils.orElse
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
@Stable
|
||||
internal interface IProgressIndicatorStyle {
|
||||
val strokeWidth: Dp
|
||||
val strokeCap: StrokeCap
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class CircularIndicatorStyle(
|
||||
override val strokeWidth: Dp,
|
||||
override val strokeCap: StrokeCap,
|
||||
val diameter: Dp,
|
||||
val rotationDuration: Int,
|
||||
val rotationsPerCycle: Int,
|
||||
val startAngleOffset: Float,
|
||||
val baseRotationAngle: Float,
|
||||
val jumpRotationAngle: Float,
|
||||
val easing: Easing
|
||||
) : IProgressIndicatorStyle
|
||||
|
||||
@Immutable
|
||||
data class LinearIndicatorStyle(
|
||||
override val strokeWidth: Dp,
|
||||
override val strokeCap: StrokeCap,
|
||||
val width: Dp,
|
||||
val animationDuration: Int,
|
||||
val firstLineHeadDuration: Int,
|
||||
val firstLineTailDuration: Int,
|
||||
val secondLineHeadDuration: Int,
|
||||
val secondLineTailDuration: Int,
|
||||
val firstLineHeadDelay: Int,
|
||||
val firstLineTailDelay: Int,
|
||||
val secondLineHeadDelay: Int,
|
||||
val secondLineTailDelay: Int,
|
||||
val firstLineHeadEasing: Easing,
|
||||
val firstLineTailEasing: Easing,
|
||||
val secondLineHeadEasing: Easing,
|
||||
val secondLineTailEasing: Easing
|
||||
) : IProgressIndicatorStyle
|
||||
|
||||
@Immutable
|
||||
data class ProgressIndicatorColors(
|
||||
val foregroundColor: Color,
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CircularProgressIndicator(
|
||||
modifier: Modifier = Modifier,
|
||||
progress: Float = -1f,
|
||||
min: Float = 0f,
|
||||
max: Float = 100f,
|
||||
indeterminate: Boolean = progress < min,
|
||||
colors: ProgressIndicatorColors = ProgressIndicator.circularColors,
|
||||
style: CircularIndicatorStyle = ProgressIndicator.circularStyle
|
||||
) {
|
||||
val stroke = with(LocalDensity.current) { Stroke(width = style.strokeWidth.toPx(), cap = style.strokeCap) }
|
||||
|
||||
@Composable
|
||||
fun Determinate() {
|
||||
val coercedProgress = progress.coerceIn(min, max)
|
||||
val normalizedProgress = (coercedProgress - min) / (max - min)
|
||||
Canvas(modifier.progressSemantics(normalizedProgress).size(style.diameter)) {
|
||||
val startAngle = 270f
|
||||
val sweep = normalizedProgress * 360f
|
||||
drawCircularIndicatorBackground(colors.backgroundColor, stroke)
|
||||
drawCircularIndicator(startAngle, sweep, colors.foregroundColor, stroke)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Indeterminate() {
|
||||
val transition = rememberInfiniteTransition()
|
||||
val currentRotation by transition.animateValue(
|
||||
initialValue = 0,
|
||||
style.rotationsPerCycle,
|
||||
Int.VectorConverter,
|
||||
infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = style.rotationDuration * style.rotationsPerCycle,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
)
|
||||
val baseRotation by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
style.baseRotationAngle,
|
||||
infiniteRepeatable(
|
||||
animation = tween(
|
||||
durationMillis = style.rotationDuration,
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
)
|
||||
val headAndTailAnimationDuration = caleHeadAndTailAnimationDuration(style.rotationDuration)
|
||||
val endAngle by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
style.jumpRotationAngle,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = headAndTailAnimationDuration * 2
|
||||
0f at 0 with style.easing
|
||||
style.jumpRotationAngle at headAndTailAnimationDuration
|
||||
}
|
||||
)
|
||||
)
|
||||
val startAngle by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
style.jumpRotationAngle,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = headAndTailAnimationDuration * 2
|
||||
0f at headAndTailAnimationDuration with style.easing
|
||||
style.jumpRotationAngle at durationMillis
|
||||
}
|
||||
)
|
||||
)
|
||||
Canvas(modifier.progressSemantics().size(style.diameter)) {
|
||||
drawCircularIndicatorBackground(colors.backgroundColor, stroke)
|
||||
val rotationAngleOffset = caleRotationAngleOffset(style.baseRotationAngle, style.jumpRotationAngle)
|
||||
val currentRotationAngleOffset = (currentRotation * rotationAngleOffset) % 360f
|
||||
val sweep = abs(endAngle - startAngle)
|
||||
val offset = style.startAngleOffset + currentRotationAngleOffset + baseRotation
|
||||
drawIndeterminateCircularIndicator(startAngle + offset, style.strokeWidth, style.diameter, sweep, colors.foregroundColor, stroke)
|
||||
}
|
||||
}
|
||||
if (indeterminate) Indeterminate() else Determinate()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
modifier: Modifier = Modifier,
|
||||
progress: Float = -1f,
|
||||
min: Float = 0f,
|
||||
max: Float = 100f,
|
||||
indeterminate: Boolean = progress < min,
|
||||
colors: ProgressIndicatorColors = ProgressIndicator.linearColors,
|
||||
style: LinearIndicatorStyle = ProgressIndicator.linearStyle
|
||||
) {
|
||||
@Composable
|
||||
fun Determinate() {
|
||||
val coercedProgress = progress.coerceIn(min, max)
|
||||
val normalizedProgress = (coercedProgress - min) / (max - min)
|
||||
Canvas(modifier.progressSemantics(normalizedProgress).size(style.width, style.strokeWidth)) {
|
||||
val strokeWidth = size.height
|
||||
drawLinearIndicatorBackground(colors.backgroundColor, strokeWidth, style.strokeCap)
|
||||
drawLinearIndicator(startFraction = 0f, normalizedProgress, colors.foregroundColor, strokeWidth, style.strokeCap)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Indeterminate() {
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val firstLineHead by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animationDuration
|
||||
0f at style.firstLineHeadDelay with style.firstLineHeadEasing
|
||||
1f at style.firstLineHeadDuration + style.firstLineHeadDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
val firstLineTail by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animationDuration
|
||||
0f at style.firstLineTailDelay with style.firstLineTailEasing
|
||||
1f at style.firstLineTailDuration + style.firstLineTailDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
val secondLineHead by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animationDuration
|
||||
0f at style.secondLineHeadDelay with style.secondLineHeadEasing
|
||||
1f at style.secondLineHeadDuration + style.secondLineHeadDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
val secondLineTail by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = style.animationDuration
|
||||
0f at style.secondLineTailDelay with style.secondLineTailEasing
|
||||
1f at style.secondLineTailDuration + style.secondLineTailDelay
|
||||
}
|
||||
)
|
||||
)
|
||||
Canvas(modifier.progressSemantics().size(style.width, style.strokeWidth)) {
|
||||
val strokeWidth = size.height
|
||||
drawLinearIndicatorBackground(colors.backgroundColor, strokeWidth, style.strokeCap)
|
||||
if (firstLineHead - firstLineTail > 0)
|
||||
drawLinearIndicator(firstLineHead, firstLineTail, colors.foregroundColor, strokeWidth, style.strokeCap)
|
||||
if (secondLineHead - secondLineTail > 0)
|
||||
drawLinearIndicator(secondLineHead, secondLineTail, colors.foregroundColor, strokeWidth, style.strokeCap)
|
||||
}
|
||||
}
|
||||
if (indeterminate) Indeterminate() else Determinate()
|
||||
}
|
||||
|
||||
private fun DrawScope.drawIndeterminateCircularIndicator(
|
||||
startAngle: Float,
|
||||
strokeWidth: Dp,
|
||||
diameter: Dp,
|
||||
sweep: Float,
|
||||
color: Color,
|
||||
stroke: Stroke
|
||||
) {
|
||||
val strokeCapOffset = if (stroke.cap == StrokeCap.Butt) 0f
|
||||
else (180.0 / PI).toFloat() * (strokeWidth / (diameter / 2)) / 2f
|
||||
val adjustedStartAngle = startAngle + strokeCapOffset
|
||||
val adjustedSweep = max(sweep, 0.1f)
|
||||
drawCircularIndicator(adjustedStartAngle, adjustedSweep, color, stroke)
|
||||
}
|
||||
|
||||
private fun DrawScope.drawCircularIndicatorBackground(
|
||||
color: Color,
|
||||
stroke: Stroke
|
||||
) = drawCircularIndicator(startAngle = 0f, sweep = 360f, color, stroke)
|
||||
|
||||
private fun DrawScope.drawCircularIndicator(
|
||||
startAngle: Float,
|
||||
sweep: Float,
|
||||
color: Color,
|
||||
stroke: Stroke
|
||||
) {
|
||||
val diameterOffset = stroke.width / 2
|
||||
val arcDimen = size.width - 2 * diameterOffset
|
||||
drawArc(
|
||||
color = color,
|
||||
startAngle = startAngle,
|
||||
sweepAngle = sweep,
|
||||
useCenter = false,
|
||||
topLeft = Offset(diameterOffset, diameterOffset),
|
||||
size = Size(arcDimen, arcDimen),
|
||||
style = stroke
|
||||
)
|
||||
}
|
||||
|
||||
private fun DrawScope.drawLinearIndicatorBackground(
|
||||
color: Color,
|
||||
strokeWidth: Float,
|
||||
strokeCap: StrokeCap,
|
||||
) = drawLinearIndicator(startFraction = 0f, endFraction = 1f, color, strokeWidth, strokeCap)
|
||||
|
||||
private fun DrawScope.drawLinearIndicator(
|
||||
startFraction: Float,
|
||||
endFraction: Float,
|
||||
color: Color,
|
||||
strokeWidth: Float,
|
||||
strokeCap: StrokeCap,
|
||||
) {
|
||||
val width = size.width
|
||||
val height = size.height
|
||||
val yOffset = height / 2
|
||||
val isLtr = layoutDirection == LayoutDirection.Ltr
|
||||
val barStart = (if (isLtr) startFraction else 1f - endFraction) * width
|
||||
val barEnd = (if (isLtr) endFraction else 1f - startFraction) * width
|
||||
if (strokeCap == StrokeCap.Butt || height > width) {
|
||||
drawLine(color, Offset(barStart, yOffset), Offset(barEnd, yOffset), strokeWidth)
|
||||
} else {
|
||||
val strokeCapOffset = strokeWidth / 2
|
||||
val coerceRange = strokeCapOffset..(width - strokeCapOffset)
|
||||
val adjustedBarStart = barStart.coerceIn(coerceRange)
|
||||
val adjustedBarEnd = barEnd.coerceIn(coerceRange)
|
||||
if (abs(endFraction - startFraction) > 0)
|
||||
drawLine(color, Offset(adjustedBarStart, yOffset), Offset(adjustedBarEnd, yOffset), strokeWidth, strokeCap)
|
||||
}
|
||||
}
|
||||
|
||||
object ProgressIndicator {
|
||||
val circularColors: ProgressIndicatorColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalProgressIndicatorColors.current.copy(
|
||||
foregroundColor = LocalProgressIndicatorColors.current.foregroundColor.orElse()
|
||||
?: defaultCircularIndicatorColors().foregroundColor,
|
||||
backgroundColor = LocalProgressIndicatorColors.current.backgroundColor.orElse()
|
||||
?: defaultCircularIndicatorColors().backgroundColor
|
||||
)
|
||||
val linearColors: ProgressIndicatorColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalProgressIndicatorColors.current.copy(
|
||||
foregroundColor = LocalProgressIndicatorColors.current.foregroundColor.orElse()
|
||||
?: defaultLinearIndicatorColors().foregroundColor,
|
||||
backgroundColor = LocalProgressIndicatorColors.current.backgroundColor.orElse()
|
||||
?: defaultLinearIndicatorColors().backgroundColor
|
||||
)
|
||||
val circularStyle: CircularIndicatorStyle
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultCircularIndicatorStyle()
|
||||
val linearStyle: LinearIndicatorStyle
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultLinearIndicatorStyle()
|
||||
}
|
||||
|
||||
internal val LocalProgressIndicatorColors = compositionLocalOf { DefaultProgressIndicatorColors }
|
||||
|
||||
internal val DefaultProgressIndicatorColors = ProgressIndicatorColors(Color.Unspecified, Color.Unspecified)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCircularIndicatorColors() = ProgressIndicatorColors(
|
||||
foregroundColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultLinearIndicatorColors() = ProgressIndicatorColors(
|
||||
foregroundColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = LocalColors.current.themeTertiary
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultCircularIndicatorStyle() = CircularIndicatorStyle(
|
||||
strokeWidth = DefaultIndicatorStrokeWidth,
|
||||
strokeCap = StrokeCap.Round,
|
||||
diameter = DefaultCircularIndicatorDiameter,
|
||||
rotationDuration = DefaultRotationDuration,
|
||||
rotationsPerCycle = DefaultRotationsPerCycle,
|
||||
startAngleOffset = DefaultStartAngleOffset,
|
||||
baseRotationAngle = DefaultBaseRotationAngle,
|
||||
jumpRotationAngle = DefaultJumpRotationAngle,
|
||||
easing = DefaultCircularEasing
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultLinearIndicatorStyle() = LinearIndicatorStyle(
|
||||
strokeWidth = DefaultIndicatorStrokeWidth,
|
||||
strokeCap = StrokeCap.Round,
|
||||
width = DefaultLinearIndicatorWidth,
|
||||
animationDuration = DefaultLinearAnimationDuration,
|
||||
firstLineHeadDuration = DefaultFirstLineHeadDuration,
|
||||
firstLineTailDuration = DefaultFirstLineTailDuration,
|
||||
secondLineHeadDuration = DefaultSecondLineHeadDuration,
|
||||
secondLineTailDuration = DefaultSecondLineTailDuration,
|
||||
firstLineHeadDelay = DefaultFirstLineHeadDelay,
|
||||
firstLineTailDelay = DefaultFirstLineTailDelay,
|
||||
secondLineHeadDelay = DefaultSecondLineHeadDelay,
|
||||
secondLineTailDelay = DefaultSecondLineTailDelay,
|
||||
firstLineHeadEasing = DefaultFirstLineHeadEasing,
|
||||
firstLineTailEasing = DefaultFirstLineTailEasing,
|
||||
secondLineHeadEasing = DefaultSecondLineHeadEasing,
|
||||
secondLineTailEasing = DefaultSecondLineTailEasing
|
||||
)
|
||||
|
||||
private fun caleRotationAngleOffset(baseRotationAngle: Float, jumpRotationAngle: Float) = (baseRotationAngle + jumpRotationAngle) % 360f
|
||||
|
||||
private fun caleHeadAndTailAnimationDuration(rotationDuration: Int) = (rotationDuration * 0.5).toInt()
|
||||
|
||||
private val DefaultIndicatorStrokeWidth = 4.dp
|
||||
private val DefaultLinearIndicatorWidth = 240.dp
|
||||
private val DefaultCircularIndicatorDiameter = 40.dp
|
||||
private const val DefaultLinearAnimationDuration = 1800
|
||||
|
||||
private const val DefaultFirstLineHeadDuration = 750
|
||||
private const val DefaultFirstLineTailDuration = 850
|
||||
private const val DefaultSecondLineHeadDuration = 567
|
||||
private const val DefaultSecondLineTailDuration = 533
|
||||
|
||||
private const val DefaultFirstLineHeadDelay = 0
|
||||
private const val DefaultFirstLineTailDelay = 333
|
||||
private const val DefaultSecondLineHeadDelay = 1000
|
||||
private const val DefaultSecondLineTailDelay = 1267
|
||||
|
||||
private const val DefaultRotationsPerCycle = 5
|
||||
private const val DefaultRotationDuration = 1332
|
||||
private const val DefaultStartAngleOffset = -90f
|
||||
private const val DefaultBaseRotationAngle = 286f
|
||||
private const val DefaultJumpRotationAngle = 290f
|
||||
|
||||
private val DefaultFirstLineHeadEasing = CubicBezierEasing(0.2f, 0f, 0.8f, 1f)
|
||||
private val DefaultFirstLineTailEasing = CubicBezierEasing(0.4f, 0f, 1f, 1f)
|
||||
private val DefaultSecondLineHeadEasing = CubicBezierEasing(0f, 0f, 0.65f, 1f)
|
||||
private val DefaultSecondLineTailEasing = CubicBezierEasing(0.1f, 0f, 0.45f, 1f)
|
||||
private val DefaultCircularEasing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f)
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/6.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: Scaffold, modeled after Scaffold in Material Design
|
||||
// Also set the global background color to Scaffold
|
||||
// Scaffold creates Surface
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/6.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.utils.orElse
|
||||
|
||||
// TODO: Linkage BetterAndroid SafeArea (SystemBarsController)
|
||||
|
||||
@Composable
|
||||
fun Surface(
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = Surface.padding,
|
||||
topPadding: Dp = Dp.Unspecified,
|
||||
startPadding: Dp = Dp.Unspecified,
|
||||
bottomPadding: Dp = Dp.Unspecified,
|
||||
endPadding: Dp = Dp.Unspecified,
|
||||
color: Color = Surface.color,
|
||||
contentColor: Color = Surface.contentColor,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalColors provides LocalColors.current.copy(
|
||||
backgroundPrimary = color,
|
||||
textPrimary = contentColor
|
||||
)
|
||||
) { Box(modifier.surface(padding, topPadding, startPadding, bottomPadding, endPadding, color), content = content) }
|
||||
}
|
||||
|
||||
private fun Modifier.surface(
|
||||
padding: Dp,
|
||||
topPadding: Dp,
|
||||
startPadding: Dp,
|
||||
bottomPadding: Dp,
|
||||
endPadding: Dp,
|
||||
color: Color
|
||||
) = background(color = color)
|
||||
.padding(
|
||||
top = topPadding.orElse() ?: padding,
|
||||
start = startPadding.orElse() ?: padding,
|
||||
bottom = bottomPadding.orElse() ?: padding,
|
||||
end = endPadding.orElse() ?: padding
|
||||
)
|
||||
|
||||
object Surface {
|
||||
val color: Color
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSurfaceColor()
|
||||
val contentColor: Color
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSurfaceContentColor()
|
||||
val padding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultSurfacePadding()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSurfacePadding() = LocalSizes.current.spacingPrimary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSurfaceColor() = LocalColors.current.backgroundPrimary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultSurfaceContentColor() = LocalColors.current.textPrimary
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import com.highcapable.flexiui.DefaultTypography
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.utils.orElse
|
||||
|
||||
@Composable
|
||||
fun Text(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign? = null,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
softWrap: Boolean = true,
|
||||
singleLine: Boolean = false,
|
||||
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = Text.style
|
||||
) {
|
||||
Text(
|
||||
text = AnnotatedString(text),
|
||||
modifier = modifier,
|
||||
color = color,
|
||||
fontSize = fontSize,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
fontFamily = fontFamily,
|
||||
letterSpacing = letterSpacing,
|
||||
textDecoration = textDecoration,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
overflow = overflow,
|
||||
softWrap = softWrap,
|
||||
maxLines = maxLines,
|
||||
minLines = minLines,
|
||||
inlineContent = emptyMap(),
|
||||
onTextLayout = onTextLayout,
|
||||
style = style
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Text(
|
||||
text: AnnotatedString,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign? = null,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
softWrap: Boolean = true,
|
||||
singleLine: Boolean = false,
|
||||
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
inlineContent: Map<String, InlineTextContent> = mapOf(),
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
style: TextStyle = Text.style
|
||||
) {
|
||||
BasicText(
|
||||
text = text,
|
||||
modifier = modifier,
|
||||
style = style.merge(
|
||||
TextStyle(
|
||||
color = color.orElse() ?: style.color.orElse() ?: Text.color,
|
||||
fontSize = fontSize.orElse() ?: style.fontSize.orElse() ?: Text.fontSize,
|
||||
fontWeight = fontWeight,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight.orElse() ?: style.lineHeight.orElse() ?: Text.lineHeight,
|
||||
fontFamily = fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
fontStyle = fontStyle,
|
||||
letterSpacing = letterSpacing
|
||||
)
|
||||
),
|
||||
onTextLayout = onTextLayout,
|
||||
overflow = overflow,
|
||||
softWrap = softWrap,
|
||||
maxLines = maxLines,
|
||||
minLines = minLines,
|
||||
inlineContent = inlineContent
|
||||
)
|
||||
}
|
||||
|
||||
object Text {
|
||||
val color: Color
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTextColor()
|
||||
val fontSize: TextUnit
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = style.fontSize
|
||||
val lineHeight: TextUnit
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = style.lineHeight
|
||||
val style: TextStyle
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalTextStyle.current
|
||||
}
|
||||
|
||||
internal val LocalTextStyle = compositionLocalOf { DefaultTextStyle }
|
||||
|
||||
internal val DefaultTextStyle = DefaultTypography.primary
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextColor() = LocalTextStyle.current.color.orElse() ?: LocalColors.current.textPrimary
|
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import com.highcapable.flexiui.utils.borderOrNot
|
||||
import com.highcapable.flexiui.utils.orElse
|
||||
|
||||
// TODO: Preset text boxes (password text box, text box with delete button, etc.)
|
||||
|
||||
@Immutable
|
||||
data class TextFieldColors(
|
||||
val cursorColor: Color,
|
||||
val selectionColors: TextSelectionColors,
|
||||
val borderInactiveColor: Color,
|
||||
val borderActiveColor: Color,
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun TextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = TextField.padding,
|
||||
topPadding: Dp = Dp.Unspecified,
|
||||
startPadding: Dp = Dp.Unspecified,
|
||||
bottomPadding: Dp = Dp.Unspecified,
|
||||
endPadding: Dp = Dp.Unspecified,
|
||||
shape: Shape = TextField.shape,
|
||||
borderInactive: BorderStroke = TextField.borderInactive,
|
||||
borderActive: BorderStroke = TextField.borderActive,
|
||||
colors: TextFieldColors = TextField.colors,
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
singleLine: Boolean = false,
|
||||
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
header: @Composable () -> Unit = {},
|
||||
placeholder: @Composable () -> Unit = {},
|
||||
footer: @Composable () -> Unit = {},
|
||||
style: TextStyle = TextField.style
|
||||
) {
|
||||
TextFieldStyle(colors) {
|
||||
BasicTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
readOnly = readOnly,
|
||||
textStyle = style,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
singleLine = singleLine,
|
||||
maxLines = maxLines,
|
||||
minLines = minLines,
|
||||
visualTransformation = visualTransformation,
|
||||
onTextLayout = onTextLayout,
|
||||
interactionSource = interactionSource,
|
||||
cursorBrush = SolidColor(colors.cursorColor),
|
||||
decorationBox = {
|
||||
TextFieldDecorationBox(
|
||||
value = value,
|
||||
modifier = modifier,
|
||||
padding = padding,
|
||||
topPadding = topPadding,
|
||||
startPadding = startPadding,
|
||||
bottomPadding = bottomPadding,
|
||||
endPadding = endPadding,
|
||||
shape = shape,
|
||||
borderInactive = borderInactive,
|
||||
borderActive = borderActive,
|
||||
colors = colors,
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
header = header,
|
||||
placeholder = placeholder,
|
||||
footer = footer,
|
||||
innerTextField = it
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextField(
|
||||
value: TextFieldValue,
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = TextField.padding,
|
||||
topPadding: Dp = Dp.Unspecified,
|
||||
startPadding: Dp = Dp.Unspecified,
|
||||
bottomPadding: Dp = Dp.Unspecified,
|
||||
endPadding: Dp = Dp.Unspecified,
|
||||
shape: Shape = TextField.shape,
|
||||
borderInactive: BorderStroke = TextField.borderInactive,
|
||||
borderActive: BorderStroke = TextField.borderActive,
|
||||
colors: TextFieldColors = TextField.colors,
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
singleLine: Boolean = false,
|
||||
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
|
||||
minLines: Int = 1,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
header: @Composable () -> Unit = {},
|
||||
placeholder: @Composable () -> Unit = {},
|
||||
footer: @Composable () -> Unit = {},
|
||||
style: TextStyle = TextField.style
|
||||
) {
|
||||
TextFieldStyle(colors) {
|
||||
BasicTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
readOnly = readOnly,
|
||||
textStyle = style,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
singleLine = singleLine,
|
||||
maxLines = maxLines,
|
||||
minLines = minLines,
|
||||
visualTransformation = visualTransformation,
|
||||
onTextLayout = onTextLayout,
|
||||
interactionSource = interactionSource,
|
||||
cursorBrush = SolidColor(colors.cursorColor),
|
||||
decorationBox = {
|
||||
TextFieldDecorationBox(
|
||||
value = value.text,
|
||||
modifier = modifier,
|
||||
padding = padding,
|
||||
topPadding = topPadding,
|
||||
startPadding = startPadding,
|
||||
bottomPadding = bottomPadding,
|
||||
endPadding = endPadding,
|
||||
shape = shape,
|
||||
borderInactive = borderInactive,
|
||||
borderActive = borderActive,
|
||||
colors = colors,
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
header = header,
|
||||
placeholder = placeholder,
|
||||
footer = footer,
|
||||
innerTextField = it
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextFieldStyle(colors: TextFieldColors, content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides colors.selectionColors) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TextFieldDecorationBox(
|
||||
value: String,
|
||||
modifier: Modifier,
|
||||
padding: Dp,
|
||||
topPadding: Dp,
|
||||
startPadding: Dp,
|
||||
bottomPadding: Dp,
|
||||
endPadding: Dp,
|
||||
shape: Shape,
|
||||
borderInactive: BorderStroke,
|
||||
borderActive: BorderStroke,
|
||||
colors: TextFieldColors,
|
||||
enabled: Boolean,
|
||||
interactionSource: MutableInteractionSource,
|
||||
header: @Composable () -> Unit,
|
||||
placeholder: @Composable () -> Unit,
|
||||
footer: @Composable () -> Unit,
|
||||
innerTextField: @Composable () -> Unit
|
||||
) {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
val border = if (focused) borderActive else borderInactive
|
||||
Box(
|
||||
modifier.textField(
|
||||
padding = padding,
|
||||
topPadding = topPadding,
|
||||
startPadding = startPadding,
|
||||
bottomPadding = bottomPadding,
|
||||
endPadding = endPadding,
|
||||
shape = shape,
|
||||
border = border,
|
||||
colors = colors,
|
||||
enabled = enabled
|
||||
)
|
||||
) {
|
||||
val placeholderAlpha by animateFloatAsState(if (value.isNotEmpty()) 0f else 1f)
|
||||
Row {
|
||||
header()
|
||||
Box {
|
||||
Box(modifier = Modifier.alpha(placeholderAlpha)) {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides LocalTextStyle.current.copy(color = LocalColors.current.textSecondary)
|
||||
) { placeholder() }
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
footer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.textField(
|
||||
padding: Dp,
|
||||
topPadding: Dp,
|
||||
startPadding: Dp,
|
||||
bottomPadding: Dp,
|
||||
endPadding: Dp,
|
||||
shape: Shape,
|
||||
border: BorderStroke,
|
||||
colors: TextFieldColors,
|
||||
enabled: Boolean
|
||||
): Modifier {
|
||||
var sModifier = clip(shape = shape)
|
||||
.background(color = colors.backgroundColor, shape = shape)
|
||||
.borderOrNot(border, shape)
|
||||
.padding(
|
||||
top = topPadding.orElse() ?: padding,
|
||||
start = startPadding.orElse() ?: padding,
|
||||
bottom = bottomPadding.orElse() ?: padding,
|
||||
end = endPadding.orElse() ?: padding
|
||||
)
|
||||
if (!enabled) sModifier = sModifier.alpha(0.5f)
|
||||
return sModifier
|
||||
}
|
||||
|
||||
object TextField {
|
||||
val padding: Dp
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalSizes.current.spacingSecondary
|
||||
val shape: Shape
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = when (LocalInAreaBox.current) {
|
||||
true -> LocalAreaBoxShape.current
|
||||
else -> LocalShapes.current.primary
|
||||
}
|
||||
val borderInactive: BorderStroke
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTextFieldInActiveBorder()
|
||||
val borderActive: BorderStroke
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTextFieldActiveBorder()
|
||||
val colors: TextFieldColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = defaultTextFieldColors()
|
||||
val style: TextStyle
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalTextStyle.current.copy(color = LocalColors.current.textPrimary)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldColors() = TextFieldColors(
|
||||
cursorColor = LocalColors.current.themePrimary,
|
||||
selectionColors = TextSelectionColors(
|
||||
handleColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = LocalColors.current.themeSecondary
|
||||
),
|
||||
borderInactiveColor = LocalColors.current.themeSecondary,
|
||||
borderActiveColor = LocalColors.current.themePrimary,
|
||||
backgroundColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldInActiveBorder() = BorderStroke(LocalSizes.current.borderSizeSecondary, LocalColors.current.themeSecondary)
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun defaultTextFieldActiveBorder() = BorderStroke(LocalSizes.current.borderSizePrimary, LocalColors.current.themePrimary)
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.interaction
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/9.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.interaction
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.interaction
|
||||
|
||||
// TODO: To be implemented
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/5.
|
||||
*/
|
||||
package com.highcapable.flexiui.utils
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.isSpecified
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.isSpecified
|
||||
|
||||
inline fun Dp.orElse() = if (isSpecified) this else null
|
||||
|
||||
inline fun Color.orElse() = if (isSpecified) this else null
|
||||
|
||||
inline fun TextUnit.orElse() = if (isSpecified) this else null
|
||||
|
||||
@Stable
|
||||
fun Modifier.borderOrNot(border: BorderStroke, shape: Shape = RectangleShape) = border.takeIf { it.width > 0.dp }?.let { border(it, shape) } ?: this
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/8.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.foundation.LocalContextMenuRepresentation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import com.highcapable.flexiui.component.defaultFlexiContextMenuRepresentation
|
||||
|
||||
@Composable
|
||||
internal actual fun FlexiThemeContent(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalContextMenuRepresentation provides defaultFlexiContextMenuRepresentation(), content = content)
|
||||
}
|
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/8.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui.component
|
||||
|
||||
import androidx.compose.foundation.ContextMenuItem
|
||||
import androidx.compose.foundation.ContextMenuRepresentation
|
||||
import androidx.compose.foundation.ContextMenuState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
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.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.focus.FocusDirection
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.input.InputMode
|
||||
import androidx.compose.ui.input.InputModeManager
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.nativeKeyCode
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.input.pointer.PointerEventType
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalInputModeManager
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import androidx.compose.ui.window.rememberPopupPositionProviderAtPosition
|
||||
import com.highcapable.flexiui.LocalColors
|
||||
import com.highcapable.flexiui.LocalShapes
|
||||
import com.highcapable.flexiui.LocalSizes
|
||||
import java.awt.event.KeyEvent
|
||||
|
||||
internal class FlexiContextMenuRepresentation(
|
||||
private val backgroundColor: Color,
|
||||
private val fillColor: Color,
|
||||
private val textColor: Color,
|
||||
private val padding: Dp,
|
||||
private val shadowSize: Dp,
|
||||
private val shape: Shape
|
||||
) : ContextMenuRepresentation {
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
override fun Representation(state: ContextMenuState, items: () -> List<ContextMenuItem>) {
|
||||
val status = state.status
|
||||
if (status is ContextMenuState.Status.Open) {
|
||||
var focusManager: FocusManager? by mutableStateOf(null)
|
||||
var inputModeManager: InputModeManager? by mutableStateOf(null)
|
||||
Popup(
|
||||
popupPositionProvider = rememberPopupPositionProviderAtPosition(positionPx = status.rect.center),
|
||||
onDismissRequest = { state.status = ContextMenuState.Status.Closed },
|
||||
properties = PopupProperties(focusable = true), onPreviewKeyEvent = { false }, onKeyEvent = {
|
||||
if (it.type == KeyEventType.KeyDown)
|
||||
when (it.key.nativeKeyCode) {
|
||||
KeyEvent.VK_DOWN -> {
|
||||
inputModeManager?.requestInputMode(InputMode.Keyboard)
|
||||
focusManager?.moveFocus(FocusDirection.Next)
|
||||
true
|
||||
}
|
||||
KeyEvent.VK_UP -> {
|
||||
inputModeManager?.requestInputMode(InputMode.Keyboard)
|
||||
focusManager?.moveFocus(FocusDirection.Previous)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else false
|
||||
}) {
|
||||
focusManager = LocalFocusManager.current
|
||||
inputModeManager = LocalInputModeManager.current
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.shadow(elevation = shadowSize, shape = shape)
|
||||
.background(color = backgroundColor, shape = shape)
|
||||
.padding(padding)
|
||||
.width(IntrinsicSize.Max)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
items().forEach { item ->
|
||||
MenuItemContent(
|
||||
fillColor = fillColor,
|
||||
shape = shape,
|
||||
onClick = {
|
||||
state.status = ContextMenuState.Status.Closed
|
||||
item.onClick()
|
||||
}
|
||||
) { Text(text = item.label) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MenuItemContent(
|
||||
fillColor: Color,
|
||||
shape: Shape,
|
||||
onClick: () -> Unit,
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
var hovered by remember { mutableStateOf(false) }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(shape)
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
indication = rememberRipple(color = fillColor),
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
.onHover { hovered = it }
|
||||
.fillMaxWidth()
|
||||
// Preferred min and max width used during the intrinsic measurement.
|
||||
.sizeIn(
|
||||
minWidth = 112.dp,
|
||||
maxWidth = 280.dp,
|
||||
minHeight = 32.dp
|
||||
)
|
||||
.padding(
|
||||
PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 0.dp
|
||||
)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) { content() }
|
||||
}
|
||||
|
||||
private fun Modifier.onHover(onHover: (Boolean) -> Unit) = pointerInput(Unit) {
|
||||
awaitPointerEventScope {
|
||||
while (true) {
|
||||
val event = awaitPointerEvent()
|
||||
when (event.type) {
|
||||
PointerEventType.Enter -> onHover(true)
|
||||
PointerEventType.Exit -> onHover(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
internal fun defaultFlexiContextMenuRepresentation() = FlexiContextMenuRepresentation(
|
||||
backgroundColor = LocalColors.current.foregroundPrimary,
|
||||
fillColor = LocalColors.current.themeSecondary,
|
||||
textColor = LocalColors.current.textPrimary,
|
||||
padding = LocalSizes.current.spacingTertiary,
|
||||
shadowSize = LocalSizes.current.zoomSizeTertiary,
|
||||
shape = LocalShapes.current.primary
|
||||
)
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Flexi UI - A flexible and useful UI component library.
|
||||
* Copyright (C) 2019-2023 HighCapable
|
||||
* https://github.com/BetterAndroid/FlexiUI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2023/11/8.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.flexiui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
internal actual fun FlexiThemeContent(content: @Composable () -> Unit) {
|
||||
content()
|
||||
}
|
Reference in New Issue
Block a user