├── .gitignore
├── .idea
├── .gitignore
├── gradle.xml
├── icon.png
├── inspectionProfiles
│ └── Project_Default.xml
├── runConfigurations
│ ├── Gallery__Android_.xml
│ ├── Gallery__JS_Build_.xml
│ ├── Gallery__JVM_.xml
│ └── Gallery__WASM_Build_.xml
└── vcs.xml
├── LICENSE
├── README.en-US.md
├── README.md
├── build.gradle.kts
├── gallery
├── build.gradle.kts
└── src
│ ├── androidMain
│ ├── AndroidManifest.xml
│ ├── kotlin
│ │ └── vip
│ │ │ └── cdms
│ │ │ └── orecompose
│ │ │ └── gallery
│ │ │ └── MainActivity.kt
│ └── res
│ │ └── mipmap
│ │ └── ic_launcher.png
│ ├── commonMain
│ ├── composeResources
│ │ ├── drawable
│ │ │ └── orecompose_logo.png
│ │ └── font
│ │ │ └── HarmonyOS_Sans_SC_Regular.ttf
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── gallery
│ │ ├── App.kt
│ │ └── ui
│ │ └── Theme.kt
│ ├── jsMain
│ ├── kotlin
│ │ └── vip
│ │ │ └── cdms
│ │ │ └── orecompose
│ │ │ └── gallery
│ │ │ └── main.kt
│ └── resources
│ │ └── index.html
│ ├── jvmMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── gallery
│ │ └── main.kt
│ ├── wasmJsMain
│ ├── kotlin
│ │ └── vip
│ │ │ └── cdms
│ │ │ └── orecompose
│ │ │ └── gallery
│ │ │ └── main.kt
│ └── resources
│ │ └── index.html
│ └── webMain
│ └── resources
│ └── main.css
├── gradle.properties
├── gradle
├── kotlin-js-store
│ └── yarn.lock
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── oreui-panorama
├── build.gradle.kts
└── src
│ ├── androidMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── layout
│ │ └── panorama
│ │ ├── GlPanoramaView.kt
│ │ └── Panorama.android.kt
│ ├── commonMain
│ ├── composeResources
│ │ └── drawable
│ │ │ └── panorama_default.png
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── layout
│ │ └── panorama
│ │ ├── FastMath.kt
│ │ ├── OrePanoramaModule.kt
│ │ ├── Panorama.kt
│ │ └── PanoramaSoft.kt
│ ├── jvmMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── layout
│ │ └── panorama
│ │ └── Panorama.jvm.kt
│ ├── nonWebMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── layout
│ │ └── panorama
│ │ └── OrePanoramaModule.nonWeb.kt
│ ├── skikoMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── layout
│ │ └── panorama
│ │ ├── PanoramaSkiko.kt
│ │ └── SkShader.kt
│ └── webMain
│ └── kotlin
│ └── vip
│ └── cdms
│ └── orecompose
│ └── layout
│ └── panorama
│ ├── OrePanoramaModule.web.kt
│ └── Panorama.web.kt
├── oreui
├── build.gradle.kts
└── src
│ ├── androidMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ ├── style
│ │ └── OreTheme.android.kt
│ │ └── utils
│ │ └── PlatformUtils.android.kt
│ ├── commonMain
│ ├── composeResources
│ │ └── font
│ │ │ └── Minecraft.ttf
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ ├── components
│ │ ├── Input.kt
│ │ └── Label.kt
│ │ ├── effect
│ │ ├── Outline.kt
│ │ ├── Pixel.kt
│ │ └── Sound.kt
│ │ ├── style
│ │ ├── McFonts.kt
│ │ ├── OreColors.kt
│ │ ├── OreTheme.kt
│ │ └── ZIndices.kt
│ │ └── utils
│ │ ├── Accessibility.kt
│ │ ├── ColorUtils.kt
│ │ ├── ComposeNester.kt
│ │ ├── DrawUtils.kt
│ │ ├── FontsFallback.kt
│ │ ├── LocalUtils.kt
│ │ ├── McFormat.kt
│ │ ├── McFormatter.kt
│ │ ├── PlatformUtils.kt
│ │ └── UnitUtils.kt
│ ├── jsMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── utils
│ │ ├── PlatformUtils.js.kt
│ │ └── ResourcePreloader.js.kt
│ ├── jvmMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ ├── style
│ │ └── OreTheme.jvm.kt
│ │ └── utils
│ │ └── PlatformUtils.jvm.kt
│ ├── wasmJsMain
│ └── kotlin
│ │ └── vip
│ │ └── cdms
│ │ └── orecompose
│ │ └── utils
│ │ ├── PlatformUtils.wasmJs.kt
│ │ └── ResourcePreloader.wasmJs.kt
│ └── webMain
│ └── kotlin
│ └── vip
│ └── cdms
│ └── orecompose
│ ├── style
│ └── OreTheme.web.kt
│ └── utils
│ └── ResourcePreloader.kt
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | ### IntelliJ IDEA ###
2 | .idea/modules
3 | .idea/modules.xml
4 | .idea/jarRepositories.xml
5 | .idea/compiler.xml
6 | .idea/kotlinc.xml
7 | .idea/libraries/
8 | .idea/artifacts
9 | .idea/*.iml
10 | .idea/misc.xml
11 |
12 | ### Kotlin ###
13 | /.kotlin
14 |
15 | ### Gradle ###
16 | .gradle
17 | build/
18 | local.properties
19 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/.idea/icon.png
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Gallery__Android_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Gallery__JS_Build_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Gallery__JVM_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | false
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Gallery__WASM_Build_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 | true
20 | false
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.en-US.md:
--------------------------------------------------------------------------------
1 | 
2 | # OreCompose
3 | [](README.md)
4 | > [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/) for Minecraft Bedrock Edition's **New UI** Style ✨
5 |
6 | **High** Decoupling, **Strong** Compatibility, **Pixel-Perfect** Restoration ⚡⚡
7 |
8 | ```
9 | Copyright 2024 Cdm2883
10 |
11 | Licensed under the Apache License, Version 2.0 (the "License");
12 | you may not use this file except in compliance with the License.
13 | You may obtain a copy of the License at
14 |
15 | http://www.apache.org/licenses/LICENSE-2.0
16 |
17 | Unless required by applicable law or agreed to in writing, software
18 | distributed under the License is distributed on an "AS IS" BASIS,
19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | See the License for the specific language governing permissions and
21 | limitations under the License.
22 | ```
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # OreCompose
3 | [](README.en-US.md)
4 | [](https://qm.qq.com/cgi-bin/qm/qr?k=Vcspr10ZnFcPd29hgPUgmxcBPsPni6pC&jump_from=webapi&authKey=uKMeRLS8aWCJwEXlYmkKeg1aBg4xmt/UbdXvEejPDOqCxhIKpqLK8Q+qG4ZFl0Nn)
5 | > [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/) 类《我的世界》基岩版**新 UI 风格**组件库 ✨
6 |
7 | 高解耦,强兼容,像素级还原 ⚡⚡
8 |
9 | ```
10 | Copyright 2024 Cdm2883
11 |
12 | Licensed under the Apache License, Version 2.0 (the "License");
13 | you may not use this file except in compliance with the License.
14 | You may obtain a copy of the License at
15 |
16 | http://www.apache.org/licenses/LICENSE-2.0
17 |
18 | Unless required by applicable law or agreed to in writing, software
19 | distributed under the License is distributed on an "AS IS" BASIS,
20 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | See the License for the specific language governing permissions and
22 | limitations under the License.
23 | ```
24 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
2 | import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
3 |
4 | plugins {
5 | // this is necessary to avoid the plugins to be loaded multiple times
6 | // in each subproject's classloader
7 | alias(libs.plugins.android.application) apply false
8 | alias(libs.plugins.android.library) apply false
9 | alias(libs.plugins.compose.multiplatform) apply false
10 | alias(libs.plugins.compose.compiler) apply false
11 | alias(libs.plugins.kotlin.multiplatform) apply false
12 | }
13 |
14 | version = libs.versions.orecompose.lib.version.get()
15 |
16 | plugins.withType {
17 | the().lockFileDirectory =
18 | rootDir.resolve("gradle/kotlin-js-store")
19 | }
20 |
--------------------------------------------------------------------------------
/gallery/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat
2 | import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
3 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
4 | import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
5 |
6 | plugins {
7 | alias(libs.plugins.kotlin.multiplatform)
8 | alias(libs.plugins.android.application)
9 | alias(libs.plugins.compose.multiplatform)
10 | alias(libs.plugins.compose.compiler)
11 | }
12 |
13 | kotlin {
14 | androidTarget {
15 | compilerOptions {
16 | jvmTarget.set(JvmTarget.JVM_11)
17 | }
18 | }
19 |
20 | jvm()
21 |
22 | @OptIn(ExperimentalWasmDsl::class)
23 | wasmJs {
24 | moduleName = "composeApp"
25 | browser {
26 | val rootDirPath = project.rootDir.path
27 | val projectDirPath = project.projectDir.path
28 | commonWebpackConfig {
29 | outputFileName = "composeApp.js"
30 | devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
31 | static = (static ?: mutableListOf()).apply {
32 | // Serve sources to debug inside browser
33 | add(rootDirPath)
34 | add(projectDirPath)
35 | }
36 | }
37 | }
38 | }
39 | binaries.executable()
40 | }
41 | js(IR) {
42 | moduleName = "composeApp"
43 | browser {
44 | commonWebpackConfig {
45 | outputFileName = "composeApp.js"
46 | }
47 | }
48 | binaries.executable()
49 | }
50 |
51 | applyDefaultHierarchyTemplate()
52 |
53 | sourceSets {
54 | commonMain.dependencies {
55 | implementation(compose.runtime)
56 | implementation(compose.ui)
57 | implementation(compose.foundation)
58 | implementation(compose.material)
59 | implementation(compose.components.resources)
60 | implementation(compose.components.uiToolingPreview)
61 | implementation(projects.oreui)
62 | implementation(projects.oreuiPanorama)
63 | }
64 | androidMain.dependencies {
65 | implementation(libs.androidx.activity.compose)
66 | }
67 | jvmMain.dependencies {
68 | implementation(compose.desktop.currentOs)
69 | implementation(libs.mayakapps.compose.window.styler)
70 | }
71 |
72 | val webMain by creating
73 | webMain.dependsOn(commonMain.get())
74 | jsMain.get().dependsOn(webMain)
75 | wasmJsMain.get().dependsOn(webMain)
76 | }
77 | }
78 |
79 | android {
80 | namespace = "vip.cdms.orecompose.gallery"
81 | compileSdk = libs.versions.android.sdk.compile.get().toInt()
82 |
83 | sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
84 | sourceSets["main"].res.srcDirs("src/androidMain/res")
85 | sourceSets["main"].resources.srcDirs("src/commonMain/resources")
86 |
87 | defaultConfig {
88 | applicationId = "vip.cdms.orecompose.gallery"
89 | minSdk = libs.versions.android.sdk.min.get().toInt()
90 | targetSdk = libs.versions.android.sdk.target.get().toInt()
91 | }
92 | packaging {
93 | resources {
94 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
95 | }
96 | }
97 | compileOptions {
98 | sourceCompatibility = JavaVersion.VERSION_11
99 | targetCompatibility = JavaVersion.VERSION_11
100 | }
101 | dependencies {
102 | debugImplementation(compose.uiTooling)
103 | }
104 | }
105 |
106 | compose.desktop {
107 | application {
108 | mainClass = "vip.cdms.orecompose.gallery.MainKt"
109 |
110 | nativeDistributions {
111 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
112 | packageName = "OreCompose Gallery"
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/gallery/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gallery/src/androidMain/kotlin/vip/cdms/orecompose/gallery/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.gallery
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 |
8 | class MainActivity : ComponentActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 |
12 | enableEdgeToEdge()
13 | setContent {
14 | App()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/gallery/src/androidMain/res/mipmap/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/gallery/src/androidMain/res/mipmap/ic_launcher.png
--------------------------------------------------------------------------------
/gallery/src/commonMain/composeResources/drawable/orecompose_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/gallery/src/commonMain/composeResources/drawable/orecompose_logo.png
--------------------------------------------------------------------------------
/gallery/src/commonMain/composeResources/font/HarmonyOS_Sans_SC_Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/gallery/src/commonMain/composeResources/font/HarmonyOS_Sans_SC_Regular.ttf
--------------------------------------------------------------------------------
/gallery/src/commonMain/kotlin/vip/cdms/orecompose/gallery/App.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.gallery
2 |
3 | import androidx.compose.foundation.LocalIndication
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.clickable
6 | import androidx.compose.foundation.interaction.MutableInteractionSource
7 | import androidx.compose.foundation.layout.*
8 | import androidx.compose.runtime.*
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.layout.boundsInParent
13 | import androidx.compose.ui.layout.onGloballyPositioned
14 | import androidx.compose.ui.semantics.Role
15 | import androidx.compose.ui.semantics.role
16 | import androidx.compose.ui.semantics.semantics
17 | import androidx.compose.ui.unit.dp
18 | import vip.cdms.orecompose.components.Label
19 | import vip.cdms.orecompose.effect.LocalOutlineColor
20 | import vip.cdms.orecompose.effect.LocalOutlineWidth
21 | import vip.cdms.orecompose.effect.outline
22 | import vip.cdms.orecompose.gallery.ui.GalleryTheme
23 | import vip.cdms.orecompose.layout.panorama.Panorama
24 | import vip.cdms.orecompose.utils.accessibilityIndicator
25 | import vip.cdms.orecompose.utils.toPx
26 |
27 | @Composable
28 | fun App() {
29 | GalleryTheme {
30 | CompositionLocalProvider(
31 | LocalOutlineColor provides Color.LightGray,
32 | ) {
33 | var screenWidth by remember { mutableStateOf(Float.MIN_VALUE) }
34 | Box(
35 | Modifier
36 | .fillMaxSize()
37 | .onGloballyPositioned { screenWidth = it.boundsInParent().width },
38 | contentAlignment = Alignment.Center
39 | ) {
40 | Panorama()
41 | Column {
42 | Label(
43 | "OreCompose! 你好世界!",
44 | color = Color.White,
45 | fontSize = (screenWidth / 16).toPx()
46 | )
47 | Row(horizontalArrangement = Arrangement.spacedBy(-LocalOutlineWidth.current.toDp())) {
48 | @Composable
49 | fun Modifier.buttonLike(onClick: () -> Unit) = remember { MutableInteractionSource() }.let { interactionSource ->
50 | semantics { role = Role.Button }
51 | .accessibilityIndicator(interactionSource)
52 | .clickable(
53 | interactionSource = interactionSource,
54 | indication = LocalIndication.current,
55 | onClick = onClick
56 | )
57 | }
58 | Spacer(Modifier.size(40.dp)
59 | .background(Color.Yellow)
60 | .buttonLike {})
61 | Spacer(Modifier.size(10.dp))
62 | Spacer(Modifier.size(40.dp)
63 | .background(Color.Red)
64 | .outline()
65 | .buttonLike {})
66 | Spacer(Modifier.size(40.dp)
67 | .background(Color.Green)
68 | .outline()
69 | .buttonLike {})
70 | Spacer(Modifier.size(40.dp)
71 | .background(Color.Blue)
72 | .outline()
73 | .buttonLike {})
74 | }
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/gallery/src/commonMain/kotlin/vip/cdms/orecompose/gallery/ui/Theme.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.gallery.ui
2 |
3 | import androidx.compose.material.ProvideTextStyle
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.text.TextStyle
6 | import androidx.compose.ui.text.font.FontFamily
7 | import orecompose.gallery.generated.resources.HarmonyOS_Sans_SC_Regular
8 | import orecompose.gallery.generated.resources.Res
9 | import org.jetbrains.compose.resources.Font
10 | import vip.cdms.orecompose.layout.panorama.OrePanoramaModule
11 | import vip.cdms.orecompose.style.OreModule
12 | import vip.cdms.orecompose.style.OreTheme
13 |
14 | private val TextFontModule = OreModule.wrap {
15 | val fontFamily = FontFamily(Font(Res.font.HarmonyOS_Sans_SC_Regular))
16 | ProvideTextStyle(TextStyle(fontFamily = fontFamily), content = it)
17 | }
18 |
19 | @Composable
20 | fun GalleryTheme(content: @Composable () -> Unit) {
21 | OreTheme(
22 | modules = arrayOf(
23 | OrePanoramaModule,
24 | TextFontModule,
25 | ),
26 | content = content
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/gallery/src/jsMain/kotlin/vip/cdms/orecompose/gallery/main.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.gallery
2 |
3 | import androidx.compose.ui.ExperimentalComposeUiApi
4 | import androidx.compose.ui.window.ComposeViewport
5 | import kotlinx.browser.document
6 | import orecompose.gallery.generated.resources.HarmonyOS_Sans_SC_Regular
7 | import orecompose.gallery.generated.resources.Res
8 | import org.jetbrains.skiko.wasm.onWasmReady
9 | import vip.cdms.orecompose.utils.PreloadResources
10 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.resourceUrl
11 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.ttf
12 |
13 | @OptIn(ExperimentalComposeUiApi::class)
14 | fun main() {
15 | onWasmReady {
16 | ComposeViewport(document.body!!) {
17 | val packaging = "orecompose.gallery.generated.resources"
18 | PreloadResources(
19 | Res.font.resourceUrl(packaging) { ::HarmonyOS_Sans_SC_Regular.ttf },
20 | ) {
21 | App()
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/gallery/src/jsMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | OreCompose Gallery (JS)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/gallery/src/jvmMain/kotlin/vip/cdms/orecompose/gallery/main.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.gallery
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.ui.window.Window
5 | import androidx.compose.ui.window.application
6 | import com.mayakapps.compose.windowstyler.WindowBackdrop
7 | import com.mayakapps.compose.windowstyler.WindowStyle
8 | import orecompose.gallery.generated.resources.Res
9 | import orecompose.gallery.generated.resources.orecompose_logo
10 | import org.jetbrains.compose.resources.painterResource
11 |
12 | fun main() = application {
13 | Window(
14 | ::exitApplication,
15 | title = "OreCompose Gallery",
16 | icon = painterResource(Res.drawable.orecompose_logo)
17 | ) {
18 | WindowStyle(
19 | isDarkTheme = isSystemInDarkTheme(),
20 | backdropType = WindowBackdrop.Mica,
21 | )
22 | App()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/gallery/src/wasmJsMain/kotlin/vip/cdms/orecompose/gallery/main.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.gallery
2 |
3 | import androidx.compose.ui.ExperimentalComposeUiApi
4 | import androidx.compose.ui.window.ComposeViewport
5 | import kotlinx.browser.document
6 | import orecompose.gallery.generated.resources.HarmonyOS_Sans_SC_Regular
7 | import orecompose.gallery.generated.resources.Res
8 | import vip.cdms.orecompose.utils.PreloadResources
9 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.resourceUrl
10 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.ttf
11 |
12 | @OptIn(ExperimentalComposeUiApi::class)
13 | fun main() {
14 | ComposeViewport(document.body!!) {
15 | val packaging = "orecompose.gallery.generated.resources"
16 | PreloadResources(
17 | Res.font.resourceUrl(packaging) { ::HarmonyOS_Sans_SC_Regular.ttf },
18 | ) {
19 | App()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/gallery/src/wasmJsMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | OreCompose Gallery (WASM)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/gallery/src/webMain/resources/main.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | height: 100%;
4 | margin: 0;
5 | padding: 0;
6 | overflow: hidden;
7 | }
8 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Kotlin
2 | kotlin.code.style=official
3 | kotlin.daemon.jvmargs=-Xmx2048M
4 | kotlin.compiler.preciseCompilationResultsBackup=true
5 |
6 | # Compose
7 | org.jetbrains.compose.experimental.jscanvas.enabled=true
8 |
9 | # Gradle
10 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
11 | org.gradle.caching=true
12 | org.gradle.parallel=true
13 |
14 | # Android
15 | android.nonTransitiveRClass=true
16 | android.useAndroidX=true
17 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | orecompose-lib-version = "0.0.1-alpha"
3 |
4 | android-gradle-plugin = "8.5.2"
5 | android-sdk-compile = "35"
6 | android-sdk-min = "24"
7 | android-sdk-target = "35"
8 | compose-multiplatform = "1.7.3"
9 | kotlin = "2.1.10"
10 |
11 | androidx-activity-compose = "1.10.0"
12 | kilua = "0.0.16"
13 | jetbrains-annotations = "26.0.2"
14 |
15 | mayakapps-compose-window-styler = "0.3.3-SNAPSHOT"
16 |
17 | [libraries]
18 | androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" }
19 | kilua-dom = { module = "dev.kilua:kilua-dom", version.ref = "kilua" }
20 | jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
21 |
22 | # gallery
23 | mayakapps-compose-window-styler = { module = "com.mayakapps.compose:window-styler", version.ref = "mayakapps-compose-window-styler" }
24 |
25 | [plugins]
26 | android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
27 | android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
28 | compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
29 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
30 | kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
31 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/oreui-panorama/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
3 |
4 | plugins {
5 | alias(libs.plugins.kotlin.multiplatform)
6 | alias(libs.plugins.android.library)
7 | alias(libs.plugins.compose.multiplatform)
8 | alias(libs.plugins.compose.compiler)
9 | }
10 |
11 | kotlin {
12 | androidTarget {
13 | compilerOptions {
14 | jvmTarget.set(JvmTarget.JVM_11)
15 | }
16 | }
17 |
18 | jvm()
19 |
20 | @OptIn(ExperimentalWasmDsl::class)
21 | wasmJs {
22 | browser()
23 | }
24 |
25 | js {
26 | browser()
27 | }
28 |
29 | applyDefaultHierarchyTemplate()
30 |
31 | sourceSets {
32 | commonMain.dependencies {
33 | implementation(compose.runtime)
34 | implementation(compose.ui)
35 | implementation(compose.foundation)
36 | implementation(compose.components.resources)
37 | compileOnly(libs.jetbrains.annotations)
38 | implementation(projects.oreui)
39 | }
40 |
41 | val nonWebMain by creating {
42 | dependsOn(commonMain.get())
43 | }
44 |
45 | androidMain {
46 | dependsOn(nonWebMain)
47 | dependencies {
48 | api(libs.jetbrains.annotations)
49 | //noinspection UseTomlInstead
50 | implementation("com.github.androidZzT:VRPanoramaView:1.0.1")
51 | }
52 | }
53 |
54 | val skikoMain by creating {
55 | dependsOn(commonMain.get())
56 | }
57 | jvmMain {
58 | dependsOn(nonWebMain)
59 | dependsOn(skikoMain)
60 | dependencies {
61 | implementation(compose.desktop.common)
62 | }
63 | }
64 |
65 | val webMain by creating {
66 | dependsOn(skikoMain)
67 | dependencies {
68 | api(libs.jetbrains.annotations)
69 | implementation(libs.kilua.dom)
70 | }
71 | }
72 | wasmJsMain {
73 | dependsOn(webMain)
74 | }
75 | jsMain {
76 | dependsOn(webMain)
77 | }
78 | }
79 |
80 | compilerOptions.freeCompilerArgs.addAll(
81 | "-Xexpect-actual-classes",
82 | )
83 | }
84 |
85 | android {
86 | namespace = "vip.cdms.orecompose.layout.panorama"
87 | compileSdk = libs.versions.android.sdk.compile.get().toInt()
88 | defaultConfig {
89 | minSdk = libs.versions.android.sdk.min.get().toInt()
90 | }
91 | compileOptions {
92 | sourceCompatibility = JavaVersion.VERSION_11
93 | targetCompatibility = JavaVersion.VERSION_11
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/oreui-panorama/src/androidMain/kotlin/vip/cdms/orecompose/layout/panorama/GlPanoramaView.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.graphics.SurfaceTexture
6 | import android.util.AttributeSet
7 | import android.view.TextureView
8 | import android.widget.FrameLayout
9 | import com.zzt.panorama.cg.GLProducerThread
10 | import com.zzt.panorama.sphere.SphereRenderer
11 | import java.util.concurrent.atomic.AtomicBoolean
12 |
13 | class GlPanoramaView @JvmOverloads constructor(
14 | context: Context, attrs: AttributeSet? = null
15 | ) : FrameLayout(context, attrs), TextureView.SurfaceTextureListener {
16 | var onGLThreadReady = {}
17 | lateinit var bitmap: Bitmap
18 |
19 | private val view by lazy { TextureView(context) }
20 | val renderer by lazy { SphereRenderer(context) }
21 |
22 | private val rendererBiasMatrixField by lazy {
23 | SphereRenderer::class.java.getDeclaredField("mBiasMatrix")
24 | .apply { isAccessible = true }
25 | }
26 | val rendererBiasMatrix
27 | get() = rendererBiasMatrixField.get(renderer) as FloatArray
28 |
29 | // val camera by lazy {
30 | // SphereRenderer::class.java.getDeclaredField("mCamera")
31 | // .apply { isAccessible = true }
32 | // .get(renderer) as Camera
33 | // }
34 |
35 | private var isGLThreadAvailable = false
36 | var glThread: GLProducerThread? = null
37 |
38 | init {
39 | renderer.enableGyroTracking(false)
40 | view.surfaceTextureListener = this
41 | addView(view)
42 | }
43 |
44 | override fun onAttachedToWindow() {
45 | super.onAttachedToWindow()
46 | renderer.onAttached()
47 | }
48 |
49 | override fun onDetachedFromWindow() {
50 | super.onDetachedFromWindow()
51 | renderer.onDetached()
52 | glThread?.enqueueEvent { glThread!!.releaseEglContext() }
53 | }
54 |
55 | override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
56 | if (!isGLThreadAvailable) {
57 | isGLThreadAvailable = true
58 | glThread = GLProducerThread(surface, renderer, AtomicBoolean(true))
59 | glThread!!.start()
60 | onGLThreadReady()
61 | glThread!!.enqueueEvent { renderer.onSurfaceChanged(width, height) }
62 | glThread!!.enqueueEvent { renderer.loadBitmap(bitmap) }
63 | } else {
64 | glThread!!.refreshSurfaceTexture(surface)
65 | glThread!!.enqueueEvent { renderer.changeTextureBitmap(bitmap) }
66 | glThread!!.onResume()
67 | }
68 | }
69 |
70 | override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
71 | glThread?.enqueueEvent { renderer.onSurfaceChanged(width, height) }
72 | }
73 |
74 | override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
75 | glThread?.onPause()
76 | return true
77 | }
78 |
79 | override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
80 | }
81 |
--------------------------------------------------------------------------------
/oreui-panorama/src/androidMain/kotlin/vip/cdms/orecompose/layout/panorama/Panorama.android.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import android.app.ActivityManager
4 | import android.content.Context
5 | import android.opengl.Matrix
6 | import android.widget.FrameLayout
7 | import androidx.compose.runtime.*
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.ImageBitmap
10 | import androidx.compose.ui.graphics.asAndroidBitmap
11 | import androidx.compose.ui.platform.LocalContext
12 | import androidx.compose.ui.viewinterop.AndroidView
13 | import com.zzt.panorama.cg.GLProducerThread
14 | import kotlinx.coroutines.Dispatchers
15 | import kotlinx.coroutines.delay
16 | import kotlinx.coroutines.isActive
17 | import kotlinx.coroutines.withContext
18 | import kotlin.time.Duration.Companion.milliseconds
19 |
20 | @Composable
21 | actual fun Panorama(equirectangular: ImageBitmap, modifier: Modifier, rotating: Boolean) {
22 | if (
23 | (LocalContext.current
24 | .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
25 | .deviceConfigurationInfo.reqGlEsVersion >= 0x20000
26 | ) { // support OpenGLES 2.0
27 | var view by remember { mutableStateOf(null) }
28 | AndroidView(
29 | factory = { context ->
30 | GlPanoramaView(context).apply {
31 | layoutParams = FrameLayout.LayoutParams(
32 | FrameLayout.LayoutParams.MATCH_PARENT,
33 | FrameLayout.LayoutParams.MATCH_PARENT
34 | )
35 | onGLThreadReady = {
36 | glThread!!.setRenderMode(if (rotating) GLProducerThread.RENDERMODE_CONTINUOUSLY else GLProducerThread.RENDERMODE_WHEN_DIRTY)
37 | // renderer.reCenter()
38 | }
39 | bitmap = equirectangular.asAndroidBitmap()
40 | }.also { view = it }
41 | },
42 | modifier,
43 | )
44 | if (rotating && view != null) LaunchedEffect(view) {
45 | withContext(Dispatchers.Unconfined) {
46 | while (isActive) {
47 | Matrix.rotateM(view!!.rendererBiasMatrix, 0, .1f, 0f, 1f, 0f)
48 | delay(30.milliseconds)
49 | }
50 | }
51 | }
52 | } else PanoramaSoft(equirectangular, modifier, rotating) // fallback to software rendering
53 | }
54 |
--------------------------------------------------------------------------------
/oreui-panorama/src/commonMain/composeResources/drawable/panorama_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/oreui-panorama/src/commonMain/composeResources/drawable/panorama_default.png
--------------------------------------------------------------------------------
/oreui-panorama/src/commonMain/kotlin/vip/cdms/orecompose/layout/panorama/FastMath.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import kotlin.math.PI
4 | import kotlin.math.abs
5 | import kotlin.math.max
6 | import kotlin.math.min
7 |
8 | fun fastTan(x: Float): Float {
9 | val x2 = x * x
10 | val x3 = x * x2
11 | val x5 = x3 * x2
12 |
13 | return x + x3 / 3 + (2 * x5 / 15)
14 | }
15 |
16 | fun fastAtan2(y: Float, x: Float): Float {
17 | val absX = abs(x)
18 | val absY = abs(y)
19 | val a = min(absX, absY) / max(absX, absY)
20 | val s = a * a
21 | val angle = (((-0.046496473f * s + 0.15931422f) * s - 0.32762277f) * s * a + a)
22 |
23 | return if (absY > absX) {
24 | (PI / 2).toFloat() - angle
25 | } else {
26 | angle
27 | } * if (x < 0) -1 else 1 * if (y < 0) -1 else 1
28 | }
29 |
30 | fun fastAtan(x: Float, terms: Int = 5): Float {
31 | var result = 0f
32 | var power = x
33 | var sign = 1
34 | for (i in 1..terms step 2) {
35 | result += sign * power / i
36 | power *= x * x
37 | sign = -sign
38 | }
39 | return result
40 | }
41 |
--------------------------------------------------------------------------------
/oreui-panorama/src/commonMain/kotlin/vip/cdms/orecompose/layout/panorama/OrePanoramaModule.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.runtime.Composable
4 | import vip.cdms.orecompose.style.OreModule
5 |
6 | /**
7 | * A common [OreModule] for preloading default panorama image.
8 | *
9 | * Attach this module if you use default panorama image
10 | * could resolve some problem like panorama disappearing.
11 | * (Only works for Web)
12 | */
13 | expect object OrePanoramaModule : OreModule {
14 | @Composable
15 | override operator fun invoke(content: @Composable () -> Unit)
16 | }
17 |
--------------------------------------------------------------------------------
/oreui-panorama/src/commonMain/kotlin/vip/cdms/orecompose/layout/panorama/Panorama.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.foundation.layout.fillMaxSize
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.draw.drawWithContent
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.graphics.ImageBitmap
9 | import orecompose.oreui_panorama.generated.resources.Res
10 | import orecompose.oreui_panorama.generated.resources.panorama_default
11 | import org.jetbrains.compose.resources.imageResource
12 | import vip.cdms.orecompose.utils.argb
13 |
14 | object PanoramaDefaults {
15 | val Equirectangular = Res.drawable.panorama_default
16 | val Modifier = androidx.compose.ui.Modifier
17 | .fillMaxSize()
18 | .foreground(0x55000000.argb) // WTF, different platforms with different color ?!
19 | }
20 |
21 | fun Modifier.foreground(color: Color) = drawWithContent {
22 | drawContent()
23 | drawRect(color)
24 | }
25 |
26 | /**
27 | * A panorama viewer with auto rotating like Minecraft UI background.
28 | *
29 | * Could use [PanoramaBE](https://github.com/MineBuilders/PanoramaBE)
30 | * to generate the equirectangular.
31 | */
32 | @Composable
33 | expect fun Panorama(
34 | equirectangular: ImageBitmap = imageResource(PanoramaDefaults.Equirectangular),
35 | modifier: Modifier = PanoramaDefaults.Modifier,
36 | rotating: Boolean = true,
37 | )
38 |
--------------------------------------------------------------------------------
/oreui-panorama/src/commonMain/kotlin/vip/cdms/orecompose/layout/panorama/PanoramaSoft.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.graphics.*
7 | import androidx.compose.ui.layout.ContentScale
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.delay
10 | import kotlinx.coroutines.isActive
11 | import kotlinx.coroutines.withContext
12 | import org.jetbrains.compose.resources.imageResource
13 | import vip.cdms.orecompose.utils.Platform
14 | import vip.cdms.orecompose.utils.RuntimePlatform
15 | import vip.cdms.orecompose.utils.drawPoint
16 | import vip.cdms.orecompose.utils.pixelPaint
17 | import kotlin.math.PI
18 | import kotlin.math.sqrt
19 | import kotlin.time.Duration
20 | import kotlin.time.Duration.Companion.milliseconds
21 | import kotlin.time.Duration.Companion.nanoseconds
22 |
23 | /**
24 | * Panorama fallback with CPU calculation directly.
25 | *
26 | * @see Panorama
27 | */
28 | @Composable
29 | fun PanoramaSoft(
30 | equirectangular: ImageBitmap = imageResource(PanoramaDefaults.Equirectangular),
31 | modifier: Modifier = PanoramaDefaults.Modifier,
32 | rotating: Boolean = true,
33 | period: Duration = if (RuntimePlatform is Platform.Web) 100.milliseconds else 1.nanoseconds,
34 | width: Int = 1500, // 50
35 | height: Int = 750, // 25
36 | ) {
37 | var image by remember { mutableStateOf(null) }
38 |
39 | val origin = remember { 35f * PI.toFloat() / 180 } // magic number
40 | var direction by remember { mutableIntStateOf(1) }
41 | var yaw by remember { mutableFloatStateOf(origin) }
42 | val deg1 = remember { PI.toFloat() / 180 / 5 }
43 | val deg360 = remember { 2 * PI.toFloat() - origin }
44 |
45 | LaunchedEffect(equirectangular) {
46 | val pixels = equirectangular.toPixelMap()
47 | val paint = pixelPaint()
48 | withContext(Dispatchers.Unconfined) {
49 | do {
50 | val bitmap = ImageBitmap(width, height)
51 | val canvas = Canvas(bitmap)
52 | canvas.viewPanoramaSoft(
53 | pixels,
54 | paint,
55 | yaw,
56 | pitch = 0f,
57 | fov = 90f * PI.toFloat() / 180f,
58 | width,
59 | height,
60 | )
61 | canvas.save()
62 | image = bitmap
63 |
64 | val next = yaw + direction * deg1
65 | if (next > deg360 || next < origin) direction *= -1
66 | else yaw = next
67 | delay(period)
68 | } while (rotating && isActive)
69 | }
70 | }
71 |
72 | if (image != null) Image(
73 | bitmap = image!!,
74 | contentDescription = null,
75 | modifier = modifier,
76 | contentScale = ContentScale.Crop,
77 | )
78 | }
79 |
80 | fun Canvas.viewPanoramaSoft(
81 | equirectangular: PixelMap,
82 | paint: Paint,
83 | yaw: Float,
84 | pitch: Float,
85 | fov: Float,
86 | width: Int,
87 | height: Int,
88 | ) {
89 | val halfWidth = width / 2
90 | val halfHeight = height / 2
91 | val focalLength = halfWidth / fastTan(fov / 2)
92 |
93 | for (x in 0 until width) {
94 | for (y in 0 until height) {
95 | val dx = x - halfWidth.toFloat()
96 | val dy = y - halfHeight.toFloat()
97 |
98 | val theta = fastAtan2(dx, focalLength) + yaw
99 | val phi = fastAtan(dy / sqrt(dx * dx + focalLength * focalLength)) + pitch
100 |
101 | val u = theta / (2 * PI)
102 | val v = phi / PI + 0.5
103 |
104 | val srcX = (u * equirectangular.width).toInt().coerceIn(0, equirectangular.width - 1)
105 | val srcY = (v * equirectangular.height).toInt().coerceIn(0, equirectangular.height - 1)
106 | val color = equirectangular[srcX, srcY]
107 |
108 | drawPoint(x.toFloat(), y.toFloat(), color, paint)
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/oreui-panorama/src/jvmMain/kotlin/vip/cdms/orecompose/layout/panorama/Panorama.jvm.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.graphics.ImageBitmap
6 |
7 | @Composable
8 | actual fun Panorama(equirectangular: ImageBitmap, modifier: Modifier, rotating: Boolean) {
9 | PanoramaSkiko(equirectangular, modifier, rotating)
10 | }
11 |
--------------------------------------------------------------------------------
/oreui-panorama/src/nonWebMain/kotlin/vip/cdms/orecompose/layout/panorama/OrePanoramaModule.nonWeb.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.runtime.Composable
4 | import vip.cdms.orecompose.style.OreModule
5 |
6 | actual object OrePanoramaModule : OreModule {
7 | @Composable
8 | actual override operator fun invoke(content: @Composable () -> Unit) {
9 | content()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/oreui-panorama/src/skikoMain/kotlin/vip/cdms/orecompose/layout/panorama/PanoramaSkiko.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.graphics.ImageBitmap
7 | import androidx.compose.ui.graphics.asSkiaBitmap
8 | import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
9 | import androidx.compose.ui.graphics.nativeCanvas
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.delay
12 | import kotlinx.coroutines.isActive
13 | import kotlinx.coroutines.withContext
14 | import org.jetbrains.skia.Paint
15 | import org.jetbrains.skia.Rect
16 | import org.jetbrains.skia.RuntimeEffect
17 | import org.jetbrains.skia.RuntimeShaderBuilder
18 | import vip.cdms.orecompose.utils.Platform
19 | import vip.cdms.orecompose.utils.RuntimePlatform
20 | import kotlin.math.PI
21 | import kotlin.time.Duration
22 | import kotlin.time.Duration.Companion.milliseconds
23 |
24 | /**
25 | * Panorama implement with Skiko.
26 | *
27 | * @see Panorama
28 | */
29 | @Composable
30 | fun PanoramaSkiko(
31 | equirectangular: ImageBitmap,
32 | modifier: Modifier,
33 | rotating: Boolean,
34 | period: Duration = if (RuntimePlatform is Platform.Web) 100.milliseconds else 1.milliseconds,
35 | yawDelta: Float = if (RuntimePlatform is Platform.Web) .001f else .0003f,
36 | // fpsCounter: FPSCounter? = null,
37 | ) {
38 | // val skiaImage = remember(equirectangular) { equirectangular.toAwtImage().toImage() } // JVM ONLY
39 | val skiaImage = remember(equirectangular) { /*Image.makeFromBitmap(*/equirectangular.asSkiaBitmap()/*)*/ }
40 | val imageShader = remember(skiaImage) { skiaImage.makeShader() }
41 |
42 | val effect = remember { RuntimeEffect.makeForShader(Sksl) }
43 | val shaderBuilder = remember { RuntimeShaderBuilder(effect) }
44 |
45 | val paint = remember { Paint()/*.apply { isAntiAlias = true }*/ }
46 |
47 | var yaw by remember { mutableFloatStateOf(0f) }
48 | LaunchedEffect(rotating) {
49 | withContext(Dispatchers.Unconfined) {
50 | while (rotating && isActive) {
51 | yaw += yawDelta
52 | delay(period)
53 | }
54 | }
55 | }
56 |
57 | Canvas(modifier = modifier) {
58 | drawIntoCanvas { canvas ->
59 | val skiaCanvas = canvas.nativeCanvas
60 | val viewportSize = this@Canvas.size
61 |
62 | shaderBuilder.uniform("iResolution", viewportSize.width, viewportSize.height)
63 | shaderBuilder.child("panorama", imageShader)
64 | shaderBuilder.uniform("panoramaSize", skiaImage.width.toFloat(), skiaImage.height.toFloat())
65 | shaderBuilder.uniform("yaw", yaw)
66 | shaderBuilder.uniform("pitch", 0f)
67 | shaderBuilder.uniform("fov", (130 * PI / 180).toFloat())
68 |
69 | paint.shader = shaderBuilder.makeShader()
70 | skiaCanvas.drawRect(Rect.makeXYWH(0f, 0f, viewportSize.width, viewportSize.height), paint)
71 |
72 | // fpsCounter?.tick()
73 | // if (fpsCounter != null) ParagraphBuilder(
74 | // ParagraphStyle(),
75 | // FontCollection().setDefaultFontManager(FontMgr.default)
76 | // )
77 | // .pushStyle(TextStyle().setColor(Color.RED).setFontSize(24f))
78 | // .addText("FPS: ${fpsCounter.average}")
79 | // .popStyle()
80 | // .build()
81 | // .layout(Float.POSITIVE_INFINITY)
82 | // .paint(skiaCanvas, 0f, 0f)
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/oreui-panorama/src/skikoMain/kotlin/vip/cdms/orecompose/layout/panorama/SkShader.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import org.intellij.lang.annotations.Language
4 |
5 | @Language("AGSL") // IDEA has no support for sksl :(
6 | @Suppress("SpellCheckingInspection")
7 | internal val Sksl = """
8 | uniform float2 iResolution;
9 | uniform shader panorama;
10 | uniform float2 panoramaSize;
11 | uniform float yaw;
12 | uniform float pitch;
13 | uniform float fov;
14 |
15 | const float PI = 3.141592653589793;
16 |
17 | half4 main(float2 fragCoord) {
18 | float2 uv = (fragCoord - 0.5 * iResolution) / iResolution.y;
19 |
20 | float fovScale = tan(fov * 0.5);
21 | float3 rd = normalize(float3(uv.x * fovScale, uv.y * fovScale, 1.0));
22 |
23 | float cosPitch = cos(pitch);
24 | float sinPitch = sin(pitch);
25 | float3 rd1 = float3(rd.x,
26 | rd.y * cosPitch - rd.z * sinPitch,
27 | rd.y * sinPitch + rd.z * cosPitch);
28 |
29 | float cosYaw = cos(yaw);
30 | float sinYaw = sin(yaw);
31 | float3 rdRot = float3(rd1.x * cosYaw + rd1.z * sinYaw,
32 | rd1.y,
33 | -rd1.x * sinYaw + rd1.z * cosYaw);
34 |
35 | float theta = atan(rdRot.z, rdRot.x);
36 | float phi = asin(clamp(rdRot.y, -1.0, 1.0));
37 |
38 | float u = (theta + PI) / (2.0 * PI);
39 | float v = (phi + 0.5 * PI) / PI;
40 | // float v = 1.0 - ((phi + 0.5 * PI) / PI); // flip Y
41 |
42 | float2 panoramaCoord = float2(u * panoramaSize.x, v * panoramaSize.y);
43 | return panorama.eval(panoramaCoord);
44 | }
45 | """.trimIndent()
46 |
--------------------------------------------------------------------------------
/oreui-panorama/src/webMain/kotlin/vip/cdms/orecompose/layout/panorama/OrePanoramaModule.web.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.runtime.Composable
4 | import orecompose.oreui_panorama.generated.resources.Res
5 | import orecompose.oreui_panorama.generated.resources.panorama_default
6 | import vip.cdms.orecompose.style.OreModule
7 | import vip.cdms.orecompose.utils.PreloadResources
8 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.png
9 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.resourceUrl
10 |
11 | actual object OrePanoramaModule : OreModule {
12 | @Composable
13 | actual override operator fun invoke(content: @Composable () -> Unit) {
14 | val packaging = "orecompose.oreui_panorama.generated.resources"
15 | PreloadResources(
16 | Res.drawable.resourceUrl(packaging) { ::panorama_default.png },
17 | ) {
18 | content()
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/oreui-panorama/src/webMain/kotlin/vip/cdms/orecompose/layout/panorama/Panorama.web.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.layout.panorama
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.graphics.ImageBitmap
6 |
7 | @Composable
8 | actual fun Panorama(equirectangular: ImageBitmap, modifier: Modifier, rotating: Boolean) {
9 | PanoramaSkiko(equirectangular, modifier, rotating)
10 | }
11 |
--------------------------------------------------------------------------------
/oreui/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
3 |
4 | plugins {
5 | alias(libs.plugins.kotlin.multiplatform)
6 | alias(libs.plugins.android.library)
7 | alias(libs.plugins.compose.multiplatform)
8 | alias(libs.plugins.compose.compiler)
9 | }
10 |
11 | kotlin {
12 | androidTarget {
13 | compilerOptions {
14 | jvmTarget.set(JvmTarget.JVM_11)
15 | }
16 | }
17 |
18 | jvm()
19 |
20 | @OptIn(ExperimentalWasmDsl::class)
21 | wasmJs {
22 | browser()
23 | }
24 |
25 | js {
26 | browser()
27 | }
28 |
29 | applyDefaultHierarchyTemplate()
30 |
31 | sourceSets {
32 | commonMain.dependencies {
33 | implementation(compose.runtime)
34 | implementation(compose.ui)
35 | implementation(compose.foundation)
36 | implementation(compose.material)
37 | implementation(compose.components.resources)
38 | }
39 |
40 | jvmMain.dependencies {
41 | implementation(compose.desktop.common)
42 | }
43 |
44 | val webMain by creating {
45 | dependsOn(commonMain.get())
46 | dependencies {
47 | implementation(libs.kilua.dom)
48 | }
49 | }
50 |
51 | wasmJsMain {
52 | dependsOn(webMain)
53 | }
54 |
55 | jsMain {
56 | dependsOn(webMain)
57 | }
58 | }
59 | }
60 |
61 | android {
62 | namespace = "vip.cdms.orecompose"
63 | compileSdk = libs.versions.android.sdk.compile.get().toInt()
64 | defaultConfig {
65 | minSdk = libs.versions.android.sdk.min.get().toInt()
66 | }
67 | compileOptions {
68 | sourceCompatibility = JavaVersion.VERSION_11
69 | targetCompatibility = JavaVersion.VERSION_11
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/oreui/src/androidMain/kotlin/vip/cdms/orecompose/style/OreTheme.android.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | @Composable
6 | internal actual fun PlatformOreTheme(content: @Composable () -> Unit) {
7 | content()
8 | }
9 |
--------------------------------------------------------------------------------
/oreui/src/androidMain/kotlin/vip/cdms/orecompose/utils/PlatformUtils.android.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | private object CurrentPlatform : Platform.Android
4 | actual fun getPlatform(): Platform = CurrentPlatform
5 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/composeResources/font/Minecraft.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cdm2883/OreCompose/8536db1e8e694556716b7462ad20475eb9099242/oreui/src/commonMain/composeResources/font/Minecraft.ttf
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/components/Input.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.components
2 |
3 | // TODO(oreui): text input
4 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/components/Label.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.components
2 |
3 | import androidx.compose.foundation.text.InlineTextContent
4 | import androidx.compose.material.LocalTextStyle
5 | import androidx.compose.material.Text
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.text.AnnotatedString
10 | import androidx.compose.ui.text.TextLayoutResult
11 | import androidx.compose.ui.text.TextStyle
12 | import androidx.compose.ui.text.font.FontFamily
13 | import androidx.compose.ui.text.font.FontStyle
14 | import androidx.compose.ui.text.font.FontWeight
15 | import androidx.compose.ui.text.style.TextAlign
16 | import androidx.compose.ui.text.style.TextDecoration
17 | import androidx.compose.ui.text.style.TextOverflow
18 | import vip.cdms.orecompose.effect.Pixel
19 | import vip.cdms.orecompose.effect.Px
20 | import vip.cdms.orecompose.utils.*
21 |
22 | object LocalLabel {
23 | val AutoFontsFallbackEnabled = staticCompositionLocalOf { false }
24 | }
25 |
26 | @Composable
27 | fun Label(
28 | text: AnnotatedString,
29 | modifier: Modifier = Modifier,
30 | color: Color = Color.Unspecified,
31 | fontSize: Px = Pixel.Unspecified,
32 | fontStyle: FontStyle? = null,
33 | fontWeight: FontWeight? = null,
34 | fontFamily: FontFamily? = null,
35 | letterSpacing: Px = Pixel.Unspecified,
36 | textDecoration: TextDecoration? = null,
37 | textAlign: TextAlign? = null,
38 | lineHeight: Px = Pixel.Unspecified,
39 | overflow: TextOverflow = TextOverflow.Clip,
40 | softWrap: Boolean = true,
41 | maxLines: Int = Int.MAX_VALUE,
42 | minLines: Int = 1,
43 | inlineContent: Map = mapOf(),
44 | onTextLayout: (TextLayoutResult) -> Unit = {},
45 | style: TextStyle = LocalTextStyle.current
46 | ) {
47 | @Composable
48 | fun Text(text: AnnotatedString) = Text(
49 | text,
50 | modifier,
51 | color,
52 | fontSize.toSp(),
53 | fontStyle,
54 | fontWeight,
55 | fontFamily,
56 | letterSpacing.toSp(),
57 | textDecoration,
58 | textAlign,
59 | lineHeight.toSp(),
60 | overflow,
61 | softWrap,
62 | maxLines,
63 | minLines,
64 | inlineContent,
65 | onTextLayout,
66 | style
67 | )
68 |
69 | // FIXME(oreui/web): cache label text
70 | if (RuntimePlatform is Platform.Web)
71 | // weird, localFontsFallback will be not working with another scope outer (like LaunchedEffect & if-else)
72 | return Text(if (LocalLabel.AutoFontsFallbackEnabled.current) text.localFontsFallback() else text)
73 |
74 | var texted by remember { mutableStateOf(null) }
75 | val localFontsFallback = LocalFontsFallback.current
76 | if (!localFontsFallback.isNullOrEmpty() && LocalLabel.AutoFontsFallbackEnabled.current) LaunchedEffect(text) {
77 | texted = text.fontsFallback(*localFontsFallback) // !!! TIME-CONSUMING !!!
78 | }
79 | Text(texted ?: text)
80 | }
81 |
82 | @Composable
83 | fun Label(
84 | text: String,
85 | modifier: Modifier = Modifier,
86 | color: Color = Color.Unspecified,
87 | fontSize: Px = Pixel.Unspecified,
88 | fontStyle: FontStyle? = null,
89 | fontWeight: FontWeight? = null,
90 | fontFamily: FontFamily? = null,
91 | letterSpacing: Px = Pixel.Unspecified,
92 | textDecoration: TextDecoration? = null,
93 | textAlign: TextAlign? = null,
94 | lineHeight: Px = Pixel.Unspecified,
95 | overflow: TextOverflow = TextOverflow.Clip,
96 | softWrap: Boolean = true,
97 | maxLines: Int = Int.MAX_VALUE,
98 | minLines: Int = 1,
99 | onTextLayout: ((TextLayoutResult) -> Unit)? = null,
100 | style: TextStyle = LocalTextStyle.current
101 | ) = Label(
102 | AnnotatedString(text),
103 | modifier,
104 | color,
105 | fontSize,
106 | fontStyle,
107 | fontWeight,
108 | fontFamily,
109 | letterSpacing,
110 | textDecoration,
111 | textAlign,
112 | lineHeight,
113 | overflow,
114 | softWrap,
115 | maxLines,
116 | minLines,
117 | onTextLayout = onTextLayout ?: {},
118 | style = style,
119 | )
120 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/effect/Outline.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.effect
2 |
3 | import androidx.compose.foundation.layout.padding
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.staticCompositionLocalOf
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.geometry.Offset
8 | import androidx.compose.ui.geometry.Size
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.graphics.drawscope.ContentDrawScope
11 | import androidx.compose.ui.node.DrawModifierNode
12 | import androidx.compose.ui.node.ModifierNodeElement
13 | import androidx.compose.ui.platform.InspectorInfo
14 | import androidx.compose.ui.unit.Dp
15 | import androidx.compose.ui.unit.dp
16 | import androidx.compose.ui.unit.isUnspecified
17 | import vip.cdms.orecompose.style.OreColors
18 |
19 | val LocalOutlineColor = staticCompositionLocalOf { OreColors.Outline }
20 | val LocalOutlineWidth = staticCompositionLocalOf { 1.px }
21 |
22 | @Composable
23 | fun Modifier.outline(
24 | color: Color = LocalOutlineColor.current,
25 | width: Px = LocalOutlineWidth.current,
26 | padding: Boolean = true
27 | ) = outline(
28 | color,
29 | width.toDp(),
30 | padding
31 | )
32 |
33 | fun Modifier.outline(color: Color, width: Dp, padding: Boolean = true) =
34 | if (padding) padding(width).outline(color, width, Dp.Unspecified)
35 | else outline(color, width, accInnerOutlinesWidth())
36 |
37 | fun Modifier.outline(color: Color, width: Dp, padding: Dp) =
38 | if (width.value > 0) this then OutlineElement(color, width, padding) else this
39 |
40 | fun Modifier.hasOutlined() = any { it is OutlineElement }
41 |
42 | internal fun Modifier.accInnerOutlinesWidth() = foldIn(0.dp) { acc, element ->
43 | acc + if (element is OutlineElement) element.width else 0.dp
44 | }
45 |
46 | private data class OutlineElement(
47 | val color: Color,
48 | val width: Dp,
49 | val padding: Dp,
50 | ) : ModifierNodeElement() {
51 | override fun create() = OutlineModifier(color, width, padding.validPadding())
52 |
53 | override fun update(node: OutlineModifier) {
54 | node.color = color
55 | node.width = width
56 | node.padding = padding.validPadding()
57 | }
58 |
59 | override fun InspectorInfo.inspectableProperties() {
60 | name = "outline"
61 | properties["color"] = color
62 | properties["width"] = width
63 | properties["padding"] = padding
64 | }
65 |
66 | fun Dp.validPadding() = if (isUnspecified) 0.dp else this
67 | }
68 |
69 | private class OutlineModifier(
70 | var color: Color,
71 | var width: Dp,
72 | var padding: Dp,
73 | ) : Modifier.Node(), DrawModifierNode {
74 | override fun ContentDrawScope.draw() {
75 | val px = width.toPx()
76 | val pd = padding.toPx()
77 | val pd2 = pd * 2
78 | drawRect(color, Offset(x = -pd - px, y = -pd - px), Size(width = size.width + pd2 + px, height = px))
79 | drawRect(color, Offset(x = size.width + pd, y = -pd - px), Size(width = px, height = size.height + pd2 + px))
80 | drawRect(color, Offset(x = -pd, y = size.height + pd), Size(width = size.width + pd2 + px, height = px))
81 | drawRect(color, Offset(x = -pd - px, y = -pd), Size(width = px, height = size.height + pd2 + px))
82 | drawContent()
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/effect/Pixel.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE")
2 |
3 | package vip.cdms.orecompose.effect
4 |
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.runtime.Immutable
7 | import androidx.compose.runtime.Stable
8 | import androidx.compose.runtime.staticCompositionLocalOf
9 | import androidx.compose.ui.unit.Dp
10 | import androidx.compose.ui.unit.dp
11 | import androidx.compose.ui.unit.isSpecified
12 | import androidx.compose.ui.unit.times
13 | import kotlin.jvm.JvmInline
14 |
15 | val DefaultPixelSize = 2.dp
16 | val LocalPixelSize = staticCompositionLocalOf { DefaultPixelSize }
17 |
18 | typealias Px = Pixel
19 |
20 | /**
21 | * Dimension value that is used to ensure that
22 | * the relative size of each place is strictly consistent.
23 | *
24 | * Just like the real pixel means, but it can be scaled.
25 | */
26 | @Immutable
27 | @JvmInline
28 | value class Pixel(val value: Float) : Comparable {
29 | //
30 | // Maybe it should be a single fixed multiple instead of
31 | // a multiple of [LocalPixelSize] which can only be got in [Composable] scope
32 | // (just like the contagiousness of coroutines & js async)?
33 | //
34 | // The [Density] is already a modifiable scale value,
35 | // is it necessary to add one more factor that is more difficult to use?
36 | //
37 | // If we don't use [LocalPixelSize], it's easier to
38 | // use in a draw function that only gets [Density] without additional wrapping.
39 | //
40 | // But now there is still a chance to modify it thanks to the excellent program decoupling design!
41 | // Save that for later when more related issues arise.
42 | //
43 | @Composable inline fun toDp() = if (isSpecified) value * LocalPixelSize.current else Dp.Unspecified
44 |
45 | @Stable inline val isSpecified get() = !value.isNaN()
46 | @Stable inline val isUnspecified get() = value.isNaN()
47 |
48 | @Stable inline operator fun plus(other: Pixel) = Pixel(value + other.value)
49 | @Stable inline operator fun minus(other: Pixel) = Pixel(value - other.value)
50 | @Stable inline operator fun unaryMinus() = Pixel(-value)
51 | @Stable inline operator fun times(other: Int) = Pixel(value * other)
52 | @Stable inline operator fun times(other: Float) = Pixel(value * other)
53 | @Stable inline operator fun div(other: Int) = Pixel(value / other)
54 | @Stable inline operator fun div(other: Float) = Pixel(value / other)
55 | @Stable inline operator fun div(other: Pixel) = value / other.value
56 | @Stable override fun compareTo(other: Pixel) = value.compareTo(other.value)
57 | @Stable override fun toString() = if (isUnspecified) "Pixel.Unspecified" else "$value.px"
58 |
59 | companion object {
60 | @Composable inline fun Dp.toPx() = if (isSpecified) Pixel(this / LocalPixelSize.current) else Unspecified
61 |
62 | @Stable val Zero = Pixel(0f)
63 | @Stable val Unspecified = Pixel(Float.NaN)
64 | }
65 | }
66 |
67 | @Stable inline val Int.px get() = Pixel(toFloat())
68 | @Stable inline val Double.px get() = Pixel(toFloat())
69 | @Stable inline val Float.px get() = Pixel(this)
70 |
71 | @Stable inline operator fun Int.times(other: Pixel) = Pixel(this * other.value)
72 | @Stable inline operator fun Float.times(other: Pixel) = Pixel(this * other.value)
73 | @Stable inline operator fun Double.times(other: Pixel) = Pixel(toFloat() * other.value)
74 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/effect/Sound.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.effect
2 |
3 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/style/McFonts.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.text.font.FontFamily
5 | import orecompose.oreui.generated.resources.Minecraft
6 | import orecompose.oreui.generated.resources.Res
7 | import org.jetbrains.compose.resources.Font
8 | import vip.cdms.orecompose.utils.FontFamilyFallback
9 |
10 | object McFonts {
11 |
12 | // https://github.com/IdreesInc/Minecraft-Font
13 | // OFL-1.1 license
14 | object Ascii {
15 | val Resource = Res.font.Minecraft
16 | val FontFamily
17 | @Composable inline get() = FontFamily(Font(Resource))
18 | val Fallback: FontFamilyFallback
19 | @Composable inline get() = FontFamily to ::hasGlyph
20 | val Glyphs = arrayOf(33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
21 | 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
22 | 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
23 | 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
24 | 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
25 | 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
26 | 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
27 | 121, 122, 123, 124, 125, 126, 161, 162, 163, 164, 165,
28 | 166, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177,
29 | 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
30 | 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
31 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
32 | 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
33 | 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
34 | 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
35 | 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 8364,
36 | 9786, 9787, 9829)
37 | @Suppress("NOTHING_TO_INLINE")
38 | inline fun hasGlyph(c: Char) = c.code in Glyphs
39 | }
40 |
41 | // TODO(oreui): bold font (e.g. title)
42 | }
43 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/style/OreColors.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | import androidx.compose.ui.graphics.Color
4 | import vip.cdms.orecompose.utils.rgb
5 |
6 | object OreColors {
7 | val PureWhite = Color.White
8 | val PureBlack = Color.Black
9 |
10 | val Outline = 0x1E1E1F.rgb
11 | val OutlineDisabled = 0x8C8D90.rgb
12 | }
13 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/style/OreTheme.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | import androidx.compose.material.MaterialTheme
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.CompositionLocalProvider
6 | import androidx.compose.ui.unit.Dp
7 | import vip.cdms.orecompose.components.LocalLabel
8 | import vip.cdms.orecompose.effect.LocalPixelSize
9 | import vip.cdms.orecompose.utils.ComposeNester
10 | import vip.cdms.orecompose.utils.LocalFontsFallback
11 | import vip.cdms.orecompose.utils.providesMore
12 |
13 | typealias OreModule = ComposeNester
14 |
15 | @Composable
16 | fun OreTheme(
17 | pixelSize: Dp? = null,
18 | fallbackMcFonts: Boolean = true,
19 | modules: Array? = null,
20 | content: @Composable () -> Unit
21 | ) = OreModule.Apply(modules, content) {
22 | val locals = buildList {
23 | if (pixelSize != null) this += LocalPixelSize provides pixelSize
24 | if (fallbackMcFonts) {
25 | this += LocalFontsFallback providesMore arrayOf(McFonts.Ascii.Fallback)
26 | this += LocalLabel.AutoFontsFallbackEnabled provides true
27 | }
28 | }
29 |
30 | CompositionLocalProvider(*locals.toTypedArray()) {
31 | PlatformOreTheme { MaterialTheme(content = it) }
32 | }
33 | }
34 |
35 | @Composable
36 | internal expect fun PlatformOreTheme(content: @Composable () -> Unit)
37 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/style/ZIndices.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | object ZIndices {
4 | const val ACCESSIBILITY_INDICATOR = 2f
5 | }
6 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/Accessibility.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.foundation.interaction.InteractionSource
4 | import androidx.compose.foundation.interaction.collectIsFocusedAsState
5 | import androidx.compose.foundation.interaction.collectIsHoveredAsState
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.composed
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.input.key.*
11 | import androidx.compose.ui.zIndex
12 | import vip.cdms.orecompose.effect.LocalOutlineWidth
13 | import vip.cdms.orecompose.style.ZIndices
14 | import vip.cdms.orecompose.effect.accInnerOutlinesWidth
15 | import vip.cdms.orecompose.effect.outline
16 | import vip.cdms.orecompose.style.OreColors
17 |
18 | val LocalAccessibilityIndicatorColor = staticCompositionLocalOf { OreColors.PureWhite }
19 |
20 | fun Modifier.accessibilityIndicator(interactionSource: InteractionSource) = accInnerOutlinesWidth().let { padding ->
21 | // WARNING: using `composed` will cause all the padding nodes after this node
22 | // calculated a wrong padding (ignore this). Because foreach modifier chain in this case
23 | // will be a ComposedModifier rather than OutlineElement. We cannot get the element inside
24 | // before it actually drawing.
25 | //
26 | // But for accessibility indicator, maybe it's not a big problem...
27 | // Because the indicator usually be the last element of modifiers.
28 | composed {
29 | val color = LocalAccessibilityIndicatorColor.current ?: return@composed this
30 |
31 | var isTab by remember { mutableStateOf(false) }
32 | val focused = interactionSource.collectIsFocusedAsState().value
33 | val hovered = interactionSource.collectIsHoveredAsState().value
34 | remember(hovered) { isTab = false }
35 |
36 | onKeyEvent {
37 | if (it.type == KeyEventType.Unknown) return@onKeyEvent false
38 | isTab = it.key == Key.Tab
39 | false
40 | }.run {
41 | if (isTab && focused && !hovered) zIndex(ZIndices.ACCESSIBILITY_INDICATOR)
42 | .outline(color, LocalOutlineWidth.current.toDp(), padding)
43 | else this
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/ColorUtils.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Int.argb
6 | inline get() = Color(this)
7 | val Long.argb
8 | inline get() = Color(this)
9 |
10 | val Int.rgb
11 | inline get() = (0xFF000000 or this.toLong()).argb
12 | val Long.rgb
13 | inline get() = (0xFF000000L or this).argb
14 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/ComposeNester.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | private typealias Component = @Composable () -> Unit
6 | private typealias ModuleWrapper = @Composable (Component) -> Unit
7 |
8 | interface ComposeNester {
9 | @Composable
10 | operator fun invoke(content: @Composable () -> Unit)
11 |
12 | companion object {
13 | inline fun wrap(crossinline wrapper: ModuleWrapper) = object : ComposeNester {
14 | @Composable
15 | override operator fun invoke(content: Component) = wrapper(content)
16 | }
17 |
18 | /**
19 | * ```kt
20 | * Apply(
21 | * modules = arrayOf(
22 | * wrap { print("1"); it() },
23 | * wrap { print("2"); it() },
24 | * ),
25 | * core = wrap { print("3"); it() }
26 | * ) { print("4") } // prints 1234
27 | * ```
28 | */
29 | @Composable
30 | inline fun Apply(modules: Array?, core: T, noinline content: Component) =
31 | // ((modules ?: emptyArray()) + core).foldRight(content) { module, acc -> { module(acc) } }()
32 | if (modules == null) core { content() }
33 | else (modules + core).foldRight(content) { module, acc -> { module(acc) } }()
34 |
35 | @Composable
36 | inline fun Apply(modules: Array?, noinline content: Component, crossinline core: ModuleWrapper) =
37 | Apply(modules, wrap(core) as T, content)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/DrawUtils.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.ui.graphics.Canvas
4 | import androidx.compose.ui.graphics.Color
5 | import androidx.compose.ui.graphics.Paint
6 | import androidx.compose.ui.graphics.PointMode
7 |
8 | fun pixelPaint() = Paint().apply {
9 | isAntiAlias = false
10 | }
11 |
12 | @Suppress("NOTHING_TO_INLINE")
13 | inline fun Canvas.drawPoint(x: Float, y: Float, color: Color, paint: Paint = pixelPaint()) {
14 | paint.color = color
15 | drawRawPoints(PointMode.Points, floatArrayOf(x, y), paint)
16 | }
17 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/FontsFallback.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.staticCompositionLocalOf
5 | import androidx.compose.ui.text.AnnotatedString
6 | import androidx.compose.ui.text.SpanStyle
7 | import androidx.compose.ui.text.buildAnnotatedString
8 | import androidx.compose.ui.text.font.FontFamily
9 | import androidx.compose.ui.text.withStyle
10 |
11 | typealias FontFamilyFallback = Pair Boolean>
12 | val LocalFontsFallback = staticCompositionLocalOf?> { null }
13 |
14 | @Composable
15 | fun CharSequence.localFontsFallback() = LocalFontsFallback.current
16 | ?.takeIf { isNotEmpty() }?.let { fontsFallback(*it) }
17 | ?: if (this is AnnotatedString) this else AnnotatedString(this.toString())
18 |
19 | fun CharSequence.fontsFallback(vararg fonts: FontFamilyFallback) =
20 | charStyle(*Array(fonts.size) { i -> SpanStyle(fontFamily = fonts[i].first) to fonts[i].second })
21 |
22 | fun CharSequence.charStyle(vararg styles: Pair Boolean>) = buildAnnotatedString {
23 | val text = this@charStyle
24 | var i = 0
25 | // too slow... but there's no another way to fallback multi-fonts
26 | // unless rewrite the whole compose text.
27 | while (i < text.length) {
28 | val char = text[i]
29 | if (char.code in 0xd800..0xdbff) {
30 | append(text.subSequence(i, i + if (text.getOrNull(i + 1) != null) 2 else 1))
31 | i += 2
32 | continue
33 | }
34 | styles.asSequence()
35 | .filter { (_, filter) -> filter(char) }
36 | .map { (style) -> style }
37 | .reduceOrNull { acc, next -> acc.merge(next) }
38 | ?.let { withStyle(it) { append(text.subSequence(i, i + 1)) } }
39 | ?: append(text.subSequence(i, i + 1))
40 | i++
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/LocalUtils.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.ProvidableCompositionLocal
5 | import kotlin.jvm.JvmName
6 |
7 | @Composable
8 | inline infix fun , R> T.providesCompute(block: @Composable T.() -> R) =
9 | provides(block(this)) // providesComputed do not allow access value itself :(
10 |
11 | @JvmName("providesNullableMore")
12 | @Composable
13 | inline infix fun ProvidableCompositionLocal?>.providesMore(items: Array) =
14 | // providesCompute { (current ?: emptyArray()) + items }
15 | providesCompute { current?.let { it + items } ?: items }
16 |
17 | @Composable
18 | inline infix fun ProvidableCompositionLocal>.providesMore(items: Array) =
19 | providesCompute { current + items }
20 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/McFormat.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | @Suppress("MemberVisibilityCanBePrivate", "ConstPropertyName", "unused", "SpellCheckingInspection", "NOTHING_TO_INLINE")
4 | object McFormatBuilderScope {
5 | inline infix fun String.with(format: Char) = s(format) + this
6 | fun String.attach(vararg format: Char) = SS + format.joinToString(SS.toString()) + this
7 | infix fun String.with(format: String) = attach(*format.toCharArray())
8 |
9 | inline fun s(format: Char) = SS + format.toString()
10 |
11 | const val black = '0'
12 | const val dark_blue = '1'
13 | const val dark_green = '2'
14 | const val dark_aqua = '3'
15 | const val dark_red = '4'
16 | const val dark_purple = '5'
17 | const val gold = '6'
18 | const val gray = '7'
19 | const val dark_gray = '8'
20 | const val blue = '9'
21 | const val green = 'a'
22 | const val aqua = 'b'
23 | const val red = 'c'
24 | const val light_purple = 'd'
25 | const val yellow = 'e'
26 | const val white = 'f'
27 | const val minecoin_gold = 'g'
28 | const val material_quartz = 'h'
29 | const val material_iron = 'i'
30 | const val material_netherite = 'j'
31 | // const val material_redstone = 'm'
32 | // const val material_copper = 'n'
33 | const val material_gold = 'p'
34 | const val material_emerald = 'q'
35 | const val material_diamond = 's'
36 | const val material_lapis = 't'
37 | const val material_amethyst = 'u'
38 |
39 | const val obfuscated = 'k'
40 | const val bold = 'l'
41 | const val strikethrough = 'm'
42 | const val underline = 'n'
43 | const val italic = 'o'
44 | const val clear = 'r'
45 |
46 | fun String.clear() = with(clear)
47 | val String.black inline get() = with(this@McFormatBuilderScope.black)
48 | val String.dark_blue inline get() = with(this@McFormatBuilderScope.dark_blue)
49 | val String.dark_green inline get() = with(this@McFormatBuilderScope.dark_green)
50 | val String.dark_aqua inline get() = with(this@McFormatBuilderScope.dark_aqua)
51 | val String.dark_red inline get() = with(this@McFormatBuilderScope.dark_red)
52 | val String.dark_purple inline get() = with(this@McFormatBuilderScope.dark_purple)
53 | val String.gold inline get() = with(this@McFormatBuilderScope.gold)
54 | val String.gray inline get() = with(this@McFormatBuilderScope.gray)
55 | val String.dark_gray inline get() = with(this@McFormatBuilderScope.dark_gray)
56 | val String.blue inline get() = with(this@McFormatBuilderScope.blue)
57 | val String.green inline get() = with(this@McFormatBuilderScope.green)
58 | val String.aqua inline get() = with(this@McFormatBuilderScope.aqua)
59 | val String.red inline get() = with(this@McFormatBuilderScope.red)
60 | val String.light_purple inline get() = with(this@McFormatBuilderScope.light_purple)
61 | val String.yellow inline get() = with(this@McFormatBuilderScope.yellow)
62 | val String.white inline get() = with(this@McFormatBuilderScope.white)
63 | val String.minecoin_gold inline get() = with(this@McFormatBuilderScope.minecoin_gold)
64 | val String.material_quartz inline get() = with(this@McFormatBuilderScope.material_quartz)
65 | val String.material_iron inline get() = with(this@McFormatBuilderScope.material_iron)
66 | val String.material_netherite inline get() = with(this@McFormatBuilderScope.material_netherite)
67 | // val String.material_redstone inline get() = with(this@McFormatBuilderScope.material_redstone)
68 | // val String.material_copper inline get() = with(this@McFormatBuilderScope.material_copper)
69 | val String.material_gold inline get() = with(this@McFormatBuilderScope.material_gold)
70 | val String.material_emerald inline get() = with(this@McFormatBuilderScope.material_emerald)
71 | val String.material_diamond inline get() = with(this@McFormatBuilderScope.material_diamond)
72 | val String.material_lapis inline get() = with(this@McFormatBuilderScope.material_lapis)
73 | val String.material_amethyst inline get() = with(this@McFormatBuilderScope.material_amethyst)
74 |
75 | val String.obfuscated inline get() = with(this@McFormatBuilderScope.obfuscated)
76 | val String.bold inline get() = with(this@McFormatBuilderScope.bold)
77 | val String.strikethrough inline get() = with(this@McFormatBuilderScope.strikethrough)
78 | val String.underline inline get() = with(this@McFormatBuilderScope.underline)
79 | val String.italic inline get() = with(this@McFormatBuilderScope.italic)
80 |
81 | // mimic connecting symbols (e.g. PHP)
82 | inline operator fun String.rangeTo(string: String) = this + SS + clear + string
83 | }
84 |
85 | const val SS = '§'
86 |
87 | typealias McFormat = McFormatBuilderScope
88 |
89 | inline fun mcFormat(builder: McFormatBuilderScope.() -> T) = builder(McFormatBuilderScope)
90 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/McFormatter.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | @Suppress("SpellCheckingInspection")
4 | val McFormattingColors = with(McFormat) {
5 | mapOf(
6 | black to 0x000000.rgb,
7 | dark_blue to 0x0000AA.rgb,
8 | dark_green to 0x00AA00.rgb,
9 | dark_aqua to 0x00AAAA.rgb,
10 | dark_red to 0xAA0000.rgb,
11 | dark_purple to 0xAA00AA.rgb,
12 | gold to 0xFFAA00.rgb,
13 | gray to 0xAAAAAA.rgb,
14 | dark_gray to 0x555555.rgb,
15 | blue to 0x5555FF.rgb, // #5455FF
16 | green to 0x55FF55.rgb, // #55FF56
17 | aqua to 0x55FFFF.rgb,
18 | red to 0xFF5555.rgb,
19 | light_purple to 0xFF55FE.rgb,
20 | yellow to 0xFFFF55.rgb,
21 | white to 0xFFFFFF.rgb,
22 | minecoin_gold to 0xEECF15.rgb,
23 | material_quartz to 0xE3D4D1.rgb,
24 | material_iron to 0xCECACA.rgb,
25 | material_netherite to 0x443A3B.rgb,
26 | // material_redstone to 0x971607.rgb,
27 | // material_copper to 0xB4684D.rgb,
28 | material_gold to 0xDEB12D.rgb,
29 | material_emerald to 0x47A036.rgb,
30 | material_diamond to 0x2CBAA8.rgb,
31 | material_lapis to 0x21497B.rgb,
32 | material_amethyst to 0x9A5CC6.rgb,
33 | )
34 | }
35 |
36 | // TODO(oreui): text color renderer (support input & rich text edit)
37 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/PlatformUtils.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | sealed interface Platform {
4 | interface Jvm : Platform
5 | interface Android : Platform
6 | interface Web : Platform
7 | interface Js : Web
8 | interface WasmJs : Web
9 | }
10 |
11 | internal expect fun getPlatform(): Platform
12 | val RuntimePlatform = getPlatform()
13 |
--------------------------------------------------------------------------------
/oreui/src/commonMain/kotlin/vip/cdms/orecompose/utils/UnitUtils.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE")
2 |
3 | package vip.cdms.orecompose.utils
4 |
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.runtime.Stable
7 | import androidx.compose.ui.platform.LocalDensity
8 | import androidx.compose.ui.unit.Dp
9 | import androidx.compose.ui.unit.TextUnit
10 | import vip.cdms.orecompose.effect.Pixel
11 | import vip.cdms.orecompose.effect.Pixel.Companion.toPx
12 |
13 | // so confusing :(
14 |
15 | private typealias IntDevicePixel = Int
16 | private typealias DevicePixel = Float
17 | private typealias OrePixel = Pixel
18 |
19 | @[Stable Composable]
20 | inline fun IntDevicePixel.toDp(): Dp = with(LocalDensity.current) { toDp() }
21 | @[Stable Composable]
22 | inline fun DevicePixel.toDp(): Dp = with(LocalDensity.current) { toDp() }
23 | @[Stable Composable]
24 | inline fun TextUnit.toDp(): Dp = with(LocalDensity.current) { toDp() }
25 |
26 | @[Stable Composable]
27 | inline fun IntDevicePixel.toSp(): TextUnit = with(LocalDensity.current) { toSp() }
28 | @[Stable Composable]
29 | inline fun DevicePixel.toSp(): TextUnit = with(LocalDensity.current) { toSp() }
30 | @[Stable Composable]
31 | inline fun Dp.toSp(): TextUnit = with(LocalDensity.current) { toSp() }
32 | @Composable
33 | inline fun OrePixel.toSp(): TextUnit = if (isSpecified) toDp().toSp() else TextUnit.Unspecified
34 |
35 | @[Stable Composable]
36 | inline fun TextUnit.toDPx(): DevicePixel = with(LocalDensity.current) { toPx() }
37 | @[Stable Composable]
38 | inline fun Dp.toDPx(): DevicePixel = with(LocalDensity.current) { toPx() }
39 | @Composable
40 | inline fun OrePixel.toDPx(): DevicePixel = toDp().toDPx()
41 |
42 | @Composable
43 | inline fun IntDevicePixel.toPx(): OrePixel = toDp().toPx()
44 | @Composable
45 | inline fun DevicePixel.toPx(): OrePixel = toDp().toPx()
46 | @Composable
47 | inline fun TextUnit.toPx(): OrePixel = toDp().toPx()
48 |
--------------------------------------------------------------------------------
/oreui/src/jsMain/kotlin/vip/cdms/orecompose/utils/PlatformUtils.js.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | private object CurrentPlatform : Platform.Js
4 | actual fun getPlatform(): Platform = CurrentPlatform
5 |
--------------------------------------------------------------------------------
/oreui/src/jsMain/kotlin/vip/cdms/orecompose/utils/ResourcePreloader.js.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.ui.text.font.FontFamily
4 | import androidx.compose.ui.text.platform.Font
5 | import kotlinx.browser.window
6 | import kotlinx.coroutines.await
7 | import org.khronos.webgl.ArrayBuffer
8 | import org.khronos.webgl.Uint8Array
9 | import org.khronos.webgl.get
10 |
11 | actual suspend fun loadResource(fontFamilyResolver: FontFamily.Resolver, resource: ResourceUrl) {
12 | val response = window.fetch(resource.url).await()
13 | if (!resource.isFont) return
14 | val fontBytes = response.arrayBuffer().await().toByteArray()
15 | val fontFamily = FontFamily(Font(resource.fileName.split(".").first(), fontBytes))
16 | fontFamilyResolver.preload(fontFamily)
17 | }
18 |
19 | fun ArrayBuffer.toByteArray(): ByteArray {
20 | val uint8Array = Uint8Array(this) // k/js pretty simple :)
21 | return ByteArray(uint8Array.length) { i -> uint8Array[i] }
22 | }
23 |
--------------------------------------------------------------------------------
/oreui/src/jvmMain/kotlin/vip/cdms/orecompose/style/OreTheme.jvm.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | @Composable
6 | internal actual fun PlatformOreTheme(content: @Composable () -> Unit) {
7 | content()
8 | }
9 |
--------------------------------------------------------------------------------
/oreui/src/jvmMain/kotlin/vip/cdms/orecompose/utils/PlatformUtils.jvm.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | private object CurrentPlatform : Platform.Jvm
4 | actual fun getPlatform(): Platform = CurrentPlatform
5 |
6 |
--------------------------------------------------------------------------------
/oreui/src/wasmJsMain/kotlin/vip/cdms/orecompose/utils/PlatformUtils.wasmJs.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | private object CurrentPlatform : Platform.WasmJs
4 | actual fun getPlatform(): Platform = CurrentPlatform
5 |
--------------------------------------------------------------------------------
/oreui/src/wasmJsMain/kotlin/vip/cdms/orecompose/utils/ResourcePreloader.wasmJs.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.ui.text.font.FontFamily
4 | import androidx.compose.ui.text.platform.Font
5 | import kotlinx.browser.window
6 | import kotlinx.coroutines.await
7 | import org.khronos.webgl.ArrayBuffer
8 | import org.khronos.webgl.Int8Array
9 | import org.w3c.fetch.Response
10 | import kotlin.wasm.unsafe.UnsafeWasmMemoryApi
11 | import kotlin.wasm.unsafe.withScopedMemoryAllocator
12 |
13 | actual suspend fun loadResource(fontFamilyResolver: FontFamily.Resolver, resource: ResourceUrl) {
14 | val response = window.fetch(resource.url).await()
15 | if (!resource.isFont) return
16 | val fontBytes = response.arrayBuffer().await().toByteArray()
17 | val fontFamily = FontFamily(Font(resource.fileName.split(".").first(), fontBytes))
18 | fontFamilyResolver.preload(fontFamily)
19 | }
20 |
21 | // https://github.com/JetBrains/compose-multiplatform-core/pull/1400/files#diff-9d3f03189881301ef36eabc7a053f7f1184e4cc9b831c80a0d56424ed8a0c548R62
22 | // https://github.com/JetBrains/compose-multiplatform-core/blob/7eabcc8e1bf32f1b0f5723def9576c7123b519bf/compose/mpp/demo/src/wasmJsMain/kotlin/androidx/compose/mpp/demo/main.js.kt#L83
23 |
24 | fun ArrayBuffer.toByteArray(): ByteArray {
25 | val source = Int8Array(this, 0, byteLength)
26 | return jsInt8ArrayToKotlinByteArray(source)
27 | }
28 |
29 | internal fun jsInt8ArrayToKotlinByteArray(x: Int8Array): ByteArray {
30 | val size = x.length
31 | @OptIn(UnsafeWasmMemoryApi::class)
32 | return withScopedMemoryAllocator { allocator ->
33 | val memBuffer = allocator.allocate(size)
34 | val dstAddress = memBuffer.address.toInt()
35 | jsExportInt8ArrayToWasm(x, size, dstAddress)
36 | ByteArray(size) { i -> (memBuffer + i).loadByte() }
37 | }
38 | }
39 |
40 | //language=JavaScript
41 | @JsFun("""
42 | (src, size, dstAddr) => {
43 | // noinspection JSUnresolvedReference
44 | const mem8 = new Int8Array(wasmExports.memory.buffer, dstAddr, size);
45 | mem8.set(src);
46 | }
47 | """)
48 | internal external fun jsExportInt8ArrayToWasm(src: Int8Array, size: Int, dstAddr: Int)
49 |
--------------------------------------------------------------------------------
/oreui/src/webMain/kotlin/vip/cdms/orecompose/style/OreTheme.web.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.style
2 |
3 | import androidx.compose.runtime.Composable
4 | import orecompose.oreui.generated.resources.Minecraft
5 | import orecompose.oreui.generated.resources.Res
6 | import vip.cdms.orecompose.utils.PreloadResources
7 | import vip.cdms.orecompose.utils.ResourcePreloader
8 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.resourceUrl
9 | import vip.cdms.orecompose.utils.ResourceUrl.Companion.ttf
10 |
11 | @Composable
12 | internal actual fun PlatformOreTheme(content: @Composable () -> Unit) {
13 | val packaging = "orecompose.oreui.generated.resources"
14 | PreloadResources(
15 | Res.font.resourceUrl(packaging) { ::Minecraft.ttf },
16 | ) {
17 | ResourcePreloader {
18 | content()
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/oreui/src/webMain/kotlin/vip/cdms/orecompose/utils/ResourcePreloader.kt:
--------------------------------------------------------------------------------
1 | package vip.cdms.orecompose.utils
2 |
3 | import androidx.compose.runtime.*
4 | import androidx.compose.ui.platform.LocalFontFamilyResolver
5 | import androidx.compose.ui.text.font.FontFamily
6 | import kotlinx.coroutines.async
7 | import kotlinx.coroutines.awaitAll
8 | import org.jetbrains.compose.resources.DrawableResource
9 | import org.jetbrains.compose.resources.FontResource
10 | import kotlin.reflect.KProperty0
11 |
12 | val LocalPreloadResources = staticCompositionLocalOf?> { null }
13 | /*value*/ class ResourceUrl(val url: String) {
14 | constructor(packaging: String, type: String, file: String) : this("$ROOT/$packaging/$type/$file")
15 | val fileName get() = url.split("/").last()
16 | val isFont get() = url.contains("/font/")
17 | companion object {
18 | const val ROOT = "./composeResources"
19 |
20 | val KProperty0.png inline get() = "$name.png"
21 | val KProperty0.jpg inline get() = "$name.jpg"
22 | val KProperty0.bmp inline get() = "$name.bmp"
23 | val KProperty0.webp inline get() = "$name.webp"
24 | val KProperty0.xml inline get() = "$name.xml"
25 |
26 | val KProperty0.ttf inline get() = "$name.ttf"
27 | val KProperty0.otf inline get() = "$name.otf"
28 |
29 | // stupid kmp reflect metadata, we need real qualifiedName >:(
30 | inline fun T.resourceUrl(packaging: String, suffix: String = "", file: T.() -> String) =
31 | ResourceUrl(packaging, T::class.simpleName!! + suffix, file(this))
32 | }
33 | }
34 |
35 | private val DisablePreloadResources = arrayOf()
36 | @Deprecated("Do NOT use this unless you know what are you doing now!", ReplaceWith("content()"))
37 | @Composable
38 | fun DisablePreloadResources(content: @Composable () -> Unit) = CompositionLocalProvider(LocalPreloadResources provides DisablePreloadResources, content)
39 |
40 | @Composable
41 | fun PreloadResources(vararg urls: /*don't allow value class*/ ResourceUrl, content: @Composable () -> Unit) {
42 | var preloadResources = LocalPreloadResources.current ?: emptyArray()
43 | if (preloadResources !== DisablePreloadResources) preloadResources += urls
44 | CompositionLocalProvider(LocalPreloadResources provides preloadResources, content)
45 | }
46 |
47 | val LocalPreloadResourcePlaceholder = staticCompositionLocalOf<(@Composable () -> Unit)?> { null }
48 | @Composable
49 | fun ResourcePreloader(content: @Composable () -> Unit) {
50 | val urls = LocalPreloadResources.current
51 | val fontFamilyResolver = LocalFontFamilyResolver.current
52 |
53 | var loaded by remember { mutableStateOf(urls == null || urls === DisablePreloadResources) }
54 | if (loaded) return content()
55 | else LocalPreloadResourcePlaceholder.current?.let { it() }
56 |
57 | LaunchedEffect(Unit) {
58 | urls!!.map { async { loadResource(fontFamilyResolver, it) } }.awaitAll()
59 | loaded = true
60 | }
61 | }
62 |
63 | expect suspend fun loadResource(fontFamilyResolver: FontFamily.Resolver, resource: ResourceUrl)
64 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "OreCompose"
2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
3 |
4 | pluginManagement {
5 | @Suppress("UnstableApiUsage")
6 | repositories {
7 | google {
8 | mavenContent {
9 | includeGroupAndSubgroups("androidx")
10 | includeGroupAndSubgroups("com.android")
11 | includeGroupAndSubgroups("com.google")
12 | }
13 | }
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | dependencyResolutionManagement {
20 | @Suppress("UnstableApiUsage")
21 | repositories {
22 | google {
23 | mavenContent {
24 | includeGroupAndSubgroups("androidx")
25 | includeGroupAndSubgroups("com.android")
26 | includeGroupAndSubgroups("com.google")
27 | }
28 | }
29 | mavenCentral()
30 | maven("https://jitpack.io")
31 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
32 | }
33 | }
34 |
35 | include(":oreui")
36 | include(":oreui-panorama")
37 | include(":gallery")
38 |
--------------------------------------------------------------------------------