diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cce4beb..9e929b6a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,33 +14,47 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/).
### ✨ New Features
-- Adds a new menu to the Scribe app allowing users to set their preferences for their respective language keyboards!
+- Adds a new menu to the Scribe app allowing users to set their preferences for their respective language keyboards ([#16](https://github.com/scribe-org/Scribe-iOS/issues/16))!
- Users now have easy access to the Scribe GitHub, the Matrix community, rating the app, sending bug reports and emailing the team.
- Settings options include:
- - Allowing the user to add a command and period to the letter keys.
- - Allowing the user to disable emoji autosuggestions and autocompletions.
- - Allowing the user to disable accented characters on the letter keys.
- - Menu screens can be swiped between via an implementation using SwipeableTabBarController.
-- German indefinite pronouns are now selectable from the case-declension display.
+ - Allowing the user to add a comma and period to the letter keys ([#196](https://github.com/scribe-org/Scribe-iOS/issues/196), [#308](https://github.com/scribe-org/Scribe-iOS/issues/308)).
+ - Allowing the user to disable emoji autosuggestions and autocompletions ([#310](https://github.com/scribe-org/Scribe-iOS/issues/196), [#311](https://github.com/scribe-org/Scribe-iOS/issues/308)).
+ - Allowing the user to disable accented characters on the letter keys ([#339](https://github.com/scribe-org/Scribe-iOS/issues/339), [#372](https://github.com/scribe-org/Scribe-iOS/issues/372)).
+ - Menu screens can be swiped between via an implementation using [SwipeableTabBarController](https://github.com/marcosgriselli/SwipeableTabBarController).
+ - Menu options have descriptions of their functionality listed beneath them ([#330](https://github.com/scribe-org/Scribe-iOS/issues/339), [#372](https://github.com/scribe-org/Scribe-iOS/issues/330)).
+- German indefinite pronouns are now selectable from the case-declension display ([#303](https://github.com/scribe-org/Scribe-iOS/issues/303)).
- German imperfect verb conjugations now insert both the auxiliary verb and the past participle with the cursor between them.
- Tab and caps lock keys and their functionalities have been added to expanded iPad layouts ([#371](https://github.com/scribe-org/Scribe-iOS/issues/371)).
### 🎨 Design Changes
-- iPad keyboards are now more reflective of their system keyboard counterparts for devices above a certain width.
+- iPad keyboards are now more reflective of their system keyboard counterparts for devices above a certain width ([#33](https://github.com/scribe-org/Scribe-iOS/issues/33), [#352](https://github.com/scribe-org/Scribe-iOS/issues/352)).
+- Vertical spacing between keys on iPads has been decreased so that keys are more square and thus more in line with system keyboards.
-
+
+### ♻️ Code Refactoring
-- Bugs were fixed that were causing the autocompletions to trigger to regularly. -->
+- Magic numbers for interface radii and other sizing dimensions have been converted to defined variables ([#379](https://github.com/scribe-org/Scribe-iOS/issues/379)).
+- Usage of force-unwraps was dramatically reduced in the codebase ([#379](https://github.com/scribe-org/Scribe-iOS/issues/379)).
+- Usage of `guard let` and `if let` increased throughout the codebase to assure that early and safe nil-unwrapping ([#379](https://github.com/scribe-org/Scribe-iOS/issues/379)).
+- The code for settings keyboard key dimensions and padding was extracted into functions for maintainability ([#383](https://github.com/scribe-org/Scribe-iOS/issues/383)).
+- While loops were replaced by for loops in places where they were being used inappropriately ([#380](https://github.com/scribe-org/Scribe-iOS/issues/380)).
+- The [Scribe-i18n](https://github.com/scribe-org/Scribe-i18n) directory has been added for future localization work.
+- SQLite queries were refactored to extract the DB access logic into a common file ([#378](https://github.com/scribe-org/Scribe-iOS/issues/378)).
# Scribe-iOS 2.3.0
### ✨ New Features
-- Noun genders are now displayed to the user under autosuggestions and autocompletions.
+- Noun genders are now displayed to the user under autosuggestions and autocompletions ([#164](https://github.com/scribe-org/Scribe-iOS/issues/164)).
- The word that the user is typing is available as an autocompletion in cases where pressing space will insert an autocompletion.
- Auto completion and suggestion buttons are deactivated if there is no word being displayed.
-- Autosuggestions and emoji suggestions are now updated when a user checks the annotation of a word by pressing the Scribe key.
+- Autosuggestions and emoji suggestions are now updated when a user checks the annotation of a word by pressing the Scribe key ([#291](https://github.com/scribe-org/Scribe-iOS/issues/291)).
- The capitalization of autosuggestions is maintained if the word is capitalized to assure that capitalized nouns are presented properly.
- The app screen now includes information about Scribe's relation to Wikimedia as well as license information for code used in development.
@@ -64,7 +78,7 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/).
### ♻️ Code Refactoring
-- All unnecessary explicit boolean checks were removed from the codes in favor of implicit checks.
+- All unnecessary explicit boolean checks were removed from the codes in favor of implicit checks ([#289](https://github.com/scribe-org/Scribe-iOS/issues/289)).
- The logic of `selectedWordAnnotation` and `typedWordAnnotation` is now shared in a single function.
- Many variable names have been changed to be zero indexed.
@@ -72,17 +86,17 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/).
### ✨ New Features
-- Emoji autocompletions and autosuggestions are now available as the user types.
+- Emoji autocompletions and autosuggestions are now available as the user types ([#51](https://github.com/scribe-org/Scribe-iOS/issues/51), ([#276](https://github.com/scribe-org/Scribe-iOS/issues/276))).
- There are a maximum of two emojis available to select on iPhones and three on iPads.
- - The user can also repeat emoji autocompletions and autosuggestions.
+ - The user can also repeat emoji autocompletions and autosuggestions ([#283](https://github.com/scribe-org/Scribe-iOS/issues/283)).
- Emoji autocomplete and autosuggest keywords have also been added as possible autocompletion words.
-- Added an action to command bar information icon to explain Wikidata and Scribe's relation to it.
-- Added highlight for autocompletion if it is the word typed.
-- If a word is the only autosuggestion, hitting the space bar inserts the suggestion.
+- Added an action to command bar information icon to explain Wikidata and Scribe's relation to it ([#214](https://github.com/scribe-org/Scribe-iOS/issues/214)).
+- Added highlight for autocompletion if it is the word typed ([#250](https://github.com/scribe-org/Scribe-iOS/issues/250)).
+- If a word is the only autosuggestion, hitting the space bar inserts the suggestion ([#256](https://github.com/scribe-org/Scribe-iOS/issues/256)).
- An undo option is included within autosuggestions if the user does not want the space completion.
-- Added Demonstrative pronouns to German preposition declension tables.
-- Added contracted preposition annotation to the German keyboard.
-- Pressing dash twice now inserts an em dash in the text proxy.
+- Added Demonstrative pronouns to German preposition declension tables ([#249](https://github.com/scribe-org/Scribe-iOS/issues/249)).
+- Added contracted preposition annotation to the German keyboard ([#279](https://github.com/scribe-org/Scribe-iOS/issues/279)).
+- Pressing dash twice now inserts an em dash in the text proxy ([#280](https://github.com/scribe-org/Scribe-iOS/issues/280)).
### 🗃️ Data Added
@@ -96,7 +110,7 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/).
### 🎨 Design Changes
-- The Scribe application receives dark mode in this version.
+- The Scribe application receives dark mode in this version ([#260](https://github.com/scribe-org/Scribe-iOS/issues/260)).
- The app icon has been made more modern and glossy and the direct shadow has been removed.
- Minor adjustments to the original app screen texts and colors have been made.
- The resolution of the Scribe key has been improved.
@@ -108,11 +122,11 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/).
- The select keyboard button has been moved to the bottom left most position on iPads.
- The gear icon in the top left of the keyboard installation steps is now a more simple version.
- iPad application texts were made slightly larger.
-- Images and videos for 6.7 inch iPhones have been added to the App Store.
+- Images and videos for 6.7 inch iPhones have been added to the App Store ([#225](https://github.com/scribe-org/Scribe-iOS/issues/225)).
### 🌐 Localization
-- The Scribe app has been localized into German for users that have it as their system language.
+- The Scribe app has been localized into German for users that have it as their system language ([#9](https://github.com/scribe-org/Scribe-iOS/issues/9)).
### 🐞 Bug Fixes
@@ -122,31 +136,31 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/).
### ⚖️ Legal
-- A German version of the privacy policy was added.
-- All versions of the privacy policy now note that the English version takes precedence over all others.
+- A German version of the privacy policy was added ([#9](https://github.com/scribe-org/Scribe-iOS/issues/9)).
+- All versions of the privacy policy now note that the English version takes precedence over all others ([#9](https://github.com/scribe-org/Scribe-iOS/issues/9)).
- The text of the privacy policy was updated slightly for readability.
- Information about data from Unicode CLDR for emoji suggestions and completions was added to the privacy policy.
### ♻️ Code Refactoring
-- Scribe data is now loaded into SQLite database tables to make data reference less memory intensive and mitigate crashes.
-- All prior JSON data references have been replaced with database queries and JSON language data files have been removed.
-- GRDB.swift was added to the dependencies.
+- Scribe data is now loaded into SQLite database tables to make data reference less memory intensive and mitigate crashes ([#96](https://github.com/scribe-org/Scribe-iOS/issues/96)).
+- All prior JSON data references have been replaced with database queries and JSON language data files have been removed ([#96](https://github.com/scribe-org/Scribe-iOS/issues/96)).
+- [GRDB.swift](https://github.com/groue/GRDB.swift) was added to the dependencies.
# Scribe-iOS 2.1.0
### ⌨️ New Keyboards
-- Adds a QWERTY keyboard option for French.
+- Adds a QWERTY keyboard option for French ([#229](https://github.com/scribe-org/Scribe-iOS/issues/229)).
### ✨ New Features
-- The left and right buttons in the conjugation and declination views are disabled now if pressing them will not lead to a change in the view.
-- Autosuggestions for pronouns have been improved for German, French and Spanish.
+- The left and right buttons in the conjugation and declination views are disabled now if pressing them will not lead to a change in the view ([#211](https://github.com/scribe-org/Scribe-iOS/issues/211)).
+- Autosuggestions for pronouns have been improved for German, French and Spanish ([#208](https://github.com/scribe-org/Scribe-iOS/issues/208)).
- The keyboards shift state is disabled by pressing an autocompletion or autosuggestion.
-- Autocomplete now functions after quotes, slashes and hashtags.
-- Scribe can now access unordered names in the user's contacts to present them as autocompletions.
-- The delete button now speeds up as the user holds it.
+- Autocomplete now functions after quotes, slashes and hashtags ([#234](https://github.com/scribe-org/Scribe-iOS/issues/234)).
+- Scribe can now access unordered names in the user's contacts to present them as autocompletions ([#201](https://github.com/scribe-org/Scribe-iOS/issues/201)).
+- The delete button now speeds up as the user holds it ([#147](https://github.com/scribe-org/Scribe-iOS/issues/147)).
- Typing a period, comma, question mark or exclamation point now removes a space before them if there is one.
### 🗃️ Data Added
@@ -164,8 +178,8 @@ Thousands of new French verb conjugations have been added!
### 🎨 Design Changes
- The labels for conjugations and declinations have been made darker in dark mode to be more readable.
-- French keyboards are now named based on their keyboard style.
-- Keyboards will now display the language followed by "(Scribe)" for a second before showing the language's word for space, similar to the system keyboards.
+- French keyboards are now named based on their keyboard style ([#229](https://github.com/scribe-org/Scribe-iOS/issues/229)).
+- Keyboards will now display the language followed by "(Scribe)" for a second before showing the language's word for space, similar to the system keyboards ([#34](https://github.com/scribe-org/Scribe-iOS/issues/34)).
### 🐞 Bug Fixes
@@ -174,8 +188,8 @@ Thousands of new French verb conjugations have been added!
### ♻️ Code Refactoring
-- Loading JSONs for language data is now handled by SwiftyJSON, with the code being refactored to implement it.
- - This is a first step in refining the data loading process to better handle large amounts of data.
+- Loading JSONs for language data is now handled by [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON), with the code being refactored to implement it ([#231](https://github.com/scribe-org/Scribe-iOS/issues/231)).
+ - This is a first step in refining the data loading process to better handle large amounts of data ([#96](https://github.com/scribe-org/Scribe-iOS/issues/96)).
- Light and dark mode colors are now defined in `Assets.xcassets` and accessed via `ScribeColor.getter:color` or `UIColor`'s new convenience initializer.
- Variants of the Scribe key icon are placed into `Assets.xcassets`, making it unnecessary to check for light/dark mode and device type in code.
@@ -183,10 +197,10 @@ Thousands of new French verb conjugations have been added!
### ✨ New Features
-- Scribe now includes a baseline Wikidata and Wikipedia based autocomplete feature.
+- Scribe now includes a baseline Wikidata and Wikipedia based autocomplete feature ([#188](https://github.com/scribe-org/Scribe-iOS/issues/188)).
- Suggestions include the next possible noun as well as the most common words in the keyboard language.
-- Scribe now includes a baseline autosuggest feature that suggests words derived from Wikipedia that most often follow a given word.
-- Preposition annotations can now be clicked to display a case pronoun display from which pronouns can be selected.
+- Scribe now includes a baseline autosuggest feature that suggests words derived from Wikipedia that most often follow a given word ([#194](https://github.com/scribe-org/Scribe-iOS/issues/194)).
+- Preposition annotations can now be clicked to display a case pronoun display from which pronouns can be selected ([#210](https://github.com/scribe-org/Scribe-iOS/issues/210)).
- Users are able to select from the display based on subjects and objects to exactly specify which pronoun they need.
### 🗃️ Data Added
@@ -201,16 +215,15 @@ Thousands of new French verb conjugations have been added!
### 🎨 Design Changes
-- Noun and preposition annotation has been updated to not show the word being annotated.
+- Noun and preposition annotation has been updated to not show the word being annotated ([#188](https://github.com/scribe-org/Scribe-iOS/issues/188)).
- This saves space above the keyboard for autocomplete and autosuggest.
- The annotation colors have been changed to match the new backgrounds.
- The delete key features a pressed state style similar to the native keyboard.
-- New layouts for pronoun declination have been added to the keyboards.
+- New layouts for pronoun declination have been added to the keyboards ([#210](https://github.com/scribe-org/Scribe-iOS/issues/210)).
- The message indicating that the word isn't in Wikidata now comes with an information icon (action pending).
-- The App Store images have been updated to reflect the new Wikipedia based autosuggest.
-- The App Store videos have been updated to reflect the changes for the new version.
-- iPhone 6.7" images and videos have been added to the App Store.
-- Other minor changes to images for the App Store.
+- The App Store images have been updated to reflect the new Wikipedia based autosuggest ([#199](https://github.com/scribe-org/Scribe-iOS/issues/199)).
+- The App Store videos have been updated to reflect the changes for the new version ([#199](https://github.com/scribe-org/Scribe-iOS/issues/199)).
+- Other minor changes to images for the App Store ([#199](https://github.com/scribe-org/Scribe-iOS/issues/199)).
### 🌐 Localization
@@ -218,11 +231,11 @@ Thousands of new French verb conjugations have been added!
### ⚖️ Legal
-- The privacy policy was updated to add information about the Wikipedia text data terms of use.
+- The privacy policy was updated to add information about the Wikipedia text data terms of use ([#194](https://github.com/scribe-org/Scribe-iOS/issues/194)).
### ♻️ Code Refactoring
-- Boolean states for commands were converted into a single enum to make keyboard states much simpler to work with.
+- Boolean states for commands were converted into a single enum to make keyboard states much simpler to work with ([#200](https://github.com/scribe-org/Scribe-iOS/issues/200)).
- Code was refactored to work with the new enum style of command state management.
- Enums are now used to control switching between conjugations.
- Enums are now used to control switching between different conjugation displays.
@@ -231,8 +244,8 @@ Thousands of new French verb conjugations have been added!
### ✨ New Features
-- Commands now include a greyed out prompt that tells the user to enter a specific word type.
-- The return key now changes its icon during commands to make it more apparent as the execution input.
+- Commands now include a greyed out prompt that tells the user to enter a specific word type ([#35](https://github.com/scribe-org/Scribe-iOS/issues/35)).
+- The return key now changes its icon during commands to make it more apparent as the execution input ([#165](https://github.com/scribe-org/Scribe-iOS/issues/165)).
- The link to GitHub in the app now goes to the iOS repo instead of the organization.
### 🗃️ Data Added
@@ -272,7 +285,7 @@ Scribe's second design sprint with Spencer Arney focussed on the App Store media
### 🐞 Bug Fixes
-- Verb conjugation tables now always return to their base conjugation each time the command is used.
+- Verb conjugation tables now always return to their base conjugation each time the command is used ([#168](https://github.com/scribe-org/Scribe-iOS/issues/168)).
# Scribe-iOS 1.3.7
@@ -324,14 +337,14 @@ Scribe's second design sprint with Spencer Arney focussed on the App Store media
### ♻️ Code Refactoring
-- The data files have been moved to a new directory within the organization on GitHub.
+- The data files have been moved to a new directory within the organization on GitHub - [Scribe-Data](https://github.com/scribe-org/Scribe-Data).
# Scribe-iOS 1.3.4
### 🎨 Design Changes
- Captions for App Store images have been updated.
-- The App Store description has been updated with a reference to Wikidata and grammar improvements.
+- The App Store description has been updated with a reference to Wikidata and grammar improvements ([#151](https://github.com/scribe-org/Scribe-iOS/issues/151)).
- The open-source images in the App Store has been updated to reference open data and Wikidata.
### ⚖️ Legal
@@ -362,7 +375,7 @@ Scribe's second design sprint with Spencer Arney focussed on the App Store media
### 🎨 Design Changes
-- Key alternate views appear more quickly.
+- Key alternate views appear more quickly ([#145](https://github.com/scribe-org/Scribe-iOS/issues/145)).
### 🐞 Bug Fixes
@@ -382,15 +395,15 @@ Scribe's second design sprint with Spencer Arney focussed on the App Store media
### 🐞 Bug Fixes
-- The alternate characters of apostrophes and quotation marks have been fixed.
-- Key alternate views now stay if the key is canceled as they were disappearing too easily.
+- The alternate characters of apostrophes and quotation marks have been fixed ([#142](https://github.com/scribe-org/Scribe-iOS/issues/142)).
+- Key alternate views now stay if the key is canceled as they were disappearing too easily ([#143](https://github.com/scribe-org/Scribe-iOS/issues/143)).
- The width of alternate character callouts for certain keys has been fixed for iPhones.
# Scribe-iOS 1.3.0
### ⌨️ New Keyboards
-- Adds an Italian keyboard.
+- Adds an Italian keyboard ([#132](https://github.com/scribe-org/Scribe-iOS/issues/132)).
### 🗃️ Data Added
@@ -398,20 +411,20 @@ Scribe's second design sprint with Spencer Arney focussed on the App Store media
### 🎨 Design Changes
-- The messages that tell the user a noun is already plural have been translated to the keyboard's language.
+- The messages that tell the user a noun is already plural have been translated to the keyboard's language ([#138](https://github.com/scribe-org/Scribe-iOS/issues/138)).
- The keyboard height has been increased for landscape mode on iPads.
-- Characters on keys have been made larger so they reflect the system keyboards better.
-- All letter, number and special keys now pop up after being pressed.
-- Hold to select characters have been redesigned to reflect the addition of keys popping up.
-- All App Store media has been redone to reflect these changes.
+- Characters on keys have been made larger so they reflect the system keyboards better ([#131](https://github.com/scribe-org/Scribe-iOS/issues/131)).
+- All letter, number and special keys now pop up after being pressed ([#26](https://github.com/scribe-org/Scribe-iOS/issues/26)).
+- Hold to select characters have been redesigned to reflect the addition of keys popping up ([#26](https://github.com/scribe-org/Scribe-iOS/issues/26)).
+- All App Store media has been redone to reflect these changes ([#139](https://github.com/scribe-org/Scribe-iOS/issues/139)).
# Scribe-iOS 1.2.1
### ✨ New Features
-- The keyboard switches back to letter keys after all appropriate symbols followed by space.
-- Scribe commands now accept inputs that are followed by a space in case the user accidentally added one.
-- Users can now translate pronouns as these were not originally included.
+- The keyboard switches back to letter keys after all appropriate symbols followed by space ([#117](https://github.com/scribe-org/Scribe-iOS/issues/117)).
+- Scribe commands now accept inputs that are followed by a space in case the user accidentally added one ([#118](https://github.com/scribe-org/Scribe-iOS/issues/118)).
+- Users can now translate pronouns as these were not originally included ([#128](https://github.com/scribe-org/Scribe-iOS/issues/128)).
### 🗃️ Data Added
@@ -430,19 +443,19 @@ Scribe's second design sprint with Spencer Arney focussed on the App Store media
### 🐞 Bug Fixes
-- The Scribe key now switches its icon color with the rest of the keyboard when the user changes color modes.
-- Annotations are no longer triggered if a user presses space during a command.
+- The Scribe key now switches its icon color with the rest of the keyboard when the user changes color modes ([#116](https://github.com/scribe-org/Scribe-iOS/issues/116)).
+- Annotations are no longer triggered if a user presses space during a command ([#123](https://github.com/scribe-org/Scribe-iOS/issues/123)).
### ♻️ Code Refactoring
-- Commands buttons are now called keys and the preview bar has been renamed the command bar.
-- Force casts are used as little as possible.
-- All lines have been reduced to a reasonable length (120 characters) where able.
-- All functions have been reduced to a reasonable length (40 lines) where able.
-- All functions have been reduced to a reasonable cyclomatic complexity (10 or less) where able.
-- All files have been reduced to a reasonable length (400 lines) where able.
-- All type bodies have been reduced to a reasonable length (200 lines) where able.
-- Scribe has been modularized to be more easily worked with.
+- Commands buttons are now called keys and the preview bar has been renamed the command bar ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- Force casts are used as little as possible ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- All lines have been reduced to a reasonable length (120 characters) where able ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- All functions have been reduced to a reasonable length (40 lines) where able ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- All functions have been reduced to a reasonable cyclomatic complexity (10 or less) where able ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- All files have been reduced to a reasonable length (400 lines) where able ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- All type bodies have been reduced to a reasonable length (200 lines) where able ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
+- Scribe has been modularized to be more easily worked with ([#1](https://github.com/scribe-org/Scribe-iOS/issues/1)).
- The app screen's text was moved to a new directory where localizations will be stored.
# Scribe-iOS 1.2.0
@@ -453,35 +466,35 @@ The entire layout of Scribe has been reworked to make the experience more aesthe
### ✨ New Features
-- Users can now get to Settings by clicking the installation steps in the app screen.
+- Users can now get to Settings by clicking the installation steps in the app screen ([#27](https://github.com/scribe-org/Scribe-iOS/issues/27)).
- All keyboards now switch to an English keyboard for translation, with this being in preparation for when more languages can be translated from.
### 🎨 Design Changes
-- The logo and icon for Scribe have been reworked to give the app a distinct style.
-- The app screen has been completely redone to be more appealing.
-- Keyboard layouts, colors and characters have been changed to match system keyboards.
-- Translation prompts were changed to be two digit abbreviations of source and target language.
-- Colors for noun annotation were updated to improve readability.
-- Noun annotation is now done with a square symbol to represent the gender.
-- Preposition annotation is now done with a rectangular symbol to represent the case.
-- Preposition case abbreviations have been changed to match the language of the keyboard.
-- All App Store media has been redone to reflect these changes.
+- The logo and icon for Scribe have been reworked to give the app a distinct style ([#24](https://github.com/scribe-org/Scribe-iOS/issues/24)).
+- The app screen has been completely redone to be more appealing ([#28](https://github.com/scribe-org/Scribe-iOS/issues/28)).
+- Keyboard layouts, colors and characters have been changed to match system keyboards ([#32](https://github.com/scribe-org/Scribe-iOS/issues/32)).
+- Translation prompts were changed to be two digit abbreviations of source and target language ([#112](https://github.com/scribe-org/Scribe-iOS/issues/112)).
+- Colors for noun annotation were updated to improve readability ([#31](https://github.com/scribe-org/Scribe-iOS/issues/31)).
+- Noun annotation is now done with a square symbol to represent the gender ([#112](https://github.com/scribe-org/Scribe-iOS/issues/112)).
+- Preposition annotation is now done with a rectangular symbol to represent the case ([#92](https://github.com/scribe-org/Scribe-iOS/issues/92)).
+- Preposition case abbreviations have been changed to match the language of the keyboard ([#92](https://github.com/scribe-org/Scribe-iOS/issues/92)).
+- All App Store media has been redone to reflect these changes ([#114](https://github.com/scribe-org/Scribe-iOS/issues/114)).
### 🐞 Bug Fixes
-- The keyboard colors should not switch randomly between light and dark mode now.
+- The keyboard colors should not switch randomly between light and dark mode now ([#112](https://github.com/scribe-org/Scribe-iOS/issues/112)).
- Removed an additional character from the Spanish iPad keyboard's special keys.
### ⚖️ Legal
-- The privacy policy was updated to reflect the addition of the GitHub, Inc icon into the app.
+- The privacy policy was updated to reflect the addition of the GitHub, Inc icon into the app ([#50](https://github.com/scribe-org/Scribe-iOS/issues/50)).
# Scribe-iOS 1.1.1
### 🗃️ Data Added
-Data updates are now all done through a single Python file - update_data.py.
+Data updates are now all done through a single Python file - update_data.py ([#95](https://github.com/scribe-org/Scribe-iOS/issues/95)).
- French: 11 nouns
- German: 152 nouns, 1 verb, 1 preposition
@@ -493,12 +506,12 @@ Data updates are now all done through a single Python file - update_data.py.
- The text size for the command bar in landscape mode for phones was made smaller.
- The height of the keyboard in landscape mode for phones was made slightly smaller.
-- App store images were updated to combine the dark mode and devices screens.
+- App store images were updated to combine the dark mode and devices screens ([#98](https://github.com/scribe-org/Scribe-iOS/issues/98)).
### 🐞 Bug Fixes
-- The keyboard colors now update if the user switches between light and dark mode.
-- Auto-capitalization and switching to the letter keys weren't always triggered after a period.
+- The keyboard colors now update if the user switches between light and dark mode ([#31](https://github.com/scribe-org/Scribe-iOS/issues/31)).
+- Auto-capitalization and switching to the letter keys weren't always triggered after a period ([#97](https://github.com/scribe-org/Scribe-iOS/issues/97)).
- Shifting orientation from portrait to landscape is now seamless, but landscape to portrait is still a WIP.
### ♻️ Code Refactoring
@@ -510,14 +523,14 @@ Data updates are now all done through a single Python file - update_data.py.
### ⌨️ New Keyboards
-- Adds Russian, French, Portuguese and Swedish keyboards.
+- Adds Russian ([#6](https://github.com/scribe-org/Scribe-iOS/issues/6)), French ([#68](https://github.com/scribe-org/Scribe-iOS/issues/68)), Portuguese ([#67](https://github.com/scribe-org/Scribe-iOS/issues/67)) and Swedish keyboards ([#78](https://github.com/scribe-org/Scribe-iOS/issues/78)).
### ✨ New Features
-- Hold-to-select functionality for symbol keys.
+- Hold-to-select functionality for symbol keys ([#69](https://github.com/scribe-org/Scribe-iOS/issues/69)).
- The keyboard keys are capitalized if the user deletes at the start of the command bar.
-- Removes noun-gender annotation for given names to avoid misgendering people.
-- Users are now able to pass upper-case arguments to translate and conjugate.
+- Removes noun-gender annotation for given names to avoid misgendering people ([#90](https://github.com/scribe-org/Scribe-iOS/issues/90)).
+- Users are now able to pass upper-case arguments to translate and conjugate ([#93](https://github.com/scribe-org/Scribe-iOS/issues/93)).
### 🗃️ Data Added
@@ -531,18 +544,18 @@ Data updates are now all done through a single Python file - update_data.py.
### 🎨 Design Changes
- Improves the display of the caps lock key by making its background the key pressed color.
-- Updates the App Store images and videos.
-- Scribe command titles are now in the keyboard language for a more immersive experience.
+- Updates the App Store images and videos ([#84](https://github.com/scribe-org/Scribe-iOS/issues/84)).
+- Scribe command titles are now in the keyboard language for a more immersive experience ([#91](https://github.com/scribe-org/Scribe-iOS/issues/91)).
- Translate for Russian switches to an English keyboard.
### 🐞 Bug Fixes
- German keyboards had the dollar sign shown on the number keys instead of the euro sign.
- iPads had a semicolon key that also had apostrophes.
-- Hold-to-select keys wouldn't return to their original color.
-- The keyboard wouldn't always be letter keys when switched to.
-- The double space period shortcut wasn't possible after certain special characters and numbers.
-- More than one singular gender wasn't being assigned to German nouns in the formatting process.
+- Hold-to-select keys wouldn't return to their original color ([#74](https://github.com/scribe-org/Scribe-iOS/issues/74)).
+- The keyboard wouldn't always be letter keys when switched to ([#80](https://github.com/scribe-org/Scribe-iOS/issues/80)).
+- The double space period shortcut wasn't possible after certain special characters and numbers ([#71](https://github.com/scribe-org/Scribe-iOS/issues/71)).
+- More than one singular gender wasn't being assigned to German nouns in the formatting process ([#76](https://github.com/scribe-org/Scribe-iOS/issues/76)).
### ♻️ Code Refactoring
@@ -554,25 +567,25 @@ Data updates are now all done through a single Python file - update_data.py.
### ✨ New Features
-- Comma-space to letter keys functionality.
-- Question mark and exclamation point followed by space to capital letter keys functionality.
+- Comma-space to letter keys functionality ([#56](https://github.com/scribe-org/Scribe-iOS/issues/56)).
+- Question mark and exclamation point followed by space to capital letter keys functionality ([#59](https://github.com/scribe-org/Scribe-iOS/issues/59)).
### 🎨 Design Changes
-- Fixes the display of the system header in the app when the user is in dark mode, as the white text was hard to read.
+- Fixes the display of the system header in the app when the user is in dark mode, as the white text was hard to read ([#57](https://github.com/scribe-org/Scribe-iOS/issues/57)).
- Fixes the display of the scroll bar in the app when the user is in dark mode, as the white bar wasn't visually appealing.
-- The keyboard has been made taller for iPhones to make the buttons larger vertically.
-- More space has been added around the buttons to make them better resemble system keyboard spacing.
+- The keyboard has been made taller for iPhones to make the buttons larger vertically ([#63](https://github.com/scribe-org/Scribe-iOS/issues/63)).
+- More space has been added around the buttons to make them better resemble system keyboard spacing ([#63](https://github.com/scribe-org/Scribe-iOS/issues/63)).
### 🐞 Bug Fixes
-- The select keyboard button wouldn't be able to be long held after an initial button is pressed.
-- Canceling a command would cause the command bar to read "Not in directory" on a subsequent command.
-- The double space period shortcut was being triggered without intent.
+- The select keyboard button wouldn't be able to be long held after an initial button is pressed ([#4](https://github.com/scribe-org/Scribe-iOS/issues/4)).
+- Canceling a command would cause the command bar to read "Not in directory" on a subsequent command ([#58](https://github.com/scribe-org/Scribe-iOS/issues/58)).
+- The double space period shortcut was being triggered without intent ([#55](https://github.com/scribe-org/Scribe-iOS/issues/55)).
### ♻️ Code Refactoring
-- The hold-to-select character functions are now combined into one.
+- The hold-to-select character functions are now combined into one ([#5](https://github.com/scribe-org/Scribe-iOS/issues/5)).
# Scribe-iOS 1.0.0
diff --git a/Keyboards/KeyboardsBase/KeyboardViewController.swift b/Keyboards/KeyboardsBase/KeyboardViewController.swift
index 3b1c2484..72dad3fb 100644
--- a/Keyboards/KeyboardsBase/KeyboardViewController.swift
+++ b/Keyboards/KeyboardsBase/KeyboardViewController.swift
@@ -1,8 +1,21 @@
-//
-// KeyboardViewController.swift
-//
-// Classes for the parent keyboard view controller that language keyboards inherit and keyboard keys.
-//
+/**
+ * Classes for the parent keyboard view controller that language keyboards.
+ *
+ * Copyright (C) 2023 Scribe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
import GRDB
import UIKit
@@ -23,26 +36,38 @@ class KeyboardViewController: UIInputViewController {
/// Changes the height of `stackViewNum` depending on device type and size.
func conditionallyShowTopNumbersRow() {
if DeviceType.isPhone {
- view.addConstraint(
- NSLayoutConstraint(
- item: stackViewNum!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0
+ if let stackViewNum = stackViewNum {
+ view.addConstraint(
+ NSLayoutConstraint(
+ item: stackViewNum, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0
+ )
)
- )
+ }
} else if DeviceType.isPad {
// Update the size of the numbers row to add it to the view.
if usingExpandedKeyboard {
- let numbersRowHeight = scribeKey.frame.height
- view.addConstraint(
- NSLayoutConstraint(
- item: stackViewNum!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: numbersRowHeight
+ let numbersRowHeight = scribeKey.frame.height * 1.8
+ if let stackViewNum = stackViewNum {
+ view.addConstraint(
+ NSLayoutConstraint(
+ item: stackViewNum,
+ attribute: .height,
+ relatedBy: .equal,
+ toItem: nil,
+ attribute: .height,
+ multiplier: 1,
+ constant: numbersRowHeight
+ )
)
- )
+ }
} else {
- view.addConstraint(
- NSLayoutConstraint(
- item: stackViewNum!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0
+ if let stackViewNum = stackViewNum {
+ view.addConstraint(
+ NSLayoutConstraint(
+ item: stackViewNum, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0
+ )
)
- )
+ }
}
}
}
@@ -126,15 +151,28 @@ class KeyboardViewController: UIInputViewController {
keyboardHeight = 270
}
} else if DeviceType.isPad {
- if isLandscapeView {
- keyboardHeight = 420
+ // Expanded keyboard on larger iPads can be higher.
+ if UIScreen.main.bounds.width > 768 {
+ if isLandscapeView {
+ keyboardHeight = 430
+ } else {
+ keyboardHeight = 360
+ }
} else {
- keyboardHeight = 340
+ if isLandscapeView {
+ keyboardHeight = 420
+ } else {
+ keyboardHeight = 340
+ }
}
}
+ guard let view = view else {
+ fatalError("The view is nil.")
+ }
+
let heightConstraint = NSLayoutConstraint(
- item: view!,
+ item: view,
attribute: NSLayoutConstraint.Attribute.height,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
@@ -193,10 +231,15 @@ class KeyboardViewController: UIInputViewController {
/// - A call to loadKeys to reload the display after an orientation change
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
- updateViewConstraints()
- isFirstKeyboardLoad = true
- loadKeys()
- isFirstKeyboardLoad = false
+ coordinator.animate(alongsideTransition: { _ in
+ self.updateViewConstraints()
+ self.loadKeys()
+ })
+ Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
+ isFirstKeyboardLoad = true
+ self.loadKeys()
+ isFirstKeyboardLoad = false
+ }
}
/// Overrides the previous color variables if the user switches between light and dark mode.
@@ -209,9 +252,11 @@ class KeyboardViewController: UIInputViewController {
alternatesShapeLayer.removeFromSuperlayer()
}
annotationState = false
- isFirstKeyboardLoad = true
- loadKeys()
- isFirstKeyboardLoad = false
+ Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in
+ isFirstKeyboardLoad = true
+ self.loadKeys()
+ isFirstKeyboardLoad = false
+ }
}
// MARK: Scribe Command Elements
@@ -322,11 +367,16 @@ class KeyboardViewController: UIInputViewController {
/// - Parameters
/// - word: the word for which corresponding emojis should be shown for.
func getEmojiAutoSuggestions(for word: String) {
- let emojisToDisplay = LanguageDBManager.shared.queryEmojis(of: word)
- if emojisToDisplay[0] != "" {
+ let query = "SELECT * FROM emoji_keywords WHERE word = ?"
+ let args = [word.lowercased()]
+ let outputCols = ["emoji_0", "emoji_1", "emoji_2"]
+ let emojisToDisplay = queryDBRow(query: query, outputCols: outputCols, args: args)
+
+ if !emojisToDisplay[0].isEmpty {
emojisToDisplayArray = [String]()
currentEmojiTriggerWord = word.lowercased()
- if emojisToDisplay[2] != "" && DeviceType.isPad {
+
+ if !emojisToDisplay[2].isEmpty && DeviceType.isPad {
for i in 0 ..< 3 {
emojisToDisplayArray.append(emojisToDisplay[i])
}
@@ -341,7 +391,7 @@ class KeyboardViewController: UIInputViewController {
padEmojiDivider1.backgroundColor = UIColor(cgColor: commandBarBorderColor)
}
conditionallyHideEmojiDividers()
- } else if emojisToDisplay[1] != "" {
+ } else if !emojisToDisplay[1].isEmpty {
for i in 0 ..< 2 {
emojisToDisplayArray.append(emojisToDisplay[i])
}
@@ -365,7 +415,7 @@ class KeyboardViewController: UIInputViewController {
/// Generates an array of the three autocomplete words.
func getAutocompletions() {
completionWords = [" ", " ", " "]
- if proxy.documentContextBeforeInput?.count != 0 {
+ if let documentContext = proxy.documentContextBeforeInput, !documentContext.isEmpty {
if let inString = proxy.documentContextBeforeInput {
// To only focus on the current word as prefix in autocomplete.
currentPrefix = inString.replacingOccurrences(of: pastStringInTextProxy, with: "")
@@ -392,16 +442,17 @@ class KeyboardViewController: UIInputViewController {
// Trigger autocompletions for selected text instead.
if proxy.selectedText != nil && [.idle, .selectCommand, .alreadyPlural, .invalid].contains(commandState) {
- currentPrefix = proxy.selectedText!
+ if let selectedText = proxy.selectedText {
+ currentPrefix = selectedText
+ }
}
// Get options for completion that start with the current prefix and are not just one letter.
- let completionOptions = LanguageDBManager.shared.queryAutocompletions(word: currentPrefix)
+ let completionOptions = queryAutocompletions(word: currentPrefix)
- if completionOptions[0] != "" {
- var i = 0
+ if !completionOptions[0].isEmpty {
if completionOptions.count <= 3 {
- while i < completionOptions.count {
+ for i in 0 ..< completionOptions.count {
if shiftButtonState == .shift {
completionWords[i] = completionOptions[i].capitalize()
} else if capsLockButtonState == .locked {
@@ -415,10 +466,9 @@ class KeyboardViewController: UIInputViewController {
} else {
completionWords[i] = completionOptions[i]
}
- i += 1
}
} else {
- while i < 3 {
+ for i in 0 ..< 3 {
if shiftButtonState == .shift {
completionWords[i] = completionOptions[i].capitalize()
} else if capsLockButtonState == .locked {
@@ -432,7 +482,6 @@ class KeyboardViewController: UIInputViewController {
} else {
completionWords[i] = completionOptions[i]
}
- i += 1
}
}
}
@@ -456,38 +505,39 @@ class KeyboardViewController: UIInputViewController {
let prefix = proxy.documentContextBeforeInput?.components(separatedBy: " ").secondToLast() ?? ""
completionWords = [String]()
- var i = 0
- while i < 3 {
+ let query = "SELECT * FROM verbs WHERE verb = ?"
+ for i in 0 ..< 3 {
// Get conjugations of the preselected verbs.
- let outputCols = [pronounAutosuggestionTenses[prefix.lowercased()]!]
- var suggestion = LanguageDBManager.shared.queryVerb(of: verbsAfterPronounsArray[i], with: outputCols)[0]
- if suggestion == "" {
- suggestion = verbsAfterPronounsArray[i]
- }
+ let args = [verbsAfterPronounsArray[i]]
+ if let tense = pronounAutosuggestionTenses[prefix.lowercased()] {
+ let outputCols = [tense]
+ var suggestion = queryDBRow(query: query, outputCols: outputCols, args: args)[0]
+ if suggestion == "" {
+ suggestion = verbsAfterPronounsArray[i]
+ }
- if suggestion == "REFLEXIVE_PRONOUN" && controllerLanguage == "Spanish" {
- suggestion = getESReflexivePronoun(pronoun: prefix.lowercased())
- }
- if shiftButtonState == .shift {
- completionWords.append(suggestion.capitalize())
- } else if capsLockButtonState == .locked {
- completionWords.append(suggestion.uppercased())
- } else {
- completionWords.append(suggestion)
+ if suggestion == "REFLEXIVE_PRONOUN" && controllerLanguage == "Spanish" {
+ suggestion = getESReflexivePronoun(pronoun: prefix.lowercased())
+ }
+ if shiftButtonState == .shift {
+ completionWords.append(suggestion.capitalize())
+ } else if capsLockButtonState == .locked {
+ completionWords.append(suggestion.uppercased())
+ } else {
+ completionWords.append(suggestion)
+ }
}
- i += 1
}
}
/// Generates an array of three words that serve as baseline autosuggestions.
func getDefaultAutosuggestions() {
- var i = 0
completionWords = [String]()
- if allowUndo {
- completionWords.append(previousWord)
- i += 1
- }
- while i < 3 {
+ for i in 0 ..< 3 {
+ if allowUndo {
+ completionWords.append(previousWord)
+ continue
+ }
if shiftButtonState == .shift {
completionWords.append(baseAutosuggestions[i].capitalize())
} else if capsLockButtonState == .locked {
@@ -495,7 +545,6 @@ class KeyboardViewController: UIInputViewController {
} else {
completionWords.append(baseAutosuggestions[i])
}
- i += 1
}
}
@@ -518,7 +567,9 @@ class KeyboardViewController: UIInputViewController {
// Trigger autocompletions for selected text instead.
if proxy.selectedText != nil && [.idle, .selectCommand, .alreadyPlural, .invalid].contains(commandState) {
- prefix = proxy.selectedText!
+ if let selectedText = proxy.selectedText {
+ prefix = selectedText
+ }
}
if prefix.isNumeric {
@@ -528,40 +579,45 @@ class KeyboardViewController: UIInputViewController {
} else {
// We have to consider these different cases as the key always has to match.
// Else, even if the lowercased prefix is present in the dictionary, if the actual prefix isn't present we won't get an output.
-
- let suggestionsLowerCasePrefix = LanguageDBManager.shared.queryAutosuggestions(of: prefix)
- let suggestionsCapitalizedPrefix = LanguageDBManager.shared.queryAutosuggestions(of: prefix)
- if suggestionsLowerCasePrefix[0] != "" {
+ let query = "SELECT * FROM autosuggestions WHERE word = ?"
+ let argsLower = [prefix.lowercased()]
+ let argsCapitalize = [prefix.capitalized]
+ let outputCols = ["suggestion_0", "suggestion_1", "suggestion_2"]
+
+ let suggestionsLowerCasePrefix = queryDBRow(query: query, outputCols: outputCols, args: argsLower)
+ let suggestionsCapitalizedPrefix = queryDBRow(query: query, outputCols: outputCols, args: argsCapitalize)
+ if !suggestionsLowerCasePrefix[0].isEmpty {
completionWords = [String]()
- var i = 0
- if allowUndo {
- completionWords.append(previousWord)
- i += 1
- }
- while i < 3 {
+ for i in 0 ..< 3 {
+ if allowUndo {
+ completionWords.append(previousWord)
+ continue
+ }
if shiftButtonState == .shift {
completionWords.append(suggestionsLowerCasePrefix[i].capitalize())
} else if capsLockButtonState == .locked {
completionWords.append(suggestionsLowerCasePrefix[i].uppercased())
} else {
- let nounForm = LanguageDBManager.shared.queryNounForm(of: suggestionsLowerCasePrefix[i])[0]
- hasNounForm = nounForm != ""
+ let nounGenderQuery = "SELECT * FROM nouns WHERE noun = ?"
+ let nounGenderArgs = [suggestionsLowerCasePrefix[i]]
+ let outputCols = ["form"]
+
+ let nounForm = queryDBRow(query: nounGenderQuery, outputCols: outputCols, args: nounGenderArgs)[0]
+ hasNounForm = !nounForm.isEmpty
if !hasNounForm {
completionWords.append(suggestionsLowerCasePrefix[i].lowercased())
} else {
completionWords.append(suggestionsLowerCasePrefix[i])
}
}
- i += 1
}
- } else if suggestionsCapitalizedPrefix[0] != "" {
+ } else if !suggestionsCapitalizedPrefix[0].isEmpty {
completionWords = [String]()
- var i = 0
- if allowUndo {
- completionWords.append(previousWord)
- i += 1
- }
- while i < 3 {
+ for i in 0 ..< 3 {
+ if allowUndo {
+ completionWords.append(previousWord)
+ continue
+ }
if shiftButtonState == .shift {
completionWords.append(suggestionsCapitalizedPrefix[i].capitalize())
} else if capsLockButtonState == .locked {
@@ -569,7 +625,6 @@ class KeyboardViewController: UIInputViewController {
} else {
completionWords.append(suggestionsCapitalizedPrefix[i])
}
- i += 1
}
} else {
getDefaultAutosuggestions()
@@ -695,9 +750,9 @@ class KeyboardViewController: UIInputViewController {
radius: commandKeyCornerRadius
)
if DeviceType.isPhone {
- pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.435)
+ pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarFontPhone)
} else if DeviceType.isPad {
- pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
+ pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarFontPad)
}
activateBtn(btn: pluralKey)
@@ -721,11 +776,11 @@ class KeyboardViewController: UIInputViewController {
styleBtn(btn: phoneEmojiKey1, title: emojisToDisplayArray[1], radius: commandKeyCornerRadius)
if DeviceType.isPhone {
- phoneEmojiKey0.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.435)
- phoneEmojiKey1.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.435)
+ phoneEmojiKey0.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarFontPhone)
+ phoneEmojiKey1.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarFontPhone)
} else if DeviceType.isPad {
- phoneEmojiKey0.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
- phoneEmojiKey1.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
+ phoneEmojiKey0.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarFontPad)
+ phoneEmojiKey1.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarFontPad)
}
activateBtn(btn: phoneEmojiKey0)
@@ -740,9 +795,9 @@ class KeyboardViewController: UIInputViewController {
styleBtn(btn: padEmojiKey1, title: emojisToDisplayArray[1], radius: commandKeyCornerRadius)
styleBtn(btn: padEmojiKey2, title: emojisToDisplayArray[2], radius: commandKeyCornerRadius)
- padEmojiKey0.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
- padEmojiKey1.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
- padEmojiKey2.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
+ padEmojiKey0.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarEmojiKeyFont)
+ padEmojiKey1.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarEmojiKeyFont)
+ padEmojiKey2.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarEmojiKeyFont)
activateBtn(btn: padEmojiKey0)
activateBtn(btn: padEmojiKey1)
@@ -765,12 +820,17 @@ class KeyboardViewController: UIInputViewController {
/// Note: the completion is appended after the typed text if this is not ran.
func clearPrefixFromTextFieldProxy() {
// Only delete characters for autocomplete, not autosuggest.
- if currentPrefix != "" && autoActionState != .suggest {
- if proxy.documentContextBeforeInput?.count != 0 {
- for _ in 0 ..< currentPrefix.count {
- proxy.deleteBackward()
- }
- }
+ guard !currentPrefix.isEmpty, autoActionState != .suggest else {
+ return
+ }
+
+ guard let documentContext = proxy.documentContextBeforeInput, !documentContext.isEmpty else {
+ return
+ }
+
+ // Delete characters in text proxy.
+ for _ in 0 ..< currentPrefix.count {
+ proxy.deleteBackward()
}
}
@@ -842,14 +902,11 @@ class KeyboardViewController: UIInputViewController {
func handleDeleteButtonPressed() {
if [.idle, .selectCommand, .alreadyPlural, .invalid].contains(commandState) {
proxy.deleteBackward()
- } else if [.translate, .conjugate, .plural].contains(commandState) && !(allPrompts.contains(commandBar.text!) || allColoredPrompts.contains(commandBar.attributedText!)) {
- guard
- let inputText = commandBar.text,
- !inputText.isEmpty
- else {
+ } else if [.translate, .conjugate, .plural].contains(commandState) && !(allPrompts.contains(commandBar.text ?? "") || allColoredPrompts.contains(commandBar.attributedText ?? NSAttributedString())) {
+ guard let inputText = commandBar.text, !inputText.isEmpty else {
return
}
- commandBar.text = commandBar.text!.deletePriorToCursor()
+ commandBar.text = inputText.deletePriorToCursor()
} else {
backspaceTimer?.invalidate()
backspaceTimer = nil
@@ -1355,8 +1412,8 @@ class KeyboardViewController: UIInputViewController {
/// Assign the verb conjugations that will be selectable in the conjugation display.
func assignVerbConjStates() {
var conjugationStateFxn: () -> String = deGetConjugationState
- if controllerLanguage != "Swedish" {
- conjugationStateFxn = keyboardConjStateDict[controllerLanguage] as! () -> String
+ if let conjugationFxn = keyboardConjStateDict[controllerLanguage] as? () -> String {
+ conjugationStateFxn = conjugationFxn
}
if !["Russian", "Swedish"].contains(controllerLanguage) {
@@ -1366,7 +1423,6 @@ class KeyboardViewController: UIInputViewController {
formFPP = conjugationStateFxn() + "FPP"
formSPP = conjugationStateFxn() + "SPP"
formTPP = conjugationStateFxn() + "TPP"
-
} else if controllerLanguage == "Russian" {
if formsDisplayDimensions == .view3x2 {
formFPS = ruGetConjugationState() + "FPS"
@@ -1381,7 +1437,6 @@ class KeyboardViewController: UIInputViewController {
formBottomLeft = "pastNeutral"
formBottomRight = "pastPlural"
}
-
} else if controllerLanguage == "Swedish" {
let swedishTenses = svGetConjugationState()
@@ -1400,9 +1455,11 @@ class KeyboardViewController: UIInputViewController {
// Set the view title and its labels.
var conjugationTitleFxn: () -> String = deGetConjugationTitle
var conjugationLabelsFxn: () -> Void = deSetConjugationLabels
- if controllerLanguage != "Swedish" {
- conjugationTitleFxn = keyboardConjTitleDict[controllerLanguage] as! () -> String
- conjugationLabelsFxn = keyboardConjLabelDict[controllerLanguage] as! () -> Void
+ if let titleFxn = keyboardConjTitleDict[controllerLanguage] as? () -> String {
+ conjugationTitleFxn = titleFxn
+ }
+ if let labelsFxn = keyboardConjLabelDict[controllerLanguage] as? () -> Void {
+ conjugationLabelsFxn = labelsFxn
}
if !["Russian", "Swedish"].contains(controllerLanguage) {
@@ -1439,8 +1496,10 @@ class KeyboardViewController: UIInputViewController {
}
// Populate conjugation view buttons.
+ let query = "SELECT * FROM verbs WHERE verb = ?"
+ let args = [verbToConjugate]
let outputCols = allConjugations
- let conjugationsToDisplay = LanguageDBManager.shared.queryVerb(of: verbConjugated, with: outputCols)
+ let conjugationsToDisplay = queryDBRow(query: query, outputCols: outputCols, args: args)
for index in 0 ..< allConjugations.count {
if conjugationsToDisplay[index] == "" {
// Assign the invalid message if the conjugation isn't present in the directory.
@@ -1525,12 +1584,276 @@ class KeyboardViewController: UIInputViewController {
}
}
+ func setKeywidth() {
+ // keyWidth determined per keyboard by the top row.
+ if isLandscapeView {
+ if DeviceType.isPhone {
+ letterKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(letterKeys[0].count) * scalarLetterNumSymKeyWidthLandscapeViewPhone
+ numSymKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(numberKeys[0].count) * scalarLetterNumSymKeyWidthLandscapeViewPhone
+ } else if DeviceType.isPad {
+ letterKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(letterKeys[0].count) * scalarLetterNumSymKeyWidthLandscapeViewPad
+ if !usingExpandedKeyboard {
+ numSymKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(numberKeys[0].count) * scalarLetterNumSymKeyWidthLandscapeViewPad
+ }
+ }
+ } else {
+ letterKeyWidth = (UIScreen.main.bounds.width - 6) / CGFloat(letterKeys[0].count) * scalarLetterNumSymKeyWidth
+ numSymKeyWidth = (UIScreen.main.bounds.width - 6) / CGFloat(symbolKeys[0].count) * scalarLetterNumSymKeyWidth
+ }
+ }
+
+ func setKeyPadding() {
+ let numRows = keyboard.count
+ for row in 0 ..< numRows {
+ for idx in 0 ..< keyboard[row].count {
+ // Set up button as a key with its values and properties.
+ let btn = KeyboardKey(type: .custom)
+ btn.row = row
+ btn.idx = idx
+ btn.style()
+ btn.setChar()
+ btn.setCharSize()
+
+ let key: String = btn.key
+
+ // Pad before key is added.
+ var leftPadding = CGFloat(0)
+ if DeviceType.isPhone
+ && key == "y"
+ && ["German", "Swedish"].contains(controllerLanguage)
+ && commandState != .translate
+ {
+ leftPadding = keyWidth / 3
+ addPadding(to: stackView2, width: leftPadding, key: "y")
+ }
+ if DeviceType.isPhone
+ && key == "a"
+ && (controllerLanguage == "Portuguese"
+ || controllerLanguage == "Italian"
+ || commandState == .translate)
+ {
+ leftPadding = keyWidth / 4
+ addPadding(to: stackView1, width: leftPadding, key: "a")
+ }
+ if DeviceType.isPad
+ && key == "a"
+ && !usingExpandedKeyboard
+ && (controllerLanguage == "Portuguese"
+ || controllerLanguage == "Italian"
+ || commandState == .translate)
+ {
+ leftPadding = keyWidth / 3
+ addPadding(to: stackView1, width: leftPadding, key: "a")
+ }
+ if DeviceType.isPad
+ && key == "@"
+ && !usingExpandedKeyboard
+ && (controllerLanguage == "Portuguese"
+ || controllerLanguage == "Italian"
+ || commandState == .translate)
+ {
+ leftPadding = keyWidth / 3
+ addPadding(to: stackView1, width: leftPadding, key: "@")
+ }
+ if DeviceType.isPad
+ && key == "€"
+ && !usingExpandedKeyboard
+ && (controllerLanguage == "Portuguese"
+ || commandState == .translate)
+ {
+ leftPadding = keyWidth / 3
+ addPadding(to: stackView1, width: leftPadding, key: "€")
+ }
+
+ keyboardKeys.append(btn)
+ if !usingExpandedKeyboard {
+ switch row {
+ case 0: stackView0.addArrangedSubview(btn)
+ case 1: stackView1.addArrangedSubview(btn)
+ case 2: stackView2.addArrangedSubview(btn)
+ case 3: stackView3.addArrangedSubview(btn)
+ default:
+ break
+ }
+ } else {
+ switch row {
+ case 0: stackViewNum.addArrangedSubview(btn)
+ case 1: stackView0.addArrangedSubview(btn)
+ case 2: stackView1.addArrangedSubview(btn)
+ case 3: stackView2.addArrangedSubview(btn)
+ case 4: stackView3.addArrangedSubview(btn)
+ default:
+ break
+ }
+ }
+
+ // Special key styling.
+ if key == "delete" {
+ let deleteLongPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(deleteLongPressed(_:)))
+ btn.addGestureRecognizer(deleteLongPressRecognizer)
+ }
+
+ if key == "selectKeyboard" {
+ selectKeyboardButton = btn
+ selectKeyboardButton.addTarget(
+ self,
+ action: #selector(handleInputModeList(from:with:)),
+ for: .allTouchEvents
+ )
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "globe")
+ }
+
+ if key == "hideKeyboard" {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "keyboard.chevron.compact.down")
+ }
+
+ if key == SpecialKeys.indent {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "arrow.forward.to.line")
+ }
+
+ if key == SpecialKeys.capsLock {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "capslock")
+ }
+
+ if key == "shift" {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "shift")
+ }
+
+ if key == "return" {
+ if [.translate, .conjugate, .plural].contains(commandState) {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "arrowtriangle.right.fill")
+ } else {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "arrow.turn.down.left")
+ }
+ }
+
+ if key == "delete" {
+ styleIconBtn(btn: btn, color: keyCharColor, iconName: "delete.left")
+ }
+
+ // Setting key pop functionality.
+ let keyHoldPop = UILongPressGestureRecognizer(
+ target: self,
+ action: #selector(genHoldPopUpView(sender:))
+ )
+ keyHoldPop.minimumPressDuration = 0.125
+
+ if allNonSpecialKeys.contains(key) {
+ btn.addTarget(self, action: #selector(genPopUpView), for: .touchDown)
+ btn.addGestureRecognizer(keyHoldPop)
+ }
+
+ // Pad after key is added.
+ var rightPadding = CGFloat(0)
+ if DeviceType.isPhone
+ && key == "m"
+ && ["German", "Swedish"].contains(controllerLanguage)
+ && commandState != .translate
+ {
+ rightPadding = keyWidth / 3
+ addPadding(to: stackView2, width: rightPadding, key: "m")
+ }
+ if DeviceType.isPhone
+ && key == "l"
+ && (controllerLanguage == "Portuguese"
+ || controllerLanguage == "Italian"
+ || commandState == .translate)
+ {
+ rightPadding = keyWidth / 4
+ addPadding(to: stackView1, width: rightPadding, key: "l")
+ }
+
+ // Set the width of the key given device and the given key.
+ btn.adjustKeyWidth()
+
+ // Update the button style.
+ btn.adjustButtonStyle()
+
+ if key == "return" && proxy.keyboardType == .webSearch && ![.translate, .conjugate, .plural].contains(commandState) {
+ // Override background color from adjustKeyWidth for "search" blue for web searches.
+ styleIconBtn(btn: btn, color: .white.withAlphaComponent(0.9), iconName: "arrow.turn.down.left")
+ btn.backgroundColor = UIColor(red: 0.0 / 255.0, green: 121.0 / 255.0, blue: 251.0 / 255.0, alpha: 1.0)
+ }
+
+ // Extend button touch areas.
+ var widthOfSpacing = CGFloat(0)
+ if keyboardState == .letters {
+ widthOfSpacing = (
+ (UIScreen.main.bounds.width - 6.0)
+ - (CGFloat(letterKeys[0].count) * keyWidth)
+ ) / (CGFloat(letterKeys[0].count)
+ - 1.0
+ )
+ } else {
+ widthOfSpacing = (
+ (UIScreen.main.bounds.width - 6.0)
+ - (CGFloat(usingExpandedKeyboard == true ? symbolKeys[0].count : numberKeys[0].count) * numSymKeyWidth)
+ ) / (CGFloat(letterKeys[0].count)
+ - 1.0
+ )
+ }
+
+ switch row {
+ case 0:
+ btn.topShift = -5
+ btn.bottomShift = -6
+ case 1:
+ btn.topShift = -6
+ btn.bottomShift = -6
+ case 2:
+ btn.topShift = -6
+ btn.bottomShift = -6
+ case 3:
+ btn.topShift = -6
+ btn.bottomShift = -5
+ default:
+ break
+ }
+
+ // Pad left and right based on if the button has been shifted.
+ if leftPadding == CGFloat(0) {
+ btn.leftShift = -(widthOfSpacing / 2)
+ } else {
+ btn.leftShift = -leftPadding
+ }
+ if rightPadding == CGFloat(0) {
+ btn.rightShift = -(widthOfSpacing / 2)
+ } else {
+ btn.rightShift = -rightPadding
+ }
+
+ // Activate keyboard interface buttons.
+ activateBtn(btn: btn)
+ if key == "shift" || key == spaceBar || key == languageTextForSpaceBar {
+ btn.addTarget(self, action: #selector(keyMultiPress(_:event:)), for: .touchDownRepeat)
+ }
+ }
+ }
+
+ // End padding.
+ switch keyboardState {
+ case .letters:
+ break
+ case .numbers:
+ break
+ case .symbols:
+ break
+ }
+ }
+
// MARK: Load Keys
/// Loads the keys given the current constraints.
func loadKeys() {
// The name of the language keyboard that's referencing KeyboardViewController.
controllerLanguage = classForCoder.description().components(separatedBy: ".KeyboardViewController")[0]
+ if let userDefaults = UserDefaults(suiteName: "group.scribe.userDefaultsContainer") {
+ if userDefaults.bool(forKey: "svAccentCharacters") {
+ disableAccentCharacters = true
+ } else {
+ disableAccentCharacters = false
+ }
+ }
// Actions to be done only on initial loads.
if isFirstKeyboardLoad {
@@ -1557,19 +1880,45 @@ class KeyboardViewController: UIInputViewController {
showKeyboardLanguage = true
// Initialize the language database and create the autosuggestions lexicon.
- //languageDB = openDBQueue()
+ languageDB = openDBQueue()
// Add UILexicon words including unpaired first and last names from Contacts to autocompletions.
- requestSupplementaryLexicon { (userLexicon: UILexicon!) in
- for item in userLexicon.entries {
- if item.documentText.count > 1 {
- LanguageDBManager.shared.insertAutocompleteLexion(of: item.documentText)
+ let addToAutocompleteLexiconQuery = "INSERT OR IGNORE INTO autocomplete_lexicon (word) VALUES (?)"
+ requestSupplementaryLexicon { (userLexicon: UILexicon?) in
+ if let lexicon = userLexicon {
+ for item in lexicon.entries {
+ if item.documentText.count > 1 {
+ writeDBRow(query: addToAutocompleteLexiconQuery, args: [item.documentText])
+ }
}
}
}
// Drop non-unique values in case the lexicon has added words that were already present.
- LanguageDBManager.shared.deleteNonUniqueAutosuggestions()
+ let dropNonUniqueAutosuggestionsQuery = """
+ DELETE FROM autocomplete_lexicon
+ WHERE rowid NOT IN (
+ SELECT
+ MIN(rowid)
+
+ FROM
+ autocomplete_lexicon
+
+ GROUP BY
+ word
+ )
+ """
+ do {
+ try languageDB.write { db in
+ try db.execute(sql: dropNonUniqueAutosuggestionsQuery)
+ }
+ } catch let error as DatabaseError {
+ let errorMessage = error.message
+ let errorSQL = error.sql
+ print(
+ "An error '\(String(describing: errorMessage))' occurred in the query: \(String(describing: errorSQL))"
+ )
+ } catch {}
}
setKeyboard()
@@ -1590,32 +1939,23 @@ class KeyboardViewController: UIInputViewController {
keyboardKeys.forEach { $0.removeFromSuperview() }
paddingViews.forEach { $0.removeFromSuperview() }
- // keyWidth determined per keyboard by the top row.
- if isLandscapeView {
- if DeviceType.isPhone {
- letterKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(letterKeys[0].count) * 1.5
- numSymKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(numberKeys[0].count) * 1.5
- } else if DeviceType.isPad {
- letterKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(letterKeys[0].count) * 1.2
- numSymKeyWidth = (UIScreen.main.bounds.height - 5) / CGFloat(numberKeys[0].count) * 1.2
- }
- } else {
- letterKeyWidth = (UIScreen.main.bounds.width - 6) / CGFloat(letterKeys[0].count) * 0.9
- numSymKeyWidth = (UIScreen.main.bounds.width - 6) / CGFloat(symbolKeys[0].count) * 0.9
- }
+ setKeywidth()
// Derive keyboard given current states and set widths.
switch keyboardState {
case .letters:
keyboard = letterKeys
keyWidth = letterKeyWidth
+
// Auto-capitalization if the cursor is at the start of the proxy.
- if proxy.documentContextBeforeInput?.count == 0 {
+ if let documentContext = proxy.documentContextBeforeInput, documentContext.isEmpty {
shiftButtonState = .shift
}
+
case .numbers:
keyboard = numberKeys
keyWidth = numSymKeyWidth
+
case .symbols:
keyboard = symbolKeys
keyWidth = numSymKeyWidth
@@ -1624,19 +1964,19 @@ class KeyboardViewController: UIInputViewController {
// Derive corner radii.
if DeviceType.isPhone {
if isLandscapeView {
- keyCornerRadius = keyWidth / 9
- commandKeyCornerRadius = keyWidth / 5
+ keyCornerRadius = keyWidth / scalarKeyCornerRadiusLandscapeViewPhone
+ commandKeyCornerRadius = keyWidth / scalarCommandKeyCornerRadiusLandscapeViewPhone
} else {
- keyCornerRadius = keyWidth / 6
- commandKeyCornerRadius = keyWidth / 3
+ keyCornerRadius = keyWidth / scalarKeyCornerRadiusPhone
+ commandKeyCornerRadius = keyWidth / scalarCommandKeyCornerRadiusPhone
}
} else if DeviceType.isPad {
if isLandscapeView {
- keyCornerRadius = keyWidth / 12
- commandKeyCornerRadius = keyWidth / 7.5
+ keyCornerRadius = keyWidth / scalarKeyCornerRadiusLandscapeViewPad
+ commandKeyCornerRadius = keyWidth / scalarCommandKeyCornerRadiusLandscapeViewPad
} else {
- keyCornerRadius = keyWidth / 9
- commandKeyCornerRadius = keyWidth / 5
+ keyCornerRadius = keyWidth / scalarKeyCornerRadiusPad
+ commandKeyCornerRadius = keyWidth / scalarCommandKeyCornerRadiusPad
}
}
@@ -1648,16 +1988,30 @@ class KeyboardViewController: UIInputViewController {
view?.isLayoutMarginsRelativeArrangement = true
// Set edge insets for stack views to provide vertical key spacing.
- if view == stackViewNum {
- view?.layoutMargins = UIEdgeInsets(top: 3, left: 0, bottom: 4, right: 0)
- } else if view == stackView0 {
- view?.layoutMargins = UIEdgeInsets(top: 3, left: 0, bottom: 8, right: 0)
- } else if view == stackView1 {
- view?.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 6, right: 0)
- } else if view == stackView2 {
- view?.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 6, right: 0)
- } else if view == stackView3 {
- view?.layoutMargins = UIEdgeInsets(top: 6, left: 0, bottom: 5, right: 0)
+ if DeviceType.isPad {
+ if view == stackViewNum {
+ view?.layoutMargins = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)
+ } else if view == stackView0 {
+ view?.layoutMargins = UIEdgeInsets(top: 2, left: 0, bottom: 6, right: 0)
+ } else if view == stackView1 {
+ view?.layoutMargins = UIEdgeInsets(top: 3, left: 0, bottom: 4, right: 0)
+ } else if view == stackView2 {
+ view?.layoutMargins = UIEdgeInsets(top: 3, left: 0, bottom: 4, right: 0)
+ } else if view == stackView3 {
+ view?.layoutMargins = UIEdgeInsets(top: 4, left: 0, bottom: 3, right: 0)
+ }
+ } else {
+ if view == stackViewNum {
+ view?.layoutMargins = UIEdgeInsets(top: 3, left: 0, bottom: 4, right: 0)
+ } else if view == stackView0 {
+ view?.layoutMargins = UIEdgeInsets(top: 3, left: 0, bottom: 8, right: 0)
+ } else if view == stackView1 {
+ view?.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 6, right: 0)
+ } else if view == stackView2 {
+ view?.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 6, right: 0)
+ } else if view == stackView3 {
+ view?.layoutMargins = UIEdgeInsets(top: 6, left: 0, bottom: 5, right: 0)
+ }
}
}
@@ -1671,13 +2025,13 @@ class KeyboardViewController: UIInputViewController {
deactivateConjugationDisplay()
if DeviceType.isPhone {
- translateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.435)
- conjugateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.435)
- pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.435)
+ translateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarCommandKeyHeightPhone)
+ conjugateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarCommandKeyHeightPhone)
+ pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarCommandKeyHeightPhone)
} else if DeviceType.isPad {
- translateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
- conjugateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
- pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * 0.475)
+ translateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarCommandKeyHeightPad)
+ conjugateKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarCommandKeyHeightPad)
+ pluralKey.titleLabel?.font = .systemFont(ofSize: scribeKey.frame.height * scalarCommandKeyHeightPad)
}
if commandState == .selectCommand {
@@ -1731,246 +2085,7 @@ class KeyboardViewController: UIInputViewController {
}
}
- let numRows = keyboard.count
- for row in 0 ..< numRows {
- for idx in 0 ..< keyboard[row].count {
- // Set up button as a key with its values and properties.
- let btn = KeyboardKey(type: .custom)
- btn.row = row
- btn.idx = idx
- btn.style()
- btn.setChar()
- btn.setCharSize()
-
- let key: String = btn.key
-
- // Pad before key is added.
- var leftPadding = CGFloat(0)
- if DeviceType.isPhone
- && key == "y"
- && ["German", "Swedish"].contains(controllerLanguage)
- && commandState != .translate
- {
- leftPadding = keyWidth / 3
- addPadding(to: stackView2, width: leftPadding, key: "y")
- }
- if DeviceType.isPhone
- && key == "a"
- && (controllerLanguage == "Portuguese"
- || controllerLanguage == "Italian"
- || commandState == .translate)
- {
- leftPadding = keyWidth / 4
- addPadding(to: stackView1, width: leftPadding, key: "a")
- }
- if DeviceType.isPad
- && key == "a"
- && (controllerLanguage == "Portuguese"
- || controllerLanguage == "Italian"
- || commandState == .translate)
- {
- leftPadding = keyWidth / 3
- addPadding(to: stackView1, width: leftPadding, key: "a")
- }
- if DeviceType.isPad
- && key == "@"
- && (controllerLanguage == "Portuguese"
- || controllerLanguage == "Italian"
- || commandState == .translate)
- {
- leftPadding = keyWidth / 3
- addPadding(to: stackView1, width: leftPadding, key: "@")
- }
- if DeviceType.isPad
- && key == "$"
- && controllerLanguage == "Italian"
- {
- leftPadding = keyWidth / 3
- addPadding(to: stackView1, width: leftPadding, key: "$")
- }
- if DeviceType.isPad
- && key == "€"
- && (controllerLanguage == "Portuguese"
- || commandState == .translate)
- {
- leftPadding = keyWidth / 3
- addPadding(to: stackView1, width: leftPadding, key: "€")
- }
-
- keyboardKeys.append(btn)
- if !usingExpandedKeyboard {
- switch row {
- case 0: stackView0.addArrangedSubview(btn)
- case 1: stackView1.addArrangedSubview(btn)
- case 2: stackView2.addArrangedSubview(btn)
- case 3: stackView3.addArrangedSubview(btn)
- default:
- break
- }
- } else {
- switch row {
- case 0: stackViewNum.addArrangedSubview(btn)
- case 1: stackView0.addArrangedSubview(btn)
- case 2: stackView1.addArrangedSubview(btn)
- case 3: stackView2.addArrangedSubview(btn)
- case 4: stackView3.addArrangedSubview(btn)
- default:
- break
- }
- }
-
- // Special key styling.
- if key == "delete" {
- let deleteLongPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(deleteLongPressed(_:)))
- btn.addGestureRecognizer(deleteLongPressRecognizer)
- }
-
- if key == "selectKeyboard" {
- selectKeyboardButton = btn
- selectKeyboardButton.addTarget(
- self,
- action: #selector(handleInputModeList(from:with:)),
- for: .allTouchEvents
- )
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "globe")
- }
-
- if key == "hideKeyboard" {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "keyboard.chevron.compact.down")
- }
-
- if key == SpecialKeys.indent {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "arrow.forward.to.line")
- }
-
- if key == SpecialKeys.capsLock {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "capslock")
- }
-
- if key == "shift" {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "shift")
- }
-
- if key == "return" {
- if [.translate, .conjugate, .plural].contains(commandState) {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "arrowtriangle.right.fill")
- } else {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "arrow.turn.down.left")
- }
- }
-
- if key == "delete" {
- styleIconBtn(btn: btn, color: keyCharColor, iconName: "delete.left")
- }
-
- // Setting key pop functionality.
- let keyHoldPop = UILongPressGestureRecognizer(
- target: self,
- action: #selector(genHoldPopUpView(sender:))
- )
- keyHoldPop.minimumPressDuration = 0.125
-
- if allNonSpecialKeys.contains(key) {
- btn.addTarget(self, action: #selector(genPopUpView), for: .touchDown)
- btn.addGestureRecognizer(keyHoldPop)
- }
-
- // Pad after key is added.
- var rightPadding = CGFloat(0)
- if DeviceType.isPhone
- && key == "m"
- && ["German", "Swedish"].contains(controllerLanguage)
- && commandState != .translate
- {
- rightPadding = keyWidth / 3
- addPadding(to: stackView2, width: rightPadding, key: "m")
- }
- if DeviceType.isPhone
- && key == "l"
- && (controllerLanguage == "Portuguese"
- || controllerLanguage == "Italian"
- || commandState == .translate)
- {
- rightPadding = keyWidth / 4
- addPadding(to: stackView1, width: rightPadding, key: "l")
- }
-
- // Set the width of the key given device and the given key.
- btn.adjustKeyWidth()
-
- // Update the button style.
- btn.adjustButtonStyle()
-
- if key == "return" && proxy.keyboardType == .webSearch && ![.translate, .conjugate, .plural].contains(commandState) {
- // Override background color from adjustKeyWidth for "search" blue for web searches.
- styleIconBtn(btn: btn, color: .white.withAlphaComponent(0.9), iconName: "arrow.turn.down.left")
- btn.backgroundColor = UIColor(red: 0.0 / 255.0, green: 121.0 / 255.0, blue: 251.0 / 255.0, alpha: 1.0)
- }
-
- // Extend button touch areas.
- var widthOfSpacing = CGFloat(0)
- if keyboardState == .letters {
- widthOfSpacing = (
- (UIScreen.main.bounds.width - 6.0)
- - (CGFloat(letterKeys[0].count) * keyWidth)
- ) / (CGFloat(letterKeys[0].count)
- - 1.0
- )
- } else {
- widthOfSpacing = (
- (UIScreen.main.bounds.width - 6.0)
- - (CGFloat(usingExpandedKeyboard == true ? symbolKeys[0].count : numberKeys[0].count) * numSymKeyWidth)
- ) / (CGFloat(letterKeys[0].count)
- - 1.0
- )
- }
-
- switch row {
- case 0:
- btn.topShift = -5
- btn.bottomShift = -6
- case 1:
- btn.topShift = -6
- btn.bottomShift = -6
- case 2:
- btn.topShift = -6
- btn.bottomShift = -6
- case 3:
- btn.topShift = -6
- btn.bottomShift = -5
- default:
- break
- }
-
- // Pad left and right based on if the button has been shifted.
- if leftPadding == CGFloat(0) {
- btn.leftShift = -(widthOfSpacing / 2)
- } else {
- btn.leftShift = -leftPadding
- }
- if rightPadding == CGFloat(0) {
- btn.rightShift = -(widthOfSpacing / 2)
- } else {
- btn.rightShift = -rightPadding
- }
-
- // Activate keyboard interface buttons.
- activateBtn(btn: btn)
- if key == "shift" || key == spaceBar || key == languageTextForSpaceBar {
- btn.addTarget(self, action: #selector(keyMultiPress(_:event:)), for: .touchDownRepeat)
- }
- }
- }
-
- // End padding.
- switch keyboardState {
- case .letters:
- break
- case .numbers:
- break
- case .symbols:
- break
- }
+ setKeyPadding()
} else {
// Load conjugation view.
@@ -2021,23 +2136,27 @@ class KeyboardViewController: UIInputViewController {
func setCommaAndPeriodKeysConditionally() {
let langCode = languagesAbbrDict[controllerLanguage] ?? "unknown"
- let userDefaults = UserDefaults(suiteName: "group.scribe.userDefaultsContainer")!
- let dictionaryKey = langCode + "CommaAndPeriod"
- let letterKeysHaveCommaPeriod = userDefaults.bool(forKey: dictionaryKey)
+ if let userDefaults = UserDefaults(suiteName: "group.scribe.userDefaultsContainer") {
+ let dictionaryKey = langCode + "CommaAndPeriod"
+ let letterKeysHaveCommaPeriod = userDefaults.bool(forKey: dictionaryKey)
- if letterKeysHaveCommaPeriod {
- let spaceIndex = letterKeys[3].firstIndex(where: { $0 == "space" })
- letterKeys[3].insert(",", at: spaceIndex!)
- letterKeys[3].insert(".", at: spaceIndex! + 2)
+ if letterKeysHaveCommaPeriod {
+ let spaceIndex = letterKeys[3].firstIndex(where: { $0 == "space" })
+ letterKeys[3].insert(",", at: spaceIndex!)
+ letterKeys[3].insert(".", at: spaceIndex! + 2)
+ }
}
}
func emojiAutosuggestIsEnabled() -> Bool {
let langCode = languagesAbbrDict[controllerLanguage] ?? "unknown"
- let userDefaults = UserDefaults(suiteName: "group.scribe.userDefaultsContainer")!
- let dictionaryKey = langCode + "EmojiAutosuggest"
+ if let userDefaults = UserDefaults(suiteName: "group.scribe.userDefaultsContainer") {
+ let dictionaryKey = langCode + "EmojiAutosuggest"
- return userDefaults.bool(forKey: dictionaryKey)
+ return userDefaults.bool(forKey: dictionaryKey)
+ } else {
+ return true // return the default value
+ }
}
// MARK: Button Actions
@@ -2308,16 +2427,22 @@ class KeyboardViewController: UIInputViewController {
if let wordSelected = proxy.selectedText {
wordToCheck = wordSelected
} else {
- wordsTyped = proxy.documentContextBeforeInput!.components(separatedBy: " ")
- let lastWordTyped = wordsTyped.secondToLast()
- if !languagesWithCapitalizedNouns.contains(controllerLanguage) {
- wordToCheck = lastWordTyped!.lowercased()
- } else {
- wordToCheck = lastWordTyped!
+ if let contextBeforeInput = proxy.documentContextBeforeInput {
+ wordsTyped = contextBeforeInput.components(separatedBy: " ")
+ let lastWordTyped = wordsTyped.secondToLast()
+ if !languagesWithCapitalizedNouns.contains(controllerLanguage) {
+ wordToCheck = lastWordTyped!.lowercased()
+ } else {
+ wordToCheck = lastWordTyped!
+ }
}
}
- let prepForm = LanguageDBManager.shared.queryPrepForm(of: wordToCheck)[0]
- hasPrepForm = prepForm != ""
+ let prepCaseQuery = "SELECT * FROM prepositions WHERE preposition = ?"
+ let prepCaseArgs = [wordToCheck.lowercased()]
+ let outputCols = ["form"]
+
+ let prepForm = queryDBRow(query: prepCaseQuery, outputCols: outputCols, args: prepCaseArgs)[0]
+ hasPrepForm = !prepForm.isEmpty
if hasPrepForm {
resetCaseDeclensionState()
commandState = .selectCaseDeclension
@@ -2332,7 +2457,7 @@ class KeyboardViewController: UIInputViewController {
annotationBtns[i].backgroundColor = annotationColors[i]
}
let emojisToSelectFrom = "🥳🎉"
- let emojis = String((0 ..< 3).map { _ in emojisToSelectFrom.randomElement()! })
+ let emojis = String((0 ..< 3).compactMap { _ in emojisToSelectFrom.randomElement() })
sender.setTitle(emojis, for: .normal)
return
@@ -2360,13 +2485,15 @@ class KeyboardViewController: UIInputViewController {
conditionallySetAutoActionBtns()
} else {
// Shift state if the user presses delete when the prompt is present.
- if allPrompts.contains((commandBar?.text!)!) || allColoredPrompts.contains(commandBar.attributedText!) {
- shiftButtonState = .shift // Auto-capitalization
- loadKeys()
- // Function call required due to return.
- // Not including means placeholder is never added on last delete action.
- commandBar.conditionallyAddPlaceholder()
- return
+ if let commandBarText = commandBar?.text, let commandBarAttributedText = commandBar?.attributedText {
+ if allPrompts.contains(commandBarText) || allColoredPrompts.contains(commandBarAttributedText) {
+ shiftButtonState = .shift // Auto-capitalization
+ loadKeys()
+ // Function call required due to return.
+ // Not including means placeholder is never added on last delete action.
+ commandBar.conditionallyAddPlaceholder()
+ return
+ }
}
handleDeleteButtonPressed()
@@ -2397,16 +2524,14 @@ class KeyboardViewController: UIInputViewController {
changeKeyboardToLetterKeys()
}
} else {
- commandBar.text! = (commandBar?.text!.insertPriorToCursor(char: " "))!
- if [
- ". " + commandCursor,
- "? " + commandCursor,
- "! " + commandCursor,
- ].contains(String(commandBar.text!.suffix(3))) {
- shiftButtonState = .shift
- }
- if keyboardState != .letters {
- changeKeyboardToLetterKeys()
+ if let commandBarText = commandBar?.text {
+ commandBar?.text = commandBarText.insertPriorToCursor(char: " ")
+ if [". " + commandCursor, "? " + commandCursor, "! " + commandCursor].contains(String(commandBarText.suffix(3))) {
+ shiftButtonState = .shift
+ }
+ if keyboardState != .letters {
+ changeKeyboardToLetterKeys()
+ }
}
}
@@ -2427,7 +2552,9 @@ class KeyboardViewController: UIInputViewController {
if ![.translate, .conjugate, .plural].contains(commandState) {
proxy.insertText("'")
} else {
- commandBar.text! = (commandBar.text!.insertPriorToCursor(char: "'"))
+ if let commandBarText = commandBar.text {
+ commandBar.text = commandBarText.insertPriorToCursor(char: "'")
+ }
}
changeKeyboardToLetterKeys()
@@ -2440,7 +2567,9 @@ class KeyboardViewController: UIInputViewController {
proxy.insertText(keyToDisplay)
}
} else {
- commandBar.text = commandBar.text!.insertPriorToCursor(char: keyToDisplay)
+ if let commandBarText = commandBar.text {
+ commandBar.text = commandBarText.insertPriorToCursor(char: keyToDisplay)
+ }
}
case ",", ".", "!", "?":
@@ -2450,7 +2579,9 @@ class KeyboardViewController: UIInputViewController {
}
proxy.insertText(keyToDisplay)
} else {
- commandBar.text = commandBar.text!.insertPriorToCursor(char: keyToDisplay)
+ if let commandBarText = commandBar.text {
+ commandBar.text = commandBarText.insertPriorToCursor(char: keyToDisplay)
+ }
}
case "shift":
@@ -2497,7 +2628,9 @@ class KeyboardViewController: UIInputViewController {
if [.idle, .selectCommand, .alreadyPlural, .invalid].contains(commandState) {
proxy.insertText(keyToDisplay)
} else {
- commandBar.text = commandBar.text!.insertPriorToCursor(char: keyToDisplay)
+ if let currentText = commandBar.text {
+ commandBar.text = currentText.insertPriorToCursor(char: keyToDisplay)
+ }
}
}
@@ -2593,50 +2726,50 @@ class KeyboardViewController: UIInputViewController {
@objc func keyMultiPress(_ sender: UIButton, event: UIEvent) {
guard var originalKey = sender.layer.value(forKey: "original") as? String else { return }
- let touch: UITouch = event.allTouches!.first!
-
- // Caps lock given two taps of shift.
- if touch.tapCount == 2 && originalKey == "shift" && capsLockPossible {
- switchToFullCaps()
- }
-
- // To make sure that the user can still use the double space period shortcut after numbers and symbols.
- let punctuationThatCancelsShortcut = ["?", "!", ",", ".", ":", ";", "-"]
- if originalKey != "shift" && proxy.documentContextBeforeInput?.count != 1 && ![.translate, .conjugate, .plural].contains(commandState) {
- let charBeforeSpace = String(Array(proxy.documentContextBeforeInput!).secondToLast()!)
- if punctuationThatCancelsShortcut.contains(charBeforeSpace) {
- originalKey = "Cancel shortcut"
+ if let touches = event.allTouches, let touch = touches.first {
+ // Caps lock given two taps of shift.
+ if touch.tapCount == 2 && originalKey == "shift" && capsLockPossible {
+ switchToFullCaps()
}
- } else if [.translate, .conjugate, .plural].contains(commandState) {
- let charBeforeSpace = String(Array((commandBar?.text!)!).secondToLast()!)
- if punctuationThatCancelsShortcut.contains(charBeforeSpace) {
- originalKey = "Cancel shortcut"
+
+ // To make sure that the user can still use the double space period shortcut after numbers and symbols.
+ let punctuationThatCancelsShortcut = ["?", "!", ",", ".", ":", ";", "-"]
+ if originalKey != "shift" && proxy.documentContextBeforeInput?.count != 1 && ![.translate, .conjugate, .plural].contains(commandState) {
+ let charBeforeSpace = String(Array(proxy.documentContextBeforeInput!).secondToLast()!)
+ if punctuationThatCancelsShortcut.contains(charBeforeSpace) {
+ originalKey = "Cancel shortcut"
+ }
+ } else if [.translate, .conjugate, .plural].contains(commandState) {
+ let charBeforeSpace = String(Array((commandBar?.text!)!).secondToLast()!)
+ if punctuationThatCancelsShortcut.contains(charBeforeSpace) {
+ originalKey = "Cancel shortcut"
+ }
}
- }
- // Double space period shortcut.
- if touch.tapCount == 2
- && (originalKey == spaceBar || originalKey == languageTextForSpaceBar)
- && proxy.documentContextBeforeInput?.count != 1
- && doubleSpacePeriodPossible
- {
- // The fist condition prevents a period if the prior characters are spaces as the user wants a series of spaces.
- if proxy.documentContextBeforeInput?.suffix(2) != " " && ![.translate, .conjugate, .plural].contains(commandState) {
- proxy.deleteBackward()
- proxy.insertText(". ")
- emojisToShow = .zero // was showing empty emoji spots
- keyboardState = .letters
- shiftButtonState = .shift
- loadKeys()
+ // Double space period shortcut.
+ if touch.tapCount == 2
+ && (originalKey == spaceBar || originalKey == languageTextForSpaceBar)
+ && proxy.documentContextBeforeInput?.count != 1
+ && doubleSpacePeriodPossible
+ {
// The fist condition prevents a period if the prior characters are spaces as the user wants a series of spaces.
- } else if commandBar.text!.suffix(2) != " " && [.translate, .conjugate, .plural].contains(commandState) {
- commandBar.text! = (commandBar?.text!.deletePriorToCursor())!
- commandBar.text! = (commandBar?.text!.insertPriorToCursor(char: ". "))!
- keyboardState = .letters
- shiftButtonState = .shift
- loadKeys()
+ if proxy.documentContextBeforeInput?.suffix(2) != " " && ![.translate, .conjugate, .plural].contains(commandState) {
+ proxy.deleteBackward()
+ proxy.insertText(". ")
+ emojisToShow = .zero // was showing empty emoji spots
+ keyboardState = .letters
+ shiftButtonState = .shift
+ loadKeys()
+ // The fist condition prevents a period if the prior characters are spaces as the user wants a series of spaces.
+ } else if commandBar.text!.suffix(2) != " " && [.translate, .conjugate, .plural].contains(commandState) {
+ commandBar.text! = (commandBar?.text!.deletePriorToCursor())!
+ commandBar.text! = (commandBar?.text!.insertPriorToCursor(char: ". "))!
+ keyboardState = .letters
+ shiftButtonState = .shift
+ loadKeys()
+ }
+ // Show auto actions if the keyboard states dictate.
+ conditionallySetAutoActionBtns()
}
- // Show auto actions if the keyboard states dictate.
- conditionallySetAutoActionBtns()
}
}
@@ -2655,7 +2788,7 @@ class KeyboardViewController: UIInputViewController {
/// - gesture: the gesture that was received.
@objc func deleteLongPressed(_ gesture: UIGestureRecognizer) {
// Prevent the command state prompt from being deleted.
- if [.translate, .conjugate, .plural].contains(commandState) && allPrompts.contains((commandBar?.text!)!) {
+ if let commandBarText = commandBar?.text, [.translate, .conjugate, .plural].contains(commandState), allPrompts.contains(commandBarText) {
gesture.state = .cancelled
commandBar.conditionallyAddPlaceholder()
}
@@ -2774,9 +2907,9 @@ class KeyboardViewController: UIInputViewController {
genAlternatesView(key: key)
alternateBtnStartX = 5.0
- var alternatesBtnY = key.frame.height * 0.15
+ var alternatesBtnY = key.frame.height * scalarAlternatesBtnYPhone
if DeviceType.isPad {
- alternatesBtnY = key.frame.height * 0.2
+ alternatesBtnY = key.frame.height * scalarAlternatesBtnYPad
}
for char in alternateKeys {
let alternateKey = KeyboardKey(
diff --git a/Keyboards/KeyboardsBase/LoadData.swift b/Keyboards/KeyboardsBase/LoadData.swift
new file mode 100644
index 00000000..539f8d4b
--- /dev/null
+++ b/Keyboards/KeyboardsBase/LoadData.swift
@@ -0,0 +1,158 @@
+/**
+ * Function for loading in data to the keyboards.
+ *
+ * Copyright (C) 2023 Scribe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import Foundation
+import GRDB
+import SwiftyJSON
+
+/// Loads a JSON file that contains grammatical information into a dictionary.
+///
+/// - Parameters
+/// - filename: the name of the JSON file to be loaded.
+func loadJSON(filename fileName: String) -> JSON? {
+ guard let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
+ let data = try? Data(contentsOf: url),
+ let jsonData = try? JSON(data: data)
+ else {
+ return nil
+ }
+ return jsonData
+}
+
+/// Makes a connection to the language database given the value for controllerLanguage.
+func openDBQueue() -> DatabaseQueue {
+ let dbName = "\(String(describing: get_iso_code(keyboardLanguage: controllerLanguage).uppercased()))LanguageData"
+ guard let dbPath = Bundle.main.path(forResource: dbName, ofType: "sqlite") else {
+ fatalError("Failed to locate database file.")
+ }
+ do {
+ let dbQueue = try DatabaseQueue(path: dbPath)
+ return dbQueue
+ } catch {
+ fatalError("Failed to initialize DatabaseQueue: \(error)")
+ }
+}
+
+// Variable to be replaced with the result of openDBQueue.
+var languageDB = try! DatabaseQueue()
+
+/// Returns a row from the language database given a query and arguments.
+///
+/// - Parameters
+/// - query: the query to run against the language database.
+/// - outputCols: the columns from which the values should come.
+/// - args: arguments to pass to `query`.
+func queryDBRow(query: String, outputCols: [String], args: [String]) -> [String] {
+ var outputValues = [String]()
+ do {
+ try languageDB.read { db in
+ if let row = try Row.fetchOne(db, sql: query, arguments: StatementArguments(args)) {
+ for col in outputCols {
+ outputValues.append(row[col])
+ }
+ }
+ }
+ } catch let error as DatabaseError {
+ let errorMessage = error.message
+ let errorSQL = error.sql
+ let errorArguments = error.arguments
+ print(
+ "An error '\(String(describing: errorMessage))' occurred in the query: \(String(describing: errorSQL)) (\(String(describing: errorArguments)))"
+ )
+ } catch {}
+
+ if outputValues == [String]() {
+ // Append an empty string so that we can check for it and trigger commandState = .invalid.
+ outputValues.append("")
+ }
+
+ return outputValues
+}
+
+/// Writes a row of a language database table given a query and arguments.
+///
+/// - Parameters
+/// - query: the query to run against the language database.
+/// - args: arguments to pass to `query`.
+func writeDBRow(query: String, args: StatementArguments) {
+ do {
+ try languageDB.write { db in
+ try db.execute(
+ sql: query,
+ arguments: args
+ )
+ }
+ } catch let error as DatabaseError {
+ let errorMessage = error.message
+ let errorSQL = error.sql
+ let errorArguments = error.arguments
+ print(
+ "An error '\(String(describing: errorMessage))' occurred in the query: \(String(describing: errorSQL)) (\(String(describing: errorArguments)))"
+ )
+ } catch {}
+}
+
+/// Returns the next three words in the `autocomplete_lexicon` that follow a given word.
+///
+/// - Parameters
+/// - word: the word that autosuggestions should be returned for.
+func queryAutocompletions(word: String) -> [String] {
+ var autocompletions = [String]()
+
+ let autocompletionsQuery = """
+ SELECT
+ word
+
+ FROM
+ autocomplete_lexicon
+
+ WHERE
+ LOWER(word) LIKE ?
+
+ ORDER BY
+ word COLLATE NOCASE ASC
+
+ LIMIT
+ 3
+ """
+ let patterns = ["\(word.lowercased())%"]
+
+ do {
+ let rows = try languageDB.read { db in
+ try Row.fetchAll(db, sql: autocompletionsQuery, arguments: StatementArguments(patterns))
+ }
+ for r in rows {
+ autocompletions.append(r["word"])
+ }
+ } catch let error as DatabaseError {
+ let errorMessage = error.message
+ let errorSQL = error.sql
+ let errorArguments = error.arguments
+ print(
+ "An error '\(String(describing: errorMessage))' occurred in the query: \(String(describing: errorSQL)) (\(String(describing: errorArguments)))"
+ )
+ } catch {}
+
+ if autocompletions == [String]() {
+ // Append an empty string so that we can check for it and trigger nothing being shown.
+ autocompletions.append("")
+ }
+
+ return autocompletions
+}
diff --git a/Keyboards/KeyboardsBase/ScribeFunctionality/Annotate.swift b/Keyboards/KeyboardsBase/ScribeFunctionality/Annotate.swift
index c9bcf28d..3871dbc6 100644
--- a/Keyboards/KeyboardsBase/ScribeFunctionality/Annotate.swift
+++ b/Keyboards/KeyboardsBase/ScribeFunctionality/Annotate.swift
@@ -1,8 +1,21 @@
-//
-// Annotate.swift
-//
-// Functions and elements that control word annotations.
-//
+/**
+ * Functions and elements that control word annotations.
+ *
+ * Copyright (C) 2023 Scribe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
import UIKit
@@ -38,11 +51,17 @@ let prepAnnotationConversionDict = [
/// - wordToAnnotate: the word that an annotation should be created for.
/// - KVC: the keyboard view controller.
func wordAnnotation(wordToAnnotate: String, KVC: KeyboardViewController) {
- let nounForm = LanguageDBManager.shared.queryNounForm(of: wordToAnnotate)[0]
- prepAnnotationForm = LanguageDBManager.shared.queryPrepForm(of: wordToAnnotate)[0]
+ let nounGenderQuery = "SELECT * FROM nouns WHERE noun = ?"
+ let prepCaseQuery = "SELECT * FROM prepositions WHERE preposition = ?"
+ let nounGenderArgs = [wordToAnnotate]
+ let prepCaseArgs = [wordToAnnotate.lowercased()]
+ let outputCols = ["form"]
- hasNounForm = nounForm != ""
- hasPrepForm = prepAnnotationForm != ""
+ let nounForm = queryDBRow(query: nounGenderQuery, outputCols: outputCols, args: nounGenderArgs)[0]
+ prepAnnotationForm = queryDBRow(query: prepCaseQuery, outputCols: outputCols, args: prepCaseArgs)[0]
+
+ hasNounForm = !nounForm.isEmpty
+ hasPrepForm = !prepAnnotationForm.isEmpty
annotationsToAssign = [String]()
annotationBtns = [UIButton]()
@@ -71,7 +90,7 @@ func wordAnnotation(wordToAnnotate: String, KVC: KeyboardViewController) {
annotationBtn.styleSingleAnnotation(fullAnnotation: true)
let emojisToSelectFrom = "🥳🎉"
- let emojis = String((0 ..< 3).map { _ in emojisToSelectFrom.randomElement()! })
+ let emojis = String((0 ..< 3).compactMap { _ in emojisToSelectFrom.randomElement() })
annotationBtn.setTitle(emojis, for: .normal)
KVC.view.addSubview(annotationBtn)
annotationBtns.append(annotationBtn)
@@ -169,7 +188,9 @@ func wordAnnotation(wordToAnnotate: String, KVC: KeyboardViewController) {
KVC.view.addSubview(annotationBtn)
annotationBtns.append(annotationBtn)
if nounFormToColorDict.keys.contains(annotationToDisplay) {
- annotationColors.append(nounFormToColorDict[annotationsToAssign[i]]!)
+ if let annotationColor = nounFormToColorDict[annotationsToAssign[i]] {
+ annotationColors.append(annotationColor)
+ }
} else {
annotationColors.append(UITraitCollection.current.userInterfaceStyle == .light ? .black : .white)
}
@@ -208,7 +229,7 @@ func wordAnnotation(wordToAnnotate: String, KVC: KeyboardViewController) {
/// - KVC: the keyboard view controller.
func selectedWordAnnotation(KVC: KeyboardViewController) {
wordToCheck = proxy.selectedText ?? ""
- if wordToCheck.count > 0 {
+ if !wordToCheck.isEmpty {
if !languagesWithCapitalizedNouns.contains(controllerLanguage) {
wordToCheck = wordToCheck.lowercased()
}
@@ -249,9 +270,13 @@ func typedWordAnnotation(KVC: KeyboardViewController) {
/// - index: the auto action key index that the annotation should be set for.
/// - KVC: the keyboard view controller.
func autoActionAnnotation(autoActionWord: String, index: Int, KVC: KeyboardViewController) {
- let nounForm = LanguageDBManager.shared.queryNounForm(of: autoActionWord)[0]
+ let nounGenderQuery = "SELECT * FROM nouns WHERE noun = ?"
+ let nounGenderArgs = [autoActionWord]
+ let outputCols = ["form"]
+
+ let nounForm = queryDBRow(query: nounGenderQuery, outputCols: outputCols, args: nounGenderArgs)[0]
- hasNounForm = nounForm != ""
+ hasNounForm = !nounForm.isEmpty
newAutoActionAnnotationsToAssign = [String]()
newAutoActionAnnotationBtns = [UIButton]()
@@ -331,9 +356,11 @@ func autoActionAnnotation(autoActionWord: String, index: Int, KVC: KeyboardViewC
KVC.view.addSubview(annotationBtn)
autoActionAnnotationBtns.append(annotationBtn)
- newAutoActionAnnotationColors.append(
- nounFormToColorDict[newAutoActionAnnotationsToAssign[i]]!.withAlphaComponent(0.75)
- )
+ if let annotationColor = nounFormToColorDict[newAutoActionAnnotationsToAssign[i]] {
+ let colorWithAlpha = annotationColor.withAlphaComponent(0.75)
+ newAutoActionAnnotationColors.append(colorWithAlpha)
+ }
+
setBtn(
btn: annotationBtn,
color: newAutoActionAnnotationColors[i],
diff --git a/Keyboards/KeyboardsBase/ScribeFunctionality/Conjugate.swift b/Keyboards/KeyboardsBase/ScribeFunctionality/Conjugate.swift
index b1ec572f..4c3badd2 100644
--- a/Keyboards/KeyboardsBase/ScribeFunctionality/Conjugate.swift
+++ b/Keyboards/KeyboardsBase/ScribeFunctionality/Conjugate.swift
@@ -1,8 +1,21 @@
-//
-// Conjugation.swift
-//
-// Functions and elements that control the conjugation command.
-//
+/**
+ * Functions and elements that control the conjugation command.
+ *
+ * Copyright (C) 2023 Scribe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
import UIKit
@@ -47,9 +60,10 @@ let keyboardConjLabelDict: [String: Any] = [
func returnDeclension(keyPressed: UIButton) {
let wordPressed = keyPressed.titleLabel?.text ?? ""
- let keyName = keyPressed.layer.value(
- forKey: "original"
- ) as! String
+ var keyName = ""
+ if let originalKeyValue = keyPressed.layer.value(forKey: "original") as? String {
+ keyName = originalKeyValue
+ }
if !(wordPressed.contains("/") || wordPressed.contains("∗")) {
proxy.insertText(wordPressed + " ")
@@ -180,17 +194,28 @@ func returnDeclension(keyPressed: UIButton) {
/// - commandBar: the command bar into which an input was entered.
func triggerVerbConjugation(commandBar: UILabel) -> Bool {
// Cancel via a return press.
- if commandBar.text! == conjugatePromptAndCursor || commandBar.text! == conjugatePromptAndPlaceholder {
+ if let commandBarText = commandBar.text,
+ commandBarText == conjugatePromptAndCursor || commandBarText == conjugatePromptAndCursor
+ {
return false
}
- verbToConjugate = (commandBar.text!.substring(with: conjugatePrompt.count ..< (commandBar.text!.count) - 1))
+
+ if let commandBarText = commandBar.text {
+ let startIndex = commandBarText.index(commandBarText.startIndex, offsetBy: conjugatePrompt.count)
+ let endIndex = commandBarText.index(commandBarText.endIndex, offsetBy: -1)
+ verbToConjugate = String(commandBarText[startIndex ..< endIndex])
+ }
verbToConjugate = String(verbToConjugate.trailingSpacesTrimmed)
// Check to see if the input was uppercase to return an uppercase conjugation.
let firstLetter = verbToConjugate.substring(toIdx: 1)
inputWordIsCapitalized = firstLetter.isUppercase
+ verbToConjugate = verbToConjugate.lowercased()
- let verbInTable = LanguageDBManager.shared.queryVerb(of: verbConjugated)[0]
+ let query = "SELECT * FROM verbs WHERE verb = ?"
+ let args = [verbToConjugate]
+ let outputCols = ["verb"]
+ let verbInTable = queryDBRow(query: query, outputCols: outputCols, args: args)[0]
return verbToConjugate == verbInTable
}
@@ -201,7 +226,6 @@ func triggerVerbConjugation(commandBar: UILabel) -> Bool {
/// - keyPressed: the button pressed as sender.
/// - requestedForm: the form that is triggered by the given key.
func returnConjugation(keyPressed: UIButton, requestedForm: String) {
- let outputCols = [requestedForm]
if commandState == .selectCaseDeclension {
returnDeclension(keyPressed: keyPressed)
return
@@ -212,11 +236,14 @@ func returnConjugation(keyPressed: UIButton, requestedForm: String) {
if wordPressed == invalidCommandMsg {
proxy.insertText("")
} else if formsDisplayDimensions == .view3x2 {
- wordToReturn = LanguageDBManager.shared.queryVerb(of: verbToConjugate, with: outputCols)[0]
+ let query = "SELECT * FROM verbs WHERE verb = ?"
+ let args = [verbToConjugate]
+ let outputCols = [requestedForm]
+ wordToReturn = queryDBRow(query: query, outputCols: outputCols, args: args)[0]
potentialWordsToReturn = wordToReturn.components(separatedBy: " ")
if inputWordIsCapitalized {
- if controllerLanguage == "German" && potentialWordsToReturn.count == 2 {
+ if controllerLanguage == "German", potentialWordsToReturn.count == 2 {
// Don't return a space as well as we have a perfect verb and the cursor will be between.
proxy.insertText(wordToReturn.capitalize())
} else {
@@ -226,7 +253,10 @@ func returnConjugation(keyPressed: UIButton, requestedForm: String) {
proxy.insertText(wordToReturn + " ")
}
} else if formsDisplayDimensions == .view2x2 {
- wordToReturn = LanguageDBManager.shared.queryVerb(of: verbToConjugate, with: outputCols)[0]
+ let query = "SELECT * FROM verbs WHERE verb = ?"
+ let args = [verbToConjugate]
+ let outputCols = [requestedForm]
+ wordToReturn = queryDBRow(query: query, outputCols: outputCols, args: args)[0]
potentialWordsToReturn = wordToReturn.components(separatedBy: " ")
if inputWordIsCapitalized {
diff --git a/Keyboards/KeyboardsBase/ScribeFunctionality/Plural.swift b/Keyboards/KeyboardsBase/ScribeFunctionality/Plural.swift
index e7bdc118..52b17cb6 100644
--- a/Keyboards/KeyboardsBase/ScribeFunctionality/Plural.swift
+++ b/Keyboards/KeyboardsBase/ScribeFunctionality/Plural.swift
@@ -1,8 +1,21 @@
-//
-// Plural.swift
-//
-// Functions that control the plural command.
-//
+/**
+ * Functions that control the plural command.
+ *
+ * Copyright (C) 2023 Scribe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
import UIKit
@@ -12,34 +25,45 @@ import UIKit
/// - commandBar: the command bar into which an input was entered.
func queryPlural(commandBar: UILabel) {
// Cancel via a return press.
- if commandBar.text! == pluralPromptAndCursor || commandBar.text! == pluralPromptAndPlaceholder {
+ if let commandBarText = commandBar.text,
+ commandBarText == pluralPromptAndCursor || commandBarText == pluralPromptAndCursor
+ {
return
}
- var noun: String = (commandBar.text!.substring(
- with: pluralPrompt.count ..< ((commandBar.text!.count) - 1))
- )
+
+ var noun = ""
+ if let commandBarText = commandBar.text {
+ let startIndex = commandBarText.index(commandBarText.startIndex, offsetBy: pluralPrompt.count)
+ let endIndex = commandBarText.index(before: commandBarText.endIndex)
+ noun = String(commandBarText[startIndex ..< endIndex])
+ }
noun = String(noun.trailingSpacesTrimmed)
// Check to see if the input was uppercase to return an uppercase plural.
inputWordIsCapitalized = false
if !languagesWithCapitalizedNouns.contains(controllerLanguage) {
inputWordIsCapitalized = noun.substring(toIdx: 1).isUppercase
+ noun = noun.lowercased()
}
- wordToReturn = LanguageDBManager.shared.queryNounPlural(of: noun)[0]
+ let query = "SELECT * FROM nouns WHERE noun = ?"
+ let args = [noun]
+ let outputCols = ["plural"]
+ wordToReturn = queryDBRow(query: query, outputCols: outputCols, args: args)[0]
- if wordToReturn != "" {
- if wordToReturn != "isPlural" {
- if inputWordIsCapitalized {
- proxy.insertText(wordToReturn.capitalized + " ")
- } else {
- proxy.insertText(wordToReturn + " ")
- }
+ guard !wordToReturn.isEmpty else {
+ commandState = .invalid
+ return
+ }
+
+ if wordToReturn != "isPlural" {
+ if inputWordIsCapitalized {
+ proxy.insertText(wordToReturn.capitalized + " ")
} else {
- proxy.insertText(noun + " ")
- commandState = .alreadyPlural
+ proxy.insertText(wordToReturn + " ")
}
} else {
- commandState = .invalid
+ proxy.insertText(noun + " ")
+ commandState = .alreadyPlural
}
}
diff --git a/Keyboards/KeyboardsBase/ScribeFunctionality/Translate.swift b/Keyboards/KeyboardsBase/ScribeFunctionality/Translate.swift
index 2ed9c970..547f13b1 100644
--- a/Keyboards/KeyboardsBase/ScribeFunctionality/Translate.swift
+++ b/Keyboards/KeyboardsBase/ScribeFunctionality/Translate.swift
@@ -1,8 +1,21 @@
-//
-// Translate.swift
-//
-// Functions that control the translate command.
-//
+/**
+ * Functions that control the translate command.
+ *
+ * Copyright (C) 2023 Scribe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
import UIKit
@@ -12,26 +25,36 @@ import UIKit
/// - commandBar: the command bar into which an input was entered.
func queryTranslation(commandBar: UILabel) {
// Cancel via a return press.
- if commandBar.text! == translatePromptAndCursor || commandBar.text! == translatePromptAndPlaceholder {
+ if let commandBarText = commandBar.text,
+ commandBarText == translatePromptAndCursor || commandBarText == translatePromptAndPlaceholder
+ {
return
}
- wordToTranslate = (commandBar.text!.substring(
- with: translatePrompt.count ..< ((commandBar.text!.count) - 1))
- )
+
+ if let commandBarText = commandBar.text {
+ let startIndex = commandBarText.index(commandBarText.startIndex, offsetBy: translatePrompt.count)
+ let endIndex = commandBarText.index(commandBarText.endIndex, offsetBy: -1)
+ wordToTranslate = String(commandBarText[startIndex ..< endIndex])
+ }
wordToTranslate = String(wordToTranslate.trailingSpacesTrimmed)
// Check to see if the input was uppercase to return an uppercase conjugation.
inputWordIsCapitalized = wordToTranslate.substring(toIdx: 1).isUppercase
+ wordToTranslate = wordToTranslate.lowercased()
- wordToReturn = LanguageDBManager.shared.queryTranslation(of: wordToTranslate)[0]
+ let query = "SELECT * FROM translations WHERE word = ?"
+ let args = [wordToTranslate]
+ let outputCols = ["translation"]
+ wordToReturn = queryDBRow(query: query, outputCols: outputCols, args: args)[0]
- if wordToReturn != "" {
- if inputWordIsCapitalized {
- proxy.insertText(wordToReturn.capitalized + " ")
- } else {
- proxy.insertText(wordToReturn + " ")
- }
- } else {
+ guard !wordToReturn.isEmpty else {
commandState = .invalid
+ return
+ }
+
+ if inputWordIsCapitalized {
+ proxy.insertText(wordToReturn.capitalized + " ")
+ } else {
+ proxy.insertText(wordToReturn + " ")
}
}
diff --git a/Scribe.xcodeproj/project.pbxproj b/Scribe.xcodeproj/project.pbxproj
index f09b43f5..08bc6a6f 100644
--- a/Scribe.xcodeproj/project.pbxproj
+++ b/Scribe.xcodeproj/project.pbxproj
@@ -386,6 +386,18 @@
D190B2582742525C00705659 /* Keyboard.xib in Resources */ = {isa = PBXBuildFile; fileRef = D1C0ACD92719E0AA001E11C3 /* Keyboard.xib */; };
D190B26827426ACD00705659 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2592742565500705659 /* KeyboardViewController.swift */; };
D190B26927426ACD00705659 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2592742565500705659 /* KeyboardViewController.swift */; };
+ D19569E12BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E22BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E32BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E42BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E52BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E62BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E72BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E82BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569E92BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569EA2BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569EB2BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
+ D19569EC2BBCCAD700D025D7 /* LoadData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19569E02BBCCAD700D025D7 /* LoadData.swift */; };
D196B360279A051000228F3F /* ENPrivacyPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D196B35F279A051000228F3F /* ENPrivacyPolicy.swift */; };
D1A2DCB127AD37BD0057A10D /* ENAppText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A2DCB027AD37BD0057A10D /* ENAppText.swift */; };
D1A2DCB427AD3EB50057A10D /* AppUISymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A2DCB327AD3EB50057A10D /* AppUISymbols.swift */; };
@@ -937,6 +949,7 @@
D190B2592742565500705659 /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = ""; };
D190B28F27426F4900705659 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = SOURCE_ROOT; };
D190B29027426F4900705659 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
+ D19569E02BBCCAD700D025D7 /* LoadData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadData.swift; sourceTree = ""; };
D196B35F279A051000228F3F /* ENPrivacyPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ENPrivacyPolicy.swift; sourceTree = ""; };
D1A2DCB027AD37BD0057A10D /* ENAppText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ENAppText.swift; sourceTree = ""; };
D1A2DCB327AD3EB50057A10D /* AppUISymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUISymbols.swift; sourceTree = ""; };
@@ -1364,6 +1377,7 @@
D171942E27AEDE110038660B /* KeyboardKeys.swift */,
D190B2592742565500705659 /* KeyboardViewController.swift */,
D190B24D2741B61000705659 /* LanguageDBManager.swift */,
+ D19569E02BBCCAD700D025D7 /* LoadData.swift */,
D16DD3A429E78A1500FB9022 /* Utilities.swift */,
D1C0ACD92719E0AA001E11C3 /* Keyboard.xib */,
);
@@ -1988,6 +2002,7 @@
D17193F827AECC930038660B /* FRCommandVariables.swift in Sources */,
D171945827AF237C0038660B /* ScribeKey.swift in Sources */,
1406B78C2A3209CF001DF45B /* AppExtensions.swift in Sources */,
+ D19569E12BBCCAD700D025D7 /* LoadData.swift in Sources */,
D1B81D4A27BBBA200085FE5E /* ITCommandVariables.swift in Sources */,
D180EC0328FDFABF0018E29B /* FR-AZERTYInterfaceVariables.swift in Sources */,
D1CDED7B2A859FBF00098546 /* ENInterfaceVariables.swift in Sources */,
@@ -2058,6 +2073,7 @@
38BD214F22D592CA00C6795D /* DEKeyboardViewController.swift in Sources */,
D171943A27AEF0560038660B /* KeyboardStyling.swift in Sources */,
CE1378C628F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569E52BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453972293B9E04003AE55B /* ToolTipViewTheme.swift in Sources */,
3045395B293B911C003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5527BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,
@@ -2119,6 +2135,7 @@
D171942127AECD170038660B /* SVCommandVariables.swift in Sources */,
D171943927AEF0560038660B /* KeyboardStyling.swift in Sources */,
CE1378C528F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569E42BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453970293B9DFF003AE55B /* ToolTipViewTheme.swift in Sources */,
30453959293B911B003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5427BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,
@@ -2180,6 +2197,7 @@
D171942327AECD170038660B /* SVCommandVariables.swift in Sources */,
D171943B27AEF0560038660B /* KeyboardStyling.swift in Sources */,
CE1378C828F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569E92BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453974293B9E05003AE55B /* ToolTipViewTheme.swift in Sources */,
3045395D293B911D003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5727BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,
@@ -2241,6 +2259,7 @@
D171942527AECD170038660B /* SVCommandVariables.swift in Sources */,
D171943D27AEF0560038660B /* KeyboardStyling.swift in Sources */,
CE1378CA28F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569EB2BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453976293B9E06003AE55B /* ToolTipViewTheme.swift in Sources */,
3045395F293B911E003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5927BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,
@@ -2302,6 +2321,7 @@
D171942427AECD170038660B /* SVCommandVariables.swift in Sources */,
D171943C27AEF0560038660B /* KeyboardStyling.swift in Sources */,
CE1378C928F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569EA2BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453975293B9E06003AE55B /* ToolTipViewTheme.swift in Sources */,
3045395E293B911E003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5827BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,
@@ -2363,6 +2383,7 @@
D171942627AECD170038660B /* SVCommandVariables.swift in Sources */,
D171943E27AEF0560038660B /* KeyboardStyling.swift in Sources */,
CE1378CB28F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569EC2BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453977293B9E06003AE55B /* ToolTipViewTheme.swift in Sources */,
30453960293B911F003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5A27BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,
@@ -2424,6 +2445,7 @@
D1AB5B2829C757A100CCB0C1 /* ESInterfaceVariables.swift in Sources */,
D1AB5B2929C757A100CCB0C1 /* SVInterfaceVariables.swift in Sources */,
D1AB5B2A29C757A100CCB0C1 /* ScribeColor.swift in Sources */,
+ D19569E82BBCCAD700D025D7 /* LoadData.swift in Sources */,
D1AB5B2B29C757A100CCB0C1 /* ToolTipViewTheme.swift in Sources */,
D1AB5B2C29C757A100CCB0C1 /* AppTextStyling.swift in Sources */,
D1AB5B2D29C757A100CCB0C1 /* ITInterfaceVariables.swift in Sources */,
@@ -2485,6 +2507,7 @@
D1AFDF0629CA66D00033BF27 /* ESInterfaceVariables.swift in Sources */,
D1AFDF0729CA66D00033BF27 /* SVInterfaceVariables.swift in Sources */,
D1AFDF0829CA66D00033BF27 /* ScribeColor.swift in Sources */,
+ D19569E32BBCCAD700D025D7 /* LoadData.swift in Sources */,
D1AFDF0929CA66D00033BF27 /* ToolTipViewTheme.swift in Sources */,
D1AFDF0A29CA66D00033BF27 /* AppTextStyling.swift in Sources */,
D1AFDF0B29CA66D00033BF27 /* ITInterfaceVariables.swift in Sources */,
@@ -2546,6 +2569,7 @@
D1AFDF8329CA66F40033BF27 /* ESInterfaceVariables.swift in Sources */,
D1AFDF8429CA66F40033BF27 /* SVInterfaceVariables.swift in Sources */,
D1AFDF8529CA66F40033BF27 /* ScribeColor.swift in Sources */,
+ D19569E22BBCCAD700D025D7 /* LoadData.swift in Sources */,
D1AFDF8629CA66F40033BF27 /* ToolTipViewTheme.swift in Sources */,
D1AFDF8729CA66F40033BF27 /* AppTextStyling.swift in Sources */,
D1AFDF8829CA66F40033BF27 /* ITInterfaceVariables.swift in Sources */,
@@ -2607,6 +2631,7 @@
D1AFDFD929CA6E900033BF27 /* ESInterfaceVariables.swift in Sources */,
D1AFDFDA29CA6E900033BF27 /* SVInterfaceVariables.swift in Sources */,
D1AFDFDB29CA6E900033BF27 /* ScribeColor.swift in Sources */,
+ D19569E62BBCCAD700D025D7 /* LoadData.swift in Sources */,
D1AFDFDC29CA6E900033BF27 /* ToolTipViewTheme.swift in Sources */,
D1AFDFDD29CA6E900033BF27 /* AppTextStyling.swift in Sources */,
D1AFDFDE29CA6E900033BF27 /* ITInterfaceVariables.swift in Sources */,
@@ -2668,6 +2693,7 @@
D1B81D3327BBB6AD0085FE5E /* ESInterfaceVariables.swift in Sources */,
D1B81D3527BBB6B50085FE5E /* SVInterfaceVariables.swift in Sources */,
CE1378C728F5D7AC00E1CBC2 /* ScribeColor.swift in Sources */,
+ D19569E72BBCCAD700D025D7 /* LoadData.swift in Sources */,
30453973293B9E05003AE55B /* ToolTipViewTheme.swift in Sources */,
3045395C293B911D003AE55B /* AppTextStyling.swift in Sources */,
D1B81D5627BBBA360085FE5E /* ITInterfaceVariables.swift in Sources */,