Compare commits
13 Commits
test/emoji
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc5ed3475c | ||
|
|
871ff0acb1 | ||
|
|
a146c6f846 | ||
|
|
40ad937384 | ||
|
|
8408cb5133 | ||
|
|
8b90cdf06e | ||
|
|
ff01120925 | ||
|
|
74b5c845aa | ||
|
|
ddc4f7f1ba | ||
|
|
afea8c721f | ||
|
|
9cffcea246 | ||
|
|
5acf80db0f | ||
|
|
80fb20885b |
@@ -1,11 +0,0 @@
|
||||
**/.*/
|
||||
!.git/
|
||||
**/build/
|
||||
**/dist/
|
||||
**/out/
|
||||
**/target/
|
||||
utils/
|
||||
!utils/repr_build/scripts/
|
||||
.env
|
||||
gradlew.bat
|
||||
local.properties
|
||||
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 🐛 Bug Report
|
||||
name: Bug Report
|
||||
description: Create a report to help FlorisBoard improve
|
||||
labels:
|
||||
- "bug"
|
||||
@@ -62,14 +62,3 @@ body:
|
||||
placeholder: e.g. 10, Stock
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: "Checklist"
|
||||
options:
|
||||
- label: "I made sure that there are *no existing issues* - [open](https://github.com/florisboard/florisboard/issues) or [closed](https://github.com/florisboard/florisboard/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to."
|
||||
required: true
|
||||
- label: "I have read and understood the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md)."
|
||||
required: true
|
||||
- label: "I have taken the time to fill in all the required details. I understand that the bug report will be dismissed otherwise."
|
||||
required: true
|
||||
|
||||
12
.github/ISSUE_TEMPLATE/config.yml
vendored
12
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Matrix
|
||||
url: https://matrix.to/#/#florisboard:matrix.org
|
||||
about: General chat about FlorisBoard and quick Q/A
|
||||
- name: ❓ Questions / Feedback
|
||||
url: https://github.com/florisboard/florisboard/discussions/new/choose
|
||||
about: Post your questions or feedback in the discussions panel
|
||||
- name: Ask a question
|
||||
url: https://github.com/florisboard/florisboard/discussions/new?category=q-a
|
||||
about: Ask here if you have a question about FlorisBoard or need assistance
|
||||
- name: General feedback
|
||||
url: https://github.com/florisboard/florisboard/discussions/new?category=feedback
|
||||
about: Give general feedback about this project
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/crash_report.yml
vendored
13
.github/ISSUE_TEMPLATE/crash_report.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 💥 Crash report
|
||||
name: Crash report
|
||||
description: Create a report with a generated crash log attached to help FlorisBoard improve
|
||||
labels:
|
||||
- "bug"
|
||||
@@ -36,14 +36,3 @@ body:
|
||||
description: Paste the generated crash log below
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: "Checklist"
|
||||
options:
|
||||
- label: "I made sure that there are *no existing issues* - [open](https://github.com/florisboard/florisboard/issues) or [closed](https://github.com/florisboard/florisboard/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to."
|
||||
required: true
|
||||
- label: "I have read and understood the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md)."
|
||||
required: true
|
||||
- label: "I have taken the time to fill in all the required details. I understand that the crash report will be dismissed otherwise."
|
||||
required: true
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: ✨ Feature request
|
||||
name: Feature request / Suggestion
|
||||
description: Suggest an idea for this project
|
||||
labels:
|
||||
- "proposal"
|
||||
@@ -20,14 +20,3 @@ body:
|
||||
description: Please explain your idea in a precise way.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: "Checklist"
|
||||
options:
|
||||
- label: "I made sure that there are *no existing issues* - [open](https://github.com/florisboard/florisboard/issues) or [closed](https://github.com/florisboard/florisboard/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to."
|
||||
required: true
|
||||
- label: "I have read and understood the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md)."
|
||||
required: true
|
||||
- label: "I have taken the time to fill in all the required details. I understand that the feature request will be dismissed otherwise."
|
||||
required: true
|
||||
|
||||
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
@@ -25,14 +25,14 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Set up CMake and Ninja
|
||||
uses: lukka/get-cmake@v4.0.2
|
||||
uses: lukka/get-cmake@latest
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean assembleDebug
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -4,34 +4,32 @@ Thanks for considering contributing to FlorisBoard!
|
||||
|
||||
There are several ways to contribute to FlorisBoard. This document provides some general guidelines for each type of contribution.
|
||||
|
||||
The FlorisBoard community is international, as such we require all contributions, including issues, pull requests, and participation in the Matrix chat to be in English and follow the [code of conduct](https://github.com/florisboard/florisboard/blob/main/CODE_OF_CONDUCT.md). Contributions not adhering to these requirements will be dismissed. Thanks for making the FlorisBoard community an inclusive and safe space for everyone!
|
||||
## General contributions
|
||||
|
||||
## Non-code contributions
|
||||
### Translation
|
||||
|
||||
### Translations
|
||||
|
||||
To make FlorisBoard accessible in as many languages as possible, the platform [Crowdin](https://crowdin.florisboard.patrickgold.dev) is used to crowdsource and manage translations. The list of languages in Crowdin covers a good range of languages, but feel free to email [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to request a new language.
|
||||
To make FlorisBoard accessible in as many languages as possible, the platform [Crowdin](https://crowdin.florisboard.patrickgold.dev) is used to crowdsource and manage translations. The list of languages in Crowdin covers a good range of languages, but feel free to send an email to [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to request a new language.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This is the only source of translations - **PRs that add/update translations are not accepted.**
|
||||
|
||||
### Feedback
|
||||
|
||||
You can [give general feedback](https://github.com/florisboard/florisboard/discussions/new?category=feedback) directly here on GitHub. This is the preferred way to give feedback, as it allows not only for me to read and respond to feedback, but for everyone in this community.
|
||||
|
||||
### Bug reporting
|
||||
|
||||
Allows us to see where FlorisBoard has flaws and should be improved to maximize stability and user experience. To make this process as smooth as possible, please use the pre-made [bug report template](https://github.com/florisboard/florisboard/issues/new?template=bug_report.yml). This makes it easy for us to understand what the bug is and how to solve it.
|
||||
This kind of contribution is the most important, as it tells where FlorisBoard has flaws and thus should be improved to maximize stability and user experience. To make this process as smooth as possible, please use the pre-made [issue template](.github/ISSUE_TEMPLATE/bug_report.md) for bug reporting. This makes it easy for us to understand what the bug is and how to solve it.
|
||||
|
||||
#### Capturing error logs
|
||||
|
||||
Logs are captured by FlorisBoard's crash handler, which gives you the ability to copy it to the clipboard and paste it in the crash report [issue template](https://github.com/florisboard/florisboard/issues/new?template=crash_report.yml). This is the preferred way to capture logs.
|
||||
Logs are captured by FlorisBoard's crash handler, which gives you the ability to copy it to the clipboard and paste it in GitHub. This is the preferred way to capture logs.
|
||||
|
||||
Alternatively, you can also use ADB (Android Debug Bridge) to capture the error log. This is recommended for experienced users only.
|
||||
|
||||
### Feature proposals
|
||||
|
||||
Use the feature proposal [issue template](https://github.com/florisboard/florisboard/issues/new?template=feature_request.yml) to suggest a new idea or improvement for this project.
|
||||
|
||||
### Feedback
|
||||
|
||||
You can [give general feedback](https://github.com/florisboard/florisboard/discussions/new?category=feedback) directly here on GitHub. This is the preferred way to give feedback, as it allows not only for me to read and respond to feedback, but for everyone in this community.
|
||||
Use the feature proposal issue template to suggest a new idea or improvement for this project.
|
||||
|
||||
## Code contributions
|
||||
|
||||
@@ -40,15 +38,15 @@ You are always welcome to contribute new features or work on existing issues, th
|
||||
> [!NOTE]
|
||||
> If you intend to implement a bigger feature please coordinate with us so we can prevent that there's a major difference in expected implementation.
|
||||
|
||||
If you are overwhelmed by the code don't hesitate to ask for help in the [dev chat](https://matrix.to/#/#florisboard-dev:matrix.org) or the discussions tab! Some issues are also marked as good first issue, which are easy to do tasks.
|
||||
If you are overwhelmed by the code don't hesistate to ask for help in the [dev chat](https://matrix.to/#/#florisboard-dev:matrix.org) or the discussions tab! Some issues are also marked as good first issue, which are easy to do tasks.
|
||||
|
||||
### System requirements for development
|
||||
|
||||
- Desktop PC with Linux or WSL2 (Windows)
|
||||
- MacOS and Windows without WSL2 probably works too however there's no official support
|
||||
- At least 16GB of RAM (because of Android Studio / IntelliJ)
|
||||
- At least 16GB of RAM (because of Android Studio)
|
||||
- The following tools must be installed:
|
||||
- Android Studio (bundles SDK and NDK) or IntelliJ with Android and Compose plugin
|
||||
- Android Studio (bundles SDK and NDK)
|
||||
- Java 17
|
||||
- CMake 3.22+
|
||||
- Clang 15+
|
||||
@@ -66,7 +64,34 @@ If you want to manually build the project without Android Studio you must ensure
|
||||
|
||||
and Gradle should take care of every build task.
|
||||
|
||||
## Joining the team
|
||||
|
||||
If you want to join the core maintainer/moderator team on a volunteer basis and be part of this project's journey that's great to hear!
|
||||
|
||||
### Basic Requirements
|
||||
|
||||
- A passion for seeing FlorisBoard flourish
|
||||
- Good English skills for team and public communication
|
||||
- A GitHub account and a Matrix handle
|
||||
|
||||
### Why Join
|
||||
|
||||
You'll have the chance to work directly with me and other team members. While the general idea is for us to work on all kinds of different aspects of the project as a team, if you're particularly interested in a specific area (e.g., UI, extensions, text processing), that's totally okay too!
|
||||
|
||||
### Available Roles
|
||||
|
||||
Currently the following roles are available and need help:
|
||||
|
||||
Role Description | Required Dev Experience
|
||||
---|---
|
||||
Software Developer (Kotlin) for Core App | Java/Kotlin development experience (on Android)
|
||||
Software Developer (Rust) for Native Core | Some Rust development experience
|
||||
GitHub Issues/Discussions Moderator | None
|
||||
Crowdin Translation Verifier | Language proficiency for the language you want to verify
|
||||
|
||||
Interested? Feel free to dm me ([@patrickgold](https://matrix.to/#/@patrickgold:matrix.org)) on Matrix or send an email to [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev).
|
||||
|
||||
## Donating
|
||||
|
||||
You can also show your support by buying me a coffee, so I can stay up all night and chase away bugs or add new cool stuff :)
|
||||
Alternatively you can also show your support by buying me a coffee, so I can stay up all night and chase away bugs or add new cool stuff :)
|
||||
See the `Sponsors` button for available options!
|
||||
|
||||
@@ -64,7 +64,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Beginning with v0.7 FlorisBoard will enter the public beta on Google Play.
|
||||
Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
## Highlighted features
|
||||
- Integrated clipboard manager / history
|
||||
@@ -74,7 +74,7 @@ Beginning with v0.7 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Word suggestions/spell checking are not included in the current releases
|
||||
> and are a major goal for the v0.6 milestone.
|
||||
> and are a major goal for the v0.5 milestone.
|
||||
|
||||
Feature roadmap: See [ROADMAP.md](ROADMAP.md)
|
||||
|
||||
|
||||
71
ROADMAP.md
71
ROADMAP.md
@@ -1,63 +1,44 @@
|
||||
# Roadmap
|
||||
|
||||
This feature roadmap intents to provide transparency to what is planned to be added to FlorisBoard in the foreseeable future. Note that there are no ETAs for any version milestones down below, experience has shown these won't hold anyway.
|
||||
This feature roadmap intents to provide transparency to what is planned to be added to FlorisBoard in the foreseeable future. Note that there are no ETAs for any version milestones down below, experience has shown these won't hold anyways.
|
||||
|
||||
Each major milestone has associated alpha/beta releases, so if you are interested in previewing features quicker, keep an eye out! Each major 0.x release has also patch releases after the initial major release, which will be published on both the stable and preview tracks.
|
||||
|
||||
## 0.5 (currently in development)
|
||||
## 0.5
|
||||
|
||||
> [!NOTE]
|
||||
> The milestone 0.5 was split, thus the word suggestions now come with version 0.6. The old version 0.6 has been moved down and is now 0.7. The time it takes to implement word suggestions will not change, but we can now release the new theme editor earlier, which would otherwise lie dormant.
|
||||
|
||||
- [ ] Theme rework part II / Snygg v2
|
||||
- [x] See https://github.com/florisboard/florisboard/pull/2855
|
||||
- [x] Spaces in URI bug (See https://github.com/florisboard/florisboard/issues/2898)
|
||||
- [ ] Rework cache manager (See https://github.com/florisboard/florisboard/issues/2870)
|
||||
- [x] Re-add time based theme switching (See https://github.com/florisboard/florisboard/pull/2977)
|
||||
- [ ] Add support for any remaining new features introduced with Android 13 / 14
|
||||
- [ ] Proper physical keyboard support (See https://github.com/florisboard/florisboard/issues/2815)
|
||||
- [x] Raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
- Implement predictive text support / spell checking
|
||||
- Consider adding proximity-based key typo detection
|
||||
- Add new extension type: Language Pack
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
- New text processing logic (maybe moved back / split to 0.6)
|
||||
- Add floating keyboard mode
|
||||
- New keyboard layout engine + file syntax based on the upcoming Unicode Keyboard v3 standard
|
||||
- Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
- Add support for any remaining new features introduced with Android 13
|
||||
|
||||
## 0.6
|
||||
|
||||
- [ ] Implement predictive text support / spell checking
|
||||
- [ ] Add new extension type: Language Pack
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
|
||||
## k3lp
|
||||
|
||||
> [!NOTE]
|
||||
> The development of k3lp is not tied to a florisboard version and takes place on [codeberg.org](https://codeberg.org/k3lp/k3lp) simultaneously.
|
||||
|
||||
- [ ] New keyboard layout engine + file syntax based on the upcoming Unicode Keyboard v3 standard
|
||||
- [ ] Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
|
||||
## 0.7+
|
||||
|
||||
> [!NOTE]
|
||||
> From 0.6 onwards we plan to have more stable 0.X releases but with at most one large feature per release, thus having a much quicker iteration of new features on the stable track, which is a benefit for everyone involved.
|
||||
|
||||
- [ ] Add floating keyboard mode
|
||||
- [ ] New text processing logic
|
||||
- [ ] Complete rework of the Emoji panel
|
||||
- [ ] Emoji search
|
||||
- [ ] Fully scrollable emoji list (soft category borders)
|
||||
- [ ] Side scrollable emoji list (swipe for next category)
|
||||
- [ ] More granular theming options
|
||||
- [ ] Layout customization (e.g. placement of category buttons)
|
||||
- [ ] Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- [ ] Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- [ ] Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.7)
|
||||
- [ ] Rework branding images and texts of FlorisBoard for the app stores
|
||||
- [ ] Focus on stability and experience improvements of the app and keyboard
|
||||
- [ ] Add support for new features introduced with Android 15 / 16
|
||||
- Complete rework of the Emoji panel
|
||||
- Emoji search
|
||||
- Fully scrollable emoji list (soft category borders)
|
||||
- More granular themeing options
|
||||
- Layout customization (e.g. placement of category buttons)
|
||||
- Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.6)
|
||||
- Rework branding images and texts of FlorisBoard for the app stores
|
||||
- Focus on stability and experience improvements of the app and keyboard
|
||||
- Add support for new features introduced with Android 14 / 15
|
||||
- Not finalized, but planned: raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
|
||||
## Backlog / Planned (unassigned)
|
||||
|
||||
**Features that MAY be added (even in versions mentioned above) or dismissed**
|
||||
|
||||
- Full on-board layout editor which allows users to create their own layouts without writing a JSON file
|
||||
- Theme rework part II
|
||||
- Adaptive themes v2
|
||||
- Voice-to-text with Mozilla's open-source voice service (or any other oss voice provider)
|
||||
- Text translation
|
||||
- Stickers/GIFs
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.application)
|
||||
@@ -22,39 +23,35 @@ plugins {
|
||||
alias(libs.plugins.kotlin.plugin.compose)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.mannodermaus.android.junit5)
|
||||
alias(libs.plugins.mikepenz.aboutlibraries)
|
||||
}
|
||||
|
||||
val projectMinSdk: String by project
|
||||
val projectTargetSdk: String by project
|
||||
val projectCompileSdk: String by project
|
||||
val projectBuildToolsVersion: String by project
|
||||
val projectNdkVersion: String by project
|
||||
val projectVersionCode: String by project
|
||||
val projectVersionName: String by project
|
||||
val projectVersionNameSuffix = projectVersionName.substringAfter("-", "").let { suffix ->
|
||||
if (suffix.isNotEmpty()) {
|
||||
"-$suffix"
|
||||
} else {
|
||||
suffix
|
||||
}
|
||||
}
|
||||
val projectVersionNameSuffix = projectVersionName.substringAfter("-", "")
|
||||
|
||||
android {
|
||||
namespace = "dev.patrickgold.florisboard"
|
||||
compileSdk = projectCompileSdk.toInt()
|
||||
buildToolsVersion = tools.versions.buildTools.get()
|
||||
ndkVersion = tools.versions.ndk.get()
|
||||
buildToolsVersion = projectBuildToolsVersion
|
||||
ndkVersion = projectNdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs = listOf(
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-Xjvm-default=all-compatibility",
|
||||
"-Xwhen-guards",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -152,13 +149,7 @@ android {
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
collect {
|
||||
configPath = file("src/main/config")
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
baseline = file("lint.xml")
|
||||
configPath = "app/src/main/config"
|
||||
}
|
||||
|
||||
testOptions {
|
||||
@@ -171,21 +162,21 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
composeCompiler {
|
||||
// DO NOT ENABLE STRONG SKIPPING! This project currently relies on
|
||||
// recomposition on parent state change to update the UI correctly.
|
||||
featureFlags.add(ComposeFeatureFlag.StrongSkipping.disabled())
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val composeBom = platform(libs.androidx.compose.bom)
|
||||
implementation(composeBom)
|
||||
// testImplementation(composeBom)
|
||||
// androidTestImplementation(composeBom)
|
||||
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.activity.ktx)
|
||||
implementation(libs.androidx.autofill)
|
||||
implementation(libs.androidx.collection.ktx)
|
||||
implementation(libs.androidx.compose.material.icons)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.runtime.livedata)
|
||||
implementation(libs.androidx.compose.ui)
|
||||
@@ -195,6 +186,7 @@ dependencies {
|
||||
implementation(libs.androidx.emoji2)
|
||||
implementation(libs.androidx.emoji2.views)
|
||||
implementation(libs.androidx.exifinterface)
|
||||
implementation(libs.androidx.material.icons)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.profileinstaller)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
@@ -215,7 +207,12 @@ dependencies {
|
||||
implementation(project(":lib:native"))
|
||||
implementation(project(":lib:snygg"))
|
||||
|
||||
testImplementation(libs.kotlin.test.junit5)
|
||||
testImplementation(libs.equalsverifier)
|
||||
testImplementation(libs.kotest.assertions.core)
|
||||
testImplementation(libs.kotest.extensions.roboelectric)
|
||||
testImplementation(libs.kotest.property)
|
||||
testImplementation(libs.kotest.runner.junit5)
|
||||
|
||||
androidTestImplementation(libs.androidx.test.ext)
|
||||
androidTestImplementation(libs.androidx.test.espresso.core)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<lint>
|
||||
<issue id="ExtraTranslation" severity="warning"/>
|
||||
<issue id="UsingMaterialAndMaterial3Libraries" severity="ignore" />
|
||||
</lint>
|
||||
|
||||
@@ -114,7 +114,6 @@
|
||||
android:name="dev.patrickgold.florisboard.lib.crashutility.CrashDialogActivity"
|
||||
android:icon="@mipmap/floris_app_icon"
|
||||
android:label="@string/crash_dialog__title"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@style/CrashDialogTheme"/>
|
||||
|
||||
<!-- Copy to Clipboard Activity -->
|
||||
|
||||
@@ -17,13 +17,6 @@
|
||||
"direction": "rtl",
|
||||
"modifier": "org.florisboard.layouts:arabic"
|
||||
},
|
||||
{
|
||||
"id": "armenian_alt_phonetic",
|
||||
"label": "Armenian Alt Phonetic",
|
||||
"authors": [ "MikayelB" ],
|
||||
"direction": "ltr",
|
||||
"modifier": "org.florisboard.layouts:armenian"
|
||||
},
|
||||
{
|
||||
"id": "western_armenian",
|
||||
"label": "Armenian (Western)",
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 2535, "label": "১", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2536, "label": "২", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2537, "label": "৩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2538, "label": "৪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2539, "label": "৫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 2540, "label": "৬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2542, "label": "৮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2543, "label": "৯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2534, "label": "০", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
{ "code": 32902, "label": "肆" },
|
||||
{ "code": 18825, "label": "䦉" },
|
||||
{ "code": 20118, "label": "亖" },
|
||||
{ "code": 65300, "label": "4" }
|
||||
{ "code": 65300, "label": "3" }
|
||||
]
|
||||
} },
|
||||
{ "code": 20116, "label": "五", "type": "numeric", "popup": {
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 2407, "label": "१", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2408, "label": "२", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2409, "label": "३", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2410, "label": "४", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2411, "label": "५", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 2412, "label": "६", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2414, "label": "८", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2415, "label": "९", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2406, "label": "०", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 1633, "label": "١", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1634, "label": "٢", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1635, "label": "٣", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1636, "label": "٤", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1637, "label": "٥", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 1638, "label": "٦", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1640, "label": "٨", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1641, "label": "٩", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1632, "label": "٠", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 2791, "label": "૧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2792, "label": "૨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2793, "label": "૩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2794, "label": "૪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2795, "label": "૫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 2796, "label": "૬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2798, "label": "૮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2799, "label": "૯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2790, "label": "૦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 2663, "label": "੧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2664, "label": "੨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2665, "label": "੩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2666, "label": "੪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2667, "label": "੫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 2668, "label": "੬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2670, "label": "੮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2671, "label": "੯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2662, "label": "੦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 3303, "label": "೧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3304, "label": "೨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3305, "label": "೩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3306, "label": "೪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3307, "label": "೫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 3308, "label": "೬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3310, "label": "೮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3311, "label": "೯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3302, "label": "೦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 3431, "label": "൧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3432, "label": "൨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3433, "label": "൩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3434, "label": "൪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3435, "label": "൫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 3436, "label": "൬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3438, "label": "൮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3439, "label": "൯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3430, "label": "൦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 2919, "label": "୧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2920, "label": "୨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2921, "label": "୩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2922, "label": "୪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2923, "label": "୫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 2924, "label": "୬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2926, "label": "୮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2927, "label": "୯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2918, "label": "୦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 1777, "label": "۱", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1778, "label": "۲", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1779, "label": "۳", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1780, "label": "۴", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1781, "label": "۵", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 1782, "label": "۶", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1784, "label": "۸", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1785, "label": "۹", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1776, "label": "۰", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 3047, "label": "௧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3048, "label": "௨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3049, "label": "௩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3050, "label": "௪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3051, "label": "௫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 3052, "label": "௬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3054, "label": "௮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3055, "label": "௯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3046, "label": "௦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 3175, "label": "౧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3176, "label": "౨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3177, "label": "౩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3178, "label": "౪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3179, "label": "౫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 3180, "label": "౬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3182, "label": "౮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3183, "label": "౯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3174, "label": "౦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,23 +3,20 @@
|
||||
{ "code": 3665, "label": "๑", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3666, "label": "๒", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
@@ -27,59 +24,44 @@
|
||||
{ "code": 3667, "label": "๓", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" }
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3668, "label": "๔", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3669, "label": "๕", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3670, "label": "๖", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" }
|
||||
]
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
} },
|
||||
{ "code": 3671, "label": "๗", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3672, "label": "๘", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" }
|
||||
]
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
} },
|
||||
{ "code": 3673, "label": "๙", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" }
|
||||
]
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
} },
|
||||
{ "code": 3664, "label": "๐", "type": "numeric", "popup": {
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,52 +3,46 @@
|
||||
{ "code": 71905, "label": "𑣡", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71906, "label": "𑣢", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71907, "label": "𑣣", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71908, "label": "𑣤", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71909, "label": "𑣥", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -56,7 +50,6 @@
|
||||
{ "code": 71910, "label": "𑣦", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -64,32 +57,27 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71912, "label": "𑣨", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71913, "label": "𑣩", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71904, "label": "𑣠", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -10,17 +10,15 @@
|
||||
"code": 49, "label": "1", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -31,7 +29,6 @@
|
||||
"code": 50, "label": "2", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
@@ -48,10 +45,9 @@
|
||||
"code": 51, "label": "3", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8535, "label": "⅗" }
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -72,7 +68,6 @@
|
||||
"code": 52, "label": "4", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
}
|
||||
@@ -91,7 +86,6 @@
|
||||
"code": 53, "label": "5", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -111,10 +105,7 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 54, "label": "6", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" }
|
||||
]
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -124,7 +115,6 @@
|
||||
"code": 55, "label": "7", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
}
|
||||
@@ -142,10 +132,7 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 56, "label": "8", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" }
|
||||
]
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -173,10 +160,7 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 57, "label": "9", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" }
|
||||
]
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -207,9 +191,7 @@
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,16 +401,6 @@
|
||||
"characters": "org.florisboard.layouts:qwertz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hy",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
"currencySet": "org.florisboard.currencysets:armenian_dram",
|
||||
"popupMapping": "org.florisboard.localization:hy",
|
||||
"preferred": {
|
||||
"characters": "org.florisboard.layouts:armenian_alt_phonetic",
|
||||
"symbols": "org.florisboard.layouts:armenian"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hy",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
"main": { "$": "auto_text_key" ,"code" : 237, "label": "í" }
|
||||
},
|
||||
"o": {
|
||||
"main": { "$": "auto_text_key", "code" : 243, "label": "ó" },
|
||||
"main": { "$": "auto_text_key", "code" : 246, "label": "ö" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code" : 246, "label": "ö" },
|
||||
{ "$": "auto_text_key", "code" : 243, "label": "ó" },
|
||||
{ "$": "auto_text_key", "code" : 337, "label": "ő" }
|
||||
]
|
||||
},
|
||||
@@ -22,9 +22,9 @@
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"main": { "$": "auto_text_key", "code" : 250, "label": "ú" },
|
||||
"main": { "$": "auto_text_key", "code" : 252, "label": "ü" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code" : 252, "label": "ü" },
|
||||
{ "$": "auto_text_key", "code" : 250, "label": "ú" },
|
||||
{ "$": "auto_text_key", "code" : 369, "label": "ű" }
|
||||
]
|
||||
},
|
||||
|
||||
65
app/src/main/assets/ime/theme/gboard_day.json
Normal file
65
app/src/main/assets/ime/theme/gboard_day.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
|
||||
"name": "gboard_day",
|
||||
"label": "Gboard Day",
|
||||
"authors": [ "patrickgold", "itskareem" ],
|
||||
"isNightTheme": false,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#0479ed",
|
||||
"colorPrimaryDark": "#0467c9",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/background",
|
||||
"navigationBarLight": "true",
|
||||
"semiTransparentColor": "#20000000",
|
||||
"textColor": "#000000"
|
||||
},
|
||||
"keyboard": {
|
||||
"background": "#D1D6DC"
|
||||
},
|
||||
"key": {
|
||||
"background": "#FCFFFF",
|
||||
"backgroundPressed": "#F5F5F5",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundPressed": "@window/textColor",
|
||||
"showBorder": "true"
|
||||
},
|
||||
"key:enter": {
|
||||
"background": "@window/colorPrimary",
|
||||
"backgroundPressed": "@window/colorPrimaryDark",
|
||||
"foreground": "#FFFFFF",
|
||||
"foregroundPressed": "#FFFFFF"
|
||||
},
|
||||
"key:shift:capslock": {
|
||||
"foreground": "@window/colorAccent",
|
||||
"foregroundPressed": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#757575"
|
||||
},
|
||||
"oneHanded": {
|
||||
"background": "@keyboard/background",
|
||||
"foreground": "#424242"
|
||||
},
|
||||
"popup": {
|
||||
"background": "#EEEEEE",
|
||||
"backgroundActive": "#BDBDBD",
|
||||
"foreground": "@window/textColor"
|
||||
},
|
||||
"privateMode": {
|
||||
"background": "#A000FF",
|
||||
"foreground": "#FFFFFF"
|
||||
},
|
||||
"smartbar": {
|
||||
"background": "@keyboard/background",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#8A8A8A"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"background": "@key/background",
|
||||
"foreground": "@key/foreground"
|
||||
},
|
||||
"glideTrail": {"foreground": "#200479ed"}
|
||||
}
|
||||
}
|
||||
65
app/src/main/assets/ime/theme/gboard_night.json
Normal file
65
app/src/main/assets/ime/theme/gboard_night.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
|
||||
"name": "gboard_night",
|
||||
"label": "Gboard Night",
|
||||
"authors": [ "Netscaping" ],
|
||||
"isNightTheme": true,
|
||||
"attributes": {
|
||||
"window": {
|
||||
"colorPrimary": "#5e97f6",
|
||||
"colorPrimaryDark": "#4285f4",
|
||||
"colorAccent": "#FF9800",
|
||||
"navigationBarColor": "@keyboard/background",
|
||||
"navigationBarLight": "false",
|
||||
"semiTransparentColor": "#20FFFFFF",
|
||||
"textColor": "#FFFFFF"
|
||||
},
|
||||
"keyboard": {
|
||||
"background": "#292e33"
|
||||
},
|
||||
"key": {
|
||||
"background": "#484c4f",
|
||||
"backgroundPressed": "#5e5e60",
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundPressed": "@window/textColor",
|
||||
"showBorder": "true"
|
||||
},
|
||||
"key:enter": {
|
||||
"background": "@window/colorPrimary",
|
||||
"backgroundPressed": "@window/colorPrimaryDark",
|
||||
"foreground": "#FFFFFF",
|
||||
"foregroundPressed": "#FFFFFF"
|
||||
},
|
||||
"key:shift:capslock": {
|
||||
"foreground": "@window/colorAccent",
|
||||
"foregroundPressed": "@window/colorAccent"
|
||||
},
|
||||
"media": {
|
||||
"foreground": "@window/textColor",
|
||||
"foregroundAlt": "#BDBDBD"
|
||||
},
|
||||
"oneHanded": {
|
||||
"background": "#373c41",
|
||||
"foreground": "#9b9da0"
|
||||
},
|
||||
"popup": {
|
||||
"background": "#373c41",
|
||||
"backgroundActive": "#5a5e60",
|
||||
"foreground": "@window/textColor"
|
||||
},
|
||||
"privateMode": {
|
||||
"background": "#A000FF",
|
||||
"foreground": "#FFFFFF"
|
||||
},
|
||||
"smartbar": {
|
||||
"background": "transparent",
|
||||
"foreground": "#d4d5d6",
|
||||
"foregroundAlt": "#73FFFFFF"
|
||||
},
|
||||
"smartbarButton": {
|
||||
"background": "#FFFFFF",
|
||||
"foreground": "#686868"
|
||||
},
|
||||
"glideTrail": {"foreground": "#205e97f6"}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
"$": "ime.extension.theme",
|
||||
"meta": {
|
||||
"id": "org.florisboard.themes",
|
||||
"version": "0.2.0",
|
||||
"version": "0.1.0",
|
||||
"title": "FlorisBoard default themes",
|
||||
"description": "Default themes (both day and night) for the keyboard UI",
|
||||
"maintainers": [ "patrickgold <patrick@patrickgold.dev>", "lm41 <lm41@lm41.xyz>" ],
|
||||
"maintainers": [ "patrickgold <patrick@patrickgold.dev>" ],
|
||||
"license": "apache-2.0"
|
||||
},
|
||||
"themes": [
|
||||
@@ -13,37 +13,49 @@
|
||||
"id": "floris_day",
|
||||
"label": "Floris Day",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNight": false
|
||||
"isNight": false,
|
||||
"isBorderless": false,
|
||||
"isMaterialYouAware": false
|
||||
},
|
||||
{
|
||||
"id": "floris_day_borderless",
|
||||
"label": "Floris Day (Borderless)",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNight": false
|
||||
"isNight": false,
|
||||
"isBorderless": true,
|
||||
"isMaterialYouAware": false
|
||||
},
|
||||
{
|
||||
"id": "floris_night",
|
||||
"label": "Floris Night",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNight": true
|
||||
"isNight": true,
|
||||
"isBorderless": false,
|
||||
"isMaterialYouAware": false
|
||||
},
|
||||
{
|
||||
"id": "floris_night_borderless",
|
||||
"label": "Floris Night (Borderless)",
|
||||
"authors": [ "patrickgold" ],
|
||||
"isNight": true
|
||||
"isNight": true,
|
||||
"isBorderless": true,
|
||||
"isMaterialYouAware": false
|
||||
},
|
||||
{
|
||||
"id": "floris_pure_night",
|
||||
"label": "Floris Pure Night",
|
||||
"authors": [ "serebit" ],
|
||||
"isNight": true
|
||||
"isNight": true,
|
||||
"isBorderless": false,
|
||||
"isMaterialYouAware": false
|
||||
},
|
||||
{
|
||||
"id": "floris_pure_night_borderless",
|
||||
"label": "Floris Pure Night (Borderless)",
|
||||
"authors": [ "serebit" ],
|
||||
"isNight": true
|
||||
"isNight": true,
|
||||
"isBorderless": true,
|
||||
"isMaterialYouAware": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,40 +1,23 @@
|
||||
{
|
||||
"$schema": "https://schemas.florisboard.org/snygg/v2/stylesheet",
|
||||
"@defines": {
|
||||
"--primary": "#4caf50",
|
||||
"--primary-variant": "#388e3c",
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#e0e0e0",
|
||||
"--background-variant": "#d0d0d0",
|
||||
"--surface": "#ffffff",
|
||||
"--surface-variant": "#f5f5f5",
|
||||
|
||||
"--popup-surface": "#eeeeee",
|
||||
"--focused-popup-surface": "#bdbdbd",
|
||||
|
||||
"--drag-marker": "rgb(255,0,0)",
|
||||
"--spacer-color": "rgba(0 ,0, 0, 0.25)",
|
||||
|
||||
"--one-hand-background": "#e8f5e9",
|
||||
"--one-hand-foreground": "#424242",
|
||||
|
||||
"--incognito-icon-color": "#00000011",
|
||||
|
||||
"--on-primary": "#f0f0f0",
|
||||
"--on-background": "#121212",
|
||||
"--on-background-disabled": "rgb(175,175,175)",
|
||||
"--on-surface": "#000000",
|
||||
"--on-surface-variant": "#5f5f5f",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)"
|
||||
"keyboard": {
|
||||
"background": "var(--background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -42,322 +25,170 @@
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "1"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key:pressed": {
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]": {
|
||||
"key[code={c:enter}]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]:pressed": {
|
||||
"key[code={c:enter}]:pressed": {
|
||||
"background": "var(--primary-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=32]": {
|
||||
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key[code={c:space}]": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"key[code=-201,-202,-203]": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"key[code=-204,-205]": {
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key[code=-205]": {
|
||||
"text-max-lines": "2"
|
||||
},
|
||||
"key[code=-11][shiftstate=`caps_lock`]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key-hint": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-family": "monospace",
|
||||
"font-size": "12sp",
|
||||
"padding": "0dp 1dp 1dp 0dp",
|
||||
"text-max-lines": "1"
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"key-popup": {
|
||||
"background": "#eeeeee",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
"key-popup:focus": {
|
||||
"background": "#bdbdbd",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"smartbar-shared-actions-toggle": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
"background": "transparent",
|
||||
"foreground": "#12121248"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "var(--background-variant)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
"background": "transparent",
|
||||
"foreground": "#12121248"
|
||||
},
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"smartbar-actions-editor-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "16sp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-subheader": {
|
||||
"foreground": "var(--secondary)",
|
||||
"font-size": "16sp",
|
||||
"font-weight": "bold",
|
||||
"padding": "12dp 16dp 12dp 8dp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-tile-grid": {
|
||||
"margin": "4dp 0dp"
|
||||
},
|
||||
"smartbar-actions-editor-tile": {
|
||||
"margin": "4dp",
|
||||
"padding": "8dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-999]": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-991]": {
|
||||
"foreground": "var(--drag-marker)"
|
||||
},
|
||||
|
||||
"smartbar-candidate-word": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rectangle()",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rectangle()"
|
||||
},
|
||||
"smartbar-candidate-word:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
|
||||
},
|
||||
"smartbar-candidate-clip:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--spacer-color)"
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"clipboard-header-button:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"clipboard-header-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "1dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-message": {
|
||||
"padding": "16dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-buttons": {
|
||||
"padding": "4dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-button": {
|
||||
|
||||
"emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"emoji-key-popup": {
|
||||
"background": "#eeeeee",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-history-disabled-title": {
|
||||
"font-weight": "bold"
|
||||
"emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"clipboard-history-disabled-message": {
|
||||
"padding": "0dp 4dp 0dp 8dp"
|
||||
},
|
||||
"clipboard-history-disabled-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"shape": "rounded-corner(24dp,24dp,24dp,24dp)"
|
||||
},
|
||||
"clipboard-history-locked-title": {
|
||||
"font-weight": "bold",
|
||||
"text-align": "center"
|
||||
},
|
||||
"clipboard-history-locked-message": {
|
||||
"padding": "0dp 4dp 0dp 0dp",
|
||||
"text-align": "center"
|
||||
"emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
|
||||
"extracted-landscape-input-layout": {
|
||||
@@ -382,82 +213,15 @@
|
||||
},
|
||||
|
||||
"incognito-mode-indicator": {
|
||||
"foreground": "var(--incognito-icon-color)"
|
||||
},
|
||||
|
||||
"inline-autofill-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"media-emoji-subheader": {
|
||||
"font-weight": "bold",
|
||||
"margin": "4dp"
|
||||
},
|
||||
"media-emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"media-emoji-key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"media-emoji-key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"media-emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
"media-bottom-row-button": {
|
||||
"padding": "16dp 0dp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key-popup-extended-indicator": {
|
||||
"foreground": "inherit"
|
||||
"foreground": "#00000011"
|
||||
},
|
||||
|
||||
"one-handed-panel": {
|
||||
"background": "var(--one-hand-background)",
|
||||
"foreground": "var(--one-hand-foreground)"
|
||||
"background": "#e8f5e9",
|
||||
"foreground": "#424242"
|
||||
},
|
||||
|
||||
"subtype-panel": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"subtype-panel-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "18sp",
|
||||
"padding": "12dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"system-nav-bar": {
|
||||
"background": "var(--background)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,83 @@
|
||||
{
|
||||
"$schema": "https://schemas.florisboard.org/snygg/v2/stylesheet",
|
||||
"@defines": {
|
||||
"--primary": "#4caf50",
|
||||
"--primary-variant": "#388e3c",
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#e0e0e0",
|
||||
"--background-variant": "#d0d0d0",
|
||||
"--surface": "#f0f0f0",
|
||||
"--surface-variant": "#ffffff",
|
||||
|
||||
"--popup-surface": "#eeeeee",
|
||||
"--focused-popup-surface": "#bdbdbd",
|
||||
|
||||
"--drag-marker": "rgb(255,0,0)",
|
||||
"--spacer-color": "rgba(0 ,0, 0, 0.25)",
|
||||
|
||||
"--one-hand-background": "#e8f5e9",
|
||||
"--one-hand-foreground": "#424242",
|
||||
|
||||
"--incognito-icon-color": "#00000011",
|
||||
|
||||
"--on-primary": "#f0f0f0",
|
||||
"--on-background": "#121212",
|
||||
"--on-background-disabled": "#12121248",
|
||||
"--on-surface": "#000000",
|
||||
"--on-surface-variant": "#5f5f5f",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)"
|
||||
"keyboard": {
|
||||
"background": "var(--background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "1"
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]": {
|
||||
"key[code={c:enter}]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "0dp 6dp"
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]:pressed": {
|
||||
"key[code={c:enter}]:pressed": {
|
||||
"background": "var(--primary-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=32]": {
|
||||
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key[code={c:space}]": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"margin": "0dp 6dp",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"key[code=-201,-202,-203]": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"key[code=-204,-205]": {
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key[code=-205]": {
|
||||
"text-max-lines": "2"
|
||||
},
|
||||
"key[code=-11][shiftstate=`caps_lock`]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key-hint": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"font-family": "monospace",
|
||||
"padding": "0dp 1dp 1dp 0dp",
|
||||
"text-max-lines": "1"
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"key-popup": {
|
||||
"background": "#eeeeee",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
"key-popup:focus": {
|
||||
"background": "#bdbdbd",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"smartbar-shared-actions-toggle": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-key:pressed": {
|
||||
@@ -126,244 +85,109 @@
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
"background": "transparent",
|
||||
"foreground": "#12121248"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "var(--background-variant)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
"background": "transparent",
|
||||
"foreground": "#12121248"
|
||||
},
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"smartbar-actions-editor-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "16sp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-subheader": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp",
|
||||
"font-weight": "bold",
|
||||
"padding": "12dp 16dp 12dp 8dp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-tile-grid": {
|
||||
"margin": "4dp 0dp"
|
||||
},
|
||||
"smartbar-actions-editor-tile": {
|
||||
"margin": "4dp",
|
||||
"padding": "8dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-999]": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-991]": {
|
||||
"foreground": "var(--drag-marker)"
|
||||
},
|
||||
|
||||
"smartbar-candidate-word": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rectangle()",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rectangle()"
|
||||
},
|
||||
"smartbar-candidate-word:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
|
||||
},
|
||||
"smartbar-candidate-clip:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"clipboard-header-button:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"clipboard-header-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "1dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-message": {
|
||||
"padding": "16dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-buttons": {
|
||||
"padding": "4dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-button": {
|
||||
|
||||
"emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"emoji-key-popup": {
|
||||
"background": "#eeeeee",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-history-disabled-title": {
|
||||
"font-weight": "bold"
|
||||
"emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"clipboard-history-disabled-message": {
|
||||
"padding": "0dp 4dp 0dp 8dp"
|
||||
},
|
||||
"clipboard-history-disabled-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"shape": "rounded-corner(24dp,24dp,24dp,24dp)"
|
||||
},
|
||||
"clipboard-history-locked-title": {
|
||||
"font-weight": "bold",
|
||||
"text-align": "center"
|
||||
},
|
||||
"clipboard-history-locked-message": {
|
||||
"padding": "0dp 4dp 0dp 0dp",
|
||||
"text-align": "center"
|
||||
"emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
|
||||
"extracted-landscape-input-layout": {
|
||||
@@ -388,82 +212,15 @@
|
||||
},
|
||||
|
||||
"incognito-mode-indicator": {
|
||||
"foreground": "var(--incognito-icon-color)"
|
||||
},
|
||||
|
||||
"inline-autofill-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"media-emoji-subheader": {
|
||||
"font-weight": "bold",
|
||||
"margin": "4dp"
|
||||
},
|
||||
"media-emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"media-emoji-key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"media-emoji-key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"media-emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
"media-bottom-row-button": {
|
||||
"padding": "16dp 0dp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key-popup-extended-indicator": {
|
||||
"foreground": "inherit"
|
||||
"foreground": "#00000011"
|
||||
},
|
||||
|
||||
"one-handed-panel": {
|
||||
"background": "var(--one-hand-background)",
|
||||
"foreground": "var(--one-hand-foreground)"
|
||||
"background": "#e8f5e9",
|
||||
"foreground": "#424242"
|
||||
},
|
||||
|
||||
"subtype-panel": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"subtype-panel-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "18sp",
|
||||
"padding": "12dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"system-nav-bar": {
|
||||
"background": "var(--background)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
{
|
||||
"$schema": "https://schemas.florisboard.org/snygg/v2/stylesheet",
|
||||
"@defines": {
|
||||
"--primary": "#4caf50",
|
||||
"--primary-variant": "#388e3c",
|
||||
"--secondary": "#f57c00",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#212121",
|
||||
"--background-variant": "#313131",
|
||||
"--surface": "#424242",
|
||||
"--surface-variant": "#616161",
|
||||
"--popup-surface": "#757575",
|
||||
"--focused-popup-surface": "#bdbdbd",
|
||||
"--drag-marker": "rgb(255,0,0)",
|
||||
"--spacer-color": "rgba(255, 255, 255, 0.25)",
|
||||
"--one-hand-background": "#1b5e20",
|
||||
"--one-hand-foreground": "#eeeeee",
|
||||
"--incognito-icon-color": "#ffffff11",
|
||||
"--on-primary": "#f0f0f0",
|
||||
|
||||
"--on-background": "#dcdcdc",
|
||||
"--on-background-disabled": "#dcdcdc48",
|
||||
"--on-surface": "#ffffff",
|
||||
"--on-surface-variant": "#a0a0a0",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)"
|
||||
"keyboard": {
|
||||
"background": "var(--background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -37,322 +25,170 @@
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "1"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key:pressed": {
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]": {
|
||||
"key[code={c:enter}]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]:pressed": {
|
||||
"key[code={c:enter}]:pressed": {
|
||||
"background": "var(--primary-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=32]": {
|
||||
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key[code={c:space}]": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"key[code=-201,-202,-203]": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"key[code=-204,-205]": {
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key[code=-205]": {
|
||||
"text-max-lines": "2"
|
||||
},
|
||||
"key[code=-11][shiftstate=`caps_lock`]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key-hint": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-family": "monospace",
|
||||
"font-size": "12sp",
|
||||
"padding": "0dp 1dp 1dp 0dp",
|
||||
"text-max-lines": "1"
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"key-popup": {
|
||||
"background": "#757575",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
"key-popup:focus": {
|
||||
"background": "#bdbdbd",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"smartbar-shared-actions-toggle": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "var(--background-variant)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"smartbar-actions-editor-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "16sp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-subheader": {
|
||||
"foreground": "var(--secondary)",
|
||||
"font-size": "16sp",
|
||||
"font-weight": "bold",
|
||||
"padding": "12dp 16dp 12dp 8dp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-tile-grid": {
|
||||
"margin": "4dp 0dp"
|
||||
},
|
||||
"smartbar-actions-editor-tile": {
|
||||
"margin": "4dp",
|
||||
"padding": "8dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-999]": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-991]": {
|
||||
"foreground": "var(--drag-marker)"
|
||||
},
|
||||
|
||||
"smartbar-candidate-word": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rectangle()",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rectangle()"
|
||||
},
|
||||
"smartbar-candidate-word:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
|
||||
},
|
||||
"smartbar-candidate-clip:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--spacer-color)"
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"clipboard-header-button:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"clipboard-header-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "1dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-message": {
|
||||
"padding": "16dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-buttons": {
|
||||
"padding": "4dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-button": {
|
||||
|
||||
"emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"emoji-key-popup": {
|
||||
"background": "#757575",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-history-disabled-title": {
|
||||
"font-weight": "bold"
|
||||
"emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"clipboard-history-disabled-message": {
|
||||
"padding": "0dp 4dp 0dp 8dp"
|
||||
},
|
||||
"clipboard-history-disabled-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"shape": "rounded-corner(24dp,24dp,24dp,24dp)"
|
||||
},
|
||||
"clipboard-history-locked-title": {
|
||||
"font-weight": "bold",
|
||||
"text-align": "center"
|
||||
},
|
||||
"clipboard-history-locked-message": {
|
||||
"padding": "0dp 4dp 0dp 0dp",
|
||||
"text-align": "center"
|
||||
"emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
|
||||
"extracted-landscape-input-layout": {
|
||||
@@ -377,81 +213,15 @@
|
||||
},
|
||||
|
||||
"incognito-mode-indicator": {
|
||||
"foreground": "var(--incognito-icon-color)"
|
||||
},
|
||||
|
||||
"inline-autofill-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"media-emoji-subheader": {
|
||||
"font-weight": "bold",
|
||||
"margin": "4dp"
|
||||
},
|
||||
"media-emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"media-emoji-key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"media-emoji-key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"media-emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
"media-bottom-row-button": {
|
||||
"padding": "16dp 0dp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key-popup-extended-indicator": {
|
||||
"foreground": "inherit"
|
||||
"foreground": "#ffffff11"
|
||||
},
|
||||
|
||||
"one-handed-panel": {
|
||||
"background": "var(--one-hand-background)",
|
||||
"foreground": "var(--one-hand-foreground)"
|
||||
"background": "#1b5e20",
|
||||
"foreground": "#eeeeee"
|
||||
},
|
||||
"subtype-panel": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"subtype-panel-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "18sp",
|
||||
"padding": "12dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
|
||||
"system-nav-bar": {
|
||||
"background": "var(--background)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +1,83 @@
|
||||
{
|
||||
"$schema": "https://schemas.florisboard.org/snygg/v2/stylesheet",
|
||||
"@defines": {
|
||||
"--primary": "#4caf50",
|
||||
"--primary-variant": "#388e3c",
|
||||
"--secondary": "#f57c00",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#212121",
|
||||
"--background-variant": "#313131",
|
||||
"--surface": "#424242",
|
||||
"--surface-variant": "#616161",
|
||||
"--popup-surface": "#757575",
|
||||
"--focused-popup-surface": "#bdbdbd",
|
||||
"--drag-marker": "rgb(255,0,0)",
|
||||
"--spacer-color": "rgba(255, 255, 255, 0.25)",
|
||||
"--one-hand-background": "#1b5e20",
|
||||
"--one-hand-foreground": "#eeeeee",
|
||||
"--incognito-icon-color": "#ffffff11",
|
||||
"--on-primary": "#f0f0f0",
|
||||
|
||||
"--on-background": "#dcdcdc",
|
||||
"--on-background-disabled": "#dcdcdc48",
|
||||
"--on-surface": "#ffffff",
|
||||
"--on-surface-variant": "#a0a0a0",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)"
|
||||
"keyboard": {
|
||||
"background": "var(--background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "1"
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]": {
|
||||
"key[code={c:enter}]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "0dp 6dp"
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]:pressed": {
|
||||
"key[code={c:enter}]:pressed": {
|
||||
"background": "var(--primary-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=32]": {
|
||||
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key[code={c:space}]": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"margin": "0dp 6dp",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"key[code=-201,-202,-203]": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"key[code=-204,-205]": {
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key[code=-205]": {
|
||||
"text-max-lines": "2"
|
||||
},
|
||||
"key[code=-11][shiftstate=`caps_lock`]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key-hint": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"font-family": "monospace",
|
||||
"padding": "0dp 1dp 1dp 0dp",
|
||||
"text-max-lines": "1"
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"key-popup": {
|
||||
"background": "#757575",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
"key-popup:focus": {
|
||||
"background": "#bdbdbd",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"smartbar-shared-actions-toggle": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-key:pressed": {
|
||||
@@ -121,244 +85,109 @@
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "var(--background-variant)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"smartbar-actions-editor-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "16sp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-subheader": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp",
|
||||
"font-weight": "bold",
|
||||
"padding": "12dp 16dp 12dp 8dp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-tile-grid": {
|
||||
"margin": "4dp 0dp"
|
||||
},
|
||||
"smartbar-actions-editor-tile": {
|
||||
"margin": "4dp",
|
||||
"padding": "8dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-999]": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-991]": {
|
||||
"foreground": "var(--drag-marker)"
|
||||
},
|
||||
|
||||
"smartbar-candidate-word": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rectangle()",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rectangle()"
|
||||
},
|
||||
"smartbar-candidate-word:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
|
||||
},
|
||||
"smartbar-candidate-clip:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"clipboard-header-button:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"clipboard-header-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "1dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-message": {
|
||||
"padding": "16dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-buttons": {
|
||||
"padding": "4dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-button": {
|
||||
|
||||
"emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"emoji-key-popup": {
|
||||
"background": "#757575",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-history-disabled-title": {
|
||||
"font-weight": "bold"
|
||||
"emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"clipboard-history-disabled-message": {
|
||||
"padding": "0dp 4dp 0dp 8dp"
|
||||
},
|
||||
"clipboard-history-disabled-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"shape": "rounded-corner(24dp,24dp,24dp,24dp)"
|
||||
},
|
||||
"clipboard-history-locked-title": {
|
||||
"font-weight": "bold",
|
||||
"text-align": "center"
|
||||
},
|
||||
"clipboard-history-locked-message": {
|
||||
"padding": "0dp 4dp 0dp 0dp",
|
||||
"text-align": "center"
|
||||
"emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
|
||||
"extracted-landscape-input-layout": {
|
||||
@@ -383,81 +212,15 @@
|
||||
},
|
||||
|
||||
"incognito-mode-indicator": {
|
||||
"foreground": "var(--incognito-icon-color)"
|
||||
},
|
||||
|
||||
"inline-autofill-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"media-emoji-subheader": {
|
||||
"font-weight": "bold",
|
||||
"margin": "4dp"
|
||||
},
|
||||
"media-emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"media-emoji-key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"media-emoji-key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"media-emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
"media-bottom-row-button": {
|
||||
"padding": "16dp 0dp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key-popup-extended-indicator": {
|
||||
"foreground": "inherit"
|
||||
"foreground": "#ffffff11"
|
||||
},
|
||||
|
||||
"one-handed-panel": {
|
||||
"background": "var(--one-hand-background)",
|
||||
"foreground": "var(--one-hand-foreground)"
|
||||
"background": "#1b5e20",
|
||||
"foreground": "#eeeeee"
|
||||
},
|
||||
"subtype-panel": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"subtype-panel-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "18sp",
|
||||
"padding": "12dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
|
||||
"system-nav-bar": {
|
||||
"background": "var(--background)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
{
|
||||
"$schema": "https://schemas.florisboard.org/snygg/v2/stylesheet",
|
||||
"@defines": {
|
||||
"--primary": "#388e3c",
|
||||
"--primary-variant": "#306d32",
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#804c00",
|
||||
"--background": "#000000",
|
||||
"--background-variant": "#111111",
|
||||
"--surface": "#212121",
|
||||
"--surface-variant": "#3d3d3d",
|
||||
"--popup-surface": "#424242",
|
||||
"--focused-popup-surface": "#707070",
|
||||
"--drag-marker": "rgb(255,0,0)",
|
||||
"--spacer-color": "rgba(255, 255, 255, 0.25)",
|
||||
"--one-hand-background": "#1b5e20",
|
||||
"--one-hand-foreground": "#eeeeee",
|
||||
"--incognito-icon-color": "#ffffff11",
|
||||
"--on-primary": "#f0f0f0",
|
||||
"--on-background-disabled": "#dcdcdc48",
|
||||
|
||||
"--on-background": "#eeeeee",
|
||||
"--on-surface": "#eeeeee",
|
||||
"--on-surface-variant": "#ffffff73",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)"
|
||||
"keyboard": {
|
||||
"background": "var(--background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -37,81 +25,60 @@
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "1"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key:pressed": {
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]": {
|
||||
"key[code={c:enter}]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]:pressed": {
|
||||
"key[code={c:enter}]:pressed": {
|
||||
"background": "var(--primary-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=32]": {
|
||||
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key[code={c:space}]": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"key[code=-201,-202,-203]": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"key[code=-204,-205]": {
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key[code=-205]": {
|
||||
"text-max-lines": "2"
|
||||
},
|
||||
"key[code=-11][shiftstate=`caps_lock`]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key-hint": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-family": "monospace",
|
||||
"font-size": "12sp",
|
||||
"padding": "0dp 1dp 1dp 0dp",
|
||||
"text-max-lines": "1"
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"key-popup": {
|
||||
"background": "#424242",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
"key-popup:focus": {
|
||||
"background": "#707070",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"smartbar-shared-actions-toggle": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-key:pressed": {
|
||||
@@ -119,244 +86,109 @@
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "var(--background-variant)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"smartbar-actions-editor-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "16sp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-subheader": {
|
||||
"foreground": "var(--secondary)",
|
||||
"font-size": "16sp",
|
||||
"font-weight": "bold",
|
||||
"padding": "12dp 16dp 12dp 8dp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-tile-grid": {
|
||||
"margin": "4dp 0dp"
|
||||
},
|
||||
"smartbar-actions-editor-tile": {
|
||||
"margin": "4dp",
|
||||
"padding": "8dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-999]": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-991]": {
|
||||
"foreground": "var(--drag-marker)"
|
||||
},
|
||||
|
||||
"smartbar-candidate-word": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rectangle()",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rectangle()"
|
||||
},
|
||||
"smartbar-candidate-word:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
|
||||
},
|
||||
"smartbar-candidate-clip:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--spacer-color)"
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"clipboard-header-button:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"clipboard-header-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "1dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-message": {
|
||||
"padding": "16dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-buttons": {
|
||||
"padding": "4dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-button": {
|
||||
|
||||
"emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"emoji-key-popup": {
|
||||
"background": "#757575",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-history-disabled-title": {
|
||||
"font-weight": "bold"
|
||||
"emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"clipboard-history-disabled-message": {
|
||||
"padding": "0dp 4dp 0dp 8dp"
|
||||
},
|
||||
"clipboard-history-disabled-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"shape": "rounded-corner(24dp,24dp,24dp,24dp)"
|
||||
},
|
||||
"clipboard-history-locked-title": {
|
||||
"font-weight": "bold",
|
||||
"text-align": "center"
|
||||
},
|
||||
"clipboard-history-locked-message": {
|
||||
"padding": "0dp 4dp 0dp 0dp",
|
||||
"text-align": "center"
|
||||
"emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
|
||||
"extracted-landscape-input-layout": {
|
||||
@@ -367,95 +199,29 @@
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp",
|
||||
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"border-color": "var(--secondary)",
|
||||
"border-width": "2dp"
|
||||
"border-color": "var(--secondary-variant)",
|
||||
"border-width": "1dp"
|
||||
},
|
||||
"extracted-landscape-input-action": {
|
||||
"background": "var(--primary)",
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
|
||||
},
|
||||
|
||||
"glide-trail": {
|
||||
"foreground": "var(--primary)"
|
||||
"foreground": "var(--primary-variant)"
|
||||
},
|
||||
|
||||
"incognito-mode-indicator": {
|
||||
"foreground": "var(--incognito-icon-color)"
|
||||
},
|
||||
|
||||
"inline-autofill-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"media-emoji-subheader": {
|
||||
"font-weight": "bold",
|
||||
"margin": "4dp"
|
||||
},
|
||||
"media-emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"media-emoji-key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"media-emoji-key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"media-emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
"media-bottom-row-button": {
|
||||
"padding": "16dp 0dp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key-popup-extended-indicator": {
|
||||
"foreground": "inherit"
|
||||
"foreground": "#ffffff11"
|
||||
},
|
||||
|
||||
"one-handed-panel": {
|
||||
"background": "var(--one-hand-background)",
|
||||
"foreground": "var(--one-hand-foreground)"
|
||||
"background": "#000000",
|
||||
"foreground": "#eeeeee"
|
||||
},
|
||||
"subtype-panel": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"subtype-panel-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "18sp",
|
||||
"padding": "12dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
|
||||
"system-nav-bar": {
|
||||
"background": "var(--background)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +1,83 @@
|
||||
{
|
||||
"$schema": "https://schemas.florisboard.org/snygg/v2/stylesheet",
|
||||
"@defines": {
|
||||
"--primary": "#388e3c",
|
||||
"--primary-variant": "#306d32",
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#804c00",
|
||||
"--background": "#000000",
|
||||
"--background-variant": "#111111",
|
||||
"--surface": "#212121",
|
||||
"--surface-variant": "#3d3d3d",
|
||||
"--popup-surface": "#424242",
|
||||
"--focused-popup-surface": "#707070",
|
||||
"--drag-marker": "rgb(255,0,0)",
|
||||
"--spacer-color": "rgba(255, 255, 255, 0.25)",
|
||||
"--one-hand-background": "#1b5e20",
|
||||
"--one-hand-foreground": "#eeeeee",
|
||||
"--incognito-icon-color": "#ffffff11",
|
||||
"--on-primary": "#f0f0f0",
|
||||
"--on-background-disabled": "#dcdcdc48",
|
||||
|
||||
"--on-background": "#eeeeee",
|
||||
"--on-surface": "#eeeeee",
|
||||
"--on-surface-variant": "#ffffff73",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)"
|
||||
"keyboard": {
|
||||
"background": "var(--background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "1"
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"background": "#6161617f",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]": {
|
||||
"key[code={c:enter}]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "0dp 6dp"
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=10]:pressed": {
|
||||
"key[code={c:enter}]:pressed": {
|
||||
"background": "var(--primary-variant)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"key[code=32]": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"margin": "0dp 6dp",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"key[code=-201,-202,-203]": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"key[code=-204,-205]": {
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key[code=-205]": {
|
||||
"text-max-lines": "2"
|
||||
},
|
||||
"key[code=-11][shiftstate=`caps_lock`]": {
|
||||
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
|
||||
"foreground": "var(--secondary)"
|
||||
},
|
||||
"key[code={c:space}]": {
|
||||
"background": "#61616146",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-hint": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"font-size": "12sp",
|
||||
"font-family": "monospace",
|
||||
"padding": "0dp 1dp 1dp 0dp",
|
||||
"text-max-lines": "1"
|
||||
"font-size": "12sp"
|
||||
},
|
||||
"key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"key-popup": {
|
||||
"background": "#363636",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
"key-popup:focus": {
|
||||
"background": "#5F5F5F",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
"smartbar-shared-actions-toggle": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-key:pressed": {
|
||||
@@ -121,244 +85,109 @@
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "var(--background-variant)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "18sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
"background": "transparent",
|
||||
"foreground": "#dcdcdc48"
|
||||
},
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"smartbar-actions-editor-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "16sp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-subheader": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp",
|
||||
"font-weight": "bold",
|
||||
"padding": "12dp 16dp 12dp 8dp",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"smartbar-actions-editor-tile-grid": {
|
||||
"margin": "4dp 0dp"
|
||||
},
|
||||
"smartbar-actions-editor-tile": {
|
||||
"margin": "4dp",
|
||||
"padding": "8dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-999]": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-editor-tile[code=-991]": {
|
||||
"foreground": "var(--drag-marker)"
|
||||
},
|
||||
|
||||
"smartbar-candidate-word": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rectangle()",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rectangle()"
|
||||
},
|
||||
"smartbar-candidate-word:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "8dp 0dp",
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
|
||||
},
|
||||
"smartbar-candidate-clip:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
"margin": "4dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"clipboard-header-button:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"clipboard-header-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"background": "var(--surface-variant)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "1dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-message": {
|
||||
"padding": "16dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-buttons": {
|
||||
"padding": "4dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog-button": {
|
||||
|
||||
"emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"emoji-key-popup": {
|
||||
"background": "#757575",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "var(--shape-variant)"
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-history-disabled-title": {
|
||||
"font-weight": "bold"
|
||||
"emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"clipboard-history-disabled-message": {
|
||||
"padding": "0dp 4dp 0dp 8dp"
|
||||
},
|
||||
"clipboard-history-disabled-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"shape": "rounded-corner(24dp,24dp,24dp,24dp)"
|
||||
},
|
||||
"clipboard-history-locked-title": {
|
||||
"font-weight": "bold",
|
||||
"text-align": "center"
|
||||
},
|
||||
"clipboard-history-locked-message": {
|
||||
"padding": "0dp 4dp 0dp 0dp",
|
||||
"text-align": "center"
|
||||
"emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
|
||||
"extracted-landscape-input-layout": {
|
||||
@@ -369,95 +198,29 @@
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp",
|
||||
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"border-color": "var(--secondary)",
|
||||
"border-width": "2dp"
|
||||
"border-color": "var(--secondary-variant)",
|
||||
"border-width": "1dp"
|
||||
},
|
||||
"extracted-landscape-input-action": {
|
||||
"background": "var(--primary)",
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
|
||||
},
|
||||
|
||||
"glide-trail": {
|
||||
"foreground": "var(--primary)"
|
||||
"foreground": "var(--primary-variant)"
|
||||
},
|
||||
|
||||
"incognito-mode-indicator": {
|
||||
"foreground": "var(--incognito-icon-color)"
|
||||
},
|
||||
|
||||
"inline-autofill-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
|
||||
"media-emoji-subheader": {
|
||||
"font-weight": "bold",
|
||||
"margin": "4dp"
|
||||
},
|
||||
"media-emoji-key": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"media-emoji-key-popup-box": {
|
||||
"background": "var(--popup-surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "22sp",
|
||||
"shape": "var(--shape)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"media-emoji-key-popup-element:focus": {
|
||||
"background": "var(--focused-popup-surface)",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-tab": {
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"media-emoji-tab:focus": {
|
||||
"foreground": "var(--primary)"
|
||||
},
|
||||
"media-bottom-row-button": {
|
||||
"padding": "16dp 0dp",
|
||||
"shape": "var(--shape)"
|
||||
},
|
||||
"media-emoji-key-popup-extended-indicator": {
|
||||
"foreground": "inherit"
|
||||
"foreground": "#ffffff11"
|
||||
},
|
||||
|
||||
"one-handed-panel": {
|
||||
"background": "var(--one-hand-background)",
|
||||
"foreground": "var(--one-hand-foreground)"
|
||||
"background": "#000000",
|
||||
"foreground": "#eeeeee"
|
||||
},
|
||||
"subtype-panel": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"shape": "rounded-corner(24dp, 24dp, 0dp, 0dp)"
|
||||
},
|
||||
"subtype-panel-header": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "18sp",
|
||||
"padding": "12dp",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
|
||||
"system-nav-bar": {
|
||||
"background": "var(--background)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyboardManager
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.FlorisEmojiCompat
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpManager
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.GlideTypingManager
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.crashutility.CrashUtility
|
||||
@@ -60,6 +61,7 @@ class FlorisApplication : Application() {
|
||||
System.loadLibrary("fl_native")
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
FlorisImeTheme.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -102,6 +103,7 @@ import dev.patrickgold.florisboard.ime.text.TextInputLayout
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.WallpaperChangeReceiver
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.SystemUiIme
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
@@ -113,7 +115,6 @@ import dev.patrickgold.florisboard.lib.util.ViewUtils
|
||||
import dev.patrickgold.florisboard.lib.util.debugSummarize
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import java.lang.ref.WeakReference
|
||||
import org.florisboard.lib.android.AndroidInternalR
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationLandscape
|
||||
@@ -121,11 +122,14 @@ import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.shape
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* Global weak reference for the [FlorisImeService] class. This is needed as certain actions (request hide, switch to
|
||||
@@ -279,21 +283,9 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (prefs.localization.displayKeyboardLabelsInSubtypeLanguage.get()) {
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
}
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.observeForever { shouldSync ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (shouldSync) {
|
||||
config.setLocale(subtypeManager.activeSubtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.observeForever {
|
||||
updateInputViewShown()
|
||||
}
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
registerReceiver(wallpaperChangeReceiver, IntentFilter(Intent.ACTION_WALLPAPER_CHANGED))
|
||||
}
|
||||
@@ -359,13 +351,6 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEvaluateInputViewShown(): Boolean {
|
||||
val config = resources.configuration
|
||||
return super.onEvaluateInputViewShown()
|
||||
|| config.keyboard == Configuration.KEYBOARD_NOKEYS
|
||||
|| prefs.physicalKeyboard.showOnScreenKeyboard.get()
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
oldSelEnd: Int,
|
||||
@@ -472,10 +457,6 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
flogInfo(LogTopic.IMS_EVENTS) { "Creating inline suggestions request" }
|
||||
val stylesBundle = themeManager.createInlineSuggestionUiStyleBundle(this)
|
||||
if (stylesBundle == null) {
|
||||
flogWarning(LogTopic.IMS_EVENTS) { "Failed to retrieve inline suggestions style bundle" }
|
||||
return null
|
||||
}
|
||||
val spec = InlinePresentationSpec.Builder(
|
||||
InlineSuggestionUiSmallestSize,
|
||||
InlineSuggestionUiBiggestSize,
|
||||
@@ -538,8 +519,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
*/
|
||||
private fun updateSoftInputWindowLayoutParameters() {
|
||||
val w = window?.window ?: return
|
||||
// TODO: Verify that this doesn't give us a padding problem
|
||||
WindowCompat.setDecorFitsSystemWindows(w, false)
|
||||
WindowCompat.setDecorFitsSystemWindows(w, true)
|
||||
ViewUtils.updateLayoutHeightOf(w, WindowManager.LayoutParams.MATCH_PARENT)
|
||||
val layoutHeight = if (isFullscreenUiMode) {
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
@@ -576,18 +556,19 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
ProvideKeyboardRowBaseHeight {
|
||||
CompositionLocalProvider(LocalInputFeedbackController provides inputFeedbackController) {
|
||||
FlorisImeTheme {
|
||||
// Do not apply system bar padding here yet, we want to draw it ourselves
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
if (!(isFullscreenUiMode && isExtractUiShown)) {
|
||||
DevtoolsOverlay(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
)
|
||||
) {
|
||||
DevtoolsOverlay(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
ImeUi()
|
||||
SystemUiIme()
|
||||
}
|
||||
SystemUiIme()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,27 +579,25 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
@Composable
|
||||
private fun ImeUi() {
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
val attributes = mapOf(
|
||||
FlorisImeUi.Attr.Mode to state.keyboardMode.toString(),
|
||||
FlorisImeUi.Attr.ShiftState to state.inputShiftState.toString(),
|
||||
val keyboardStyle = FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.Keyboard,
|
||||
mode = state.inputShiftState.value,
|
||||
)
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
LaunchedEffect(layoutDirection) {
|
||||
keyboardManager.activeState.layoutDirection = layoutDirection
|
||||
SideEffect {
|
||||
if (keyboardManager.activeState.layoutDirection != layoutDirection) {
|
||||
keyboardManager.activeState.layoutDirection = layoutDirection
|
||||
}
|
||||
}
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.Window.elementName,
|
||||
attributes = attributes,
|
||||
SnyggSurface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.onGloballyPositioned { coords -> inputViewSize = coords.size },
|
||||
clickAndSemanticsModifier = Modifier
|
||||
.onGloballyPositioned { coords -> inputViewSize = coords.size }
|
||||
// Do not remove below line or touch input may get stuck
|
||||
.pointerInteropFilter { false },
|
||||
supportsBackgroundImage = true,
|
||||
allowClip = false,
|
||||
style = keyboardStyle,
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val bottomOffset by if (configuration.isOrientationPortrait()) {
|
||||
@@ -630,18 +609,18 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
// Apply system bars padding here (we already drew our keyboard background)
|
||||
.safeDrawingPadding()
|
||||
// FIXME: removing this fixes the Smartbar sizing but breaks one-handed-mode
|
||||
//.height(IntrinsicSize.Min)
|
||||
.padding(bottom = bottomOffset),
|
||||
) {
|
||||
val oneHandedMode by prefs.keyboard.oneHandedMode.observeAsState()
|
||||
val oneHandedModeEnabled by prefs.keyboard.oneHandedModeEnabled.observeAsState()
|
||||
val oneHandedModeScaleFactor by prefs.keyboard.oneHandedModeScaleFactor.observeAsState()
|
||||
val keyboardWeight = when {
|
||||
!oneHandedModeEnabled || configuration.isOrientationLandscape() -> 1f
|
||||
oneHandedMode == OneHandedMode.OFF || configuration.isOrientationLandscape() -> 1f
|
||||
else -> oneHandedModeScaleFactor / 100f
|
||||
}
|
||||
if (oneHandedModeEnabled && oneHandedMode == OneHandedMode.END && configuration.isOrientationPortrait()) {
|
||||
if (oneHandedMode == OneHandedMode.END && configuration.isOrientationPortrait()) {
|
||||
OneHandedPanel(
|
||||
panelSide = OneHandedMode.START,
|
||||
weight = 1f - keyboardWeight,
|
||||
@@ -660,7 +639,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oneHandedModeEnabled && oneHandedMode == OneHandedMode.START && configuration.isOrientationPortrait()) {
|
||||
if (oneHandedMode == OneHandedMode.START && configuration.isOrientationPortrait()) {
|
||||
OneHandedPanel(
|
||||
panelSide = OneHandedMode.END,
|
||||
weight = 1f - keyboardWeight,
|
||||
@@ -761,39 +740,45 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@Composable
|
||||
fun Content() {
|
||||
val context = LocalContext.current
|
||||
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
FlorisImeTheme {
|
||||
val layoutStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputLayout)
|
||||
val fieldStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputField)
|
||||
val actionStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputAction)
|
||||
val activeEditorInfo by editorInstance.activeInfoFlow.collectAsState()
|
||||
SnyggBox(FlorisImeUi.ExtractedLandscapeInputLayout.elementName) {
|
||||
SnyggRow(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.snyggBackground(context, layoutStyle, FlorisImeTheme.fallbackSurfaceColor()),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.ExtractedLandscapeInputLayout.elementName,
|
||||
val fieldColor =
|
||||
fieldStyle.foreground.solidColor(context, FlorisImeTheme.fallbackContentColor())
|
||||
AndroidView(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.fillMaxHeight()
|
||||
.weight(1f),
|
||||
) {
|
||||
val fieldStyle = rememberSnyggThemeQuery(FlorisImeUi.ExtractedLandscapeInputField.elementName)
|
||||
val foreground = fieldStyle.foreground()
|
||||
AndroidView(
|
||||
factory = { extractEditText },
|
||||
update = { view ->
|
||||
view.background = null
|
||||
view.backgroundTintList = null
|
||||
view.foregroundTintList = null
|
||||
view.setTextColor(foreground.toArgb())
|
||||
view.setHintTextColor(foreground.copy(foreground.alpha * 0.6f).toArgb())
|
||||
view.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
fieldStyle.fontSize(default = 16.sp).value,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
SnyggButton(
|
||||
FlorisImeUi.ExtractedLandscapeInputAction.elementName,
|
||||
.weight(1f)
|
||||
.snyggShadow(fieldStyle)
|
||||
.snyggBorder(context, fieldStyle)
|
||||
.snyggBackground(context, fieldStyle),
|
||||
factory = { extractEditText },
|
||||
update = { view ->
|
||||
view.background = null
|
||||
view.backgroundTintList = null
|
||||
view.foregroundTintList = null
|
||||
view.setTextColor(fieldColor.toArgb())
|
||||
view.setHintTextColor(fieldColor.copy(fieldColor.alpha * 0.6f).toArgb())
|
||||
view.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
fieldStyle.fontSize.spSize(default = 16.sp).value,
|
||||
)
|
||||
},
|
||||
)
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
if (activeEditorInfo.extractedActionId != 0) {
|
||||
currentInputConnection?.performEditorAction(activeEditorInfo.extractedActionId)
|
||||
@@ -802,13 +787,21 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
) {
|
||||
SnyggText(
|
||||
text = activeEditorInfo.extractedActionLabel
|
||||
?: getTextForImeAction(activeEditorInfo.imeOptions.action.toInt())
|
||||
?: "ACTION",
|
||||
)
|
||||
}
|
||||
text = activeEditorInfo.extractedActionLabel
|
||||
?: getTextForImeAction(activeEditorInfo.imeOptions.action.toInt())
|
||||
?: "ACTION",
|
||||
shape = actionStyle.shape.shape(),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = actionStyle.background.solidColor(
|
||||
context,
|
||||
FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
contentColor = actionStyle.foreground.solidColor(
|
||||
context,
|
||||
FlorisImeTheme.fallbackSurfaceColor()
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayColorsAs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.SnyggLevel
|
||||
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
|
||||
import dev.patrickgold.florisboard.ime.clipboard.CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
|
||||
@@ -42,15 +41,11 @@ import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickAction
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionArrangement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionJsonConfig
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintConfiguration
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.ime.theme.extCoreTheme
|
||||
import dev.patrickgold.florisboard.lib.compose.ColorPreferenceSerializer
|
||||
@@ -58,20 +53,53 @@ import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.model.LocalTime
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceType
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.color.DEFAULT_GREEN
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
|
||||
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
val advanced = Advanced()
|
||||
inner class Advanced {
|
||||
val settingsTheme = enum(
|
||||
key = "advanced__settings_theme",
|
||||
default = AppTheme.AUTO,
|
||||
)
|
||||
val accentColor = custom(
|
||||
key = "advanced__accent_color",
|
||||
default = when (AndroidVersion.ATLEAST_API31_S) {
|
||||
true -> Color.Unspecified
|
||||
false -> DEFAULT_GREEN
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
val settingsLanguage = string(
|
||||
key = "advanced__settings_language",
|
||||
default = "auto",
|
||||
)
|
||||
val showAppIcon = boolean(
|
||||
key = "advanced__show_app_icon",
|
||||
default = true,
|
||||
)
|
||||
val incognitoMode = enum(
|
||||
key = "advanced__incognito_mode",
|
||||
default = IncognitoMode.DYNAMIC_ON_OFF,
|
||||
)
|
||||
// Internal pref
|
||||
val forceIncognitoModeFromDynamic = boolean(
|
||||
key = "advanced__force_incognito_mode_from_dynamic",
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
|
||||
val clipboard = Clipboard()
|
||||
inner class Clipboard {
|
||||
val useInternalClipboard = boolean(
|
||||
@@ -90,23 +118,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "clipboard__history_enabled",
|
||||
default = false,
|
||||
)
|
||||
val numHistoryGridColumnsPortrait = int(
|
||||
key = "clipboard__num_history_grid_columns_portrait",
|
||||
default = CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO,
|
||||
)
|
||||
val numHistoryGridColumnsLandscape = int(
|
||||
key = "clipboard__num_history_grid_columns_landscape",
|
||||
default = CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO,
|
||||
)
|
||||
@Composable
|
||||
fun numHistoryGridColumns(): PreferenceData<Int> {
|
||||
val configuration = LocalConfiguration.current
|
||||
return if (configuration.isOrientationPortrait()) {
|
||||
numHistoryGridColumnsPortrait
|
||||
} else {
|
||||
numHistoryGridColumnsLandscape
|
||||
}
|
||||
}
|
||||
val cleanUpOld = boolean(
|
||||
key = "clipboard__clean_up_old",
|
||||
default = false,
|
||||
@@ -135,14 +146,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "clipboard__clear_primary_clip_deletes_last_item",
|
||||
default = true,
|
||||
)
|
||||
val suggestionEnabled = boolean(
|
||||
key = "clipboard__suggestion_enabled",
|
||||
default = true,
|
||||
)
|
||||
val suggestionTimeout = int(
|
||||
key = "clipboard__suggestion_timeout",
|
||||
default = 60,
|
||||
)
|
||||
}
|
||||
|
||||
val correction = Correction()
|
||||
@@ -503,11 +506,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
val oneHandedMode = enum(
|
||||
key = "keyboard__one_handed_mode",
|
||||
default = OneHandedMode.END,
|
||||
)
|
||||
val oneHandedModeEnabled = boolean(
|
||||
key = "keyboard__one_handed_mode_enabled",
|
||||
default = false,
|
||||
default = OneHandedMode.OFF,
|
||||
)
|
||||
val oneHandedModeScaleFactor = int(
|
||||
key = "keyboard__one_handed_mode_scale_factor",
|
||||
@@ -579,14 +578,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
@Composable
|
||||
fun fontSizeMultiplier(): Float {
|
||||
val configuration = LocalConfiguration.current
|
||||
val oneHandedModeEnabled by oneHandedModeEnabled.observeAsState()
|
||||
val oneHandedMode by oneHandedMode.observeAsState()
|
||||
val oneHandedModeFactor by oneHandedModeScaleFactor.observeAsTransformingState { it / 100.0f }
|
||||
val fontSizeMultiplierBase by if (configuration.isOrientationPortrait()) {
|
||||
fontSizeMultiplierPortrait
|
||||
} else {
|
||||
fontSizeMultiplierLandscape
|
||||
}.observeAsTransformingState { it / 100.0f }
|
||||
val fontSizeMultiplier = fontSizeMultiplierBase * if (oneHandedModeEnabled && configuration.isOrientationPortrait()) {
|
||||
val fontSizeMultiplier = fontSizeMultiplierBase * if (oneHandedMode != OneHandedMode.OFF && configuration.isOrientationPortrait()) {
|
||||
oneHandedModeFactor
|
||||
} else {
|
||||
1.0f
|
||||
@@ -601,10 +600,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "localization__display_language_names_in",
|
||||
default = DisplayLanguageNamesIn.SYSTEM_LOCALE,
|
||||
)
|
||||
val displayKeyboardLabelsInSubtypeLanguage = boolean(
|
||||
key = "localization__display_keyboard_labels_in_subtype_language",
|
||||
default = false,
|
||||
)
|
||||
val activeSubtypeId = long(
|
||||
key = "localization__active_subtype_id",
|
||||
default = Subtype.DEFAULT.id,
|
||||
@@ -615,38 +610,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val other = Other()
|
||||
inner class Other {
|
||||
val settingsTheme = enum(
|
||||
key = "other__settings_theme",
|
||||
default = AppTheme.AUTO,
|
||||
)
|
||||
val accentColor = custom(
|
||||
key = "other__accent_color",
|
||||
default = when (AndroidVersion.ATLEAST_API31_S) {
|
||||
true -> Color.Unspecified
|
||||
false -> DEFAULT_GREEN
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
val settingsLanguage = string(
|
||||
key = "other__settings_language",
|
||||
default = "auto",
|
||||
)
|
||||
val showAppIcon = boolean(
|
||||
key = "other__show_app_icon",
|
||||
default = true,
|
||||
)
|
||||
}
|
||||
|
||||
val physicalKeyboard = PhysicalKeyboard()
|
||||
inner class PhysicalKeyboard {
|
||||
val showOnScreenKeyboard = boolean(
|
||||
key = "physical_keyboard__show_on_screen_keyboard",
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
|
||||
val smartbar = Smartbar()
|
||||
inner class Smartbar {
|
||||
val enabled = boolean(
|
||||
@@ -723,14 +686,13 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "suggestion__block_possibly_offensive",
|
||||
default = true,
|
||||
)
|
||||
val incognitoMode = enum(
|
||||
key = "suggestion__incognito_mode",
|
||||
default = IncognitoMode.DYNAMIC_ON_OFF,
|
||||
val clipboardContentEnabled = boolean(
|
||||
key = "suggestion__clipboard_content_enabled",
|
||||
default = true,
|
||||
)
|
||||
// Internal pref
|
||||
val forceIncognitoModeFromDynamic = boolean(
|
||||
key = "suggestion__force_incognito_mode_from_dynamic",
|
||||
default = false,
|
||||
val clipboardContentTimeout = int(
|
||||
key = "suggestion__clipboard_content_timeout",
|
||||
default = 60,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -758,17 +720,17 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
val sunriseTime = time(
|
||||
key = "theme__sunrise_time",
|
||||
default = LocalTime(6, 0),
|
||||
)
|
||||
val sunsetTime = time(
|
||||
key = "theme__sunset_time",
|
||||
default = LocalTime(18, 0),
|
||||
)
|
||||
val editorColorRepresentation = enum(
|
||||
key = "theme__editor_color_representation",
|
||||
default = ColorRepresentation.HEX,
|
||||
//val sunriseTime = localTime(
|
||||
// key = "theme__sunrise_time",
|
||||
// default = LocalTime.of(6, 0),
|
||||
//)
|
||||
//val sunsetTime = localTime(
|
||||
// key = "theme__sunset_time",
|
||||
// default = LocalTime.of(18, 0),
|
||||
//)
|
||||
val editorDisplayColorsAs = enum(
|
||||
key = "theme__editor_display_colors_as",
|
||||
default = DisplayColorsAs.HEX8,
|
||||
)
|
||||
val editorDisplayKbdAfterDialogs = enum(
|
||||
key = "theme__editor_display_kbd_after_dialogs",
|
||||
@@ -782,6 +744,34 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
|
||||
override fun migrate(entry: PreferenceMigrationEntry): PreferenceMigrationEntry {
|
||||
return when (entry.key) {
|
||||
// Migrate enums from their lowercase to uppercase representation
|
||||
// Keep migration rule until: 0.5 dev cycle
|
||||
"advanced__settings_theme", "gestures__swipe_up", "gestures__swipe_down", "gestures__swipe_left",
|
||||
"gestures__swipe_right", "gestures__space_bar_swipe_up", "gestures__space_bar_swipe_left",
|
||||
"gestures__space_bar_swipe_right", "gestures__space_bar_long_press", "gestures__delete_key_swipe_left",
|
||||
"gestures__delete_key_long_press", "keyboard__hinted_number_row_mode", "keyboard__hinted_symbols_mode",
|
||||
"keyboard__utility_key_action", "keyboard__one_handed_mode", "keyboard__landscape_input_ui_mode",
|
||||
"localization__display_language_names_in", "smartbar__primary_actions_row_type",
|
||||
"smartbar__secondary_actions_placement", "smartbar__secondary_actions_row_type", "spelling__language_mode",
|
||||
"suggestion__display_mode", "theme__mode", "theme__editor_display_colors_as",
|
||||
"theme__editor_display_kbd_after_dialogs", "theme__editor_level",
|
||||
-> {
|
||||
entry.transform(rawValue = entry.rawValue.uppercase())
|
||||
}
|
||||
|
||||
// Migrate old private mode force flag as this is a sensitive preference
|
||||
// Keep migration rule until: 0.5 dev cycle
|
||||
"advanced__force_private_mode" -> {
|
||||
if (entry.rawValue.toBoolean()) {
|
||||
entry.transform(
|
||||
type = PreferenceType.string(),
|
||||
key = "advanced__incognito_mode",
|
||||
rawValue = IncognitoMode.FORCE_ON.toString(),
|
||||
)
|
||||
} else {
|
||||
entry.reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate media prefs to emoji prefs
|
||||
// Keep migration rule until: 0.6 dev cycle
|
||||
@@ -796,81 +786,18 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
"media__emoji_recently_used_max_size" -> {
|
||||
entry.transform(key = "emoji__history_recent_max_size")
|
||||
}
|
||||
|
||||
// Migrate advanced prefs to other prefs
|
||||
// Keep migration rules until: 0.7 dev cycle
|
||||
"advanced__settings_theme" -> {
|
||||
entry.transform(key = "other__settings_theme")
|
||||
}
|
||||
"advanced__accent_color" -> {
|
||||
entry.transform(key = "other__accent_color")
|
||||
}
|
||||
"advanced__settings_language" -> {
|
||||
entry.transform(key = "other__settings_language")
|
||||
}
|
||||
"advanced__show_app_icon" -> {
|
||||
entry.transform(key = "other__show_app_icon")
|
||||
}
|
||||
"advanced__incognito_mode" -> {
|
||||
entry.transform(key = "suggestion__incognito_mode")
|
||||
}
|
||||
"advanced__force_incognito_mode_from_dynamic" -> {
|
||||
entry.transform(key = "suggestion__force_incognito_mode_from_dynamic")
|
||||
}
|
||||
// Migrate clipboard suggestion prefs to clipboard
|
||||
// Keep migration rules until: 0.7 dev cycle
|
||||
"suggestion__clipboard_content_enabled" -> {
|
||||
entry.transform(key = "clipboard__suggestion_enabled")
|
||||
}
|
||||
"suggestion__clipboard_content_timeout" -> {
|
||||
entry.transform(key = "clipboard__suggestion_timeout")
|
||||
}
|
||||
|
||||
//Migrate one hand mode prefs keep until: 0.7 dev cycle
|
||||
"keyboard__one_handed_mode" -> {
|
||||
if (entry.rawValue == "OFF") {
|
||||
entry.reset()
|
||||
} else {
|
||||
entry.keepAsIs()
|
||||
}
|
||||
}
|
||||
"smartbar__action_arrangement" -> {
|
||||
fun migrateAction(action: QuickAction): QuickAction {
|
||||
return if (action is QuickAction.InsertKey && action.data.code == KeyCode.COMPACT_LAYOUT_TO_RIGHT) {
|
||||
action.copy(TextKeyData.TOGGLE_COMPACT_LAYOUT)
|
||||
} else {
|
||||
action
|
||||
}
|
||||
}
|
||||
|
||||
val arrangement = QuickActionJsonConfig.decodeFromString<QuickActionArrangement>(entry.rawValue)
|
||||
var newArrangement = arrangement.copy(
|
||||
stickyAction = arrangement.stickyAction?.let{ migrateAction(it) },
|
||||
dynamicActions = arrangement.dynamicActions.map { migrateAction(it) },
|
||||
hiddenActions = arrangement.hiddenActions.map { migrateAction(it) },
|
||||
)
|
||||
if (QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH) !in newArrangement) {
|
||||
newArrangement = newArrangement.copy(
|
||||
dynamicActions = newArrangement.dynamicActions.plus(QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH))
|
||||
)
|
||||
}
|
||||
val json = QuickActionJsonConfig.encodeToString(newArrangement.distinct())
|
||||
entry.transform(rawValue = json)
|
||||
}
|
||||
|
||||
// Migrate theme editor fine-tuning
|
||||
// Keep migration rule until: 0.6 dev cycle
|
||||
"theme__editor_display_colors_as" -> {
|
||||
val colorRepresentation = when (entry.rawValue) {
|
||||
"RGBA" -> ColorRepresentation.RGB
|
||||
else -> ColorRepresentation.HEX
|
||||
}
|
||||
"media__emoji_preferred_skin_tone" -> {
|
||||
entry.transform(
|
||||
key = "theme__editor_color_representation",
|
||||
rawValue = colorRepresentation.name,
|
||||
key = "emoji__preferred_skin_tone",
|
||||
rawValue = entry.rawValue.uppercase(), // keep until: 0.5 dev cycle
|
||||
)
|
||||
}
|
||||
"media__emoji_preferred_hair_style" -> {
|
||||
entry.transform(
|
||||
key = "emoji__preferred_hair_style",
|
||||
rawValue = entry.rawValue.uppercase(), // keep until: 0.5 dev cycle
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Default: keep entry
|
||||
else -> entry.keepAsIs()
|
||||
|
||||
@@ -18,15 +18,13 @@ package dev.patrickgold.florisboard.app
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayColorsAs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.SnyggLevel
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputShiftState
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
@@ -43,9 +41,9 @@ import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreferenceEntry
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@@ -60,19 +58,19 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.AUTO_AMOLED,
|
||||
label = stringRes(R.string.pref__other__settings_theme__auto_amoled),
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__auto_amoled),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.LIGHT,
|
||||
label = stringRes(R.string.pref__other__settings_theme__light),
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__light),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.DARK,
|
||||
label = stringRes(R.string.pref__other__settings_theme__dark),
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__dark),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.AMOLED_DARK,
|
||||
label = stringRes(R.string.pref__other__settings_theme__amoled_dark),
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__amoled_dark),
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -104,24 +102,18 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
)
|
||||
}
|
||||
},
|
||||
ColorRepresentation::class to DEFAULT to {
|
||||
DisplayColorsAs::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = ColorRepresentation.HEX,
|
||||
label = stringRes(R.string.enum__color_representation__hex),
|
||||
key = DisplayColorsAs.HEX8,
|
||||
label = stringRes(R.string.enum__display_colors_as__hex8),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "#4caf50ff"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = ColorRepresentation.RGB,
|
||||
label = stringRes(R.string.enum__color_representation__rgb),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "rgba(76, 175, 80, 1.0)"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = ColorRepresentation.HSV,
|
||||
label = stringRes(R.string.enum__color_representation__hsv),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "hsva(122, 56, 68, 1.0)"),
|
||||
key = DisplayColorsAs.RGBA,
|
||||
label = stringRes(R.string.enum__display_colors_as__rgba),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "rgba(76,175,80,1.0)"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
@@ -366,58 +358,6 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
)
|
||||
}
|
||||
},
|
||||
InputShiftState::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = InputShiftState.UNSHIFTED,
|
||||
label = stringRes(R.string.enum__input_shift_state__unshifted),
|
||||
)
|
||||
entry(
|
||||
key = InputShiftState.SHIFTED_MANUAL,
|
||||
label = stringRes(R.string.enum__input_shift_state__shifted_manual),
|
||||
)
|
||||
entry(
|
||||
key = InputShiftState.SHIFTED_AUTOMATIC,
|
||||
label = stringRes(R.string.enum__input_shift_state__shifted_automatic),
|
||||
)
|
||||
entry(
|
||||
key = InputShiftState.CAPS_LOCK,
|
||||
label = stringRes(R.string.enum__input_shift_state__caps_lock),
|
||||
)
|
||||
}
|
||||
},
|
||||
KeyboardMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = KeyboardMode.CHARACTERS,
|
||||
label = stringRes(R.string.enum__keyboard_mode__characters),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.SYMBOLS,
|
||||
label = stringRes(R.string.enum__keyboard_mode__symbols),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.SYMBOLS2,
|
||||
label = stringRes(R.string.enum__keyboard_mode__symbols2),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.NUMERIC,
|
||||
label = stringRes(R.string.enum__keyboard_mode__numeric),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.NUMERIC_ADVANCED,
|
||||
label = stringRes(R.string.enum__keyboard_mode__numeric_advanced),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.PHONE,
|
||||
label = stringRes(R.string.enum__keyboard_mode__phone),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.PHONE2,
|
||||
label = stringRes(R.string.enum__keyboard_mode__phone2),
|
||||
)
|
||||
}
|
||||
},
|
||||
LandscapeInputUiMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
@@ -436,6 +376,10 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
},
|
||||
OneHandedMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = OneHandedMode.OFF,
|
||||
label = stringRes(R.string.enum__one_handed_mode__off),
|
||||
)
|
||||
entry(
|
||||
key = OneHandedMode.START,
|
||||
label = stringRes(R.string.enum__one_handed_mode__start),
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.isUnspecified
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
@@ -88,17 +89,17 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
prefs.other.settingsTheme.observe(this) {
|
||||
prefs.advanced.settingsTheme.observe(this) {
|
||||
appTheme = it
|
||||
}
|
||||
prefs.other.settingsLanguage.observe(this) {
|
||||
prefs.advanced.settingsLanguage.observe(this) {
|
||||
val config = Configuration(resources.configuration)
|
||||
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
|
||||
config.setLocale(locale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
if (AndroidVersion.ATMOST_API28_P) {
|
||||
prefs.other.showAppIcon.observe(this) {
|
||||
prefs.advanced.showAppIcon.observe(this) {
|
||||
showAppIcon = it
|
||||
}
|
||||
}
|
||||
@@ -117,7 +118,8 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
FlorisAppTheme(theme = appTheme) {
|
||||
val accentColor by prefs.advanced.accentColor.observeAsState()
|
||||
FlorisAppTheme(theme = appTheme, isMaterialYouAware = accentColor.isUnspecified) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
AppContent()
|
||||
}
|
||||
|
||||
@@ -47,9 +47,8 @@ import dev.patrickgold.florisboard.app.settings.HomeScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.AboutScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.ProjectLicenseScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.ThirdPartyLicensesScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.AdvancedScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.BackupScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.OtherScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.PhysicalKeyboardScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.RestoreScreen
|
||||
import dev.patrickgold.florisboard.app.settings.clipboard.ClipboardScreen
|
||||
import dev.patrickgold.florisboard.app.settings.dictionary.DictionaryScreen
|
||||
@@ -111,10 +110,9 @@ object Routes {
|
||||
|
||||
const val Media = "settings/media"
|
||||
|
||||
const val Other = "settings/other"
|
||||
const val PhysicalKeyboard = "settings/other/physical-keyboard"
|
||||
const val Backup = "settings/other/backup"
|
||||
const val Restore = "settings/other/restore"
|
||||
const val Advanced = "settings/advanced"
|
||||
const val Backup = "settings/advanced/backup"
|
||||
const val Restore = "settings/advanced/restore"
|
||||
|
||||
const val About = "settings/about"
|
||||
const val ProjectLicense = "settings/about/project-license"
|
||||
@@ -241,8 +239,7 @@ object Routes {
|
||||
|
||||
composableWithDeepLink(Settings.Media) { MediaScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Other) { OtherScreen() }
|
||||
composableWithDeepLink(Settings.PhysicalKeyboard) { PhysicalKeyboardScreen() }
|
||||
composableWithDeepLink(Settings.Advanced) { AdvancedScreen() }
|
||||
composableWithDeepLink(Settings.Backup) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore) { RestoreScreen() }
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.content.Context
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -77,38 +79,59 @@ private val LightColorPalette = lightColorScheme(
|
||||
@Composable
|
||||
fun getColorScheme(
|
||||
context: Context,
|
||||
isMaterialYouAware: Boolean,
|
||||
themeColor: Color,
|
||||
theme: AppTheme,
|
||||
): ColorScheme {
|
||||
val prefs by florisPreferenceModel()
|
||||
val accentColor by prefs.other.accentColor.observeAsState()
|
||||
val isDark = isSystemInDarkTheme()
|
||||
|
||||
return when (theme) {
|
||||
|
||||
AppTheme.AUTO -> {
|
||||
if (isDark) {
|
||||
ColorMappings.dynamicDarkColorScheme(context, accentColor)
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
when {
|
||||
isDark -> dynamicDarkColorScheme(context)
|
||||
else -> dynamicLightColorScheme(context)
|
||||
}
|
||||
} else {
|
||||
ColorMappings.dynamicLightColorScheme(context, accentColor)
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark, true)
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.DARK -> {
|
||||
ColorMappings.dynamicDarkColorScheme(context, accentColor)
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
dynamicDarkColorScheme(context)
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark = true, settings = true)
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.LIGHT -> {
|
||||
ColorMappings.dynamicLightColorScheme(context, accentColor)
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
dynamicLightColorScheme(context)
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark = false, settings = true)
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.AMOLED_DARK -> {
|
||||
ColorMappings.dynamicDarkColorScheme(context, accentColor).amoled()
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
dynamicDarkColorScheme(context).amoled()
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark = true, settings = true).amoled()
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.AUTO_AMOLED -> {
|
||||
if (isDark) {
|
||||
ColorMappings.dynamicDarkColorScheme(context, accentColor).amoled()
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
when {
|
||||
isDark -> dynamicDarkColorScheme(context).amoled()
|
||||
else -> dynamicLightColorScheme(context)
|
||||
}
|
||||
} else {
|
||||
ColorMappings.dynamicLightColorScheme(context, accentColor)
|
||||
with(ColorMappings.getColorSchemeOrDefault(themeColor, isDark, true)) {
|
||||
if (isDark) amoled() else this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,11 +144,17 @@ fun ColorScheme.amoled(): ColorScheme {
|
||||
@Composable
|
||||
fun FlorisAppTheme(
|
||||
theme: AppTheme,
|
||||
isMaterialYouAware: Boolean,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val accent by prefs.advanced.accentColor.observeAsState()
|
||||
|
||||
val colors = getColorScheme(
|
||||
context = LocalContext.current,
|
||||
theme = theme,
|
||||
isMaterialYouAware = isMaterialYouAware,
|
||||
themeColor = accent,
|
||||
)
|
||||
|
||||
val darkTheme =
|
||||
|
||||
@@ -29,7 +29,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -46,15 +45,12 @@ import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CachedLayout
|
||||
import dev.patrickgold.florisboard.ime.keyboard.DebugLayoutComputationResult
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.SnyggMissingSchemaException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -66,7 +62,6 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val prefs by florisPreferenceModel()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
val devtoolsEnabled by prefs.devtools.enabled.observeAsState()
|
||||
val showPrimaryClip by prefs.devtools.showPrimaryClip.observeAsState()
|
||||
@@ -75,7 +70,6 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
|
||||
val debugLayoutResult by keyboardManager.layoutManager.debugLayoutComputationResultFlow.collectAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.observeAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -97,10 +91,6 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
val loadFailure = themeInfo?.loadFailure
|
||||
if (loadFailure != null) {
|
||||
DevtoolsStylesheetFailedToLoadOverlay(loadFailure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,39 +225,6 @@ private fun DevtoolsInlineAutofillOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsStylesheetFailedToLoadOverlay(loadFailure: ThemeManager.LoadFailure) {
|
||||
DevtoolsOverlayBox(title = "Failed to load stylesheet, fell back to base style") {
|
||||
DevtoolsSubGroup(title = "Extension") {
|
||||
DevtoolsText(text = "id: ${loadFailure.extension.id}")
|
||||
DevtoolsText(text = "title: ${loadFailure.extension.title}")
|
||||
DevtoolsText(text = "version: ${loadFailure.extension.version}")
|
||||
}
|
||||
DevtoolsSubGroup(title = "Component") {
|
||||
DevtoolsText(text = "id: ${loadFailure.component.id}")
|
||||
DevtoolsText(text = "label: ${loadFailure.component.label}")
|
||||
DevtoolsText(text = "path: ${loadFailure.component.stylesheetPath()}")
|
||||
}
|
||||
val cause = loadFailure.cause
|
||||
DevtoolsSubGroup(title = "Cause") {
|
||||
DevtoolsText(text = "${cause.message}")
|
||||
}
|
||||
if (cause is SnyggMissingSchemaException) {
|
||||
DevtoolsSubGroup(title = "Explanation") {
|
||||
DevtoolsText(
|
||||
text = """
|
||||
It appears you’re trying to load a theme designed for FlorisBoard v0.4 (Snygg v1), which isn’t compatible with the latest release using Snygg v2.
|
||||
|
||||
If you are the theme author, please update your theme to support Snygg v2.
|
||||
|
||||
If you’re a user, please update your theme via the Addons Store. If an updated version isn’t available yet, please select one of the built-in themes during this transition period.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsOverlayBox(
|
||||
title: String,
|
||||
|
||||
@@ -27,8 +27,6 @@ import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.FlorisUserDictionaryDatabase
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionArrangement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionJsonConfig
|
||||
import org.florisboard.lib.android.AndroidSettings
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
|
||||
@@ -106,15 +104,6 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
onClick = { setShowDialog(true) },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_quick_actions_to_default__label),
|
||||
summary = stringRes(R.string.devtools__reset_quick_actions_to_default__summary),
|
||||
onClick = {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
context.showLongToast(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
},
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_flag__label, "flag_name" to "isImeSetUp"),
|
||||
summary = stringRes(R.string.devtools__reset_flag_is_ime_set_up__summary),
|
||||
|
||||
@@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Input
|
||||
import androidx.compose.material.icons.filled.Shop
|
||||
import androidx.compose.material.icons.outlined.FileDownload
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -30,8 +29,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
@@ -44,41 +41,6 @@ import dev.patrickgold.florisboard.lib.ext.generateUpdateUrl
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
|
||||
@Composable
|
||||
fun ImportExtensionBox(navController: NavController) {
|
||||
val context = LocalContext.current
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 4.dp),
|
||||
text = stringRes(id = R.string.ext__home__info),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
context.launchUrl("https://${BuildConfig.FLADDONS_STORE_URL}/")
|
||||
},
|
||||
icon = Icons.Default.Shop,
|
||||
text = stringRes(id = R.string.ext__home__visit_store),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Import(ExtensionImportScreenType.EXT_ANY, null))
|
||||
},
|
||||
icon = Icons.AutoMirrored.Filled.Input,
|
||||
text = stringRes(R.string.action__import),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpdateBox(extensionIndex: List<Extension>) {
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -81,11 +81,14 @@ fun ExtensionComponentView(
|
||||
when (component) {
|
||||
is ThemeExtensionComponent -> {
|
||||
val text = remember(
|
||||
component.authors, component.isNightTheme, component.stylesheetPath(),
|
||||
component.authors, component.isNightTheme, component.isBorderless,
|
||||
component.isMaterialYouAware, component.stylesheetPath(),
|
||||
) {
|
||||
buildString {
|
||||
appendLine("authors = ${component.authors}")
|
||||
appendLine("isNightTheme = ${component.isNightTheme}")
|
||||
appendLine("isBorderless = ${component.isBorderless}")
|
||||
appendLine("isMaterialYouAware = ${component.isMaterialYouAware}")
|
||||
append("stylesheetPath = ${component.stylesheetPath()}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The FlorisBoard Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Photo
|
||||
import androidx.compose.material.icons.filled.TextFields
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.query
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.kotlin.io.parentDir
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.mimeTypeFilterOf
|
||||
|
||||
const val FONTS = "fonts"
|
||||
const val IMAGES = "images"
|
||||
|
||||
val MIME_TYPES = mapOf(
|
||||
FONTS to mimeTypeFilterOf(
|
||||
// Source: https://www.alienfactory.co.uk/articles/mime-types-for-web-fonts-in-bedsheet#mimeTypes
|
||||
"font/*",
|
||||
"application/font-*",
|
||||
"application/x-font-*",
|
||||
"application/vnd.ms-fontobject",
|
||||
),
|
||||
IMAGES to mimeTypeFilterOf(
|
||||
"image/*",
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = FlorisScreen {
|
||||
title = stringRes(R.string.ext__editor__files__title)
|
||||
|
||||
fun handleBackPress() {
|
||||
workspace.currentAction = null
|
||||
}
|
||||
|
||||
navigationIcon {
|
||||
FlorisIconButton(
|
||||
onClick = { handleBackPress() },
|
||||
icon = Icons.Default.Close,
|
||||
)
|
||||
}
|
||||
|
||||
content {
|
||||
val context = LocalContext.current
|
||||
var version by rememberSaveable { mutableIntStateOf(0) }
|
||||
val fontFiles = remember(version) {
|
||||
workspace.extDir.subDir(FONTS).listFiles { it.isFile }.orEmpty().asList()
|
||||
}
|
||||
val imageFiles = remember(version) {
|
||||
workspace.extDir.subDir(IMAGES).listFiles { it.isFile }.orEmpty().asList()
|
||||
}
|
||||
|
||||
var currentImportDest by remember { mutableStateOf<String?>(null) }
|
||||
var currentImportResult by remember { mutableStateOf<Result<Pair<File, String>>?>(null) }
|
||||
|
||||
val importLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.GetContent(),
|
||||
onResult = { uri ->
|
||||
currentImportResult = runCatching {
|
||||
checkNotNull(uri) { "" }
|
||||
val tempFile = context.cacheDir.subFile("temp_${UUID.randomUUID()}")
|
||||
context.contentResolver.readToFile(uri, tempFile)
|
||||
val mimeType = context.contentResolver.getType(uri)
|
||||
val filter = MIME_TYPES[currentImportDest!!]!!
|
||||
check(filter.matches(mimeType)) {
|
||||
"Given file mime type was '$mimeType', expected one of ${filter.types}"
|
||||
}
|
||||
val fileName = context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME)).use { cursor ->
|
||||
if (cursor == null || !cursor.moveToFirst()) return@use null
|
||||
val name = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
cursor.getString(name)
|
||||
}
|
||||
tempFile to fileName.orEmpty()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
LaunchedEffect(currentImportResult) {
|
||||
val message = currentImportResult?.exceptionOrNull()?.message
|
||||
if (!message.isNullOrBlank()) {
|
||||
context.showLongToast(message)
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
handleBackPress()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FileList(title: String, icon: ImageVector, files: List<File>, onAdd: () -> Unit) {
|
||||
var dialogFile by remember { mutableStateOf<File?>(null) }
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = title,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
},
|
||||
trailingContent = {
|
||||
IconButton(
|
||||
onClick = onAdd,
|
||||
) {
|
||||
Icon(Icons.Default.Add, null)
|
||||
}
|
||||
},
|
||||
)
|
||||
for (file in files) {
|
||||
Preference(
|
||||
onClick = {
|
||||
dialogFile = file
|
||||
},
|
||||
icon = icon,
|
||||
title = file.name,
|
||||
)
|
||||
}
|
||||
|
||||
dialogFile?.let { file ->
|
||||
var fileNameInput by rememberSaveable { mutableStateOf(file.name) }
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.general__properties),
|
||||
confirmLabel = stringRes(R.string.action__apply),
|
||||
dismissLabel = stringRes(R.string.action__cancel),
|
||||
neutralLabel = stringRes(R.string.action__delete),
|
||||
allowOutsideDismissal = true,
|
||||
onNeutral = {
|
||||
if (file.delete()) {
|
||||
context.showShortToast("Successfully deleted")
|
||||
} else {
|
||||
context.showShortToast("Failed to delete")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
},
|
||||
onConfirm = {
|
||||
val newFile = file.parentFile!!.subFile(fileNameInput).canonicalFile
|
||||
if (newFile.parentFile != file.canonicalFile.parentFile) {
|
||||
context.showLongToast("Invalid file name!")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
if (newFile.exists()) {
|
||||
context.showShortToast("Filename already exists.")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
val success = file.renameTo(newFile)
|
||||
if (success) {
|
||||
context.showShortToast("Successfully renamed")
|
||||
} else {
|
||||
context.showShortToast("Failed to rename the file.")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
},
|
||||
onDismiss = {
|
||||
dialogFile = null
|
||||
},
|
||||
) {
|
||||
JetPrefTextField(
|
||||
labelText = stringRes(R.string.general__file_name),
|
||||
value = fileNameInput,
|
||||
onValueChange = { fileNameInput = it },
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileList(
|
||||
title = stringRes(R.string.ext__editor__files__type_fonts),
|
||||
icon = Icons.Default.TextFields,
|
||||
files = fontFiles,
|
||||
) {
|
||||
currentImportDest = FONTS
|
||||
importLauncher.launch("*/*")
|
||||
}
|
||||
|
||||
FileList(
|
||||
title = stringRes(R.string.ext__editor__files__type_images),
|
||||
icon = Icons.Default.Photo,
|
||||
files = imageFiles,
|
||||
) {
|
||||
currentImportDest = IMAGES
|
||||
importLauncher.launch("*/*")
|
||||
}
|
||||
|
||||
val dest = currentImportDest
|
||||
val result = currentImportResult?.getOrNull()
|
||||
if (dest != null && result != null) {
|
||||
var fileNameInput by rememberSaveable { mutableStateOf(result.second) }
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.action__import_file),
|
||||
confirmLabel = stringRes(R.string.action__add),
|
||||
onConfirm = {
|
||||
val fileName = fileNameInput.trim()
|
||||
val dir = workspace.extDir.subDir(dest)
|
||||
dir.mkdirs()
|
||||
val file = dir.subFile(fileName)
|
||||
if (file.parentDir != workspace.extDir.subDir(dest)) {
|
||||
context.showShortToast("Invalid file name")
|
||||
} else if (file.exists()) {
|
||||
context.showShortToast("File already exists")
|
||||
} else {
|
||||
val tempFile = result.first
|
||||
if (!tempFile.renameTo(file)) {
|
||||
context.showShortToast("Failed to rename file")
|
||||
tempFile.delete()
|
||||
}
|
||||
currentImportDest = null
|
||||
currentImportResult = null
|
||||
version++
|
||||
}
|
||||
},
|
||||
dismissLabel = stringRes(R.string.action__cancel),
|
||||
onDismiss = {
|
||||
val tempFile = result.first
|
||||
tempFile.delete()
|
||||
currentImportDest = null
|
||||
currentImportResult = null
|
||||
},
|
||||
) {
|
||||
JetPrefTextField(
|
||||
value = fileNameInput,
|
||||
onValueChange = { fileNameInput = it },
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import androidx.compose.material.icons.automirrored.outlined.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Code
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -48,7 +49,6 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.RadioListItem
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DialogProperty
|
||||
import dev.patrickgold.florisboard.app.settings.theme.PrettyPrintConfig
|
||||
import dev.patrickgold.florisboard.app.settings.theme.ThemeEditorScreen
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
@@ -64,9 +64,9 @@ import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisUnsavedChangesDialog
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
@@ -83,17 +83,15 @@ import dev.patrickgold.florisboard.lib.ext.validate
|
||||
import dev.patrickgold.florisboard.lib.io.FlorisRef
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import org.florisboard.lib.snygg.SnyggStylesheetJsonConfig
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.io.writeJson
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
private val TextFieldVerticalPadding = 8.dp
|
||||
@@ -199,7 +197,7 @@ private fun ExtensionEditScreenSheetSwitcher(
|
||||
ManageDependenciesScreen(workspace)
|
||||
}
|
||||
is EditorAction.ManageFiles -> {
|
||||
ExtensionEditFilesScreen(workspace)
|
||||
ManageFilesScreen(workspace)
|
||||
}
|
||||
is EditorAction.CreateComponent<*> -> {
|
||||
CreateComponentScreen(workspace, action.type)
|
||||
@@ -263,33 +261,17 @@ private fun EditScreen(
|
||||
return
|
||||
}
|
||||
val manifest = extEditor.build()
|
||||
workspace.saverDir.deleteContentsRecursively()
|
||||
val manifestFile = workspace.saverDir.subFile(ExtensionDefaults.MANIFEST_FILE_NAME)
|
||||
manifestFile.writeJson(manifest, ExtensionJsonConfig)
|
||||
when (extEditor) {
|
||||
is ThemeExtensionEditor -> {
|
||||
// TODO: this is hacky
|
||||
val fonts = workspace.extDir.subDir("fonts")
|
||||
if (fonts.exists()) {
|
||||
fonts.copyRecursively(workspace.saverDir.subDir("fonts"), overwrite = true)
|
||||
}
|
||||
val images = workspace.extDir.subDir("images")
|
||||
if (images.exists()) {
|
||||
images.copyRecursively(workspace.saverDir.subDir("images"), overwrite = true)
|
||||
}
|
||||
for (theme in extEditor.themes) {
|
||||
val stylesheetFile = workspace.saverDir.subFile(theme.stylesheetPath())
|
||||
stylesheetFile.parentFile?.mkdirs()
|
||||
val stylesheetEditor = theme.stylesheetEditor
|
||||
if (stylesheetEditor != null) {
|
||||
runCatching {
|
||||
val stylesheet = stylesheetEditor.build().toJson(PrettyPrintConfig).getOrThrow()
|
||||
stylesheetFile.writeText(stylesheet)
|
||||
}.onFailure {
|
||||
// TODO: better error handling
|
||||
context.showLongToast(it.message.toString())
|
||||
return
|
||||
}
|
||||
val stylesheet = stylesheetEditor.build()
|
||||
stylesheetFile.writeJson(stylesheet, SnyggStylesheetJsonConfig)
|
||||
} else {
|
||||
val unmodifiedStylesheetFile = workspace.extDir.subFile(theme.stylesheetPath())
|
||||
if (unmodifiedStylesheetFile.exists()) {
|
||||
@@ -600,6 +582,35 @@ private fun ManageDependenciesScreen(workspace: CacheManager.ExtEditorWorkspace<
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ManageFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = FlorisScreen {
|
||||
title = stringRes(R.string.ext__editor__files__title)
|
||||
|
||||
fun handleBackPress() {
|
||||
workspace.currentAction = null
|
||||
}
|
||||
|
||||
navigationIcon {
|
||||
FlorisIconButton(
|
||||
onClick = { handleBackPress() },
|
||||
icon = Icons.Default.Close,
|
||||
)
|
||||
}
|
||||
|
||||
content {
|
||||
BackHandler {
|
||||
handleBackPress()
|
||||
}
|
||||
|
||||
FlorisInfoCard(
|
||||
modifier = Modifier.padding(all = 8.dp),
|
||||
text = """
|
||||
Managing archive files is currently not supported.
|
||||
""".trimIndent().replace('\n', ' '),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class CreateFrom {
|
||||
EMPTY,
|
||||
EXISTING;
|
||||
@@ -692,14 +703,15 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
val component = editor.themes.find { it.id == componentName.componentId } ?: return
|
||||
val componentEditor = component.let { c ->
|
||||
ThemeExtensionComponentEditor(
|
||||
componentId, c.label, c.authors, c.isNightTheme, stylesheetPath = "",
|
||||
componentId, c.label, c.authors, c.isNightTheme, c.isBorderless,
|
||||
c.isMaterialYouAware, stylesheetPath = "",
|
||||
).also { it.stylesheetEditor = c.stylesheetEditor }
|
||||
}
|
||||
if (componentEditor.stylesheetEditor != null) {
|
||||
val stylesheet = componentEditor.stylesheetEditor!!.build()
|
||||
val stylesheetFile = workspace.extDir.subFile(componentEditor.stylesheetPath())
|
||||
stylesheetFile.parentFile?.mkdirs()
|
||||
val stylesheet = componentEditor.stylesheetEditor!!.build().toJson(PrettyPrintConfig).getOrThrow()
|
||||
stylesheetFile.writeText(stylesheet)
|
||||
stylesheetFile.writeJson(stylesheet, SnyggStylesheetJsonConfig)
|
||||
componentEditor.stylesheetEditor = null
|
||||
} else {
|
||||
val srcStylesheetFile = workspace.extDir.subFile(component.stylesheetPath())
|
||||
@@ -801,37 +813,36 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = stringRes(R.string.ext__meta__id),
|
||||
) {
|
||||
JetPrefTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
FlorisOutlinedTextField(
|
||||
value = newId,
|
||||
onValueChange = { newId = it },
|
||||
singleLine = true,
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = newIdValidation,
|
||||
)
|
||||
Validation(showValidationErrors, newIdValidation)
|
||||
}
|
||||
DialogProperty(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = stringRes(R.string.ext__meta__label),
|
||||
) {
|
||||
JetPrefTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
FlorisOutlinedTextField(
|
||||
value = newLabel,
|
||||
onValueChange = { newLabel = it },
|
||||
singleLine = true,
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = newLabelValidation,
|
||||
)
|
||||
Validation(showValidationErrors, newLabelValidation)
|
||||
|
||||
}
|
||||
DialogProperty(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = stringRes(R.string.ext__meta__authors),
|
||||
) {
|
||||
JetPrefTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
FlorisOutlinedTextField(
|
||||
value = newAuthors,
|
||||
onValueChange = { newAuthors = it },
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = newAuthorsValidation,
|
||||
)
|
||||
Validation(showValidationErrors, newAuthorsValidation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,6 +860,7 @@ private fun EditorSheetTextField(
|
||||
showValidationError: Boolean = false,
|
||||
validationResult: ValidationResult? = null,
|
||||
) {
|
||||
val borderColor = MaterialTheme.colorScheme.outline
|
||||
Column(modifier = Modifier.padding(vertical = TextFieldVerticalPadding)) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -868,13 +880,18 @@ private fun EditorSheetTextField(
|
||||
)
|
||||
}
|
||||
}
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
enabled = enabled,
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
singleLine = singleLine,
|
||||
showValidationError = showValidationError,
|
||||
validationResult = validationResult,
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedBorderColor = borderColor,
|
||||
disabledBorderColor = borderColor,
|
||||
)
|
||||
)
|
||||
Validation(showValidationError, validationResult)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,33 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Input
|
||||
import androidx.compose.material.icons.filled.Keyboard
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.Palette
|
||||
import androidx.compose.material.icons.filled.Shop
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
|
||||
@Composable
|
||||
@@ -41,7 +56,36 @@ fun ExtensionHomeScreen() = FlorisScreen {
|
||||
val extensionIndex = extensionManager.combinedExtensionList()
|
||||
|
||||
content {
|
||||
ImportExtensionBox(navController)
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 4.dp),
|
||||
text = stringRes(id = R.string.ext__home__info),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
context.launchUrl("https://${BuildConfig.FLADDONS_STORE_URL}/")
|
||||
},
|
||||
icon = Icons.Default.Shop,
|
||||
text = stringRes(id = R.string.ext__home__visit_store),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Import(ExtensionImportScreenType.EXT_ANY, null))
|
||||
},
|
||||
icon = Icons.AutoMirrored.Filled.Input,
|
||||
text = stringRes(R.string.action__import),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
UpdateBox(extensionIndex = extensionIndex)
|
||||
|
||||
|
||||
@@ -113,9 +113,6 @@ fun ExtensionListScreen(type: ExtensionListScreenType, showUpdate: Boolean) = Fl
|
||||
contentPadding = PaddingValues(bottom = fabHeightDp),
|
||||
) {
|
||||
if (showUpdate) {
|
||||
item {
|
||||
ImportExtensionBox(navController)
|
||||
}
|
||||
item {
|
||||
UpdateBox(extensionIndex = extensionIndex)
|
||||
}
|
||||
|
||||
@@ -152,8 +152,8 @@ fun HomeScreen() = FlorisScreen {
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Outlined.Build,
|
||||
title = stringRes(R.string.settings__other__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Other) },
|
||||
title = stringRes(R.string.settings__advanced__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Advanced) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Outlined.Info,
|
||||
|
||||
@@ -21,9 +21,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.libraryColors
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibraryDefaults
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
|
||||
@@ -34,6 +34,7 @@ import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
@@ -51,8 +52,8 @@ import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
|
||||
@Composable
|
||||
fun OtherScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__other__title)
|
||||
fun AdvancedScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__advanced__title)
|
||||
previewFieldVisible = false
|
||||
|
||||
val navController = LocalNavController.current
|
||||
@@ -60,14 +61,14 @@ fun OtherScreen() = FlorisScreen {
|
||||
|
||||
content {
|
||||
ListPreference(
|
||||
prefs.other.settingsTheme,
|
||||
prefs.advanced.settingsTheme,
|
||||
icon = Icons.Default.Palette,
|
||||
title = stringRes(R.string.pref__other__settings_theme__label),
|
||||
title = stringRes(R.string.pref__advanced__settings_theme__label),
|
||||
entries = enumDisplayEntriesOf(AppTheme::class),
|
||||
)
|
||||
ColorPickerPreference(
|
||||
pref = prefs.other.accentColor,
|
||||
title = stringRes(R.string.pref__other__settings_accent_color__label),
|
||||
pref = prefs.advanced.accentColor,
|
||||
title = stringRes(R.string.pref__advanced__settings_accent_color__label),
|
||||
defaultValueLabel = stringRes(R.string.action__default),
|
||||
icon = Icons.Default.FormatColorFill,
|
||||
defaultColors = ColorMappings.colors,
|
||||
@@ -82,9 +83,9 @@ fun OtherScreen() = FlorisScreen {
|
||||
}
|
||||
)
|
||||
ListPreference(
|
||||
prefs.other.settingsLanguage,
|
||||
prefs.advanced.settingsLanguage,
|
||||
icon = Icons.Default.Language,
|
||||
title = stringRes(R.string.pref__other__settings_language__label),
|
||||
title = stringRes(R.string.pref__advanced__settings_language__label),
|
||||
entries = listPrefEntries {
|
||||
listOf(
|
||||
"auto",
|
||||
@@ -146,19 +147,20 @@ fun OtherScreen() = FlorisScreen {
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.other.showAppIcon,
|
||||
prefs.advanced.showAppIcon,
|
||||
icon = Icons.Default.Preview,
|
||||
title = stringRes(R.string.pref__other__show_app_icon__label),
|
||||
title = stringRes(R.string.pref__advanced__show_app_icon__label),
|
||||
summary = when {
|
||||
AndroidVersion.ATLEAST_API29_Q -> stringRes(R.string.pref__other__show_app_icon__summary_atleast_q)
|
||||
AndroidVersion.ATLEAST_API29_Q -> stringRes(R.string.pref__advanced__show_app_icon__summary_atleast_q)
|
||||
else -> null
|
||||
},
|
||||
enabledIf = { AndroidVersion.ATMOST_API28_P },
|
||||
)
|
||||
Preference(
|
||||
icon = vectorResource(R.drawable.ic_keyboard_keys),
|
||||
title = stringRes(R.string.physical_keyboard__title),
|
||||
onClick = { navController.navigate(Routes.Settings.PhysicalKeyboard) },
|
||||
ListPreference(
|
||||
prefs.advanced.incognitoMode,
|
||||
icon = vectorResource(id = R.drawable.ic_incognito),
|
||||
title = stringRes(R.string.pref__advanced__incognito_mode__label),
|
||||
entries = enumDisplayEntriesOf(IncognitoMode::class),
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Adb,
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The FlorisBoard Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.advanced
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
|
||||
@Composable
|
||||
fun PhysicalKeyboardScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.physical_keyboard__title)
|
||||
|
||||
val context = LocalContext.current
|
||||
val physicalKeyboardAttached by remember {
|
||||
mutableStateOf(context.resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS)
|
||||
}
|
||||
|
||||
val activityForResult = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { }
|
||||
|
||||
content {
|
||||
if (physicalKeyboardAttached) {
|
||||
Preference(
|
||||
title = stringRes(R.string.physical_keyboard__system_settings__title),
|
||||
summary = stringRes(R.string.physical_keyboard__system_settings__summary),
|
||||
onClick = {
|
||||
activityForResult.launch(Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS))
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Preference(
|
||||
title = stringRes(R.string.physical_keyboard__system_settings__title),
|
||||
summary = stringRes(R.string.physical_keyboard__system_settings__summary_not_attached),
|
||||
)
|
||||
}
|
||||
SwitchPreference(
|
||||
pref = prefs.physicalKeyboard.showOnScreenKeyboard,
|
||||
title = stringRes(R.string.physical_keyboard__show_on_screen_keyboard__title),
|
||||
summary = stringRes(R.string.physical_keyboard__show_on_screen_keyboard__summary),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package dev.patrickgold.florisboard.app.settings.clipboard
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.clipboard.CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.pluralsRes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
@@ -53,47 +52,12 @@ fun ClipboardScreen() = FlorisScreen {
|
||||
enabledIf = { prefs.clipboard.useInternalClipboard isEqualTo true },
|
||||
)
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.pref__clipboard__group_clipboard_suggestion__label)) {
|
||||
SwitchPreference(
|
||||
prefs.clipboard.suggestionEnabled,
|
||||
title = stringRes(R.string.pref__clipboard__suggestion_enabled__label),
|
||||
summary = stringRes(R.string.pref__clipboard__suggestion_enabled__summary),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.clipboard.suggestionTimeout,
|
||||
title = stringRes(R.string.pref__clipboard__suggestion_timeout__label),
|
||||
valueLabel = { stringRes(R.string.pref__clipboard__suggestion_timeout__summary, "v" to it) },
|
||||
min = 30,
|
||||
max = 300,
|
||||
stepIncrement = 5,
|
||||
enabledIf = { prefs.clipboard.suggestionEnabled isEqualTo true },
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.pref__clipboard__group_clipboard_history__label)) {
|
||||
SwitchPreference(
|
||||
prefs.clipboard.historyEnabled,
|
||||
title = stringRes(R.string.pref__clipboard__enable_clipboard_history__label),
|
||||
summary = stringRes(R.string.pref__clipboard__enable_clipboard_history__summary),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
primaryPref = prefs.clipboard.numHistoryGridColumnsPortrait,
|
||||
secondaryPref = prefs.clipboard.numHistoryGridColumnsLandscape,
|
||||
title = stringRes(R.string.pref__clipboard__num_history_grid_columns__label),
|
||||
primaryLabel = stringRes(R.string.screen_orientation__portrait),
|
||||
secondaryLabel = stringRes(R.string.screen_orientation__landscape),
|
||||
valueLabel = { numGridColumns ->
|
||||
if (numGridColumns == CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO) {
|
||||
stringRes(R.string.general__auto)
|
||||
} else {
|
||||
numGridColumns.toString()
|
||||
}
|
||||
},
|
||||
min = 0,
|
||||
max = 10,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.clipboard.historyEnabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.clipboard.cleanUpOld,
|
||||
title = stringRes(R.string.pref__clipboard__clean_up_old__label),
|
||||
|
||||
@@ -56,15 +56,14 @@ import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryEntry
|
||||
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryValidation
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
@@ -367,35 +366,39 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
) {
|
||||
Column {
|
||||
DialogProperty(text = stringRes(R.string.settings__udm__dialog__word_label)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = word,
|
||||
onValueChange = { word = it },
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = wordValidation,
|
||||
)
|
||||
Validation(showValidationErrors, wordValidation)
|
||||
}
|
||||
DialogProperty(text = stringRes(
|
||||
R.string.settings__udm__dialog__freq_label,
|
||||
"f_min" to FREQUENCY_MIN, "f_max" to FREQUENCY_MAX,
|
||||
)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = freq,
|
||||
onValueChange = { freq = it },
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = freqValidation,
|
||||
)
|
||||
Validation(showValidationErrors, freqValidation)
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.settings__udm__dialog__shortcut_label)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = shortcut,
|
||||
onValueChange = { shortcut = it },
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = shortcutValidation,
|
||||
)
|
||||
Validation(showValidationErrors, shortcutValidation)
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.settings__udm__dialog__locale_label)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = locale,
|
||||
onValueChange = { locale = it },
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = localeValidation,
|
||||
)
|
||||
Validation(showValidationErrors, localeValidation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
|
||||
@@ -29,8 +32,6 @@ import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -137,6 +138,8 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
summary = { strength ->
|
||||
if (vibrator == null || !vibrator.hasVibrator()) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_vibrator)
|
||||
} else if (AndroidVersion.ATMOST_API25_N_MR1) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_unsupported_android_version)
|
||||
} else if (!vibrator.hasAmplitudeControl()) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_amplitude_ctrl)
|
||||
} else {
|
||||
@@ -154,7 +157,7 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
prefs.inputFeedback.hapticEnabled isEqualTo true &&
|
||||
prefs.inputFeedback.hapticVibrationMode isEqualTo HapticVibrationMode.USE_VIBRATOR_DIRECTLY &&
|
||||
vibrator != null && vibrator.hasVibrator() &&
|
||||
vibrator.hasAmplitudeControl()
|
||||
AndroidVersion.ATLEAST_API26_O && vibrator.hasAmplitudeControl()
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
|
||||
@@ -107,10 +107,8 @@ fun KeyboardScreen() = FlorisScreen {
|
||||
PreferenceGroup(title = stringRes(R.string.pref__keyboard__group_layout__label)) {
|
||||
ListPreference(
|
||||
prefs.keyboard.oneHandedMode,
|
||||
prefs.keyboard.oneHandedModeEnabled,
|
||||
title = stringRes(R.string.pref__keyboard__one_handed_mode__label),
|
||||
entries = enumDisplayEntriesOf(OneHandedMode::class),
|
||||
summarySwitchDisabled = stringRes(R.string.state__disabled),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.keyboard.oneHandedModeScaleFactor,
|
||||
@@ -119,7 +117,7 @@ fun KeyboardScreen() = FlorisScreen {
|
||||
min = 70,
|
||||
max = 90,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.keyboard.oneHandedModeEnabled.isTrue() },
|
||||
enabledIf = { prefs.keyboard.oneHandedMode isNotEqualTo OneHandedMode.OFF },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.keyboard.landscapeInputUiMode,
|
||||
|
||||
@@ -53,7 +53,6 @@ import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -104,10 +103,6 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__localization__display_language_names_in__label),
|
||||
entries = enumDisplayEntriesOf(DisplayLanguageNamesIn::class),
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage,
|
||||
title = stringRes(R.string.settings__localization__display_keyboard_labels_in_subtype_language),
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.settings__localization__language_pack_title),
|
||||
summary = stringRes(R.string.settings__localization__language_pack_summary),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The FlorisBoard Contributors
|
||||
* Copyright (C) 2022-2025 The FlorisBoard Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,16 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.florisboard.lib.kotlin
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun KClass<*>.simpleNameOrEnclosing(): String? {
|
||||
return if (this.simpleName == "Companion") {
|
||||
// Companion object => get the enclosing class
|
||||
this.java.enclosingClass.simpleName
|
||||
} else {
|
||||
// Normal object => directly get class
|
||||
this.simpleName
|
||||
}
|
||||
/**
|
||||
* DisplayColorsAs indicates how color strings should be visually presented to the user.
|
||||
*/
|
||||
enum class DisplayColorsAs {
|
||||
HEX8,
|
||||
RGBA;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,8 +48,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -59,6 +59,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
@@ -69,7 +70,6 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.input.InputKeyEventReceiver
|
||||
import dev.patrickgold.florisboard.ime.input.InputShiftState
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
@@ -82,35 +82,30 @@ import dev.patrickgold.florisboard.ime.keyboard.computeImageVector
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeLabel
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.NATIVE_NULLPTR
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextFieldDefaults
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
import org.florisboard.lib.snygg.SnyggAttributes
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownMenu
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.NonNullSaver
|
||||
import kotlin.reflect.KClass
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import org.florisboard.lib.kotlin.getKeyByValue
|
||||
|
||||
private val TransparentTextSelectionColors = TextSelectionColors(
|
||||
handleColor = Color.Transparent,
|
||||
backgroundColor = Color.Transparent,
|
||||
)
|
||||
internal val SnyggEmptyRuleForAdding = SnyggElementRule(elementName = "--select--")
|
||||
internal val SnyggEmptyRuleForAdding = SnyggRule(element = "- select -")
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
@@ -121,69 +116,69 @@ internal fun EditRuleDialog(
|
||||
onDeleteRule: (rule: SnyggRule) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val isAddRuleDialog = initRule == SnyggEmptyRuleForAdding
|
||||
var showSelectAsError by rememberSaveable { mutableStateOf(false) }
|
||||
var showAlreadyExistsError by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val possibleRuleTemplates = remember {
|
||||
buildList {
|
||||
add(SnyggEmptyRuleForAdding)
|
||||
add(SnyggAnnotationRule.Font(fontName = ""))
|
||||
FlorisImeUi.elementNames.forEach { name ->
|
||||
add(SnyggElementRule(name))
|
||||
}
|
||||
}
|
||||
}
|
||||
val possibleRuleLabels = possibleRuleTemplates.map { rule ->
|
||||
val elementName = when (rule) {
|
||||
is SnyggElementRule -> rule.elementName
|
||||
else -> rule.decl().name
|
||||
}
|
||||
context.translateElementName(elementName, level) ?: rule
|
||||
val possibleElementNames = remember {
|
||||
listOf(SnyggEmptyRuleForAdding.element) + FlorisImeUiSpec.elements.keys
|
||||
}
|
||||
val possibleElementLabels = possibleElementNames.map { translateElementName(it, level) ?: it }
|
||||
var elementsExpanded by remember { mutableStateOf(false) }
|
||||
var elementsSelectedIndex by rememberSaveable {
|
||||
val index = possibleRuleTemplates
|
||||
.indexOfFirst { rule ->
|
||||
val elementName = when (rule) {
|
||||
is SnyggElementRule -> rule.elementName
|
||||
else -> rule.decl().name
|
||||
}
|
||||
val initElementName = when (initRule) {
|
||||
is SnyggElementRule -> initRule.elementName
|
||||
else -> initRule.decl().name
|
||||
}
|
||||
elementName == initElementName
|
||||
}
|
||||
.coerceIn(possibleRuleTemplates.indices)
|
||||
val index = possibleElementNames.indexOf(initRule.element).coerceIn(possibleElementNames.indices)
|
||||
mutableIntStateOf(index)
|
||||
}
|
||||
var currentRule by rememberSaveable(elementsSelectedIndex, stateSaver = SnyggRule.NonNullSaver) {
|
||||
mutableStateOf(
|
||||
if (isAddRuleDialog) possibleRuleTemplates[elementsSelectedIndex] else initRule
|
||||
)
|
||||
|
||||
val codes = rememberSaveable(saver = IntListSaver) { initRule.codes.toMutableStateList() }
|
||||
var editCodeDialogValue by rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
val groups = rememberSaveable(saver = IntListSaver) { initRule.groups.toMutableStateList() }
|
||||
var shiftStateUnshifted by rememberSaveable {
|
||||
mutableStateOf(initRule.shiftStates.contains(InputShiftState.UNSHIFTED.value))
|
||||
}
|
||||
var shiftStateShiftedManual by rememberSaveable {
|
||||
mutableStateOf(initRule.shiftStates.contains(InputShiftState.SHIFTED_MANUAL.value))
|
||||
}
|
||||
var shiftStateShiftedAutomatic by rememberSaveable {
|
||||
mutableStateOf(initRule.shiftStates.contains(InputShiftState.SHIFTED_AUTOMATIC.value))
|
||||
}
|
||||
var shiftStateCapsLock by rememberSaveable {
|
||||
mutableStateOf(initRule.shiftStates.contains(InputShiftState.CAPS_LOCK.value))
|
||||
}
|
||||
var pressedSelector by rememberSaveable { mutableStateOf(initRule.pressedSelector) }
|
||||
var focusSelector by rememberSaveable { mutableStateOf(initRule.focusSelector) }
|
||||
var disabledSelector by rememberSaveable { mutableStateOf(initRule.disabledSelector) }
|
||||
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(
|
||||
if (isAddRuleDialog) {
|
||||
R.string.settings__theme_editor__add_rule
|
||||
} else {
|
||||
R.string.settings__theme_editor__edit_rule
|
||||
}
|
||||
),
|
||||
confirmLabel = stringRes(
|
||||
if (isAddRuleDialog) {
|
||||
R.string.action__add
|
||||
} else {
|
||||
R.string.action__apply
|
||||
}
|
||||
),
|
||||
title = stringRes(if (isAddRuleDialog) {
|
||||
R.string.settings__theme_editor__add_rule
|
||||
} else {
|
||||
R.string.settings__theme_editor__edit_rule
|
||||
}),
|
||||
confirmLabel = stringRes(if (isAddRuleDialog) {
|
||||
R.string.action__add
|
||||
} else {
|
||||
R.string.action__apply
|
||||
}),
|
||||
onConfirm = {
|
||||
if (isAddRuleDialog && elementsSelectedIndex == 0) {
|
||||
showSelectAsError = true
|
||||
} else {
|
||||
if (!onConfirmRule(initRule, currentRule)) {
|
||||
val newRule = SnyggRule(
|
||||
element = possibleElementNames[elementsSelectedIndex],
|
||||
codes = codes.toList(),
|
||||
groups = groups.toList(),
|
||||
shiftStates = buildList {
|
||||
if (shiftStateUnshifted) { add(InputShiftState.UNSHIFTED.value) }
|
||||
if (shiftStateShiftedManual) { add(InputShiftState.SHIFTED_MANUAL.value) }
|
||||
if (shiftStateShiftedAutomatic) { add(InputShiftState.SHIFTED_AUTOMATIC.value) }
|
||||
if (shiftStateCapsLock) { add(InputShiftState.CAPS_LOCK.value) }
|
||||
},
|
||||
pressedSelector = pressedSelector,
|
||||
focusSelector = focusSelector,
|
||||
disabledSelector = disabledSelector,
|
||||
)
|
||||
if (!onConfirmRule(initRule, newRule)) {
|
||||
showAlreadyExistsError = true
|
||||
}
|
||||
}
|
||||
@@ -207,168 +202,154 @@ internal fun EditRuleDialog(
|
||||
)
|
||||
}
|
||||
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_name)) {
|
||||
JetPrefDropdown(
|
||||
options = possibleRuleLabels,
|
||||
selectedOptionIndex = elementsSelectedIndex,
|
||||
onSelectOption = { elementsSelectedIndex = it },
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_element)) {
|
||||
FlorisDropdownMenu(
|
||||
items = possibleElementLabels,
|
||||
expanded = elementsExpanded,
|
||||
enabled = isAddRuleDialog,
|
||||
selectedIndex = elementsSelectedIndex,
|
||||
isError = showSelectAsError && elementsSelectedIndex == 0,
|
||||
onSelectItem = { elementsSelectedIndex = it },
|
||||
onExpandRequest = { elementsExpanded = true },
|
||||
onDismissRequest = { elementsExpanded = false },
|
||||
)
|
||||
}
|
||||
|
||||
(currentRule as? SnyggAnnotationRule.Font)?.apply {
|
||||
DialogProperty(text = stringRes(R.string.snygg__rule_annotation__font_name)) {
|
||||
JetPrefTextField(
|
||||
modifier = Modifier,
|
||||
value = fontName,
|
||||
onValueChange = {
|
||||
currentRule = copy(fontName = it)
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_selectors)) {
|
||||
Row(modifier = Modifier.florisHorizontalScroll()) {
|
||||
FlorisChip(
|
||||
onClick = { pressedSelector = !pressedSelector },
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.PRESSED_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
},
|
||||
singleLine = true,
|
||||
selected = pressedSelector,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { focusSelector = !focusSelector },
|
||||
modifier = Modifier.padding( end = 4.dp),
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.FOCUS_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
},
|
||||
selected = focusSelector,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { disabledSelector = !disabledSelector },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.DISABLED_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__disabled)
|
||||
},
|
||||
selected = disabledSelector,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move to toplevel @Composable function
|
||||
(currentRule as? SnyggElementRule)?.apply {
|
||||
if (elementName == SnyggEmptyRuleForAdding.elementName) {
|
||||
return@apply
|
||||
}
|
||||
fun updateCurrentRule(newSelector: SnyggSelector) {
|
||||
currentRule = if (selector == newSelector) {
|
||||
copy(selector = SnyggSelector.NONE)
|
||||
DialogProperty(
|
||||
text = stringRes(R.string.settings__theme_editor__rule_codes),
|
||||
trailingIconTitle = {
|
||||
FlorisIconButton(
|
||||
onClick = { editCodeDialogValue = NATIVE_NULLPTR.toInt() },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.Add,
|
||||
)
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
text = stringRes(if (codes.isEmpty()) {
|
||||
R.string.settings__theme_editor__no_codes_defined
|
||||
} else {
|
||||
copy(selector = newSelector)
|
||||
}
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_selectors)) {
|
||||
Row(
|
||||
modifier = Modifier.florisHorizontalScroll(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
// TODO: avoid code duplication
|
||||
R.string.settings__theme_editor__codes_defined
|
||||
}),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
FlowRow {
|
||||
for (code in codes) {
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.PRESSED) },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.PRESSED.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
},
|
||||
selected = selector == SnyggSelector.PRESSED,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.FOCUS) },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.FOCUS.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
},
|
||||
selected = selector == SnyggSelector.FOCUS,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.HOVER) },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.HOVER.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__hover)
|
||||
},
|
||||
selected = selector == SnyggSelector.HOVER,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.DISABLED) },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.DISABLED.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__disabled)
|
||||
},
|
||||
selected = selector == SnyggSelector.DISABLED,
|
||||
onClick = { editCodeDialogValue = code },
|
||||
text = code.toString(),
|
||||
selected = editCodeDialogValue == code,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val codes = remember(currentRule) {
|
||||
attributes[FlorisImeUi.Attr.Code] ?: emptyList()
|
||||
}
|
||||
var editCodeDialogValue by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val initCodeValue = editCodeDialogValue
|
||||
if (initCodeValue != null) {
|
||||
EditCodeValueDialog(
|
||||
codeValue = initCodeValue,
|
||||
checkExisting = { codes.contains(it) },
|
||||
onAdd = {
|
||||
currentRule = copy(
|
||||
attributes = attributes.including(FlorisImeUi.Attr.Code to it)
|
||||
)
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_shift_states)) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
FlorisChip(
|
||||
onClick = { shiftStateUnshifted = !shiftStateUnshifted },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> remember {
|
||||
SnyggRule.Placeholders.getKeyByValue(InputShiftState.UNSHIFTED.value)
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__unshifted)
|
||||
},
|
||||
onDelete = {
|
||||
currentRule = copy(
|
||||
attributes = attributes.excluding(FlorisImeUi.Attr.Code to it)
|
||||
)
|
||||
selected = shiftStateUnshifted,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateShiftedManual = !shiftStateShiftedManual },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> remember {
|
||||
SnyggRule.Placeholders.getKeyByValue(InputShiftState.SHIFTED_MANUAL.value)
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_manual)
|
||||
},
|
||||
onDismiss = { editCodeDialogValue = null },
|
||||
selected = shiftStateShiftedManual,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateShiftedAutomatic = !shiftStateShiftedAutomatic },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> remember {
|
||||
SnyggRule.Placeholders.getKeyByValue(InputShiftState.SHIFTED_AUTOMATIC.value)
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_automatic)
|
||||
},
|
||||
selected = shiftStateShiftedAutomatic,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateCapsLock = !shiftStateCapsLock },
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> remember {
|
||||
SnyggRule.Placeholders.getKeyByValue(InputShiftState.CAPS_LOCK.value)
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__caps_lock)
|
||||
},
|
||||
selected = shiftStateCapsLock,
|
||||
)
|
||||
}
|
||||
DialogProperty(
|
||||
text = stringRes(R.string.settings__theme_editor__rule_codes),
|
||||
trailingIconTitle = {
|
||||
FlorisIconButton(
|
||||
onClick = { editCodeDialogValue = KeyCode.UNSPECIFIED.toString() },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.Add,
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (codes.isEmpty()) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__no_codes_defined),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (code in codes) {
|
||||
FlorisChip(
|
||||
onClick = { editCodeDialogValue = code },
|
||||
text = code,
|
||||
selected = editCodeDialogValue == code,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnumLikeAttributeBox(
|
||||
text = stringRes(R.string.settings__theme_editor__rule_modes),
|
||||
enumClass = KeyboardMode::class,
|
||||
attribute = FlorisImeUi.Attr.Mode,
|
||||
attributes = attributes,
|
||||
setAttributes = { currentRule = copy(attributes = it) },
|
||||
level = level,
|
||||
)
|
||||
|
||||
EnumLikeAttributeBox(
|
||||
text = stringRes(R.string.settings__theme_editor__rule_shift_states),
|
||||
enumClass = InputShiftState::class,
|
||||
attribute = FlorisImeUi.Attr.ShiftState,
|
||||
attributes = attributes,
|
||||
setAttributes = { currentRule = copy(attributes = it) },
|
||||
level = level,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val initCodeValue = editCodeDialogValue
|
||||
if (initCodeValue != null) {
|
||||
EditCodeValueDialog(
|
||||
codeValue = initCodeValue,
|
||||
checkExisting = { codes.contains(it) },
|
||||
onAdd = { codes.add(it) },
|
||||
onDelete = { codes.remove(it) },
|
||||
onDismiss = { editCodeDialogValue = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun EditCodeValueDialog(
|
||||
codeValue: String,
|
||||
checkExisting: (String) -> Boolean,
|
||||
onAdd: (String) -> Unit,
|
||||
onDelete: (String) -> Unit,
|
||||
codeValue: Int,
|
||||
checkExisting: (Int) -> Boolean,
|
||||
onAdd: (Int) -> Unit,
|
||||
onDelete: (Int) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
var inputCodeString by rememberSaveable(codeValue) {
|
||||
val str = if (codeValue == KeyCode.UNSPECIFIED.toString()) "" else codeValue.toString()
|
||||
val str = if (codeValue == 0) "" else codeValue.toString()
|
||||
mutableStateOf(str)
|
||||
}
|
||||
val textKeyData = remember(inputCodeString) {
|
||||
@@ -425,7 +406,6 @@ private fun EditCodeValueDialog(
|
||||
inputCodeString = data.code.toString()
|
||||
isRecordingKey = false
|
||||
}
|
||||
|
||||
override fun onInputKeyRepeat(data: KeyData) = Unit
|
||||
override fun onInputKeyCancel(data: KeyData) = Unit
|
||||
}
|
||||
@@ -443,20 +423,16 @@ private fun EditCodeValueDialog(
|
||||
}
|
||||
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(
|
||||
if (codeValue == KeyCode.UNSPECIFIED.toString()) {
|
||||
R.string.settings__theme_editor__add_code
|
||||
} else {
|
||||
R.string.settings__theme_editor__edit_code
|
||||
}
|
||||
),
|
||||
confirmLabel = stringRes(
|
||||
if (codeValue == KeyCode.UNSPECIFIED.toString()) {
|
||||
R.string.action__add
|
||||
} else {
|
||||
R.string.action__apply
|
||||
}
|
||||
),
|
||||
title = stringRes(if (codeValue == NATIVE_NULLPTR.toInt()) {
|
||||
R.string.settings__theme_editor__add_code
|
||||
} else {
|
||||
R.string.settings__theme_editor__edit_code
|
||||
}),
|
||||
confirmLabel = stringRes(if (codeValue == NATIVE_NULLPTR.toInt()) {
|
||||
R.string.action__add
|
||||
} else {
|
||||
R.string.action__apply
|
||||
}),
|
||||
onConfirm = {
|
||||
val code = inputCodeString.trim().toIntOrNull(radix = 10)
|
||||
when {
|
||||
@@ -464,28 +440,25 @@ private fun EditCodeValueDialog(
|
||||
errorId = R.string.settings__theme_editor__code_invalid
|
||||
showError = true
|
||||
}
|
||||
|
||||
code.toString() == codeValue -> {
|
||||
code == codeValue -> {
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
checkExisting(code.toString()) -> {
|
||||
checkExisting(code) -> {
|
||||
errorId = R.string.settings__theme_editor__code_already_exists
|
||||
showError = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (codeValue != KeyCode.UNSPECIFIED.toString()) {
|
||||
if (codeValue != NATIVE_NULLPTR.toInt()) {
|
||||
onDelete(codeValue)
|
||||
}
|
||||
onAdd(code.toString())
|
||||
onAdd(code)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissLabel = stringRes(R.string.action__cancel),
|
||||
onDismiss = onDismiss,
|
||||
neutralLabel = if (codeValue != KeyCode.UNSPECIFIED.toString()) {
|
||||
neutralLabel = if (codeValue != NATIVE_NULLPTR.toInt()) {
|
||||
stringRes(R.string.action__delete)
|
||||
} else {
|
||||
null
|
||||
@@ -532,7 +505,7 @@ private fun EditCodeValueDialog(
|
||||
LocalTextSelectionColors.current
|
||||
}
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides textSelectionColors) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.weight(1f),
|
||||
@@ -541,7 +514,7 @@ private fun EditCodeValueDialog(
|
||||
inputCodeString = v
|
||||
showError = false
|
||||
},
|
||||
placeholderText = when {
|
||||
placeholder = when {
|
||||
isRecordingKey -> {
|
||||
stringRes(R.string.settings__theme_editor__code_recording_placeholder)
|
||||
}
|
||||
@@ -554,25 +527,21 @@ private fun EditCodeValueDialog(
|
||||
},
|
||||
isError = showError,
|
||||
singleLine = true,
|
||||
appearance = JetPrefTextFieldDefaults.filled(
|
||||
colors = if (isRecordingKey) {
|
||||
TextFieldDefaults.colors(
|
||||
focusedTextColor = Color.Transparent,
|
||||
cursorColor = Color.Transparent,
|
||||
)
|
||||
} else {
|
||||
TextFieldDefaults.colors()
|
||||
}
|
||||
),
|
||||
trailingIcon = {
|
||||
FlorisIconButton(
|
||||
onClick = { requestStartRecording() },
|
||||
icon = Icons.Default.Pageview,
|
||||
iconColor = recordingKeyColor,
|
||||
colors = if (isRecordingKey) {
|
||||
OutlinedTextFieldDefaults.colors(
|
||||
focusedTextColor = Color.Transparent,
|
||||
cursorColor = Color.Transparent,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
OutlinedTextFieldDefaults.colors()
|
||||
},
|
||||
)
|
||||
}
|
||||
FlorisIconButton(
|
||||
onClick = { requestStartRecording() },
|
||||
icon = Icons.Default.Pageview,
|
||||
iconColor = recordingKeyColor,
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(visible = showError) {
|
||||
Text(
|
||||
@@ -604,12 +573,9 @@ private fun TextKeyDataPreviewBox(
|
||||
override val mode = KeyboardMode.NUMERIC_ADVANCED
|
||||
override fun getKeyForPos(pointerX: Float, pointerY: Float) = error("not implemented")
|
||||
override fun keys() = error("not implemented")
|
||||
override fun layout(
|
||||
keyboardWidth: Float, keyboardHeight: Float, desiredKey: Key,
|
||||
extendTouchBoundariesDownwards: Boolean,
|
||||
) = error("not implemented")
|
||||
override fun layout(keyboardWidth: Float, keyboardHeight: Float, desiredKey: Key,
|
||||
extendTouchBoundariesDownwards: Boolean) = error("not implemented")
|
||||
}
|
||||
|
||||
override fun context() = context
|
||||
}
|
||||
}
|
||||
@@ -637,13 +603,7 @@ private fun TextKeyDataPreviewBox(
|
||||
.align(Alignment.CenterVertically),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
modifier = Modifier.requiredSize(24.dp),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
)
|
||||
} else if (label != null) {
|
||||
if (label != null) {
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 16.sp,
|
||||
@@ -651,6 +611,13 @@ private fun TextKeyDataPreviewBox(
|
||||
softWrap = false,
|
||||
)
|
||||
}
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
modifier = Modifier.requiredSize(24.dp),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(text = displayName)
|
||||
@@ -658,79 +625,3 @@ private fun TextKeyDataPreviewBox(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun <V : Any> EnumLikeAttributeBox(
|
||||
text: String,
|
||||
enumClass: KClass<V>,
|
||||
attribute: String,
|
||||
attributes: SnyggAttributes,
|
||||
setAttributes: (SnyggAttributes) -> Unit,
|
||||
level: SnyggLevel,
|
||||
) {
|
||||
val allEntries = enumDisplayEntriesOf(enumClass)
|
||||
val (alreadyAddedEntries, notYetAddedEntries) = remember(attributes, attribute) {
|
||||
allEntries.partition { entry ->
|
||||
attributes[attribute]?.contains(entry.key.toString()) == true
|
||||
}
|
||||
}
|
||||
var showAddDialog by remember { mutableStateOf(false) }
|
||||
|
||||
DialogProperty(
|
||||
text = text,
|
||||
trailingIconTitle = {
|
||||
FlorisIconButton(
|
||||
onClick = { showAddDialog = true },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.Add,
|
||||
)
|
||||
},
|
||||
) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (entry in alreadyAddedEntries) {
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
setAttributes(attributes.excluding(attribute to entry.key.toString()))
|
||||
},
|
||||
text = entry.label,
|
||||
)
|
||||
}
|
||||
if (alreadyAddedEntries.isEmpty()) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__no_codes_defined),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showAddDialog) {
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.action__add),
|
||||
dismissLabel = stringRes(R.string.action__cancel),
|
||||
onDismiss = { showAddDialog = false },
|
||||
) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (entry in notYetAddedEntries) {
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
setAttributes(attributes.including(attribute to entry.key.toString()))
|
||||
showAddDialog = false
|
||||
},
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> entry.key.toString()
|
||||
else -> entry.label
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
if (notYetAddedEntries.isEmpty()) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__no_enum_value_to_add_anymore),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
|
||||
private val FineTuneContentPadding = PaddingValues(horizontal = 8.dp)
|
||||
@@ -44,9 +44,9 @@ fun FineTuneDialog(onDismiss: () -> Unit) {
|
||||
entries = enumDisplayEntriesOf(SnyggLevel::class),
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorColorRepresentation,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__color_representation),
|
||||
entries = enumDisplayEntriesOf(ColorRepresentation::class),
|
||||
listPref = prefs.theme.editorDisplayColorsAs,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__display_colors_as),
|
||||
entries = enumDisplayEntriesOf(DisplayColorsAs::class),
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorDisplayKbdAfterDialogs,
|
||||
|
||||
@@ -26,64 +26,29 @@ import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatAlignLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatAlignRight
|
||||
import androidx.compose.material.icons.automirrored.filled.WrapText
|
||||
import androidx.compose.material.icons.filled.AttachFile
|
||||
import androidx.compose.material.icons.filled.CheckBox
|
||||
import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
|
||||
import androidx.compose.material.icons.filled.FontDownload
|
||||
import androidx.compose.material.icons.filled.FormatAlignCenter
|
||||
import androidx.compose.material.icons.filled.FormatAlignJustify
|
||||
import androidx.compose.material.icons.filled.FormatBold
|
||||
import androidx.compose.material.icons.filled.FormatItalic
|
||||
import androidx.compose.material.icons.filled.FormatSize
|
||||
import androidx.compose.material.icons.filled.FormatStrikethrough
|
||||
import androidx.compose.material.icons.filled.FormatUnderlined
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.OpenInFull
|
||||
import androidx.compose.material.icons.filled.Padding
|
||||
import androidx.compose.material.icons.filled.Straighten
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggStaticColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValue
|
||||
import dev.patrickgold.jetpref.material.ui.checkeredBackground
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
import org.florisboard.lib.color.getColor
|
||||
import org.florisboard.lib.snygg.value.SnyggContentScaleValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCustomFontFamilyValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDynamicDarkColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDynamicLightColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggFontStyleValue
|
||||
import org.florisboard.lib.snygg.value.SnyggFontWeightValue
|
||||
import org.florisboard.lib.snygg.value.SnyggGenericFontFamilyValue
|
||||
import org.florisboard.lib.snygg.value.SnyggNoValue
|
||||
import org.florisboard.lib.snygg.value.SnyggPaddingValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextAlignValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextDecorationLineValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextOverflowValue
|
||||
import org.florisboard.lib.snygg.value.SnyggUriValue
|
||||
import org.florisboard.lib.snygg.value.SnyggYesValue
|
||||
|
||||
object SnyggValueIcon {
|
||||
interface Spec {
|
||||
@@ -121,51 +86,13 @@ internal fun SnyggValueIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
spec: SnyggValueIcon.Spec = SnyggValueIcon.Normal,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
when (value) {
|
||||
is SnyggStaticColorValue -> {
|
||||
is SnyggSolidColorValue -> {
|
||||
SnyggValueColorBox(modifier = modifier, spec = spec, backgroundColor = value.color)
|
||||
}
|
||||
is SnyggDynamicLightColorValue -> {
|
||||
val colorScheme = ColorMappings.dynamicLightColorScheme(context, accentColor)
|
||||
SnyggValueColorBox(modifier = modifier, spec = spec, backgroundColor = colorScheme.getColor(value.colorName))
|
||||
}
|
||||
is SnyggDynamicDarkColorValue -> {
|
||||
val colorScheme = ColorMappings.dynamicDarkColorScheme(context, accentColor)
|
||||
SnyggValueColorBox(modifier = modifier, spec = spec, backgroundColor = colorScheme.getColor(value.colorName))
|
||||
}
|
||||
|
||||
is SnyggGenericFontFamilyValue, is SnyggCustomFontFamilyValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.FontDownload,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is SnyggFontStyleValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.FormatItalic,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is SnyggFontWeightValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.FormatBold,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
is SnyggPaddingValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.Padding,
|
||||
contentDescription = null,
|
||||
)
|
||||
is SnyggMaterialYouValue -> {
|
||||
SnyggValueColorBox(modifier = modifier, spec = spec, backgroundColor = value.loadColor(LocalContext.current))
|
||||
}
|
||||
|
||||
is SnyggShapeValue -> {
|
||||
@@ -175,7 +102,6 @@ internal fun SnyggValueIcon(
|
||||
.border(spec.borderWith, MaterialTheme.colorScheme.onBackground, value.alwaysPercentShape())
|
||||
)
|
||||
}
|
||||
|
||||
is SnyggDpSizeValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
@@ -190,37 +116,6 @@ internal fun SnyggValueIcon(
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
is SnyggTextAlignValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = when (value.textAlign) {
|
||||
TextAlign.Left, TextAlign.Start -> Icons.AutoMirrored.Default.FormatAlignLeft
|
||||
TextAlign.Right, TextAlign.End -> Icons.AutoMirrored.Default.FormatAlignRight
|
||||
TextAlign.Justify -> Icons.Default.FormatAlignJustify
|
||||
else -> Icons.Default.FormatAlignCenter
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is SnyggTextDecorationLineValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = when (value.textDecoration) {
|
||||
TextDecoration.LineThrough -> Icons.Default.FormatStrikethrough
|
||||
else -> Icons.Default.FormatUnderlined
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is SnyggTextOverflowValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.AutoMirrored.Default.WrapText,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
is SnyggDefinedVarValue -> {
|
||||
val realValue = definedVariables[value.key]
|
||||
if (realValue == null) {
|
||||
@@ -255,37 +150,6 @@ internal fun SnyggValueIcon(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is SnyggUriValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.AttachFile,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is SnyggContentScaleValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.OpenInFull,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
is SnyggYesValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.FormatBold,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
is SnyggNoValue -> {
|
||||
Icon(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
imageVector = Icons.Default.CheckBoxOutlineBlank,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Render nothing
|
||||
}
|
||||
|
||||
@@ -36,16 +36,11 @@ import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material.icons.filled.Tune
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
@@ -58,8 +53,11 @@ import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -73,18 +71,17 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.apptheme.Shapes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.ime.theme.extPreviewTheme
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.PreviewKeyboardField
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.rememberPreviewFieldController
|
||||
@@ -96,46 +93,26 @@ import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.kotlin.io.readJson
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
import org.florisboard.lib.snygg.SnyggJsonConfiguration
|
||||
import org.florisboard.lib.snygg.SnyggMultiplePropertySetsEditor
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggPropertySetEditor
|
||||
import org.florisboard.lib.snygg.SnyggPropertySetSpec
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.SnyggSinglePropertySetEditor
|
||||
import org.florisboard.lib.snygg.SnyggSpec
|
||||
import org.florisboard.lib.snygg.SnyggSpecDecl
|
||||
import org.florisboard.lib.snygg.SnyggStylesheet
|
||||
import org.florisboard.lib.snygg.SnyggStylesheetEditor
|
||||
import org.florisboard.lib.snygg.ui.Saver
|
||||
import kotlin.Boolean
|
||||
import kotlin.String
|
||||
import org.florisboard.lib.snygg.SnyggStylesheetJsonConfig
|
||||
import org.florisboard.lib.snygg.definedVariablesRule
|
||||
import org.florisboard.lib.snygg.isDefinedVariablesRule
|
||||
|
||||
internal val PrettyPrintConfig = SnyggJsonConfiguration.of(
|
||||
prettyPrint = true,
|
||||
prettyPrintIndent = " ",
|
||||
internal val IntListSaver = Saver<SnapshotStateList<Int>, ArrayList<Int>>(
|
||||
save = { ArrayList(it) },
|
||||
restore = { it.toMutableStateList() },
|
||||
)
|
||||
|
||||
private val LenientConfig = SnyggJsonConfiguration.of(
|
||||
ignoreMissingSchema = true,
|
||||
ignoreInvalidSchema = true,
|
||||
ignoreUnsupportedSchema = true,
|
||||
ignoreInvalidRules = true,
|
||||
ignoreInvalidProperties = true,
|
||||
ignoreInvalidValues = true,
|
||||
)
|
||||
|
||||
private enum class StylesheetLoadingStrategy {
|
||||
TRY_LOAD_OR_ASK_ON_CONFLICT, // default state
|
||||
TRY_LOAD_OR_EMPTY, // user chose to not auto-fix errors
|
||||
TRY_LOAD_OR_PARSE_LENIENT; // user chose to auto-fix errors
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun ThemeEditorScreen(
|
||||
@@ -153,66 +130,35 @@ fun ThemeEditorScreen(
|
||||
val scope = rememberCoroutineScope()
|
||||
val previewFieldController = rememberPreviewFieldController().also { it.isVisible = true }
|
||||
|
||||
var stylesheetLoadingStrategy by rememberSaveable {
|
||||
mutableStateOf(StylesheetLoadingStrategy.TRY_LOAD_OR_ASK_ON_CONFLICT)
|
||||
}
|
||||
var stylesheetEditorFailure by remember { mutableStateOf<Throwable?>(null) }
|
||||
val stylesheetEditor = remember(stylesheetLoadingStrategy) {
|
||||
val stylesheetEditor = remember {
|
||||
editor.stylesheetEditor ?: run {
|
||||
stylesheetEditorFailure = null
|
||||
val stylesheetPath = editor.stylesheetPath()
|
||||
editor.stylesheetPathOnLoad = stylesheetPath
|
||||
val stylesheetFile = workspace.extDir.subFile(stylesheetPath)
|
||||
val stylesheetEditor = if (stylesheetFile.exists()) {
|
||||
try {
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
val config = when (stylesheetLoadingStrategy) {
|
||||
StylesheetLoadingStrategy.TRY_LOAD_OR_PARSE_LENIENT -> LenientConfig
|
||||
else -> PrettyPrintConfig
|
||||
}
|
||||
SnyggStylesheet.fromJson(stylesheetJson, config).getOrThrow().edit(CustomRuleComparator)
|
||||
} catch (error: Throwable) {
|
||||
stylesheetEditorFailure = when (stylesheetLoadingStrategy) {
|
||||
StylesheetLoadingStrategy.TRY_LOAD_OR_ASK_ON_CONFLICT -> error
|
||||
else -> null
|
||||
}
|
||||
SnyggStylesheetEditor(SnyggStylesheet.SCHEMA_V2, comparator = CustomRuleComparator)
|
||||
stylesheetFile.readJson<SnyggStylesheet>(SnyggStylesheetJsonConfig).edit()
|
||||
} catch (e: Throwable) {
|
||||
SnyggStylesheetEditor()
|
||||
}
|
||||
} else {
|
||||
SnyggStylesheetEditor(SnyggStylesheet.SCHEMA_V2, comparator = CustomRuleComparator)
|
||||
SnyggStylesheetEditor()
|
||||
}
|
||||
if (stylesheetEditor.rules.none { (rule, _) -> rule.isDefinedVariablesRule() }) {
|
||||
stylesheetEditor.rules[SnyggRule.definedVariablesRule()] = SnyggPropertySetEditor()
|
||||
}
|
||||
stylesheetEditor.rules.putIfAbsent(SnyggAnnotationRule.Defines, SnyggSinglePropertySetEditor())
|
||||
stylesheetEditor
|
||||
}.also { editor.stylesheetEditor = it }
|
||||
}
|
||||
|
||||
val definedVariables = remember(stylesheetEditor.rules, workspace.version) {
|
||||
stylesheetEditor.rules.firstNotNullOfOrNull { (rule, propertySet) ->
|
||||
if (rule is SnyggAnnotationRule.Defines && propertySet is SnyggSinglePropertySetEditor) {
|
||||
propertySet.properties
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} ?: emptyMap()
|
||||
}
|
||||
|
||||
val fontNames = remember(stylesheetEditor.rules, workspace.version) {
|
||||
stylesheetEditor.rules.mapNotNull { (rule, _) ->
|
||||
if (rule is SnyggAnnotationRule.Font) {
|
||||
rule.fontName
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val snyggLevel by prefs.theme.editorLevel.observeAsState()
|
||||
val colorRepresentation by prefs.theme.editorColorRepresentation.observeAsState()
|
||||
val displayColorsAs by prefs.theme.editorDisplayColorsAs.observeAsState()
|
||||
val displayKbdAfterDialogs by prefs.theme.editorDisplayKbdAfterDialogs.observeAsState()
|
||||
var oldFocusState by remember { mutableStateOf(false) }
|
||||
var snyggRuleToEdit by rememberSaveable(stateSaver = SnyggRule.Saver) { mutableStateOf(null) }
|
||||
var snyggPropertyToEdit by remember { mutableStateOf<PropertyInfo?>(null) }
|
||||
var snyggPropertySetForEditing = remember<SnyggSinglePropertySetEditor?> { null }
|
||||
var snyggPropertySetForEditing = remember<SnyggPropertySetEditor?> { null }
|
||||
var snyggPropertySetSpecForEditing = remember<SnyggPropertySetSpec?> { null }
|
||||
var showEditComponentMetaDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showFineTuneDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@@ -252,33 +198,6 @@ fun ThemeEditorScreen(
|
||||
}
|
||||
|
||||
content {
|
||||
stylesheetEditorFailure?.let { failure ->
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.settings__theme_editor__stylesheet_error_title),
|
||||
confirmLabel = stringRes(R.string.action__yes),
|
||||
onConfirm = {
|
||||
editor.stylesheetEditor = null
|
||||
stylesheetLoadingStrategy = StylesheetLoadingStrategy.TRY_LOAD_OR_PARSE_LENIENT
|
||||
},
|
||||
dismissLabel = stringRes(R.string.action__no),
|
||||
onDismiss = {
|
||||
editor.stylesheetEditor = null
|
||||
stylesheetLoadingStrategy = StylesheetLoadingStrategy.TRY_LOAD_OR_EMPTY
|
||||
},
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 8.dp),
|
||||
text = failure.message.toString(),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__stylesheet_error_description),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
handleBackPress()
|
||||
}
|
||||
@@ -310,16 +229,23 @@ fun ThemeEditorScreen(
|
||||
|
||||
DisposableEffect(workspace.version) {
|
||||
themeManager.previewThemeInfo = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
name = extPreviewTheme(System.currentTimeMillis().toString()),
|
||||
config = editor.build(),
|
||||
stylesheet = stylesheetEditor.build(),
|
||||
loadedDir = workspace.extDir,
|
||||
stylesheet = stylesheetEditor.build().compileToFullyQualified(FlorisImeUiSpec),
|
||||
)
|
||||
onDispose {
|
||||
themeManager.previewThemeInfo = null
|
||||
}
|
||||
}
|
||||
|
||||
val definedVariables = remember(stylesheetEditor.rules) {
|
||||
stylesheetEditor.rules.firstNotNullOfOrNull { (rule, propertySet) ->
|
||||
if (rule.isDefinedVariablesRule()) {
|
||||
propertySet.properties
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} ?: emptyMap()
|
||||
}
|
||||
|
||||
// TODO: (priority = low)
|
||||
// Floris scrollbar does not like lazy lists with non-constant item heights.
|
||||
// Consider building a custom scrollbar tailored for this list specifically.
|
||||
@@ -337,7 +263,7 @@ fun ThemeEditorScreen(
|
||||
onEditBtnClick = { showEditComponentMetaDialog = true },
|
||||
)
|
||||
if (stylesheetEditor.rules.isEmpty() ||
|
||||
(stylesheetEditor.rules.size == 1 && stylesheetEditor.rules.all { (rule, _) -> rule == SnyggAnnotationRule.Defines })
|
||||
(stylesheetEditor.rules.size == 1 && stylesheetEditor.rules.keys.all { it.isDefinedVariablesRule() })
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
@@ -348,9 +274,9 @@ fun ThemeEditorScreen(
|
||||
}
|
||||
}
|
||||
|
||||
items(stylesheetEditor.rules.toList()) { (rule, propertySet) -> key(rule) {
|
||||
val propertySetSpec = SnyggSpec.propertySetSpecOf(rule)
|
||||
val isVariablesRule = rule == SnyggAnnotationRule.Defines
|
||||
items(stylesheetEditor.rules.entries.toList()) { (rule, propertySet) -> key(rule) {
|
||||
val isVariablesRule = rule.isDefinedVariablesRule()
|
||||
val propertySetSpec = FlorisImeUiSpec.propertySetSpec(rule.element)
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||
@@ -365,129 +291,33 @@ fun ThemeEditorScreen(
|
||||
snyggRuleToEdit = rule
|
||||
},
|
||||
onAddPropertyBtnClick = {
|
||||
when(propertySet) {
|
||||
is SnyggMultiplePropertySetsEditor -> {
|
||||
workspace.update {
|
||||
propertySet.sets.add(SnyggSinglePropertySetEditor())
|
||||
}
|
||||
}
|
||||
is SnyggSinglePropertySetEditor -> {
|
||||
snyggPropertySetForEditing = propertySet
|
||||
snyggPropertyToEdit = SnyggEmptyPropertyInfoForAdding.copy(
|
||||
rule = rule,
|
||||
)
|
||||
}
|
||||
}
|
||||
snyggPropertySetForEditing = propertySet
|
||||
snyggPropertySetSpecForEditing = propertySetSpec
|
||||
snyggPropertyToEdit = SnyggEmptyPropertyInfoForAdding
|
||||
},
|
||||
)
|
||||
if (isVariablesRule) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 8.dp, start = 16.dp, end = 16.dp),
|
||||
text = stringRes(R.string.snygg__rule_annotation__defines_description),
|
||||
text = stringRes(R.string.snygg__rule_element__defines_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SinglePropertySetEditor(
|
||||
propertySet: SnyggSinglePropertySetEditor,
|
||||
) {
|
||||
for ((propertyName, propertySpec) in propertySetSpec?.properties.orEmpty()) {
|
||||
if (propertySpec.required && !propertySet.properties.containsKey(propertyName)) {
|
||||
FlorisOutlinedBox(title = "Errors", modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp),
|
||||
text = "Required property '$propertyName' does not exist",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
for ((propertyName, propertyValue) in propertySet.properties) {
|
||||
if (true /*propertySpec != null && propertySpec.level <= snyggLevel*/ || isVariablesRule) {
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.rippleClickable {
|
||||
snyggPropertySetForEditing = propertySet
|
||||
snyggPropertyToEdit = PropertyInfo(rule, propertyName, propertyValue)
|
||||
},
|
||||
text = context.translatePropertyName(propertyName, snyggLevel),
|
||||
secondaryText = context.translatePropertyValue(propertyValue, snyggLevel, colorRepresentation),
|
||||
singleLineSecondaryText = true,
|
||||
trailing = { SnyggValueIcon(propertyValue, definedVariables) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (propertySet) {
|
||||
is SnyggSinglePropertySetEditor -> {
|
||||
SinglePropertySetEditor(propertySet)
|
||||
}
|
||||
is SnyggMultiplePropertySetsEditor -> {
|
||||
val sets = propertySet.sets
|
||||
sets.forEachIndexed { propertySetIndex, propertySet ->
|
||||
key(propertySet.uuid) {
|
||||
FlorisOutlinedBox(Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)) {
|
||||
Row {
|
||||
Text("Source set", Modifier
|
||||
.padding(start = 16.dp)
|
||||
.align(Alignment.CenterVertically))
|
||||
Spacer(Modifier.weight(1f))
|
||||
FlorisIconButton(
|
||||
onClick = {
|
||||
workspace.update {
|
||||
if (propertySetIndex > 0) {
|
||||
val set = sets.removeAt(propertySetIndex)
|
||||
sets.add(propertySetIndex - 1, set)
|
||||
}
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.KeyboardArrowUp,
|
||||
iconColor = MaterialTheme.colorScheme.primary,
|
||||
iconModifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
enabled = propertySetIndex > 0,
|
||||
)
|
||||
FlorisIconButton(
|
||||
onClick = {
|
||||
workspace.update {
|
||||
if (propertySetIndex + 1 < sets.size) {
|
||||
val set = sets.removeAt(propertySetIndex)
|
||||
sets.add(propertySetIndex + 1, set)
|
||||
}
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.KeyboardArrowDown,
|
||||
iconColor = MaterialTheme.colorScheme.primary,
|
||||
iconModifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
enabled = propertySetIndex + 1 < sets.size,
|
||||
)
|
||||
FlorisIconButton(
|
||||
onClick = {
|
||||
workspace.update {
|
||||
sets.removeAt(propertySetIndex)
|
||||
}
|
||||
},
|
||||
icon = Icons.Default.Delete,
|
||||
iconColor = MaterialTheme.colorScheme.primary,
|
||||
iconModifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
FlorisIconButton(
|
||||
onClick = {
|
||||
snyggPropertySetForEditing = propertySet
|
||||
snyggPropertyToEdit = SnyggEmptyPropertyInfoForAdding.copy(
|
||||
rule = rule,
|
||||
)
|
||||
},
|
||||
icon = Icons.Default.Add,
|
||||
iconColor = MaterialTheme.colorScheme.primary,
|
||||
iconModifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
}
|
||||
SinglePropertySetEditor(propertySet)
|
||||
}
|
||||
}
|
||||
}
|
||||
for ((propertyName, propertyValue) in propertySet.properties) {
|
||||
val propertySpec = propertySetSpec?.propertySpec(propertyName)
|
||||
if (propertySpec != null && propertySpec.level <= snyggLevel || isVariablesRule) {
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.rippleClickable {
|
||||
snyggPropertySetForEditing = propertySet
|
||||
snyggPropertySetSpecForEditing = propertySetSpec
|
||||
snyggPropertyToEdit = PropertyInfo(propertyName, propertyValue)
|
||||
},
|
||||
text = translatePropertyName(propertyName, snyggLevel),
|
||||
secondaryText = translatePropertyValue(propertyValue, snyggLevel, displayColorsAs),
|
||||
singleLineSecondaryText = true,
|
||||
trailing = { SnyggValueIcon(propertyValue, definedVariables) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -539,14 +369,7 @@ fun ThemeEditorScreen(
|
||||
true
|
||||
}
|
||||
oldRule == SnyggEmptyRuleForAdding -> {
|
||||
when (SnyggSpec.propertySetSpecOf(newRule)!!.type) {
|
||||
SnyggSpecDecl.PropertySet.Type.SINGLE_SET -> {
|
||||
rules[newRule] = SnyggSinglePropertySetEditor()
|
||||
}
|
||||
SnyggSpecDecl.PropertySet.Type.MULTIPLE_SETS -> {
|
||||
rules[newRule] = SnyggMultiplePropertySetsEditor()
|
||||
}
|
||||
}
|
||||
rules[newRule] = SnyggPropertySetEditor()
|
||||
snyggRuleToEdit = null
|
||||
scope.launch {
|
||||
lazyListState.animateScrollToItem(index = rules.keys.indexOf(newRule))
|
||||
@@ -573,12 +396,11 @@ fun ThemeEditorScreen(
|
||||
val propertyToEdit = snyggPropertyToEdit
|
||||
if (propertyToEdit != null) {
|
||||
EditPropertyDialog(
|
||||
propertySetSpec = snyggPropertySetSpecForEditing,
|
||||
initProperty = propertyToEdit,
|
||||
level = snyggLevel,
|
||||
colorRepresentation = colorRepresentation,
|
||||
displayColorsAs = displayColorsAs,
|
||||
definedVariables = definedVariables,
|
||||
fontNames = fontNames,
|
||||
workspace = workspace,
|
||||
onConfirmNewValue = { name, value ->
|
||||
val properties = snyggPropertySetForEditing?.properties ?: return@EditPropertyDialog false
|
||||
if (propertyToEdit == SnyggEmptyPropertyInfoForAdding && properties.containsKey(name)) {
|
||||
@@ -619,6 +441,8 @@ private fun ComponentMetaEditorDialog(
|
||||
var authors by rememberSaveable { mutableStateOf(editor.authors.joinToString("\n")) }
|
||||
val authorsValidation = rememberValidationResult(ExtensionValidation.ComponentAuthors, authors)
|
||||
var isNightTheme by rememberSaveable { mutableStateOf(editor.isNightTheme) }
|
||||
var isBorderless by rememberSaveable { mutableStateOf(editor.isBorderless) }
|
||||
val isMaterialYouAware by rememberSaveable { mutableStateOf(editor.isMaterialYouAware) }
|
||||
var stylesheetPath by rememberSaveable { mutableStateOf(editor.stylesheetPath) }
|
||||
val stylesheetPathValidation = rememberValidationResult(ExtensionValidation.ThemeComponentStylesheetPath, stylesheetPath)
|
||||
|
||||
@@ -640,6 +464,8 @@ private fun ComponentMetaEditorDialog(
|
||||
editor.label = label.trim()
|
||||
editor.authors = authors.lines().map { it.trim() }.filter { it.isNotBlank() }
|
||||
editor.isNightTheme = isNightTheme
|
||||
editor.isBorderless = isBorderless
|
||||
editor.isMaterialYouAware = isMaterialYouAware
|
||||
editor.stylesheetPath = stylesheetPath.trim()
|
||||
}
|
||||
onConfirm()
|
||||
@@ -651,28 +477,31 @@ private fun ComponentMetaEditorDialog(
|
||||
) {
|
||||
Column {
|
||||
DialogProperty(text = stringRes(R.string.ext__meta__id)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = id,
|
||||
onValueChange = { id = it },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
|
||||
singleLine = true,
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = idValidation,
|
||||
)
|
||||
Validation(showValidationErrors, idValidation)
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.ext__meta__label)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = label,
|
||||
onValueChange = { label = it },
|
||||
singleLine = true,
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = labelValidation,
|
||||
)
|
||||
Validation(showValidationErrors, labelValidation)
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.ext__meta__authors)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = authors,
|
||||
onValueChange = { authors = it },
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = authorsValidation,
|
||||
)
|
||||
Validation(showValidationErrors, authorsValidation)
|
||||
}
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.toggleable(isNightTheme) { isNightTheme = it },
|
||||
@@ -680,21 +509,28 @@ private fun ComponentMetaEditorDialog(
|
||||
trailing = {
|
||||
Switch(checked = isNightTheme, onCheckedChange = null)
|
||||
},
|
||||
colors = ListItemDefaults.colors(containerColor = AlertDialogDefaults.containerColor)
|
||||
)
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.toggleable(isBorderless) { isBorderless = it },
|
||||
text = stringRes(R.string.settings__theme_editor__component_meta_is_borderless),
|
||||
trailing = {
|
||||
Switch(checked = isBorderless, onCheckedChange = null)
|
||||
},
|
||||
)
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__component_meta_stylesheet_path)) {
|
||||
JetPrefTextField(
|
||||
FlorisOutlinedTextField(
|
||||
value = stylesheetPath,
|
||||
onValueChange = { stylesheetPath = it },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
|
||||
singleLine = true,
|
||||
placeholderText = if (stylesheetPath.isEmpty()) {
|
||||
placeholder = if (stylesheetPath.isEmpty()) {
|
||||
ThemeExtensionComponent.defaultStylesheetPath(id.trim())
|
||||
} else {
|
||||
null
|
||||
},
|
||||
showValidationError = showValidationErrors,
|
||||
validationResult = stylesheetPathValidation,
|
||||
)
|
||||
Validation(showValidationErrors, stylesheetPathValidation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -708,8 +544,6 @@ private fun SnyggRuleRow(
|
||||
onEditRuleBtnClick: () -> Unit,
|
||||
onAddPropertyBtnClick: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
@Composable
|
||||
fun Selector(text: String) {
|
||||
Text(
|
||||
@@ -731,6 +565,7 @@ private fun SnyggRuleRow(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.56f),
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
@@ -745,53 +580,38 @@ private fun SnyggRuleRow(
|
||||
.weight(1f)
|
||||
.padding(vertical = 8.dp, horizontal = 10.dp),
|
||||
) {
|
||||
if (rule is SnyggElementRule) {
|
||||
Text(
|
||||
text = context.translateElementName(rule, level),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
if (rule.selector == SnyggSelector.PRESSED) {
|
||||
Selector(
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.PRESSED.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (rule.selector == SnyggSelector.FOCUS) {
|
||||
Selector(
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.FOCUS.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (rule.selector == SnyggSelector.HOVER) {
|
||||
Selector(
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.HOVER.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__hover)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (rule.selector == SnyggSelector.DISABLED) {
|
||||
Selector(
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.DISABLED.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__disabled)
|
||||
}
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = translateElementName(rule, level),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
if (rule.pressedSelector) {
|
||||
Selector(text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.PRESSED_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
})
|
||||
}
|
||||
for ((attrKey, attrValue) in rule.attributes) {
|
||||
AttributesList(text = attrKey, list = attrValue.toString())
|
||||
if (rule.focusSelector) {
|
||||
Selector(text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.FOCUS_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Text(text = rule.toString())
|
||||
if (rule.disabledSelector) {
|
||||
Selector(text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.DISABLED_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__disabled)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (rule.codes.isNotEmpty()) {
|
||||
AttributesList(text = "code", list = remember(rule.codes) { rule.codes.toString() })
|
||||
}
|
||||
if (rule.shiftStates.isNotEmpty()) {
|
||||
AttributesList(text = "shiftstate", list = remember(rule.shiftStates) { rule.shiftStates.toString() })
|
||||
}
|
||||
}
|
||||
if (showEditBtn) {
|
||||
@@ -825,31 +645,10 @@ internal fun DialogProperty(
|
||||
.weight(1f)
|
||||
.padding(vertical = 8.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
trailingIconTitle()
|
||||
}
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
private object CustomRuleComparator : Comparator<SnyggRule> {
|
||||
@Suppress("IfThenToElvis")
|
||||
override fun compare(a: SnyggRule, b: SnyggRule): Int {
|
||||
return if (a !is SnyggElementRule || b !is SnyggElementRule || a.elementName == b.elementName) {
|
||||
a.compareTo(b)
|
||||
} else {
|
||||
val aOrdinal = FlorisImeUi.elementNamesToOrdinals[a.elementName]
|
||||
val bOrdinal = FlorisImeUi.elementNamesToOrdinals[b.elementName]
|
||||
if (aOrdinal == null && bOrdinal == null) {
|
||||
a.elementName.compareTo(b.elementName)
|
||||
} else if (bOrdinal == null) {
|
||||
-1
|
||||
} else if (aOrdinal == null) {
|
||||
1
|
||||
} else {
|
||||
aOrdinal.compareTo(bOrdinal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,19 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Brightness2
|
||||
import androidx.compose.material.icons.filled.BrightnessAuto
|
||||
import androidx.compose.material.icons.filled.ColorLens
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.WbTwilight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
@@ -36,6 +37,7 @@ import dev.patrickgold.florisboard.app.ext.AddonManagementReferenceBox
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionListScreenType
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
@@ -43,7 +45,6 @@ import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.LocalTimePickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
@@ -65,15 +66,35 @@ fun ThemeScreen() = FlorisScreen {
|
||||
}
|
||||
|
||||
content {
|
||||
val themeMode by prefs.theme.mode.observeAsState()
|
||||
val dayThemeId by prefs.theme.dayThemeId.observeAsState()
|
||||
val nightThemeId by prefs.theme.nightThemeId.observeAsState()
|
||||
|
||||
/*Card(modifier = Modifier.padding(8.dp)) {
|
||||
Column(modifier = Modifier.padding(8.dp)) {
|
||||
Text("If you want to give feedback on the new stylesheet editor and theme engine, please do so in below linked feedback thread:\n")
|
||||
Button(onClick = {
|
||||
context.launchUrl("https://github.com/florisboard/florisboard/discussions/1531")
|
||||
}) {
|
||||
Text("Open Feedback Thread")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
ListPreference(
|
||||
prefs.theme.mode,
|
||||
icon = Icons.Default.BrightnessAuto,
|
||||
title = stringRes(R.string.pref__theme__mode__label),
|
||||
entries = enumDisplayEntriesOf(ThemeMode::class),
|
||||
)
|
||||
if (themeMode == ThemeMode.FOLLOW_TIME) {
|
||||
FlorisInfoCard(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = """
|
||||
The theme mode "Follow time" is not available in this beta release.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
icon = Icons.Default.LightMode,
|
||||
title = stringRes(R.string.pref__theme__day),
|
||||
@@ -92,18 +113,6 @@ fun ThemeScreen() = FlorisScreen {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_NIGHT))
|
||||
},
|
||||
)
|
||||
LocalTimePickerPreference(
|
||||
pref = prefs.theme.sunriseTime,
|
||||
title = stringRes(R.string.pref__theme__sunrise_time__label),
|
||||
icon = Icons.Default.WbTwilight,
|
||||
enabledIf = { prefs.theme.mode isEqualTo ThemeMode.FOLLOW_TIME },
|
||||
)
|
||||
LocalTimePickerPreference(
|
||||
pref = prefs.theme.sunsetTime,
|
||||
title = stringRes(R.string.pref__theme__sunset_time__label),
|
||||
icon = Icons.Default.Brightness2,
|
||||
enabledIf = { prefs.theme.mode isEqualTo ThemeMode.FOLLOW_TIME },
|
||||
)
|
||||
ColorPickerPreference(
|
||||
pref = prefs.theme.accentColor,
|
||||
title = stringRes(R.string.pref__theme__theme_accent_color__label),
|
||||
|
||||
@@ -16,108 +16,134 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.UnicodeCtrlChar
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
import org.florisboard.lib.kotlin.simpleNameOrEnclosing
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.Snygg
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.value.RgbaColor
|
||||
import org.florisboard.lib.snygg.value.SnyggCircleShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCustomFontFamilyValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDynamicDarkColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDynamicLightColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggFontStyleValue
|
||||
import org.florisboard.lib.snygg.value.SnyggFontWeightValue
|
||||
import org.florisboard.lib.snygg.value.SnyggGenericFontFamilyValue
|
||||
import org.florisboard.lib.snygg.value.SnyggInheritValue
|
||||
import org.florisboard.lib.snygg.value.SnyggNoValue
|
||||
import org.florisboard.lib.snygg.value.SnyggContentScaleValue
|
||||
import org.florisboard.lib.snygg.value.SnyggPaddingValue
|
||||
import org.florisboard.lib.snygg.value.SnyggExplicitInheritValue
|
||||
import org.florisboard.lib.snygg.value.SnyggImplicitInheritValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouDarkColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouLightColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggPercentageSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRectangleShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggStaticColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextAlignValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextDecorationLineValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextMaxLinesValue
|
||||
import org.florisboard.lib.snygg.value.SnyggTextOverflowValue
|
||||
import org.florisboard.lib.snygg.value.SnyggUndefinedValue
|
||||
import org.florisboard.lib.snygg.value.SnyggUriValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValueEncoder
|
||||
import org.florisboard.lib.snygg.value.SnyggYesValue
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
internal fun Context.translateElementName(rule: SnyggElementRule, level: SnyggLevel): String {
|
||||
return translateElementName(rule.elementName, level) ?: rule.elementName
|
||||
}
|
||||
|
||||
internal fun Context.translateElementName(element: String, level: SnyggLevel): String? {
|
||||
return when (level) {
|
||||
SnyggLevel.DEVELOPER -> null
|
||||
else -> FlorisImeUi.elementNamesToTranslation[element]?.let { getString(it) }
|
||||
@Composable
|
||||
internal fun translateElementName(rule: SnyggRule, level: SnyggLevel): String {
|
||||
return translateElementName(rule.element, level) ?: remember {
|
||||
buildString {
|
||||
if (rule.isAnnotation) {
|
||||
append(SnyggRule.ANNOTATION_MARKER)
|
||||
}
|
||||
append(rule.element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val PropertyNameMap = mapOf(
|
||||
Snygg.Background to R.string.snygg__property_name__background,
|
||||
Snygg.Foreground to R.string.snygg__property_name__foreground,
|
||||
Snygg.BackgroundImage to R.string.snygg__property_name__background_image,
|
||||
Snygg.ContentScale to R.string.snygg__property_name__content_scale,
|
||||
Snygg.BorderColor to R.string.snygg__property_name__border_color,
|
||||
Snygg.BorderStyle to R.string.snygg__property_name__border_style,
|
||||
Snygg.BorderWidth to R.string.snygg__property_name__border_width,
|
||||
Snygg.FontFamily to R.string.snygg__property_name__font_family,
|
||||
Snygg.FontSize to R.string.snygg__property_name__font_size,
|
||||
Snygg.FontStyle to R.string.snygg__property_name__font_style,
|
||||
Snygg.FontWeight to R.string.snygg__property_name__font_weight,
|
||||
Snygg.LetterSpacing to R.string.snygg__property_name__letter_spacing,
|
||||
Snygg.LineHeight to R.string.snygg__property_name__line_height,
|
||||
Snygg.Margin to R.string.snygg__property_name__margin,
|
||||
Snygg.Padding to R.string.snygg__property_name__padding,
|
||||
Snygg.ShadowColor to R.string.snygg__property_name__shadow_color,
|
||||
Snygg.ShadowElevation to R.string.snygg__property_name__shadow_elevation,
|
||||
Snygg.Shape to R.string.snygg__property_name__shape,
|
||||
Snygg.Clip to R.string.snygg__property_name__clip,
|
||||
Snygg.Src to R.string.snygg__property_name__src,
|
||||
Snygg.TextAlign to R.string.snygg__property_name__text_align,
|
||||
Snygg.TextDecorationLine to R.string.snygg__property_name__text_decoration_line,
|
||||
Snygg.TextMaxLines to R.string.snygg__property_name__text_max_lines,
|
||||
Snygg.TextOverflow to R.string.snygg__property_name__text_overflow,
|
||||
"--primary" to R.string.snygg__property_name__var_primary,
|
||||
"--primary-variant" to R.string.snygg__property_name__var_primary_variant,
|
||||
"--secondary" to R.string.snygg__property_name__var_secondary,
|
||||
"--secondary-variant" to R.string.snygg__property_name__var_secondary_variant,
|
||||
"--background" to R.string.snygg__property_name__var_background,
|
||||
"--surface" to R.string.snygg__property_name__var_surface,
|
||||
"--surface-variant" to R.string.snygg__property_name__var_surface_variant,
|
||||
"--on-primary" to R.string.snygg__property_name__var_on_primary,
|
||||
"--on-secondary" to R.string.snygg__property_name__var_on_secondary,
|
||||
"--on-background" to R.string.snygg__property_name__var_on_background,
|
||||
"--on-surface" to R.string.snygg__property_name__var_on_surface,
|
||||
"--on-surface-variant" to R.string.snygg__property_name__var_on_surface_variant,
|
||||
"--shape" to R.string.snygg__property_name__var_shape,
|
||||
"--shape-variant" to R.string.snygg__property_name__var_shape_variant
|
||||
)
|
||||
|
||||
internal fun Context.translatePropertyName(propertyName: String, level: SnyggLevel): String {
|
||||
@Composable
|
||||
internal fun translateElementName(element: String, level: SnyggLevel): String? {
|
||||
return when (level) {
|
||||
SnyggLevel.DEVELOPER -> null
|
||||
else -> PropertyNameMap[propertyName]
|
||||
else -> when (element) {
|
||||
"defines" -> R.string.snygg__rule_element__defines
|
||||
FlorisImeUi.Keyboard -> R.string.snygg__rule_element__keyboard
|
||||
FlorisImeUi.Key -> R.string.snygg__rule_element__key
|
||||
FlorisImeUi.KeyHint -> R.string.snygg__rule_element__key_hint
|
||||
FlorisImeUi.KeyPopup -> R.string.snygg__rule_element__key_popup
|
||||
FlorisImeUi.ClipboardHeader -> R.string.snygg__rule_element__clipboard_header
|
||||
FlorisImeUi.ClipboardItem -> R.string.snygg__rule_element__clipboard_item
|
||||
FlorisImeUi.ClipboardItemPopup -> R.string.snygg__rule_element__clipboard_item_popup
|
||||
FlorisImeUi.EmojiKey -> R.string.snygg__rule_element__emoji_key
|
||||
FlorisImeUi.EmojiKeyPopup -> R.string.snygg__rule_element__emoji_key_popup
|
||||
FlorisImeUi.EmojiTab -> R.string.snygg__rule_element__emoji_key_tab
|
||||
FlorisImeUi.ExtractedLandscapeInputLayout -> R.string.snygg__rule_element__extracted_landscape_input_layout
|
||||
FlorisImeUi.ExtractedLandscapeInputField -> R.string.snygg__rule_element__extracted_landscape_input_field
|
||||
FlorisImeUi.ExtractedLandscapeInputAction -> R.string.snygg__rule_element__extracted_landscape_input_action
|
||||
FlorisImeUi.GlideTrail -> R.string.snygg__rule_element__glide_trail
|
||||
FlorisImeUi.IncognitoModeIndicator -> R.string.snygg__rule_element__incognito_mode_indicator
|
||||
FlorisImeUi.OneHandedPanel -> R.string.snygg__rule_element__one_handed_panel
|
||||
FlorisImeUi.Smartbar -> R.string.snygg__rule_element__smartbar
|
||||
FlorisImeUi.SmartbarSharedActionsRow -> R.string.snygg__rule_element__smartbar_shared_actions_row
|
||||
FlorisImeUi.SmartbarSharedActionsToggle -> R.string.snygg__rule_element__smartbar_shared_actions_toggle
|
||||
FlorisImeUi.SmartbarExtendedActionsRow -> R.string.snygg__rule_element__smartbar_extended_actions_row
|
||||
FlorisImeUi.SmartbarExtendedActionsToggle -> R.string.snygg__rule_element__smartbar_extended_actions_toggle
|
||||
FlorisImeUi.SmartbarActionKey -> R.string.snygg__rule_element__smartbar_action_key
|
||||
FlorisImeUi.SmartbarActionTile -> R.string.snygg__rule_element__smartbar_action_tile
|
||||
FlorisImeUi.SmartbarActionsOverflow -> R.string.snygg__rule_element__smartbar_actions_overflow
|
||||
FlorisImeUi.SmartbarActionsOverflowCustomizeButton -> R.string.snygg__rule_element__smartbar_actions_overflow_customize_button
|
||||
FlorisImeUi.SmartbarActionsEditor -> R.string.snygg__rule_element__smartbar_actions_editor
|
||||
FlorisImeUi.SmartbarActionsEditorHeader -> R.string.snygg__rule_element__smartbar_actions_editor_header
|
||||
FlorisImeUi.SmartbarActionsEditorSubheader -> R.string.snygg__rule_element__smartbar_actions_editor_subheader
|
||||
FlorisImeUi.SmartbarCandidatesRow -> R.string.snygg__rule_element__smartbar_candidates_row
|
||||
FlorisImeUi.SmartbarCandidateWord -> R.string.snygg__rule_element__smartbar_candidate_word
|
||||
FlorisImeUi.SmartbarCandidateClip -> R.string.snygg__rule_element__smartbar_candidate_clip
|
||||
FlorisImeUi.SmartbarCandidateSpacer -> R.string.snygg__rule_element__smartbar_candidate_spacer
|
||||
FlorisImeUi.SystemNavBar -> R.string.snygg__rule_element__system_nav_bar
|
||||
else -> null
|
||||
}
|
||||
}.let { if (it != null) { stringRes(it) } else { null } }
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun translatePropertyName(propertyName: String, level: SnyggLevel): String {
|
||||
return when (level) {
|
||||
SnyggLevel.DEVELOPER -> null
|
||||
else -> when (propertyName) {
|
||||
Snygg.Width -> R.string.snygg__property_name__width
|
||||
Snygg.Height -> R.string.snygg__property_name__height
|
||||
Snygg.Background -> R.string.snygg__property_name__background
|
||||
Snygg.Foreground -> R.string.snygg__property_name__foreground
|
||||
Snygg.BorderColor -> R.string.snygg__property_name__border_color
|
||||
Snygg.BorderStyle -> R.string.snygg__property_name__border_style
|
||||
Snygg.BorderWidth -> R.string.snygg__property_name__border_width
|
||||
Snygg.FontFamily -> R.string.snygg__property_name__font_family
|
||||
Snygg.FontSize -> R.string.snygg__property_name__font_size
|
||||
Snygg.FontStyle -> R.string.snygg__property_name__font_style
|
||||
Snygg.FontVariant -> R.string.snygg__property_name__font_variant
|
||||
Snygg.FontWeight -> R.string.snygg__property_name__font_weight
|
||||
Snygg.ShadowElevation -> R.string.snygg__property_name__shadow_elevation
|
||||
Snygg.Shape -> R.string.snygg__property_name__shape
|
||||
"--primary" -> R.string.snygg__property_name__var_primary
|
||||
"--primary-variant" -> R.string.snygg__property_name__var_primary_variant
|
||||
"--secondary" -> R.string.snygg__property_name__var_secondary
|
||||
"--secondary-variant" -> R.string.snygg__property_name__var_secondary_variant
|
||||
"--background" -> R.string.snygg__property_name__var_background
|
||||
"--surface" -> R.string.snygg__property_name__var_surface
|
||||
"--surface-variant" -> R.string.snygg__property_name__var_surface_variant
|
||||
"--on-primary" -> R.string.snygg__property_name__var_on_primary
|
||||
"--on-secondary" -> R.string.snygg__property_name__var_on_secondary
|
||||
"--on-background" -> R.string.snygg__property_name__var_on_background
|
||||
"--on-surface" -> R.string.snygg__property_name__var_on_surface
|
||||
"--on-surface-variant" -> R.string.snygg__property_name__var_on_surface_variant
|
||||
"--shape" -> R.string.snygg__property_name__var_shape
|
||||
"--shape-variant" -> R.string.snygg__property_name__var_shape_variant
|
||||
else -> null
|
||||
}
|
||||
}.let { resId ->
|
||||
when {
|
||||
resId != null -> {
|
||||
getString(resId)
|
||||
stringRes(resId)
|
||||
}
|
||||
propertyName.isBlank() -> {
|
||||
getString(R.string.general__select_dropdown_value_placeholder)
|
||||
stringRes(R.string.general__select_dropdown_value_placeholder)
|
||||
}
|
||||
else -> {
|
||||
propertyName
|
||||
@@ -126,14 +152,15 @@ internal fun Context.translatePropertyName(propertyName: String, level: SnyggLev
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Context.translatePropertyValue(
|
||||
@Composable
|
||||
internal fun translatePropertyValue(
|
||||
propertyValue: SnyggValue,
|
||||
level: SnyggLevel,
|
||||
colorRepresentation: ColorRepresentation,
|
||||
displayColorsAs: DisplayColorsAs,
|
||||
): String {
|
||||
return when (propertyValue) {
|
||||
is SnyggStaticColorValue -> {
|
||||
colorRepresentation.formatColor(propertyValue.color, withAlpha = true)
|
||||
is SnyggSolidColorValue -> remember(propertyValue.color, displayColorsAs) {
|
||||
buildColorString(propertyValue.color, displayColorsAs)
|
||||
}
|
||||
else -> when (level) {
|
||||
SnyggLevel.DEVELOPER -> null
|
||||
@@ -149,38 +176,51 @@ internal fun Context.translatePropertyValue(
|
||||
}
|
||||
}
|
||||
|
||||
private val PropertyValueEncoderNameMap = mapOf(
|
||||
SnyggUndefinedValue to R.string.general__select_dropdown_value_placeholder,
|
||||
SnyggInheritValue to R.string.snygg__property_value__explicit_inherit,
|
||||
SnyggDefinedVarValue to R.string.snygg__property_value__defined_var,
|
||||
SnyggYesValue to R.string.snygg__property_value__yes,
|
||||
SnyggNoValue to R.string.snygg__property_value__no,
|
||||
SnyggStaticColorValue to R.string.snygg__property_value__solid_color,
|
||||
SnyggDynamicLightColorValue to R.string.snygg__property_value__material_you_light_color,
|
||||
SnyggDynamicDarkColorValue to R.string.snygg__property_value__material_you_dark_color,
|
||||
SnyggGenericFontFamilyValue to R.string.snygg__property_value__font_family_generic,
|
||||
SnyggCustomFontFamilyValue to R.string.snygg__property_value__font_family_custom,
|
||||
SnyggFontStyleValue to R.string.snygg__property_value__font_style,
|
||||
SnyggFontWeightValue to R.string.snygg__property_value__font_weight,
|
||||
SnyggPaddingValue to R.string.snygg__property_value__padding,
|
||||
SnyggRectangleShapeValue to R.string.snygg__property_value__rectangle_shape,
|
||||
SnyggCircleShapeValue to R.string.snygg__property_value__circle_shape,
|
||||
SnyggCutCornerDpShapeValue to R.string.snygg__property_value__cut_corner_shape_dp,
|
||||
SnyggCutCornerPercentShapeValue to R.string.snygg__property_value__cut_corner_shape_percent,
|
||||
SnyggRoundedCornerDpShapeValue to R.string.snygg__property_value__rounded_corner_shape_dp,
|
||||
SnyggRoundedCornerPercentShapeValue to R.string.snygg__property_value__rounded_corner_shape_percent,
|
||||
SnyggDpSizeValue to R.string.snygg__property_value__dp_size,
|
||||
SnyggSpSizeValue to R.string.snygg__property_value__sp_size,
|
||||
SnyggPercentageSizeValue to R.string.snygg__property_value__percentage_size,
|
||||
SnyggContentScaleValue to R.string.snygg__property_value__content_scale,
|
||||
SnyggTextAlignValue to R.string.snygg__property_value__text_align,
|
||||
SnyggTextDecorationLineValue to R.string.snygg__property_value__text_decoration_line,
|
||||
SnyggTextMaxLinesValue to R.string.snygg__property_value__text_max_lines,
|
||||
SnyggTextOverflowValue to R.string.snygg__property_value__text_overflow,
|
||||
SnyggUriValue to R.string.snygg__property_value__uri,
|
||||
)
|
||||
|
||||
internal fun Context.translatePropertyValueEncoderName(encoder: SnyggValueEncoder): String {
|
||||
return PropertyValueEncoderNameMap[encoder]?.let { getString(it) }
|
||||
?: encoder::class.simpleNameOrEnclosing().orEmpty()
|
||||
internal fun buildColorString(color: Color, displayColorsAs: DisplayColorsAs): String {
|
||||
return when (displayColorsAs) {
|
||||
DisplayColorsAs.HEX8 -> buildString {
|
||||
append(UnicodeCtrlChar.LeftToRightIsolate)
|
||||
append("#")
|
||||
append((color.red * RgbaColor.RedMax).roundToInt().toString(16).padStart(2, '0'))
|
||||
append((color.green * RgbaColor.GreenMax).roundToInt().toString(16).padStart(2, '0'))
|
||||
append((color.blue * RgbaColor.BlueMax).roundToInt().toString(16).padStart(2, '0'))
|
||||
append((color.alpha * 0xFF).roundToInt().toString(16).padStart(2, '0'))
|
||||
append(UnicodeCtrlChar.PopDirectionalIsolate)
|
||||
}
|
||||
DisplayColorsAs.RGBA -> buildString {
|
||||
append(UnicodeCtrlChar.LeftToRightIsolate)
|
||||
append("rgba(")
|
||||
append((color.red * RgbaColor.RedMax).roundToInt())
|
||||
append(",")
|
||||
append((color.green * RgbaColor.GreenMax).roundToInt())
|
||||
append(",")
|
||||
append((color.blue * RgbaColor.BlueMax).roundToInt())
|
||||
append(",")
|
||||
append(color.alpha)
|
||||
append(")")
|
||||
append(UnicodeCtrlChar.PopDirectionalIsolate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun translatePropertyValueEncoderName(encoder: SnyggValueEncoder): String {
|
||||
return when (encoder) {
|
||||
SnyggImplicitInheritValue -> R.string.general__select_dropdown_value_placeholder
|
||||
SnyggExplicitInheritValue -> R.string.snygg__property_value__explicit_inherit
|
||||
SnyggSolidColorValue -> R.string.snygg__property_value__solid_color
|
||||
SnyggMaterialYouLightColorValue -> R.string.snygg__property_value__material_you_light_color
|
||||
SnyggMaterialYouDarkColorValue -> R.string.snygg__property_value__material_you_dark_color
|
||||
SnyggRectangleShapeValue -> R.string.snygg__property_value__rectangle_shape
|
||||
SnyggCircleShapeValue -> R.string.snygg__property_value__circle_shape
|
||||
SnyggCutCornerDpShapeValue -> R.string.snygg__property_value__cut_corner_shape_dp
|
||||
SnyggCutCornerPercentShapeValue -> R.string.snygg__property_value__cut_corner_shape_percent
|
||||
SnyggRoundedCornerDpShapeValue -> R.string.snygg__property_value__rounded_corner_shape_dp
|
||||
SnyggRoundedCornerPercentShapeValue -> R.string.snygg__property_value__rounded_corner_shape_percent
|
||||
SnyggDpSizeValue -> R.string.snygg__property_value__dp_size
|
||||
SnyggSpSizeValue -> R.string.snygg__property_value__sp_size
|
||||
SnyggPercentageSizeValue -> R.string.snygg__property_value__percentage_size
|
||||
SnyggDefinedVarValue -> R.string.snygg__property_value__defined_var
|
||||
else -> null
|
||||
}.let { if (it != null) { stringRes(it) } else { encoder::class.simpleName ?: "" } }.toString()
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
@@ -48,7 +47,6 @@ import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@@ -81,18 +79,28 @@ fun TypingScreen() = FlorisScreen {
|
||||
summary = stringRes(R.string.pref__suggestion__block_possibly_offensive__summary),
|
||||
enabledIf = { prefs.suggestion.enabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.suggestion.clipboardContentEnabled,
|
||||
title = stringRes(R.string.pref__suggestion__clipboard_content_enabled__label),
|
||||
summary = stringRes(R.string.pref__suggestion__clipboard_content_enabled__summary),
|
||||
enabledIf = { prefs.suggestion.enabled isEqualTo true },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.suggestion.clipboardContentTimeout,
|
||||
title = stringRes(R.string.pref__suggestion__clipboard_content_timeout__label),
|
||||
valueLabel = { stringRes(R.string.pref__suggestion__clipboard_content_timeout__summary, "v" to it) },
|
||||
min = 30,
|
||||
max = 300,
|
||||
stepIncrement = 5,
|
||||
enabledIf = { prefs.suggestion.enabled isEqualTo true },
|
||||
visibleIf = { prefs.suggestion.clipboardContentEnabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.suggestion.api30InlineSuggestionsEnabled,
|
||||
title = stringRes(R.string.pref__suggestion__api30_inline_suggestions_enabled__label),
|
||||
summary = stringRes(R.string.pref__suggestion__api30_inline_suggestions_enabled__summary),
|
||||
visibleIf = { AndroidVersion.ATLEAST_API30_R },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.suggestion.incognitoMode,
|
||||
icon = vectorResource(id = R.drawable.ic_incognito),
|
||||
title = stringRes(R.string.pref__suggestion__incognito_mode__label),
|
||||
entries = enumDisplayEntriesOf(IncognitoMode::class),
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.pref__correction__title)) {
|
||||
|
||||
@@ -22,57 +22,45 @@ import android.media.MediaMetadataRetriever
|
||||
import android.media.ThumbnailUtils
|
||||
import android.provider.MediaStore
|
||||
import android.util.Size
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.Backspace
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.DeleteSweep
|
||||
import androidx.compose.material.icons.filled.FilterList
|
||||
import androidx.compose.material.icons.filled.FilterListOff
|
||||
import androidx.compose.material.icons.filled.Image
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.Movie
|
||||
import androidx.compose.material.icons.filled.Phone
|
||||
import androidx.compose.material.icons.filled.TextFields
|
||||
import androidx.compose.material.icons.filled.ClearAll
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.ToggleOff
|
||||
import androidx.compose.material.icons.filled.ToggleOn
|
||||
import androidx.compose.material.icons.filled.Videocam
|
||||
import androidx.compose.material.icons.outlined.ContentPaste
|
||||
import androidx.compose.material.icons.outlined.Email
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableStateSetOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -80,11 +68,17 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
@@ -95,43 +89,42 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.media.KeyboardLikeButton
|
||||
import dev.patrickgold.florisboard.ime.smartbar.AnimationDuration
|
||||
import dev.patrickgold.florisboard.ime.smartbar.VerticalEnterTransition
|
||||
import dev.patrickgold.florisboard.ime.smartbar.VerticalExitTransition
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.LocalLocalizedDateTimeFormatter
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButtonWithInnerPadding
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisStaggeredVerticalGrid
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
import dev.patrickgold.florisboard.lib.compose.autoMirrorForRtl
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.NetworkUtils
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.coroutines.delay
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggChip
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import java.time.Instant
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggClip
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
private val ContentPadding = PaddingValues(horizontal = 4.dp)
|
||||
private val ItemMargin = PaddingValues(all = 6.dp)
|
||||
private val ItemPadding = PaddingValues(vertical = 8.dp, horizontal = 12.dp)
|
||||
private val DescriptionPadding = PaddingValues(top = 4.dp, start = 12.dp, end = 12.dp)
|
||||
private val ItemWidth = 200.dp
|
||||
private val DialogWidth = 240.dp
|
||||
|
||||
const val CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO: Int = 0
|
||||
|
||||
@Composable
|
||||
fun ClipboardInputLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -144,126 +137,90 @@ fun ClipboardInputLayout(
|
||||
|
||||
val deviceLocked = androidKeyguardManager.let { it.isDeviceLocked || it.isKeyguardLocked }
|
||||
val historyEnabled by prefs.clipboard.historyEnabled.observeAsState()
|
||||
val unfilteredHistory by clipboardManager.history.observeAsNonNullState()
|
||||
val history by clipboardManager.history.observeAsNonNullState()
|
||||
|
||||
var isFilterRowShown by remember { mutableStateOf(false) }
|
||||
val activeFilterTypes = remember { mutableStateSetOf<ItemType>() }
|
||||
|
||||
val history = remember(unfilteredHistory, activeFilterTypes.toSet()) {
|
||||
if (activeFilterTypes.isEmpty()) {
|
||||
unfilteredHistory
|
||||
} else {
|
||||
unfilteredHistory.all
|
||||
.filter { activeFilterTypes.contains(it.type) }
|
||||
.let { ClipboardManager.ClipboardHistory(it) }
|
||||
}
|
||||
}
|
||||
|
||||
val gridState = rememberLazyStaggeredGridState()
|
||||
val innerHeight = FlorisImeSizing.imeUiHeight() - FlorisImeSizing.smartbarHeight
|
||||
var popupItem by remember(history) { mutableStateOf<ClipboardItem?>(null) }
|
||||
var showClearAllHistory by remember { mutableStateOf(false) }
|
||||
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardHeader)
|
||||
val itemStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardItem)
|
||||
val popupStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardItemPopup)
|
||||
val enableHistoryButtonStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardEnableHistoryButton)
|
||||
|
||||
fun isPopupSurfaceActive() = popupItem != null || showClearAllHistory
|
||||
|
||||
LaunchedEffect(isFilterRowShown) {
|
||||
delay(AnimationDuration.toLong())
|
||||
if (!isFilterRowShown) {
|
||||
activeFilterTypes.clear()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(activeFilterTypes.toSet()) {
|
||||
gridState.scrollToItem(0)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HeaderRow() {
|
||||
SnyggRow(FlorisImeUi.ClipboardHeader.elementName,
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
.height(FlorisImeSizing.smartbarHeight)
|
||||
.snyggBackground(context, headerStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val sizeModifier = Modifier
|
||||
.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight)
|
||||
.aspectRatio(1f)
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
FlorisIconButtonWithInnerPadding(
|
||||
onClick = { keyboardManager.activeState.imeUiMode = ImeUiMode.TEXT },
|
||||
modifier = sizeModifier,
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
)
|
||||
}
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardHeaderText.elementName,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringRes(R.string.clipboard__header_title),
|
||||
color = headerStyle.foreground.solidColor(context),
|
||||
fontSize = headerStyle.fontSize.spSize(),
|
||||
)
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
FlorisIconButtonWithInnerPadding(
|
||||
onClick = { prefs.clipboard.historyEnabled.set(!historyEnabled) },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
modifier = Modifier.autoMirrorForRtl(),
|
||||
icon = if (historyEnabled) {
|
||||
Icons.Default.ToggleOn
|
||||
} else {
|
||||
Icons.Default.ToggleOff
|
||||
},
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
enabled = !deviceLocked && !isPopupSurfaceActive(),
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = if (historyEnabled) {
|
||||
Icons.Default.ToggleOn
|
||||
} else {
|
||||
Icons.Default.ToggleOff
|
||||
},
|
||||
)
|
||||
}
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
)
|
||||
FlorisIconButtonWithInnerPadding(
|
||||
onClick = { showClearAllHistory = true },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
enabled = !deviceLocked && historyEnabled && unfilteredHistory.all.isNotEmpty() && !isPopupSurfaceActive(),
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = Icons.Default.DeleteSweep,
|
||||
)
|
||||
}
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = { isFilterRowShown = !isFilterRowShown },
|
||||
modifier = sizeModifier,
|
||||
enabled = !deviceLocked && historyEnabled && unfilteredHistory.all.isNotEmpty() && !isPopupSurfaceActive(),
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = if (!isFilterRowShown) {
|
||||
Icons.Default.FilterList
|
||||
} else {
|
||||
Icons.Default.FilterListOff
|
||||
},
|
||||
)
|
||||
}
|
||||
modifier = Modifier.autoMirrorForRtl(),
|
||||
icon = Icons.Default.ClearAll,
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
enabled = !deviceLocked && historyEnabled && history.all.isNotEmpty() && !isPopupSurfaceActive(),
|
||||
)
|
||||
FlorisIconButtonWithInnerPadding(
|
||||
onClick = {
|
||||
context.showShortToast("TODO: implement inline clip item editing")
|
||||
},
|
||||
icon = Icons.Default.Edit,
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
enabled = !deviceLocked && historyEnabled && !isPopupSurfaceActive(),
|
||||
)
|
||||
KeyboardLikeButton(
|
||||
modifier = sizeModifier,
|
||||
inputEventDispatcher = keyboardManager.inputEventDispatcher,
|
||||
keyData = TextKeyData.DELETE,
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
element = FlorisImeUi.ClipboardHeader,
|
||||
) {
|
||||
SnyggIcon(imageVector = Icons.AutoMirrored.Outlined.Backspace)
|
||||
Icon(Icons.AutoMirrored.Outlined.Backspace, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ClipItemView(
|
||||
elementName: String,
|
||||
item: ClipboardItem,
|
||||
style: SnyggPropertySet,
|
||||
contentScrollInsteadOfClip: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val attributes = remember(item) {
|
||||
mapOf("type" to item.type.toString().lowercase())
|
||||
}
|
||||
SnyggBox(
|
||||
elementName = elementName,
|
||||
attributes = attributes,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
val fontSize = style.fontSize.spSize() safeTimes 1f
|
||||
SnyggSurface(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(ItemMargin),
|
||||
style = style,
|
||||
clip = true,
|
||||
clickAndSemanticsModifier = Modifier.combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(),
|
||||
@@ -295,9 +252,14 @@ fun ClipboardInputLayout(
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
} else {
|
||||
SnyggText(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(ItemPadding),
|
||||
text = bitmap.exceptionOrNull()?.message ?: "Unknown error",
|
||||
style = TextStyle(textDirection = TextDirection.Ltr),
|
||||
color = Color.Red,
|
||||
fontSize = fontSize,
|
||||
)
|
||||
}
|
||||
} else if (item.type == ItemType.VIDEO) {
|
||||
@@ -337,24 +299,31 @@ fun ClipboardInputLayout(
|
||||
tint = Color.Black,
|
||||
)
|
||||
} else {
|
||||
SnyggText(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(ItemPadding),
|
||||
text = bitmap.exceptionOrNull()?.message ?: "Unknown error",
|
||||
style = TextStyle(textDirection = TextDirection.Ltr),
|
||||
color = Color.Red,
|
||||
fontSize = fontSize,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val text = item.stringRepresentation()
|
||||
Column {
|
||||
ClipTextItemDescription(
|
||||
elementName = FlorisImeUi.ClipboardItemDescription.elementName,
|
||||
attributes = attributes,
|
||||
text = text,
|
||||
)
|
||||
SnyggText(
|
||||
ClipTextItemDescription(text, style)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.run { if (contentScrollInsteadOfClip) this.florisVerticalScroll() else this },
|
||||
.run { if (contentScrollInsteadOfClip) this.florisVerticalScroll() else this }
|
||||
.padding(ItemPadding),
|
||||
text = item.displayText(),
|
||||
style = TextStyle(textDirection = TextDirection.ContentOrLtr),
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = style.fontSize.spSize(),
|
||||
maxLines = if (contentScrollInsteadOfClip) Int.MAX_VALUE else 5,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -363,235 +332,159 @@ fun ClipboardInputLayout(
|
||||
|
||||
@Composable
|
||||
fun HistoryMainView() {
|
||||
SnyggBox(FlorisImeUi.ClipboardContent.elementName,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(innerHeight),
|
||||
) {
|
||||
val historyAlpha by animateFloatAsState(targetValue = if (isPopupSurfaceActive()) 0.12f else 1f)
|
||||
val staggeredGridCells by prefs.clipboard.numHistoryGridColumns()
|
||||
.observeAsTransformingState { numGridColumns ->
|
||||
if (numGridColumns == CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO) {
|
||||
StaggeredGridCells.Adaptive(160.dp)
|
||||
} else {
|
||||
StaggeredGridCells.Fixed(numGridColumns)
|
||||
}
|
||||
}
|
||||
|
||||
fun LazyStaggeredGridScope.clipboardItems(
|
||||
items: List<ClipboardItem>,
|
||||
key: String,
|
||||
@StringRes title: Int,
|
||||
) {
|
||||
if (items.isNotEmpty()) {
|
||||
item(key, span = StaggeredGridItemSpan.FullLine) {
|
||||
ClipCategoryTitle(text = stringRes(title))
|
||||
}
|
||||
items(items) { item ->
|
||||
ClipItemView(
|
||||
elementName = FlorisImeUi.ClipboardItem.elementName,
|
||||
item = item,
|
||||
contentScrollInsteadOfClip = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.alpha(historyAlpha),
|
||||
.padding(ContentPadding)
|
||||
.fillMaxSize()
|
||||
.alpha(historyAlpha)
|
||||
.florisVerticalScroll(),
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = isFilterRowShown,
|
||||
enter = VerticalEnterTransition,
|
||||
exit = VerticalExitTransition,
|
||||
) {
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.ClipboardFilterRow.elementName,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
clickAndSemanticsModifier = Modifier.florisHorizontalScroll(),
|
||||
) {
|
||||
@Composable
|
||||
fun FilterChip(
|
||||
imageVector: ImageVector,
|
||||
text: String,
|
||||
itemType: ItemType,
|
||||
) {
|
||||
val active = activeFilterTypes.contains(itemType)
|
||||
val attributes = remember(active) {
|
||||
mapOf(
|
||||
"state" to if (active) "active" else "inactive",
|
||||
"type" to itemType.toString().lowercase(),
|
||||
)
|
||||
}
|
||||
SnyggChip(
|
||||
elementName = FlorisImeUi.ClipboardFilterChip.elementName,
|
||||
attributes = attributes,
|
||||
onClick = {
|
||||
if (!activeFilterTypes.add(itemType)) {
|
||||
activeFilterTypes.remove(itemType)
|
||||
}
|
||||
},
|
||||
imageVector = imageVector,
|
||||
text = text,
|
||||
)
|
||||
if (history.pinned.isNotEmpty()) {
|
||||
ClipCategoryTitle(
|
||||
text = stringRes(R.string.clipboard__group_pinned),
|
||||
style = itemStyle,
|
||||
)
|
||||
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
|
||||
for (item in history.pinned) {
|
||||
ClipItemView(item, itemStyle, contentScrollInsteadOfClip = false)
|
||||
}
|
||||
|
||||
FilterChip(
|
||||
imageVector = Icons.Default.TextFields,
|
||||
text = "Text",
|
||||
itemType = ItemType.TEXT,
|
||||
)
|
||||
FilterChip(
|
||||
imageVector = Icons.Default.Image,
|
||||
text = "Images",
|
||||
itemType = ItemType.IMAGE,
|
||||
)
|
||||
FilterChip(
|
||||
imageVector = Icons.Default.Movie,
|
||||
text = "Videos",
|
||||
itemType = ItemType.VIDEO,
|
||||
)
|
||||
}
|
||||
}
|
||||
SnyggBox(FlorisImeUi.ClipboardGrid.elementName,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
LazyVerticalStaggeredGrid(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = gridState,
|
||||
columns = staggeredGridCells,
|
||||
) {
|
||||
clipboardItems(
|
||||
items = history.pinned,
|
||||
key = "pinned-header",
|
||||
title = R.string.clipboard__group_pinned,
|
||||
)
|
||||
clipboardItems(
|
||||
items = history.recent,
|
||||
key = "recent-header",
|
||||
title = R.string.clipboard__group_recent,
|
||||
)
|
||||
clipboardItems(
|
||||
items = history.other,
|
||||
key = "other-header",
|
||||
title = R.string.clipboard__group_other,
|
||||
)
|
||||
if (history.recent.isNotEmpty()) {
|
||||
ClipCategoryTitle(
|
||||
text = stringRes(R.string.clipboard__group_recent),
|
||||
style = itemStyle,
|
||||
)
|
||||
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
|
||||
for (item in history.recent) {
|
||||
ClipItemView(item, itemStyle, contentScrollInsteadOfClip = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (history.other.isNotEmpty()) {
|
||||
ClipCategoryTitle(
|
||||
text = stringRes(R.string.clipboard__group_other),
|
||||
style = itemStyle,
|
||||
)
|
||||
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
|
||||
for (item in history.other) {
|
||||
ClipItemView(item, itemStyle, contentScrollInsteadOfClip = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (popupItem != null) {
|
||||
SnyggRow(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(ContentPadding)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures { popupItem = null }
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
SnyggColumn(modifier = Modifier.weight(0.5f)) {
|
||||
ClipItemView(
|
||||
elementName = FlorisImeUi.ClipboardItemPopup.elementName,
|
||||
modifier = Modifier.widthIn(max = ItemWidth),
|
||||
item = popupItem!!,
|
||||
contentScrollInsteadOfClip = true,
|
||||
)
|
||||
SnyggBox(FlorisImeUi.ClipboardItemTimestamp.elementName) {
|
||||
val formatter = LocalLocalizedDateTimeFormatter.current
|
||||
SnyggText(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = formatter.format(Instant.ofEpochMilli(popupItem!!.creationTimestampMs)),
|
||||
)
|
||||
ClipItemView(
|
||||
modifier = Modifier.widthIn(max = ItemWidth),
|
||||
item = popupItem!!,
|
||||
style = itemStyle,
|
||||
contentScrollInsteadOfClip = true,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(ItemMargin)
|
||||
.snyggShadow(popupStyle)
|
||||
.snyggBorder(context, popupStyle)
|
||||
.snyggBackground(context, popupStyle)
|
||||
.snyggClip(popupStyle),
|
||||
) {
|
||||
PopupAction(
|
||||
iconId = R.drawable.ic_pin,
|
||||
text = stringRes(if (popupItem!!.isPinned) {
|
||||
R.string.clip__unpin_item
|
||||
} else {
|
||||
R.string.clip__pin_item
|
||||
}),
|
||||
style = popupStyle,
|
||||
) {
|
||||
if (popupItem!!.isPinned) {
|
||||
clipboardManager.unpinClip(popupItem!!)
|
||||
} else {
|
||||
clipboardManager.pinClip(popupItem!!)
|
||||
}
|
||||
popupItem = null
|
||||
}
|
||||
}
|
||||
SnyggColumn(modifier = Modifier.weight(0.5f)) {
|
||||
SnyggColumn(FlorisImeUi.ClipboardItemActions.elementName) {
|
||||
PopupAction(
|
||||
icon = Icons.Outlined.PushPin,
|
||||
text = stringRes(if (popupItem!!.isPinned) {
|
||||
R.string.clip__unpin_item
|
||||
} else {
|
||||
R.string.clip__pin_item
|
||||
}),
|
||||
) {
|
||||
if (popupItem!!.isPinned) {
|
||||
clipboardManager.unpinClip(popupItem!!)
|
||||
} else {
|
||||
clipboardManager.pinClip(popupItem!!)
|
||||
}
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringRes(R.string.clip__delete_item),
|
||||
) {
|
||||
clipboardManager.deleteClip(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
icon = Icons.Outlined.ContentPaste,
|
||||
text = stringRes(R.string.clip__paste_item),
|
||||
) {
|
||||
clipboardManager.pasteItem(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
iconId = R.drawable.ic_delete,
|
||||
text = stringRes(R.string.clip__delete_item),
|
||||
style = popupStyle,
|
||||
) {
|
||||
clipboardManager.deleteClip(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
iconId = R.drawable.ic_content_paste,
|
||||
text = stringRes(R.string.clip__paste_item),
|
||||
style = popupStyle,
|
||||
) {
|
||||
clipboardManager.pasteItem(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showClearAllHistory) {
|
||||
SnyggRow(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(ContentPadding)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures { showClearAllHistory = false }
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
SnyggColumn(
|
||||
elementName = FlorisImeUi.ClipboardClearAllDialog.elementName,
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(DialogWidth)
|
||||
.snyggShadow(popupStyle)
|
||||
.snyggBorder(context, popupStyle)
|
||||
.snyggBackground(context, popupStyle)
|
||||
.snyggClip(popupStyle)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures { /* Do nothing */ }
|
||||
},
|
||||
) {
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardClearAllDialogMessage.elementName,
|
||||
Text(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
text = stringRes(R.string.clipboard__confirm_clear_history__message),
|
||||
color = popupStyle.foreground.solidColor(context),
|
||||
)
|
||||
SnyggRow(FlorisImeUi.ClipboardClearAllDialogButtons.elementName) {
|
||||
Row(modifier = Modifier.padding(horizontal = 8.dp)) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
SnyggButton(
|
||||
elementName = FlorisImeUi.ClipboardClearAllDialogButton.elementName,
|
||||
attributes = mapOf("action" to "no"),
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
showClearAllHistory = false
|
||||
},
|
||||
) {
|
||||
SnyggText(
|
||||
text = stringRes(R.string.action__no),
|
||||
)
|
||||
}
|
||||
SnyggButton(
|
||||
elementName = FlorisImeUi.ClipboardClearAllDialogButton.elementName,
|
||||
attributes = mapOf("action" to "yes"),
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
text = stringRes(R.string.action__no),
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = popupStyle.foreground.solidColor(context)),
|
||||
)
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
clipboardManager.clearHistory()
|
||||
context.showShortToast(R.string.clipboard__cleared_history)
|
||||
showClearAllHistory = false
|
||||
isFilterRowShown = false
|
||||
},
|
||||
) {
|
||||
SnyggText(
|
||||
text = stringRes(R.string.action__yes),
|
||||
)
|
||||
}
|
||||
text = stringRes(R.string.action__yes),
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = popupStyle.foreground.solidColor(context)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -601,62 +494,98 @@ fun ClipboardInputLayout(
|
||||
|
||||
@Composable
|
||||
fun HistoryEmptyView() {
|
||||
SnyggColumn(FlorisImeUi.ClipboardContent.elementName,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(innerHeight)
|
||||
.padding(ContentPadding),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SnyggText(
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 8.dp),
|
||||
text = stringRes(R.string.clipboard__empty__title),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1.1f,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
SnyggText(
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__empty__message),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1f,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HistoryDisabledView() {
|
||||
SnyggColumn(FlorisImeUi.ClipboardContent.elementName,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(innerHeight)
|
||||
.padding(ContentPadding),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardHistoryDisabledTitle.elementName,
|
||||
modifier = Modifier.padding(bottom = 8.dp),
|
||||
text = stringRes(R.string.clipboard__disabled__title),
|
||||
)
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardHistoryDisabledMessage.elementName,
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
)
|
||||
SnyggButton(FlorisImeUi.ClipboardHistoryDisabledButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
SnyggSurface(
|
||||
modifier = Modifier
|
||||
.padding(ItemMargin)
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
style = itemStyle,
|
||||
contentPadding = ItemPadding,
|
||||
) {
|
||||
SnyggText(
|
||||
text = stringRes(R.string.clipboard__disabled__enable_button),
|
||||
)
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 8.dp),
|
||||
text = stringRes(R.string.clipboard__disabled__title),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1.1f,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1f,
|
||||
)
|
||||
SnyggButton(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.align(Alignment.End),
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
style = enableHistoryButtonStyle,
|
||||
text = stringRes(R.string.clipboard__disabled__enable_button)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HistoryLockedView() {
|
||||
SnyggColumn(FlorisImeUi.ClipboardContent.elementName,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(innerHeight)
|
||||
.padding(ContentPadding),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardHistoryLockedTitle.elementName,
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 8.dp),
|
||||
text = stringRes(R.string.clipboard__locked__title),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1.1f,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardHistoryLockedMessage.elementName,
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__locked__message),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1f,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SnyggColumn(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.imeUiHeight()),
|
||||
@@ -666,7 +595,7 @@ fun ClipboardInputLayout(
|
||||
HistoryLockedView()
|
||||
} else {
|
||||
if (historyEnabled) {
|
||||
if (history.all.isNotEmpty() || !activeFilterTypes.isEmpty()) {
|
||||
if (history.all.isNotEmpty()) {
|
||||
HistoryMainView()
|
||||
} else {
|
||||
HistoryEmptyView()
|
||||
@@ -681,54 +610,71 @@ fun ClipboardInputLayout(
|
||||
@Composable
|
||||
private fun ClipCategoryTitle(
|
||||
text: String,
|
||||
style: SnyggPropertySet,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SnyggText(FlorisImeUi.ClipboardSubheader.elementName,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
val context = LocalContext.current
|
||||
Text(
|
||||
modifier = modifier
|
||||
.padding(ItemMargin)
|
||||
.padding(top = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
text = text.uppercase(),
|
||||
color = style.foreground.solidColor(context),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = style.fontSize.spSize() safeTimes 0.8f,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ClipTextItemDescription(
|
||||
elementName: String,
|
||||
attributes: SnyggQueryAttributes,
|
||||
text: String,
|
||||
style: SnyggPropertySet,
|
||||
modifier: Modifier = Modifier,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
val icon: ImageVector?
|
||||
val context = LocalContext.current
|
||||
val iconId: Int?
|
||||
val description: String?
|
||||
when {
|
||||
NetworkUtils.isEmailAddress(text) -> {
|
||||
icon = Icons.Outlined.Email
|
||||
iconId = R.drawable.ic_email
|
||||
description = stringRes(R.string.clipboard__item_description_email)
|
||||
}
|
||||
NetworkUtils.isUrl(text) -> {
|
||||
icon = Icons.Default.Link
|
||||
iconId = R.drawable.ic_link
|
||||
description = stringRes(R.string.clipboard__item_description_url)
|
||||
}
|
||||
NetworkUtils.isPhoneNumber(text) -> {
|
||||
icon = Icons.Default.Phone
|
||||
iconId = R.drawable.ic_phone
|
||||
description = stringRes(R.string.clipboard__item_description_phone)
|
||||
}
|
||||
else -> {
|
||||
icon = null
|
||||
iconId = null
|
||||
description = null
|
||||
}
|
||||
}
|
||||
if (icon != null && description != null) {
|
||||
SnyggRow(
|
||||
elementName = elementName,
|
||||
attributes = attributes,
|
||||
modifier = modifier,
|
||||
if (iconId != null && description != null) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.padding(DescriptionPadding)
|
||||
.offset(y = DescriptionPadding.calculateTopPadding()),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = icon,
|
||||
val fontSize = style.fontSize.spSize()
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.requiredSize(fontSize.toDp()),
|
||||
painter = painterResource(id = iconId),
|
||||
contentDescription = null,
|
||||
tint = style.foreground.solidColor(context),
|
||||
)
|
||||
SnyggText(
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = description,
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = fontSize safeTimes 0.8f,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -736,21 +682,31 @@ private fun ClipTextItemDescription(
|
||||
|
||||
@Composable
|
||||
private fun PopupAction(
|
||||
icon: ImageVector,
|
||||
@DrawableRes iconId: Int,
|
||||
text: String,
|
||||
style: SnyggPropertySet,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
SnyggRow(FlorisImeUi.ClipboardItemAction.elementName,
|
||||
modifier = modifier.rippleClickable(onClick = onClick),
|
||||
val context = LocalContext.current
|
||||
Row(
|
||||
modifier = modifier
|
||||
.width(ItemWidth)
|
||||
.rippleClickable(onClick = onClick)
|
||||
.padding(all = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggIcon(FlorisImeUi.ClipboardItemActionIcon.elementName,
|
||||
imageVector = icon,
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
painter = painterResource(iconId),
|
||||
contentDescription = null,
|
||||
tint = style.foreground.solidColor(context),
|
||||
)
|
||||
SnyggText(FlorisImeUi.ClipboardItemActionText.elementName,
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = text,
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = style.fontSize.spSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package dev.patrickgold.florisboard.ime.clipboard
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.activity.ComponentActivity
|
||||
@@ -43,6 +43,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.isUnspecified
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -53,19 +54,16 @@ import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.mimeTypeFilterOf
|
||||
|
||||
class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
private var error: CopyToClipboardError? = null
|
||||
private var bitmap: Bitmap? = null
|
||||
private val clipboardManager by lazy { systemService(AndroidClipboardManager::class) }
|
||||
private val filter = mimeTypeFilterOf("image/*")
|
||||
|
||||
internal enum class CopyToClipboardError {
|
||||
UNKNOWN_ERROR,
|
||||
ANDROID_VERSION_TO_OLD_ERROR,
|
||||
TYPE_NOT_SUPPORTED_ERROR;
|
||||
|
||||
@Composable
|
||||
@@ -73,100 +71,89 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
val textId = when (this) {
|
||||
UNKNOWN_ERROR -> R.string.send_to_clipboard__unknown_error
|
||||
TYPE_NOT_SUPPORTED_ERROR -> R.string.send_to_clipboard__type_not_supported_error
|
||||
ANDROID_VERSION_TO_OLD_ERROR -> R.string.send_to_clipboard__android_version_to_old_error
|
||||
}
|
||||
return stringRes(id = textId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
setContent {
|
||||
Content()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
finish()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent) {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val systemClipboardManager = this.systemService(AndroidClipboardManager::class)
|
||||
val type = intent.type
|
||||
val action = intent.action
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
|
||||
if (Intent.ACTION_SEND != action || type == null) {
|
||||
error = CopyToClipboardError.UNKNOWN_ERROR
|
||||
return
|
||||
}
|
||||
if (!filter.matches(type) || !intent.hasExtra(Intent.EXTRA_STREAM)) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
return
|
||||
}
|
||||
|
||||
val uri: Uri? =
|
||||
if (AndroidVersion.ATLEAST_API33_T) {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
return
|
||||
}
|
||||
bitmap = uriToBitmap(uri)
|
||||
}
|
||||
|
||||
private fun uriToBitmap(uri: Uri): Bitmap {
|
||||
val clip = ClipData.newUri(contentResolver, "image", uri)
|
||||
clipboardManager.setPrimaryClip(clip)
|
||||
return if (AndroidVersion.ATLEAST_API28_P) {
|
||||
val source = ImageDecoder.createSource(contentResolver, uri)
|
||||
ImageDecoder.decodeBitmap(source)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
MediaStore.Images.Media.getBitmap(contentResolver, uri)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content() {
|
||||
val prefs by florisPreferenceModel()
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.other.settingsTheme.observeAsState()
|
||||
FlorisAppTheme(theme) {
|
||||
BottomSheet {
|
||||
Row {
|
||||
Text(
|
||||
text = error?.showError()
|
||||
?: bitmap?.let { stringRes(id = R.string.send_to_clipboard__description__copied_image_to_clipboard) }
|
||||
?: stringRes(R.string.send_to_clipboard__unknown_error),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (type.startsWith("image/")) {
|
||||
val hasExtraStream = intent.hasExtra(Intent.EXTRA_STREAM)
|
||||
if (!hasExtraStream) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
} else {
|
||||
// pasting images via virtual keyboard only available since Android 7.1 (API 25)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
|
||||
error = CopyToClipboardError.ANDROID_VERSION_TO_OLD_ERROR
|
||||
} else {
|
||||
val uri: Uri? =
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
} else {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
}
|
||||
val clip = ClipData.newUri(contentResolver, "image", uri)
|
||||
systemClipboardManager.setPrimaryClip(clip)
|
||||
bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, uri)
|
||||
}
|
||||
bitmap?.let {
|
||||
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 64.dp, end = 64.dp, top = 32.dp, bottom = 8.dp),
|
||||
bitmap = bitmap!!.asImageBitmap(),
|
||||
contentDescription = null
|
||||
}
|
||||
} else {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
setContent {
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.advanced.settingsTheme.observeAsState()
|
||||
val accentColor by prefs.advanced.accentColor.observeAsState()
|
||||
FlorisAppTheme(theme, accentColor.isUnspecified) {
|
||||
BottomSheet {
|
||||
Row {
|
||||
Text(
|
||||
text = error?.showError()
|
||||
?: bitmap?.let { stringRes(id = R.string.send_to_clipboard__description__copied_image_to_clipboard) }
|
||||
?: stringRes(R.string.send_to_clipboard__unknown_error),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
bitmap?.let {
|
||||
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 64.dp, end = 64.dp, top = 32.dp, bottom = 8.dp),
|
||||
bitmap = bitmap!!.asImageBitmap(),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun BottomSheet(
|
||||
internal fun BottomSheet(
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
@@ -180,7 +167,9 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
.align(Alignment.End)
|
||||
.padding(16.dp),
|
||||
onClick = { finish() },
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
//containerColor = buttonContainer.background.solidColor(context = context),
|
||||
)
|
||||
) {
|
||||
Text(text = stringRes(id = R.string.action__ok))
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ data class ClipboardItem @OptIn(ExperimentalSerializationApi::class) constructor
|
||||
|
||||
fun stringRepresentation(): String {
|
||||
return when {
|
||||
text != null -> text
|
||||
text != null -> text.take(500)
|
||||
uri != null -> "(Image) $uri"
|
||||
else -> "#ERROR"
|
||||
}
|
||||
|
||||
@@ -17,31 +17,43 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.RadioButtonChecked
|
||||
import androidx.compose.material.icons.filled.RadioButtonUnchecked
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardState
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggListItem
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggClip
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
@Composable
|
||||
fun SelectSubtypePanel(modifier: Modifier = Modifier) {
|
||||
@@ -54,21 +66,37 @@ fun SelectSubtypePanel(modifier: Modifier = Modifier) {
|
||||
|
||||
val currentlySelected = subtypeManager.activeSubtype.id
|
||||
|
||||
SnyggColumn(FlorisImeUi.SubtypePanel.elementName, modifier = modifier.safeDrawingPadding()) {
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.SubtypePanelHeader.elementName,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
val panelStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditor)
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorHeader)
|
||||
val subheaderStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorSubheader)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.snyggBackground(context, panelStyle, fallbackColor = FlorisImeTheme.fallbackSurfaceColor())
|
||||
.snyggClip(panelStyle),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.snyggBackground(context, headerStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggText(
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.clickable(false) {},
|
||||
text = stringRes(R.string.select_subtype_panel__header),
|
||||
color = headerStyle.foreground.solidColor(
|
||||
context,
|
||||
default = FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
fontSize = headerStyle.fontSize.spSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
|
||||
SnyggBox(FlorisImeUi.SubtypePanelList.elementName) {
|
||||
Box {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
) {
|
||||
@@ -78,24 +106,37 @@ fun SelectSubtypePanel(modifier: Modifier = Modifier) {
|
||||
it.id
|
||||
}
|
||||
) {
|
||||
SnyggListItem(
|
||||
elementName = FlorisImeUi.SubtypePanelListItem.elementName,
|
||||
onClick = {
|
||||
subtypeManager.switchToSubtypeById(it.id)
|
||||
keyboardManager.activeState.isSubtypeSelectionVisible = false
|
||||
},
|
||||
leadingImageVector = when {
|
||||
currentlySelected == it.id -> Icons.Default.RadioButtonChecked
|
||||
else -> Icons.Default.RadioButtonUnchecked
|
||||
JetPrefListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.rippleClickable {
|
||||
subtypeManager.switchToSubtypeById(it.id)
|
||||
keyboardManager.activeState.isSubtypeSelectionVisible = false
|
||||
},
|
||||
icon = {
|
||||
if (currentlySelected == it.id) {
|
||||
Icon(Icons.Default.RadioButtonChecked, null)
|
||||
} else {
|
||||
Icon(Icons.Default.RadioButtonUnchecked, null)
|
||||
}
|
||||
},
|
||||
text = it.primaryLocale.displayName(),
|
||||
colors = ListItemDefaults.colors(
|
||||
leadingIconColor = subheaderStyle.foreground.solidColor(context),
|
||||
headlineColor = subheaderStyle.foreground.solidColor(context),
|
||||
containerColor = subheaderStyle.background.solidColor(context),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier
|
||||
.systemBarsPadding()
|
||||
.snyggBackground(context, panelStyle))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun KeyboardState.isSubtypeSelectionShowing(): Boolean {
|
||||
return isSubtypeSelectionVisible
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ abstract class AbstractEditorInstance(context: Context) {
|
||||
abstract fun determineComposer(composerName: ExtensionComponentName): Composer
|
||||
|
||||
protected open fun shouldDetermineComposingRegion(editorInfo: FlorisEditorInfo): Boolean {
|
||||
return editorInfo.isRichInputEditor && !editorInfo.inputAttributes.flagTextNoSuggestions
|
||||
return editorInfo.isRichInputEditor
|
||||
}
|
||||
|
||||
private suspend fun determineLocalComposing(
|
||||
|
||||
@@ -19,6 +19,7 @@ package dev.patrickgold.florisboard.ime.editor
|
||||
import android.content.ClipDescription
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.KeyEvent
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||
import androidx.core.view.inputmethod.InputContentInfoCompat
|
||||
@@ -29,20 +30,21 @@ import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputShiftState
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.text.composing.Appender
|
||||
import dev.patrickgold.florisboard.ime.text.composing.Composer
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
companion object {
|
||||
@@ -117,11 +119,11 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
//!instance.inputAttributes.flagTextAutoComplete &&
|
||||
//!instance.inputAttributes.flagTextNoSuggestions
|
||||
}
|
||||
activeState.isIncognitoMode = when (prefs.suggestion.incognitoMode.get()) {
|
||||
activeState.isIncognitoMode = when (prefs.advanced.incognitoMode.get()) {
|
||||
IncognitoMode.FORCE_OFF -> false
|
||||
IncognitoMode.FORCE_ON -> true
|
||||
IncognitoMode.DYNAMIC_ON_OFF -> {
|
||||
editorInfo.imeOptions.flagNoPersonalizedLearning || prefs.suggestion.forceIncognitoModeFromDynamic.get()
|
||||
editorInfo.imeOptions.flagNoPersonalizedLearning || prefs.advanced.forceIncognitoModeFromDynamic.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +324,16 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
)
|
||||
val ic = currentInputConnection() ?: return false
|
||||
ic.finishComposingText()
|
||||
val flags = InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
|
||||
var flags = 0
|
||||
if (AndroidVersion.ATLEAST_API25_N_MR1) {
|
||||
flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
|
||||
} else {
|
||||
appContext.grantUriPermission(
|
||||
activeInfo.packageName,
|
||||
item.uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION,
|
||||
)
|
||||
}
|
||||
InputConnectionCompat.commitContent(ic, activeInfo.base, inputContentInfo, flags, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,5 @@ enum class InputShiftState(val value: Int) {
|
||||
fun fromInt(int: Int) = entries.firstOrNull { it.value == int } ?: UNSHIFTED
|
||||
}
|
||||
|
||||
override fun toString() = name.lowercase()
|
||||
|
||||
fun toInt() = value
|
||||
}
|
||||
|
||||
@@ -206,8 +206,7 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
|
||||
Icons.Default.DeleteSweep
|
||||
}
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT,
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT,
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> {
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> {
|
||||
context()?.vectorResource(id = R.drawable.ic_accessibility_one_handed)
|
||||
}
|
||||
KeyCode.VOICE_INPUT -> {
|
||||
|
||||
@@ -119,7 +119,7 @@ fun ProvideKeyboardRowBaseHeight(content: @Composable () -> Unit) {
|
||||
|
||||
val heightFactorPortrait by prefs.keyboard.heightFactorPortrait.observeAsTransformingState { it.toFloat() / 100f }
|
||||
val heightFactorLandscape by prefs.keyboard.heightFactorLandscape.observeAsTransformingState { it.toFloat() / 100f }
|
||||
val oneHandedMode by prefs.keyboard.oneHandedModeEnabled.observeAsState()
|
||||
val oneHandedMode by prefs.keyboard.oneHandedMode.observeAsState()
|
||||
val oneHandedModeScaleFactor by prefs.keyboard.oneHandedModeScaleFactor.observeAsTransformingState { it.toFloat() / 100f }
|
||||
|
||||
// Only set systemBarHeights on api 35 or later because https://developer.android.com/about/versions/15/behavior-changes-15#stable-configuration
|
||||
@@ -134,7 +134,7 @@ fun ProvideKeyboardRowBaseHeight(content: @Composable () -> Unit) {
|
||||
) {
|
||||
calcInputViewHeight(resources, systemBarHeights) * when {
|
||||
configuration.isOrientationLandscape() -> heightFactorLandscape
|
||||
else -> heightFactorPortrait * (if (oneHandedMode) oneHandedModeScaleFactor else 1f)
|
||||
else -> heightFactorPortrait * (if (oneHandedMode != OneHandedMode.OFF) oneHandedModeScaleFactor else 1f)
|
||||
}
|
||||
}
|
||||
val smartbarHeight = baseRowHeight * 0.753f
|
||||
|
||||
@@ -63,7 +63,6 @@ import dev.patrickgold.florisboard.lib.uppercase
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
@@ -78,7 +77,7 @@ import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
private val DoubleSpacePeriodMatcher = """([^.!?‽\s]\s)""".toRegex()
|
||||
|
||||
@@ -101,7 +100,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private var lastToastReference = WeakReference<Toast>(null)
|
||||
|
||||
private val activeEvaluatorGuard = Mutex(locked = false)
|
||||
private var activeEvaluatorVersion = AtomicInteger(0)
|
||||
private var activeEvaluatorVersion: Int = 1
|
||||
private val _activeEvaluator = MutableStateFlow<ComputingEvaluator>(DefaultComputingEvaluator)
|
||||
val activeEvaluator get() = _activeEvaluator.asStateFlow()
|
||||
private val _activeSmartbarEvaluator = MutableStateFlow<ComputingEvaluator>(DefaultComputingEvaluator)
|
||||
@@ -192,7 +191,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
).await()
|
||||
}
|
||||
val computingEvaluator = ComputingEvaluatorImpl(
|
||||
version = activeEvaluatorVersion.getAndAdd(1),
|
||||
version = activeEvaluatorVersion++,
|
||||
keyboard = computedKeyboard,
|
||||
editorInfo = editorInfo,
|
||||
state = state,
|
||||
@@ -237,8 +236,11 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
return subtypeManager.subtypes.size > 1
|
||||
}
|
||||
|
||||
fun toggleOneHandedMode() {
|
||||
prefs.keyboard.oneHandedModeEnabled.set(!prefs.keyboard.oneHandedModeEnabled.get())
|
||||
fun toggleOneHandedMode(isRight: Boolean) {
|
||||
prefs.keyboard.oneHandedMode.set(when (prefs.keyboard.oneHandedMode.get()) {
|
||||
OneHandedMode.OFF -> if (isRight) { OneHandedMode.END } else { OneHandedMode.START }
|
||||
else -> OneHandedMode.OFF
|
||||
})
|
||||
}
|
||||
|
||||
fun executeSwipeAction(swipeAction: SwipeAction) {
|
||||
@@ -581,7 +583,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
* Handles a [KeyCode.TOGGLE_INCOGNITO_MODE] event.
|
||||
*/
|
||||
private fun handleToggleIncognitoMode() {
|
||||
prefs.suggestion.forceIncognitoModeFromDynamic.set(!prefs.suggestion.forceIncognitoModeFromDynamic.get())
|
||||
prefs.advanced.forceIncognitoModeFromDynamic.set(!prefs.advanced.forceIncognitoModeFromDynamic.get())
|
||||
val newState = !activeState.isIncognitoMode
|
||||
activeState.isIncognitoMode = newState
|
||||
lastToastReference.get()?.cancel()
|
||||
@@ -718,15 +720,8 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
clipboardManager.updatePrimaryClip(null)
|
||||
appContext.showShortToast(R.string.clipboard__cleared_primary_clip)
|
||||
}
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> toggleOneHandedMode()
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.START)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.END)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> toggleOneHandedMode(isRight = false)
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> toggleOneHandedMode(isRight = true)
|
||||
KeyCode.DELETE -> handleDelete()
|
||||
KeyCode.DELETE_WORD -> handleDeleteWord()
|
||||
KeyCode.ENTER -> handleEnter()
|
||||
@@ -970,13 +965,10 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
KeyCode.CLIPBOARD_SELECT_ALL -> {
|
||||
editorInfo.isRichInputEditor
|
||||
}
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> when (prefs.suggestion.incognitoMode.get()) {
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> when (prefs.advanced.incognitoMode.get()) {
|
||||
IncognitoMode.FORCE_OFF, IncognitoMode.FORCE_ON -> false
|
||||
IncognitoMode.DYNAMIC_ON_OFF -> !editorInfo.imeOptions.flagNoPersonalizedLearning
|
||||
}
|
||||
KeyCode.LANGUAGE_SWITCH -> {
|
||||
subtypeManager.subtypes.size > 1
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
@@ -1024,7 +1016,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
|
||||
fun asSmartbarQuickActionsEvaluator(): ComputingEvaluatorImpl {
|
||||
return ComputingEvaluatorImpl(
|
||||
version = version,
|
||||
version = activeEvaluatorVersion,
|
||||
keyboard = SmartbarQuickActionsKeyboard,
|
||||
editorInfo = editorInfo,
|
||||
state = state,
|
||||
|
||||
@@ -19,7 +19,6 @@ package dev.patrickgold.florisboard.ime.keyboard
|
||||
enum class KeyboardMode(val value: Int) {
|
||||
UNSPECIFIED(-1),
|
||||
CHARACTERS(0),
|
||||
@Deprecated(message = "TODO: remove")
|
||||
EDITING(1),
|
||||
SYMBOLS(2),
|
||||
SYMBOLS2(3),
|
||||
@@ -27,9 +26,7 @@ enum class KeyboardMode(val value: Int) {
|
||||
NUMERIC_ADVANCED(5),
|
||||
PHONE(6),
|
||||
PHONE2(7),
|
||||
@Deprecated(message = "TODO: remove")
|
||||
SMARTBAR_CLIPBOARD_CURSOR_ROW(8),
|
||||
@Deprecated(message = "TODO: remove")
|
||||
SMARTBAR_NUMBER_ROW(9),
|
||||
SMARTBAR_QUICK_ACTIONS(10);
|
||||
|
||||
@@ -37,7 +34,5 @@ enum class KeyboardMode(val value: Int) {
|
||||
fun fromInt(int: Int) = entries.firstOrNull { it.value == int } ?: CHARACTERS
|
||||
}
|
||||
|
||||
override fun toString() = name.lowercase()
|
||||
|
||||
fun toInt() = value
|
||||
}
|
||||
|
||||
@@ -20,19 +20,18 @@ import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Backspace
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -47,6 +46,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
@@ -54,12 +54,10 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiData
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiPaletteView
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
|
||||
@SuppressLint("MutableCollectionMutableState")
|
||||
@Composable
|
||||
@@ -75,8 +73,7 @@ fun MediaInputLayout(
|
||||
}
|
||||
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
SnyggColumn(
|
||||
elementName = FlorisImeUi.Media.elementName,
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.imeUiHeight()),
|
||||
@@ -85,17 +82,14 @@ fun MediaInputLayout(
|
||||
modifier = Modifier.weight(1f),
|
||||
fullEmojiMappings = emojiLayoutDataMap,
|
||||
)
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.MediaBottomRow.elementName,
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.keyboardRowBaseHeight * 0.8f),
|
||||
) {
|
||||
KeyboardLikeButton(
|
||||
elementName = FlorisImeUi.MediaBottomRowButton.elementName,
|
||||
inputEventDispatcher = keyboardManager.inputEventDispatcher,
|
||||
keyData = TextKeyData.IME_UI_MODE_TEXT,
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
) {
|
||||
Text(
|
||||
text = "ABC",
|
||||
@@ -104,10 +98,8 @@ fun MediaInputLayout(
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
KeyboardLikeButton(
|
||||
elementName = FlorisImeUi.MediaBottomRowButton.elementName,
|
||||
inputEventDispatcher = keyboardManager.inputEventDispatcher,
|
||||
keyData = TextKeyData.DELETE,
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Backspace, contentDescription = null)
|
||||
}
|
||||
@@ -121,45 +113,42 @@ internal fun KeyboardLikeButton(
|
||||
modifier: Modifier = Modifier,
|
||||
inputEventDispatcher: InputEventDispatcher,
|
||||
keyData: KeyData,
|
||||
elementName: String = FlorisImeUi.MediaEmojiKey.elementName,
|
||||
content: @Composable () -> Unit,
|
||||
element: String = FlorisImeUi.EmojiKey,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed by interactionSource.collectIsPressedAsState()
|
||||
val selector = if (isPressed) {
|
||||
SnyggSelector.PRESSED
|
||||
} else {
|
||||
SnyggSelector.NONE
|
||||
}
|
||||
|
||||
SnyggBox(
|
||||
elementName = elementName,
|
||||
attributes = mapOf(FlorisImeUi.Attr.Code to keyData.code),
|
||||
selector = selector,
|
||||
clickAndSemanticsModifier = modifier
|
||||
.indication(interactionSource, ripple())
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
val down = awaitFirstDown(requireUnconsumed = false).also {
|
||||
if (it.pressed != it.previousPressed) it.consume()
|
||||
}
|
||||
val press = PressInteraction.Press(down.position)
|
||||
interactionSource.tryEmit(press)
|
||||
inputEventDispatcher.sendDown(keyData)
|
||||
inputFeedbackController.keyPress(keyData)
|
||||
val up = waitForUpOrCancellation()
|
||||
if (up != null) {
|
||||
interactionSource.tryEmit(PressInteraction.Release(press))
|
||||
inputEventDispatcher.sendUp(keyData)
|
||||
} else {
|
||||
interactionSource.tryEmit(PressInteraction.Cancel(press))
|
||||
inputEventDispatcher.sendCancel(keyData)
|
||||
}
|
||||
var isPressed by remember { mutableStateOf(false) }
|
||||
val keyStyle = FlorisImeTheme.style.get(
|
||||
element = element,
|
||||
code = keyData.code,
|
||||
isPressed = isPressed,
|
||||
)
|
||||
SnyggSurface(
|
||||
modifier = modifier.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
awaitFirstDown(requireUnconsumed = false).also {
|
||||
if (it.pressed != it.previousPressed) it.consume()
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
isPressed = true
|
||||
inputEventDispatcher.sendDown(keyData)
|
||||
inputFeedbackController.keyPress(keyData)
|
||||
val up = waitForUpOrCancellation()
|
||||
isPressed = false
|
||||
if (up != null) {
|
||||
inputEventDispatcher.sendUp(keyData)
|
||||
} else {
|
||||
inputEventDispatcher.sendCancel(keyData)
|
||||
}
|
||||
}
|
||||
},
|
||||
style = keyStyle,
|
||||
) {
|
||||
content()
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -38,8 +39,6 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.GenericShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
@@ -47,6 +46,8 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
@@ -54,7 +55,6 @@ import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
@@ -63,7 +63,6 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -89,22 +88,23 @@ import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.header
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
import kotlin.math.ceil
|
||||
|
||||
private val EmojiCategoryValues = EmojiCategory.entries
|
||||
@@ -164,6 +164,10 @@ fun EmojiPaletteView(
|
||||
|
||||
val preferredSkinTone by prefs.emoji.preferredSkinTone.observeAsState()
|
||||
val emojiHistoryEnabled by prefs.emoji.historyEnabled.observeAsState()
|
||||
val fontSizeMultiplier = prefs.keyboard.fontSizeMultiplier()
|
||||
val emojiKeyStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiKey)
|
||||
val emojiKeyFontSize = emojiKeyStyle.fontSize.spSize(default = EmojiDefaultFontSize) safeTimes fontSizeMultiplier
|
||||
val contentColor = emojiKeyStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
|
||||
var activeCategory by remember(emojiHistoryEnabled) {
|
||||
if (emojiHistoryEnabled) {
|
||||
@@ -173,13 +177,16 @@ fun EmojiPaletteView(
|
||||
}
|
||||
}
|
||||
var recentlyUsedVersion by remember { mutableIntStateOf(0) }
|
||||
val lazyListState = rememberLazyGridState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@Composable
|
||||
fun GridHeader(text: String) {
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.MediaEmojiSubheader.elementName,
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = contentColor,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -195,6 +202,9 @@ fun EmojiPaletteView(
|
||||
preferredSkinTone = preferredSkinTone,
|
||||
isPinned = isPinned,
|
||||
isRecent = isRecent,
|
||||
contentColor = contentColor,
|
||||
fontSize = emojiKeyFontSize,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
onEmojiInput = { emoji ->
|
||||
keyboardManager.inputEventDispatcher.sendDownUp(emoji)
|
||||
scope.launch {
|
||||
@@ -207,110 +217,22 @@ fun EmojiPaletteView(
|
||||
)
|
||||
}
|
||||
|
||||
fun calculatePageNumbers(): Int {
|
||||
return when {
|
||||
!emojiHistoryEnabled -> EmojiCategoryValues.size - 1
|
||||
else -> EmojiCategoryValues.size
|
||||
}
|
||||
}
|
||||
|
||||
fun pageNumberToCategory(pageNumber: Int): EmojiCategory {
|
||||
return when {
|
||||
!emojiHistoryEnabled -> EmojiCategoryValues[pageNumber+1]
|
||||
else -> EmojiCategoryValues[pageNumber]
|
||||
}
|
||||
}
|
||||
|
||||
fun categoryToPageNumber(category: EmojiCategory): Int {
|
||||
return if (emojiHistoryEnabled) {
|
||||
EmojiCategoryValues.indexOf(category)
|
||||
} else {
|
||||
EmojiCategoryValues.indexOf(category) - 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun EmojiCategoriesTabRow(
|
||||
activeCategory: EmojiCategory,
|
||||
onCategoryChange: (EmojiCategory) -> Unit,
|
||||
) {
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val selectedTabIndex = categoryToPageNumber(activeCategory)
|
||||
val style = rememberSnyggThemeQuery(FlorisImeUi.MediaEmojiTab.elementName)
|
||||
TabRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = style.foreground(),
|
||||
indicator = { tabPositions ->
|
||||
val style = rememberSnyggThemeQuery(
|
||||
elementName = FlorisImeUi.MediaEmojiTab.elementName,
|
||||
selector = SnyggSelector.FOCUS,
|
||||
)
|
||||
TabRowDefaults.PrimaryIndicator(
|
||||
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
|
||||
height = 4.dp,
|
||||
color = style.foreground(),
|
||||
)
|
||||
},
|
||||
) {
|
||||
for (category in EmojiCategoryValues) {
|
||||
if (category == EmojiCategory.RECENTLY_USED && !emojiHistoryEnabled) {
|
||||
continue
|
||||
}
|
||||
Tab(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
|
||||
onCategoryChange(category)
|
||||
},
|
||||
selected = activeCategory == category,
|
||||
icon = { SnyggIcon(
|
||||
elementName = FlorisImeUi.MediaEmojiTab.elementName,
|
||||
selector = if (activeCategory == category) SnyggSelector.FOCUS else SnyggSelector.NONE,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
imageVector = category.icon(),
|
||||
) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
) {
|
||||
val pagerState = rememberPagerState(
|
||||
pageCount = { calculatePageNumbers() }
|
||||
)
|
||||
|
||||
// Reset the pager to the first page when emojiHistory is enabled
|
||||
LaunchedEffect(emojiHistoryEnabled) {
|
||||
pagerState.animateScrollToPage(0)
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
EmojiCategoriesTabRow(
|
||||
activeCategory = activeCategory,
|
||||
onCategoryChange = { category ->
|
||||
scope.launch { lazyListState.scrollToItem(0) }
|
||||
activeCategory = category
|
||||
scope.launch { pagerState.animateScrollToPage(categoryToPageNumber(activeCategory)) }
|
||||
},
|
||||
emojiHistoryEnabled = emojiHistoryEnabled,
|
||||
)
|
||||
HorizontalPager(pagerState, beyondViewportPageCount = 1) { page ->
|
||||
// Every page needs its own lazyGridState in order to scroll correctly
|
||||
val lazyGridState = rememberLazyGridState()
|
||||
|
||||
// Update the lazyGridState and active category on scroll
|
||||
LaunchedEffect(pagerState) {
|
||||
snapshotFlow { pagerState.currentPage }.collect { page ->
|
||||
lazyGridState.scrollToItem(0)
|
||||
activeCategory = pageNumberToCategory(page)
|
||||
}
|
||||
}
|
||||
|
||||
val category = pageNumberToCategory(page)
|
||||
val emojiMapping = if (category == EmojiCategory.RECENTLY_USED) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
val emojiMapping = if (activeCategory == EmojiCategory.RECENTLY_USED) {
|
||||
// Purposely using remember here to prevent recomposition, as this would cause rapid
|
||||
// emoji changes for the user when in recently used category.
|
||||
remember(recentlyUsedVersion) {
|
||||
@@ -325,68 +247,66 @@ fun EmojiPaletteView(
|
||||
EmojiMappingForView(
|
||||
pinned = emptyList(),
|
||||
recent = emptyList(),
|
||||
simple = emojiMappings[category]!!,
|
||||
simple = emojiMappings[activeCategory]!!,
|
||||
)
|
||||
}
|
||||
|
||||
val isEmojiHistoryEmpty = emojiMapping.pinned.isEmpty() && emojiMapping.recent.isEmpty()
|
||||
when (category) {
|
||||
EmojiCategory.RECENTLY_USED if deviceLocked -> {
|
||||
Column(
|
||||
if (activeCategory == EmojiCategory.RECENTLY_USED && deviceLocked) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__phone_locked_message),
|
||||
color = contentColor,
|
||||
)
|
||||
}
|
||||
} else if (activeCategory == EmojiCategory.RECENTLY_USED && isEmojiHistoryEmpty) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__empty_message),
|
||||
color = contentColor,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = stringRes(R.string.emoji__history__usage_tip),
|
||||
color = contentColor,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
} else key(emojiMapping) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
.florisScrollbar(lazyListState, color = contentColor.copy(alpha = 0.28f)),
|
||||
columns = GridCells.Adaptive(minSize = EmojiBaseWidth),
|
||||
state = lazyListState,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__phone_locked_message),
|
||||
)
|
||||
}
|
||||
}
|
||||
EmojiCategory.RECENTLY_USED if isEmojiHistoryEmpty -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__empty_message),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = stringRes(R.string.emoji__history__usage_tip),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> key(emojiMapping) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.florisScrollbar(lazyGridState),
|
||||
columns = GridCells.Adaptive(minSize = EmojiBaseWidth),
|
||||
state = lazyGridState,
|
||||
) {
|
||||
if (emojiMapping.pinned.isNotEmpty()) {
|
||||
header("header_pinned") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__pinned))
|
||||
}
|
||||
items(emojiMapping.pinned) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isPinned = true)
|
||||
}
|
||||
if (emojiMapping.pinned.isNotEmpty()) {
|
||||
header("header_pinned") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__pinned))
|
||||
}
|
||||
if (emojiMapping.recent.isNotEmpty()) {
|
||||
header("header_recent") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__recent))
|
||||
}
|
||||
items(emojiMapping.recent) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isRecent = true)
|
||||
}
|
||||
items(emojiMapping.pinned) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isPinned = true)
|
||||
}
|
||||
if (emojiMapping.simple.isNotEmpty()) {
|
||||
items(emojiMapping.simple) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet)
|
||||
}
|
||||
}
|
||||
if (emojiMapping.recent.isNotEmpty()) {
|
||||
header("header_recent") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__recent))
|
||||
}
|
||||
items(emojiMapping.recent) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isRecent = true)
|
||||
}
|
||||
}
|
||||
if (emojiMapping.simple.isNotEmpty()) {
|
||||
items(emojiMapping.simple) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,6 +316,61 @@ fun EmojiPaletteView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmojiCategoriesTabRow(
|
||||
activeCategory: EmojiCategory,
|
||||
onCategoryChange: (EmojiCategory) -> Unit,
|
||||
emojiHistoryEnabled: Boolean,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val tabStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiTab)
|
||||
val tabStyleFocused = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiTab, isFocus = true)
|
||||
val unselectedContentColor = tabStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
val selectedContentColor = tabStyleFocused.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
|
||||
val selectedTabIndex = if (emojiHistoryEnabled) {
|
||||
EmojiCategoryValues.indexOf(activeCategory)
|
||||
} else {
|
||||
EmojiCategoryValues.indexOf(activeCategory) - 1
|
||||
}
|
||||
TabRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = selectedContentColor,
|
||||
indicator = { tabPositions ->
|
||||
TabRowDefaults.PrimaryIndicator(
|
||||
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
|
||||
color = selectedContentColor,
|
||||
height = 4.dp
|
||||
)
|
||||
},
|
||||
) {
|
||||
for (category in EmojiCategoryValues) {
|
||||
if (category == EmojiCategory.RECENTLY_USED && !emojiHistoryEnabled) {
|
||||
continue
|
||||
}
|
||||
Tab(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
|
||||
onCategoryChange(category)
|
||||
},
|
||||
selected = activeCategory == category,
|
||||
icon = { Icon(
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
imageVector = category.icon(),
|
||||
contentDescription = null,
|
||||
) },
|
||||
unselectedContentColor = unselectedContentColor,
|
||||
selectedContentColor = selectedContentColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmojiKey(
|
||||
emojiSet: EmojiSet,
|
||||
@@ -403,6 +378,9 @@ private fun EmojiKey(
|
||||
preferredSkinTone: EmojiSkinTone,
|
||||
isPinned: Boolean,
|
||||
isRecent: Boolean,
|
||||
contentColor: Color,
|
||||
fontSize: TextUnit,
|
||||
fontSizeMultiplier: Float,
|
||||
onEmojiInput: (Emoji) -> Unit,
|
||||
onHistoryAction: () -> Unit,
|
||||
) {
|
||||
@@ -411,7 +389,7 @@ private fun EmojiKey(
|
||||
val variations = emojiSet.variations(withoutSkinTone = preferredSkinTone)
|
||||
var showVariantsBox by remember { mutableStateOf(false) }
|
||||
|
||||
SnyggBox(FlorisImeUi.MediaEmojiKey.elementName,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.aspectRatio(1f)
|
||||
.pointerInput(Unit) {
|
||||
@@ -435,9 +413,10 @@ private fun EmojiKey(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = base.value,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
color = contentColor,
|
||||
fontSize = fontSize,
|
||||
)
|
||||
if (variations.isNotEmpty() || isPinned || isRecent) {
|
||||
val style = rememberSnyggThemeQuery(FlorisImeUi.MediaEmojiKeyPopupExtendedIndicator.elementName)
|
||||
val shape = when (LocalLayoutDirection.current) {
|
||||
LayoutDirection.Ltr -> VariantsTriangleShapeLtr
|
||||
LayoutDirection.Rtl -> VariantsTriangleShapeRtl
|
||||
@@ -447,7 +426,7 @@ private fun EmojiKey(
|
||||
.align(Alignment.BottomEnd)
|
||||
.offset(x = (-4).dp, y = (-4).dp)
|
||||
.size(4.dp)
|
||||
.background(style.foreground(), shape),
|
||||
.background(contentColor, shape),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -469,6 +448,7 @@ private fun EmojiKey(
|
||||
variations = variations,
|
||||
visible = showVariantsBox,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
onEmojiTap = { emoji ->
|
||||
onEmojiInput(emoji)
|
||||
showVariantsBox = false
|
||||
@@ -487,10 +467,13 @@ private fun EmojiVariationsPopup(
|
||||
variations: List<Emoji>,
|
||||
visible: Boolean,
|
||||
emojiCompatInstance: EmojiCompat?,
|
||||
fontSizeMultiplier: Float,
|
||||
onEmojiTap: (Emoji) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val popupStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiKeyPopup)
|
||||
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
|
||||
val context = LocalContext.current
|
||||
|
||||
if (visible) {
|
||||
Popup(
|
||||
@@ -501,25 +484,29 @@ private fun EmojiVariationsPopup(
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.MediaEmojiKeyPopupBox.elementName,
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.widthIn(max = EmojiBaseWidth * 6),
|
||||
.widthIn(max = EmojiBaseWidth * 6)
|
||||
.snyggShadow(popupStyle)
|
||||
.snyggBorder(context, popupStyle)
|
||||
.snyggBackground(context, popupStyle, fallbackColor = FlorisImeTheme.fallbackSurfaceColor()),
|
||||
) {
|
||||
for (emoji in variations) {
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.MediaEmojiKeyPopupElement.elementName,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures { onEmojiTap(emoji) }
|
||||
}
|
||||
.width(EmojiBaseWidth)
|
||||
.height(emojiKeyHeight),
|
||||
.height(emojiKeyHeight)
|
||||
.padding(all = 4.dp),
|
||||
) {
|
||||
EmojiText(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = emoji.value,
|
||||
emojiCompatInstance = emojiCompatInstance,
|
||||
color = popupStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
fontSize = popupStyle.fontSize.spSize(default = EmojiDefaultFontSize) safeTimes fontSizeMultiplier,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -539,6 +526,7 @@ private fun EmojiHistoryPopup(
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val scope = rememberCoroutineScope()
|
||||
val popupStyle = FlorisImeTheme.style.get(element = FlorisImeUi.EmojiKeyPopup)
|
||||
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
|
||||
val context = LocalContext.current
|
||||
val pinnedUS by prefs.emoji.historyPinnedUpdateStrategy.observeAsState()
|
||||
@@ -548,8 +536,7 @@ private fun EmojiHistoryPopup(
|
||||
|
||||
@Composable
|
||||
fun Action(icon: ImageVector, action: suspend () -> Unit) {
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.MediaEmojiKeyPopupElement.elementName,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures {
|
||||
@@ -560,11 +547,14 @@ private fun EmojiHistoryPopup(
|
||||
}
|
||||
}
|
||||
.width(EmojiBaseWidth)
|
||||
.height(emojiKeyHeight),
|
||||
.height(emojiKeyHeight)
|
||||
.padding(all = 4.dp),
|
||||
) {
|
||||
SnyggIcon(
|
||||
Icon(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = popupStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -579,10 +569,12 @@ private fun EmojiHistoryPopup(
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.MediaEmojiKeyPopupBox.elementName,
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.widthIn(max = EmojiBaseWidth * 6),
|
||||
.widthIn(max = EmojiBaseWidth * 6)
|
||||
.snyggShadow(popupStyle)
|
||||
.snyggBorder(context, popupStyle)
|
||||
.snyggBackground(context, popupStyle, fallbackColor = FlorisImeTheme.fallbackSurfaceColor()),
|
||||
) {
|
||||
if (isCurrentlyPinned) {
|
||||
Action(
|
||||
|
||||
@@ -96,7 +96,7 @@ class NlpManager(context: Context) {
|
||||
prefs.suggestion.enabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.clipboard.suggestionEnabled.observeForever {
|
||||
prefs.suggestion.clipboardContentEnabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.emoji.suggestionEnabled.observeForever {
|
||||
@@ -370,14 +370,14 @@ class NlpManager(context: Context) {
|
||||
isPrivateSession: Boolean,
|
||||
): List<SuggestionCandidate> {
|
||||
// Check if enabled
|
||||
if (!prefs.clipboard.suggestionEnabled.get()) return emptyList()
|
||||
if (!prefs.suggestion.clipboardContentEnabled.get()) return emptyList()
|
||||
|
||||
val currentItem = validateClipboardItem(clipboardManager.primaryClip, lastClipboardItemId, content.text)
|
||||
?: return emptyList()
|
||||
|
||||
return buildList {
|
||||
val now = System.currentTimeMillis()
|
||||
if ((now - currentItem.creationTimestampMs) < prefs.clipboard.suggestionTimeout.get() * 1000) {
|
||||
if ((now - currentItem.creationTimestampMs) < prefs.suggestion.clipboardContentTimeout.get() * 1000) {
|
||||
add(ClipboardSuggestionCandidate(currentItem, sourceProvider = this@ClipboardSuggestionProvider, context = context))
|
||||
if (currentItem.isSensitive) {
|
||||
return@buildList
|
||||
|
||||
@@ -20,6 +20,7 @@ package dev.patrickgold.florisboard.ime.onehanded
|
||||
* Static object which contains all possible one-handed mode strings.
|
||||
*/
|
||||
enum class OneHandedMode {
|
||||
OFF,
|
||||
START,
|
||||
END;
|
||||
}
|
||||
|
||||
@@ -17,26 +17,29 @@
|
||||
package dev.patrickgold.florisboard.ime.onehanded
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.ZoomOutMap
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
|
||||
@Composable
|
||||
fun RowScope.OneHandedPanel(
|
||||
@@ -46,43 +49,38 @@ fun RowScope.OneHandedPanel(
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val oneHandedPanelStyle = FlorisImeTheme.style.get(FlorisImeUi.OneHandedPanel)
|
||||
val context = LocalContext.current
|
||||
|
||||
SnyggColumn(
|
||||
FlorisImeUi.OneHandedPanel.elementName,
|
||||
Column(
|
||||
modifier = modifier
|
||||
.weight(weight)
|
||||
.snyggBackground(context, oneHandedPanelStyle)
|
||||
.height(FlorisImeSizing.imeUiHeight()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
IconButton(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.OFF)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
SnyggIcon(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
Icon(
|
||||
imageVector = Icons.Default.ZoomOutMap,
|
||||
contentDescription = stringRes(R.string.one_handed__close_btn_content_description),
|
||||
tint = oneHandedPanelStyle.foreground.solidColor(context),
|
||||
)
|
||||
}
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
IconButton(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
) {
|
||||
SnyggIcon(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
Icon(
|
||||
imageVector = if (panelSide == OneHandedMode.START) {
|
||||
Icons.AutoMirrored.Filled.KeyboardArrowLeft
|
||||
} else {
|
||||
@@ -95,6 +93,7 @@ fun RowScope.OneHandedPanel(
|
||||
R.string.one_handed__move_end_btn_content_description
|
||||
}
|
||||
),
|
||||
tint = oneHandedPanelStyle.foreground.solidColor(context),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,58 +17,79 @@
|
||||
package dev.patrickgold.florisboard.ime.popup
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreHoriz
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
@Composable
|
||||
fun PopupBaseBox(
|
||||
modifier: Modifier = Modifier,
|
||||
attributes: SnyggQueryAttributes,
|
||||
key: Key,
|
||||
fontSizeMultiplier: Float,
|
||||
shouldIndicateExtendedPopups: Boolean,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.KeyPopupBox.elementName,
|
||||
attributes = attributes,
|
||||
val context = LocalContext.current
|
||||
|
||||
val popupStyle = FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.KeyPopup,
|
||||
)
|
||||
val fontSize = popupStyle.fontSize.spSize() safeTimes fontSizeMultiplier
|
||||
SnyggSurface(
|
||||
modifier = modifier,
|
||||
style = popupStyle,
|
||||
clip = true,
|
||||
) {
|
||||
key.label?.let { label ->
|
||||
SnyggBox(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(key.visibleBounds.height.toDp())
|
||||
.align(Alignment.TopCenter),
|
||||
) {
|
||||
SnyggText(
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = label,
|
||||
color = popupStyle.foreground.solidColor(context),
|
||||
fontSize = fontSize,
|
||||
maxLines = 1,
|
||||
softWrap = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (shouldIndicateExtendedPopups) {
|
||||
SnyggIcon(
|
||||
elementName = FlorisImeUi.KeyPopupExtendedIndicator.elementName,
|
||||
attributes = attributes,
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(fontSize.toDp() * 0.65f)
|
||||
.align(Alignment.CenterEnd),
|
||||
imageVector = Icons.Default.MoreHoriz,
|
||||
contentDescription = null,
|
||||
tint = popupStyle.foreground.solidColor(context),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -77,43 +98,72 @@ fun PopupBaseBox(
|
||||
@Composable
|
||||
fun PopupExtBox(
|
||||
modifier: Modifier = Modifier,
|
||||
attributes: SnyggQueryAttributes,
|
||||
elements: List<List<PopupUiController.Element>>,
|
||||
fontSizeMultiplier: Float,
|
||||
elemArrangement: Arrangement.Horizontal,
|
||||
elemWidth: Dp,
|
||||
elemHeight: Dp,
|
||||
activeElementIndex: Int,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
SnyggColumn(FlorisImeUi.KeyPopupBox.elementName, attributes, modifier = modifier) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val popupStyle = FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.KeyPopup,
|
||||
isFocus = false,
|
||||
)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.snyggShadow(popupStyle)
|
||||
.snyggBorder(context, popupStyle)
|
||||
.snyggBackground(context, popupStyle),
|
||||
) {
|
||||
for (row in elements.asReversed()) {
|
||||
SnyggRow(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.requiredHeight(elemHeight),
|
||||
horizontalArrangement = elemArrangement,
|
||||
) {
|
||||
for (element in row) {
|
||||
val selector = if (activeElementIndex == element.orderedIndex) {
|
||||
SnyggSelector.FOCUS
|
||||
val elemStyle = if (activeElementIndex == element.orderedIndex) {
|
||||
FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.KeyPopup,
|
||||
isFocus = true,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
popupStyle
|
||||
}
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.KeyPopupElement.elementName,
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
modifier = Modifier.size(elemWidth, elemHeight),
|
||||
val elemFontSize = elemStyle.fontSize.spSize() safeTimes fontSizeMultiplier safeTimes
|
||||
if (element.data.code == KeyCode.URI_COMPONENT_TLD) { 0.6f } else { 1.0f }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(elemWidth, elemHeight)
|
||||
.run {
|
||||
if (activeElementIndex == element.orderedIndex) {
|
||||
snyggBackground(context, elemStyle)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
},
|
||||
) {
|
||||
element.label?.let { label ->
|
||||
SnyggText(
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = label,
|
||||
color = elemStyle.foreground.solidColor(context),
|
||||
fontSize = elemFontSize,
|
||||
maxLines = 1,
|
||||
softWrap = false,
|
||||
)
|
||||
}
|
||||
element.icon?.let { icon ->
|
||||
SnyggIcon(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(elemFontSize.toDp() * 1.1f)
|
||||
.align(Alignment.Center),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = elemStyle.foreground.solidColor(context),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintConfiguration
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.FlorisRect
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
|
||||
@@ -82,6 +81,7 @@ class PopupUiController(
|
||||
|
||||
private var activeElementIndex by mutableIntStateOf(-1)
|
||||
var evaluator: ComputingEvaluator = DefaultComputingEvaluator
|
||||
var fontSizeMultiplier: Float = 1.0f
|
||||
var keyHintConfiguration: KeyHintConfiguration = KeyHintConfiguration.HINTS_DISABLED
|
||||
|
||||
/** Is true if the preview popup is visible to the user, else false */
|
||||
@@ -448,17 +448,13 @@ class PopupUiController(
|
||||
|
||||
@Composable
|
||||
fun RenderPopups(): Unit = with(LocalDensity.current) {
|
||||
val attributes = mapOf(
|
||||
FlorisImeUi.Attr.Mode to evaluator.keyboard.mode.toString(),
|
||||
FlorisImeUi.Attr.ShiftState to evaluator.state.inputShiftState.toString(),
|
||||
)
|
||||
baseRenderInfo?.let { renderInfo ->
|
||||
PopupBaseBox(
|
||||
modifier = Modifier
|
||||
.requiredSize(renderInfo.bounds.size.toDpSize())
|
||||
.absoluteOffset { renderInfo.bounds.topLeft.toIntOffset() },
|
||||
attributes = attributes,
|
||||
key = renderInfo.key,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
shouldIndicateExtendedPopups = renderInfo.shouldIndicateExtendedPopups && extRenderInfo == null,
|
||||
)
|
||||
}
|
||||
@@ -470,8 +466,8 @@ class PopupUiController(
|
||||
modifier = Modifier
|
||||
.requiredSize(renderInfo.bounds.size.toDpSize())
|
||||
.absoluteOffset { renderInfo.bounds.topLeft.toIntOffset() },
|
||||
attributes = attributes,
|
||||
elements = renderInfo.elements,
|
||||
fontSizeMultiplier = fontSizeMultiplier,
|
||||
elemArrangement = if (renderInfo.anchorLeft) {
|
||||
Arrangement.Start
|
||||
} else {
|
||||
|
||||
@@ -21,11 +21,17 @@ import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -39,24 +45,25 @@ import androidx.compose.ui.input.pointer.PointerInputChange
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.nlp.ClipboardSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.conditional
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggSpacer
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
val CandidatesRowScrollbarHeight = 2.dp
|
||||
|
||||
@@ -71,10 +78,13 @@ fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
val displayMode by prefs.suggestion.displayMode.observeAsState()
|
||||
val candidates by nlpManager.activeCandidatesFlow.collectAsState()
|
||||
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.SmartbarCandidatesRow.elementName,
|
||||
val rowStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarCandidatesRow)
|
||||
val spacerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarCandidateSpacer)
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.snyggBackground(context, rowStyle)
|
||||
.conditional(displayMode == CandidatesDisplayMode.DYNAMIC_SCROLLABLE && candidates.size > 1) {
|
||||
florisHorizontalScroll(scrollbarHeight = CandidatesRowScrollbarHeight)
|
||||
},
|
||||
@@ -105,12 +115,12 @@ fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
}
|
||||
for ((n, candidate) in list.withIndex()) {
|
||||
if (n > 0) {
|
||||
SnyggSpacer(
|
||||
elementName = FlorisImeUi.SmartbarCandidateSpacer.elementName,
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.fillMaxHeight(0.6f)
|
||||
.align(Alignment.CenterVertically),
|
||||
.align(Alignment.CenterVertically)
|
||||
.snyggBackground(context, spacerStyle),
|
||||
)
|
||||
}
|
||||
CandidateItem(
|
||||
@@ -146,21 +156,24 @@ private fun CandidateItem(
|
||||
onLongPress: () -> Boolean = { false },
|
||||
longPressDelay: Long,
|
||||
) = with(LocalDensity.current) {
|
||||
val context = LocalContext.current
|
||||
var isPressed by remember { mutableStateOf(false) }
|
||||
|
||||
val elementName = if (candidate is ClipboardSuggestionCandidate) {
|
||||
FlorisImeUi.SmartbarCandidateClip
|
||||
val style = if (candidate is ClipboardSuggestionCandidate) {
|
||||
FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.SmartbarCandidateClip,
|
||||
isPressed = isPressed,
|
||||
)
|
||||
} else {
|
||||
FlorisImeUi.SmartbarCandidateWord
|
||||
}.elementName
|
||||
val attributes = mapOf("auto-commit" to if (candidate.isEligibleForAutoCommit) 1 else 0)
|
||||
val selector = if (isPressed) SnyggSelector.PRESSED else SnyggSelector.NONE
|
||||
FlorisImeTheme.style.get(
|
||||
element = FlorisImeUi.SmartbarCandidateWord,
|
||||
isPressed = isPressed,
|
||||
)
|
||||
}
|
||||
|
||||
SnyggRow(
|
||||
elementName = elementName,
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
Row(
|
||||
modifier = modifier
|
||||
.snyggBackground(context, style)
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
val down = awaitFirstDown()
|
||||
@@ -184,35 +197,47 @@ private fun CandidateItem(
|
||||
}
|
||||
isPressed = false
|
||||
}
|
||||
},
|
||||
}
|
||||
.padding(horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (candidate.icon != null) {
|
||||
SnyggBox(
|
||||
elementName = "$elementName-icon",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
) {
|
||||
SnyggIcon(imageVector = candidate.icon!!)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(
|
||||
style.fontSize
|
||||
.spSize()
|
||||
.toDp() * 1.5f
|
||||
)
|
||||
.padding(end = 4.dp),
|
||||
imageVector = candidate.icon!!,
|
||||
contentDescription = null,
|
||||
tint = style.foreground.solidColor(context),
|
||||
)
|
||||
}
|
||||
SnyggColumn(
|
||||
Column(
|
||||
modifier = if (displayMode == CandidatesDisplayMode.CLASSIC) Modifier.weight(1f) else Modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SnyggText(
|
||||
elementName = "$elementName-text",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
Text(
|
||||
text = candidate.text.toString(),
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = style.fontSize.spSize(),
|
||||
fontWeight = if (candidate.isEligibleForAutoCommit) FontWeight.Bold else FontWeight.Normal,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
if (candidate.secondaryText != null) {
|
||||
SnyggText(
|
||||
elementName = "$elementName-secondary-text",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
Text(
|
||||
text = candidate.secondaryText!!.toString(),
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = style.fontSize.spSize() safeTimes 0.6,
|
||||
fontWeight = if (candidate.isEligibleForAutoCommit) FontWeight.Bold else FontWeight.Normal,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -33,22 +32,8 @@ import androidx.compose.ui.layout.positionInParent
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofillSuggestion
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
import org.florisboard.lib.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.snygg.SnyggSinglePropertySet
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
|
||||
var CachedInlineSuggestionsChipStyleSet: SnyggSinglePropertySet? = null
|
||||
|
||||
@Composable
|
||||
fun InlineSuggestionsStyleCache() {
|
||||
val chipStyleSet = rememberSnyggThemeQuery(FlorisImeUi.InlineAutofillChip.elementName)
|
||||
LaunchedEffect(chipStyleSet) {
|
||||
CachedInlineSuggestionsChipStyleSet = chipStyleSet
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@Composable
|
||||
|
||||
@@ -25,19 +25,22 @@ import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.UnfoldLess
|
||||
import androidx.compose.material.icons.filled.UnfoldMore
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
@@ -58,6 +61,7 @@ import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionButton
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionsRow
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.ToggleOverflowPanelAction
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.horizontalTween
|
||||
@@ -66,17 +70,15 @@ import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
|
||||
const val AnimationDuration = 200
|
||||
private const val AnimationDuration = 200
|
||||
|
||||
val VerticalEnterTransition = EnterTransition.verticalTween(AnimationDuration)
|
||||
val VerticalExitTransition = ExitTransition.verticalTween(AnimationDuration)
|
||||
private val VerticalEnterTransition = EnterTransition.verticalTween(AnimationDuration)
|
||||
private val VerticalExitTransition = ExitTransition.verticalTween(AnimationDuration)
|
||||
|
||||
private val HorizontalEnterTransition = EnterTransition.horizontalTween(AnimationDuration)
|
||||
private val HorizontalExitTransition = ExitTransition.horizontalTween(AnimationDuration)
|
||||
@@ -100,25 +102,24 @@ fun Smartbar() {
|
||||
) {
|
||||
when (extendedActionsPlacement) {
|
||||
ExtendedActionsPlacement.ABOVE_CANDIDATES -> {
|
||||
SnyggColumn(FlorisImeUi.Smartbar.elementName) {
|
||||
Column {
|
||||
SmartbarSecondaryRow()
|
||||
SmartbarMainRow()
|
||||
}
|
||||
}
|
||||
|
||||
ExtendedActionsPlacement.BELOW_CANDIDATES -> {
|
||||
SnyggColumn(FlorisImeUi.Smartbar.elementName) {
|
||||
Column {
|
||||
SmartbarMainRow()
|
||||
SmartbarSecondaryRow()
|
||||
}
|
||||
}
|
||||
|
||||
ExtendedActionsPlacement.OVERLAY_APP_UI -> {
|
||||
SnyggBox(FlorisImeUi.Smartbar.elementName,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
allowClip = false,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -156,47 +157,65 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
val shouldAnimate by prefs.smartbar.sharedActionsExpandWithAnimation.observeAsState()
|
||||
|
||||
val smartbarStyle = FlorisImeTheme.style.get(FlorisImeUi.Smartbar)
|
||||
val primaryActionsToggleStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarSharedActionsToggle)
|
||||
val secondaryActionsToggleStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarExtendedActionsToggle)
|
||||
|
||||
@Composable
|
||||
fun SharedActionsToggle() {
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.SmartbarSharedActionsToggle.elementName,
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (/* was */ sharedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
val transition = updateTransition(sharedActionsExpanded, label = "sharedActionsExpandedToggleBtn")
|
||||
val rotation by transition.animateFloat(
|
||||
transitionSpec = {
|
||||
if (shouldAnimate) AnimationTween else NoAnimationTween
|
||||
},
|
||||
label = "rotation",
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxHeight()
|
||||
.aspectRatio(1f)
|
||||
.snyggShadow(primaryActionsToggleStyle)
|
||||
.snyggBorder(context, primaryActionsToggleStyle)
|
||||
.snyggBackground(context, primaryActionsToggleStyle),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (it) 180f else 0f
|
||||
}
|
||||
val arrowIcon = if (flipToggles) {
|
||||
Icons.AutoMirrored.Default.KeyboardArrowLeft
|
||||
} else {
|
||||
Icons.AutoMirrored.Default.KeyboardArrowRight
|
||||
}
|
||||
val incognitoIcon = vectorResource(id = R.drawable.ic_incognito)
|
||||
val incognitoDisplayMode = prefs.keyboard.incognitoDisplayMode.observeAsState()
|
||||
val isIncognitoMode = keyboardManager.activeState.isIncognitoMode
|
||||
val icon = if (isIncognitoMode) {
|
||||
when (incognitoDisplayMode.value) {
|
||||
IncognitoDisplayMode.REPLACE_SHARED_ACTIONS_TOGGLE -> incognitoIcon!!
|
||||
IncognitoDisplayMode.DISPLAY_BEHIND_KEYBOARD -> arrowIcon
|
||||
val transition = updateTransition(sharedActionsExpanded, label = "sharedActionsExpandedToggleBtn")
|
||||
val rotation by transition.animateFloat(
|
||||
transitionSpec = {
|
||||
if (shouldAnimate) AnimationTween else NoAnimationTween
|
||||
},
|
||||
label = "rotation",
|
||||
) {
|
||||
if (it) 180f else 0f
|
||||
}
|
||||
} else {
|
||||
arrowIcon
|
||||
val arrowIcon = if (flipToggles) {
|
||||
Icons.AutoMirrored.Default.KeyboardArrowLeft
|
||||
} else {
|
||||
Icons.AutoMirrored.Default.KeyboardArrowRight
|
||||
}
|
||||
val incognitoIcon = vectorResource(id = R.drawable.ic_incognito)
|
||||
val incognitoDisplayMode = prefs.keyboard.incognitoDisplayMode.observeAsState()
|
||||
val isIncognitoMode = keyboardManager.activeState.isIncognitoMode
|
||||
val icon = if (isIncognitoMode) {
|
||||
when (incognitoDisplayMode.value) {
|
||||
IncognitoDisplayMode.REPLACE_SHARED_ACTIONS_TOGGLE -> incognitoIcon!!
|
||||
IncognitoDisplayMode.DISPLAY_BEHIND_KEYBOARD -> arrowIcon
|
||||
}
|
||||
} else {
|
||||
arrowIcon
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.rotate(if (incognitoDisplayMode.value == IncognitoDisplayMode.DISPLAY_BEHIND_KEYBOARD) rotation else 0f),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = primaryActionsToggleStyle.foreground.solidColor(
|
||||
context,
|
||||
default = FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
)
|
||||
}
|
||||
SnyggIcon(
|
||||
modifier = Modifier.rotate(if (incognitoDisplayMode.value == IncognitoDisplayMode.DISPLAY_BEHIND_KEYBOARD) rotation else 0f),
|
||||
imageVector = icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,10 +246,10 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
exit = exitTransition,
|
||||
) {
|
||||
QuickActionsRow(
|
||||
FlorisImeUi.SmartbarSharedActionsRow.elementName,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
elementName = FlorisImeUi.SmartbarSharedActionsRow,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -238,35 +257,46 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
@Composable
|
||||
fun ExtendedActionsToggle() {
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.SmartbarExtendedActionsToggle.elementName,
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (/* was */ extendedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
val transition = updateTransition(extendedActionsExpanded, label = "smartbarSecondaryRowToggleBtn")
|
||||
val alpha by transition.animateFloat(label = "alpha") { if (it) 1f else 0f }
|
||||
val rotation by transition.animateFloat(label = "rotation") { if (it) 180f else 0f }
|
||||
// Expanded icon
|
||||
SnyggIcon(
|
||||
FlorisImeUi.SmartbarExtendedActionsToggle.elementName,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.alpha(alpha)
|
||||
.rotate(rotation),
|
||||
imageVector = Icons.Default.UnfoldLess,
|
||||
)
|
||||
// Not expanded icon
|
||||
SnyggIcon(
|
||||
FlorisImeUi.SmartbarExtendedActionsToggle.elementName,
|
||||
modifier = Modifier
|
||||
.alpha(1f - alpha)
|
||||
.rotate(rotation - 180f),
|
||||
imageVector = Icons.Default.UnfoldMore,
|
||||
)
|
||||
.padding(4.dp)
|
||||
.fillMaxHeight()
|
||||
.aspectRatio(1f)
|
||||
.snyggShadow(secondaryActionsToggleStyle)
|
||||
.snyggBorder(context, secondaryActionsToggleStyle)
|
||||
.snyggBackground(context, secondaryActionsToggleStyle),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val transition = updateTransition(extendedActionsExpanded, label = "smartbarSecondaryRowToggleBtn")
|
||||
val alpha by transition.animateFloat(label = "alpha") { if (it) 1f else 0f }
|
||||
val rotation by transition.animateFloat(label = "rotation") { if (it) 180f else 0f }
|
||||
// Expanded icon
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.alpha(alpha)
|
||||
.rotate(rotation),
|
||||
imageVector = Icons.Default.UnfoldLess,
|
||||
contentDescription = null,
|
||||
tint = secondaryActionsToggleStyle.foreground.solidColor(context),
|
||||
)
|
||||
// Not expanded icon
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.alpha(1f - alpha)
|
||||
.rotate(rotation - 180f),
|
||||
imageVector = Icons.Default.UnfoldMore,
|
||||
contentDescription = null,
|
||||
tint = secondaryActionsToggleStyle.foreground.solidColor(context),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,10 +338,11 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
}
|
||||
}
|
||||
|
||||
SnyggRow(
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
.height(FlorisImeSizing.smartbarHeight)
|
||||
.snyggBackground(context, smartbarStyle),
|
||||
) {
|
||||
when (smartbarLayout) {
|
||||
SmartbarLayout.SUGGESTIONS_ONLY -> {
|
||||
@@ -326,7 +357,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (shouldShowInlineSuggestionsUi) {
|
||||
InlineSuggestionsUi(inlineSuggestions)
|
||||
} else {
|
||||
QuickActionsRow(FlorisImeUi.SmartbarSharedActionsRow.elementName)
|
||||
QuickActionsRow(elementName = FlorisImeUi.SmartbarSharedActionsRow)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,16 +390,16 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarSecondaryRow(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val prefs by florisPreferenceModel()
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val secondaryRowStyle = rememberSnyggThemeQuery(FlorisImeUi.SmartbarExtendedActionsRow.elementName)
|
||||
val windowStyle = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)
|
||||
val secondaryRowStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarExtendedActionsRow)
|
||||
val extendedActionsExpanded by prefs.smartbar.extendedActionsExpanded.observeAsState()
|
||||
val extendedActionsPlacement by prefs.smartbar.extendedActionsPlacement.observeAsState()
|
||||
val background = secondaryRowStyle.background().let { color ->
|
||||
val background = secondaryRowStyle.background.solidColor(context).let { color ->
|
||||
if (extendedActionsPlacement == ExtendedActionsPlacement.OVERLAY_APP_UI) {
|
||||
if (color.isUnspecified || color.alpha == 0f) {
|
||||
windowStyle.background(default = Color.Black)
|
||||
FlorisImeTheme.style.get(FlorisImeUi.Keyboard).background.solidColor(context, default = Color.Black)
|
||||
} else {
|
||||
color
|
||||
}
|
||||
@@ -383,11 +414,11 @@ private fun SmartbarSecondaryRow(modifier: Modifier = Modifier) {
|
||||
exit = VerticalExitTransition,
|
||||
) {
|
||||
QuickActionsRow(
|
||||
FlorisImeUi.SmartbarExtendedActionsRow.elementName,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight)
|
||||
.background(background),
|
||||
elementName = FlorisImeUi.SmartbarExtendedActionsRow,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,6 @@ fun QuickAction.computeDisplayName(evaluator: ComputingEvaluator): String {
|
||||
KeyCode.CLIPBOARD_SELECT_ALL -> R.string.quick_action__clipboard_select_all
|
||||
KeyCode.IME_UI_MODE_CLIPBOARD -> R.string.quick_action__ime_ui_mode_clipboard
|
||||
KeyCode.IME_UI_MODE_MEDIA -> R.string.quick_action__ime_ui_mode_media
|
||||
KeyCode.LANGUAGE_SWITCH -> R.string.quick_action__language_switch
|
||||
KeyCode.SETTINGS -> R.string.quick_action__settings
|
||||
KeyCode.UNDO -> R.string.quick_action__undo
|
||||
KeyCode.REDO -> R.string.quick_action__redo
|
||||
@@ -98,7 +97,7 @@ fun QuickAction.computeDisplayName(evaluator: ComputingEvaluator): String {
|
||||
KeyCode.TOGGLE_AUTOCORRECT -> R.string.quick_action__toggle_autocorrect
|
||||
KeyCode.VOICE_INPUT -> R.string.quick_action__voice_input
|
||||
// TODO: In the future this will be merged into the resize keyboard panel, for now it is a separate action
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> R.string.quick_action__one_handed_mode
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> R.string.quick_action__one_handed_mode
|
||||
KeyCode.DRAG_MARKER -> if (evaluator.state.debugShowDragAndDropHelpers) {
|
||||
R.string.quick_action__drag_marker
|
||||
} else {
|
||||
@@ -126,7 +125,6 @@ fun QuickAction.computeTooltip(evaluator: ComputingEvaluator): String {
|
||||
KeyCode.CLIPBOARD_SELECT_ALL -> R.string.quick_action__clipboard_select_all__tooltip
|
||||
KeyCode.IME_UI_MODE_CLIPBOARD -> R.string.quick_action__ime_ui_mode_clipboard__tooltip
|
||||
KeyCode.IME_UI_MODE_MEDIA -> R.string.quick_action__ime_ui_mode_media__tooltip
|
||||
KeyCode.LANGUAGE_SWITCH -> R.string.quick_action__language_switch__tooltip
|
||||
KeyCode.SETTINGS -> R.string.quick_action__settings__tooltip
|
||||
KeyCode.UNDO -> R.string.quick_action__undo__tooltip
|
||||
KeyCode.REDO -> R.string.quick_action__redo__tooltip
|
||||
@@ -135,7 +133,7 @@ fun QuickAction.computeTooltip(evaluator: ComputingEvaluator): String {
|
||||
KeyCode.TOGGLE_AUTOCORRECT -> R.string.quick_action__toggle_autocorrect__tooltip
|
||||
KeyCode.VOICE_INPUT -> R.string.quick_action__voice_input__tooltip
|
||||
// TODO: In the future this will be merged into the resize keyboard panel, for now it is a separate action
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> R.string.quick_action__one_handed_mode__tooltip
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> R.string.quick_action__one_handed_mode__tooltip
|
||||
KeyCode.DRAG_MARKER -> if (evaluator.state.debugShowDragAndDropHelpers) {
|
||||
R.string.quick_action__drag_marker__tooltip
|
||||
} else {
|
||||
|
||||
@@ -20,12 +20,13 @@ import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.lib.io.DefaultJsonConfig
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.plus
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
|
||||
val QuickActionJsonConfig = Json(DefaultJsonConfig) {
|
||||
private val QuickActionJsonConfig = Json(DefaultJsonConfig) {
|
||||
classDiscriminator = "$"
|
||||
encodeDefaults = false
|
||||
ignoreUnknownKeys = true
|
||||
@@ -46,24 +47,10 @@ data class QuickActionArrangement(
|
||||
val dynamicActions: List<QuickAction>,
|
||||
val hiddenActions: List<QuickAction>,
|
||||
) {
|
||||
operator fun contains(action: QuickAction): Boolean {
|
||||
fun contains(action: QuickAction): Boolean {
|
||||
return stickyAction == action || dynamicActions.contains(action) || hiddenActions.contains(action)
|
||||
}
|
||||
|
||||
fun distinct(): QuickActionArrangement {
|
||||
val distinctSet = mutableSetOf<QuickAction>()
|
||||
if (stickyAction != null) {
|
||||
distinctSet.add(stickyAction)
|
||||
}
|
||||
val distinctDynamicActions = dynamicActions.filter { distinctSet.add(it) }
|
||||
val distinctHiddenActions = hiddenActions.filter { distinctSet.add(it) }
|
||||
return QuickActionArrangement(
|
||||
stickyAction = stickyAction,
|
||||
dynamicActions = distinctDynamicActions,
|
||||
hiddenActions = distinctHiddenActions,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Default = QuickActionArrangement(
|
||||
stickyAction = QuickAction.InsertKey(TextKeyData.VOICE_INPUT),
|
||||
@@ -74,7 +61,7 @@ data class QuickActionArrangement(
|
||||
QuickAction.InsertKey(TextKeyData.TOGGLE_INCOGNITO_MODE),
|
||||
QuickAction.InsertKey(TextKeyData.IME_UI_MODE_CLIPBOARD),
|
||||
QuickAction.InsertKey(TextKeyData.IME_UI_MODE_MEDIA),
|
||||
QuickAction.InsertKey(TextKeyData.TOGGLE_COMPACT_LAYOUT),
|
||||
QuickAction.InsertKey(TextKeyData.COMPACT_LAYOUT_TO_RIGHT),
|
||||
QuickAction.InsertKey(TextKeyData.TOGGLE_AUTOCORRECT),
|
||||
QuickAction.InsertKey(TextKeyData.ARROW_UP),
|
||||
QuickAction.InsertKey(TextKeyData.ARROW_DOWN),
|
||||
@@ -85,7 +72,6 @@ data class QuickActionArrangement(
|
||||
QuickAction.InsertKey(TextKeyData.CLIPBOARD_CUT),
|
||||
QuickAction.InsertKey(TextKeyData.CLIPBOARD_PASTE),
|
||||
QuickAction.InsertKey(TextKeyData.CLIPBOARD_SELECT_ALL),
|
||||
QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH),
|
||||
),
|
||||
hiddenActions = listOf(
|
||||
),
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
@@ -24,32 +28,52 @@ import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.compose.tooltip.PlainTooltip
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.compose.tooltip.tooltip
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeImageVector
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeLabel
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.ui.shape
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggClip
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
|
||||
private val BackgroundAnimationSpec = tween<Color>(durationMillis = 150, easing = FastOutSlowInEasing)
|
||||
private val DebugHelperColor = Color.Red.copy(alpha = 0.5f)
|
||||
|
||||
enum class QuickActionBarType {
|
||||
INTERACTIVE_BUTTON,
|
||||
INTERACTIVE_TILE,
|
||||
EDITOR_TILE;
|
||||
STATIC_TILE;
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -64,18 +88,47 @@ fun QuickActionButton(
|
||||
val inputFeedbackController = FlorisImeService.inputFeedbackController()
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed by interactionSource.collectIsPressedAsState()
|
||||
val isEnabled = type == QuickActionBarType.EDITOR_TILE || evaluator.evaluateEnabled(action.keyData())
|
||||
val elementName = when (type) {
|
||||
val isEnabled = type == QuickActionBarType.STATIC_TILE || evaluator.evaluateEnabled(action.keyData())
|
||||
val element = when (type) {
|
||||
QuickActionBarType.INTERACTIVE_BUTTON -> FlorisImeUi.SmartbarActionKey
|
||||
QuickActionBarType.INTERACTIVE_TILE -> FlorisImeUi.SmartbarActionTile
|
||||
QuickActionBarType.EDITOR_TILE -> FlorisImeUi.SmartbarActionsEditorTile
|
||||
}.elementName
|
||||
val attributes = mapOf(FlorisImeUi.Attr.Code to action.keyData().code)
|
||||
val selector = when {
|
||||
isPressed -> SnyggSelector.PRESSED
|
||||
!isEnabled -> SnyggSelector.DISABLED
|
||||
else -> null
|
||||
else -> FlorisImeUi.SmartbarActionTile
|
||||
}
|
||||
// We always need to know both state's styles to animate smoothly
|
||||
val actionStyleNotPressed = FlorisImeTheme.style.get(
|
||||
element = element,
|
||||
code = action.keyData().code,
|
||||
isPressed = false,
|
||||
isDisabled = !isEnabled,
|
||||
)
|
||||
val actionStylePressed = FlorisImeTheme.style.get(
|
||||
element = element,
|
||||
code = action.keyData().code,
|
||||
isPressed = true,
|
||||
isDisabled = !isEnabled,
|
||||
)
|
||||
val actionStyle = if (isPressed) actionStylePressed else actionStyleNotPressed
|
||||
val bgColor by animateColorAsState(
|
||||
targetValue = if (isPressed) {
|
||||
actionStylePressed.background.solidColor(context)
|
||||
} else {
|
||||
if (actionStyleNotPressed.background.solidColor(context).alpha == 0f) {
|
||||
actionStylePressed.background.solidColor(context).copy(0f)
|
||||
} else {
|
||||
actionStyleNotPressed.background.solidColor(context)
|
||||
}
|
||||
},
|
||||
animationSpec = BackgroundAnimationSpec, label = "bgColor",
|
||||
)
|
||||
val fgColor = when (action.keyData().code) {
|
||||
KeyCode.DRAG_MARKER -> {
|
||||
DebugHelperColor
|
||||
}
|
||||
|
||||
else -> {
|
||||
actionStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor())
|
||||
}
|
||||
}
|
||||
val fgAlpha = if (action.keyData().code == KeyCode.NOOP) 0.5f else 1f
|
||||
|
||||
// Need to manually cancel an action if this composable suddenly leaves the composition to prevent the key from
|
||||
// being stuck in the pressed state
|
||||
@@ -87,82 +140,92 @@ fun QuickActionButton(
|
||||
}
|
||||
}
|
||||
|
||||
PlainTooltip(action.computeTooltip(evaluator), enabled = type == QuickActionBarType.INTERACTIVE_BUTTON) {
|
||||
SnyggBox(
|
||||
elementName = elementName,
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
modifier = modifier,
|
||||
clickAndSemanticsModifier = Modifier
|
||||
.aspectRatio(1f)
|
||||
.indication(interactionSource, LocalIndication.current)
|
||||
.pointerInput(action, isEnabled) {
|
||||
awaitEachGesture {
|
||||
val down = awaitFirstDown()
|
||||
down.consume()
|
||||
if (isEnabled && type != QuickActionBarType.EDITOR_TILE) {
|
||||
val press = PressInteraction.Press(down.position)
|
||||
inputFeedbackController?.keyPress(TextKeyData.UNSPECIFIED)
|
||||
interactionSource.tryEmit(press)
|
||||
action.onPointerDown(context)
|
||||
val up = waitForUpOrCancellation()
|
||||
if (up != null) {
|
||||
up.consume()
|
||||
interactionSource.tryEmit(PressInteraction.Release(press))
|
||||
action.onPointerUp(context)
|
||||
} else {
|
||||
interactionSource.tryEmit(PressInteraction.Cancel(press))
|
||||
action.onPointerCancel(context)
|
||||
}
|
||||
val tooltipModifier = if (type == QuickActionBarType.INTERACTIVE_BUTTON) {
|
||||
Modifier.tooltip(action.computeTooltip(evaluator))
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.aspectRatio(1f)
|
||||
.alpha(fgAlpha)
|
||||
.snyggShadow(actionStyle)
|
||||
.snyggBorder(context, actionStyle)
|
||||
.background(bgColor, actionStyle.shape.shape())
|
||||
.snyggClip(actionStyle)
|
||||
.indication(interactionSource, LocalIndication.current)
|
||||
.pointerInput(action, isEnabled) {
|
||||
awaitEachGesture {
|
||||
val down = awaitFirstDown()
|
||||
down.consume()
|
||||
if (isEnabled && type != QuickActionBarType.STATIC_TILE) {
|
||||
val press = PressInteraction.Press(down.position)
|
||||
inputFeedbackController?.keyPress(TextKeyData.UNSPECIFIED)
|
||||
interactionSource.tryEmit(press)
|
||||
action.onPointerDown(context)
|
||||
val up = waitForUpOrCancellation()
|
||||
if (up != null) {
|
||||
up.consume()
|
||||
interactionSource.tryEmit(PressInteraction.Release(press))
|
||||
action.onPointerUp(context)
|
||||
} else {
|
||||
interactionSource.tryEmit(PressInteraction.Cancel(press))
|
||||
action.onPointerCancel(context)
|
||||
}
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
// Render foreground
|
||||
}
|
||||
}
|
||||
.then(tooltipModifier)
|
||||
.padding(8.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
// Render foreground
|
||||
Box(
|
||||
modifier = Modifier.size(FlorisImeSizing.smartbarHeight * 0.5f),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
when (action) {
|
||||
is QuickAction.InsertKey -> {
|
||||
val (imageVector, label) = remember(action, evaluator) {
|
||||
evaluator.computeImageVector(action.data) to evaluator.computeLabel(action.data)
|
||||
}
|
||||
if (imageVector != null) {
|
||||
SnyggBox(
|
||||
elementName = "$elementName-icon",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
) {
|
||||
SnyggIcon(imageVector = imageVector)
|
||||
}
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
tint = fgColor,
|
||||
)
|
||||
} else if (label != null) {
|
||||
SnyggText(
|
||||
elementName = "$elementName-text",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
Text(
|
||||
text = label,
|
||||
color = fgColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is QuickAction.InsertText -> {
|
||||
SnyggText(
|
||||
elementName = "$elementName-text",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
Text(
|
||||
text = action.data.firstOrNull().toString().ifBlank { "?" },
|
||||
color = fgColor,
|
||||
fontSize = 16.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render additional info if this is a tile
|
||||
if (type != QuickActionBarType.INTERACTIVE_BUTTON) {
|
||||
SnyggText(
|
||||
elementName = "$elementName-text",
|
||||
attributes = attributes,
|
||||
selector = selector,
|
||||
text = action.computeDisplayName(evaluator = evaluator),
|
||||
)
|
||||
}
|
||||
// Render additional info if this is a tile
|
||||
if (type != QuickActionBarType.INTERACTIVE_BUTTON) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = action.computeDisplayName(evaluator = evaluator),
|
||||
color = fgColor,
|
||||
fontSize = if (type == QuickActionBarType.STATIC_TILE) 10.sp else 13.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
|
||||
@@ -34,6 +34,7 @@ import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -47,6 +48,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -56,24 +59,25 @@ import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggClip
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
private const val ItemNotFound = -1
|
||||
private val NoopAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.NOOP))
|
||||
private val DragMarkerAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.DRAG_MARKER))
|
||||
|
||||
@Composable
|
||||
fun QuickActionsEditorPanel() {
|
||||
fun QuickActionsEditorPanel(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -96,6 +100,10 @@ fun QuickActionsEditorPanel() {
|
||||
var activeDragPosition by remember { mutableStateOf(IntOffset.Zero) }
|
||||
var activeDragSize by remember { mutableStateOf(IntSize.Zero) }
|
||||
|
||||
val panelStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditor)
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorHeader)
|
||||
val subheaderStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorSubheader)
|
||||
|
||||
fun findItemForOffsetOrClosestInRow(offset: IntOffset): LazyGridItemInfo? {
|
||||
var closestItemInRow: LazyGridItemInfo? = null
|
||||
// Using manual for loop with indices instead of firstOrNull() because this method gets
|
||||
@@ -242,34 +250,35 @@ fun QuickActionsEditorPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
SnyggColumn(FlorisImeUi.SmartbarActionsEditor.elementName, modifier = Modifier.safeDrawingPadding()) {
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.SmartbarActionsEditorHeader.elementName,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
Column(
|
||||
modifier = modifier
|
||||
.snyggBackground(context, panelStyle, fallbackColor = FlorisImeTheme.fallbackSurfaceColor())
|
||||
.snyggClip(panelStyle),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.snyggBackground(context, headerStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
// Extra box wrapper is needed to enforce size constraint but still allow for Snygg margin to be used
|
||||
Box(modifier = Modifier.size(48.dp)) {
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.SmartbarActionsEditorHeaderButton.elementName,
|
||||
modifier = Modifier.fillMaxHeight().aspectRatio(1f),
|
||||
onClick = {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
},
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft,
|
||||
)
|
||||
}
|
||||
}
|
||||
SnyggText(
|
||||
FlorisIconButton(
|
||||
onClick = {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
},
|
||||
icon = Icons.AutoMirrored.Filled.KeyboardArrowLeft,
|
||||
iconColor = headerStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringRes(R.string.quick_actions_editor__header),
|
||||
color = headerStyle.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
fontSize = headerStyle.fontSize.spSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(Modifier.size(48.dp))
|
||||
}
|
||||
|
||||
SnyggBox(FlorisImeUi.SmartbarActionsEditorTileGrid.elementName) {
|
||||
Box {
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
.pointerInput(Unit) {
|
||||
@@ -287,6 +296,7 @@ fun QuickActionsEditorPanel() {
|
||||
val n = if (stickyAction != NoopAction) 1 else 0
|
||||
Subheader(
|
||||
text = stringRes(R.string.quick_actions_editor__subheader_sticky_action, "n" to n),
|
||||
style = subheaderStyle,
|
||||
)
|
||||
}
|
||||
item(key = keyOf(stickyAction)) {
|
||||
@@ -294,13 +304,14 @@ fun QuickActionsEditorPanel() {
|
||||
modifier = Modifier.animateItem(),
|
||||
action = stickyAction,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.EDITOR_TILE,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
)
|
||||
}
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
val n = dynamicActions.count { it != NoopAction }
|
||||
Subheader(
|
||||
text = stringRes(R.string.quick_actions_editor__subheader_dynamic_actions, "n" to n),
|
||||
style = subheaderStyle,
|
||||
)
|
||||
}
|
||||
itemsIndexed(dynamicActions, key = { i, a -> keyOf(a) ?: i }) { _, action ->
|
||||
@@ -308,13 +319,14 @@ fun QuickActionsEditorPanel() {
|
||||
modifier = Modifier.animateItem(),
|
||||
action = action,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.EDITOR_TILE,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
)
|
||||
}
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
val n = hiddenActions.count { it != NoopAction }
|
||||
Subheader(
|
||||
text = stringRes(R.string.quick_actions_editor__subheader_hidden_actions, "n" to n),
|
||||
style = subheaderStyle,
|
||||
)
|
||||
}
|
||||
itemsIndexed(hiddenActions, key = { i, a -> keyOf(a) ?: i }) { _, action ->
|
||||
@@ -322,7 +334,7 @@ fun QuickActionsEditorPanel() {
|
||||
modifier = Modifier.animateItem(),
|
||||
action = action,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.EDITOR_TILE,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -337,21 +349,28 @@ fun QuickActionsEditorPanel() {
|
||||
.offset(-size.width / 2, -size.height / 2),
|
||||
action = activeDragAction!!,
|
||||
evaluator = evaluator,
|
||||
type = QuickActionBarType.EDITOR_TILE,
|
||||
type = QuickActionBarType.STATIC_TILE,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.systemBarsPadding().snyggBackground(context, panelStyle))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subheader(
|
||||
text: String,
|
||||
style: SnyggPropertySet,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.SmartbarActionsEditorSubheader.elementName,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
val context = LocalContext.current
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, bottom = 8.dp, start = 16.dp, end = 16.dp),
|
||||
text = text,
|
||||
color = style.foreground.solidColor(context, default = FlorisImeTheme.fallbackContentColor()),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = style.fontSize.spSize() safeTimes 0.8f,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
@@ -29,16 +31,17 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
|
||||
@Composable
|
||||
fun QuickActionsOverflowPanel() {
|
||||
@@ -60,15 +63,19 @@ fun QuickActionsOverflowPanel() {
|
||||
actionArrangement.dynamicActions.takeLast(dynamicActionsCountToShow)
|
||||
}
|
||||
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.SmartbarActionsOverflow.elementName,
|
||||
val panelStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsOverflow)
|
||||
val buttonStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsOverflowCustomizeButton)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.keyboardUiHeight()),
|
||||
.height(FlorisImeSizing.keyboardUiHeight())
|
||||
.snyggBackground(context, panelStyle),
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp),
|
||||
columns = GridCells.Adaptive(FlorisImeSizing.smartbarHeight * 2.2f),
|
||||
) {
|
||||
items(visibleActions) { action ->
|
||||
@@ -80,15 +87,13 @@ fun QuickActionsOverflowPanel() {
|
||||
}
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
SnyggButton(
|
||||
elementName = FlorisImeUi.SmartbarActionsOverflowCustomizeButton.elementName,
|
||||
onClick = { keyboardManager.activeState.isActionsEditorVisible = true },
|
||||
modifier = Modifier
|
||||
.wrapContentWidth(),
|
||||
) {
|
||||
SnyggText(
|
||||
text = stringRes(R.string.quick_actions_overflow__customize_actions_button),
|
||||
)
|
||||
}
|
||||
.wrapContentWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
text = stringRes(R.string.quick_actions_overflow__customize_actions_button),
|
||||
style = buttonStyle,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
@@ -31,9 +32,10 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
|
||||
internal val ToggleOverflowPanelAction = QuickAction.InsertKey(TextKeyData.TOGGLE_ACTIONS_OVERFLOW)
|
||||
|
||||
@@ -65,6 +67,8 @@ fun QuickActionsRow(
|
||||
val showOverflowAction = actionArrangement.stickyAction != null ||
|
||||
smartbarLayout != SmartbarLayout.SUGGESTIONS_ACTIONS_SHARED || !sharedActionsExpanded
|
||||
|
||||
val rowStyle = FlorisImeTheme.style.get(elementName)
|
||||
|
||||
BoxWithConstraints(modifier = modifier.fillMaxSize()) {
|
||||
val width = constraints.maxWidth.toDp()
|
||||
val height = constraints.maxHeight.toDp()
|
||||
@@ -81,9 +85,10 @@ fun QuickActionsRow(
|
||||
}.coerceAtLeast(0)
|
||||
}
|
||||
|
||||
SnyggRow(
|
||||
elementName = elementName,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.snyggBackground(context, rowStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user