commit 1ba8d5ed6e98fdc974913e4c97e4e87b84d279fa Author: fankesyooni Date: Thu Nov 9 02:50:19 2023 +0800 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7101007 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# noinspection EditorConfigKeyCorrectness +[{*.kt,*.kts}] +ktlint_standard_annotation = disabled +ktlint_standard_filename = disabled +ktlint_standard_wrapping = disabled +ktlint_standard_import-ordering = enabled +ktlint_standard_max-line-length = disabled +ktlint_standard_multiline-if-else = disabled +ktlint_standard_argument-list-wrapping = disabled +ktlint_standard_parameter-list-wrapping = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled +ktlint_function_signature_body_expression_wrapping = multiline +ij_continuation_indent_size = 2 +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 150 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7415cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..3ad4a12 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/gradle.xml +/misc.xml +/workspace.xml \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..d18d286 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +FlexiUI \ No newline at end of file diff --git a/.idea/appInsightsSettings.xml b/.idea/appInsightsSettings.xml new file mode 100644 index 0000000..371f2e2 --- /dev/null +++ b/.idea/appInsightsSettings.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/.idea/artifacts/desktopApp_desktop.xml b/.idea/artifacts/desktopApp_desktop.xml new file mode 100644 index 0000000..b035eb2 --- /dev/null +++ b/.idea/artifacts/desktopApp_desktop.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/samples/desktopApp/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/flexi_core_desktop.xml b/.idea/artifacts/flexi_core_desktop.xml new file mode 100644 index 0000000..74f7073 --- /dev/null +++ b/.idea/artifacts/flexi_core_desktop.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/flexi-core/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/flexiui_core_desktop.xml b/.idea/artifacts/flexiui_core_desktop.xml new file mode 100644 index 0000000..1471f76 --- /dev/null +++ b/.idea/artifacts/flexiui_core_desktop.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/flexiui-core/build/libs + + + + + \ No newline at end of file diff --git a/.idea/artifacts/shared_desktop.xml b/.idea/artifacts/shared_desktop.xml new file mode 100644 index 0000000..2c3caff --- /dev/null +++ b/.idea/artifacts/shared_desktop.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/samples/shared/build/libs + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..f255eeb --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000..959a79c Binary files /dev/null and b/.idea/icon.png differ diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/ktlint.xml b/.idea/ktlint.xml new file mode 100644 index 0000000..92c4441 --- /dev/null +++ b/.idea/ktlint.xml @@ -0,0 +1,6 @@ + + + + false + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/README-zh-CN.md b/README-zh-CN.md new file mode 100644 index 0000000..d8ecf0c --- /dev/null +++ b/README-zh-CN.md @@ -0,0 +1,45 @@ +# Flexi UI (Jetpack Compose) + +[![GitHub license](https://img.shields.io/github/license/BetterAndroid/FlexiUI?color=blue)](https://github.com/BetterAndroid/FlexiUI/blob/main/LICENSE) +[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/BetterAndroid) +[![Telegram](https://img.shields.io/badge/discussion%20dev-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev) + +[English](https://github.com/BetterAndroid/FlexiUI/blob/compose-dev/README.md) | 简体中文 + +| LOGO | [BetterAndroid](https://github.com/BetterAndroid) | +|---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------| + +这个项目属于上述组织,**点击上方链接关注这个组织**,发现更多好项目。 + +## 开发进行中说明 + +> **Warning** +> 该项目仍处于开发的早期阶段,所有 API 都有可能发生重大变化。 +> +> 该项目正式发布后,将会推送代码从 `compose-dev` 到 `compose` 分支。 + +你可以随时为此分支贡献你的代码 (如果可能) 以更快地完善这个项目。 + +## 许可证 + +- [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +``` +Apache License Version 2.0 + +Copyright (C) 2019-2023 HighCapable + +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. +``` + +版权所有 © 2019-2023 HighCapable \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b80995 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Flexi UI (Jetpack Compose) + +[![GitHub license](https://img.shields.io/github/license/BetterAndroid/FlexiUI?color=blue)](https://github.com/BetterAndroid/FlexiUI/blob/main/LICENSE) +[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/BetterAndroid) +[![Telegram](https://img.shields.io/badge/discussion%20dev-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev) + +English | [简体中文](https://github.com/BetterAndroid/FlexiUI/blob/compose-dev/README-zh-CN.md) + +| LOGO | [BetterAndroid](https://github.com/BetterAndroid) | +|---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------| + +This project belongs to the above-mentioned organization, **click the link above to follow this organization** and discover more good projects. + +## WIP Notice + +> **Warning** +> This project is still in the early stages of development, and all APIs are subject to major changes. +> +> After the project is officially released, the code will be pushed from `compose-dev` to the `compose` branch. + +You can always contribute your code to this branch (if possible) to improve the project faster. + +## License + +- [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +``` +Apache License Version 2.0 + +Copyright (C) 2019-2023 HighCapable + +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. +``` + +Copyright © 2019-2023 HighCapable \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..19264c6 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + autowire(libs.plugins.kotlin.multiplatform) apply false + autowire(libs.plugins.android.application) apply false + autowire(libs.plugins.android.library) apply false + autowire(libs.plugins.jetbrains.compose) apply false +} \ No newline at end of file diff --git a/flexiui-core/.gitignore b/flexiui-core/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/flexiui-core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/flexiui-core/build.gradle.kts b/flexiui-core/build.gradle.kts new file mode 100644 index 0000000..55f7d28 --- /dev/null +++ b/flexiui-core/build.gradle.kts @@ -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 +} \ No newline at end of file diff --git a/flexiui-core/src/androidMain/AndroidManifest.xml b/flexiui-core/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..568741e --- /dev/null +++ b/flexiui-core/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flexiui-core/src/androidMain/kotlin/com/highcapable/flexiui/FlexiTheme.android.kt b/flexiui-core/src/androidMain/kotlin/com/highcapable/flexiui/FlexiTheme.android.kt new file mode 100644 index 0000000..3c2c9e6 --- /dev/null +++ b/flexiui-core/src/androidMain/kotlin/com/highcapable/flexiui/FlexiTheme.android.kt @@ -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() +} \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Colors.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Colors.kt new file mode 100644 index 0000000..7027a89 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Colors.kt @@ -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) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/FlexiTheme.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/FlexiTheme.kt new file mode 100644 index 0000000..a5a8c81 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/FlexiTheme.kt @@ -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) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Shapes.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Shapes.kt new file mode 100644 index 0000000..61d8ce1 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Shapes.kt @@ -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) +) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Sizes.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Sizes.kt new file mode 100644 index 0000000..6c9a530 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Sizes.kt @@ -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 +) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Typography.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Typography.kt new file mode 100644 index 0000000..8470555 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/Typography.kt @@ -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 + ) +) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/ActionBar.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/ActionBar.kt new file mode 100644 index 0000000..67ac06d --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/ActionBar.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/AreaBox.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/AreaBox.kt new file mode 100644 index 0000000..18ba524 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/AreaBox.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Button.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Button.kt new file mode 100644 index 0000000..6961b04 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Button.kt @@ -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 +) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Checkbox.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Checkbox.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Checkbox.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Dropdown.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Dropdown.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Dropdown.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Navigation.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Navigation.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Navigation.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/ProgressIndicator.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/ProgressIndicator.kt new file mode 100644 index 0000000..bed3d10 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/ProgressIndicator.kt @@ -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) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/RadioButton.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/RadioButton.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/RadioButton.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Scaffold.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Scaffold.kt new file mode 100644 index 0000000..6622543 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Scaffold.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Slider.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Slider.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Slider.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Surface.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Surface.kt new file mode 100644 index 0000000..28c0fc9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Surface.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Switch.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Switch.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Switch.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Tab.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Tab.kt new file mode 100644 index 0000000..2cfbfd9 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Tab.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Text.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Text.kt new file mode 100644 index 0000000..ab1f730 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/Text.kt @@ -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 = 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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/TextField.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/TextField.kt new file mode 100644 index 0000000..8a335f2 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/component/TextField.kt @@ -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) \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Dialog.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Dialog.kt new file mode 100644 index 0000000..35d8229 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Dialog.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Poptips.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Poptips.kt new file mode 100644 index 0000000..35d8229 --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Poptips.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Popup.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Popup.kt new file mode 100644 index 0000000..bb6716f --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/interaction/Popup.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/utils/UiFactory.kt b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/utils/UiFactory.kt new file mode 100644 index 0000000..bd1265a --- /dev/null +++ b/flexiui-core/src/commonMain/kotlin/com/highcapable/flexiui/utils/UiFactory.kt @@ -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 \ No newline at end of file diff --git a/flexiui-core/src/desktopMain/kotlin/com/highcapable/flexiui/FlexiTheme.desktop.kt b/flexiui-core/src/desktopMain/kotlin/com/highcapable/flexiui/FlexiTheme.desktop.kt new file mode 100644 index 0000000..49a374a --- /dev/null +++ b/flexiui-core/src/desktopMain/kotlin/com/highcapable/flexiui/FlexiTheme.desktop.kt @@ -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) +} \ No newline at end of file diff --git a/flexiui-core/src/desktopMain/kotlin/com/highcapable/flexiui/component/DesktopContextMenu.kt b/flexiui-core/src/desktopMain/kotlin/com/highcapable/flexiui/component/DesktopContextMenu.kt new file mode 100644 index 0000000..8e83aae --- /dev/null +++ b/flexiui-core/src/desktopMain/kotlin/com/highcapable/flexiui/component/DesktopContextMenu.kt @@ -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) { + 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 +) \ No newline at end of file diff --git a/flexiui-core/src/iosMain/kotlin/com/highcapable/flexiui/FlexiTheme.ios.kt b/flexiui-core/src/iosMain/kotlin/com/highcapable/flexiui/FlexiTheme.ios.kt new file mode 100644 index 0000000..3c2c9e6 --- /dev/null +++ b/flexiui-core/src/iosMain/kotlin/com/highcapable/flexiui/FlexiTheme.ios.kt @@ -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() +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7817e98 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,20 @@ +# Compiler Configuration +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +android.useAndroidX=true +android.nonTransitiveRClass=true +kotlin.code.style=official +kotlin.incremental.useClasspathSnapshot=true +org.jetbrains.compose.experimental.uikit.enabled=true +# Project Configuration +project.name=FlexiUI +project.description=A flexible and useful UI component library. +project.url=https://github.com/BetterAndroid/FlexiUI +project.groupName=com.highcapable.flexiui +project.licence.name=Apache License 2.0 +project.licence.url=https://github.com/BetterAndroid/FlexiUI/blob/main/LICENSE +project.sharedApp.packageName=${project.groupName}.demo +project.android.compileSdk=34 +project.android.minSdk=21 +project.android.targetSdk=34 +project.androidApp.versionName=1.0.0 +project.androidApp.versionCode=1 \ No newline at end of file diff --git a/gradle/sweet-dependency/sweet-dependency-config.yaml b/gradle/sweet-dependency/sweet-dependency-config.yaml new file mode 100644 index 0000000..3391510 --- /dev/null +++ b/gradle/sweet-dependency/sweet-dependency-config.yaml @@ -0,0 +1,55 @@ +preferences: + autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES + repositories-mode: FAIL_ON_PROJECT_REPOS + +repositories: + gradle-plugin-portal: + scope: PLUGINS + google: + maven-central: + +plugins: + org.jetbrains.kotlin.multiplatform: + alias: kotlin-multiplatform + version: 1.9.20 + org.jetbrains.compose: + alias: jetbrains-compose + version: 1.5.10 + com.android.application: + alias: android-application + version: 8.1.2 + com.android.library: + alias: android-library + version-ref: android-application + +libraries: + com.highcapable.betterandroid: + ui-component: + version: 1.0.0 + ui-extension: + version: 1.0.0 + system-extension: + version: 1.0.0 + androidx.activity: + activity: + version: 1.8.0 + activity-compose: + version: 1.8.0 + androidx.core: + core-ktx: + version: 1.12.0 + androidx.appcompat: + appcompat: + version: 1.6.1 + com.google.android.material: + material: + version: 1.10.0 + androidx.test.ext: + junit: + version: 1.1.5 + androidx.test.espresso: + espresso-core: + version: 3.5.1 + junit: + junit: + version: 4.13.2 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..692fc5c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/img-src/icon.png b/img-src/icon.png new file mode 100644 index 0000000..959a79c Binary files /dev/null and b/img-src/icon.png differ diff --git a/samples/androidApp/.gitignore b/samples/androidApp/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/androidApp/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/androidApp/build.gradle.kts b/samples/androidApp/build.gradle.kts new file mode 100644 index 0000000..52d1a24 --- /dev/null +++ b/samples/androidApp/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + autowire(libs.plugins.kotlin.multiplatform) + autowire(libs.plugins.android.application) + autowire(libs.plugins.jetbrains.compose) +} + +group = property.project.sharedApp.packageName + +kotlin { + androidTarget() + jvmToolchain(17) + sourceSets { + val androidMain by getting { + dependencies { + implementation(projects.samples.shared) + } + } + } +} + +android { + namespace = property.project.sharedApp.packageName + compileSdk = property.project.android.compileSdk + + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + + defaultConfig { + applicationId = property.project.sharedApp.packageName + minSdk = property.project.android.minSdk + targetSdk = property.project.android.targetSdk + versionName = property.project.androidApp.versionName + versionCode = property.project.androidApp.versionCode + } + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } +} \ No newline at end of file diff --git a/samples/androidApp/proguard-rules.pro b/samples/androidApp/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/samples/androidApp/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/AndroidManifest.xml b/samples/androidApp/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..4b85f11 --- /dev/null +++ b/samples/androidApp/src/androidMain/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/ic_launcher-playstore.png b/samples/androidApp/src/androidMain/ic_launcher-playstore.png new file mode 100644 index 0000000..959a79c Binary files /dev/null and b/samples/androidApp/src/androidMain/ic_launcher-playstore.png differ diff --git a/samples/androidApp/src/androidMain/kotlin/com/highcapable/flexiui/demo/MainActivity.kt b/samples/androidApp/src/androidMain/kotlin/com/highcapable/flexiui/demo/MainActivity.kt new file mode 100644 index 0000000..8c047ae --- /dev/null +++ b/samples/androidApp/src/androidMain/kotlin/com/highcapable/flexiui/demo/MainActivity.kt @@ -0,0 +1,35 @@ +/* + * 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.demo + +import MainView +import android.os.Bundle +import androidx.activity.compose.setContent +import com.highcapable.betterandroid.ui.component.activity.AppViewsActivity + +class MainActivity : AppViewsActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { MainView() } + } +} \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/samples/androidApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/samples/androidApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/res/drawable/ic_launcher_background.xml b/samples/androidApp/src/androidMain/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/samples/androidApp/src/androidMain/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/samples/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/samples/androidApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp b/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..44bcb00 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp b/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..9c5ec23 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp b/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..4685576 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp b/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..8b6bc1b Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp b/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..65355a7 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp b/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..2de270d Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp b/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..d0d513b Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp b/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..02dd30f Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp b/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..6591e87 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp b/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..a033146 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..e7cc049 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp b/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..040c8f3 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp b/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..522a828 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..ba35137 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp b/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..be8a341 Binary files /dev/null and b/samples/androidApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/samples/androidApp/src/androidMain/res/values/ic_launcher_background.xml b/samples/androidApp/src/androidMain/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..930f6a4 --- /dev/null +++ b/samples/androidApp/src/androidMain/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #4285F4 + \ No newline at end of file diff --git a/samples/androidApp/src/androidMain/res/values/strings.xml b/samples/androidApp/src/androidMain/res/values/strings.xml new file mode 100644 index 0000000..d055c68 --- /dev/null +++ b/samples/androidApp/src/androidMain/res/values/strings.xml @@ -0,0 +1,3 @@ + + FlexiUI Demo + \ No newline at end of file diff --git a/samples/build/generated/sweet-property/FlexiUI/generated/SamplesProperties.kt b/samples/build/generated/sweet-property/FlexiUI/generated/SamplesProperties.kt new file mode 100644 index 0000000..79039cc --- /dev/null +++ b/samples/build/generated/sweet-property/FlexiUI/generated/SamplesProperties.kt @@ -0,0 +1,103 @@ +package FlexiUI.generated + +import kotlin.Boolean +import kotlin.Int +import kotlin.String + +/** + * This class is generated by SweetProperty at Nov 5, 2023, 2:53:46 AM + * + * The content here is automatically generated according to the properties of your projects + * + * You can visit [here](https://github.com/HighCapable/SweetProperty) for more help + */ +public object SamplesProperties { + /** + * Resolve the "android.nonTransitiveRClass" value true + */ + public const val ANDROID_NON_TRANSITIVE_RCLASS: Boolean = true + + /** + * Resolve the "org.jetbrains.compose.experimental.uikit.enabled" value true + */ + public const val ORG_JETBRAINS_COMPOSE_EXPERIMENTAL_UIKIT_ENABLED: Boolean = true + + /** + * Resolve the "project.description" value "This is a Jetpack Compose Multiplatform demo." + */ + public const val PROJECT_DESCRIPTION: String = "This is a Jetpack Compose Multiplatform demo." + + /** + * Resolve the "project.android.compileSdk" value 34 + */ + public const val PROJECT_ANDROID_COMPILE_SDK: Int = 34 + + /** + * Resolve the "project.licence.name" value "Apache License 2.0" + */ + public const val PROJECT_LICENCE_NAME: String = "Apache License 2.0" + + /** + * Resolve the "project.licence.url" value + * "https://github.com/BetterAndroid/compose-multiplatform-template/blob/main/LICENSE" + */ + public const val PROJECT_LICENCE_URL: String = + "https://github.com/BetterAndroid/compose-multiplatform-template/blob/main/LICENSE" + + /** + * Resolve the "project.name" value "FlexiUI" + */ + public const val PROJECT_NAME: String = "FlexiUI" + + /** + * Resolve the "org.gradle.jvmargs" value "-Xmx2048m -Dfile.encoding=UTF-8" + */ + public const val ORG_GRADLE_JVMARGS: String = "-Xmx2048m -Dfile.encoding=UTF-8" + + /** + * Resolve the "android.useAndroidX" value true + */ + public const val ANDROID_USE_ANDROID_X: Boolean = true + + /** + * Resolve the "project.androidApp.versionName" value "1.0.0" + */ + public const val PROJECT_ANDROID_APP_VERSION_NAME: String = "1.0.0" + + /** + * Resolve the "kotlin.code.style" value "official" + */ + public const val KOTLIN_CODE_STYLE: String = "official" + + /** + * Resolve the "project.url" value + * "https://github.com/BetterAndroid/compose-multiplatform-template" + */ + public const val PROJECT_URL: String = + "https://github.com/BetterAndroid/compose-multiplatform-template" + + /** + * Resolve the "project.androidApp.versionCode" value 1 + */ + public const val PROJECT_ANDROID_APP_VERSION_CODE: Int = 1 + + /** + * Resolve the "project.groupName" value "com.highcapable.composemultiplatformdemo" + */ + public const val PROJECT_GROUP_NAME: String = "com.highcapable.composemultiplatformdemo" + + /** + * Resolve the "project.android.minSdk" value 21 + */ + public const val PROJECT_ANDROID_MIN_SDK: Int = 21 + + /** + * Resolve the "project.android.targetSdk" value 34 + */ + public const val PROJECT_ANDROID_TARGET_SDK: Int = 34 + + /** + * Resolve the "kotlin.incremental.useClasspathSnapshot" value true + */ + public const val KOTLIN_INCREMENTAL_USE_CLASSPATH_SNAPSHOT: Boolean = true +} diff --git a/samples/desktopApp/.gitignore b/samples/desktopApp/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/desktopApp/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/desktopApp/build.gradle.kts b/samples/desktopApp/build.gradle.kts new file mode 100644 index 0000000..eb9b2a0 --- /dev/null +++ b/samples/desktopApp/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + autowire(libs.plugins.kotlin.multiplatform) + autowire(libs.plugins.jetbrains.compose) +} + +group = property.project.sharedApp.packageName + +kotlin { + jvm("desktop") + jvmToolchain(17) + sourceSets { + val desktopMain by getting { + dependencies { + implementation(projects.samples.shared) + } + } + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +compose.desktop { + application { + mainClass = "${property.project.sharedApp.packageName}.MainKt" + } +} \ No newline at end of file diff --git a/samples/desktopApp/src/desktopMain/kotlin/com/highcapable/flexiui/demo/Main.kt b/samples/desktopApp/src/desktopMain/kotlin/com/highcapable/flexiui/demo/Main.kt new file mode 100644 index 0000000..f2a4095 --- /dev/null +++ b/samples/desktopApp/src/desktopMain/kotlin/com/highcapable/flexiui/demo/Main.kt @@ -0,0 +1,36 @@ +/* + * 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.demo + +import MainView +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import androidx.compose.ui.window.rememberWindowState + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "FlexiUI Demo", + state = rememberWindowState(width = 550.dp, height = 600.dp) + ) { MainView() } +} \ No newline at end of file diff --git a/samples/iosApp/.gitignore b/samples/iosApp/.gitignore new file mode 100644 index 0000000..a0a2d2e --- /dev/null +++ b/samples/iosApp/.gitignore @@ -0,0 +1,6 @@ +## User settings +xcuserdata/ + +## Xcode 8 and earlier +*.xcscmblueprint +*.xccheckout \ No newline at end of file diff --git a/samples/iosApp/Configuration/Config.xcconfig b/samples/iosApp/Configuration/Config.xcconfig new file mode 100644 index 0000000..c8eb9b1 --- /dev/null +++ b/samples/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID= +BUNDLE_ID=com.highcapable.flexiui.demo +APP_NAME=FlexiUIDemo diff --git a/samples/iosApp/iosApp.xcodeproj/project.pbxproj b/samples/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8e9c866 --- /dev/null +++ b/samples/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,402 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* FlexiUIDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlexiUIDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F85CB1118929364A9C6EFABC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* FlexiUIDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + 05D91A912A5EF49C00F138EB /* Compile Kotlin */, + 7555FF77242A565900829871 /* Sources */, + 7555FF79242A565900829871 /* Resources */, + F85CB1118929364A9C6EFABC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* FlexiUIDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1130; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 05D91A912A5EF49C00F138EB /* Compile Kotlin */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"$(dirname \"$(dirname \"$SRCROOT\")\")\"\n./gradlew :samples:shared:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = SJ5C2V6386; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)\n", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + "shared\n$(inherited)", + "-framework", + "shared\n", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.highcapable.flexiui.demo; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = SJ5C2V6386; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)\n", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + "shared\n$(inherited)", + "-framework", + "shared\n", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.highcapable.flexiui.demo; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} diff --git a/samples/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/samples/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/samples/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/samples/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/samples/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/samples/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/samples/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..8edf56e --- /dev/null +++ b/samples/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/samples/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000..53fc536 Binary files /dev/null and b/samples/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png differ diff --git a/samples/iosApp/iosApp/Assets.xcassets/Contents.json b/samples/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/samples/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/iosApp/iosApp/ContentView.swift b/samples/iosApp/iosApp/ContentView.swift new file mode 100644 index 0000000..82a9037 --- /dev/null +++ b/samples/iosApp/iosApp/ContentView.swift @@ -0,0 +1,19 @@ +import UIKit +import SwiftUI +import shared + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + let viewController = App_iosKt.createUIViewController() + return viewController + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea(.all, edges: .bottom) // Compose has own keyboard handler + } +} diff --git a/samples/iosApp/iosApp/Info.plist b/samples/iosApp/iosApp/Info.plist new file mode 100644 index 0000000..9a6090f --- /dev/null +++ b/samples/iosApp/iosApp/Info.plist @@ -0,0 +1,52 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + FlexiUI Demo + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/samples/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/samples/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/samples/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/samples/iosApp/iosApp/iOSApp.swift b/samples/iosApp/iosApp/iOSApp.swift new file mode 100644 index 0000000..b7bf2f4 --- /dev/null +++ b/samples/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/samples/shared/.gitignore b/samples/shared/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/shared/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/shared/build.gradle.kts b/samples/shared/build.gradle.kts new file mode 100644 index 0000000..2fb90a2 --- /dev/null +++ b/samples/shared/build.gradle.kts @@ -0,0 +1,80 @@ +plugins { + autowire(libs.plugins.kotlin.multiplatform) + autowire(libs.plugins.android.library) + autowire(libs.plugins.jetbrains.compose) +} + +group = property.project.sharedApp.packageName + +kotlin { + androidTarget() + jvm("desktop") + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64(), + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = projects.samples.shared.name + isStatic = true + } + } + jvmToolchain(17) + sourceSets { + val commonMain by getting { + dependencies { + implementation(compose.runtime) + implementation(compose.foundation) + implementation(projects.flexiuiCore) + } + } + val androidMain by getting { + dependencies { + api(compose.foundation) + api(com.highcapable.betterandroid.ui.component) + api(com.highcapable.betterandroid.ui.extension) + api(com.highcapable.betterandroid.system.extension) + api(androidx.core.core.ktx) + api(androidx.appcompat.appcompat) + api(androidx.activity.activity) + api(androidx.activity.activity.compose) + } + } + val desktopMain by getting { + dependencies { + api(compose.desktop.currentOs) + api(compose.runtime) + api(compose.foundation) + } + } + 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.sharedApp.packageName + 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 +} \ No newline at end of file diff --git a/samples/shared/src/androidMain/kotlin/App.android.kt b/samples/shared/src/androidMain/kotlin/App.android.kt new file mode 100644 index 0000000..6cf792a --- /dev/null +++ b/samples/shared/src/androidMain/kotlin/App.android.kt @@ -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. + */ + +import androidx.compose.runtime.Composable + +@Composable +fun MainView() = App() \ No newline at end of file diff --git a/samples/shared/src/commonMain/kotlin/App.kt b/samples/shared/src/commonMain/kotlin/App.kt new file mode 100644 index 0000000..8e38fc5 --- /dev/null +++ b/samples/shared/src/commonMain/kotlin/App.kt @@ -0,0 +1,147 @@ +/* + * 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. + */ + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.highcapable.flexiui.Colors +import com.highcapable.flexiui.FlexiTheme +import com.highcapable.flexiui.blueColors +import com.highcapable.flexiui.component.AreaBox +import com.highcapable.flexiui.component.Button +import com.highcapable.flexiui.component.Surface +import com.highcapable.flexiui.component.Text +import com.highcapable.flexiui.component.TextField +import com.highcapable.flexiui.defaultColors +import com.highcapable.flexiui.greenColors +import com.highcapable.flexiui.orangeColors +import com.highcapable.flexiui.pinkColors +import com.highcapable.flexiui.purpleColors +import com.highcapable.flexiui.redColors +import com.highcapable.flexiui.yellowColors + +@Composable +fun App() { + val themeColor = remember { mutableStateOf(greenColors()) } + FlexiTheme(colors = themeColor.value) { + Surface { + var greeting by remember { mutableStateOf("Hello World!") } + var input by remember { mutableStateOf("") } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + AreaBox(modifier = Modifier.weight(1f)) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = greeting) + Spacer(Modifier.padding(15.dp)) + Button(onClick = { greeting = "Hello Jetpack Compose Multiplatform!" }) { + Text(text = "Greeting") + } + Spacer(Modifier.padding(15.dp)) + TextField( + value = input, + placeholder = { Text(text = "Type something here...") }, + onValueChange = { input = it } + ) + } + } + Spacer(Modifier.padding(10.dp)) + ThemeTestLayout(themeColor) + } + } + } +} + +@Composable +private fun ThemeTestLayout(themeColor: MutableState) { + Text(text = "Here are some theme color tests.") + Spacer(Modifier.padding(10.dp)) + Row { + Button(onClick = { themeColor.value = defaultColors() }) { + Text(text = "Default") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = defaultColors(darkMode = true) }) { + Text(text = "Default (Dark)") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = defaultColors(darkMode = true, blackDarkMode = true) }) { + Text(text = "Default (Black)") + } + } + Spacer(Modifier.padding(10.dp)) + Row { + Button(onClick = { themeColor.value = redColors() }) { + Text(text = "Red") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = pinkColors() }) { + Text(text = "Pink") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = purpleColors() }) { + Text(text = "Purple") + } + } + Spacer(Modifier.padding(10.dp)) + Row { + Button(onClick = { themeColor.value = greenColors() }) { + Text(text = "Green") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = orangeColors() }) { + Text(text = "Orange") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = yellowColors() }) { + Text(text = "Yellow") + } + } + Spacer(Modifier.padding(10.dp)) + Row { + Button(onClick = { themeColor.value = blueColors() }) { + Text(text = "Blue") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = greenColors(darkMode = true) }) { + Text(text = "Green (Dark)") + } + Spacer(Modifier.padding(10.dp)) + Button(onClick = { themeColor.value = greenColors(darkMode = true, blackDarkMode = true) }) { + Text(text = "Green (Black)") + } + } +} \ No newline at end of file diff --git a/samples/shared/src/desktopMain/kotlin/App.desktop.kt b/samples/shared/src/desktopMain/kotlin/App.desktop.kt new file mode 100644 index 0000000..6cf792a --- /dev/null +++ b/samples/shared/src/desktopMain/kotlin/App.desktop.kt @@ -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. + */ + +import androidx.compose.runtime.Composable + +@Composable +fun MainView() = App() \ No newline at end of file diff --git a/samples/shared/src/iosMain/kotlin/App.ios.kt b/samples/shared/src/iosMain/kotlin/App.ios.kt new file mode 100644 index 0000000..b9b1d7e --- /dev/null +++ b/samples/shared/src/iosMain/kotlin/App.ios.kt @@ -0,0 +1,30 @@ +/* + * 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") + +import androidx.compose.runtime.Composable +import androidx.compose.ui.window.ComposeUIViewController + +fun createUIViewController() = ComposeUIViewController { MainView() } + +@Composable +fun MainView() = App() \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..5ef8ddf --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,20 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +plugins { + id("com.highcapable.sweetdependency") version "1.0.3" + id("com.highcapable.sweetproperty") version "1.0.5" +} +sweetProperty { + global { sourcesCode { className = "FlexiUI" } } + rootProject { all { isEnable = false } } + project(":samples", ":samples:androidApp", ":samples:desktopApp", ":samples:shared") { sourcesCode { isEnable = false } } +} +rootProject.name = "FlexiUI" +include(":samples:androidApp", ":samples:desktopApp", ":samples:shared") +include(":flexiui-core") \ No newline at end of file