mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-01 21:27:40 +00:00
feat(mac): add functions from removing keyboard
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled
also added comments for Settings renamed activeKeyboards to enabledKeyboards via settings migration for Keyman 19
This commit is contained in:
parent
dd0f2043bf
commit
af09f89bc9
7 changed files with 127 additions and 46 deletions
|
|
@ -34,20 +34,6 @@ struct ConfigView: View {
|
|||
}
|
||||
.padding()
|
||||
|
||||
// ScrollView(showsIndicators: false) {
|
||||
// List {
|
||||
// // Use $ to get binding to the published array
|
||||
// ForEach($settings.installedKeyboardPackages) { $package in
|
||||
// Section(header: Text("Section")) {
|
||||
// // Use $ to get binding to the nested array
|
||||
// ForEach($package.keyboards) { $keyboard in
|
||||
// Toggle(keyboard.keyboardId, isOn: $keyboard.enabled) // Binding here
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .padding()
|
||||
// }
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
ForEach(Array(settings.installedPackages.enumerated()), id: \.offset) { index, package in
|
||||
|
|
@ -60,7 +46,7 @@ struct ConfigView: View {
|
|||
// Example of Icon-Only Button
|
||||
Spacer()
|
||||
Button(action: {
|
||||
settings.installedPackages.remove(at: index)
|
||||
settings.removePackage(at: index)
|
||||
}) {
|
||||
Label("remove", systemImage: "trash.fill")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,18 @@
|
|||
#import "KMLogs.h"
|
||||
#import "KMDataRepository.h"
|
||||
|
||||
/**
|
||||
* The UserDefaults key `KMActiveKeyboardsKey` identifies the list of installed keyboard which
|
||||
* the user has enabled to be included in the Keyman keyboard menu. This key name is a little confusing
|
||||
* because the only truly active keyboard is the one that Keyman is applying while typing.
|
||||
* So, with the migration of data for Keyman version 19, this key is renamed to `KMEnabledKeyboardsKey`
|
||||
* Every enabled keyboard will appear in the Keyman keyboards menu.
|
||||
* Only one of these keyboards can be selected at a time, and the selected keyboard is the one that
|
||||
* is actively being used by Keyman for each keystroke.
|
||||
*/
|
||||
NSString *const kActiveKeyboardsKey = @"KMActiveKeyboardsKey";
|
||||
NSString *const kEnabledKeyboardsKey = @"KMEnabledKeyboardsKey";
|
||||
|
||||
NSString *const kSelectedKeyboardKey = @"KMSelectedKeyboardKey";
|
||||
NSString *const kPersistedOptionsKey = @"KMPersistedOptionsKey";
|
||||
NSString *const kShowOskOnActivate = @"KMShowOskOnActivate";
|
||||
|
|
@ -87,7 +98,7 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
os_log([KMLogs dataLog], "settings indicate that keyboards are stored in ~/Library: %{public}@", keyboardsStoredInLibrary ? @"YES" : @"NO" );
|
||||
|
||||
BOOL migrationNeeded = keymanSettingsExist && !keyboardsStoredInLibrary;
|
||||
os_log([KMLogs dataLog], "dataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
os_log([KMLogs dataLog], "keyman18DataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
|
||||
return migrationNeeded;
|
||||
}
|
||||
|
|
@ -104,7 +115,7 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
os_log([KMLogs dataLog], "settings indicate that keyboards are stored in ~/Library: %{public}@", keyboardsStoredInLibrary ? @"YES" : @"NO" );
|
||||
|
||||
BOOL migrationNeeded = keymanSettingsExistForInputMethod;
|
||||
os_log([KMLogs dataLog], "dataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
os_log([KMLogs dataLog], "keyman19SettingsMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
|
||||
return migrationNeeded;
|
||||
}
|
||||
|
|
@ -132,7 +143,7 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
|
||||
NSArray * activeKeyboards = [self.appDefaults arrayForKey:kActiveKeyboardsKey];
|
||||
if (activeKeyboards != nil) {
|
||||
[self.groupDefaults setObject:activeKeyboards forKey:kActiveKeyboardsKey];
|
||||
[self.groupDefaults setObject:activeKeyboards forKey:kEnabledKeyboardsKey];
|
||||
}
|
||||
|
||||
if ([self.appDefaults objectForKey:kShowOskOnActivate] != nil) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
*
|
||||
* Created by Shawn Schantz on 2026-04-02
|
||||
*
|
||||
* Class that exposes all settings information to the Config app
|
||||
* Provides a place for the Config app can bind directly to the settings
|
||||
* Class that exposes all settings information to the Keyman Configuration app
|
||||
* Provides a place for the config app can bind directly to the settings
|
||||
* and update when changes are made
|
||||
*
|
||||
*/
|
||||
|
|
@ -63,6 +63,9 @@ public class SettingsContainer : ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* for debugging: prints UserDefaults values
|
||||
*/
|
||||
public func logSettings() {
|
||||
self.settingsRepository.logSettings()
|
||||
}
|
||||
|
|
@ -80,6 +83,23 @@ public class SettingsContainer : ObservableObject {
|
|||
return package
|
||||
}
|
||||
|
||||
public func removePackage(at index: Int) {
|
||||
let package = self.installedPackages[index]
|
||||
|
||||
// will removing this package cause the removal of any enabled keyboards?
|
||||
let removingEnabledKeyboards = !package.getEnabledKeyboardsSettingsKeys().isEmpty
|
||||
|
||||
// delete package from disk
|
||||
self.packageRepository.deletePackage(package: package)
|
||||
|
||||
// remove package from installed packages list
|
||||
_ = self.installedPackages.remove(at: index)
|
||||
|
||||
// if we removed any enabled keyboards, then update settings
|
||||
if removingEnabledKeyboards {
|
||||
self.persistKeyboardState()
|
||||
}
|
||||
}
|
||||
/**
|
||||
* returns true if the keyboard is enabled
|
||||
* when enabled, the keyboard appears in the Keyman sub menu in the mac
|
||||
|
|
@ -109,7 +129,7 @@ public class SettingsContainer : ObservableObject {
|
|||
print ("Could not read keyboard state for package: \(packageId) and keyboard: \(keyboardId)")
|
||||
}
|
||||
|
||||
// update persisted state in UserDefaults activeKeyboards array
|
||||
// update persisted state in UserDefaults enabledKeyboards array
|
||||
self.persistKeyboardState()
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +138,7 @@ public class SettingsContainer : ObservableObject {
|
|||
*/
|
||||
func persistKeyboardState() {
|
||||
let enabledKeyboards = self.getAllEnabledKeyboardSettingsKeys()
|
||||
self.settingsRepository.writeActiveKeyboards(activeKeyboardsArray: Array(enabledKeyboards))
|
||||
self.settingsRepository.writeEnabledKeyboards(enabledKeyboardsArray: Array(enabledKeyboards))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -175,14 +195,14 @@ public class SettingsContainer : ObservableObject {
|
|||
*/
|
||||
func validateSettings() {
|
||||
let installedKeyboardKeys = self.getAllKeyboardSettingsKeys()
|
||||
let activeKeyboardKeys = self.settingsRepository.readActiveKeyboards()
|
||||
let enabledKeyboardKeys = self.settingsRepository.readEnabledKeyboards()
|
||||
|
||||
if (activeKeyboardKeys.isSubset(of: installedKeyboardKeys)) {
|
||||
print("only installed keyboards are listed as active: no need to synchronize")
|
||||
if (enabledKeyboardKeys.isSubset(of: installedKeyboardKeys)) {
|
||||
print("only installed keyboards are listed as enabled: no need to synchronize")
|
||||
} else {
|
||||
print("active keyboards list contains uninstalled keyboards: synchronize active keyboards list")
|
||||
let installedActiveKeyboardKeys = activeKeyboardKeys.intersection(installedKeyboardKeys)
|
||||
self.settingsRepository.writeActiveKeyboards(activeKeyboardsArray: Array(installedActiveKeyboardKeys))
|
||||
print("enabled keyboards list contains uninstalled keyboards: synchronize enabled keyboards list")
|
||||
let installedEnabledKeyboardKeys = enabledKeyboardKeys.intersection(installedKeyboardKeys)
|
||||
self.settingsRepository.writeEnabledKeyboards(enabledKeyboardsArray: Array(installedEnabledKeyboardKeys))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,11 +212,11 @@ public class SettingsContainer : ObservableObject {
|
|||
func applySettingsToInstalledPackages() {
|
||||
self.validateSettings()
|
||||
|
||||
let activeKeyboards = self.settingsRepository.readActiveKeyboards()
|
||||
let enabledKeyboards = self.settingsRepository.readEnabledKeyboards()
|
||||
|
||||
// set enabled flag if the keyboard is contained in the set of activeKeyboards
|
||||
// set enabled flag if the keyboard is contained in the set of enabledKeyboards
|
||||
self.installedPackages.forEach { $0.keyboards.forEach
|
||||
{$0.enabled = activeKeyboards.contains($0.keyboardSettingsKey)}
|
||||
{$0.enabled = enabledKeyboards.contains($0.keyboardSettingsKey)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class Keyboard: Identifiable, Hashable, Equatable {
|
|||
public var keyboardDirectoryUrl: URL
|
||||
// the URL of the .kmx file for the package
|
||||
public let kmxFileUrl: URL
|
||||
// the UserDefaults key for this package, used for the selected Keyboard and active keyboards
|
||||
// the UserDefaults key for this package, used for the selected Keyboard and enabled keyboards
|
||||
// the key is in the form "/[package directory]/[package name].kmx"
|
||||
// for example, "/khmer_angkor/khmer_angkor.kmx"
|
||||
public let keyboardSettingsKey: String
|
||||
|
|
|
|||
|
|
@ -100,6 +100,18 @@ public class KeymanPackage: Identifiable, Hashable, Equatable {
|
|||
return keyboard.keyboardSettingsKey
|
||||
}
|
||||
|
||||
public func getEnabledKeyboardsSettingsKeys() -> [String] {
|
||||
var settingsKeyArray = [String]()
|
||||
|
||||
self.keyboards.forEach { keyboard in
|
||||
if (keyboard.enabled) {
|
||||
settingsKeyArray.append(keyboard.keyboardSettingsKey)
|
||||
}
|
||||
}
|
||||
|
||||
return settingsKeyArray
|
||||
}
|
||||
|
||||
public func validate() -> Bool {
|
||||
var validKeyboards = self.keyboards.isEmpty == false
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,19 @@ public struct PackageRepository {
|
|||
return installedPackages
|
||||
}
|
||||
|
||||
/**
|
||||
* delete the package from disk
|
||||
*/
|
||||
func deletePackage(package: KeymanPackage) {
|
||||
print("deleting package: \(package.sourceDirectoryUrl)")
|
||||
do {
|
||||
try FileManager.default.removeItem(at: package.sourceDirectoryUrl)
|
||||
print("deleted package: \(package.sourceDirectoryUrl)")
|
||||
} catch {
|
||||
print("could not delete directory: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the directory tree where keyboards are stored under the standard 'Group Containers' directory
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public struct SettingsRepository {
|
|||
let defaultsSuiteName: String
|
||||
let defaults: UserDefaults
|
||||
|
||||
let kActiveKeyboardsKey = "KMActiveKeyboardsKey"
|
||||
let kEnabledKeyboardsKey = "KMEnabledKeyboardsKey"
|
||||
let kSelectedKeyboardKey = "KMSelectedKeyboardKey"
|
||||
let kPersistedOptionsKey = "KMPersistedOptionsKey"
|
||||
let kDataModelVersionKey = "KMDataModelVersion"
|
||||
|
|
@ -38,38 +38,69 @@ public struct SettingsRepository {
|
|||
self.defaults = userDefaults
|
||||
}
|
||||
|
||||
public func readActiveKeyboards() -> Set<String> {
|
||||
let activeKeyboardsArray = self.defaults.stringArray(forKey: kActiveKeyboardsKey) ?? []
|
||||
let activeKeyboardsSet = Set(activeKeyboardsArray)
|
||||
/**
|
||||
* get the list of enabled keyboards from the UserDefaults
|
||||
* The enabled keyboards are those that are shown in the Keyman menu
|
||||
* when it is selected from the System Input Source menu.
|
||||
*/
|
||||
public func readEnabledKeyboards() -> Set<String> {
|
||||
let enabledKeyboardsArray = self.defaults.stringArray(forKey: kEnabledKeyboardsKey) ?? []
|
||||
let enabledKeyboardsSet = Set(enabledKeyboardsArray)
|
||||
|
||||
return activeKeyboardsSet
|
||||
return enabledKeyboardsSet
|
||||
}
|
||||
|
||||
public func writeActiveKeyboards(activeKeyboardsArray: [String]) {
|
||||
self.defaults.set(activeKeyboardsArray, forKey: kActiveKeyboardsKey)
|
||||
/**
|
||||
* update the list of enabled keyboards in the UserDefaults
|
||||
*/
|
||||
public func writeEnabledKeyboards(enabledKeyboardsArray: [String]) {
|
||||
self.defaults.set(enabledKeyboardsArray, forKey: kEnabledKeyboardsKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* return the selected keyboard from the UserDefaults
|
||||
* The selected keyboard is the one that Keyman is applying for each keydown event.
|
||||
*/
|
||||
public func readSelectedKeyboard() -> String {
|
||||
return self.defaults.string(forKey: kSelectedKeyboardKey) ?? ""
|
||||
}
|
||||
|
||||
/**
|
||||
* update the selected keyboard keyboards in the UserDefaults
|
||||
*/
|
||||
public func writeSelectedKeyboard(keyboardName: String) {
|
||||
self.defaults.set(keyboardName, forKey: kSelectedKeyboardKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* read the boolean setting which allows a Sentry error to be generated for testing
|
||||
* This value is set in the **standard** application UserDefaults -- not the shared app group defaults.
|
||||
* This is necessary because it must be set to true from the command line.
|
||||
*/
|
||||
public func readForceSentryError() -> Bool {
|
||||
return self.defaults.bool(forKey: kShowOskOnActivateKey)
|
||||
}
|
||||
|
||||
public func readShowOskOnActivate() -> Bool {
|
||||
return self.defaults.bool(forKey: kShowOskOnActivateKey)
|
||||
return UserDefaults.standard.bool(forKey: kShowOskOnActivateKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* read the data model version for the settings to know what values and format to expect
|
||||
*/
|
||||
public func readDataModelVersion() -> Int {
|
||||
// note that zero is returned if key is not found in UserDefaults,
|
||||
return self.defaults.integer(forKey: kDataModelVersionKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* read the boolean setting which indicates whether the OSK is opened when the input method is activated
|
||||
* There may be no need to read this from with the config app.
|
||||
*/
|
||||
public func readShowOskOnActivate() -> Bool {
|
||||
return self.defaults.bool(forKey: kShowOskOnActivateKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* read the list of persisted options that are recorded dynamically from the input method
|
||||
* There may be no need to read this from with the config app.
|
||||
*/
|
||||
public func readPersistedOptions() -> Dictionary<String, Any> {
|
||||
if let options: Dictionary<String, Any> = self.defaults.dictionary(forKey: kPersistedOptionsKey) {
|
||||
return options
|
||||
|
|
@ -78,17 +109,25 @@ public struct SettingsRepository {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* for debugging: prints UserDefaults values to the console
|
||||
* with app group UserDefaults, there is no way to view from the command line
|
||||
* (unlike standard application-level UserDefaults)
|
||||
*/
|
||||
public func logSettings() {
|
||||
print("UserDefaults:")
|
||||
print("\(kSelectedKeyboardKey): \(self.readSelectedKeyboard())")
|
||||
print("\(kDataModelVersionKey): \(self.readDataModelVersion())")
|
||||
print("\(kForceSentryErrorKey): \(self.readForceSentryError())")
|
||||
print("\(kShowOskOnActivateKey): \(self.readShowOskOnActivate())")
|
||||
print("\(kActiveKeyboardsKey): \(self.readActiveKeyboards())")
|
||||
print("\(kEnabledKeyboardsKey): \(self.readEnabledKeyboards())")
|
||||
print("\(kPersistedOptionsKey): \(self.readPersistedOptions())")
|
||||
}
|
||||
|
||||
/**
|
||||
* for debugging: clear all the entries for the app group UserDefaults
|
||||
* unlike standard application-level UserDefaults, there is no way to view from the command line
|
||||
*/
|
||||
public func clearSettings() {
|
||||
self.defaults.dictionaryRepresentation().keys.forEach { key in
|
||||
self.defaults.removeObject(forKey: key)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue