M=QSRqCh
zfz?BjWLTob{CLfaGSEnZ5O<>gt`O#!P^zLg(qIxDX36|lvZ=T_N6-5hXpDl1`!o|(Hv?gT(H(AthF
zceusZKOYn7ck$urBbB@gI1V3R&P9#$;#Y=GnVku;8V|KWVhNjS{e4yI9M7HBGi
z&TQd3ZUVm$v5c?&ESC_Hk{4mC6lTz-$cPf6Hk
Date: Sat, 13 Jan 2024 09:43:49 +0800
Subject: [PATCH 10/72] fix: crash SIGABRT: *** -[EZTextView
replaceCharactersInRange:withString:]: nil NSString given
---
Easydict/Feature/ViewController/View/TextView/EZTextView.m | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Easydict/Feature/ViewController/View/TextView/EZTextView.m b/Easydict/Feature/ViewController/View/TextView/EZTextView.m
index ba8edbc24..fcc3dc36e 100644
--- a/Easydict/Feature/ViewController/View/TextView/EZTextView.m
+++ b/Easydict/Feature/ViewController/View/TextView/EZTextView.m
@@ -246,6 +246,11 @@ - (void)updatePlaceholderVisibility {
}
- (void)setString:(NSString *)string {
+ // Fix: SIGABRT: *** -[EZTextView replaceCharactersInRange:withString:]: nil NSString given.
+ if (!string) {
+ string = @"";
+ }
+
[super setString:string];
[self updatePlaceholderVisibility];
}
From bec66dad6df7427a9abff7af1419644c73cfde62 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Sun, 14 Jan 2024 11:48:51 +0800
Subject: [PATCH 11/72] chore: auto add .git for Alamofire
---
Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 4516ef259..bc7e5cc78 100644
--- a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -12,7 +12,7 @@
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
- "location" : "https://github.com/Alamofire/Alamofire",
+ "location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "3dc6a42c7727c49bf26508e29b0a0b35f9c7e1ad",
"version" : "5.8.1"
From 35222c2e150bebe2fb200b7c0fbb5236e8229cb5 Mon Sep 17 00:00:00 2001
From: Lava <34743145+CanglongCl@users.noreply.github.com>
Date: Sun, 14 Jan 2024 01:25:22 -0800
Subject: [PATCH 12/72] Introduce `Defaults` package into project and implement
Setting - General with SwiftUI (#323)
* import Defaults package
* refactor EZConfiguration using Defaults package and Implement Boolean options in SwiftUI Setting View
* complete enum options in `GeneralTab` of `Setting`
* Font Size and Appearance setting
* rename EZConfiguration file in swift to Configuration
* rename `TTSService` with `TTSServiceType`
* renaming TTSService to TTSServiceType and remove EZ prefix of some file
* maintain the order of languages
* filter classical Chinese in first/second language options
* `Defaults ` package use version instead of branch
---
Easydict.xcodeproj/project.pbxproj | 73 +++
.../xcshareddata/swiftpm/Package.resolved | 9 +
Easydict/App/Localizable.xcstrings | 532 +++++++++++++++++-
.../Feature/Configuration/Appearance.swift | 3 +-
.../Feature/Configuration/EZConfiguration.h | 1 +
.../Tencent/TencentTranslateType.swift | 14 -
.../NewApp/Configuration/Configuration.swift | 55 ++
Easydict/NewApp/Model/TTSServiceType.swift | 80 +++
.../LanguageDetectOptimizeExtensions.swift | 32 ++
.../Extensions/LanguageExtensions.swift | 78 +++
.../ShowWindowPositionExtensions.swift | 34 ++
.../Extensions/WindowTypeExtensions.swift | 34 ++
.../View/SettingView/Tabs/GeneralTab.swift | 268 ++++++++-
13 files changed, 1176 insertions(+), 37 deletions(-)
create mode 100644 Easydict/NewApp/Configuration/Configuration.swift
create mode 100644 Easydict/NewApp/Model/TTSServiceType.swift
create mode 100644 Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift
create mode 100644 Easydict/NewApp/Utility/Extensions/LanguageExtensions.swift
create mode 100644 Easydict/NewApp/Utility/Extensions/ShowWindowPositionExtensions.swift
create mode 100644 Easydict/NewApp/Utility/Extensions/WindowTypeExtensions.swift
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index 56aeb6d3f..ec44cd1ab 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -260,6 +260,13 @@
DC3C643F2B187119008EEDD8 /* ChangeFontSizeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3C643E2B187119008EEDD8 /* ChangeFontSizeView.swift */; };
DC6D9C872B352EBC0055EFFC /* FontSizeHintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C862B352EBC0055EFFC /* FontSizeHintView.swift */; };
DC6D9C892B3969510055EFFC /* Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C882B3969510055EFFC /* Appearance.swift */; };
+ EA3B81F92B5254AA004C0E8B /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3B81F82B5254AA004C0E8B /* Configuration.swift */; };
+ EA3B81FC2B52555C004C0E8B /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = EA3B81FB2B52555C004C0E8B /* Defaults */; };
+ EA9943E32B534C3300EE7B97 /* TTSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */; };
+ EA9943E82B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */; };
+ EA9943EE2B5353AB00EE7B97 /* WindowTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */; };
+ EA9943F02B5354C400EE7B97 /* ShowWindowPositionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */; };
+ EA9943F22B5358BF00EE7B97 /* LanguageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -737,6 +744,12 @@
DC3C643E2B187119008EEDD8 /* ChangeFontSizeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeFontSizeView.swift; sourceTree = ""; };
DC6D9C862B352EBC0055EFFC /* FontSizeHintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSizeHintView.swift; sourceTree = ""; };
DC6D9C882B3969510055EFFC /* Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = ""; };
+ EA3B81F82B5254AA004C0E8B /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; };
+ EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTSServiceType.swift; sourceTree = ""; };
+ EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageDetectOptimizeExtensions.swift; sourceTree = ""; };
+ EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTypeExtensions.swift; sourceTree = ""; };
+ EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowWindowPositionExtensions.swift; sourceTree = ""; };
+ EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageExtensions.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -778,6 +791,7 @@
03A830922B4073E700112834 /* AppCenterCrashes in Frameworks */,
03022F1C2B35DEBA00B63209 /* Hue in Frameworks */,
03A830952B4076FC00112834 /* FirebaseAnalyticsSwift in Frameworks */,
+ EA3B81FC2B52555C004C0E8B /* Defaults in Frameworks */,
03CF27FE2B3DA7D500E19B57 /* Realm in Frameworks */,
03A830902B4073E700112834 /* AppCenterAnalytics in Frameworks */,
03B63ABF2A86967800E155ED /* CoreServices.framework in Frameworks */,
@@ -1994,6 +2008,9 @@
27FE98032B3DCA9F000AD654 /* NewApp */ = {
isa = PBXGroup;
children = (
+ EA9943E12B534C2900EE7B97 /* Model */,
+ EA9943DD2B534BAE00EE7B97 /* Utility */,
+ EA3B81F72B52549B004C0E8B /* Configuration */,
27FE95262B3DC55F000AD654 /* EasydictApp.swift */,
27FE98042B3DCB09000AD654 /* NewAppManager.swift */,
27FE98062B3DD525000AD654 /* View */,
@@ -2143,6 +2160,41 @@
path = ChangeFontSizeView;
sourceTree = "";
};
+ EA3B81F72B52549B004C0E8B /* Configuration */ = {
+ isa = PBXGroup;
+ children = (
+ EA3B81F82B5254AA004C0E8B /* Configuration.swift */,
+ );
+ path = Configuration;
+ sourceTree = "";
+ };
+ EA9943DD2B534BAE00EE7B97 /* Utility */ = {
+ isa = PBXGroup;
+ children = (
+ EA9943E62B534D7C00EE7B97 /* Extensions */,
+ );
+ path = Utility;
+ sourceTree = "";
+ };
+ EA9943E12B534C2900EE7B97 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */,
+ );
+ path = Model;
+ sourceTree = "";
+ };
+ EA9943E62B534D7C00EE7B97 /* Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */,
+ EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */,
+ EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */,
+ EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */,
+ );
+ path = Extensions;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -2234,6 +2286,7 @@
038030962B4106800009230C /* CocoaLumberjackSwift */,
038EA1A92B41169C008A6DD1 /* ZipArchive */,
038EA1AC2B41282F008A6DD1 /* MJExtension */,
+ EA3B81FB2B52555C004C0E8B /* Defaults */,
);
productName = Bob;
productReference = C99EEB182385796700FEE666 /* Easydict-debug.app */;
@@ -2292,6 +2345,7 @@
038030932B4106800009230C /* XCRemoteSwiftPackageReference "CocoaLumberjack" */,
038EA1A82B41169C008A6DD1 /* XCRemoteSwiftPackageReference "ZipArchive" */,
038EA1AB2B41282F008A6DD1 /* XCRemoteSwiftPackageReference "MJExtension" */,
+ EA3B81FA2B52555C004C0E8B /* XCRemoteSwiftPackageReference "Defaults" */,
);
productRefGroup = C99EEB192385796700FEE666 /* Products */;
projectDirPath = "";
@@ -2493,6 +2547,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ EA9943EE2B5353AB00EE7B97 /* WindowTypeExtensions.swift in Sources */,
030570E22ADB919900C9905E /* EZAppleScriptManager.m in Sources */,
03991158292927E000E1B06D /* EZTitlebar.m in Sources */,
03D8A65C2A433B4100D9A968 /* EZConfiguration+EZUserData.m in Sources */,
@@ -2591,6 +2646,7 @@
03B0233229231FA6001C7E63 /* MMLog.swift in Sources */,
03DC7C5E2A3ABE28000BF7C9 /* EZConstKey.m in Sources */,
62E2BF4C2B4082BA00E42D38 /* AliTranslateType.swift in Sources */,
+ EA3B81F92B5254AA004C0E8B /* Configuration.swift in Sources */,
03E3E7C22ADE318800812C84 /* EZQueryMenuTextView.m in Sources */,
03B0231829231FA6001C7E63 /* SnipWindowController.m in Sources */,
03542A342936F70F00C34C33 /* EZLanguageManager.m in Sources */,
@@ -2627,6 +2683,7 @@
0333FDA62A035D5700891515 /* NSString+EZChineseText.m in Sources */,
03E02A222924E77100A10260 /* EZMenuItemManager.m in Sources */,
039D119929D5E26300C93F46 /* EZAudioUtils.m in Sources */,
+ EA9943F22B5358BF00EE7B97 /* LanguageExtensions.swift in Sources */,
03B0231729231FA6001C7E63 /* Snip.m in Sources */,
03BFFC6E295FE59C004E033E /* EZQueryResult+EZYoudaoDictModel.m in Sources */,
DC3C643F2B187119008EEDD8 /* ChangeFontSizeView.swift in Sources */,
@@ -2644,10 +2701,12 @@
0320C5872B29F35700861B3D /* QueryServiceRecord.swift in Sources */,
03FC699A2B39D13A0035D2DA /* EZOpenAIChatResponse.m in Sources */,
03B022FA29231FA6001C7E63 /* EZServiceTypes.m in Sources */,
+ EA9943F02B5354C400EE7B97 /* ShowWindowPositionExtensions.swift in Sources */,
03B0233129231FA6001C7E63 /* MMCrash.m in Sources */,
03B0232629231FA6001C7E63 /* NSAttributedString+MM.m in Sources */,
03542A402937B3C900C34C33 /* EZOCRResult.m in Sources */,
C4DD01E92B12B3C80025EE8E /* TencentService.swift in Sources */,
+ EA9943E32B534C3300EE7B97 /* TTSServiceType.swift in Sources */,
036A0DBB2AD941F9006E6D4F /* EZReplaceTextButton.m in Sources */,
03DC7C662A3CA465000BF7C9 /* HWSegmentedControl.m in Sources */,
037E006D2B3DC098006491C6 /* EZOpenAIService+EZPromptMessages.m in Sources */,
@@ -2683,6 +2742,7 @@
DC6D9C872B352EBC0055EFFC /* FontSizeHintView.swift in Sources */,
03B0232429231FA6001C7E63 /* NSUserDefaults+MM.m in Sources */,
03542A5E2938F05B00C34C33 /* EZLanguageModel.m in Sources */,
+ EA9943E82B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift in Sources */,
03F639952AA6CFBB009B9914 /* EZBingConfig.m in Sources */,
03D2A3E329F4C6F50035CED4 /* EZNetworkManager.m in Sources */,
0309E1ED292B439A00AFB76A /* EZTextView.m in Sources */,
@@ -3251,6 +3311,14 @@
minimumVersion = 5.8.1;
};
};
+ EA3B81FA2B52555C004C0E8B /* XCRemoteSwiftPackageReference "Defaults" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/sindresorhus/Defaults.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 7.3.1;
+ };
+ };
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@@ -3329,6 +3397,11 @@
package = 2721E4CE2AFE920700A059AC /* XCRemoteSwiftPackageReference "Alamofire" */;
productName = Alamofire;
};
+ EA3B81FB2B52555C004C0E8B /* Defaults */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = EA3B81FA2B52555C004C0E8B /* XCRemoteSwiftPackageReference "Defaults" */;
+ productName = Defaults;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = C99EEB102385796700FEE666 /* Project object */;
diff --git a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
index bc7e5cc78..9c63ea7f5 100644
--- a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -54,6 +54,15 @@
"version" : "1.8.0"
}
},
+ {
+ "identity" : "defaults",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/sindresorhus/Defaults.git",
+ "state" : {
+ "revision" : "3efef5a28ebdbbe922d4a2049493733ed14475a6",
+ "version" : "7.3.1"
+ }
+ },
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index 160e6409b..aa35e533b 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -845,7 +845,7 @@
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "划词内容为空时生效"
+ "value" : "划词内容为空时禁用复制提示音"
}
}
}
@@ -907,7 +907,7 @@
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
- "state" : "translated",
+ "state" : "needs_review",
"value" : "以 SwiftUI App模式启动,重启App生效"
}
}
@@ -1675,6 +1675,9 @@
}
}
}
+ },
+ "none_window" : {
+
},
"ocr_result_is_empty" : {
"localizations" : {
@@ -2173,6 +2176,528 @@
}
}
},
+ "setting.general.advance.default_tts_service" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Default TTS Service"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "默认 TTS 服务"
+ }
+ }
+ }
+ },
+ "setting.general.advance.enable_beta_feature" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Enable Beta features"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "启用 Beta 特性"
+ }
+ }
+ }
+ },
+ "setting.general.advance.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Advance"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "高级"
+ }
+ }
+ }
+ },
+ "setting.general.appearance.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Appearance"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "外观"
+ }
+ }
+ }
+ },
+ "setting.general.appearance.light_dark_appearance" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Appearance"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "外观"
+ }
+ }
+ }
+ },
+ "setting.general.auto_copy.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Auto Copy"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "自动复制"
+ }
+ }
+ }
+ },
+ "setting.general.auto_query.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Auto Query"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "自动查询"
+ }
+ }
+ }
+ },
+ "setting.general.font.font_size.label" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Font Size"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "字体大小"
+ }
+ }
+ }
+ },
+ "setting.general.font.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Font"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "字体"
+ }
+ }
+ }
+ },
+ "setting.general.input.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Input Field"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "输入框"
+ }
+ }
+ }
+ },
+ "setting.general.language.duplicated_alert %@%@%@" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "The first language should not be the same as the second language (%1$@). The %2$@ was replaced with %3$@."
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "第一语言不能与第二语言相同(%1$@)。%2$@已被替换为%3$@。"
+ }
+ }
+ }
+ },
+ "setting.general.language.duplicated_alert.field.first" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "first language"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "第二语言"
+ }
+ }
+ }
+ },
+ "setting.general.language.duplicated_alert.field.second" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "second language"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "第一语言"
+ }
+ }
+ }
+ },
+ "setting.general.language.duplicated_alert.title" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Language Duplicated"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "语言重复"
+ }
+ }
+ }
+ },
+ "setting.general.language.first_language" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "First Language"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "第一语言"
+ }
+ }
+ }
+ },
+ "setting.general.language.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Language"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "语言"
+ }
+ }
+ }
+ },
+ "setting.general.language.language_detect_optimize" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Language Detection"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "语种识别"
+ }
+ }
+ }
+ },
+ "setting.general.language.second_language" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Second Language"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "第二语言"
+ }
+ }
+ }
+ },
+ "setting.general.mouse_query.adjust_pop_button_origin" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Adjust Query Icon Position to avoid conflict with PopClip"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "调整查询图标位置(避免和 PopClip 显示冲突)"
+ }
+ }
+ }
+ },
+ "setting.general.mouse_query.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Query with Mouse"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "鼠标查询"
+ }
+ }
+ }
+ },
+ "setting.general.quick_link.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Quick Link"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "快捷功能"
+ }
+ }
+ }
+ },
+ "setting.general.voice.auto_play_word_audio" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "查询英语单词后自动播放发音"
+ }
+ }
+ }
+ },
+ "setting.general.voice.disable_empty_copy_beep_msg" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Disable 'beep' when selected text is empty"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "划词内容为空时"
+ }
+ }
+ }
+ },
+ "setting.general.voice.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Sound"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "声音"
+ }
+ }
+ }
+ },
+ "setting.general.window.fixed_window_position" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Floating Window Position"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "侧悬浮窗口位置"
+ }
+ }
+ }
+ },
+ "setting.general.window.mouse_select_translate_window_type" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Mouse Window Type"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "鼠标划词窗口类型"
+ }
+ }
+ }
+ },
+ "setting.general.window.shortcut_select_translate_window_type" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Shortcut Window Type"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "快捷键划词窗口类型"
+ }
+ }
+ }
+ },
+ "setting.general.windows.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Window"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "窗口"
+ }
+ }
+ }
+ },
+ "setting.tts_service.options.apple" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Apple"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "苹果"
+ }
+ }
+ }
+ },
+ "setting.tts_service.options.baidu" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Baidu"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "百度"
+ }
+ }
+ }
+ },
+ "setting.tts_service.options.bing" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Bing"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Bing"
+ }
+ }
+ }
+ },
+ "setting.tts_service.options.google" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Google"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Google"
+ }
+ }
+ }
+ },
+ "setting.tts_service.options.youdao" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Youdao"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "有道"
+ }
+ }
+ }
+ },
"Settings..." : {
"localizations" : {
"zh-Hans" : {
@@ -2516,6 +3041,9 @@
}
}
}
+ },
+ "unknown_option" : {
+
},
"unpin" : {
"localizations" : {
diff --git a/Easydict/Feature/Configuration/Appearance.swift b/Easydict/Feature/Configuration/Appearance.swift
index 3a59f1124..eb49593cc 100644
--- a/Easydict/Feature/Configuration/Appearance.swift
+++ b/Easydict/Feature/Configuration/Appearance.swift
@@ -6,9 +6,10 @@
// Copyright © 2023 izual. All rights reserved.
//
+import Defaults
import Foundation
-@objc enum AppearenceType: Int, CaseIterable {
+@objc enum AppearenceType: Int, CaseIterable, Defaults.Serializable {
case followSystem = 0
case light
case dark
diff --git a/Easydict/Feature/Configuration/EZConfiguration.h b/Easydict/Feature/Configuration/EZConfiguration.h
index bc07911ee..310f47031 100644
--- a/Easydict/Feature/Configuration/EZConfiguration.h
+++ b/Easydict/Feature/Configuration/EZConfiguration.h
@@ -23,6 +23,7 @@ static NSString *const EZFontSizeUpdateNotification = @"EZFontSizeUpdateNotifica
static NSString *const EZIntelligentQueryModeKey = @"IntelligentQueryMode";
+
typedef NS_ENUM(NSUInteger, EZLanguageDetectOptimize) {
EZLanguageDetectOptimizeNone = 0,
EZLanguageDetectOptimizeBaidu = 1,
diff --git a/Easydict/Feature/Service/Tencent/TencentTranslateType.swift b/Easydict/Feature/Service/Tencent/TencentTranslateType.swift
index e82753154..84ac8b712 100644
--- a/Easydict/Feature/Service/Tencent/TencentTranslateType.swift
+++ b/Easydict/Feature/Service/Tencent/TencentTranslateType.swift
@@ -75,17 +75,3 @@ struct TencentTranslateType: Equatable {
return TencentTranslateType(sourceLanguage: fromLanguage, targetLanguage: toLanguage)
}
}
-
-extension [Language] {
- /// Contains Chinese language,
- func containsChinese() -> Bool {
- contains { $0.isKindOfChinese() }
- }
-}
-
-extension Language {
- /// Is kind of Chinese language, means it is simplifiedChinese or traditionalChinese.
- func isKindOfChinese() -> Bool {
- self == .simplifiedChinese || self == .traditionalChinese
- }
-}
diff --git a/Easydict/NewApp/Configuration/Configuration.swift b/Easydict/NewApp/Configuration/Configuration.swift
new file mode 100644
index 000000000..9ecdff830
--- /dev/null
+++ b/Easydict/NewApp/Configuration/Configuration.swift
@@ -0,0 +1,55 @@
+//
+// Configuration.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/12.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import Foundation
+
+extension Defaults.Keys {
+ // rename `from`
+ static let queryFromLanguage = Key("EZConfiguration_kFromKey", default: .auto)
+ // rename `to`
+ static let queryToLanguage = Key("EZConfiguration_kToKey", default: .auto)
+
+ static let firstLanguage = Key("EZConfiguration_kFirstLanguageKey", default: EZLanguageManager.shared().systemPreferredTwoLanguages[0])
+ static let secondLanguage = Key("EZConfiguration_kSecondLanguageKey", default: EZLanguageManager.shared().systemPreferredTwoLanguages[1])
+
+ static let autoSelectText = Key("EZConfiguration_kAutoSelectTextKey", default: true)
+ static let forceAutoGetSelectedText = Key("EZConfiguration_kForceAutoGetSelectedText", default: false)
+
+ static let disableEmptyCopyBeep = Key("EZConfiguration_kDisableEmptyCopyBeepKey", default: true)
+ static let clickQuery = Key("EZConfiguration_kClickQueryKey", default: false)
+ static let autoPlayAudio = Key("EZConfiguration_kAutoPlayAudioKey", default: true)
+ static let launchAtStartup = Key("EZConfiguration_kLaunchAtStartupKey", default: false)
+ static let hideMainWindow = Key("EZConfiguration_kHideMainWindowKey", default: true)
+ static let autoQueryOCRText = Key("EZConfiguration_kAutoQueryOCTTextKey", default: true)
+ static let autoQuerySelectedText = Key("EZConfiguration_kAutoQuerySelectedTextKey", default: true)
+ static let autoQueryPastedText = Key("EZConfiguration_kAutoQueryPastedTextKey", default: false)
+ static let autoCopyOCRText = Key("EZConfiguration_kAutoCopyOCRTextKey", default: false)
+ static let autoCopySelectedText = Key("EZConfiguration_kAutoCopySelectedTextKey", default: false)
+ static let autoCopyFirstTranslatedText = Key("EZConfiguration_kAutoCopyFirstTranslatedTextKey", default: false)
+ static let languageDetectOptimize = Key("EZConfiguration_kLanguageDetectOptimizeTypeKey", default: EZLanguageDetectOptimize.none)
+ @available(macOS 13, *)
+ static let defaultTTSServiceType = Key("EZConfiguration_kDefaultTTSServiceTypeKey", default: TTSServiceType.youdao)
+ static let showGoogleQuickLink = Key("EZConfiguration_kShowGoogleLinkKey", default: true)
+ static let showEudicQuickLink = Key("EZConfiguration_kShowEudicLinkKey", default: true)
+ static let showAppleDictionaryQuickLink = Key("EZConfiguration_kShowAppleDictionaryLinkKey", default: true)
+ static let hideMenuBarIcon = Key("EZConfiguration_kHideMenuBarIconKey", default: false)
+ static let fixedWindowPosition = Key("EZConfiguration_kShowFixedWindowPositionKey", default: .right)
+ static let mouseSelectTranslateWindowType = Key("EZConfiguration_kMouseSelectTranslateWindowTypeKey", default: .mini)
+ static let shortcutSelectTranslateWindowType = Key("EZConfiguration_kShortcutSelectTranslateWindowTypeKey", default: .fixed)
+ static let adjustPopButtonOrigin = Key("EZConfiguration_kAdjustPopButtomOriginKey", default: false)
+ static let allowCrashLog = Key("EZConfiguration_kAllowCrashLogKey", default: true)
+ static let allowAnalytics = Key("EZConfiguration_kAllowAnalyticsKey", default: true)
+ static let clearInput = Key("EZConfiguration_kClearInputKey", default: true)
+ static let enableBetaNewApp = Key("EZConfiguration_kEnableBetaNewAppKey", default: false)
+
+ static let enableBetaFeature = Key("EZBetaFeatureKey", default: false)
+
+ static let appearanceType = Key("EZConfiguration_kApperanceKey", default: .followSystem)
+ static let fontSizeOptionIndex = Key("EZConfiguration_kTranslationControllerFontKey", default: 0)
+}
diff --git a/Easydict/NewApp/Model/TTSServiceType.swift b/Easydict/NewApp/Model/TTSServiceType.swift
new file mode 100644
index 000000000..6eda5cf16
--- /dev/null
+++ b/Easydict/NewApp/Model/TTSServiceType.swift
@@ -0,0 +1,80 @@
+//
+// TTSServiceType.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/13.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import Foundation
+
+@available(macOS 13, *)
+enum TTSServiceType: String, CaseIterable, CustomLocalizedStringResourceConvertible {
+ var localizedStringResource: LocalizedStringResource {
+ switch self {
+ case .youdao:
+ "setting.tts_service.options.youdao"
+ case .bing:
+ "setting.tts_service.options.bing"
+ case .google:
+ "setting.tts_service.options.google"
+ case .baidu:
+ "setting.tts_service.options.baidu"
+ case .apple:
+ "setting.tts_service.options.apple"
+ }
+ }
+
+ case youdao
+ case bing
+ case google
+ case baidu
+ case apple
+}
+
+@available(macOS 13, *)
+extension TTSServiceType: Defaults.Serializable {
+ // while in the future, ServiceType was deleted, then you can safely delete this struct and `bridge`
+ struct TTSServiceTypeBridge: Defaults.Bridge {
+ func serialize(_ value: TTSServiceType?) -> String? {
+ guard let value else { return nil }
+ switch value {
+ case .youdao:
+ return ServiceType.youdao.rawValue
+ case .bing:
+ return ServiceType.bing.rawValue
+ case .google:
+ return ServiceType.google.rawValue
+ case .baidu:
+ return ServiceType.baidu.rawValue
+ case .apple:
+ return ServiceType.apple.rawValue
+ }
+ }
+
+ func deserialize(_ object: String?) -> TTSServiceType? {
+ guard let object else { return nil }
+ switch object {
+ case "Youdao":
+ return .youdao
+ case "Bing":
+ return .bing
+ case "Google":
+ return .google
+ case "Baidu":
+ return .baidu
+ case "Apple":
+ return .apple
+ default:
+ return nil
+ }
+ }
+
+ typealias Value = TTSServiceType
+
+ typealias Serializable = String
+ }
+
+ static let bridge = TTSServiceTypeBridge()
+}
diff --git a/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift b/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift
new file mode 100644
index 000000000..82ec281a1
--- /dev/null
+++ b/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift
@@ -0,0 +1,32 @@
+//
+// LanguageDetectOptimizeExtensions.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/13.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import Foundation
+
+extension EZLanguageDetectOptimize: Defaults.Serializable {}
+
+extension EZLanguageDetectOptimize: CaseIterable {
+ public static let allCases: [EZLanguageDetectOptimize] = [.none, .baidu, .google]
+}
+
+@available(macOS 13, *)
+extension EZLanguageDetectOptimize: CustomLocalizedStringResourceConvertible {
+ public var localizedStringResource: LocalizedStringResource {
+ switch self {
+ case .none:
+ "language_detect_optimize_none"
+ case .google:
+ "language_detect_optimize_google"
+ case .baidu:
+ "language_detect_optimize_baidu"
+ @unknown default:
+ "unknown_option"
+ }
+ }
+}
diff --git a/Easydict/NewApp/Utility/Extensions/LanguageExtensions.swift b/Easydict/NewApp/Utility/Extensions/LanguageExtensions.swift
new file mode 100644
index 000000000..b7cc84eae
--- /dev/null
+++ b/Easydict/NewApp/Utility/Extensions/LanguageExtensions.swift
@@ -0,0 +1,78 @@
+//
+// LanguageExtensions.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/13.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import Foundation
+
+extension Language: Defaults.Serializable {}
+
+extension Language: CaseIterable {
+ public static let allCases: [Language] = EZLanguageModel.allLanguagesDict().sortedKeys().map { rawValue in
+ Language(rawValue: rawValue as String)
+ }
+
+ public static let allAvailableOptions: [Language] = allCases.filter { language in
+ language != .auto && language != .classicalChinese
+ }
+}
+
+public extension Language {
+ var model: EZLanguageModel {
+ EZLanguageModel.allLanguagesDict().object(forKey: rawValue as NSString)
+ }
+
+ var chineseName: String {
+ model.chineseName
+ }
+
+ var englishName: String {
+ model.englishName.rawValue
+ }
+
+ var localName: String {
+ model.localName
+ }
+
+ var flagEmoji: String {
+ model.flagEmoji
+ }
+
+ var voiceName: String {
+ model.voiceName
+ }
+
+ var localeIdentifier: String {
+ model.localeIdentifier
+ }
+
+ var localizedName: String {
+ if EZLanguageManager.shared().isSystemChineseFirstLanguage() {
+ chineseName
+ } else {
+ if self == .auto {
+ "Auto"
+ } else {
+ englishName
+ }
+ }
+ }
+}
+
+extension [Language] {
+ /// Contains Chinese language,
+ func containsChinese() -> Bool {
+ contains { $0.isKindOfChinese() }
+ }
+}
+
+extension Language {
+ /// Is kind of Chinese language, means it is simplifiedChinese or traditionalChinese.
+ func isKindOfChinese() -> Bool {
+ self == .simplifiedChinese || self == .traditionalChinese
+ }
+}
diff --git a/Easydict/NewApp/Utility/Extensions/ShowWindowPositionExtensions.swift b/Easydict/NewApp/Utility/Extensions/ShowWindowPositionExtensions.swift
new file mode 100644
index 000000000..e531d64c1
--- /dev/null
+++ b/Easydict/NewApp/Utility/Extensions/ShowWindowPositionExtensions.swift
@@ -0,0 +1,34 @@
+//
+// ShowWindowPositionExtensions.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/13.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import Foundation
+
+extension EZShowWindowPosition: Defaults.Serializable {}
+
+extension EZShowWindowPosition: CaseIterable {
+ public static let allCases: [EZShowWindowPosition] = [.right, .mouse, .former, .center]
+}
+
+@available(macOS 13, *)
+extension EZShowWindowPosition: CustomLocalizedStringResourceConvertible {
+ public var localizedStringResource: LocalizedStringResource {
+ switch self {
+ case .right:
+ "fixed_window_position_right"
+ case .mouse:
+ "fixed_window_position_mouse"
+ case .former:
+ "fixed_window_position_former"
+ case .center:
+ "fixed_window_position_center"
+ @unknown default:
+ "unknown_option"
+ }
+ }
+}
diff --git a/Easydict/NewApp/Utility/Extensions/WindowTypeExtensions.swift b/Easydict/NewApp/Utility/Extensions/WindowTypeExtensions.swift
new file mode 100644
index 000000000..34b60c049
--- /dev/null
+++ b/Easydict/NewApp/Utility/Extensions/WindowTypeExtensions.swift
@@ -0,0 +1,34 @@
+//
+// WindowTypeExtensions.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/13.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import Foundation
+
+extension EZWindowType: Defaults.Serializable {}
+
+public extension EZWindowType {
+ static let availableOptions: [EZWindowType] = [.mini, .fixed]
+}
+
+@available(macOS 13, *)
+extension EZWindowType: CustomLocalizedStringResourceConvertible {
+ public var localizedStringResource: LocalizedStringResource {
+ switch self {
+ case .fixed:
+ "fixed_window"
+ case .main:
+ "main_window"
+ case .mini:
+ "mini_window"
+ case .none:
+ "none_window"
+ @unknown default:
+ "unknown_option"
+ }
+ }
+}
diff --git a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
index e9368eaea..852631707 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
@@ -6,6 +6,7 @@
// Copyright © 2023 izual. All rights reserved.
//
+import Defaults
import SwiftUI
@available(macOS 13, *)
@@ -13,44 +14,271 @@ struct GeneralTab: View {
var body: some View {
Form {
Section {
- HStack {
- Text("show_main_window")
- Toggle(isOn: $hideMainWindow) {
- Text("hide_main_window")
+ Picker("setting.general.appearance.light_dark_appearance", selection: $appearanceType) {
+ ForEach(AppearenceType.allCases, id: \.rawValue) { option in
+ Text(option.title)
+ .tag(option)
}
}
- HStack {
- Text("launch")
- Toggle(isOn: $launchAtStartup) {
- Text("launch_at_startup")
+ } header: {
+ Text("setting.general.appearance.header")
+ }
+ Section {
+ FirstAndSecondLanguageSettingView()
+ Picker("setting.general.language.language_detect_optimize", selection: $languageDetectOptimize) {
+ ForEach(EZLanguageDetectOptimize.allCases, id: \.rawValue) { option in
+ Text(option.localizedStringResource)
+ .tag(option)
+ }
+ }
+ } header: {
+ Text("setting.general.language.header")
+ }
+
+ Section {
+ Toggle("auto_show_query_icon", isOn: $autoSelectText)
+ Toggle("force_auto_get_selected_text", isOn: $forceAutoGetSelectedText)
+ Toggle("click_icon_query_info", isOn: $clickQuery)
+ Toggle("setting.general.mouse_query.adjust_pop_button_origin", isOn: $adjustPopButtonOrigin) // 调整查询图标位置:
+ } header: {
+ Text("setting.general.mouse_query.header")
+ }
+
+ Section {
+ Toggle("setting.general.voice.disable_empty_copy_beep_msg", isOn: $disableEmptyCopyBeep) // 禁用提示音:划词内容为空时生效
+ Toggle("setting.general.voice.auto_play_word_audio", isOn: $autoPlayAudio) // 查询英语单词后自动播放发音
+ } header: {
+ Text("setting.general.voice.header")
+ }
+
+ Section {
+ Picker("setting.general.window.mouse_select_translate_window_type", selection: $mouseSelectTranslateWindowType) {
+ ForEach(EZWindowType.availableOptions, id: \.rawValue) { option in
+ Text(option.localizedStringResource)
+ .tag(option)
}
}
- HStack {
- Text("menu_bar_icon")
- Toggle(isOn: $hideMenuBarIcon) {
- Text("hide_menu_bar_icon")
+ Picker("setting.general.window.shortcut_select_translate_window_type", selection: $shortcutSelectTranslateWindowType) {
+ ForEach(EZWindowType.availableOptions, id: \.rawValue) { option in
+ Text(option.localizedStringResource)
+ .tag(option)
}
}
- HStack {
- Text("beta_new_app")
- Toggle(isOn: $enableBetaNewApp) {
- Text("enable_beta_new_app")
+ Picker("setting.general.window.fixed_window_position", selection: $fixedWindowPosition) {
+ ForEach(EZShowWindowPosition.allCases, id: \.rawValue) { option in
+ Text(option.localizedStringResource)
+ .tag(option)
}
}
+ } header: {
+ Text("setting.general.windows.header")
+ }
+
+ Section {
+ Toggle("clear_input_when_translating", isOn: $clearInput)
+ } header: {
+ Text("setting.general.input.header")
+ }
+
+ Section {
+ Toggle("auto_query_ocr_text", isOn: $autoQueryOCRText)
+ Toggle("auto_query_selected_text", isOn: $autoQuerySelectedText)
+ Toggle("auto_query_pasted_text", isOn: $autoQueryPastedText)
+ } header: {
+ Text("setting.general.auto_query.header")
+ }
+
+ Section {
+ Toggle("auto_copy_ocr_text", isOn: $autoCopyOCRText)
+ Toggle("auto_copy_selected_text", isOn: $autoCopySelectedText)
+ Toggle("auto_copy_first_translated_text", isOn: $autoCopyFirstTranslatedText)
+ } header: {
+ Text("setting.general.auto_copy.header")
+ }
+
+ Section {
+ Toggle("show_google_quick_link", isOn: $showGoogleQuickLink)
+ Toggle("show_eudic_quick_link", isOn: $showEudicQuickLink)
+ Toggle("show_apple_dictionary_quick_link", isOn: $showAppleDictionaryQuickLink)
+ } header: {
+ Text("setting.general.quick_link.header")
+ }
+
+ Section {
+ let bindingFontSize = Binding(get: {
+ Double(fontSizeOptionIndex)
+ }, set: { newValue in
+ fontSizeOptionIndex = UInt(newValue)
+ })
+ Slider(value: bindingFontSize, in: 0.0 ... 4.0, step: 1) {
+ Text("setting.general.font.font_size.label")
+ } minimumValueLabel: {
+ Text("small")
+ .font(.system(size: 10))
+ } maximumValueLabel: {
+ Text("large")
+ .font(.system(size: 14))
+ }
+ } header: {
+ Text("setting.general.font.header")
+ } footer: {
+ Text("hints_keyboard_shortcuts_font_size")
+ .font(.footnote)
+ }
+
+ Section {
+ Toggle(isOn: $launchAtStartup) {
+ Text("launch_at_startup")
+ }
+ Toggle(isOn: $hideMainWindow) {
+ Text("hide_main_window")
+ }
+ Toggle(isOn: $hideMenuBarIcon) {
+ Text("hide_menu_bar_icon")
+ }
} header: {
Text("other")
}
+
+ Section {
+ Picker("setting.general.advance.default_tts_service", selection: $defaultTTSServiceType) {
+ ForEach(TTSServiceType.allCases, id: \.rawValue) { option in
+ Text(option.localizedStringResource)
+ .tag(option)
+ }
+ }
+ Toggle("setting.general.advance.enable_beta_feature", isOn: $enableBetaFeature)
+ Toggle(isOn: $enableBetaNewApp) {
+ Text("enable_beta_new_app")
+ }
+ } header: {
+ Text("setting.general.advance.header")
+ }
}
.formStyle(.grouped)
}
- @AppStorage(kHideMainWindowKey) private var hideMainWindow = false
- @AppStorage(kLaunchAtStartupKey) private var launchAtStartup = false
- @AppStorage(kHideMenuBarIconKey) private var hideMenuBarIcon = false
- @AppStorage(kEnableBetaNewAppKey) private var enableBetaNewApp = false
+ @Default(.autoSelectText) private var autoSelectText
+ @Default(.forceAutoGetSelectedText) private var forceAutoGetSelectedText
+ @Default(.clickQuery) private var clickQuery
+ @Default(.adjustPopButtonOrigin) private var adjustPopButtonOrigin
+
+ @Default(.clearInput) private var clearInput
+
+ @Default(.disableEmptyCopyBeep) private var disableEmptyCopyBeep
+ @Default(.autoPlayAudio) private var autoPlayAudio
+
+ @Default(.autoQueryOCRText) private var autoQueryOCRText
+ @Default(.autoQuerySelectedText) private var autoQuerySelectedText
+ @Default(.autoQueryPastedText) private var autoQueryPastedText
+
+ @Default(.autoCopyOCRText) private var autoCopyOCRText
+ @Default(.autoCopySelectedText) private var autoCopySelectedText
+ @Default(.autoCopyFirstTranslatedText) private var autoCopyFirstTranslatedText
+
+ @Default(.showGoogleQuickLink) private var showGoogleQuickLink
+ @Default(.showEudicQuickLink) private var showEudicQuickLink
+ @Default(.showAppleDictionaryQuickLink) private var showAppleDictionaryQuickLink
+
+ @Default(.hideMainWindow) private var hideMainWindow
+ @Default(.launchAtStartup) private var launchAtStartup
+ @Default(.hideMenuBarIcon) private var hideMenuBarIcon
+ @Default(.enableBetaNewApp) private var enableBetaNewApp
+
+ @Default(.languageDetectOptimize) private var languageDetectOptimize
+ @Default(.defaultTTSServiceType) private var defaultTTSServiceType
+
+ @Default(.fixedWindowPosition) private var fixedWindowPosition
+ @Default(.mouseSelectTranslateWindowType) private var mouseSelectTranslateWindowType
+ @Default(.shortcutSelectTranslateWindowType) private var shortcutSelectTranslateWindowType
+ @Default(.enableBetaFeature) private var enableBetaFeature
+ @Default(.appearanceType) private var appearanceType
+
+ @Default(.fontSizeOptionIndex) private var fontSizeOptionIndex
}
@available(macOS 13, *)
#Preview {
GeneralTab()
}
+
+@available(macOS 13, *)
+private struct FirstAndSecondLanguageSettingView: View {
+ var body: some View {
+ Group {
+ Picker("setting.general.language.first_language", selection: $firstLanguage) {
+ ForEach(Language.allAvailableOptions, id: \.rawValue) { option in
+ Text(verbatim: "\(option.flagEmoji) \(option.localizedName)")
+ .tag(option)
+ }
+ }
+ Picker("setting.general.language.second_language", selection: $secondLanguage) {
+ ForEach(Language.allAvailableOptions, id: \.rawValue) { option in
+ Text(verbatim: "\(option.flagEmoji) \(option.localizedName)")
+ .tag(option)
+ }
+ }
+ }
+ .onChange(of: firstLanguage) { [firstLanguage] newValue in
+ let oldValue = firstLanguage
+ if newValue == secondLanguage {
+ secondLanguage = oldValue
+ languageDuplicatedAlert = .init(duplicatedLanguage: newValue, setField: .second, setLanguage: oldValue)
+ }
+ }
+ .onChange(of: secondLanguage) { [secondLanguage] newValue in
+ let oldValue = secondLanguage
+ if newValue == firstLanguage {
+ firstLanguage = oldValue
+ languageDuplicatedAlert = .init(duplicatedLanguage: newValue, setField: .first, setLanguage: oldValue)
+ }
+ }
+ .alert("setting.general.language.duplicated_alert.title", isPresented: showLanguageDuplicatedAlert, presenting: languageDuplicatedAlert) { _ in
+
+ } message: { alert in
+ Text(alert.description)
+ }
+ }
+
+ @Default(.firstLanguage) private var firstLanguage
+ @Default(.secondLanguage) private var secondLanguage
+
+ private struct LanguageDuplicateAlert: CustomStringConvertible {
+ var description: String {
+ // First language should not be same as second language. (\(duplicatedLanguage))
+ // \(setField) is replaced with \(setLanguage).
+ String(localized: "setting.general.language.duplicated_alert \(duplicatedLanguage.localizedName)\(String(localized: setField.localizedStringResource))\(setLanguage.localizedName)")
+ }
+
+ let duplicatedLanguage: Language
+
+ let setField: Field
+
+ let setLanguage: Language
+
+ enum Field: CustomLocalizedStringResourceConvertible {
+ var localizedStringResource: LocalizedStringResource {
+ switch self {
+ case .first:
+ "setting.general.language.duplicated_alert.field.first"
+ case .second:
+ "setting.general.language.duplicated_alert.field.second"
+ }
+ }
+
+ case first
+ case second
+ }
+ }
+
+ @State private var languageDuplicatedAlert: LanguageDuplicateAlert?
+ private var showLanguageDuplicatedAlert: Binding {
+ .init {
+ languageDuplicatedAlert != nil
+ } set: { newValue in
+ if !newValue {
+ languageDuplicatedAlert = nil
+ }
+ }
+ }
+}
From 39b7730e9a21793da9e2dfe5e8cb7f71d2d16208 Mon Sep 17 00:00:00 2001
From: liyafly
Date: Sun, 14 Jan 2024 17:56:52 +0800
Subject: [PATCH 13/72] Refactor settings menu in swiftui only UI (#316)
* refactor: refactor the Settings page in SwiftUI
* refactor: add Settings page some localizable
* refactor: Add functionality to list options on menu pages
* Fix Xcode nullable warning
* refactor: renames the repeatedly defined properties
(cherry picked from commit ca75f5a8bc83e2d9b2db6def8c964010cfdfdf00)
* refactor: Perfect SPUStandardUpdaterController delegate implementation
---------
Co-authored-by: Kyle
Co-authored-by: liyafei
Co-authored-by: Tisfeng
---
Easydict/App/Easydict-Bridging-Header.h | 2 +
Easydict/App/Localizable.xcstrings | 122 +++++++++++++++
.../EZCategory/NSViewController+EZWindow.h | 2 +-
.../EZCategory/NSViewController+EZWindow.m | 2 +-
.../EZBaseQueryViewController.h | 2 +-
.../EZBaseQueryViewController.m | 6 +-
.../BaseQueryWindow/EZBaseQueryWindow.m | 2 +-
Easydict/NewApp/EasydictApp.swift | 33 ++++-
Easydict/NewApp/View/MenuItemView.swift | 140 +++++++++++++++++-
9 files changed, 299 insertions(+), 12 deletions(-)
diff --git a/Easydict/App/Easydict-Bridging-Header.h b/Easydict/App/Easydict-Bridging-Header.h
index 2747a587f..3753f8f67 100644
--- a/Easydict/App/Easydict-Bridging-Header.h
+++ b/Easydict/App/Easydict-Bridging-Header.h
@@ -23,3 +23,5 @@
#import "EZConfiguration.h"
#import "NSString+EZConvenience.h"
+#import "EZWindowManager.h"
+#import "NSViewController+EZWindow.h"
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index aa35e533b..924386b73 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -1058,6 +1058,16 @@
}
}
},
+ "Export Log" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "导出日志"
+ }
+ }
+ }
+ },
"Failed to allocate memory" : {
"comment" : "Error reason",
"localizations" : {
@@ -1069,6 +1079,16 @@
}
}
},
+ "Feedback" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "反馈问题"
+ }
+ }
+ }
+ },
"first_language" : {
"localizations" : {
"en" : {
@@ -1283,6 +1303,16 @@
}
}
},
+ "Help" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "帮助"
+ }
+ }
+ }
+ },
"hide" : {
"localizations" : {
"en" : {
@@ -1569,6 +1599,16 @@
}
}
},
+ "Log Directory" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "日志目录"
+ }
+ }
+ }
+ },
"main_window" : {
"localizations" : {
"en" : {
@@ -1612,6 +1652,88 @@
}
}
},
+ "menu_input_translate" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Input Translate"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "输入翻译"
+ }
+ }
+ }
+ },
+ "menu_screenshot_Translate" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Screenshot Translate"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "截图翻译"
+ }
+ }
+ }
+ },
+ "menu_selectWord_Translate" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Select Translate"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "划词翻译"
+ }
+ }
+ }
+ },
+ "menu_show_mini_window" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Show Mini Window"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "显示迷你窗口"
+ }
+ }
+ }
+ },
+ "menu_silent_screenshot_OCR" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Silent Screenshot OCR"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
+ "value" : "静默截图 OCR"
+ }
+ }
+ }
+ },
"mini_window" : {
"localizations" : {
"en" : {
diff --git a/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.h b/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.h
index de32711b0..9890d7b89 100644
--- a/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.h
+++ b/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.h
@@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface NSViewController (EZWindow)
-- (NSWindow *)window;
+- (nullable NSWindow *)window;
@end
diff --git a/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.m b/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.m
index a73e9e2aa..a46341981 100644
--- a/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.m
+++ b/Easydict/Feature/Utility/EZCategory/NSViewController+EZWindow.m
@@ -10,7 +10,7 @@
@implementation NSViewController (EZWindow)
-- (NSWindow *)window {
+- (nullable NSWindow *)window {
NSResponder *responder = self;
while ((responder = [responder nextResponder])) {
if ([responder isKindOfClass:[NSWindow class]]) {
diff --git a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.h b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.h
index e407b1360..e7301b5c8 100644
--- a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.h
+++ b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.h
@@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy) NSString *inputText;
@property (nonatomic, assign) EZWindowType windowType;
-@property (nonatomic, weak) EZBaseQueryWindow *window;
+@property (nullable, nonatomic, weak) EZBaseQueryWindow *baseQueryWindow;
@property (nonatomic, strong, readonly) NSArray *services;
diff --git a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m
index fd28c7928..58d48a8a3 100644
--- a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m
+++ b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m
@@ -522,11 +522,11 @@ - (void)retryQuery {
- (void)focusInputTextView {
// Fix ⚠️: ERROR: Setting as the first responder for window , but it is in a different window ((null))! This would eventually crash when the view is freed. The first responder will be set to nil.
- if (self.queryView.window == self.window) {
+ if (self.queryView.window == self.baseQueryWindow) {
// Need to activate the current application first.
[NSApp activateIgnoringOtherApps:YES];
- [self.window makeFirstResponder:self.queryView.textView];
+ [self.baseQueryWindow makeFirstResponder:self.queryView.textView];
}
}
@@ -1420,7 +1420,7 @@ - (void)updateWindowViewHeightWithLock:(BOOL)lockFlag
// ???: why set window frame will change tableView height?
// ???: why this window animation will block cell rendering?
// [self.window setFrame:safeFrame display:NO animate:animateFlag];
- [self.window setFrame:safeFrame display:NO];
+ [self.baseQueryWindow setFrame:safeFrame display:NO];
// Restore tableView height.
self.tableView.height = tableViewHeight;
diff --git a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryWindow.m b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryWindow.m
index 1e16d79e8..b2b56b8bd 100644
--- a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryWindow.m
+++ b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryWindow.m
@@ -70,7 +70,7 @@ - (void)setWindowType:(EZWindowType)windowType {
- (void)setQueryViewController:(EZBaseQueryViewController *)viewController {
_queryViewController = viewController;
- viewController.window = self;
+ viewController.baseQueryWindow = self;
self.contentViewController = viewController;
}
diff --git a/Easydict/NewApp/EasydictApp.swift b/Easydict/NewApp/EasydictApp.swift
index ae385f4e7..c81ba83f4 100644
--- a/Easydict/NewApp/EasydictApp.swift
+++ b/Easydict/NewApp/EasydictApp.swift
@@ -6,6 +6,7 @@
// Copyright © 2023 izual. All rights reserved.
//
+import Sparkle
import SwiftUI
@main
@@ -20,6 +21,22 @@ enum EasydictCmpatibilityEntry {
}
}
+class SPUUpdaterHelper: NSObject, SPUUpdaterDelegate {
+ func feedURLString(for _: SPUUpdater) -> String? {
+ var feedURLString = "https://raw.githubusercontent.com/tisfeng/Easydict/main/appcast.xml"
+ #if DEBUG
+ feedURLString = "http://localhost:8000/appcast.xml"
+ #endif
+ return feedURLString
+ }
+}
+
+class SPUUserDriverHelper: NSObject, SPUStandardUserDriverDelegate {
+ var supportsGentleScheduledUpdateReminders: Bool {
+ true
+ }
+}
+
struct EasydictApp: App {
@NSApplicationDelegateAdaptor
var delegate: AppDelegate
@@ -35,10 +52,22 @@ struct EasydictApp: App {
#endif
}
+ let userDriverHelper = SPUUserDriverHelper()
+ let upadterHelper = SPUUpdaterHelper()
+
+ private let updaterController: SPUStandardUpdaterController
+
+ init() {
+ // 参考 https://sparkle-project.org/documentation/programmatic-setup/
+ // If you want to start the updater manually, pass false to startingUpdater and call .startUpdater() later
+ // This is where you can also pass an updater delegate if you need one
+ updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: upadterHelper, userDriverDelegate: userDriverHelper)
+ }
+
var body: some Scene {
if #available(macOS 13, *) {
MenuBarExtra(isInserted: $hideMenuBar.toggledValue) {
- MenuItemView()
+ MenuItemView(updater: updaterController.updater)
} label: {
Label {
Text("Easydict")
@@ -49,7 +78,7 @@ struct EasydictApp: App {
.scaledToFit()
}
.help("Easydict 🍃")
- }
+ }.menuBarExtraStyle(.menu)
Settings {
SettingView()
}
diff --git a/Easydict/NewApp/View/MenuItemView.swift b/Easydict/NewApp/View/MenuItemView.swift
index 806abc807..999bd03b3 100644
--- a/Easydict/NewApp/View/MenuItemView.swift
+++ b/Easydict/NewApp/View/MenuItemView.swift
@@ -8,16 +8,44 @@
import Sparkle
import SwiftUI
+import ZipArchive
+
+@available(macOS 13, *)
+final class MenuItemStore: ObservableObject {
+ @Published var canCheckForUpdates = false
+ var updater: SPUUpdater
+ init(updater: SPUUpdater) {
+ self.updater = updater
+ self.updater.publisher(for: \.canCheckForUpdates)
+ .assign(to: &$canCheckForUpdates)
+ }
+}
@available(macOS 13, *)
struct MenuItemView: View {
+ @ObservedObject var store: MenuItemStore
+
+ init(updater: SPUUpdater) {
+ store = MenuItemStore(updater: updater)
+ }
+
var body: some View {
+ // ️.menuBarExtraStyle为 .menu 时某些控件可能会失效 ,只能显示内容(按照菜单项高度、图像以 template 方式渲染)无法交互 ,比如 Stepper、Slider 等,像基本的 Button、Text、Divider、Image 等还是能正常显示的。
+ // Button 和Label的systemImage是不会渲染的
Group {
versionItem
Divider()
+ inputItem
+ screenshotItem
+ selectWordItem
+ miniWindowItem
+ Divider()
+ ocrItem
+ Divider()
settingItem
.keyboardShortcut(.init(","))
checkUpdateItem
+ helpItem
Divider()
quitItem
.keyboardShortcut(.init("q"))
@@ -72,12 +100,81 @@ struct MenuItemView: View {
}
}
+ // MARK: - List of functions
+
+ @ViewBuilder
+ private var inputItem: some View {
+ Button {
+ NSLog("输入翻译")
+ EZWindowManager.shared().inputTranslate()
+ } label: {
+ HStack {
+ Image(systemName: "keyboard")
+ Text("menu_input_translate")
+ }
+ }
+ }
+
+ @ViewBuilder
+ private var screenshotItem: some View {
+ Button {
+ NSLog("截图翻译")
+ EZWindowManager.shared().snipTranslate()
+ } label: {
+ HStack {
+ Image(systemName: "camera.viewfinder")
+ Text("menu_screenshot_Translate")
+ }
+ }
+ }
+
+ @ViewBuilder
+ private var selectWordItem: some View {
+ Button {
+ NSLog("划词翻译")
+ EZWindowManager.shared().selectTextTranslate()
+ } label: {
+ HStack {
+ Image(systemName: "highlighter")
+ Text("menu_selectWord_Translate")
+ }
+ }
+ }
+
+ @ViewBuilder
+ private var miniWindowItem: some View {
+ Button {
+ NSLog("显示迷你窗口")
+ EZWindowManager.shared().showMiniFloatingWindow()
+ } label: {
+ HStack {
+ Image(systemName: "dock.rectangle")
+ Text("menu_show_mini_window")
+ }
+ }
+ }
+
+ @ViewBuilder
+ private var ocrItem: some View {
+ Button {
+ NSLog("静默截图OCR")
+ EZWindowManager.shared().screenshotOCR()
+ } label: {
+ HStack {
+ Image(systemName: "camera.metering.spot")
+ Text("menu_silent_screenshot_OCR")
+ }
+ }
+ }
+
+ // MARK: - Setting
+
@ViewBuilder
private var checkUpdateItem: some View {
Button("check_updates") {
NSLog("检查更新")
- SPUStandardUpdaterController(updaterDelegate: nil, userDriverDelegate: nil).checkForUpdates(nil)
- }
+ store.updater.checkForUpdates()
+ }.disabled(!store.canCheckForUpdates)
}
@ViewBuilder
@@ -87,9 +184,46 @@ struct MenuItemView: View {
NSApplication.shared.terminate(nil)
}
}
+
+ @ViewBuilder
+ private var helpItem: some View {
+ Menu("Help") {
+ Button("Feedback") {
+ guard let versionURL = URL(string: "\(EZGithubRepoEasydictURL)/issues") else {
+ return
+ }
+ openURL(versionURL)
+ }
+ Button("Export Log") {
+ exportLogAction()
+ }
+ Button("Log Directory") {
+ NSLog("日志目录")
+ let logPath = MMManagerForLog.rootLogDirectory() ?? ""
+ let directoryURL = URL(fileURLWithPath: logPath)
+ NSWorkspace.shared.open(directoryURL)
+ }
+ }
+ }
+
+ private func exportLogAction() {
+ NSLog("导出日志")
+ let logPath = MMManagerForLog.rootLogDirectory() ?? ""
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateFormat = "yyyy-MM-dd HH-mm-ss-SSS"
+ let dataString = dateFormatter.string(from: Date())
+ let downloadDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
+ let zipPath = downloadDirectory.appendingPathComponent("Easydict log \(dataString).zip").path(percentEncoded: false)
+ let success = SSZipArchive.createZipFile(atPath: zipPath, withContentsOfDirectory: logPath, keepParentDirectory: false)
+ if success {
+ NSWorkspace.shared.selectFile(zipPath, inFileViewerRootedAtPath: "")
+ } else {
+ MMLogInfo("导出日志失败")
+ }
+ }
}
@available(macOS 13, *)
#Preview {
- MenuItemView()
+ MenuItemView(updater: SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil).updater)
}
From baca1fdef1d0b8fe57e633f518ee8d2b2cd759bf Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Sun, 14 Jan 2024 18:34:01 +0800
Subject: [PATCH 14/72] Add service tab in settings use SwiftUI (#311)
* feat: add service tab screen use SwiftUI
* feat: add service list move function
* fix: Removed a file change
* fix: service list order issue and UI change
* style: format code
* fix: change service tab frame size
* feat: update the style of service list
* style: auto update Localizable.xcstrings
* perf: remove unused variables
* fix: service list index of bounds issue and tap issue
* fix: serviceTypes count is greater than services count, cause crash
* perf: adjust service rowHeight to 30
* perf: hide scrollIndicators
* fix: reset service list selection
* feat: optimize tab selection window transition
* fix: window frame not reset issue
* fix: onTap conflict with onMove in service tab
* fix: remove tap event print
---------
Co-authored-by: tisfeng
---
Easydict.xcodeproj/project.pbxproj | 40 +++++
.../Contents.json | 38 +++++
Easydict/App/Easydict-Bridging-Header.h | 1 +
Easydict/App/Localizable.xcstrings | 3 +
.../Swift/Binding/Binding+DidSet.swift | 23 +++
.../Notification/Notification+Name.swift | 17 ++
Easydict/NewApp/View/ServiceItemView.swift | 41 +++++
.../NewApp/View/SettingView/SettingView.swift | 43 +++++-
.../View/SettingView/Tabs/AboutTab.swift | 2 +
.../View/SettingView/Tabs/ServiceTab.swift | 146 ++++++++++++++++++
Easydict/NewApp/View/TapHandlerView.swift | 42 +++++
Easydict/NewApp/View/WindowAccessor.swift | 23 +++
12 files changed, 415 insertions(+), 4 deletions(-)
create mode 100644 Easydict/App/Assets.xcassets/service_cell_highlight.colorset/Contents.json
create mode 100644 Easydict/Feature/Utility/Swift/Binding/Binding+DidSet.swift
create mode 100644 Easydict/Feature/Utility/Swift/Notification/Notification+Name.swift
create mode 100644 Easydict/NewApp/View/ServiceItemView.swift
create mode 100644 Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
create mode 100644 Easydict/NewApp/View/TapHandlerView.swift
create mode 100644 Easydict/NewApp/View/WindowAccessor.swift
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index ec44cd1ab..7438afb68 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -228,6 +228,12 @@
03FC699A2B39D13A0035D2DA /* EZOpenAIChatResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 03FC69992B39D13A0035D2DA /* EZOpenAIChatResponse.m */; };
03FD68BB2B1DC59600FD388E /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 03FD68BA2B1DC59600FD388E /* CryptoSwift */; };
03FD68BE2B1E151A00FD388E /* String+EncryptAES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */; };
+ 0A057D6D2B499A000025C51D /* ServiceTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A057D6C2B499A000025C51D /* ServiceTab.swift */; };
+ 0A057D6F2B499A0B0025C51D /* ServiceItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A057D6E2B499A0B0025C51D /* ServiceItemView.swift */; };
+ 0A2BA9602B49A989002872A4 /* Binding+DidSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */; };
+ 0A2BA9642B4A3CCD002872A4 /* Notification+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */; };
+ 0AC11B222B4D16A500F07198 /* WindowAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC11B212B4D16A500F07198 /* WindowAccessor.swift */; };
+ 0AC11B242B4E46B300F07198 /* TapHandlerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC11B232B4E46B300F07198 /* TapHandlerView.swift */; };
17BCAEF72B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 17BCAEF52B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m */; };
17BCAEF82B0DFF9000A7D372 /* EZNiuTransTranslate.m in Sources */ = {isa = PBXBuildFile; fileRef = 17BCAEF62B0DFF9000A7D372 /* EZNiuTransTranslate.m */; };
2721E4D02AFE920700A059AC /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 2721E4CF2AFE920700A059AC /* Alamofire */; };
@@ -696,6 +702,12 @@
03FC69992B39D13A0035D2DA /* EZOpenAIChatResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOpenAIChatResponse.m; sourceTree = ""; };
03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+EncryptAES.swift"; sourceTree = ""; };
06E15747A7BD34D510ADC6A8 /* Pods-Easydict.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Easydict.debug.xcconfig"; path = "Target Support Files/Pods-Easydict/Pods-Easydict.debug.xcconfig"; sourceTree = ""; };
+ 0A057D6C2B499A000025C51D /* ServiceTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTab.swift; sourceTree = ""; };
+ 0A057D6E2B499A0B0025C51D /* ServiceItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceItemView.swift; sourceTree = ""; };
+ 0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+DidSet.swift"; sourceTree = ""; };
+ 0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Name.swift"; sourceTree = ""; };
+ 0AC11B212B4D16A500F07198 /* WindowAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowAccessor.swift; sourceTree = ""; };
+ 0AC11B232B4E46B300F07198 /* TapHandlerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapHandlerView.swift; sourceTree = ""; };
17BCAEF32B0DFF9000A7D372 /* EZNiuTransTranslateResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZNiuTransTranslateResponse.h; sourceTree = ""; };
17BCAEF42B0DFF9000A7D372 /* EZNiuTransTranslate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZNiuTransTranslate.h; sourceTree = ""; };
17BCAEF52B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZNiuTransTranslateResponse.m; sourceTree = ""; };
@@ -1806,6 +1818,8 @@
03CF88602B137ECB0030C199 /* Swift */ = {
isa = PBXGroup;
children = (
+ 0A2BA9622B4A3CBB002872A4 /* Notification */,
+ 0A2BA95E2B49A967002872A4 /* Binding */,
03FD68BC2B1E14B500FD388E /* String */,
03CF88612B137ED60030C199 /* Array */,
);
@@ -1984,6 +1998,22 @@
path = String;
sourceTree = "";
};
+ 0A2BA95E2B49A967002872A4 /* Binding */ = {
+ isa = PBXGroup;
+ children = (
+ 0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */,
+ );
+ path = Binding;
+ sourceTree = "";
+ };
+ 0A2BA9622B4A3CBB002872A4 /* Notification */ = {
+ isa = PBXGroup;
+ children = (
+ 0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */,
+ );
+ path = Notification;
+ sourceTree = "";
+ };
17BCAEF22B0DFF9000A7D372 /* Niutrans */ = {
isa = PBXGroup;
children = (
@@ -2022,6 +2052,9 @@
isa = PBXGroup;
children = (
27FE980A2B3DD5D1000AD654 /* MenuItemView.swift */,
+ 0A057D6E2B499A0B0025C51D /* ServiceItemView.swift */,
+ 0AC11B212B4D16A500F07198 /* WindowAccessor.swift */,
+ 0AC11B232B4E46B300F07198 /* TapHandlerView.swift */,
27FE98072B3DD52B000AD654 /* SettingView */,
);
path = View;
@@ -2040,6 +2073,7 @@
isa = PBXGroup;
children = (
278540332B3DE04F004E9488 /* GeneralTab.swift */,
+ 0A057D6C2B499A000025C51D /* ServiceTab.swift */,
276742042B3DC230002A2C75 /* PrivacyTab.swift */,
276742052B3DC230002A2C75 /* AboutTab.swift */,
);
@@ -2571,6 +2605,7 @@
03991166292A8A4400E1B06D /* EZTitleBarMoveView.m in Sources */,
03542A582937CC3200C34C33 /* EZConfiguration.m in Sources */,
27FE98092B3DD536000AD654 /* SettingView.swift in Sources */,
+ 0A057D6F2B499A0B0025C51D /* ServiceItemView.swift in Sources */,
035E37E72A0953120061DFAF /* EZToast.m in Sources */,
03542A492937B5CF00C34C33 /* EZGoogleTranslate.m in Sources */,
03D0435A2928C4C800E7559E /* EZWindowManager.m in Sources */,
@@ -2608,6 +2643,7 @@
27FE95272B3DC55F000AD654 /* EasydictApp.swift in Sources */,
03882F9129D95044005B5A52 /* CTCommon.m in Sources */,
276742082B3DC230002A2C75 /* PrivacyTab.swift in Sources */,
+ 0AC11B242B4E46B300F07198 /* TapHandlerView.swift in Sources */,
03882F8F29D95044005B5A52 /* CTScreen.m in Sources */,
27FE980B2B3DD5D1000AD654 /* MenuItemView.swift in Sources */,
03DC7C6A2A3CA852000BF7C9 /* EZAppCell.m in Sources */,
@@ -2619,6 +2655,7 @@
033B7134293CE2430096E2DF /* EZWebViewTranslator.m in Sources */,
03CF88632B137F650030C199 /* Array+Convenience.swift in Sources */,
03B0231229231FA6001C7E63 /* NSObject+DarkMode.m in Sources */,
+ 0AC11B222B4D16A500F07198 /* WindowAccessor.swift in Sources */,
03B0233829231FA6001C7E63 /* MMOrderedDictionary.m in Sources */,
278540342B3DE04F004E9488 /* GeneralTab.swift in Sources */,
03BDA7BC2A26DA280079D04F /* XPMArgumentSignature.m in Sources */,
@@ -2637,6 +2674,7 @@
03B0230529231FA6001C7E63 /* EZButton.m in Sources */,
03B0232329231FA6001C7E63 /* NSString+MM.m in Sources */,
036196772A000F5900806370 /* NSData+CommonCrypto.m in Sources */,
+ 0A057D6D2B499A000025C51D /* ServiceTab.swift in Sources */,
03882F8D29D95044005B5A52 /* CTView.m in Sources */,
278322602B0FB0EA0026644C /* CaiyunResponse.swift in Sources */,
03B3B8B52925DD3D00168E8D /* EZPopButtonViewController.m in Sources */,
@@ -2662,6 +2700,7 @@
039CC90D292F664E0037B91E /* NSObject+EZWindowType.m in Sources */,
03B0232229231FA6001C7E63 /* NSImage+MM.m in Sources */,
03BB2DEF29F59C8A00447EDD /* EZSymbolImageButton.m in Sources */,
+ 0A2BA9642B4A3CCD002872A4 /* Notification+Name.swift in Sources */,
62A2D03F2A82967F007EEB01 /* EZBingRequest.m in Sources */,
03BDA7BE2A26DA280079D04F /* XPMCountedArgument.m in Sources */,
03D35DAA2AA6C49B00B023FE /* NSString+EZRegex.m in Sources */,
@@ -2706,6 +2745,7 @@
03B0232629231FA6001C7E63 /* NSAttributedString+MM.m in Sources */,
03542A402937B3C900C34C33 /* EZOCRResult.m in Sources */,
C4DD01E92B12B3C80025EE8E /* TencentService.swift in Sources */,
+ 0A2BA9602B49A989002872A4 /* Binding+DidSet.swift in Sources */,
EA9943E32B534C3300EE7B97 /* TTSServiceType.swift in Sources */,
036A0DBB2AD941F9006E6D4F /* EZReplaceTextButton.m in Sources */,
03DC7C662A3CA465000BF7C9 /* HWSegmentedControl.m in Sources */,
diff --git a/Easydict/App/Assets.xcassets/service_cell_highlight.colorset/Contents.json b/Easydict/App/Assets.xcassets/service_cell_highlight.colorset/Contents.json
new file mode 100644
index 000000000..f6b481345
--- /dev/null
+++ b/Easydict/App/Assets.xcassets/service_cell_highlight.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "1.000",
+ "green" : "0.847",
+ "red" : "0.706"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.251",
+ "green" : "0.251",
+ "red" : "0.251"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Easydict/App/Easydict-Bridging-Header.h b/Easydict/App/Easydict-Bridging-Header.h
index 3753f8f67..8030b13fa 100644
--- a/Easydict/App/Easydict-Bridging-Header.h
+++ b/Easydict/App/Easydict-Bridging-Header.h
@@ -21,6 +21,7 @@
#import "entry.h"
#import "AppDelegate.h"
#import "EZConfiguration.h"
+#import "EZLocalStorage.h"
#import "NSString+EZConvenience.h"
#import "EZWindowManager.h"
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index 924386b73..c75689b1e 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -1,6 +1,9 @@
{
"sourceLanguage" : "en",
"strings" : {
+ "" : {
+
+ },
"about" : {
"comment" : "about",
"localizations" : {
diff --git a/Easydict/Feature/Utility/Swift/Binding/Binding+DidSet.swift b/Easydict/Feature/Utility/Swift/Binding/Binding+DidSet.swift
new file mode 100644
index 000000000..244b735cb
--- /dev/null
+++ b/Easydict/Feature/Utility/Swift/Binding/Binding+DidSet.swift
@@ -0,0 +1,23 @@
+//
+// Binding+DidSet.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/6.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import SwiftUI
+
+// Ref https://stackoverflow.com/a/62871938
+// Toggle onChange not trigger issue
+extension Binding {
+ func didSet(execute: @escaping (Value) -> Void) -> Binding {
+ Binding(
+ get: { self.wrappedValue },
+ set: {
+ self.wrappedValue = $0
+ execute($0)
+ }
+ )
+ }
+}
diff --git a/Easydict/Feature/Utility/Swift/Notification/Notification+Name.swift b/Easydict/Feature/Utility/Swift/Notification/Notification+Name.swift
new file mode 100644
index 000000000..71c773f81
--- /dev/null
+++ b/Easydict/Feature/Utility/Swift/Notification/Notification+Name.swift
@@ -0,0 +1,17 @@
+//
+// Notification+Name.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/7.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Foundation
+
+extension Notification.Name {
+ static let serviceHasUpdated = Notification.Name(EZServiceHasUpdatedNotification)
+}
+
+@objc public extension NSNotification {
+ static let serviceHasUpdated = Notification.Name.serviceHasUpdated
+}
diff --git a/Easydict/NewApp/View/ServiceItemView.swift b/Easydict/NewApp/View/ServiceItemView.swift
new file mode 100644
index 000000000..d21f17e98
--- /dev/null
+++ b/Easydict/NewApp/View/ServiceItemView.swift
@@ -0,0 +1,41 @@
+//
+// ServiceItemView.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/6.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import SwiftUI
+
+@available(macOS 13.0, *)
+struct ServiceItemView: View {
+ @Binding var service: QueryService
+
+ var toggleValueChanged: (Bool) -> Void
+
+ var body: some View {
+ HStack {
+ Image(nsImage: NSImage(named: service.serviceType().rawValue) ?? NSImage())
+ .resizable()
+ .frame(maxWidth: 18.0, maxHeight: 18.0)
+
+ Text(service.name())
+
+ Toggle(isOn: $service.enabled.didSet(execute: { value in
+ toggleValueChanged(value)
+ })) {}
+ .toggleStyle(.switch)
+ .controlSize(.small)
+ }
+ .padding(4.0)
+ }
+}
+
+@available(macOS 13, *)
+#Preview {
+ let service = EZLocalStorage.shared().allServices(.mini).first ?? QueryService()
+ return ServiceItemView(service: .constant(service)) { val in
+ print("toggle value changed: \(val)")
+ }
+}
diff --git a/Easydict/NewApp/View/SettingView/SettingView.swift b/Easydict/NewApp/View/SettingView/SettingView.swift
index 2e5da7bf3..8c4fec008 100644
--- a/Easydict/NewApp/View/SettingView/SettingView.swift
+++ b/Easydict/NewApp/View/SettingView/SettingView.swift
@@ -8,20 +8,55 @@
import SwiftUI
+enum SettingTab: Int {
+ case general
+ case service
+ case privacy
+ case about
+}
+
@available(macOS 13, *)
struct SettingView: View {
+ @State private var selection = SettingTab.general.rawValue
+ @State private var window: NSWindow?
+
var body: some View {
- TabView {
+ TabView(selection: $selection.didSet(execute: { _ in
+ resizeWindowFrame()
+ })) {
GeneralTab()
.tabItem { Label("setting_general", systemImage: "gear") }
- .frame(width: 500, height: 400)
+ .tag(SettingTab.general.rawValue)
+
+ ServiceTab()
+ .tabItem { Label("service", systemImage: "briefcase") }
+ .tag(SettingTab.service.rawValue)
+
PrivacyTab()
.tabItem { Label("privacy", systemImage: "hand.raised.square") }
- .frame(width: 500, height: 400)
+ .tag(SettingTab.privacy.rawValue)
+
AboutTab()
.tabItem { Label("about", systemImage: "info.bubble") }
- .frame(width: 500, height: 400)
+ .tag(SettingTab.about.rawValue)
}
+ .background(WindowAccessor(window: $window.didSet(execute: { _ in
+ // reset frame when first launch
+ resizeWindowFrame()
+ })))
+ }
+
+ func resizeWindowFrame() {
+ guard let window else { return }
+
+ let originalFrame = window.frame
+ let newSize = selection == SettingTab.service.rawValue
+ ? CGSize(width: 360, height: 520) : CGSize(width: 500, height: 400)
+
+ let newY = originalFrame.origin.y + originalFrame.size.height - newSize.height
+ let newRect = NSRect(origin: CGPoint(x: originalFrame.origin.x, y: newY), size: newSize)
+
+ window.setFrame(newRect, display: true, animate: true)
}
}
diff --git a/Easydict/NewApp/View/SettingView/Tabs/AboutTab.swift b/Easydict/NewApp/View/SettingView/Tabs/AboutTab.swift
index 92de82c24..aa6f6a4e4 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/AboutTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/AboutTab.swift
@@ -34,7 +34,9 @@ struct AboutTab: View {
}
.padding(.horizontal, 50)
.padding(.vertical, 30)
+ .frame(maxWidth: .infinity)
}
+ .scrollIndicators(.hidden)
.task {
let version = await EZMenuItemManager.shared().fetchRepoLatestVersion(EZGithubRepoEasydict)
await MainActor.run {
diff --git a/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift b/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
new file mode 100644
index 000000000..3da7ca423
--- /dev/null
+++ b/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
@@ -0,0 +1,146 @@
+//
+// ServiceTab.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/6.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import SwiftUI
+
+@available(macOS 13, *)
+struct ServiceTab: View {
+ @State private var windowTypeValue = EZWindowType.mini.rawValue
+ @State private var serviceTypes: [ServiceType] = []
+ @State private var services: [QueryService] = []
+ @State private var selectedIndex: Int?
+ // workaround for tap gesture conflict with onMove
+ @State private var isNeedTapHandler = true
+
+ var segmentCtrl: some View {
+ Picker("", selection: $windowTypeValue) {
+ Text("mini_window")
+ .tag(EZWindowType.mini.rawValue)
+
+ Text("fixed_window")
+ .tag(EZWindowType.fixed.rawValue)
+
+ Text("main_window")
+ .tag(EZWindowType.main.rawValue)
+ }
+ .padding()
+ .pickerStyle(.segmented)
+ .onChange(of: windowTypeValue) { type in
+ loadService(type: type)
+ selectedIndex = nil
+ }
+ }
+
+ var serviceList: some View {
+ List {
+ ForEach(Array(zip(serviceTypes.indices, serviceTypes)), id: \.0) { index, _ in
+ ServiceItemView(
+ service: $services[index]
+ ) { isEnable in
+ serviceToggled(index: index, isEnable: isEnable)
+ selectedIndex = nil
+ isNeedTapHandler = false
+ }
+ .frame(height: 30)
+ .tag(index)
+ .listRowBackground(selectedIndex == index ? Color("service_cell_highlight") : Color.clear)
+ .overlay(TapHandler(tapAction: {
+ if !isNeedTapHandler {
+ isNeedTapHandler.toggle()
+ return
+ }
+ if selectedIndex == nil || selectedIndex != index {
+ selectedIndex = index
+ } else {
+ selectedIndex = nil
+ }
+ }))
+ }
+ .onMove(perform: { indices, newOffset in
+ onServiceItemMove(fromOffsets: indices, toOffset: newOffset)
+ selectedIndex = nil
+ })
+ .listRowSeparator(.hidden)
+ }
+ .scrollIndicators(.hidden)
+ .listStyle(.plain)
+ .clipShape(RoundedRectangle(cornerRadius: 8.0))
+ .padding([.horizontal, .bottom])
+ }
+
+ var body: some View {
+ VStack {
+ segmentCtrl
+
+ serviceList
+ }
+ .onAppear {
+ loadService(type: windowTypeValue)
+ }
+ }
+
+ func loadService(type: Int) {
+ let windowType = EZWindowType(rawValue: type) ?? .none
+ services = EZLocalStorage.shared().allServices(windowType)
+ serviceTypes = services.compactMap { $0.serviceType() }
+ }
+
+ func serviceToggled(index: Int, isEnable: Bool) {
+ let service = services[index]
+ service.enabled = isEnable
+ if isEnable {
+ service.enabledQuery = true
+ }
+ let windowType = EZWindowType(rawValue: windowTypeValue) ?? .none
+ EZLocalStorage.shared().setService(services[index], windowType: windowType)
+ // refresh service list
+ loadService(type: windowTypeValue)
+ postUpdateServiceNotification()
+ }
+
+ func enabledServices(in services: [QueryService]) -> [QueryService] {
+ services.filter(\.enabled)
+ }
+
+ func onServiceItemMove(fromOffsets: IndexSet, toOffset: Int) {
+ let oldEnabledServices = enabledServices(in: services)
+
+ services.move(fromOffsets: fromOffsets, toOffset: toOffset)
+ serviceTypes.move(fromOffsets: fromOffsets, toOffset: toOffset)
+
+ let windowType = EZWindowType(rawValue: windowTypeValue) ?? .none
+ EZLocalStorage.shared().setAllServiceTypes(serviceTypes, windowType: windowType)
+ let newServices = EZLocalStorage.shared().allServices(windowType)
+ let newEnabledServices = enabledServices(in: newServices)
+
+ // post notification after enabled services order changed
+ if isEnabledServicesOrderChanged(source: oldEnabledServices, dest: newEnabledServices) {
+ postUpdateServiceNotification()
+ }
+ }
+
+ func isEnabledServicesOrderChanged(
+ source: [QueryService],
+ dest: [QueryService]
+ ) -> Bool {
+ !source.elementsEqual(dest) { sItem, dItem in
+ sItem.serviceType() == dItem.serviceType() && sItem.name() == dItem.name()
+ }
+ }
+
+ func postUpdateServiceNotification() {
+ let userInfo: [String: Any] = [EZWindowTypeKey: windowTypeValue]
+ let notification = Notification(name: .serviceHasUpdated, object: nil, userInfo: userInfo)
+ NotificationCenter.default.post(notification)
+ }
+}
+
+@available(macOS 13, *)
+#Preview {
+ ServiceTab()
+}
diff --git a/Easydict/NewApp/View/TapHandlerView.swift b/Easydict/NewApp/View/TapHandlerView.swift
new file mode 100644
index 000000000..39bdff50f
--- /dev/null
+++ b/Easydict/NewApp/View/TapHandlerView.swift
@@ -0,0 +1,42 @@
+//
+// TapHandlerView.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/10.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import SwiftUI
+
+// Ref: https://stackoverflow.com/a/64194868/8378840
+// Fix conflicts between onTap and onMove modifier
+class TapHandlerView: NSView {
+ var tapAction: () -> Void
+
+ init(_ block: @escaping () -> Void) {
+ tapAction = block
+ super.init(frame: .zero)
+ }
+
+ @available(*, unavailable)
+ required init?(coder _: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func mouseDown(with event: NSEvent) {
+ super.mouseDown(with: event)
+ tapAction()
+ }
+}
+
+struct TapHandler: NSViewRepresentable {
+ let tapAction: () -> Void
+
+ func makeNSView(context _: Context) -> TapHandlerView {
+ TapHandlerView(tapAction)
+ }
+
+ func updateNSView(_ nsView: TapHandlerView, context _: Context) {
+ nsView.tapAction = tapAction
+ }
+}
diff --git a/Easydict/NewApp/View/WindowAccessor.swift b/Easydict/NewApp/View/WindowAccessor.swift
new file mode 100644
index 000000000..b748023cb
--- /dev/null
+++ b/Easydict/NewApp/View/WindowAccessor.swift
@@ -0,0 +1,23 @@
+//
+// WindowAccessor.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/9.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import SwiftUI
+
+struct WindowAccessor: NSViewRepresentable {
+ @Binding var window: NSWindow?
+
+ func makeNSView(context _: Context) -> NSView {
+ let view = NSView()
+ DispatchQueue.main.async {
+ window = view.window
+ }
+ return view
+ }
+
+ func updateNSView(_: NSView, context _: Context) {}
+}
From 068203e9914e371c3ea0adc89588924130ae56a2 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Sun, 14 Jan 2024 21:23:40 +0800
Subject: [PATCH 15/72] perf: adjust App name in menu item
---
Easydict/NewApp/View/MenuItemView.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Easydict/NewApp/View/MenuItemView.swift b/Easydict/NewApp/View/MenuItemView.swift
index 999bd03b3..5f25919b1 100644
--- a/Easydict/NewApp/View/MenuItemView.swift
+++ b/Easydict/NewApp/View/MenuItemView.swift
@@ -78,7 +78,7 @@ struct MenuItemView: View {
}
private var versionString: String {
- let defaultLabel = "Easydict \(currentVersion)"
+ let defaultLabel = "Easydict \(currentVersion)"
if let latestVersion,
currentVersion.compare(latestVersion, options: .numeric) == .orderedAscending
{
From f2bcfa07a2244b74e34feb0a90812fc6b78ab026 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Mon, 15 Jan 2024 10:04:54 +0800
Subject: [PATCH 16/72] Revert "chore: auto add .git for Alamofire"
This reverts commit bec66dad6df7427a9abff7af1419644c73cfde62.
---
Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 9c63ea7f5..38cf97c19 100644
--- a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -12,7 +12,7 @@
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
- "location" : "https://github.com/Alamofire/Alamofire.git",
+ "location" : "https://github.com/Alamofire/Alamofire",
"state" : {
"revision" : "3dc6a42c7727c49bf26508e29b0a0b35f9c7e1ad",
"version" : "5.8.1"
From 89b6a872f184e59101124a0d2804ab9630efb7cb Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:10:41 +0800
Subject: [PATCH 17/72] fix: swift format issue while enable SwiftUI preview
(#328)
* fix: swift format while enable swiftui preview
* Update project.pbxproj
Co-authored-by: Tisfeng
---------
Co-authored-by: Tisfeng
---
Easydict.xcodeproj/project.pbxproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index 7438afb68..a1e5d3696 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -2467,7 +2467,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" \"$SRCROOT/$TARGET_NAME\" --swiftversion 5.7\n";
+ shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [ \"${ENABLE_PREVIEWS}\" = \"YES\" ]; then\n echo \"SwiftFormat skipped for Xcode Previews.\"\n exit 0;\nfi\n\"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" \"$SRCROOT/$TARGET_NAME\" --swiftversion 5.7\n";
};
124D690EE7236D6430CF945E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
From 2ae4e7e16388c571a6acb7c4da466b6b5bd928c0 Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Tue, 16 Jan 2024 17:56:09 +0800
Subject: [PATCH 18/72] fix: add some missing English translations (#327)
* fix: add some missing English translations
* fix: remove needs_review label
* fix: update label in localized strings
* perf: update Localizable.xcstrings
---------
Co-authored-by: tisfeng
---
Easydict/App/Localizable.xcstrings | 55 +++++++++++++++++++++++++-----
1 file changed, 46 insertions(+), 9 deletions(-)
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index c75689b1e..ae094d259 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -2,7 +2,14 @@
"sourceLanguage" : "en",
"strings" : {
"" : {
-
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : ""
+ }
+ }
+ }
},
"about" : {
"comment" : "about",
@@ -490,9 +497,15 @@
},
"beta_new_app" : {
"localizations" : {
- "zh-Hans" : {
+ "en" : {
"stringUnit" : {
"state" : "translated",
+ "value" : "[Beta] SwiftUI App mode"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "needs_review",
"value" : "[Beta] SwiftUI App模式"
}
}
@@ -560,6 +573,12 @@
},
"check_updates" : {
"localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Check for Updates"
+ }
+ },
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
@@ -908,9 +927,15 @@
},
"enable_beta_new_app" : {
"localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Launching in SwiftUI App mode, changes take effect upon restarting the app."
+ }
+ },
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "以 SwiftUI App模式启动,重启App生效"
}
}
@@ -1666,7 +1691,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "输入翻译"
}
}
@@ -1683,7 +1708,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "截图翻译"
}
}
@@ -1699,7 +1724,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "划词翻译"
}
}
@@ -1715,7 +1740,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "显示迷你窗口"
}
}
@@ -1731,7 +1756,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "静默截图 OCR"
}
}
@@ -2180,6 +2205,12 @@
},
"quit" : {
"localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Quit"
+ }
+ },
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
@@ -2599,7 +2630,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "调整查询图标位置(避免和 PopClip 显示冲突)"
}
}
@@ -2639,6 +2670,12 @@
},
"setting.general.voice.auto_play_word_audio" : {
"localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Auto play pronunciation after querying English words"
+ }
+ },
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
From 3c46e75e70a7b197ab1d83c39f8f3d068662469a Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Tue, 16 Jan 2024 21:55:34 +0800
Subject: [PATCH 19/72] docs: update sponsor list
---
README.md | 2 ++
README_EN.md | 2 ++
2 files changed, 4 insertions(+)
diff --git a/README.md b/README.md
index 375d4e6ea..1645ba8af 100644
--- a/README.md
+++ b/README.md
@@ -821,6 +821,8 @@ Easydict 作为一个免费开源的非盈利项目,目前主要是作者个
| 2023-12-07 | 小逗。🎈 | 5 | |
| 2023-12-26 | ㅤ Yee | 5 | 感谢开源 |
| 2024-01-09 | ㅤ Jack | 20 | 目前用过最好用的字典软件,谢谢! |
+| 2024-01-15 | ㅤ | 20 | 感谢开源,感谢有你:) |
+| 2024-01-16 | ㅤ sd | 5 | 大佬牛逼🐂🍺 |
diff --git a/README_EN.md b/README_EN.md
index 727d367a0..026b259d3 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -818,6 +818,8 @@ If you don't want your username to be displayed in the list, please choose anony
| 2023-12-07 | 小逗。🎈 | 5 | |
| 2023-12-26 | ㅤ Yee | 5 | 感谢开源 |
| 2024-01-09 | ㅤ Jack | 20 | 目前用过最好用的字典软件,谢谢! |
+| 2024-01-15 | ㅤ | 20 | 感谢开源,感谢有你:) |
+| 2024-01-16 | ㅤ sd | 5 | 大佬牛逼🐂🍺 |
From 029a7d8d81f073e90a843a3f771977b3b54add91 Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Wed, 17 Jan 2024 10:05:10 +0800
Subject: [PATCH 20/72] fix: update README (#333)
* fix: update README
* Update README.md
Co-authored-by: Tisfeng
---------
Co-authored-by: Tisfeng
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1645ba8af..2f0a8a952 100644
--- a/README.md
+++ b/README.md
@@ -734,7 +734,7 @@ Easydict 有一些应用内快捷键,方便你在使用过程中更加高效
## 致谢
-- /是以 [Bob (GPL-3.0)](https://github.com/1xiaocainiao/Bob) 为基础开发。Easydict 在原项目上进行了许多改进和优化,很多功能和 UI 都参考了 Bob。
+- 这个项目的灵感来自 [saladict](https://github.com/crimx/ext-saladict) 和 [Bob](https://github.com/ripperhe/Bob),且初始版本是以 [Bob (GPL-3.0)](https://github.com/1xiaocainiao/Bob) 为基础开发。Easydict 在原项目上进行了许多改进和优化,很多功能和 UI 都参考了 Bob。
- 截图功能是基于 [isee15](https://github.com/isee15) 的 [Capture-Screen-For-Multi-Screens-On-Mac](https://github.com/isee15/Capture-Screen-For-Multi-Screens-On-Mac),并在此基础上进行了优化。
- 鼠标划词功能参考了 [PopClip](https://pilotmoon.com/popclip/)。
From 63729d6d4e5bc2094ee2cb1d0b126549632ee687 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Tue, 16 Jan 2024 22:46:52 +0800
Subject: [PATCH 21/72] refactor: rewrite BingLanguageVoice with Swift by
Jerry23011
---
Easydict.xcodeproj/project.pbxproj | 10 +++----
.../Service/Bing/BingLanguageVoice.swift | 26 +++++++++++++++++++
.../Service/Bing/EZBingLanguageVoice.h | 24 -----------------
.../Service/Bing/EZBingLanguageVoice.m | 20 --------------
Easydict/Feature/Service/Bing/EZBingRequest.m | 2 +-
5 files changed, 31 insertions(+), 51 deletions(-)
create mode 100644 Easydict/Feature/Service/Bing/BingLanguageVoice.swift
delete mode 100644 Easydict/Feature/Service/Bing/EZBingLanguageVoice.h
delete mode 100644 Easydict/Feature/Service/Bing/EZBingLanguageVoice.m
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index a1e5d3696..adc5e75bc 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -19,6 +19,7 @@
0309E1ED292B439A00AFB76A /* EZTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0309E1EC292B439A00AFB76A /* EZTextView.m */; };
0309E1F0292B4A5E00AFB76A /* NSView+EZGetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0309E1EF292B4A5E00AFB76A /* NSView+EZGetViewController.m */; };
0309E1F4292BD6A100AFB76A /* EZQueryModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0309E1F3292BD6A100AFB76A /* EZQueryModel.m */; };
+ 030DB2612B56CC6500E27DEA /* BingLanguageVoice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030DB2602B56CC6500E27DEA /* BingLanguageVoice.swift */; };
0310C8272A94F5DF00B1D81E /* apple-dictionary.html in Resources */ = {isa = PBXBuildFile; fileRef = 0310C8262A94EFA100B1D81E /* apple-dictionary.html */; };
0313F8702AD5577400A5CFB0 /* EasydictTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0313F86F2AD5577400A5CFB0 /* EasydictTests.m */; };
031DBD792AE01E130071CF85 /* easydict in Resources */ = {isa = PBXBuildFile; fileRef = 031DBD782AE01E130071CF85 /* easydict */; };
@@ -66,7 +67,6 @@
037852B329583F5200D0E2CF /* EZServiceCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 037852B229583F5200D0E2CF /* EZServiceCell.m */; };
037852B629588EDE00D0E2CF /* EZCustomTableRowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 037852B529588EDE00D0E2CF /* EZCustomTableRowView.m */; };
037852B9295D49F900D0E2CF /* EZTableRowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 037852B8295D49F900D0E2CF /* EZTableRowView.m */; };
- 037BEFCD2A98FDF700D0F17F /* EZBingLanguageVoice.m in Sources */ = {isa = PBXBuildFile; fileRef = 037BEFCC2A98FDF700D0F17F /* EZBingLanguageVoice.m */; };
037E006D2B3DC098006491C6 /* EZOpenAIService+EZPromptMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 03CF27FA2B3A787900E19B57 /* EZOpenAIService+EZPromptMessages.m */; };
038030952B4106800009230C /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 038030942B4106800009230C /* CocoaLumberjack */; };
038030972B4106800009230C /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 038030962B4106800009230C /* CocoaLumberjackSwift */; };
@@ -325,6 +325,7 @@
0309E1EF292B4A5E00AFB76A /* NSView+EZGetViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSView+EZGetViewController.m"; sourceTree = ""; };
0309E1F2292BD6A100AFB76A /* EZQueryModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZQueryModel.h; sourceTree = ""; };
0309E1F3292BD6A100AFB76A /* EZQueryModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZQueryModel.m; sourceTree = ""; };
+ 030DB2602B56CC6500E27DEA /* BingLanguageVoice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BingLanguageVoice.swift; sourceTree = ""; };
0310C8262A94EFA100B1D81E /* apple-dictionary.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "apple-dictionary.html"; sourceTree = ""; };
0313F86D2AD5577400A5CFB0 /* EasydictTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EasydictTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0313F86F2AD5577400A5CFB0 /* EasydictTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EasydictTests.m; sourceTree = ""; };
@@ -414,8 +415,6 @@
037852B529588EDE00D0E2CF /* EZCustomTableRowView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZCustomTableRowView.m; sourceTree = ""; };
037852B7295D49F900D0E2CF /* EZTableRowView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZTableRowView.h; sourceTree = ""; };
037852B8295D49F900D0E2CF /* EZTableRowView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZTableRowView.m; sourceTree = ""; };
- 037BEFCB2A98FDF700D0F17F /* EZBingLanguageVoice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZBingLanguageVoice.h; sourceTree = ""; };
- 037BEFCC2A98FDF700D0F17F /* EZBingLanguageVoice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZBingLanguageVoice.m; sourceTree = ""; };
03839141292FBE120009828C /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; };
03839142292FBE120009828C /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
03839143292FBE120009828C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
@@ -2091,8 +2090,7 @@
6295DE302A84D82E006145F4 /* EZBingTranslateModel.m */,
6295DE322A84EF76006145F4 /* EZBingLookupModel.h */,
6295DE332A84EF76006145F4 /* EZBingLookupModel.m */,
- 037BEFCB2A98FDF700D0F17F /* EZBingLanguageVoice.h */,
- 037BEFCC2A98FDF700D0F17F /* EZBingLanguageVoice.m */,
+ 030DB2602B56CC6500E27DEA /* BingLanguageVoice.swift */,
03F639932AA6CFBB009B9914 /* EZBingConfig.h */,
03F639942AA6CFBB009B9914 /* EZBingConfig.m */,
);
@@ -2586,7 +2584,6 @@
03991158292927E000E1B06D /* EZTitlebar.m in Sources */,
03D8A65C2A433B4100D9A968 /* EZConfiguration+EZUserData.m in Sources */,
03BD282229486CF200F5891A /* EZBlueTextButton.m in Sources */,
- 037BEFCD2A98FDF700D0F17F /* EZBingLanguageVoice.m in Sources */,
03BDA7C22A26DA280079D04F /* NSString+Indenter.m in Sources */,
03542A462937B4C300C34C33 /* EZBaiduTranslateResponse.m in Sources */,
0309E1F0292B4A5E00AFB76A /* NSView+EZGetViewController.m in Sources */,
@@ -2722,6 +2719,7 @@
0333FDA62A035D5700891515 /* NSString+EZChineseText.m in Sources */,
03E02A222924E77100A10260 /* EZMenuItemManager.m in Sources */,
039D119929D5E26300C93F46 /* EZAudioUtils.m in Sources */,
+ 030DB2612B56CC6500E27DEA /* BingLanguageVoice.swift in Sources */,
EA9943F22B5358BF00EE7B97 /* LanguageExtensions.swift in Sources */,
03B0231729231FA6001C7E63 /* Snip.m in Sources */,
03BFFC6E295FE59C004E033E /* EZQueryResult+EZYoudaoDictModel.m in Sources */,
diff --git a/Easydict/Feature/Service/Bing/BingLanguageVoice.swift b/Easydict/Feature/Service/Bing/BingLanguageVoice.swift
new file mode 100644
index 000000000..3751e9433
--- /dev/null
+++ b/Easydict/Feature/Service/Bing/BingLanguageVoice.swift
@@ -0,0 +1,26 @@
+//
+// BingLanguageVoice.swift
+// Easydict
+//
+// Created by Jerry on 2023-10-14.
+// Copyright © 2023 izual. All rights reserved.
+//
+
+import Foundation
+
+// Docs: https://learn.microsoft.com/zh-cn/azure/ai-services/speech-service/language-support?tabs=tts
+
+@objc(EZBingLanguageVoice)
+class BingLanguageVoice: NSObject {
+ @objc var lang: String // BCP-47, en-US
+ @objc var voiceName: String // en-US-JennyNeural
+
+ init(lang: String, voiceName: String) {
+ self.lang = lang
+ self.voiceName = voiceName
+ }
+
+ @objc class func voice(withLanguage language: String, voiceName: String) -> BingLanguageVoice {
+ BingLanguageVoice(lang: language, voiceName: voiceName)
+ }
+}
diff --git a/Easydict/Feature/Service/Bing/EZBingLanguageVoice.h b/Easydict/Feature/Service/Bing/EZBingLanguageVoice.h
deleted file mode 100644
index 2a193cbbd..000000000
--- a/Easydict/Feature/Service/Bing/EZBingLanguageVoice.h
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-// EZBingTTSVoice.h
-// Easydict
-//
-// Created by tisfeng on 2023/8/25.
-// Copyright © 2023 izual. All rights reserved.
-//
-
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-// Docs: https://learn.microsoft.com/zh-cn/azure/ai-services/speech-service/language-support?tabs=tts
-
-@interface EZBingLanguageVoice : NSObject
-
-@property (nonatomic, copy) NSString *lang; // BCP-47, en-US
-@property (nonatomic, copy) NSString *voiceName; // en-US-JennyNeural
-
-+ (instancetype)voiceWithLanguage:(NSString *)langauge voiceName:(NSString *)voiceName;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/Easydict/Feature/Service/Bing/EZBingLanguageVoice.m b/Easydict/Feature/Service/Bing/EZBingLanguageVoice.m
deleted file mode 100644
index 4ba73a88d..000000000
--- a/Easydict/Feature/Service/Bing/EZBingLanguageVoice.m
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// EZBingTTSVoice.m
-// Easydict
-//
-// Created by tisfeng on 2023/8/25.
-// Copyright © 2023 izual. All rights reserved.
-//
-
-#import "EZBingLanguageVoice.h"
-
-@implementation EZBingLanguageVoice
-
-+ (instancetype)voiceWithLanguage:(NSString *)langauge voiceName:(NSString *)voiceName {
- EZBingLanguageVoice *voice = [[EZBingLanguageVoice alloc] init];
- voice.lang = langauge;
- voice.voiceName = voiceName;
- return voice;
-}
-
-@end
diff --git a/Easydict/Feature/Service/Bing/EZBingRequest.m b/Easydict/Feature/Service/Bing/EZBingRequest.m
index 3d6849b35..512e31b87 100644
--- a/Easydict/Feature/Service/Bing/EZBingRequest.m
+++ b/Easydict/Feature/Service/Bing/EZBingRequest.m
@@ -8,8 +8,8 @@
#import "EZBingRequest.h"
#import "EZError.h"
-#import "EZBingLanguageVoice.h"
#import "NSString+EZRegex.h"
+#import "Easydict-Swift.h"
static NSString *const kAudioMIMEType = @"audio/mpeg";
static NSString *const kBingConfigKey = @"kBingConfigKey";
From 2df80b922865103b24d19a22b541b89c3218bf3e Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Wed, 17 Jan 2024 10:05:10 +0800
Subject: [PATCH 22/72] fix: update README (#333)
* fix: update README
* Update README.md
Co-authored-by: Tisfeng
---------
Co-authored-by: Tisfeng
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ce11f5f41..91ab698d1 100644
--- a/README.md
+++ b/README.md
@@ -734,7 +734,7 @@ Easydict 有一些应用内快捷键,方便你在使用过程中更加高效
## 致谢
-- /是以 [Bob (GPL-3.0)](https://github.com/1xiaocainiao/Bob) 为基础开发。Easydict 在原项目上进行了许多改进和优化,很多功能和 UI 都参考了 Bob。
+- 这个项目的灵感来自 [saladict](https://github.com/crimx/ext-saladict) 和 [Bob](https://github.com/ripperhe/Bob),且初始版本是以 [Bob (GPL-3.0)](https://github.com/1xiaocainiao/Bob) 为基础开发。Easydict 在原项目上进行了许多改进和优化,很多功能和 UI 都参考了 Bob。
- 截图功能是基于 [isee15](https://github.com/isee15) 的 [Capture-Screen-For-Multi-Screens-On-Mac](https://github.com/isee15/Capture-Screen-For-Multi-Screens-On-Mac),并在此基础上进行了优化。
- 鼠标划词功能参考了 [PopClip](https://pilotmoon.com/popclip/)。
From a0cb61e70f40249853e10301b583a92e74d274e4 Mon Sep 17 00:00:00 2001
From: Lava <34743145+CanglongCl@users.noreply.github.com>
Date: Wed, 17 Jan 2024 03:33:54 -0800
Subject: [PATCH 23/72] TTSServiceType get rid of limitation to more than macOS
13 (#336)
---
.../NewApp/Configuration/Configuration.swift | 1 -
Easydict/NewApp/Model/TTSServiceType.swift | 17 +++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/Easydict/NewApp/Configuration/Configuration.swift b/Easydict/NewApp/Configuration/Configuration.swift
index 9ecdff830..976ab72f5 100644
--- a/Easydict/NewApp/Configuration/Configuration.swift
+++ b/Easydict/NewApp/Configuration/Configuration.swift
@@ -33,7 +33,6 @@ extension Defaults.Keys {
static let autoCopySelectedText = Key("EZConfiguration_kAutoCopySelectedTextKey", default: false)
static let autoCopyFirstTranslatedText = Key("EZConfiguration_kAutoCopyFirstTranslatedTextKey", default: false)
static let languageDetectOptimize = Key("EZConfiguration_kLanguageDetectOptimizeTypeKey", default: EZLanguageDetectOptimize.none)
- @available(macOS 13, *)
static let defaultTTSServiceType = Key("EZConfiguration_kDefaultTTSServiceTypeKey", default: TTSServiceType.youdao)
static let showGoogleQuickLink = Key("EZConfiguration_kShowGoogleLinkKey", default: true)
static let showEudicQuickLink = Key("EZConfiguration_kShowEudicLinkKey", default: true)
diff --git a/Easydict/NewApp/Model/TTSServiceType.swift b/Easydict/NewApp/Model/TTSServiceType.swift
index 6eda5cf16..c28ef5938 100644
--- a/Easydict/NewApp/Model/TTSServiceType.swift
+++ b/Easydict/NewApp/Model/TTSServiceType.swift
@@ -9,8 +9,16 @@
import Defaults
import Foundation
+enum TTSServiceType: String, CaseIterable {
+ case youdao
+ case bing
+ case google
+ case baidu
+ case apple
+}
+
@available(macOS 13, *)
-enum TTSServiceType: String, CaseIterable, CustomLocalizedStringResourceConvertible {
+extension TTSServiceType: CustomLocalizedStringResourceConvertible {
var localizedStringResource: LocalizedStringResource {
switch self {
case .youdao:
@@ -25,15 +33,8 @@ enum TTSServiceType: String, CaseIterable, CustomLocalizedStringResourceConverti
"setting.tts_service.options.apple"
}
}
-
- case youdao
- case bing
- case google
- case baidu
- case apple
}
-@available(macOS 13, *)
extension TTSServiceType: Defaults.Serializable {
// while in the future, ServiceType was deleted, then you can safely delete this struct and `bridge`
struct TTSServiceTypeBridge: Defaults.Bridge {
From 8edb419954d47a4ba10096ef9fdcb26a53be9a16 Mon Sep 17 00:00:00 2001
From: Lava <34743145+CanglongCl@users.noreply.github.com>
Date: Wed, 17 Jan 2024 05:26:49 -0800
Subject: [PATCH 24/72] Refactor Setting - Service and provide service
configuration view (#326)
* service tab refactor
* service configuration view
* add comment for ServiceStringConfigurationSection
* add comments for ConfigurableService
* rename openAIAPI with openAIAPIKey
* UI optimization
* revert schema
* fix: service setting in dark mode
* fix: cannot move and scroll position error
* introduce a view model in service tab
* delete unused code
* fix: do not post update notification if service enabled is not changed
* fix: resizing windows animation in service view
* small refactor on viewmodels
* refactor: ServiceItems
* reset selection after window type changes
* perf: update Localizable.xcstrings
---------
Co-authored-by: tisfeng
Co-authored-by: phlpsong <103433299+phlpsong@users.noreply.github.com>
---
Easydict.xcodeproj/project.pbxproj | 40 ++-
Easydict/App/Easydict-Bridging-Header.h | 2 +
Easydict/App/Localizable.xcstrings | 148 +++++++++-
Easydict/Feature/Service/Ali/AliService.swift | 7 +-
.../Service/Caiyun/CaiyunService.swift | 3 +-
.../Feature/Service/OpenAI/EZOpenAIService.m | 1 +
.../Service/Tencent/TencentService.swift | 5 +-
.../NewApp/Configuration/Configuration.swift | 35 +++
.../OpenAIService+ConfigurableService.swift | 35 +++
.../Protocol/ConfigurableService.swift | 29 ++
Easydict/NewApp/View/ServiceItemView.swift | 41 ---
.../NewApp/View/SettingView/SettingView.swift | 33 ++-
.../ServiceConfigurationSection.swift | 84 ++++++
.../View/SettingView/Tabs/ServiceTab.swift | 262 +++++++++++-------
14 files changed, 549 insertions(+), 176 deletions(-)
create mode 100644 Easydict/NewApp/Utility/Extensions/QueryService+ConfigurableService/OpenAIService+ConfigurableService.swift
create mode 100644 Easydict/NewApp/Utility/Protocol/ConfigurableService.swift
delete mode 100644 Easydict/NewApp/View/ServiceItemView.swift
create mode 100644 Easydict/NewApp/View/SettingView/Tabs/ServiceConfiguration/ServiceConfigurationSection.swift
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index adc5e75bc..9bb89217d 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -229,7 +229,6 @@
03FD68BB2B1DC59600FD388E /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 03FD68BA2B1DC59600FD388E /* CryptoSwift */; };
03FD68BE2B1E151A00FD388E /* String+EncryptAES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */; };
0A057D6D2B499A000025C51D /* ServiceTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A057D6C2B499A000025C51D /* ServiceTab.swift */; };
- 0A057D6F2B499A0B0025C51D /* ServiceItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A057D6E2B499A0B0025C51D /* ServiceItemView.swift */; };
0A2BA9602B49A989002872A4 /* Binding+DidSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */; };
0A2BA9642B4A3CCD002872A4 /* Notification+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */; };
0AC11B222B4D16A500F07198 /* WindowAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC11B212B4D16A500F07198 /* WindowAccessor.swift */; };
@@ -273,6 +272,9 @@
EA9943EE2B5353AB00EE7B97 /* WindowTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */; };
EA9943F02B5354C400EE7B97 /* ShowWindowPositionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */; };
EA9943F22B5358BF00EE7B97 /* LanguageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */; };
+ EAED41EC2B54AA920005FE0A /* ServiceConfigurationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAED41EB2B54AA920005FE0A /* ServiceConfigurationSection.swift */; };
+ EAED41EF2B54B1430005FE0A /* ConfigurableService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAED41EE2B54B1430005FE0A /* ConfigurableService.swift */; };
+ EAED41F22B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAED41F12B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -702,7 +704,6 @@
03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+EncryptAES.swift"; sourceTree = ""; };
06E15747A7BD34D510ADC6A8 /* Pods-Easydict.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Easydict.debug.xcconfig"; path = "Target Support Files/Pods-Easydict/Pods-Easydict.debug.xcconfig"; sourceTree = ""; };
0A057D6C2B499A000025C51D /* ServiceTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTab.swift; sourceTree = ""; };
- 0A057D6E2B499A0B0025C51D /* ServiceItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceItemView.swift; sourceTree = ""; };
0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+DidSet.swift"; sourceTree = ""; };
0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Name.swift"; sourceTree = ""; };
0AC11B212B4D16A500F07198 /* WindowAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowAccessor.swift; sourceTree = ""; };
@@ -761,6 +762,9 @@
EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTypeExtensions.swift; sourceTree = ""; };
EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowWindowPositionExtensions.swift; sourceTree = ""; };
EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageExtensions.swift; sourceTree = ""; };
+ EAED41EB2B54AA920005FE0A /* ServiceConfigurationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceConfigurationSection.swift; sourceTree = ""; };
+ EAED41EE2B54B1430005FE0A /* ConfigurableService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurableService.swift; sourceTree = ""; };
+ EAED41F12B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAIService+ConfigurableService.swift"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -2051,7 +2055,6 @@
isa = PBXGroup;
children = (
27FE980A2B3DD5D1000AD654 /* MenuItemView.swift */,
- 0A057D6E2B499A0B0025C51D /* ServiceItemView.swift */,
0AC11B212B4D16A500F07198 /* WindowAccessor.swift */,
0AC11B232B4E46B300F07198 /* TapHandlerView.swift */,
27FE98072B3DD52B000AD654 /* SettingView */,
@@ -2071,6 +2074,7 @@
27FE980C2B3DD749000AD654 /* Tabs */ = {
isa = PBXGroup;
children = (
+ EAED41EA2B54A4900005FE0A /* ServiceConfiguration */,
278540332B3DE04F004E9488 /* GeneralTab.swift */,
0A057D6C2B499A000025C51D /* ServiceTab.swift */,
276742042B3DC230002A2C75 /* PrivacyTab.swift */,
@@ -2203,6 +2207,7 @@
EA9943DD2B534BAE00EE7B97 /* Utility */ = {
isa = PBXGroup;
children = (
+ EAED41ED2B54B1390005FE0A /* Protocol */,
EA9943E62B534D7C00EE7B97 /* Extensions */,
);
path = Utility;
@@ -2219,6 +2224,7 @@
EA9943E62B534D7C00EE7B97 /* Extensions */ = {
isa = PBXGroup;
children = (
+ EAED41F02B54B1A60005FE0A /* QueryService+ConfigurableService */,
EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */,
EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */,
EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */,
@@ -2227,6 +2233,30 @@
path = Extensions;
sourceTree = "";
};
+ EAED41EA2B54A4900005FE0A /* ServiceConfiguration */ = {
+ isa = PBXGroup;
+ children = (
+ EAED41EB2B54AA920005FE0A /* ServiceConfigurationSection.swift */,
+ );
+ path = ServiceConfiguration;
+ sourceTree = "";
+ };
+ EAED41ED2B54B1390005FE0A /* Protocol */ = {
+ isa = PBXGroup;
+ children = (
+ EAED41EE2B54B1430005FE0A /* ConfigurableService.swift */,
+ );
+ path = Protocol;
+ sourceTree = "";
+ };
+ EAED41F02B54B1A60005FE0A /* QueryService+ConfigurableService */ = {
+ isa = PBXGroup;
+ children = (
+ EAED41F12B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift */,
+ );
+ path = "QueryService+ConfigurableService";
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -2602,7 +2632,6 @@
03991166292A8A4400E1B06D /* EZTitleBarMoveView.m in Sources */,
03542A582937CC3200C34C33 /* EZConfiguration.m in Sources */,
27FE98092B3DD536000AD654 /* SettingView.swift in Sources */,
- 0A057D6F2B499A0B0025C51D /* ServiceItemView.swift in Sources */,
035E37E72A0953120061DFAF /* EZToast.m in Sources */,
03542A492937B5CF00C34C33 /* EZGoogleTranslate.m in Sources */,
03D0435A2928C4C800E7559E /* EZWindowManager.m in Sources */,
@@ -2634,6 +2663,7 @@
03F14A3B2956016B00CB7379 /* EZVolcanoTranslate.m in Sources */,
03B0230429231FA6001C7E63 /* EZHoverButton.m in Sources */,
0342A9812AD64924002A9F5F /* NSString+EZSplit.m in Sources */,
+ EAED41EF2B54B1430005FE0A /* ConfigurableService.swift in Sources */,
03BD2825294875AE00F5891A /* EZMyLabel.m in Sources */,
03B0233029231FA6001C7E63 /* MMCrashUncaughtExceptionHandler.m in Sources */,
03D5FCFF2A5EF4E400AD26BE /* EZDeviceSystemInfo.m in Sources */,
@@ -2775,6 +2805,7 @@
039F5508294B6E29004AB940 /* EZAboutViewController.m in Sources */,
03D8A6592A42A1A300D9A968 /* EZAppModel.m in Sources */,
036E7D7B293F4FC8002675DF /* EZOpenLinkButton.m in Sources */,
+ EAED41EC2B54AA920005FE0A /* ServiceConfigurationSection.swift in Sources */,
276742092B3DC230002A2C75 /* AboutTab.swift in Sources */,
03008B2E2941956D0062B821 /* EZURLSchemeHandler.m in Sources */,
DC6D9C872B352EBC0055EFFC /* FontSizeHintView.swift in Sources */,
@@ -2790,6 +2821,7 @@
03008B3F29444B0A0062B821 /* NSView+EZAnimatedHidden.m in Sources */,
03B022FD29231FA6001C7E63 /* EZFixedQueryWindow.m in Sources */,
03B0232C29231FA6001C7E63 /* NSView+MM.m in Sources */,
+ EAED41F22B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift in Sources */,
033C31002A74CECE0095926A /* EZAppleDictionary.m in Sources */,
03E2BF752A298F2B00E010F3 /* NSString+EZUtils.m in Sources */,
03B022F529231FA6001C7E63 /* EZDetectManager.m in Sources */,
diff --git a/Easydict/App/Easydict-Bridging-Header.h b/Easydict/App/Easydict-Bridging-Header.h
index 8030b13fa..f98fcafec 100644
--- a/Easydict/App/Easydict-Bridging-Header.h
+++ b/Easydict/App/Easydict-Bridging-Header.h
@@ -26,3 +26,5 @@
#import "NSString+EZConvenience.h"
#import "EZWindowManager.h"
#import "NSViewController+EZWindow.h"
+
+#import "EZOpenAIService.h"
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index ae094d259..2114e3a63 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -1,16 +1,6 @@
{
"sourceLanguage" : "en",
"strings" : {
- "" : {
- "localizations" : {
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : ""
- }
- }
- }
- },
"about" : {
"comment" : "about",
"localizations" : {
@@ -505,7 +495,7 @@
},
"zh-Hans" : {
"stringUnit" : {
- "state" : "needs_review",
+ "state" : "translated",
"value" : "[Beta] SwiftUI App模式"
}
}
@@ -1827,7 +1817,14 @@
}
},
"none_window" : {
-
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : ""
+ }
+ }
+ }
},
"ocr_result_is_empty" : {
"localizations" : {
@@ -2316,6 +2313,92 @@
}
}
},
+ "service.configuration.openai.api_key.footer" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "API Key的一些说明或者加入链接"
+ }
+ }
+ }
+ },
+ "service.configuration.openai.api_key.header" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "OpenAI API Key"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "OpenAI API Key"
+ }
+ }
+ }
+ },
+ "service.configuration.openai.api_key.prompt" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ }
+ }
+ }
+ },
+ "service.configuration.openai.api_key.title" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "OpenAI API Key"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "OpenAI API Key"
+ }
+ }
+ }
+ },
+ "service.configuration.openai.translation.footer" : {
+
+ },
+ "service.configuration.openai.translation.header" : {
+
+ },
+ "service.configuration.openai.translation.prompt" : {
+
+ },
+ "service.configuration.openai.translation.title" : {
+
+ },
+ "service.service_configuration.reset" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Reset"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "重置"
+ }
+ }
+ }
+ },
"setting_general" : {
"localizations" : {
"en" : {
@@ -2780,6 +2863,38 @@
}
}
},
+ "setting.service.detail.no_configuration %@" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "No configuration for %@"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%@没有可供配置的选项"
+ }
+ }
+ }
+ },
+ "setting.service.detail.no_selection" : {
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Select a service to show configuration"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "选择服务以查看配置"
+ }
+ }
+ }
+ },
"setting.tts_service.options.apple" : {
"localizations" : {
"en" : {
@@ -3205,7 +3320,14 @@
}
},
"unknown_option" : {
-
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : ""
+ }
+ }
+ }
},
"unpin" : {
"localizations" : {
diff --git a/Easydict/Feature/Service/Ali/AliService.swift b/Easydict/Feature/Service/Ali/AliService.swift
index 6ba555109..9788fc063 100644
--- a/Easydict/Feature/Service/Ali/AliService.swift
+++ b/Easydict/Feature/Service/Ali/AliService.swift
@@ -8,6 +8,7 @@
import Alamofire
import CryptoKit
+import Defaults
import Foundation
@objc(EZAliService)
@@ -76,8 +77,10 @@ class AliService: QueryService {
easydict://writeKeyValue?EZAliAccessKeyId=
easydict://writeKeyValue?EZAliAccessKeySecret=
*/
- if let id = UserDefaults.standard.string(forKey: EZAliAccessKeyId),
- let secret = UserDefaults.standard.string(forKey: EZAliAccessKeySecret), !id.isEmpty, !secret.isEmpty
+ if let id = Defaults[.aliAccessKeyId],
+ let secret = Defaults[.aliAccessKeySecret],
+ !id.isEmpty,
+ !secret.isEmpty
{
requestByAPI(id: id, secret: secret, transType: transType, text: text, from: from, to: to, completion: completion)
} else { // use web api
diff --git a/Easydict/Feature/Service/Caiyun/CaiyunService.swift b/Easydict/Feature/Service/Caiyun/CaiyunService.swift
index 2c6b3dcf3..b36cfbdf8 100644
--- a/Easydict/Feature/Service/Caiyun/CaiyunService.swift
+++ b/Easydict/Feature/Service/Caiyun/CaiyunService.swift
@@ -7,6 +7,7 @@
//
import Alamofire
+import Defaults
import Foundation
@objc(EZCaiyunService)
@@ -44,7 +45,7 @@ public final class CaiyunService: QueryService {
// easydict://writeKeyValue?EZCaiyunToken=
private var token: String {
- let token = UserDefaults.standard.string(forKey: EZCaiyunToken)
+ let token = Defaults[.caiyunToken]
if let token, !token.isEmpty {
return token
} else {
diff --git a/Easydict/Feature/Service/OpenAI/EZOpenAIService.m b/Easydict/Feature/Service/OpenAI/EZOpenAIService.m
index ad01eff55..1123049e4 100644
--- a/Easydict/Feature/Service/OpenAI/EZOpenAIService.m
+++ b/Easydict/Feature/Service/OpenAI/EZOpenAIService.m
@@ -28,6 +28,7 @@ @interface EZOpenAIService ()
@end
+
@implementation EZOpenAIService
- (instancetype)init {
diff --git a/Easydict/Feature/Service/Tencent/TencentService.swift b/Easydict/Feature/Service/Tencent/TencentService.swift
index cc75b7db0..aaa07b9ab 100644
--- a/Easydict/Feature/Service/Tencent/TencentService.swift
+++ b/Easydict/Feature/Service/Tencent/TencentService.swift
@@ -7,6 +7,7 @@
//
import Alamofire
+import Defaults
import Foundation
@objc(EZTencentService)
@@ -64,7 +65,7 @@ public final class TencentService: QueryService {
// easydict://writeKeyValue?EZTencentSecretId=xxx
private var secretId: String {
- let secretId = UserDefaults.standard.string(forKey: EZTencentSecretId)
+ let secretId = Defaults[.tencentSecretId]
if let secretId, !secretId.isEmpty {
return secretId
} else {
@@ -74,7 +75,7 @@ public final class TencentService: QueryService {
// easydict://writeKeyValue?EZTencentSecretKey=xxx
private var secretKey: String {
- let secretKey = UserDefaults.standard.string(forKey: EZTencentSecretKey)
+ let secretKey = Defaults[.tencentSecretKey]
if let secretKey, !secretKey.isEmpty {
return secretKey
} else {
diff --git a/Easydict/NewApp/Configuration/Configuration.swift b/Easydict/NewApp/Configuration/Configuration.swift
index 976ab72f5..5777aa144 100644
--- a/Easydict/NewApp/Configuration/Configuration.swift
+++ b/Easydict/NewApp/Configuration/Configuration.swift
@@ -9,6 +9,7 @@
import Defaults
import Foundation
+// Setting
extension Defaults.Keys {
// rename `from`
static let queryFromLanguage = Key("EZConfiguration_kFromKey", default: .auto)
@@ -52,3 +53,37 @@ extension Defaults.Keys {
static let appearanceType = Key("EZConfiguration_kApperanceKey", default: .followSystem)
static let fontSizeOptionIndex = Key("EZConfiguration_kTranslationControllerFontKey", default: 0)
}
+
+// Service Configuration
+extension Defaults.Keys {
+ // OPENAI
+ static let openAIAPIKey = Key("EZOpenAIAPIKey")
+ static let openAITranslation = Key("EZOpenAITranslationKey")
+ static let openAIDictionary = Key("EZOpenAIDictionaryKey")
+ static let openAISentence = Key("EZOpenAISentenceKey")
+ static let openAIServiceUsageStatus = Key("EZOpenAIServiceUsageStatusKey")
+ static let openAIDomain = Key("EZOpenAIDomainKey")
+ static let openAIEndPoint = Key("EZOpenAIEndPointKey")
+ static let openAIModel = Key("EZOpenAIModelKey")
+
+ // DEEPL
+ static let deepLAuth = Key("EZDeepLAuthKey")
+ static let deepLTranslateEndPointKey = Key("EZDeepLTranslateEndPointKey")
+
+ // BING
+ static let bingCookieKey = Key("EZBingCookieKey")
+
+ // niu
+ static let niuTransAPIKey = Key("EZNiuTransAPIKey")
+
+ // Caiyun
+ static let caiyunToken = Key("EZCaiyunToken")
+
+ // tencent
+ static let tencentSecretId = Key("EZTencentSecretId")
+ static let tencentSecretKey = Key("EZTencentSecretKey")
+
+ // ALI
+ static let aliAccessKeyId = Key("EZAliAccessKeyId")
+ static let aliAccessKeySecret = Key("EZAliAccessKeySecret")
+}
diff --git a/Easydict/NewApp/Utility/Extensions/QueryService+ConfigurableService/OpenAIService+ConfigurableService.swift b/Easydict/NewApp/Utility/Extensions/QueryService+ConfigurableService/OpenAIService+ConfigurableService.swift
new file mode 100644
index 000000000..960cada25
--- /dev/null
+++ b/Easydict/NewApp/Utility/Extensions/QueryService+ConfigurableService/OpenAIService+ConfigurableService.swift
@@ -0,0 +1,35 @@
+//
+// OpenAIService+ConfigurableService.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/14.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Foundation
+import SwiftUI
+
+@available(macOS 12.0, *)
+extension EZOpenAIService: ConfigurableService {
+ func configurationListItems() -> some View {
+ ServiceStringConfigurationSection(
+ textFieldTitleKey: "service.configuration.openai.api_key.header",
+ headerTitleKey: "service.configuration.openai.api_key.title",
+ key: .openAIAPIKey,
+ prompt: "service.configuration.openai.api_key.prompt",
+ footer: {
+ Text("service.configuration.openai.api_key.footer")
+ }
+ )
+
+ ServiceStringConfigurationSection(
+ textFieldTitleKey: "service.configuration.openai.translation.header",
+ headerTitleKey: "service.configuration.openai.translation.title",
+ key: .openAITranslation,
+ prompt: "service.configuration.openai.translation.prompt",
+ footer: {
+ Text("service.configuration.openai.translation.footer")
+ }
+ )
+ }
+}
diff --git a/Easydict/NewApp/Utility/Protocol/ConfigurableService.swift b/Easydict/NewApp/Utility/Protocol/ConfigurableService.swift
new file mode 100644
index 000000000..058151a5c
--- /dev/null
+++ b/Easydict/NewApp/Utility/Protocol/ConfigurableService.swift
@@ -0,0 +1,29 @@
+//
+// ConfigurableService.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/14.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Foundation
+import SwiftUI
+
+/// A service can provide configuration view in setting
+protocol ConfigurableService {
+ associatedtype T: View
+
+ /// Items in Configuration Form. Use ServiceStringConfigurationSection or other customize view.
+ @ViewBuilder
+ func configurationListItems() -> T
+}
+
+@available(macOS 13.0, *)
+extension ConfigurableService {
+ func configurationView() -> some View {
+ Form {
+ configurationListItems()
+ }
+ .formStyle(.grouped)
+ }
+}
diff --git a/Easydict/NewApp/View/ServiceItemView.swift b/Easydict/NewApp/View/ServiceItemView.swift
deleted file mode 100644
index d21f17e98..000000000
--- a/Easydict/NewApp/View/ServiceItemView.swift
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// ServiceItemView.swift
-// Easydict
-//
-// Created by phlpsong on 2024/1/6.
-// Copyright © 2024 izual. All rights reserved.
-//
-
-import SwiftUI
-
-@available(macOS 13.0, *)
-struct ServiceItemView: View {
- @Binding var service: QueryService
-
- var toggleValueChanged: (Bool) -> Void
-
- var body: some View {
- HStack {
- Image(nsImage: NSImage(named: service.serviceType().rawValue) ?? NSImage())
- .resizable()
- .frame(maxWidth: 18.0, maxHeight: 18.0)
-
- Text(service.name())
-
- Toggle(isOn: $service.enabled.didSet(execute: { value in
- toggleValueChanged(value)
- })) {}
- .toggleStyle(.switch)
- .controlSize(.small)
- }
- .padding(4.0)
- }
-}
-
-@available(macOS 13, *)
-#Preview {
- let service = EZLocalStorage.shared().allServices(.mini).first ?? QueryService()
- return ServiceItemView(service: .constant(service)) { val in
- print("toggle value changed: \(val)")
- }
-}
diff --git a/Easydict/NewApp/View/SettingView/SettingView.swift b/Easydict/NewApp/View/SettingView/SettingView.swift
index 8c4fec008..a7e27c991 100644
--- a/Easydict/NewApp/View/SettingView/SettingView.swift
+++ b/Easydict/NewApp/View/SettingView/SettingView.swift
@@ -17,41 +17,48 @@ enum SettingTab: Int {
@available(macOS 13, *)
struct SettingView: View {
- @State private var selection = SettingTab.general.rawValue
+ @State private var selection = SettingTab.general
@State private var window: NSWindow?
var body: some View {
- TabView(selection: $selection.didSet(execute: { _ in
- resizeWindowFrame()
- })) {
+ TabView(selection: $selection) {
GeneralTab()
.tabItem { Label("setting_general", systemImage: "gear") }
- .tag(SettingTab.general.rawValue)
+ .tag(SettingTab.general)
ServiceTab()
.tabItem { Label("service", systemImage: "briefcase") }
- .tag(SettingTab.service.rawValue)
+ .tag(SettingTab.service)
PrivacyTab()
.tabItem { Label("privacy", systemImage: "hand.raised.square") }
- .tag(SettingTab.privacy.rawValue)
+ .tag(SettingTab.privacy)
AboutTab()
.tabItem { Label("about", systemImage: "info.bubble") }
- .tag(SettingTab.about.rawValue)
+ .tag(SettingTab.about)
}
- .background(WindowAccessor(window: $window.didSet(execute: { _ in
- // reset frame when first launch
+ .background(
+ WindowAccessor(window: $window.didSet(execute: { _ in
+ // reset frame when first launch
+ resizeWindowFrame()
+ }))
+ )
+ .onChange(of: selection) { _ in
resizeWindowFrame()
- })))
+ }
}
func resizeWindowFrame() {
guard let window else { return }
let originalFrame = window.frame
- let newSize = selection == SettingTab.service.rawValue
- ? CGSize(width: 360, height: 520) : CGSize(width: 500, height: 400)
+ let newSize = switch selection {
+ case .general, .privacy, .about:
+ CGSize(width: 500, height: 520)
+ case .service:
+ CGSize(width: 800, height: 520)
+ }
let newY = originalFrame.origin.y + originalFrame.size.height - newSize.height
let newRect = NSRect(origin: CGPoint(x: originalFrame.origin.x, y: newY), size: newSize)
diff --git a/Easydict/NewApp/View/SettingView/Tabs/ServiceConfiguration/ServiceConfigurationSection.swift b/Easydict/NewApp/View/SettingView/Tabs/ServiceConfiguration/ServiceConfigurationSection.swift
new file mode 100644
index 000000000..42c59d0ee
--- /dev/null
+++ b/Easydict/NewApp/View/SettingView/Tabs/ServiceConfiguration/ServiceConfigurationSection.swift
@@ -0,0 +1,84 @@
+//
+// ServiceConfigurationSection.swift
+// Easydict
+//
+// Created by 戴藏龙 on 2024/1/14.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import SwiftUI
+
+@available(macOS 12.0, *)
+struct ServiceStringConfigurationSection: View {
+ /// Title of text field
+ let textFieldTitleKey: LocalizedStringKey
+ /// Header of section. If there is no need to add an header, just leave empty string
+ let headerTitleKey: LocalizedStringKey
+ /// Defaults key for configuration. Please refer to `Configuration` - `Configuration`
+ let key: Defaults.Key
+ /// Prompt of text field
+ let prompt: LocalizedStringKey
+ /// Footer of section. Add comments, footnotes or links to describe the field.
+ @ViewBuilder let footer: () -> F
+
+ var body: some View {
+ ServiceConfigurationSection(
+ headerTitleKey,
+ key: key,
+ field: { value in
+ let value = Binding.init {
+ value.wrappedValue ?? ""
+ } set: { newValue in
+ value.wrappedValue = newValue
+ }
+ TextField(textFieldTitleKey, text: value, prompt: Text(prompt))
+ },
+ footer: footer
+ )
+ }
+}
+
+@available(macOS 12.0, *)
+struct ServiceConfigurationSection: View {
+ @Default var value: T
+
+ init(
+ _ titleKey: LocalizedStringKey,
+ key: Defaults.Key,
+ @ViewBuilder field: @escaping (Binding) -> V,
+ footer: (() -> F)?
+ ) {
+ self.titleKey = titleKey
+ _value = .init(key)
+ self.footer = footer
+ self.field = field
+ }
+
+ let field: (Binding) -> V
+ let footer: (() -> F)?
+
+ let titleKey: LocalizedStringKey
+
+ var body: some View {
+ Section {
+ field($value)
+ } header: {
+ HStack(alignment: .lastTextBaseline) {
+ Text(titleKey)
+ Spacer()
+ Button("service.service_configuration.reset") {
+ _value.reset()
+ }
+ .buttonStyle(.plain)
+ .foregroundStyle(Color.accentColor)
+ .font(.footnote)
+ }
+ } footer: {
+ if let footer {
+ footer()
+ .font(.footnote)
+ }
+ }
+ }
+}
diff --git a/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift b/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
index 3da7ca423..5399f9af7 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
@@ -6,141 +6,203 @@
// Copyright © 2024 izual. All rights reserved.
//
+import Combine
import SwiftUI
@available(macOS 13, *)
struct ServiceTab: View {
- @State private var windowTypeValue = EZWindowType.mini.rawValue
- @State private var serviceTypes: [ServiceType] = []
- @State private var services: [QueryService] = []
- @State private var selectedIndex: Int?
- // workaround for tap gesture conflict with onMove
- @State private var isNeedTapHandler = true
-
- var segmentCtrl: some View {
- Picker("", selection: $windowTypeValue) {
- Text("mini_window")
- .tag(EZWindowType.mini.rawValue)
-
- Text("fixed_window")
- .tag(EZWindowType.fixed.rawValue)
-
- Text("main_window")
- .tag(EZWindowType.main.rawValue)
- }
- .padding()
- .pickerStyle(.segmented)
- .onChange(of: windowTypeValue) { type in
- loadService(type: type)
- selectedIndex = nil
- }
+ @StateObject private var viewModel: ServiceTabViewModel = .init()
+
+ @Environment(\.colorScheme) private var colorScheme
+
+ var bgColor: Color {
+ Color(nsColor: colorScheme == .light ? .windowBackgroundColor : .controlBackgroundColor)
}
- var serviceList: some View {
- List {
- ForEach(Array(zip(serviceTypes.indices, serviceTypes)), id: \.0) { index, _ in
- ServiceItemView(
- service: $services[index]
- ) { isEnable in
- serviceToggled(index: index, isEnable: isEnable)
- selectedIndex = nil
- isNeedTapHandler = false
+ var tableColor: Color {
+ Color(nsColor: colorScheme == .light ? .ez_tableRowViewBgLight() : .ez_tableRowViewBgDark())
+ }
+
+ var body: some View {
+ HStack {
+ VStack {
+ WindowTypePicker(windowType: $viewModel.windowType)
+ .padding()
+ List {
+ ServiceItems()
}
- .frame(height: 30)
- .tag(index)
- .listRowBackground(selectedIndex == index ? Color("service_cell_highlight") : Color.clear)
- .overlay(TapHandler(tapAction: {
- if !isNeedTapHandler {
- isNeedTapHandler.toggle()
- return
- }
- if selectedIndex == nil || selectedIndex != index {
- selectedIndex = index
+ .scrollContentBackground(.hidden)
+ .listStyle(.plain)
+ .scrollIndicators(.never)
+ .clipShape(RoundedRectangle(cornerRadius: 10))
+ .background(bgColor, in: RoundedRectangle(cornerRadius: 10))
+ .padding(.bottom)
+ .padding(.horizontal)
+ }
+ .background(bgColor)
+ Group {
+ if let service = viewModel.selectedService {
+ // To provide configuration options for a service, follow these steps
+ // 1. If the Service is an object of Objc, expose it to Swift.
+ // 2. Create a new file in the Utility - Extensions - QueryService+ConfigurableService,
+ // 3. referring to OpenAIService+ConfigurableService, `extension` the Service as `ConfigurableService` to provide the configuration items.
+ if let service = service as? (any ConfigurableService) {
+ AnyView(service.configurationView())
} else {
- selectedIndex = nil
+ HStack {
+ Spacer()
+ // No configuration for service xxx
+ Text("setting.service.detail.no_configuration \(service.name())")
+ Spacer()
+ }
+ }
+ } else {
+ HStack {
+ Spacer()
+ Text("setting.service.detail.no_selection")
+ Spacer()
}
- }))
+ }
}
- .onMove(perform: { indices, newOffset in
- onServiceItemMove(fromOffsets: indices, toOffset: newOffset)
- selectedIndex = nil
- })
- .listRowSeparator(.hidden)
+ .layoutPriority(1)
}
- .scrollIndicators(.hidden)
- .listStyle(.plain)
- .clipShape(RoundedRectangle(cornerRadius: 8.0))
- .padding([.horizontal, .bottom])
+ .environmentObject(viewModel)
}
+}
- var body: some View {
- VStack {
- segmentCtrl
-
- serviceList
- }
- .onAppear {
- loadService(type: windowTypeValue)
+private class ServiceTabViewModel: ObservableObject {
+ @Published var windowType = EZWindowType.mini {
+ didSet {
+ if oldValue != windowType {
+ updateServices()
+ selectedService = nil
+ }
}
}
- func loadService(type: Int) {
- let windowType = EZWindowType(rawValue: type) ?? .none
- services = EZLocalStorage.shared().allServices(windowType)
- serviceTypes = services.compactMap { $0.serviceType() }
- }
+ @Published var selectedService: QueryService?
- func serviceToggled(index: Int, isEnable: Bool) {
- let service = services[index]
- service.enabled = isEnable
- if isEnable {
- service.enabledQuery = true
- }
- let windowType = EZWindowType(rawValue: windowTypeValue) ?? .none
- EZLocalStorage.shared().setService(services[index], windowType: windowType)
- // refresh service list
- loadService(type: windowTypeValue)
- postUpdateServiceNotification()
+ @Published private(set) var services: [QueryService] = EZLocalStorage.shared().allServices(.mini)
+
+ func updateServices() {
+ services = getServices()
}
- func enabledServices(in services: [QueryService]) -> [QueryService] {
- services.filter(\.enabled)
+ func getServices() -> [QueryService] {
+ EZLocalStorage.shared().allServices(windowType)
}
func onServiceItemMove(fromOffsets: IndexSet, toOffset: Int) {
- let oldEnabledServices = enabledServices(in: services)
+ var services = services
services.move(fromOffsets: fromOffsets, toOffset: toOffset)
- serviceTypes.move(fromOffsets: fromOffsets, toOffset: toOffset)
- let windowType = EZWindowType(rawValue: windowTypeValue) ?? .none
+ let serviceTypes = services.map { service in
+ service.serviceType()
+ }
+
EZLocalStorage.shared().setAllServiceTypes(serviceTypes, windowType: windowType)
- let newServices = EZLocalStorage.shared().allServices(windowType)
- let newEnabledServices = enabledServices(in: newServices)
- // post notification after enabled services order changed
- if isEnabledServicesOrderChanged(source: oldEnabledServices, dest: newEnabledServices) {
- postUpdateServiceNotification()
- }
- }
+ postUpdateServiceNotification()
- func isEnabledServicesOrderChanged(
- source: [QueryService],
- dest: [QueryService]
- ) -> Bool {
- !source.elementsEqual(dest) { sItem, dItem in
- sItem.serviceType() == dItem.serviceType() && sItem.name() == dItem.name()
- }
+ updateServices()
}
func postUpdateServiceNotification() {
- let userInfo: [String: Any] = [EZWindowTypeKey: windowTypeValue]
+ let userInfo: [String: Any] = [EZWindowTypeKey: windowType.rawValue]
let notification = Notification(name: .serviceHasUpdated, object: nil, userInfo: userInfo)
NotificationCenter.default.post(notification)
}
}
+@available(macOS 13.0, *)
+private struct ServiceItems: View {
+ @EnvironmentObject private var viewModel: ServiceTabViewModel
+
+ private var servicesWithID: [(QueryService, String)] {
+ viewModel.services.map { service in
+ (service, service.name())
+ }
+ }
+
+ var body: some View {
+ ForEach(servicesWithID, id: \.1) { service, _ in
+ ServiceItemView(service: service)
+ .tag(service)
+ }
+ .onMove(perform: viewModel.onServiceItemMove)
+ }
+}
+
+@available(macOS 13.0, *)
+private struct ServiceItemView: View {
+ let service: QueryService
+
+ @EnvironmentObject private var viewModel: ServiceTabViewModel
+
+ private var enabled: Binding {
+ .init {
+ service.enabled
+ } set: { newValue in
+ guard service.enabled != newValue else { return }
+ service.enabled = newValue
+ if newValue {
+ service.enabledQuery = newValue
+ }
+ EZLocalStorage.shared().setService(service, windowType: viewModel.windowType)
+ viewModel.postUpdateServiceNotification()
+ }
+ }
+
+ var body: some View {
+ Toggle(isOn: enabled) {
+ HStack {
+ Image(service.serviceType().rawValue)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 20.0, height: 20.0)
+ Text(service.name())
+ .lineLimit(1)
+ .fixedSize()
+ }
+ }
+ .padding(4.0)
+ .toggleStyle(.switch)
+ .controlSize(.small)
+ .listRowSeparator(.hidden)
+ .listRowInsets(.init())
+ .padding(10)
+ .listRowBackground(viewModel.selectedService == service ? Color("service_cell_highlight") : tableColor)
+ .overlay {
+ TapHandler {
+ viewModel.selectedService = service
+ }
+ }
+ }
+
+ @Environment(\.colorScheme) private var colorScheme
+
+ private var tableColor: Color {
+ Color(nsColor: colorScheme == .light ? .ez_tableRowViewBgLight() : .ez_tableRowViewBgDark())
+ }
+}
+
@available(macOS 13, *)
-#Preview {
- ServiceTab()
+private struct WindowTypePicker: View {
+ @Binding var windowType: EZWindowType
+
+ var body: some View {
+ HStack {
+ Picker(selection: $windowType) {
+ ForEach([EZWindowType]([.mini, .fixed, .main]), id: \.rawValue) { windowType in
+ Text(windowType.localizedStringResource)
+ .tag(windowType)
+ }
+ } label: {
+ EmptyView()
+ }
+ .labelsHidden()
+ .pickerStyle(.segmented)
+ }
+ }
}
From 51b9c810f411dcee1c6bc733542d8270d613ed82 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Fri, 19 Jan 2024 10:08:41 +0800
Subject: [PATCH 25/72] docs: update Longman dict link
---
docs/How-to-use-macOS-system-dictionary-in-Easydict-en.md | 2 +-
docs/How-to-use-macOS-system-dictionary-in-Easydict-zh.md | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/How-to-use-macOS-system-dictionary-in-Easydict-en.md b/docs/How-to-use-macOS-system-dictionary-in-Easydict-en.md
index 42a3be4c6..9cfe6d240 100644
--- a/docs/How-to-use-macOS-system-dictionary-in-Easydict-en.md
+++ b/docs/How-to-use-macOS-system-dictionary-in-Easydict-en.md
@@ -43,7 +43,7 @@ Longman, Collins, and Oxford are three substantial yet outstanding dictionaries.
| Concise English-Chinese dictionary | Chinese-English | [GitHub](https://github.com/skywind3000/ECDICT) | https://drive.google.com/file/d/1-RoulJykOmcADGRHSmUjX2SkwiyLTHP1/view?usp=sharing |
| Youdao Words Analysis | Chinese-English | [freemdict](https://downloads.freemdict.com/%E5%B0%9A%E6%9C%AA%E6%95%B4%E7%90%86/%E5%85%B1%E4%BA%AB2020.5.11/qwjs/39_%E6%9C%89%E9%81%93%E8%AF%8D%E8%AF%AD%E8%BE%A8%E6%9E%90/) | https://drive.google.com/file/d/1-HGanRhQDRR0OSMLb19or07lPwn_R0cn/view?usp=sharing |
| Great Cictionary | Chinese-English | [mdict](https://mdict.org/post/dacihai/) | https://drive.google.com/file/d/1-8cBLcuA_N4PAjIMn_-d03ELv4uVrmIr/view?usp=sharing |
-| Longman Dictionary of Contemporary Advanced English | Chinese-English | [v2ex](https://www.v2ex.com/t/907272) | https://drive.google.com/file/d/1-7g-hDiwqAFtweL1qePKSRcGFJvruu97/view?usp=share_link |
+| Longman Dictionary of Contemporary Advanced English | Chinese-English | [v2ex](https://www.v2ex.com/t/907272) | https://drive.google.com/file/d/1scunXbe2JppVuKxNvn2uOidTbAZpiktk/view?usp=drive_link |
| Collins Advanced English-Chinese Dictionary | Chinese-English | [《柯林斯双解》for macOS](https://placeless.net/blog/macos-dictionaries) | https://drive.google.com/file/d/1-KQmILchx71L2rFqhIZMtusIcemIlM01/view?usp=share_link |
| Oxford Advanced Learner's English-Chinese Dictionary (8th Edition) | Chinese-English | [Jianshu](https://www.jianshu.com/p/e279d4a979fa) | https://drive.google.com/file/d/1-N0kiXmfTHREcBtumAmNn4sUM5poyiC7/view?usp=share_link |
| Oxford Advanced Learner's English-Chinese Dictionary (8) | Chinese-English | Source unknown, I modified the css myself | https://drive.google.com/file/d/1-SigzdPPjQlycPwBHICgQSUOHpR8mMf7/view?usp=share_link |
diff --git a/docs/How-to-use-macOS-system-dictionary-in-Easydict-zh.md b/docs/How-to-use-macOS-system-dictionary-in-Easydict-zh.md
index d990d6210..5faddb4c0 100644
--- a/docs/How-to-use-macOS-system-dictionary-in-Easydict-zh.md
+++ b/docs/How-to-use-macOS-system-dictionary-in-Easydict-zh.md
@@ -43,9 +43,9 @@ Easydict 自动支持词典 App 中系统自带的词典,如牛津英汉汉英
| 简明英汉字典 | 中英 | [GitHub](https://github.com/skywind3000/ECDICT) | https://drive.google.com/file/d/1-RoulJykOmcADGRHSmUjX2SkwiyLTHP1/view?usp=sharing |
| 有道词语辨析 | 中英 | [freemdict](https://downloads.freemdict.com/%E5%B0%9A%E6%9C%AA%E6%95%B4%E7%90%86/%E5%85%B1%E4%BA%AB2020.5.11/qwjs/39_%E6%9C%89%E9%81%93%E8%AF%8D%E8%AF%AD%E8%BE%A8%E6%9E%90/) | https://drive.google.com/file/d/1-HGanRhQDRR0OSMLb19or07lPwn_R0cn/view?usp=sharing |
| 大辞海 | 中文 | [mdict](https://mdict.org/post/dacihai/) | https://drive.google.com/file/d/1-8cBLcuA_N4PAjIMn_-d03ELv4uVrmIr/view?usp=sharing |
-| 朗文当代高级英语辞典 | 中英 | [v2ex](https://www.v2ex.com/t/907272) | https://drive.google.com/file/d/1-7g-hDiwqAFtweL1qePKSRcGFJvruu97/view?usp=share_link |
+| 朗文当代高级英语辞典 | 中英 | [v2ex](https://www.v2ex.com/t/907272) | https://drive.google.com/file/d/1scunXbe2JppVuKxNvn2uOidTbAZpiktk/view?usp=drive_link |
| 柯林斯高阶英汉双解 | 中英 | [《柯林斯双解》for macOS](https://placeless.net/blog/macos-dictionaries) | https://drive.google.com/file/d/1-KQmILchx71L2rFqhIZMtusIcemIlM01/view?usp=share_link |
-| 牛津高阶英汉双解词典(第8版) | 中英 | [简书](https://www.jianshu.com/p/e279d4a979fa) | https://drive.google.com/file/d/1-N0kiXmfTHREcBtumAmNn4sUM5poyiC7/view?usp=share_link |
+| 牛津高阶英汉双解词典(第 8 版) | 中英 | [简书](https://www.jianshu.com/p/e279d4a979fa) | https://drive.google.com/file/d/1-N0kiXmfTHREcBtumAmNn4sUM5poyiC7/view?usp=share_link |
| 牛津高阶英汉双解词典(8) | 中英 | 来源不详,我自己修改的 css | https://drive.google.com/file/d/1-SigzdPPjQlycPwBHICgQSUOHpR8mMf7/view?usp=share_link |
### 简明英汉字典
@@ -71,7 +71,7 @@ Easydict 自动支持词典 App 中系统自带的词典,如牛津英汉汉英
![image-20231001184454574](https://raw.githubusercontent.com/tisfeng/ImageBed/main/uPic/image-20231001184454574-1696157094.png)
-### 牛津高阶英汉双解词典(第8版)
+### 牛津高阶英汉双解词典(第 8 版)
![image-20231001185812289](https://raw.githubusercontent.com/tisfeng/ImageBed/main/uPic/image-20231001185812289-1696157892.png)
From 95ea1dba96ea83090bca03c933ed7c15714ef635 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Fri, 19 Jan 2024 18:03:38 +0800
Subject: [PATCH 26/72] fix: do not check Beta class, since it is Bool in Swift
---
Easydict/Feature/Configuration/EZConfiguration.m | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Easydict/Feature/Configuration/EZConfiguration.m b/Easydict/Feature/Configuration/EZConfiguration.m
index 939744f59..b15c3184d 100644
--- a/Easydict/Feature/Configuration/EZConfiguration.m
+++ b/Easydict/Feature/Configuration/EZConfiguration.m
@@ -665,7 +665,7 @@ - (void)setBeta:(BOOL)beta {
[NSUserDefaults mm_write:stringValue forKey:EZBetaFeatureKey];
}
- (BOOL)isBeta {
- NSString *stringValue = [NSUserDefaults mm_readString:EZBetaFeatureKey defaultValue:@"0"];
+ NSString *stringValue = [NSUserDefaults mm_read:EZBetaFeatureKey];
BOOL isBeta = [stringValue boolValue];
return isBeta;
}
From 25b651c7513511aef0acc9d2fda75406d376ce8a Mon Sep 17 00:00:00 2001
From: Tisfeng
Date: Sat, 20 Jan 2024 12:24:50 +0800
Subject: [PATCH 27/72] perf: adjust setting UI (#341)
* perf: set all setting pages to the same
* perf: disable zoom button in Settings
---
.../NewApp/View/SettingView/SettingView.swift | 21 ++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/Easydict/NewApp/View/SettingView/SettingView.swift b/Easydict/NewApp/View/SettingView/SettingView.swift
index a7e27c991..c6446538c 100644
--- a/Easydict/NewApp/View/SettingView/SettingView.swift
+++ b/Easydict/NewApp/View/SettingView/SettingView.swift
@@ -51,15 +51,26 @@ struct SettingView: View {
func resizeWindowFrame() {
guard let window else { return }
+
+ // Disable zoom button, ref: https://stackoverflow.com/a/66039864/8378840
+ window.standardWindowButton(.zoomButton)?.isEnabled = false
- let originalFrame = window.frame
- let newSize = switch selection {
- case .general, .privacy, .about:
- CGSize(width: 500, height: 520)
+ // Keep the settings page windows all the same width to avoid strange animations.
+ let maxWidth = 650
+ let height = switch selection {
+ case .general:
+ maxWidth
case .service:
- CGSize(width: 800, height: 520)
+ 500
+ case .privacy:
+ 320
+ case .about:
+ 450
}
+ let newSize = CGSize(width: maxWidth, height: height)
+
+ let originalFrame = window.frame
let newY = originalFrame.origin.y + originalFrame.size.height - newSize.height
let newRect = NSRect(origin: CGPoint(x: originalFrame.origin.x, y: newY), size: newSize)
From 1f6fdc27ce25060057d6b0aca71eb7719e521f24 Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Sat, 20 Jan 2024 16:32:43 +0800
Subject: [PATCH 28/72] add disabled app list tab (#340)
* feat: add disabled app tab
* fix: refactor app list issues
* fix: refactor disabled app tab
* fix: remove list row bg color
* fix: bundle application name issue
* fix: update code for review comments
* fix: update app item view model
* fix: remove item view model
* fix: refactor disable app item view
* perf: adjust add & minus bg color
* perf: add a border for list
* perf: adjust custom color name
* perf: show minus image color according to enable state
* perf: adjust disabled tab UI
* fix: add disabled title horizontal padding
* fix: code refactor
* fix: disabled environment
* fix: disable issue
---------
Co-authored-by: tisfeng
---
Easydict.xcodeproj/project.pbxproj | 23 +-
.../App/Assets.xcassets/Colors/Contents.json | 6 +
.../add_minus_bg_color.colorset/Contents.json | 38 +++
.../list_border_color.colorset/Contents.json | 38 +++
.../Contents.json | 0
Easydict/App/Easydict-Bridging-Header.h | 1 +
Easydict/App/Localizable.xcstrings | 20 ++
.../Utility/Swift/Bundle/Bundle+AppInfo.swift | 23 ++
.../NewApp/View/SettingView/SettingView.swift | 9 +-
.../SettingView/Tabs/DisabledAppTab.swift | 231 ++++++++++++++++++
.../View/SettingView/Tabs/ServiceTab.swift | 2 +-
11 files changed, 385 insertions(+), 6 deletions(-)
create mode 100644 Easydict/App/Assets.xcassets/Colors/Contents.json
create mode 100644 Easydict/App/Assets.xcassets/Colors/add_minus_bg_color.colorset/Contents.json
create mode 100644 Easydict/App/Assets.xcassets/Colors/list_border_color.colorset/Contents.json
rename Easydict/App/Assets.xcassets/{service_cell_highlight.colorset => Colors/service_cell_highlight_color.colorset}/Contents.json (100%)
create mode 100644 Easydict/Feature/Utility/Swift/Bundle/Bundle+AppInfo.swift
create mode 100644 Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index 9bb89217d..c1c23e80f 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -229,8 +229,10 @@
03FD68BB2B1DC59600FD388E /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 03FD68BA2B1DC59600FD388E /* CryptoSwift */; };
03FD68BE2B1E151A00FD388E /* String+EncryptAES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */; };
0A057D6D2B499A000025C51D /* ServiceTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A057D6C2B499A000025C51D /* ServiceTab.swift */; };
+ 0A2A05A62B59757100EEA142 /* Bundle+AppInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2A05A52B59757100EEA142 /* Bundle+AppInfo.swift */; };
0A2BA9602B49A989002872A4 /* Binding+DidSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */; };
0A2BA9642B4A3CCD002872A4 /* Notification+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */; };
+ 0A8685C82B552A590022534F /* DisabledAppTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8685C72B552A590022534F /* DisabledAppTab.swift */; };
0AC11B222B4D16A500F07198 /* WindowAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC11B212B4D16A500F07198 /* WindowAccessor.swift */; };
0AC11B242B4E46B300F07198 /* TapHandlerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC11B232B4E46B300F07198 /* TapHandlerView.swift */; };
17BCAEF72B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 17BCAEF52B0DFF9000A7D372 /* EZNiuTransTranslateResponse.m */; };
@@ -704,8 +706,10 @@
03FD68BD2B1E151A00FD388E /* String+EncryptAES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+EncryptAES.swift"; sourceTree = ""; };
06E15747A7BD34D510ADC6A8 /* Pods-Easydict.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Easydict.debug.xcconfig"; path = "Target Support Files/Pods-Easydict/Pods-Easydict.debug.xcconfig"; sourceTree = ""; };
0A057D6C2B499A000025C51D /* ServiceTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceTab.swift; sourceTree = ""; };
+ 0A2A05A52B59757100EEA142 /* Bundle+AppInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AppInfo.swift"; sourceTree = ""; };
0A2BA95F2B49A989002872A4 /* Binding+DidSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+DidSet.swift"; sourceTree = ""; };
0A2BA9632B4A3CCD002872A4 /* Notification+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Name.swift"; sourceTree = ""; };
+ 0A8685C72B552A590022534F /* DisabledAppTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisabledAppTab.swift; sourceTree = ""; };
0AC11B212B4D16A500F07198 /* WindowAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowAccessor.swift; sourceTree = ""; };
0AC11B232B4E46B300F07198 /* TapHandlerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapHandlerView.swift; sourceTree = ""; };
17BCAEF32B0DFF9000A7D372 /* EZNiuTransTranslateResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZNiuTransTranslateResponse.h; sourceTree = ""; };
@@ -1821,6 +1825,7 @@
03CF88602B137ECB0030C199 /* Swift */ = {
isa = PBXGroup;
children = (
+ 0A2A05A42B59755F00EEA142 /* Bundle */,
0A2BA9622B4A3CBB002872A4 /* Notification */,
0A2BA95E2B49A967002872A4 /* Binding */,
03FD68BC2B1E14B500FD388E /* String */,
@@ -2001,6 +2006,14 @@
path = String;
sourceTree = "";
};
+ 0A2A05A42B59755F00EEA142 /* Bundle */ = {
+ isa = PBXGroup;
+ children = (
+ 0A2A05A52B59757100EEA142 /* Bundle+AppInfo.swift */,
+ );
+ path = Bundle;
+ sourceTree = "";
+ };
0A2BA95E2B49A967002872A4 /* Binding */ = {
isa = PBXGroup;
children = (
@@ -2077,6 +2090,7 @@
EAED41EA2B54A4900005FE0A /* ServiceConfiguration */,
278540332B3DE04F004E9488 /* GeneralTab.swift */,
0A057D6C2B499A000025C51D /* ServiceTab.swift */,
+ 0A8685C72B552A590022534F /* DisabledAppTab.swift */,
276742042B3DC230002A2C75 /* PrivacyTab.swift */,
276742052B3DC230002A2C75 /* AboutTab.swift */,
);
@@ -2320,7 +2334,7 @@
buildConfigurationList = C99EEB2C2385796900FEE666 /* Build configuration list for PBXNativeTarget "Easydict" */;
buildPhases = (
21D768ECC6D11E109E6EB73A /* [CP] Check Pods Manifest.lock */,
- 03B04B582B2D4B8E00E30823 /* ShellScript */,
+ 03B04B582B2D4B8E00E30823 /* Run Script */,
C99EEB142385796700FEE666 /* Sources */,
C99EEB152385796700FEE666 /* Frameworks */,
C99EEB162385796700FEE666 /* Resources */,
@@ -2479,16 +2493,17 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 03B04B582B2D4B8E00E30823 /* ShellScript */ = {
+ 03B04B582B2D4B8E00E30823 /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
- buildActionMask = 2147483647;
+ buildActionMask = 12;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
+ name = "Run Script";
outputFileListPaths = (
);
outputPaths = (
@@ -2652,6 +2667,7 @@
9672D7D22B4008B40023B8FB /* MASShortcutBinder+EZMASShortcutBinder.m in Sources */,
03BDA7BF2A26DA280079D04F /* NSScanner+EscapedScanning.m in Sources */,
03542A4C2937B5F100C34C33 /* EZYoudaoTranslate.m in Sources */,
+ 0A2A05A62B59757100EEA142 /* Bundle+AppInfo.swift in Sources */,
037852B329583F5200D0E2CF /* EZServiceCell.m in Sources */,
03247E362968158B00AFCD67 /* EZScriptExecutor.m in Sources */,
03882F8E29D95044005B5A52 /* ToastWindowController.m in Sources */,
@@ -2691,6 +2707,7 @@
17BCAEF82B0DFF9000A7D372 /* EZNiuTransTranslate.m in Sources */,
039F5506294B6E29004AB940 /* EZSettingViewController.m in Sources */,
03BD281E29481C0400F5891A /* EZAudioPlayer.m in Sources */,
+ 0A8685C82B552A590022534F /* DisabledAppTab.swift in Sources */,
03E02A2629250D1D00A10260 /* EZEventMonitor.m in Sources */,
03B0233429231FA6001C7E63 /* MMConsoleLogFormatter.m in Sources */,
037852B9295D49F900D0E2CF /* EZTableRowView.m in Sources */,
diff --git a/Easydict/App/Assets.xcassets/Colors/Contents.json b/Easydict/App/Assets.xcassets/Colors/Contents.json
new file mode 100644
index 000000000..73c00596a
--- /dev/null
+++ b/Easydict/App/Assets.xcassets/Colors/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Easydict/App/Assets.xcassets/Colors/add_minus_bg_color.colorset/Contents.json b/Easydict/App/Assets.xcassets/Colors/add_minus_bg_color.colorset/Contents.json
new file mode 100644
index 000000000..a83dbf8e7
--- /dev/null
+++ b/Easydict/App/Assets.xcassets/Colors/add_minus_bg_color.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xE9",
+ "green" : "0xE9",
+ "red" : "0xEA"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x3A",
+ "green" : "0x39",
+ "red" : "0x3B"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Easydict/App/Assets.xcassets/Colors/list_border_color.colorset/Contents.json b/Easydict/App/Assets.xcassets/Colors/list_border_color.colorset/Contents.json
new file mode 100644
index 000000000..d60e3bb57
--- /dev/null
+++ b/Easydict/App/Assets.xcassets/Colors/list_border_color.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xDE",
+ "green" : "0xDD",
+ "red" : "0xDE"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x50",
+ "green" : "0x50",
+ "red" : "0x50"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Easydict/App/Assets.xcassets/service_cell_highlight.colorset/Contents.json b/Easydict/App/Assets.xcassets/Colors/service_cell_highlight_color.colorset/Contents.json
similarity index 100%
rename from Easydict/App/Assets.xcassets/service_cell_highlight.colorset/Contents.json
rename to Easydict/App/Assets.xcassets/Colors/service_cell_highlight_color.colorset/Contents.json
diff --git a/Easydict/App/Easydict-Bridging-Header.h b/Easydict/App/Easydict-Bridging-Header.h
index f98fcafec..06670f99e 100644
--- a/Easydict/App/Easydict-Bridging-Header.h
+++ b/Easydict/App/Easydict-Bridging-Header.h
@@ -21,6 +21,7 @@
#import "entry.h"
#import "AppDelegate.h"
#import "EZConfiguration.h"
+#import "EZAppModel.h"
#import "EZLocalStorage.h"
#import "NSString+EZConvenience.h"
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index 2114e3a63..aacf0329e 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -1,6 +1,9 @@
{
"sourceLanguage" : "en",
"strings" : {
+ "" : {
+
+ },
"about" : {
"comment" : "about",
"localizations" : {
@@ -2415,6 +2418,23 @@
}
}
},
+ "setting.disabled.import_app_error.message" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Unable to add Application"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "无法添加应用"
+ }
+ }
+ }
+ },
"setting.general.advance.default_tts_service" : {
"localizations" : {
"en" : {
diff --git a/Easydict/Feature/Utility/Swift/Bundle/Bundle+AppInfo.swift b/Easydict/Feature/Utility/Swift/Bundle/Bundle+AppInfo.swift
new file mode 100644
index 000000000..c095084cb
--- /dev/null
+++ b/Easydict/Feature/Utility/Swift/Bundle/Bundle+AppInfo.swift
@@ -0,0 +1,23 @@
+//
+// Bundle+AppInfo.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/18.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Foundation
+
+extension Bundle {
+ var applicationName: String {
+ if let displayName: String = object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
+ return displayName
+ } else if let name: String = object(forInfoDictionaryKey: "CFBundleName") as? String {
+ return name
+ }
+ if let executableURL {
+ return executableURL.deletingLastPathComponent().lastPathComponent
+ }
+ return ""
+ }
+}
diff --git a/Easydict/NewApp/View/SettingView/SettingView.swift b/Easydict/NewApp/View/SettingView/SettingView.swift
index c6446538c..f64f70efc 100644
--- a/Easydict/NewApp/View/SettingView/SettingView.swift
+++ b/Easydict/NewApp/View/SettingView/SettingView.swift
@@ -11,6 +11,7 @@ import SwiftUI
enum SettingTab: Int {
case general
case service
+ case disabled
case privacy
case about
}
@@ -30,6 +31,10 @@ struct SettingView: View {
.tabItem { Label("service", systemImage: "briefcase") }
.tag(SettingTab.service)
+ DisabledAppTab()
+ .tabItem { Label("disabled_app_list", systemImage: "nosign") }
+ .tag(SettingTab.disabled)
+
PrivacyTab()
.tabItem { Label("privacy", systemImage: "hand.raised.square") }
.tag(SettingTab.privacy)
@@ -51,7 +56,7 @@ struct SettingView: View {
func resizeWindowFrame() {
guard let window else { return }
-
+
// Disable zoom button, ref: https://stackoverflow.com/a/66039864/8378840
window.standardWindowButton(.zoomButton)?.isEnabled = false
@@ -60,7 +65,7 @@ struct SettingView: View {
let height = switch selection {
case .general:
maxWidth
- case .service:
+ case .service, .disabled:
500
case .privacy:
320
diff --git a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
new file mode 100644
index 000000000..a121652c7
--- /dev/null
+++ b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
@@ -0,0 +1,231 @@
+//
+// DisabledAppTab.swift
+// Easydict
+//
+// Created by phlpsong on 2024/1/15.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Combine
+import SwiftUI
+
+private class DisabledAppViewModel: ObservableObject {
+ @Published var appModelList: [EZAppModel] = []
+ @Published var selectedAppModels: Set = []
+ @Published var isImporting = false
+ @Published var isShowImportErrorAlert = false
+
+ init() {
+ fetchDisabledApps()
+ }
+
+ func fetchDisabledApps() {
+ appModelList = EZLocalStorage.shared().selectTextTypeAppModelList
+ }
+
+ func saveDisabledApps() {
+ EZLocalStorage.shared().selectTextTypeAppModelList = appModelList
+ }
+
+ func removeDisabledApp() {
+ appModelList = appModelList.filter { !selectedAppModels.contains($0) }
+ saveDisabledApps()
+ selectedAppModels = []
+ }
+
+ func newAppURLsSelected(from urls: [URL]) {
+ urls.forEach { url in
+ let gotAccess = url.startAccessingSecurityScopedResource()
+ if !gotAccess { return }
+ appendNewDisabledApp(for: url)
+ url.stopAccessingSecurityScopedResource()
+ }
+ }
+
+ func appendNewDisabledApp(for url: URL) {
+ guard let selectAppModel = disabledAppModel(from: url) else { return }
+ guard !appModelList.contains(selectAppModel) else { return }
+ appModelList.append(selectAppModel)
+ saveDisabledApps()
+ }
+
+ func disabledAppModel(from url: URL) -> EZAppModel? {
+ let appModel = EZAppModel()
+ guard let bundle = Bundle(url: url) else { return nil }
+ appModel.appBundleID = bundle.bundleIdentifier ?? ""
+ appModel.triggerType = []
+ return appModel
+ }
+}
+
+@available(macOS 13.0, *)
+struct DisabledAppTab: View {
+ @StateObject private var disabledAppViewModel = DisabledAppViewModel()
+
+ var listToolbar: some View {
+ ListToolbar()
+ .fileImporter(
+ isPresented: $disabledAppViewModel.isImporting,
+ allowedContentTypes: [.application],
+ allowsMultipleSelection: true
+ ) { result in
+ switch result {
+ case let .success(urls):
+ disabledAppViewModel.newAppURLsSelected(from: urls)
+ case let .failure(error):
+ print("fileImporter error: \(error)")
+ disabledAppViewModel.isShowImportErrorAlert.toggle()
+ }
+ }
+ .alert(isPresented: $disabledAppViewModel.isShowImportErrorAlert) {
+ Alert(title: Text(""), message: Text("setting.disabled.import_app_error.message"), dismissButton: .default(Text("ok")))
+ }
+ }
+
+ var appListView: some View {
+ List(selection: $disabledAppViewModel.selectedAppModels) {
+ ForEach(disabledAppViewModel.appModelList, id: \.self) { app in
+ BlockAppItemView(with: app)
+ .tag(app)
+ }
+ .listRowSeparator(.hidden)
+ }
+ .listStyle(.plain)
+ .scrollIndicators(.never)
+ }
+
+ var appListViewWithToolbar: some View {
+ VStack(spacing: 0) {
+ appListView
+
+ listToolbar
+ }
+ .clipShape(RoundedRectangle(cornerRadius: 8))
+ .overlay(content: {
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(Color("list_border_color"), lineWidth: 0.5)
+ })
+ .padding(.horizontal, 25)
+ .padding(.bottom, 25)
+ .onTapGesture {
+ disabledAppViewModel.selectedAppModels = []
+ }
+ }
+
+ var body: some View {
+ VStack {
+ Text("disabled_title")
+ .padding(.horizontal)
+ .padding(.top, 18)
+ .padding(.bottom, 8)
+
+ appListViewWithToolbar
+ }
+ .environmentObject(disabledAppViewModel)
+ }
+}
+
+@available(macOS 13.0, *)
+private struct ListToolbar: View {
+ @EnvironmentObject private var disabledAppViewModel: DisabledAppViewModel
+
+ var body: some View {
+ VStack(spacing: 0) {
+ Divider()
+ HStack(spacing: 0) {
+ ListButton(systemName: "plus") {
+ disabledAppViewModel.isImporting.toggle()
+ }
+ .disabled(false)
+ Divider()
+ .padding(.vertical, 1)
+ ListButton(systemName: "minus") {
+ disabledAppViewModel.removeDisabledApp()
+ }
+ .disabled(disabledAppViewModel.selectedAppModels.isEmpty)
+ Spacer()
+ }
+ .padding(2)
+ }
+ .frame(height: 28)
+ .background(Color("add_minus_bg_color"))
+ }
+}
+
+@available(macOS 13.0, *)
+private struct ListButton: View {
+ @Environment(\.isEnabled) private var isEnabled: Bool
+ var systemName: String
+ var action: () -> Void
+
+ var body: some View {
+ Button(action: {
+ action()
+ }) {
+ Image(systemName: systemName)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 10, height: 10)
+ .padding(.horizontal, 8)
+ .contentShape(Rectangle())
+ .foregroundStyle(isEnabled ? Color(.secondaryLabelColor) : Color(.tertiaryLabelColor))
+ .font(.system(size: 14, weight: .semibold))
+ }
+ .buttonStyle(BorderlessButtonStyle())
+ }
+}
+
+@available(macOS 13.0, *)
+private struct BlockAppItemView: View {
+ @EnvironmentObject var disabledAppViewModel: DisabledAppViewModel
+
+ let appIcon: NSImage
+ let appName: String
+
+ init(with appModel: EZAppModel) {
+ let appBundleId = appModel.appBundleID
+ let workspace = NSWorkspace.shared
+ let appURL = workspace.urlForApplication(withBundleIdentifier: appBundleId)
+ guard let appURL else {
+ appIcon = .init()
+ appName = ""
+ return
+ }
+
+ let appPath = NSWorkspace.shared.urlForApplication(withBundleIdentifier: appBundleId)
+ guard let appPath else {
+ appIcon = .init()
+ appName = ""
+ return
+ }
+ appIcon = workspace.icon(forFile: appPath.path(percentEncoded: false))
+
+ guard let appBundle = Bundle(url: appURL) else {
+ appName = ""
+ return
+ }
+ appName = appBundle.applicationName
+ }
+
+ var body: some View {
+ HStack(alignment: .center) {
+ Image(nsImage: appIcon)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 24, height: 24)
+
+ Text(appName)
+
+ Spacer()
+ }
+ .frame(maxWidth: .infinity)
+ .contentShape(Rectangle())
+ .padding(.vertical, 4)
+ .padding(.leading, 6)
+ }
+}
+
+@available(macOS 13.0, *)
+#Preview {
+ DisabledAppTab()
+}
diff --git a/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift b/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
index 5399f9af7..83a5f6398 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/ServiceTab.swift
@@ -172,7 +172,7 @@ private struct ServiceItemView: View {
.listRowSeparator(.hidden)
.listRowInsets(.init())
.padding(10)
- .listRowBackground(viewModel.selectedService == service ? Color("service_cell_highlight") : tableColor)
+ .listRowBackground(viewModel.selectedService == service ? Color("service_cell_highlight_color") : tableColor)
.overlay {
TapHandler {
viewModel.selectedService = service
From 5384b39a05cad484b1f383d2a8668cf36220ae0e Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Sun, 21 Jan 2024 09:37:12 +0800
Subject: [PATCH 29/72] fix: disable app list lag after add too many apps
(#347)
---
.../SettingView/Tabs/DisabledAppTab.swift | 58 ++++++++++---------
1 file changed, 32 insertions(+), 26 deletions(-)
diff --git a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
index a121652c7..7d70402ff 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
@@ -179,42 +179,20 @@ private struct ListButton: View {
private struct BlockAppItemView: View {
@EnvironmentObject var disabledAppViewModel: DisabledAppViewModel
- let appIcon: NSImage
- let appName: String
+ @StateObject private var appItemViewModel: AppItemViewModel
init(with appModel: EZAppModel) {
- let appBundleId = appModel.appBundleID
- let workspace = NSWorkspace.shared
- let appURL = workspace.urlForApplication(withBundleIdentifier: appBundleId)
- guard let appURL else {
- appIcon = .init()
- appName = ""
- return
- }
-
- let appPath = NSWorkspace.shared.urlForApplication(withBundleIdentifier: appBundleId)
- guard let appPath else {
- appIcon = .init()
- appName = ""
- return
- }
- appIcon = workspace.icon(forFile: appPath.path(percentEncoded: false))
-
- guard let appBundle = Bundle(url: appURL) else {
- appName = ""
- return
- }
- appName = appBundle.applicationName
+ _appItemViewModel = StateObject(wrappedValue: AppItemViewModel(appModel: appModel))
}
var body: some View {
HStack(alignment: .center) {
- Image(nsImage: appIcon)
+ Image(nsImage: appItemViewModel.appIcon)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
- Text(appName)
+ Text(appItemViewModel.appName)
Spacer()
}
@@ -225,6 +203,34 @@ private struct BlockAppItemView: View {
}
}
+@available(macOS 13.0, *)
+private class AppItemViewModel: ObservableObject {
+ @Published var appIcon = NSImage()
+
+ @Published var appName = ""
+
+ var appModel: EZAppModel
+
+ init(appModel: EZAppModel) {
+ self.appModel = appModel
+ getAppBundleInfo()
+ }
+
+ func getAppBundleInfo() {
+ let appBundleId = appModel.appBundleID
+ let workspace = NSWorkspace.shared
+ let appURL = workspace.urlForApplication(withBundleIdentifier: appBundleId)
+ guard let appURL else { return }
+
+ let appPath = NSWorkspace.shared.urlForApplication(withBundleIdentifier: appBundleId)
+ guard let appPath else { return }
+ appIcon = workspace.icon(forFile: appPath.path(percentEncoded: false))
+
+ guard let appBundle = Bundle(url: appURL) else { return }
+ appName = appBundle.applicationName
+ }
+}
+
@available(macOS 13.0, *)
#Preview {
DisabledAppTab()
From 18ae512d90b684208d8a1235de49ad40ed467eab Mon Sep 17 00:00:00 2001
From: NeverAgain11
Date: Tue, 23 Jan 2024 17:08:43 +0800
Subject: [PATCH 30/72] [Refactor] rewrite EZConfiguration using Swift (#335)
* rewrite EZConfiguration using swift
* add nullable to the error in EZScriptExecutor
* use DefaultsWrapper to replace Defaults
* refactor EZConfiguration+EZUserData using swift
* Remove the limitation to the defaultTTSServiceType
* add missing quotation
* Replace EZConfiguration with Configuration
* fix: observe Configuration keys when init cause dead cycle
* fixed Simultaneous accesses
* make the rawValue of TTSServiceType equivalent to ServiceType
* fix: updateDarkMode missing parameter
* fix: key changes trigger the observer multi times
* fixed Simultaneous accesses
* fix input error service type
* remove unused code
* remove unused code
Co-authored-by: Tisfeng
* rename files
* remove #import "EZConfiguration.h" from Easydict-Bridging-Header.h
* Remove unnecessary code
---------
Co-authored-by: tisfeng
Co-authored-by: Lava <34743145+CanglongCl@users.noreply.github.com>
---
Easydict.xcodeproj/project.pbxproj | 16 +-
Easydict/App/AppDelegate+EZURLScheme.m | 6 +-
Easydict/App/AppDelegate.m | 2 +-
Easydict/App/Easydict-Bridging-Header.h | 7 +-
.../Configuration+UserData.swift | 63 ++
.../Feature/Configuration/Configuration.swift | 701 ++++++++++++++++++
.../Feature/Configuration/EZConfiguration.m | 2 +-
Easydict/Feature/DarkMode/DarkModeManager.h | 5 +-
Easydict/Feature/DarkMode/DarkModeManager.m | 10 +-
.../Feature/EventMonitor/EZEventMonitor.m | 11 +-
.../EZDisableAutoSelectTextViewController.m | 5 +-
.../PerferenceWindow/EZAboutViewController.m | 5 +-
.../EZPrivacyViewController.m | 9 +-
.../EZSettingViewController.m | 6 +-
.../Feature/Service/Apple/EZAppleService.m | 3 +-
.../Feature/Service/Apple/EZScriptExecutor.h | 8 +-
.../Feature/Service/Apple/EZScriptExecutor.m | 2 +-
.../Service/AudioPlayer/EZAudioPlayer.m | 3 +-
.../Feature/Service/Baidu/EZBaiduTranslate.m | 6 +-
Easydict/Feature/Service/Bing/EZBingService.m | 3 +-
.../Service/Google/EZGoogleTranslate.m | 3 +-
.../Service/Language/EZLanguageManager.m | 5 +-
.../Feature/Service/Model/EZDetectManager.m | 7 +-
.../Feature/Service/Model/EZQueryService.m | 3 +-
.../OpenAI/EZOpenAIService+EZPromptMessages.m | 5 +-
.../Feature/Service/OpenAI/EZOpenAIService.m | 4 +-
.../Service/Youdao/EZYoudaoTranslate.m | 2 +-
.../Feature/StatusItem/EZMenuItemManager.m | 6 +-
.../AppleScript/EZAppleScriptManager.m | 3 +-
.../Utility/EZLinkParser/EZSchemeParser.m | 15 +-
Easydict/Feature/Utility/EZLog/EZLog.m | 2 +-
.../Cell/EZSelectLanguageCell.m | 15 +-
.../ViewController/Model/EZQueryModel.m | 7 +-
.../EZQueryMenuTextView/EZQueryMenuTextView.m | 7 +-
.../View/QueryView/EZQueryView.m | 4 +-
.../ViewController/View/Titlebar/EZTitlebar.m | 7 +-
.../View/WordResultView/EZWebViewManager.m | 3 +-
.../View/WordResultView/EZWordResultView.m | 8 +-
.../EZBaseQueryViewController.m | 14 +-
.../Window/WindowManager/EZLayoutManager.m | 5 +-
.../Window/WindowManager/EZWindowManager.m | 29 +-
...ion.swift => Configuration+Defaults.swift} | 78 +-
Easydict/NewApp/Model/TTSServiceType.swift | 10 +-
.../LanguageDetectOptimizeExtensions.swift | 8 +-
.../View/SettingView/Tabs/GeneralTab.swift | 2 +-
45 files changed, 1000 insertions(+), 125 deletions(-)
create mode 100644 Easydict/Feature/Configuration/Configuration+UserData.swift
create mode 100644 Easydict/Feature/Configuration/Configuration.swift
rename Easydict/NewApp/Configuration/{Configuration.swift => Configuration+Defaults.swift} (68%)
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index c1c23e80f..43ca6f7b7 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -265,9 +265,11 @@
C4DE3D6D2AC00EB500C2B85D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = C4DE3D6C2AC00EB500C2B85D /* Localizable.xcstrings */; };
C98CAE75239F4619005F7DCA /* EasydictHelper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = C90BE309239F38EB00ADE88B /* EasydictHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
DC3C643F2B187119008EEDD8 /* ChangeFontSizeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3C643E2B187119008EEDD8 /* ChangeFontSizeView.swift */; };
+ DC46DF802B4417B900DEAE3E /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC46DF7F2B4417B900DEAE3E /* Configuration.swift */; };
DC6D9C872B352EBC0055EFFC /* FontSizeHintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C862B352EBC0055EFFC /* FontSizeHintView.swift */; };
DC6D9C892B3969510055EFFC /* Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6D9C882B3969510055EFFC /* Appearance.swift */; };
- EA3B81F92B5254AA004C0E8B /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3B81F82B5254AA004C0E8B /* Configuration.swift */; };
+ DCF176F22B57CED700CA6026 /* Configuration+UserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF176F12B57CED700CA6026 /* Configuration+UserData.swift */; };
+ EA3B81F92B5254AA004C0E8B /* Configuration+Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3B81F82B5254AA004C0E8B /* Configuration+Defaults.swift */; };
EA3B81FC2B52555C004C0E8B /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = EA3B81FB2B52555C004C0E8B /* Defaults */; };
EA9943E32B534C3300EE7B97 /* TTSServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */; };
EA9943E82B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */; };
@@ -758,9 +760,11 @@
C90BE309239F38EB00ADE88B /* EasydictHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EasydictHelper.app; sourceTree = BUILT_PRODUCTS_DIR; };
C99EEB182385796700FEE666 /* Easydict-debug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Easydict-debug.app"; sourceTree = BUILT_PRODUCTS_DIR; };
DC3C643E2B187119008EEDD8 /* ChangeFontSizeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeFontSizeView.swift; sourceTree = ""; };
+ DC46DF7F2B4417B900DEAE3E /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; };
DC6D9C862B352EBC0055EFFC /* FontSizeHintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSizeHintView.swift; sourceTree = ""; };
DC6D9C882B3969510055EFFC /* Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = ""; };
- EA3B81F82B5254AA004C0E8B /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; };
+ DCF176F12B57CED700CA6026 /* Configuration+UserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Configuration+UserData.swift"; sourceTree = ""; };
+ EA3B81F82B5254AA004C0E8B /* Configuration+Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Configuration+Defaults.swift"; sourceTree = ""; };
EA9943E22B534C3300EE7B97 /* TTSServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTSServiceType.swift; sourceTree = ""; };
EA9943E72B534D8900EE7B97 /* LanguageDetectOptimizeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageDetectOptimizeExtensions.swift; sourceTree = ""; };
EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTypeExtensions.swift; sourceTree = ""; };
@@ -1298,6 +1302,8 @@
03542A572937CC3200C34C33 /* EZConfiguration.m */,
03D8A65A2A433B4100D9A968 /* EZConfiguration+EZUserData.h */,
03D8A65B2A433B4100D9A968 /* EZConfiguration+EZUserData.m */,
+ DC46DF7F2B4417B900DEAE3E /* Configuration.swift */,
+ DCF176F12B57CED700CA6026 /* Configuration+UserData.swift */,
DC6D9C882B3969510055EFFC /* Appearance.swift */,
);
path = Configuration;
@@ -2213,7 +2219,7 @@
EA3B81F72B52549B004C0E8B /* Configuration */ = {
isa = PBXGroup;
children = (
- EA3B81F82B5254AA004C0E8B /* Configuration.swift */,
+ EA3B81F82B5254AA004C0E8B /* Configuration+Defaults.swift */,
);
path = Configuration;
sourceTree = "";
@@ -2654,6 +2660,7 @@
03FD68BE2B1E151A00FD388E /* String+EncryptAES.swift in Sources */,
03B0230729231FA6001C7E63 /* EZCommonView.m in Sources */,
03B0233329231FA6001C7E63 /* MMLog.m in Sources */,
+ DCF176F22B57CED700CA6026 /* Configuration+UserData.swift in Sources */,
0309E1F4292BD6A100AFB76A /* EZQueryModel.m in Sources */,
03BFFC7129612E10004E033E /* NSString+EZConvenience.m in Sources */,
03BDA7BD2A26DA280079D04F /* XPMArguments_Coalescer_Internal.m in Sources */,
@@ -2728,7 +2735,7 @@
03B0233229231FA6001C7E63 /* MMLog.swift in Sources */,
03DC7C5E2A3ABE28000BF7C9 /* EZConstKey.m in Sources */,
62E2BF4C2B4082BA00E42D38 /* AliTranslateType.swift in Sources */,
- EA3B81F92B5254AA004C0E8B /* Configuration.swift in Sources */,
+ EA3B81F92B5254AA004C0E8B /* Configuration+Defaults.swift in Sources */,
03E3E7C22ADE318800812C84 /* EZQueryMenuTextView.m in Sources */,
03B0231829231FA6001C7E63 /* SnipWindowController.m in Sources */,
03542A342936F70F00C34C33 /* EZLanguageManager.m in Sources */,
@@ -2821,6 +2828,7 @@
6220AD5B2A82812300BBFB52 /* EZBingService.m in Sources */,
039F5508294B6E29004AB940 /* EZAboutViewController.m in Sources */,
03D8A6592A42A1A300D9A968 /* EZAppModel.m in Sources */,
+ DC46DF802B4417B900DEAE3E /* Configuration.swift in Sources */,
036E7D7B293F4FC8002675DF /* EZOpenLinkButton.m in Sources */,
EAED41EC2B54AA920005FE0A /* ServiceConfigurationSection.swift in Sources */,
276742092B3DC230002A2C75 /* AboutTab.swift in Sources */,
diff --git a/Easydict/App/AppDelegate+EZURLScheme.m b/Easydict/App/AppDelegate+EZURLScheme.m
index eef2674c5..f3ce651bd 100644
--- a/Easydict/App/AppDelegate+EZURLScheme.m
+++ b/Easydict/App/AppDelegate+EZURLScheme.m
@@ -10,7 +10,7 @@
#import
#import "EZWindowManager.h"
#import "EZSchemeParser.h"
-#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
@implementation AppDelegate (EZURLScheme)
@@ -70,8 +70,8 @@ - (void)registerRouters {
- (void)showFloatingWindowAndAutoQueryText:(NSString *)text {
EZWindowManager *windowManager = [EZWindowManager shared];
- EZWindowType windowType = EZConfiguration.shared.shortcutSelectTranslateWindowType;
-
+ EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType;
+
[windowManager showFloatingWindowType:windowType
queryText:text.trim
autoQuery:YES
diff --git a/Easydict/App/AppDelegate.m b/Easydict/App/AppDelegate.m
index 8aa9e5f27..6e6efd49c 100644
--- a/Easydict/App/AppDelegate.m
+++ b/Easydict/App/AppDelegate.m
@@ -12,7 +12,6 @@
#import "MMCrash.h"
#import "EZWindowManager.h"
#import "EZLanguageManager.h"
-#import "EZConfiguration.h"
#import "EZLog.h"
#import "EZSchemeParser.h"
#import "AppDelegate+EZURLScheme.h"
@@ -31,6 +30,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Capturing crash logs must be placed first.
[MMCrash registerHandler];
+
[EZLog setupCrashLogService];
[EZLog logAppInfo];
diff --git a/Easydict/App/Easydict-Bridging-Header.h b/Easydict/App/Easydict-Bridging-Header.h
index 06670f99e..7bcde5af5 100644
--- a/Easydict/App/Easydict-Bridging-Header.h
+++ b/Easydict/App/Easydict-Bridging-Header.h
@@ -20,12 +20,15 @@
#import "entry.h"
#import "AppDelegate.h"
-#import "EZConfiguration.h"
+
#import "EZAppModel.h"
#import "EZLocalStorage.h"
#import "NSString+EZConvenience.h"
#import "EZWindowManager.h"
#import "NSViewController+EZWindow.h"
-
+#import "EZLog.h"
+#import "EZLanguageManager.h"
+#import "DarkModeManager.h"
+#import "EZScriptExecutor.h"
#import "EZOpenAIService.h"
diff --git a/Easydict/Feature/Configuration/Configuration+UserData.swift b/Easydict/Feature/Configuration/Configuration+UserData.swift
new file mode 100644
index 000000000..0b6784aac
--- /dev/null
+++ b/Easydict/Feature/Configuration/Configuration+UserData.swift
@@ -0,0 +1,63 @@
+//
+// Configuration+UserData.swift
+// Easydict
+//
+// Created by ljk on 2024/1/17.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Foundation
+
+extension Configuration {
+ var userDefaultsData: [String: Any] {
+ let userDefaults = UserDefaults.standard
+
+ var userConfigDict = [String: Any]()
+ if let bundleIdentifier = Bundle.main.bundleIdentifier, let appUserDefaultsData = userDefaults.persistentDomain(forName: bundleIdentifier) {
+ for (key, value) in appUserDefaultsData {
+ if !key.hasPrefix("MASPreferences"), !(value is Data) {
+ userConfigDict[key] = value
+ }
+ }
+ }
+
+ return userConfigDict
+ }
+
+ func saveUserDefaultsDataToDownloadFolder() {
+ writeDictToDownloadFolder(userDefaultsData)
+ }
+
+ func resetUserDefaultsData() {
+ guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return }
+ UserDefaults.standard.removePersistentDomain(forName: bundleIdentifier)
+ }
+
+ func writeDictToDownloadFolder(_ dict: [String: Any]) {
+ let downloadPath = downloadPath
+ let name = ProcessInfo.processInfo.processName
+ let date = currentDate
+ let fileName = "\(name)_\(date).plist"
+ let plistPath = (downloadPath as NSString?)?.appendingPathComponent(fileName)
+ guard let path = plistPath else { return }
+
+ let plistData = try? PropertyListSerialization.data(fromPropertyList: dict, format: .binary, options: 0)
+ try? plistData?.write(to: URL(fileURLWithPath: path))
+ }
+
+ var downloadPath: String? {
+ NSSearchPathForDirectoriesInDomains(.downloadsDirectory, .userDomainMask, true).first
+ }
+
+ var currentDate: String {
+ let currentDate = Date()
+ let formatter = DateFormatter()
+ formatter.dateStyle = .medium
+ formatter.timeStyle = .medium
+
+ let formattedDate = formatter.string(from: currentDate)
+ print("Formatted Date: ", formattedDate)
+
+ return formattedDate
+ }
+}
diff --git a/Easydict/Feature/Configuration/Configuration.swift b/Easydict/Feature/Configuration/Configuration.swift
new file mode 100644
index 000000000..f888889c2
--- /dev/null
+++ b/Easydict/Feature/Configuration/Configuration.swift
@@ -0,0 +1,701 @@
+//
+// Configuration.swift
+// Easydict
+//
+// Created by ljk on 2024/1/2.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Combine
+import Defaults
+import Foundation
+
+@objc enum LanguageDetectOptimize: Int {
+ case none = 0
+ case baidu = 1
+ case google = 2
+}
+
+let kEnableBetaNewAppKey = "EZConfiguration_kEnableBetaNewAppKey"
+let kHideMenuBarIconKey = "EZConfiguration_kHideMenuBarIconKey"
+
+@objcMembers class Configuration: NSObject {
+ private(set) static var shared = Configuration()
+
+ override private init() {
+ super.init()
+ DispatchQueue.main.async { [weak self] in
+ guard let self else { return }
+ observeKeys()
+ }
+ }
+
+ var appDelegate = NSApp.delegate as? AppDelegate
+
+ var updater: SPUUpdater? {
+ appDelegate?.updaterController.updater
+ }
+
+ @DefaultsWrapper(.firstLanguage)
+ var firstLanguage: Language
+
+ @DefaultsWrapper(.secondLanguage)
+ var secondLanguage: Language
+
+ @DefaultsWrapper(.queryFromLanguage)
+ var fromLanguage: Language
+
+ @DefaultsWrapper(.queryToLanguage)
+ var toLanguage: Language
+
+ @DefaultsWrapper(.autoSelectText)
+ var autoSelectText: Bool
+
+ @DefaultsWrapper(.forceAutoGetSelectedText)
+ var forceAutoGetSelectedText: Bool
+
+ @DefaultsWrapper(.disableEmptyCopyBeep)
+ var disableEmptyCopyBeep: Bool // Some apps will beep when empty copy.
+
+ @DefaultsWrapper(.clickQuery)
+ var clickQuery: Bool
+
+ @DefaultsWrapper(.launchAtStartup)
+ var launchAtStartup: Bool
+
+ var automaticallyChecksForUpdates: Bool {
+ get {
+ updater?.automaticallyChecksForUpdates ?? false
+ }
+ set {
+ updater?.automaticallyChecksForUpdates = newValue
+ logSettings(["automatically_checks_for_updates": newValue])
+ }
+ }
+
+ @DefaultsWrapper(.hideMainWindow)
+ var hideMainWindow: Bool
+
+ @DefaultsWrapper(.autoQueryOCRText)
+ var autoQueryOCRText: Bool
+
+ @DefaultsWrapper(.autoQuerySelectedText)
+ var autoQuerySelectedText: Bool
+
+ @DefaultsWrapper(.autoQueryPastedText)
+ var autoQueryPastedText: Bool
+
+ @DefaultsWrapper(.autoPlayAudio)
+ var autoPlayAudio: Bool
+
+ @DefaultsWrapper(.autoCopySelectedText)
+ var autoCopySelectedText: Bool
+
+ @DefaultsWrapper(.autoCopyOCRText)
+ var autoCopyOCRText: Bool
+
+ @DefaultsWrapper(.autoCopyFirstTranslatedText)
+ var autoCopyFirstTranslatedText: Bool
+
+ @DefaultsWrapper(.languageDetectOptimize)
+ var languageDetectOptimize: LanguageDetectOptimize
+
+ var defaultTTSServiceType: ServiceType {
+ get {
+ ServiceType(rawValue: Defaults[.defaultTTSServiceType].rawValue)
+ }
+ set {
+ Defaults[.defaultTTSServiceType] = TTSServiceType(rawValue: newValue.rawValue) ?? .youdao
+ }
+ }
+
+ @DefaultsWrapper(.showGoogleQuickLink)
+ var showGoogleQuickLink: Bool
+
+ @DefaultsWrapper(.showEudicQuickLink)
+ var showEudicQuickLink: Bool
+
+ @DefaultsWrapper(.showAppleDictionaryQuickLink)
+ var showAppleDictionaryQuickLink: Bool
+
+ @DefaultsWrapper(.hideMenuBarIcon)
+ var hideMenuBarIcon: Bool
+
+ @DefaultsWrapper(.enableBetaNewApp)
+ var enableBetaNewApp: Bool
+
+ @DefaultsWrapper(.fixedWindowPosition)
+ var fixedWindowPosition: EZShowWindowPosition
+
+ @DefaultsWrapper(.mouseSelectTranslateWindowType)
+ var mouseSelectTranslateWindowType: EZWindowType
+
+ @DefaultsWrapper(.shortcutSelectTranslateWindowType)
+ var shortcutSelectTranslateWindowType: EZWindowType
+
+ @DefaultsWrapper(.adjustPopButtonOrigin)
+ var adjustPopButtomOrigin: Bool
+
+ @DefaultsWrapper(.allowCrashLog)
+ var allowCrashLog: Bool
+
+ @DefaultsWrapper(.allowAnalytics)
+ var allowAnalytics: Bool
+
+ @DefaultsWrapper(.clearInput)
+ var clearInput: Bool
+
+ var disabledAutoSelect: Bool = false
+
+ var isRecordingSelectTextShortcutKey: Bool = false
+
+ let fontSizes: [CGFloat] = [1, 1.1, 1.2, 1.3, 1.4]
+
+ var fontSizeRatio: CGFloat {
+ fontSizes[Int(fontSizeIndex)]
+ }
+
+ @DefaultsWrapper(.fontSizeOptionIndex)
+ var fontSizeIndex: UInt
+
+ @DefaultsWrapper(.appearanceType)
+ var appearance: AppearenceType
+
+ @DefaultsWrapper(.enableBetaFeature)
+ private(set) var beta: Bool
+
+ static func destroySharedInstance() {
+ shared = Configuration()
+ }
+
+ private func observeKeys() {
+ cancellables.append(
+ Defaults.publisher(.firstLanguage)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetFirstLanguage()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.secondLanguage)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetSecondLanguage()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoSelectText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoSelectText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.forceAutoGetSelectedText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetForceAutoGetSelectedText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.disableEmptyCopyBeep)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetDisableEmptyCopyBeep()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.clickQuery)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetClickQuery()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.launchAtStartup)
+ .removeDuplicates()
+ .sink { [weak self] change in
+ self?.didSetLaunchAtStartup(change.oldValue, new: change.newValue)
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.hideMainWindow)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetHideMainWindow()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoQueryOCRText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoQueryOCRText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoQuerySelectedText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoQuerySelectedText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoQueryPastedText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoQueryPastedText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoPlayAudio)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoPlayAudio()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoCopySelectedText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoCopySelectedText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoCopyOCRText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoCopyOCRText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.autoCopyFirstTranslatedText)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAutoCopyFirstTranslatedText()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.languageDetectOptimize)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetLanguageDetectOptimize()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.defaultTTSServiceType)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetDefaultTTSServiceType()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.showGoogleQuickLink)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetShowGoogleQuickLink()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.showEudicQuickLink)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetShowEudicQuickLink()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.showAppleDictionaryQuickLink)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetShowAppleDictionaryQuickLink()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.hideMenuBarIcon)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetHideMenuBarIcon()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.enableBetaNewApp)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetEnableBetaNewApp()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.fixedWindowPosition)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetFixedWindowPosition()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.mouseSelectTranslateWindowType)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetMouseSelectTranslateWindowType()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.shortcutSelectTranslateWindowType)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetShortcutSelectTranslateWindowType()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.adjustPopButtonOrigin)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAdjustPopButtomOrigin()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.allowCrashLog)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAllowCrashLog()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.allowAnalytics)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetAllowAnalytics()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.clearInput)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetClearInput()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.fontSizeOptionIndex)
+ .removeDuplicates()
+ .sink { [weak self] _ in
+ self?.didSetFontSizeIndex()
+ }
+ )
+
+ cancellables.append(
+ Defaults.publisher(.appearanceType)
+ .removeDuplicates()
+ .sink { [weak self] change in
+ let newValue = change.newValue
+
+ self?.didSetAppearance(newValue)
+ }
+ )
+ }
+
+ var cancellables: [AnyCancellable] = []
+
+ func enableBetaFeaturesIfNeeded() {
+ guard beta else { return }
+ }
+}
+
+// MARK: setter
+
+private extension Configuration {
+ func didSetFirstLanguage() {
+ logSettings(["first_language": firstLanguage])
+ }
+
+ func didSetSecondLanguage() {
+ logSettings(["second_language": secondLanguage])
+ }
+
+ func didSetAutoSelectText() {
+ logSettings(["auto_select_sext": autoSelectText])
+ }
+
+ func didSetForceAutoGetSelectedText() {
+ logSettings(["force_get_selected_text": forceAutoGetSelectedText])
+ }
+
+ func didSetDisableEmptyCopyBeep() {
+ logSettings(["disableEmptyCopyBeep": disableEmptyCopyBeep])
+ }
+
+ func didSetClickQuery() {
+ EZWindowManager.shared().updatePopButtonQueryAction()
+
+ logSettings(["click_query": clickQuery])
+ }
+
+ func didSetLaunchAtStartup(_ old: Bool, new: Bool) {
+ if new != old {
+ updateLoginItemWithLaunchAtStartup(new)
+ }
+
+ logSettings(["launch_at_startup": new])
+ }
+
+ func didSetAutomaticallyChecksForUpdates() {
+ logSettings(["automatically_checks_for_updates": automaticallyChecksForUpdates])
+ }
+
+ func didSetHideMainWindow() {
+ let windowManger = EZWindowManager.shared()
+ windowManger.updatePopButtonQueryAction()
+ if hideMainWindow {
+ windowManger.closeMainWindowIfNeeded()
+ }
+
+ logSettings(["hide_main_window": hideMainWindow])
+ }
+
+ func didSetAutoQueryOCRText() {
+ logSettings(["auto_query_ocr_text": autoQueryOCRText])
+ }
+
+ func didSetAutoQuerySelectedText() {
+ logSettings(["auto_query_selected_text": autoQuerySelectedText])
+ }
+
+ func didSetAutoQueryPastedText() {
+ logSettings(["auto_query_pasted_text": autoQueryPastedText])
+ }
+
+ func didSetAutoPlayAudio() {
+ logSettings(["auto_play_word_audio": autoPlayAudio])
+ }
+
+ func didSetAutoCopySelectedText() {
+ logSettings(["auto_copy_selected_text": autoCopySelectedText])
+ }
+
+ func didSetAutoCopyOCRText() {
+ logSettings(["auto_copy_ocr_text": autoCopyOCRText])
+ }
+
+ func didSetAutoCopyFirstTranslatedText() {
+ logSettings(["auto_copy_first_translated_text": autoCopyFirstTranslatedText])
+ }
+
+ func didSetLanguageDetectOptimize() {
+ logSettings(["detect_optimize": languageDetectOptimize])
+ }
+
+ func didSetDefaultTTSServiceType() {
+ let value = defaultTTSServiceType
+ logSettings(["tts": value])
+ }
+
+ func didSetShowGoogleQuickLink() {
+ postUpdateQuickLinkButtonNotification()
+
+ EZMenuItemManager.shared().googleItem?.isHidden = !showGoogleQuickLink
+
+ logSettings(["show_google_link": showGoogleQuickLink])
+ }
+
+ func didSetShowEudicQuickLink() {
+ postUpdateQuickLinkButtonNotification()
+
+ EZMenuItemManager.shared().eudicItem?.isHidden = !showEudicQuickLink
+
+ logSettings(["show_eudic_link": showEudicQuickLink])
+ }
+
+ func didSetShowAppleDictionaryQuickLink() {
+ postUpdateQuickLinkButtonNotification()
+
+ EZMenuItemManager.shared().appleDictionaryItem?.isHidden = !showAppleDictionaryQuickLink
+
+ logSettings(["show_apple_dictionary_link": showAppleDictionaryQuickLink])
+ }
+
+ func didSetHideMenuBarIcon() {
+ if !NewAppManager.shared.enable {
+ hideMenuBarIcon(hidden: hideMenuBarIcon)
+ }
+
+ logSettings(["hide_menu_bar_icon": hideMenuBarIcon])
+ }
+
+ func didSetEnableBetaNewApp() {
+ logSettings(["enable_beta_new_app": enableBetaNewApp])
+ }
+
+ func didSetFixedWindowPosition() {
+ logSettings(["show_fixed_window_position": fixedWindowPosition])
+ }
+
+ func didSetMouseSelectTranslateWindowType() {
+ logSettings(["show_mouse_window_type": mouseSelectTranslateWindowType])
+ }
+
+ func didSetShortcutSelectTranslateWindowType() {
+ logSettings(["show_shortcut_window_type": shortcutSelectTranslateWindowType])
+ }
+
+ func didSetAdjustPopButtomOrigin() {
+ logSettings(["adjust_pop_buttom_origin": adjustPopButtomOrigin])
+ }
+
+ func didSetAllowCrashLog() {
+ EZLog.setCrashEnabled(allowCrashLog)
+ logSettings(["allow_crash_log": allowCrashLog])
+ }
+
+ func didSetAllowAnalytics() {
+ logSettings(["allow_analytics": allowAnalytics])
+ }
+
+ func didSetClearInput() {
+ logSettings(["clear_input": clearInput])
+ }
+
+ func didSetFontSizeIndex() {
+ NotificationCenter.default.post(name: .init(ChangeFontSizeView.changeFontSizeNotificationName), object: nil)
+ }
+
+ func didSetAppearance(_ appearance: AppearenceType) {
+ DarkModeManager.sharedManager().updateDarkMode(appearance.rawValue)
+ }
+}
+
+// MARK: Window Frame
+
+extension Configuration {
+ func windowFrameWithType(_ windowType: EZWindowType) -> CGRect {
+ Defaults[.windorFrame(for: windowType)]
+ }
+
+ func setWindowFrame(_ frame: CGRect, windowType: EZWindowType) {
+ Defaults[.windorFrame(for: windowType)] = frame
+ }
+}
+
+// MARK: Intelligent Query Text Type of Service
+
+extension Configuration {
+ func setIntelligentQueryTextType(_ queryTextType: EZQueryTextType, serviceType: ServiceType) {
+ Defaults[.intelligentQueryTextType(for: serviceType)] = queryTextType
+ }
+
+ func intelligentQueryTextTypeForServiceType(_ serviceType: ServiceType) -> EZQueryTextType {
+ Defaults[.intelligentQueryTextType(for: serviceType)]
+ }
+}
+
+// MARK: Intelligent Query Text Type of Service
+
+extension Configuration {
+ func setQueryTextType(_ queryTextType: EZQueryTextType, serviceType: ServiceType) {
+ Defaults[.queryTextType(for: serviceType)] = queryTextType
+ }
+
+ func queryTextTypeForServiceType(_ serviceType: ServiceType) -> EZQueryTextType {
+ Defaults[.queryTextType(for: serviceType)]
+ }
+}
+
+// MARK: Intelligent Query Mode
+
+extension Configuration {
+ func setIntelligentQueryMode(_ enabled: Bool, windowType: EZWindowType) {
+ let key = EZConstKey.constkey("IntelligentQueryMode", windowType: windowType)
+ let stringValue = "\(enabled)"
+ UserDefaults.standard.set(stringValue, forKey: key)
+
+ let parameters = [
+ "enabled": enabled,
+ "window_type": windowType.rawValue,
+ ] as [String: Any]
+
+ EZLog.logEvent(withName: "intelligent_query_mode", parameters: parameters)
+ }
+
+ func intelligentQueryModeForWindowType(_ windowType: EZWindowType) -> Bool {
+ let key = EZConstKey.constkey("IntelligentQueryMode", windowType: windowType)
+ let defaultValue = "0"
+ // Turn on intelligent query mode by default in mini window.
+ if windowType == .mini {
+ return true
+ }
+ return UserDefaults.standard.string(forKey: key) ?? defaultValue == "1"
+ }
+}
+
+private extension Configuration {
+ func postUpdateQuickLinkButtonNotification() {
+ let notification = Notification(name: .init("EZQuickLinkButtonUpdateNotification"), object: nil)
+ NotificationCenter.default.post(notification)
+ }
+
+ func hideMenuBarIcon(hidden: Bool) {
+ if hidden {
+ EZMenuItemManager.shared().remove()
+ } else {
+ EZMenuItemManager.shared().setup()
+ }
+ }
+
+ func updateLoginItemWithLaunchAtStartup(_ launchAtStartup: Bool) {
+ let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable") as? String
+ let appBundlePath = Bundle.main.bundlePath
+
+ let script = """
+ tell application "System Events" to get the name of every login item
+ tell application "System Events"
+ set loginItems to every login item
+ repeat with aLoginItem in loginItems
+ if (aLoginItem's name is "\(appName ?? "")") then
+ delete aLoginItem
+ end if
+ end repeat
+ if \(launchAtStartup) then
+ make login item at end with properties {path:"\(appBundlePath)", hidden:false}
+ end if
+ end tell
+ """
+
+ let exeCommand = EZScriptExecutor()
+ exeCommand.runAppleScript(script) { result, error in
+ if let error {
+ MMLogInfo("launchAtStartup error: error: \(error)")
+ } else {
+ print("launchAtStartup result:", result)
+ }
+ }
+ }
+
+ func logSettings(_ parameters: [String: Any]) {
+ EZLog.logEvent(withName: "settings", parameters: parameters)
+ }
+}
diff --git a/Easydict/Feature/Configuration/EZConfiguration.m b/Easydict/Feature/Configuration/EZConfiguration.m
index b15c3184d..e1b40f69b 100644
--- a/Easydict/Feature/Configuration/EZConfiguration.m
+++ b/Easydict/Feature/Configuration/EZConfiguration.m
@@ -455,7 +455,7 @@ - (void)setAppearance:(EZAppearenceType)appearance {
[NSUserDefaults mm_write:@(appearance) forKey:kApperanceKey];
- [[DarkModeManager manager] updateDarkMode];
+ [[DarkModeManager manager] updateDarkMode:appearance];
}
#pragma mark - Window Frame
diff --git a/Easydict/Feature/DarkMode/DarkModeManager.h b/Easydict/Feature/DarkMode/DarkModeManager.h
index 722bee8a3..446803e26 100644
--- a/Easydict/Feature/DarkMode/DarkModeManager.h
+++ b/Easydict/Feature/DarkMode/DarkModeManager.h
@@ -10,15 +10,14 @@
NS_ASSUME_NONNULL_BEGIN
-
@interface DarkModeManager : NSObject
@property (nonatomic, assign, readonly) BOOL systemDarkMode;
-+ (instancetype)manager;
++ (instancetype)manager NS_SWIFT_NAME(sharedManager());
- (void)excuteLight:(void (^)(void))light dark:(void (^)(void))dark;
-- (void)updateDarkMode;
+- (void)updateDarkMode:(NSInteger)apperance;
@end
diff --git a/Easydict/Feature/DarkMode/DarkModeManager.m b/Easydict/Feature/DarkMode/DarkModeManager.m
index af749d661..7dcdf5e59 100644
--- a/Easydict/Feature/DarkMode/DarkModeManager.m
+++ b/Easydict/Feature/DarkMode/DarkModeManager.m
@@ -42,14 +42,18 @@ - (void)excuteLight:(void (^)(void))light dark:(void (^)(void))dark {
- (void)monitor {
NSString *const darkModeNotificationName = @"AppleInterfaceThemeChangedNotification";
- [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDarkMode) name:darkModeNotificationName object:nil];
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(themeDidChange) name:darkModeNotificationName object:nil];
}
-- (void)updateDarkMode {
+- (void)themeDidChange {
+ [self updateDarkMode:Configuration.shared.appearance];
+}
+
+- (void)updateDarkMode:(NSInteger)apperance {
BOOL isDarkMode = [self isDarkMode];
NSLog(@"%@", isDarkMode ? @"深色模式" : @"浅色模式");
- AppearenceType type = (AppearenceType)EZConfiguration.shared.appearance;
+ AppearenceType type = apperance;
switch (type) {
case AppearenceTypeDark:
self.systemDarkMode = true;
diff --git a/Easydict/Feature/EventMonitor/EZEventMonitor.m b/Easydict/Feature/EventMonitor/EZEventMonitor.m
index 2578efd37..f3acfe5ab 100644
--- a/Easydict/Feature/EventMonitor/EZEventMonitor.m
+++ b/Easydict/Feature/EventMonitor/EZEventMonitor.m
@@ -17,6 +17,7 @@
#import "EZLocalStorage.h"
#import "EZAppleScriptManager.h"
#import "EZSystemUtility.h"
+#import "Easydict-Swift.h"
static CGFloat const kDismissPopButtonDelayTime = 0.1;
static NSTimeInterval const kDelayGetSelectedTextTime = 0.1;
@@ -300,7 +301,7 @@ - (void)getSelectedText:(BOOL)checkTextFrame completion:(void (^)(NSString *_Nul
self.selectTextType = EZSelectTextTypeAccessibility;
// Monitor CGEventTap must be required after using Accessibility successfully.
- if (EZConfiguration.shared.autoSelectText) {
+ if (Configuration.shared.autoSelectText) {
[self monitorCGEventTap];
}
@@ -407,7 +408,7 @@ - (void)autoGetSelectedText:(BOOL)checkTextFrame {
}
- (BOOL)enabledAutoSelectText {
- EZConfiguration *config = [EZConfiguration shared];
+ Configuration *config = [Configuration shared];
BOOL enabled = config.autoSelectText && !config.disabledAutoSelect;
if (!enabled) {
NSLog(@"disabled autoSelectText");
@@ -437,7 +438,7 @@ - (void)getSelectedTextBySimulatedKey:(void (^)(NSString *_Nullable))completion
// If playing audio, we do not silence system volume.
[EZAudioUtils isPlayingAudio:^(BOOL isPlaying) {
- BOOL shouldTurnOffSoundTemporarily = EZConfiguration.shared.disableEmptyCopyBeep && !isPlaying;
+ BOOL shouldTurnOffSoundTemporarily = Configuration.shared.disableEmptyCopyBeep && !isPlaying;
// Set volume to 0 to avoid system alert.
if (shouldTurnOffSoundTemporarily) {
@@ -596,7 +597,7 @@ - (BOOL)isAccessibilityEnabled {
/// Check if should use simulation key to get selected text.
- (BOOL)shouldUseSimulatedKey:(NSString *)text error:(AXError)error {
BOOL isAutoSelectQuery = self.actionType == EZActionTypeAutoSelectQuery;
- BOOL allowedForceAutoGetSelectedText = [EZConfiguration.shared forceAutoGetSelectedText];
+ BOOL allowedForceAutoGetSelectedText = [Configuration.shared forceAutoGetSelectedText];
NSString *easydictBundleID = [[NSBundle mainBundle] bundleIdentifier];
@@ -610,7 +611,7 @@ - (BOOL)shouldUseSimulatedKey:(NSString *)text error:(AXError)error {
FIX: https://github.com/tisfeng/Easydict/issues/192#issuecomment-1797878909
*/
- if (isInEasydict && EZConfiguration.shared.isRecordingSelectTextShortcutKey) {
+ if (isInEasydict && Configuration.shared.isRecordingSelectTextShortcutKey) {
return NO;
}
diff --git a/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m b/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m
index 477b392e3..86a5c8e56 100644
--- a/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m
+++ b/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m
@@ -16,6 +16,7 @@
#import "EZConfiguration.h"
#import "NSImage+EZSymbolmage.h"
#import "NSImage+EZResize.h"
+#import "Easydict-Swift.h"
static CGFloat const kMargin = 20;
static CGFloat const kRowHeight = 45;
@@ -254,7 +255,7 @@ - (void)selectApp {
[openPanel setAllowedContentTypes:allowedTypes];
// ???: Since [auto select] will cause lag when dragging select apps, I don't know why 😰
- EZConfiguration.shared.disabledAutoSelect = YES;
+ Configuration.shared.disabledAutoSelect = YES;
NSModalResponse result = [openPanel runModal];
if (result == NSModalResponseOK) {
@@ -267,7 +268,7 @@ - (void)selectApp {
[self.tableView reloadData];
}
- EZConfiguration.shared.disabledAutoSelect = NO;
+ Configuration.shared.disabledAutoSelect = NO;
}
- (NSArray *)appModelsFromBundleIDDict:(NSDictionary *)appBundleIDDict {
diff --git a/Easydict/Feature/PerferenceWindow/EZAboutViewController.m b/Easydict/Feature/PerferenceWindow/EZAboutViewController.m
index 67fc4d277..9196c696a 100644
--- a/Easydict/Feature/PerferenceWindow/EZAboutViewController.m
+++ b/Easydict/Feature/PerferenceWindow/EZAboutViewController.m
@@ -10,6 +10,7 @@
#import "EZBlueTextButton.h"
#import "EZConfiguration.h"
#import "EZMenuItemManager.h"
+#import "Easydict-Swift.h"
@interface EZAboutViewController ()
@@ -35,7 +36,7 @@ - (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
- self.autoCheckUpdateButton.mm_isOn = EZConfiguration.shared.automaticallyChecksForUpdates;
+ self.autoCheckUpdateButton.mm_isOn = Configuration.shared.automaticallyChecksForUpdates;
[self updateViewSize];
@@ -177,7 +178,7 @@ - (void)updateViewConstraints {
#pragma mark - Actions
- (void)autoCheckUpdateButtonClicked:(NSButton *)sender {
- EZConfiguration.shared.automaticallyChecksForUpdates = sender.mm_isOn;
+ Configuration.shared.automaticallyChecksForUpdates = sender.mm_isOn;
}
- (void)updateLatestVersion {
diff --git a/Easydict/Feature/PerferenceWindow/EZPrivacyViewController.m b/Easydict/Feature/PerferenceWindow/EZPrivacyViewController.m
index e5fc7ccd3..fb00a64e3 100644
--- a/Easydict/Feature/PerferenceWindow/EZPrivacyViewController.m
+++ b/Easydict/Feature/PerferenceWindow/EZPrivacyViewController.m
@@ -11,6 +11,7 @@
#import "EZConfiguration.h"
#import "NSViewController+EZWindow.h"
#import "NSImage+EZSymbolmage.h"
+#import "Easydict-Swift.h"
@interface EZPrivacyViewController ()
@@ -61,7 +62,7 @@ - (void)setupUI {
action:@selector(analyticsButtonClicked:)];
[self.contentView addSubview:self.analyticsButton];
- EZConfiguration *configuration = [EZConfiguration shared];
+ Configuration *configuration = [Configuration shared];
self.crashLogButton.mm_isOn = configuration.allowCrashLog;
self.analyticsButton.mm_isOn = configuration.allowAnalytics;
}
@@ -120,16 +121,16 @@ - (void)crashLogButtonClicked:(NSButton *)sender {
} else {
sender.mm_isOn = YES;
}
- EZConfiguration.shared.allowCrashLog = sender.mm_isOn;
+ Configuration.shared.allowCrashLog = sender.mm_isOn;
}];
} else {
- EZConfiguration.shared.allowCrashLog = YES;
+ Configuration.shared.allowCrashLog = YES;
}
}
- (void)analyticsButtonClicked:(NSButton *)sender {
- EZConfiguration.shared.allowAnalytics = sender.mm_isOn;
+ Configuration.shared.allowAnalytics = sender.mm_isOn;
}
#pragma mark - MASPreferencesViewController
diff --git a/Easydict/Feature/PerferenceWindow/EZSettingViewController.m b/Easydict/Feature/PerferenceWindow/EZSettingViewController.m
index 528ab13e0..38d2aec42 100644
--- a/Easydict/Feature/PerferenceWindow/EZSettingViewController.m
+++ b/Easydict/Feature/PerferenceWindow/EZSettingViewController.m
@@ -17,7 +17,7 @@
@interface EZSettingViewController ()
-@property (nonatomic, strong) EZConfiguration *config;
+@property (nonatomic, strong) Configuration *config;
@property (nonatomic, strong) NSTextField *selectLabel;
@property (nonatomic, strong) NSTextField *inputLabel;
@@ -153,7 +153,7 @@ - (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
- self.config = [EZConfiguration shared];
+ self.config = [Configuration shared];
[self setupUI];
@@ -165,7 +165,7 @@ - (void)viewDidLoad {
// Observe selectionShortcutView.recording status.
[self.KVOController observe:self.selectionShortcutView keyPath:@"recording" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew block:^(EZSettingViewController *settingVC, MASShortcutView *selectionShortcutView, NSDictionary *_Nonnull change) {
- EZConfiguration.shared.isRecordingSelectTextShortcutKey = [change[NSKeyValueChangeNewKey] boolValue];
+ Configuration.shared.isRecordingSelectTextShortcutKey = [change[NSKeyValueChangeNewKey] boolValue];
}];
}
diff --git a/Easydict/Feature/Service/Apple/EZAppleService.m b/Easydict/Feature/Service/Apple/EZAppleService.m
index e2a4b567c..be76679f2 100644
--- a/Easydict/Feature/Service/Apple/EZAppleService.m
+++ b/Easydict/Feature/Service/Apple/EZAppleService.m
@@ -16,6 +16,7 @@
#import
#import "NSString+EZUtils.h"
#import "EZAppleDictionary.h"
+#import "Easydict-Swift.h"
static NSString *const kLineBreakText = @"\n";
static NSString *const kParagraphBreakText = @"\n\n";
@@ -607,7 +608,7 @@ - (NLLanguage)detectUnkownText:(NSString *)text {
NLLanguage language = NLLanguageEnglish;
// 729
if ([text isNumbers]) {
- EZLanguage firstLanguage = EZConfiguration.shared.firstLanguage;
+ EZLanguage firstLanguage = Configuration.shared.firstLanguage;
language = [self appleLanguageFromLanguageEnum:firstLanguage];
}
diff --git a/Easydict/Feature/Service/Apple/EZScriptExecutor.h b/Easydict/Feature/Service/Apple/EZScriptExecutor.h
index a0b6259df..8ad161b9f 100644
--- a/Easydict/Feature/Service/Apple/EZScriptExecutor.h
+++ b/Easydict/Feature/Service/Apple/EZScriptExecutor.h
@@ -17,17 +17,17 @@ typedef void(^AppleScriptCompletionHandler)( NSString *_Nullable result, EZError
/// Run translate shortcut with parameters.
- (NSTask *)runTranslateShortcut:(NSDictionary *)parameters
- completionHandler:(void (^)(NSString *result, EZError *error))completionHandler;
+ completionHandler:(void (^)(NSString *result, EZError * _Nullable error))completionHandler;
/// Run shortcut with parameters.
- (NSTask *)runShortcut:(NSString *)shortcutName
parameters:(NSDictionary *)parameters
- completionHandler:(void (^)(NSString *result, EZError *error))completionHandler;
+ completionHandler:(void (^)(NSString *result, EZError * _Nullable error))completionHandler;
/// Use NSTask to run AppleScript.
-- (NSTask *)runAppleScriptWithTask:(NSString *)script completionHandler:(void (^)(NSString *result, EZError *error))completionHandler;
+- (NSTask *)runAppleScriptWithTask:(NSString *)script completionHandler:(void (^)(NSString *result, EZError * _Nullable error))completionHandler;
-- (void)runAppleScript:(NSString *)script completionHandler:(void (^)(NSString *result, EZError *error))completionHandler;
+- (void)runAppleScript:(NSString *)script completionHandler:(void (^)(NSString *result, EZError * _Nullable error))completionHandler;
@end
diff --git a/Easydict/Feature/Service/Apple/EZScriptExecutor.m b/Easydict/Feature/Service/Apple/EZScriptExecutor.m
index 51ff38453..702f0fb08 100644
--- a/Easydict/Feature/Service/Apple/EZScriptExecutor.m
+++ b/Easydict/Feature/Service/Apple/EZScriptExecutor.m
@@ -94,7 +94,7 @@ - (NSTask *)runAppleScriptWithTask:(NSString *)script completionHandler:(void (^
/// Use NSAppleScript to run AppleScript, faster than NSTask.
/// !!!: Note that this method may fail due to execution permissions, it will not automatically apply for permissions when I test.
-- (void)runAppleScript:(NSString *)script completionHandler:(void (^)(NSString *result, EZError *error))completionHandler {
+- (void)runAppleScript:(NSString *)script completionHandler:(void (^)(NSString *result, EZError * _Nullable error))completionHandler {
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:script];
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
diff --git a/Easydict/Feature/Service/AudioPlayer/EZAudioPlayer.m b/Easydict/Feature/Service/AudioPlayer/EZAudioPlayer.m
index 7ef68a030..ce07b3d7a 100644
--- a/Easydict/Feature/Service/AudioPlayer/EZAudioPlayer.m
+++ b/Easydict/Feature/Service/AudioPlayer/EZAudioPlayer.m
@@ -17,6 +17,7 @@
#import "EZServiceTypes.h"
#import "EZConfiguration.h"
#import
+#import "Easydict-Swift.h"
static NSString *const kFileExtendedAttributes = @"NSFileExtendedAttributes";
@@ -128,7 +129,7 @@ - (void)setIsPlaying:(BOOL)playing {
// Note that user may change it when using, so we need to read it every time.
- (EZQueryService *)defaultTTSService {
- EZServiceType defaultTTSServiceType = EZConfiguration.shared.defaultTTSServiceType;
+ EZServiceType defaultTTSServiceType = Configuration.shared.defaultTTSServiceType;
if (![_defaultTTSService.serviceType isEqualToString:defaultTTSServiceType]) {
EZQueryService *defaultTTSService = [EZServiceTypes.shared serviceWithType:defaultTTSServiceType];
_defaultTTSService = defaultTTSService;
diff --git a/Easydict/Feature/Service/Baidu/EZBaiduTranslate.m b/Easydict/Feature/Service/Baidu/EZBaiduTranslate.m
index 5b6137076..726add2ad 100644
--- a/Easydict/Feature/Service/Baidu/EZBaiduTranslate.m
+++ b/Easydict/Feature/Service/Baidu/EZBaiduTranslate.m
@@ -13,6 +13,7 @@
#import "EZNetworkManager.h"
#import "EZConfiguration.h"
#import "NSString+EZRegex.h"
+#import "Easydict-Swift.h"
static NSString *const kBaiduTranslateURL = @"https://fanyi.baidu.com";
@@ -124,7 +125,8 @@ - (EZServiceType)serviceType {
- (EZQueryTextType)queryTextType {
EZQueryTextType defaultType = EZQueryTextTypeDictionary | EZQueryTextTypeSentence | EZQueryTextTypeTranslation;
- EZQueryTextType type = [EZConfiguration.shared queryTextTypeForServiceType:self.serviceType];
+ EZQueryTextType type = [Configuration.shared queryTextTypeForServiceType:self.serviceType];
+
if (type == 0) {
type = defaultType;
}
@@ -132,7 +134,7 @@ - (EZQueryTextType)queryTextType {
}
- (EZQueryTextType)intelligentQueryTextType {
- EZQueryTextType type = [EZConfiguration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
+ EZQueryTextType type = [Configuration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
return type;
}
diff --git a/Easydict/Feature/Service/Bing/EZBingService.m b/Easydict/Feature/Service/Bing/EZBingService.m
index db282c86b..2b08d0ed1 100644
--- a/Easydict/Feature/Service/Bing/EZBingService.m
+++ b/Easydict/Feature/Service/Bing/EZBingService.m
@@ -12,6 +12,7 @@
#import "EZBingLookupModel.h"
#import "EZConfiguration.h"
#import "NSString+EZUtils.h"
+#import "Easydict-Swift.h"
@interface EZBingService ()
@property (nonatomic, strong) EZBingRequest *request;
@@ -32,7 +33,7 @@ - (instancetype)init {
#pragma mark - override
- (EZQueryTextType)intelligentQueryTextType {
- EZQueryTextType type = [EZConfiguration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
+ EZQueryTextType type = [Configuration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
return type;
}
diff --git a/Easydict/Feature/Service/Google/EZGoogleTranslate.m b/Easydict/Feature/Service/Google/EZGoogleTranslate.m
index 5c65d0e22..f2af0a9ae 100644
--- a/Easydict/Feature/Service/Google/EZGoogleTranslate.m
+++ b/Easydict/Feature/Service/Google/EZGoogleTranslate.m
@@ -11,6 +11,7 @@
#import
#import "NSString+EZUtils.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
static NSString *const kGoogleTranslateURL = @"https://translate.google.com";
@@ -115,7 +116,7 @@ - (EZQueryTextType)queryTextType {
}
- (EZQueryTextType)intelligentQueryTextType {
- EZQueryTextType type = [EZConfiguration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
+ EZQueryTextType type = [Configuration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
return type;
}
diff --git a/Easydict/Feature/Service/Language/EZLanguageManager.m b/Easydict/Feature/Service/Language/EZLanguageManager.m
index f2e430349..6650763d5 100644
--- a/Easydict/Feature/Service/Language/EZLanguageManager.m
+++ b/Easydict/Feature/Service/Language/EZLanguageManager.m
@@ -9,6 +9,7 @@
#import "EZLanguageManager.h"
#import "EZAppleService.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
@interface EZLanguageManager ()
@@ -170,7 +171,7 @@ - (EZLanguage)systemSecondLanguage {
}
- (EZLanguage)userFirstLanguage {
- EZLanguage firstLanguage = EZConfiguration.shared.firstLanguage;
+ EZLanguage firstLanguage = Configuration.shared.firstLanguage;
if (!firstLanguage) {
firstLanguage = [self systemPreferredTwoLanguages][0];
}
@@ -178,7 +179,7 @@ - (EZLanguage)userFirstLanguage {
}
- (EZLanguage)userSecondLanguage {
- EZLanguage secondLanguage = EZConfiguration.shared.secondLanguage;
+ EZLanguage secondLanguage = Configuration.shared.secondLanguage;
if (!secondLanguage) {
secondLanguage = [self systemPreferredTwoLanguages][1];
}
diff --git a/Easydict/Feature/Service/Model/EZDetectManager.m b/Easydict/Feature/Service/Model/EZDetectManager.m
index 40d6c9bff..a4c5575ee 100644
--- a/Easydict/Feature/Service/Model/EZDetectManager.m
+++ b/Easydict/Feature/Service/Model/EZDetectManager.m
@@ -12,6 +12,7 @@
#import "EZConfiguration.h"
#import "EZYoudaoTranslate.h"
#import "EZConfiguration+EZUserData.h"
+#import "Easydict-Swift.h"
@interface EZDetectManager ()
@@ -101,7 +102,7 @@ - (void)detectText:(NSString *)queryText completion:(void (^)(EZQueryModel *_Non
[self.appleService detectText:queryText completion:^(EZLanguage appleDetectdedLanguage, NSError *_Nullable error) {
NSMutableArray *preferredLanguages = [[EZLanguageManager.shared preferredLanguages] mutableCopy];
- EZLanguageDetectOptimize languageDetectOptimize = EZConfiguration.shared.languageDetectOptimize;
+ LanguageDetectOptimize languageDetectOptimize = Configuration.shared.languageDetectOptimize;
// Add English and Chinese to the preferred language list, in general, sysytem detect English and Chinese is relatively accurate, so we don't need to use google or baidu to detect again.
[preferredLanguages addObjectsFromArray:@[
@@ -111,7 +112,7 @@ - (void)detectText:(NSString *)queryText completion:(void (^)(EZQueryModel *_Non
]];
BOOL isPreferredLanguage = [preferredLanguages containsObject:appleDetectdedLanguage];
- if (isPreferredLanguage || languageDetectOptimize == EZLanguageDetectOptimizeNone) {
+ if (isPreferredLanguage || languageDetectOptimize == LanguageDetectOptimizeNone) {
[self handleDetectedLanguage:appleDetectdedLanguage error:error completion:completion];
return;
}
@@ -237,7 +238,7 @@ - (void)handleOCRResult:(EZOCRResult *_Nullable)ocrResult error:(NSError *_Nulla
Sometimes Apple OCR may fail, like Japanese text, but we have set Japanese as preferred language and OCR again when OCR result is empty, currently it seems work, but we do not guarantee it is always work in other languages.
*/
- if ([EZConfiguration.shared isBeta]) {
+ if (Configuration.shared.beta) {
[self.youdaoService ocr:self.queryModel completion:^(EZOCRResult *_Nullable youdaoOCRResult, NSError *_Nullable youdaoOCRError) {
if (!youdaoOCRError) {
completion(youdaoOCRResult, nil);
diff --git a/Easydict/Feature/Service/Model/EZQueryService.m b/Easydict/Feature/Service/Model/EZQueryService.m
index b7cc43e1c..a2a5f7734 100644
--- a/Easydict/Feature/Service/Model/EZQueryService.m
+++ b/Easydict/Feature/Service/Model/EZQueryService.m
@@ -12,6 +12,7 @@
#import "NSString+EZChineseText.h"
#import "NSString+EZUtils.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
#define MethodNotImplemented() \
@throw [NSException exceptionWithName:NSInternalInconsistencyException \
@@ -54,7 +55,7 @@ - (BOOL)enabledAutoQuery {
return NO;
}
- if ([EZConfiguration.shared intelligentQueryModeForWindowType:self.windowType]) {
+ if ([Configuration.shared intelligentQueryModeForWindowType:self.windowType]) {
// We usually don't want to lookup dictionary if text word > 1.
EZQueryTextType queryType = [self.queryModel.queryText queryTypeWithLanguage:self.queryModel.queryFromLanguage maxWordCount:1];
if ((queryType & self.intelligentQueryTextType) != queryType) {
diff --git a/Easydict/Feature/Service/OpenAI/EZOpenAIService+EZPromptMessages.m b/Easydict/Feature/Service/OpenAI/EZOpenAIService+EZPromptMessages.m
index 2bfa712d6..2b32e8d74 100644
--- a/Easydict/Feature/Service/OpenAI/EZOpenAIService+EZPromptMessages.m
+++ b/Easydict/Feature/Service/OpenAI/EZOpenAIService+EZPromptMessages.m
@@ -9,6 +9,7 @@
#import "EZOpenAIService+EZPromptMessages.h"
#import "EZConfiguration.h"
#import "NSString+EZUtils.h"
+#import "Easydict-Swift.h"
// You are a faithful translation assistant that can only translate text and cannot interpret it, you can only return the translated text, do not show additional descriptions and annotations.
@@ -84,7 +85,7 @@ - (NSArray *)translatioMessages:(NSString *)text from:(EZLanguage)sourceLanguage
/// Sentence messages.
- (NSArray *)sentenceMessages:(NSString *)sentence from:(EZLanguage)sourceLanguage to:(EZLanguage)targetLanguage {
- NSString *answerLanguage = EZConfiguration.shared.firstLanguage;
+ NSString *answerLanguage = Configuration.shared.firstLanguage;
self.result.to = answerLanguage;
NSString *prompt = @"";
@@ -274,7 +275,7 @@ - (NSArray *)translatioMessages:(NSString *)text from:(EZLanguage)sourceLanguage
// V5. prompt
NSString *prompt = @"";
- NSString *answerLanguage = EZConfiguration.shared.firstLanguage;
+ NSString *answerLanguage = Configuration.shared.firstLanguage;
self.result.to = answerLanguage;
NSString *pronunciation = @"Pronunciation";
diff --git a/Easydict/Feature/Service/OpenAI/EZOpenAIService.m b/Easydict/Feature/Service/OpenAI/EZOpenAIService.m
index 1123049e4..f97a0ea71 100644
--- a/Easydict/Feature/Service/OpenAI/EZOpenAIService.m
+++ b/Easydict/Feature/Service/OpenAI/EZOpenAIService.m
@@ -57,7 +57,7 @@ - (NSString *)apiKey {
// easydict://writeKeyValue?EZOpenAIAPIKey=
NSString *apiKey = [[NSUserDefaults standardUserDefaults] stringForKey:EZOpenAIAPIKey] ?: @"";
- if (apiKey.length == 0 && EZConfiguration.shared.isBeta) {
+ if (apiKey.length == 0 && Configuration.shared.beta) {
apiKey = self.defaultAPIKey;
}
return apiKey;
@@ -135,7 +135,7 @@ - (EZQueryTextType)queryTextType {
}
- (EZQueryTextType)intelligentQueryTextType {
- EZQueryTextType type = [EZConfiguration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
+ EZQueryTextType type = [Configuration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
return type;
}
diff --git a/Easydict/Feature/Service/Youdao/EZYoudaoTranslate.m b/Easydict/Feature/Service/Youdao/EZYoudaoTranslate.m
index 0df3d35b1..3c9f476b4 100644
--- a/Easydict/Feature/Service/Youdao/EZYoudaoTranslate.m
+++ b/Easydict/Feature/Service/Youdao/EZYoudaoTranslate.m
@@ -174,7 +174,7 @@ - (EZQueryTextType)queryTextType {
}
- (EZQueryTextType)intelligentQueryTextType {
- EZQueryTextType type = [EZConfiguration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
+ EZQueryTextType type = [Configuration.shared intelligentQueryTextTypeForServiceType:self.serviceType];
return type;
}
diff --git a/Easydict/Feature/StatusItem/EZMenuItemManager.m b/Easydict/Feature/StatusItem/EZMenuItemManager.m
index 1d47cb1d6..8d9f3b017 100644
--- a/Easydict/Feature/StatusItem/EZMenuItemManager.m
+++ b/Easydict/Feature/StatusItem/EZMenuItemManager.m
@@ -70,7 +70,7 @@ - (void)setup {
if (self.statusItem) {
return;
}
- if (EZConfiguration.shared.hideMenuBarIcon) {
+ if (Configuration.shared.hideMenuBarIcon) {
return;
}
@@ -273,12 +273,12 @@ - (IBAction)appleDictionaryAction:(NSMenuItem *)sender {
}
- (IBAction)increaseFontSizeAction:(NSMenuItem *)sender {
- EZConfiguration.shared.fontSizeIndex += 1;
+ Configuration.shared.fontSizeIndex += 1;
}
- (IBAction)decreaseFontSizeAction:(NSMenuItem *)sender {
- EZConfiguration.shared.fontSizeIndex -= 1;
+ Configuration.shared.fontSizeIndex -= 1;
}
diff --git a/Easydict/Feature/Utility/AppleScript/EZAppleScriptManager.m b/Easydict/Feature/Utility/AppleScript/EZAppleScriptManager.m
index a32fb0dda..6cf871203 100644
--- a/Easydict/Feature/Utility/AppleScript/EZAppleScriptManager.m
+++ b/Easydict/Feature/Utility/AppleScript/EZAppleScriptManager.m
@@ -8,6 +8,7 @@
#import "EZAppleScriptManager.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
@interface EZAppleScriptManager ()
@@ -225,7 +226,7 @@ - (void)checkApplicationSupportCopyAction:(NSString *)appBundleID completion:(vo
NSString *edit;
if (!appLanguage) {
- appLanguage = EZConfiguration.shared.firstLanguage;
+ appLanguage = Configuration.shared.firstLanguage;
}
if ([appLanguage isEqualToString:EZLanguageEnglish]) {
diff --git a/Easydict/Feature/Utility/EZLinkParser/EZSchemeParser.m b/Easydict/Feature/Utility/EZLinkParser/EZSchemeParser.m
index cd8ba1630..6a7a25442 100644
--- a/Easydict/Feature/Utility/EZLinkParser/EZSchemeParser.m
+++ b/Easydict/Feature/Utility/EZLinkParser/EZSchemeParser.m
@@ -14,6 +14,7 @@
#import "EZConfiguration+EZUserData.h"
#import "EZConfiguration.h"
#import "EZLocalStorage.h"
+#import "Easydict-Swift.h"
@implementation EZSchemeParser
@@ -110,14 +111,14 @@ - (BOOL)writeKeyValues:(NSDictionary *)keyValues {
NSString *value = keyValues[key];
handled = [self enabledReadWriteKey:key];
if (handled) {
- EZConfiguration *config = [EZConfiguration shared];
- BOOL isBeta = config.isBeta;
+ Configuration *config = [Configuration shared];
+ BOOL isBeta = config.beta;
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
// If enabling beta feature, setup beta features.
- if (!isBeta && config.isBeta) {
- [EZConfiguration.shared enableBetaFeaturesIfNeeded];
+ if (!isBeta && config.beta) {
+ [Configuration.shared enableBetaFeaturesIfNeeded];
}
}
}
@@ -154,15 +155,15 @@ - (BOOL)enabledReadWriteKey:(NSString *)key {
- (void)resetUserDefaultsData {
// easydict://resetUserDefaultsData
- [EZConfiguration.shared resetUserDefaultsData];
+ [Configuration.shared resetUserDefaultsData];
[EZLocalStorage destroySharedInstance];
- [EZConfiguration destroySharedInstance];
+ [Configuration destroySharedInstance];
}
- (void)saveUserDefaultsDataToDownloadFolder {
// easydict://saveUserDefaultsDataToDownloadFolder
- [EZConfiguration.shared saveUserDefaultsDataToDownloadFolder];
+ [Configuration.shared saveUserDefaultsDataToDownloadFolder];
}
diff --git a/Easydict/Feature/Utility/EZLog/EZLog.m b/Easydict/Feature/Utility/EZLog/EZLog.m
index 81ba2860b..c17f1e7b9 100644
--- a/Easydict/Feature/Utility/EZLog/EZLog.m
+++ b/Easydict/Feature/Utility/EZLog/EZLog.m
@@ -47,7 +47,7 @@ + (void)setCrashEnabled:(BOOL)enabled {
+ (void)logEventWithName:(NSString *)name parameters:(nullable NSDictionary *)dict {
// NSLog(@"log event: %@, %@", name, dict);
- if (![EZConfiguration.shared allowAnalytics]) {
+ if (![Configuration.shared allowAnalytics]) {
return;
}
diff --git a/Easydict/Feature/ViewController/Cell/EZSelectLanguageCell.m b/Easydict/Feature/ViewController/Cell/EZSelectLanguageCell.m
index bc993c1df..1be65ee65 100644
--- a/Easydict/Feature/ViewController/Cell/EZSelectLanguageCell.m
+++ b/Easydict/Feature/ViewController/Cell/EZSelectLanguageCell.m
@@ -11,6 +11,7 @@
#import "EZConfiguration.h"
#import "NSColor+MyColors.h"
#import "EZHoverButton.h"
+#import "Easydict-Swift.h"
@interface EZSelectLanguageCell ()
@@ -86,8 +87,8 @@ - (void)setup {
mm_strongify(self);
self.queryModel.userSourceLanguage = selectedLanguage;
- if (![selectedLanguage isEqualToString:EZConfiguration.shared.from]) {
- EZConfiguration.shared.from = selectedLanguage;
+ if (![selectedLanguage isEqualToString:Configuration.shared.fromLanguage]) {
+ Configuration.shared.fromLanguage = selectedLanguage;
[self enterAction];
}
}];
@@ -103,8 +104,8 @@ - (void)setup {
mm_strongify(self);
self.queryModel.userTargetLanguage = selectedLanguage;
- if (![selectedLanguage isEqualToString:EZConfiguration.shared.to]) {
- EZConfiguration.shared.to = selectedLanguage;
+ if (![selectedLanguage isEqualToString:Configuration.shared.toLanguage]) {
+ Configuration.shared.toLanguage = selectedLanguage;
[self enterAction];
}
}];
@@ -164,8 +165,8 @@ - (void)toggleTranslationLanguages {
EZLanguage toLang = self.queryModel.userTargetLanguage;
if (![fromLang isEqualToString:toLang]) {
- EZConfiguration.shared.from = toLang;
- EZConfiguration.shared.to = fromLang;
+ Configuration.shared.fromLanguage = toLang;
+ Configuration.shared.toLanguage = fromLang;
[self.fromLanguageButton setSelectedLanguage:toLang];
[self.toLanguageButton setSelectedLanguage:fromLang];
@@ -181,7 +182,7 @@ - (void)enterAction {
[self setNeedsUpdateConstraints:YES];
if (self.enterActionBlock) {
- self.enterActionBlock(EZConfiguration.shared.from, EZConfiguration.shared.to);
+ self.enterActionBlock(Configuration.shared.fromLanguage, Configuration.shared.toLanguage);
}
}
diff --git a/Easydict/Feature/ViewController/Model/EZQueryModel.m b/Easydict/Feature/ViewController/Model/EZQueryModel.m
index c398ffd87..810385940 100644
--- a/Easydict/Feature/ViewController/Model/EZQueryModel.m
+++ b/Easydict/Feature/ViewController/Model/EZQueryModel.m
@@ -13,6 +13,7 @@
#import "NSString+EZSplit.h"
#import "EZAppleDictionary.h"
#import "NSString+EZHandleInputText.h"
+#import "Easydict-Swift.h"
@interface EZQueryModel ()
@@ -29,10 +30,10 @@ @implementation EZQueryModel
- (instancetype)init {
if (self = [super init]) {
- [self.KVOController observe:EZConfiguration.shared keyPath:@"from" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew block:^(EZQueryModel *queryModel, EZConfiguration *config, NSDictionary *_Nonnull change) {
+ [self.KVOController observe:Configuration.shared keyPath:@"fromLanguage" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew block:^(EZQueryModel *queryModel, Configuration *config, NSDictionary *_Nonnull change) {
queryModel.userSourceLanguage = change[NSKeyValueChangeNewKey];
}];
- [self.KVOController observe:EZConfiguration.shared keyPath:@"to" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew block:^(EZQueryModel *queryModel, EZConfiguration *config, NSDictionary *_Nonnull change) {
+ [self.KVOController observe:Configuration.shared keyPath:@"toLanguage" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew block:^(EZQueryModel *queryModel, Configuration *config, NSDictionary *_Nonnull change) {
queryModel.userTargetLanguage = change[NSKeyValueChangeNewKey];
}];
@@ -198,7 +199,7 @@ - (NSString *)handleInputText:(NSString *)inputText {
}
}
- if (EZConfiguration.shared.isBeta) {
+ if (Configuration.shared.beta) {
// Remove prefix [//,#,*,] and join texts.
queryText = [queryText removeCommentBlockSymbols];
}
diff --git a/Easydict/Feature/ViewController/View/EZQueryMenuTextView/EZQueryMenuTextView.m b/Easydict/Feature/ViewController/View/EZQueryMenuTextView/EZQueryMenuTextView.m
index 6a88cde89..ab46a8a8b 100644
--- a/Easydict/Feature/ViewController/View/EZQueryMenuTextView/EZQueryMenuTextView.m
+++ b/Easydict/Feature/ViewController/View/EZQueryMenuTextView/EZQueryMenuTextView.m
@@ -12,6 +12,7 @@
#import "EZCoordinateUtils.h"
#import "EZLog.h"
#import "NSString+EZUtils.h"
+#import "Easydict-Swift.h"
@interface EZQueryMenuTextView ()
@@ -54,10 +55,10 @@ - (void)queryInApp:(id)sender {
EZWindowManager *windowManager = [EZWindowManager shared];
EZWindowType floatingWindowType = windowManager.floatingWindowType;
- if (EZConfiguration.shared.mouseSelectTranslateWindowType == floatingWindowType) {
- anotherWindowType = EZConfiguration.shared.shortcutSelectTranslateWindowType;
+ if (Configuration.shared.mouseSelectTranslateWindowType == floatingWindowType) {
+ anotherWindowType = Configuration.shared.shortcutSelectTranslateWindowType;
} else {
- anotherWindowType = EZConfiguration.shared.mouseSelectTranslateWindowType;
+ anotherWindowType = Configuration.shared.mouseSelectTranslateWindowType;
}
if (anotherWindowType != floatingWindowType) {
diff --git a/Easydict/Feature/ViewController/View/QueryView/EZQueryView.m b/Easydict/Feature/ViewController/View/QueryView/EZQueryView.m
index 964cd6483..a99a4d73a 100644
--- a/Easydict/Feature/ViewController/View/QueryView/EZQueryView.m
+++ b/Easydict/Feature/ViewController/View/QueryView/EZQueryView.m
@@ -74,7 +74,7 @@ - (void)setup {
textView.delegate = self;
textView.textStorage.delegate = self;
textView.textContainerInset = CGSizeMake(6, 8);
- textView.font = [NSFont systemFontOfSize:14 * EZConfiguration.shared.fontSizeRatio];
+ textView.font = [NSFont systemFontOfSize:14 * Configuration.shared.fontSizeRatio];
mm_weakify(self);
[textView setPasteTextBlock:^(NSString *_Nonnull text) {
@@ -87,7 +87,7 @@ - (void)setup {
[[NSNotificationCenter defaultCenter] addObserverForName:ChangeFontSizeView.changeFontSizeNotificationName object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull notification) {
mm_strongify(self);
- self.textView.font = [NSFont systemFontOfSize:14 * EZConfiguration.shared.fontSizeRatio];
+ self.textView.font = [NSFont systemFontOfSize:14 * Configuration.shared.fontSizeRatio];
}];
// When programatically setting the text, like auto select text, or OCR text.
diff --git a/Easydict/Feature/ViewController/View/Titlebar/EZTitlebar.m b/Easydict/Feature/ViewController/View/Titlebar/EZTitlebar.m
index e6cb2d6e7..2b2c364aa 100644
--- a/Easydict/Feature/ViewController/View/Titlebar/EZTitlebar.m
+++ b/Easydict/Feature/ViewController/View/Titlebar/EZTitlebar.m
@@ -13,6 +13,7 @@
#import "NSObject+EZDarkMode.h"
#import "EZBaseQueryWindow.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
@interface EZTitlebar ()
@@ -92,7 +93,7 @@ - (void)updateConstraints {
// TODO: We should refactor it later.
// Google
- if (EZConfiguration.shared.showGoogleQuickLink) {
+ if (Configuration.shared.showGoogleQuickLink) {
EZOpenLinkButton *googleButton = [[EZOpenLinkButton alloc] init];
[self addSubview:googleButton];
self.googleButton = googleButton;
@@ -116,7 +117,7 @@ - (void)updateConstraints {
}
// Apple Dictionary
- if (EZConfiguration.shared.showAppleDictionaryQuickLink) {
+ if (Configuration.shared.showAppleDictionaryQuickLink) {
EZOpenLinkButton *appleDictButton = [[EZOpenLinkButton alloc] init];
[self addSubview:appleDictButton];
self.appleDictionaryButton = appleDictButton;
@@ -140,7 +141,7 @@ - (void)updateConstraints {
}
// Eudic
- if (EZConfiguration.shared.showEudicQuickLink) {
+ if (Configuration.shared.showEudicQuickLink) {
EZOpenLinkButton *eudicButton = [[EZOpenLinkButton alloc] init];
// !!!: Note that some applications have multiple channel versions. Ref: https://github.com/tisfeng/Raycast-Easydict/issues/16
diff --git a/Easydict/Feature/ViewController/View/WordResultView/EZWebViewManager.m b/Easydict/Feature/ViewController/View/WordResultView/EZWebViewManager.m
index aafe9b446..7c32f3634 100644
--- a/Easydict/Feature/ViewController/View/WordResultView/EZWebViewManager.m
+++ b/Easydict/Feature/ViewController/View/WordResultView/EZWebViewManager.m
@@ -8,6 +8,7 @@
#import "EZWebViewManager.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
static NSString *kObjcHandler = @"objcHandler";
static NSString *kMethod = @"method";
@@ -57,7 +58,7 @@ - (void)userContentController:(WKUserContentController *)userContentController d
#pragma mark - WebView evaluateJavaScript
- (void)updateAllIframe {
- CGFloat fontSize = EZConfiguration.shared.fontSizeRatio; // 1.4 --> 140%
+ CGFloat fontSize = Configuration.shared.fontSizeRatio; // 1.4 --> 140%
NSString *script = [NSString stringWithFormat:@"changeIframeBodyFontSize(%.1f); updateAllIframeStyle();", fontSize];
[self.webView evaluateJavaScript:script completionHandler:^(id _Nullable result, NSError *_Nullable error) {
if (!error) {
diff --git a/Easydict/Feature/ViewController/View/WordResultView/EZWordResultView.m b/Easydict/Feature/ViewController/View/WordResultView/EZWordResultView.m
index 10268c06b..0bf1f2b72 100644
--- a/Easydict/Feature/ViewController/View/WordResultView/EZWordResultView.m
+++ b/Easydict/Feature/ViewController/View/WordResultView/EZWordResultView.m
@@ -58,7 +58,7 @@ - (instancetype)initWithFrame:(NSRect)frame {
if (self) {
self.wantsLayer = YES;
self.layer.cornerRadius = EZCornerRadius_8;
- self.fontSizeRatio = EZConfiguration.shared.fontSizeRatio;
+ self.fontSizeRatio = Configuration.shared.fontSizeRatio;
[self.layer excuteLight:^(CALayer *layer) {
layer.backgroundColor = [NSColor ez_resultViewBgLightColor].CGColor;
} dark:^(CALayer *layer) {
@@ -71,7 +71,7 @@ - (instancetype)initWithFrame:(NSRect)frame {
// TODO: This method is too long, need to refactor.
- (void)refreshWithResult:(EZQueryResult *)result {
self.result = result;
- self.fontSizeRatio = EZConfiguration.shared.fontSizeRatio;
+ self.fontSizeRatio = Configuration.shared.fontSizeRatio;
EZTranslateWordResult *wordResult = result.wordResult;
self.webView = result.webViewManager.webView;
@@ -826,7 +826,7 @@ - (void)refreshWithResult:(EZQueryResult *)result {
language = result.to;
}
- EZServiceType defaultTTSServiceType = EZConfiguration.shared.defaultTTSServiceType;
+ EZServiceType defaultTTSServiceType = Configuration.shared.defaultTTSServiceType;
EZQueryService *defaultTTSService = [EZServiceTypes.shared serviceWithType:defaultTTSServiceType];
[result.service.audioPlayer playTextAudio:text
@@ -1324,7 +1324,7 @@ - (void)getTextWithHref:(NSString *)href completionHandler:(void (^_Nullable)(NS
}
- (void)updateWebViewAllIframeFontSize {
- CGFloat fontSize = EZConfiguration.shared.fontSizeRatio * 100;
+ CGFloat fontSize = Configuration.shared.fontSizeRatio * 100;
NSString *jsCode = [NSString stringWithFormat:
@"var iframes = document.querySelectorAll('iframe');"
diff --git a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m
index 58d48a8a3..c1bb5cc13 100644
--- a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m
+++ b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m
@@ -192,7 +192,7 @@ - (void)setupServices {
NSMutableArray *services = [NSMutableArray array];
self.youdaoService = nil;
- EZServiceType defaultTTSServiceType = EZConfiguration.shared.defaultTTSServiceType;
+ EZServiceType defaultTTSServiceType = Configuration.shared.defaultTTSServiceType;
NSArray *allServices = [EZLocalStorage.shared allServices:self.windowType];
for (EZQueryService *service in allServices) {
@@ -352,7 +352,7 @@ - (NSString *)queryText {
}
- (EZQueryService *)defaultTTSService {
- EZServiceType defaultTTSServiceType = EZConfiguration.shared.defaultTTSServiceType;
+ EZServiceType defaultTTSServiceType = Configuration.shared.defaultTTSServiceType;
if (![_defaultTTSService.serviceType isEqualToString:defaultTTSServiceType]) {
_defaultTTSService = [EZServiceTypes.shared serviceWithType:defaultTTSServiceType];
}
@@ -480,7 +480,7 @@ - (void)startOCRImage:(NSImage *)image actionType:(EZActionType)actionType {
return;
}
- if (EZConfiguration.shared.autoCopyOCRText) {
+ if (Configuration.shared.autoCopyOCRText) {
[inputText copyToPasteboardSafely];
}
@@ -493,7 +493,7 @@ - (void)startOCRImage:(NSImage *)image actionType:(EZActionType)actionType {
return;
}
- BOOL autoSnipTranslate = EZConfiguration.shared.autoQueryOCRText;
+ BOOL autoSnipTranslate = Configuration.shared.autoQueryOCRText;
if (autoSnipTranslate && queryModel.autoQuery) {
[self startQueryText];
}
@@ -1199,7 +1199,7 @@ - (EZQueryView *)createQueryView {
[queryView setPasteTextBlock:^(NSString *_Nonnull text) {
mm_strongify(self);
[self detectQueryText:^(NSString *_Nonnull language) {
- if ([EZConfiguration.shared autoQueryPastedText]) {
+ if ([Configuration.shared autoQueryPastedText]) {
[self startQueryWithType:EZActionTypeInputQuery];
}
}];
@@ -1484,7 +1484,7 @@ - (CGFloat)miniQueryViewHeight {
#pragma mark - Auto play English word
- (void)autoPlayEnglishWordAudio {
- if (!EZConfiguration.shared.autoPlayAudio) {
+ if (!Configuration.shared.autoPlayAudio) {
return;
}
@@ -1502,7 +1502,7 @@ - (void)autoPlayEnglishWordAudio {
/// Auto copy translated text.
- (void)autoCopyTranslatedTextOfService:(EZQueryService *)service {
- if (![EZConfiguration.shared autoCopyFirstTranslatedText]) {
+ if (![Configuration.shared autoCopyFirstTranslatedText]) {
service.autoCopyTranslatedTextBlock = nil;
return;
}
diff --git a/Easydict/Feature/ViewController/Window/WindowManager/EZLayoutManager.m b/Easydict/Feature/ViewController/Window/WindowManager/EZLayoutManager.m
index 0fecef8c3..3b8490d02 100644
--- a/Easydict/Feature/ViewController/Window/WindowManager/EZLayoutManager.m
+++ b/Easydict/Feature/ViewController/Window/WindowManager/EZLayoutManager.m
@@ -9,6 +9,7 @@
#import "EZLayoutManager.h"
#import "EZBaseQueryWindow.h"
#import "EZConfiguration.h"
+#import "Easydict-Swift.h"
@interface EZLayoutManager ()
@@ -46,7 +47,7 @@ - (void)commonInitialize {
self.screen = NSScreen.mainScreen;
self.minimumWindowSize = CGSizeMake(300, 70);
- EZConfiguration *configuration = [EZConfiguration shared];
+ Configuration *configuration = [Configuration shared];
self.miniWindowFrame = [configuration windowFrameWithType:EZWindowTypeMini];
if (CGRectEqualToRect(self.miniWindowFrame, CGRectZero)) {
@@ -207,7 +208,7 @@ - (void)updateWindowFrame:(EZBaseQueryWindow *)window {
break;
}
- [EZConfiguration.shared setWindowFrame:window.frame windowType:windowType];
+ [Configuration.shared setWindowFrame:window.frame windowType:windowType];
}
@end
diff --git a/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m b/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m
index 24204c5e4..dd2c18664 100644
--- a/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m
+++ b/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m
@@ -15,6 +15,7 @@
#import "EZPreferencesWindowController.h"
#import "EZConfiguration.h"
#import "EZLog.h"
+#import "Easydict-Swift.h"
@interface EZWindowManager ()
@@ -154,7 +155,7 @@ - (void)updatePopButtonQueryAction {
mm_weakify(self);
EZButton *popButton = self.popButtonWindow.popButton;
- EZConfiguration *config = [EZConfiguration shared];
+ Configuration *config = [Configuration shared];
if (config.hideMainWindow) {
// FIXME: Click pop button will also show preferences window.
@@ -182,7 +183,7 @@ - (void)updatePopButtonQueryAction {
}
- (void)popButtonWindowClicked {
- EZWindowType windowType = EZConfiguration.shared.mouseSelectTranslateWindowType;
+ EZWindowType windowType = Configuration.shared.mouseSelectTranslateWindowType;
self.actionType = EZActionTypeAutoSelectQuery;
[self showFloatingWindowType:windowType queryText:self.selectedText];
[self->_popButtonWindow close];
@@ -291,7 +292,7 @@ - (void)showFloatingWindowType:(EZWindowType)windowType queryText:(nullable NSSt
- (void)showFloatingWindowType:(EZWindowType)windowType
queryText:(nullable NSString *)queryText
actionType:(EZActionType)actionType {
- BOOL autoQuery = [EZConfiguration.shared autoQuerySelectedText];
+ BOOL autoQuery = [Configuration.shared autoQuerySelectedText];
[self showFloatingWindowType:windowType queryText:queryText autoQuery:autoQuery actionType:actionType];
}
@@ -308,7 +309,7 @@ - (void)showFloatingWindowType:(EZWindowType)windowType
actionType:(EZActionType)actionType
atPoint:(CGPoint)point
completionHandler:(nullable void (^)(void))completionHandler {
- BOOL autoQuery = [EZConfiguration.shared autoQuerySelectedText];
+ BOOL autoQuery = [Configuration.shared autoQuerySelectedText];
[self showFloatingWindowType:windowType queryText:queryText autoQuery:autoQuery actionType:actionType atPoint:point completionHandler:completionHandler];
}
@@ -361,7 +362,7 @@ - (void)showFloatingWindowType:(EZWindowType)windowType
}
// TODO: Maybe we should remove this option, it seems useless.
- if ([EZConfiguration.shared autoCopySelectedText]) {
+ if ([Configuration.shared autoCopySelectedText]) {
[queryText copyToPasteboard];
}
@@ -517,7 +518,7 @@ - (CGPoint)getPopButtonWindowLocation {
// NSLog(@"start point: %@", NSStringFromPoint(startLocation));
// NSLog(@"end point: %@", NSStringFromPoint(endLocation));
- if (EZConfiguration.shared.adjustPopButtomOrigin) {
+ if (Configuration.shared.adjustPopButtomOrigin) {
// Since the pop button may cover selected text, we need to move it to the left.
CGFloat horizontalOffset = 20;
@@ -539,7 +540,7 @@ - (CGPoint)getPopButtonWindowLocation {
- (CGPoint)getMiniWindowLocation {
CGPoint position = [self getShowingMouseLocation];
- if (EZConfiguration.shared.adjustPopButtomOrigin) {
+ if (Configuration.shared.adjustPopButtomOrigin) {
position.y = position.y - 8;
}
@@ -587,7 +588,7 @@ - (CGPoint)getMouseLocation:(BOOL)offsetFlag {
/// !!!: This return value is top-left point.
- (CGPoint)getFixedWindowLocation {
CGPoint position = CGPointZero;
- EZShowWindowPosition windowPosition = EZConfiguration.shared.fixedWindowPosition;
+ EZShowWindowPosition windowPosition = Configuration.shared.fixedWindowPosition;
switch (windowPosition) {
case EZShowWindowPositionRight: {
position = [self getFloatingWindowInRightSideOfScreenPoint:self.fixedWindow];
@@ -651,7 +652,7 @@ - (void)saveFrontmostApplication {
}
- (void)showMainWindowIfNedded {
- BOOL showFlag = !EZConfiguration.shared.hideMainWindow;
+ BOOL showFlag = !Configuration.shared.hideMainWindow;
NSApplicationActivationPolicy activationPolicy = showFlag ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory;
[NSApp setActivationPolicy:activationPolicy];
@@ -689,7 +690,7 @@ - (void)selectTextTranslate {
return;
}
- EZWindowType windowType = EZConfiguration.shared.shortcutSelectTranslateWindowType;
+ EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType;
NSLog(@"selectTextTranslate windowType: %@", @(windowType));
self.eventMonitor.actionType = EZActionTypeShortcutQuery;
[self.eventMonitor getSelectedText:^(NSString *_Nullable text) {
@@ -715,7 +716,7 @@ - (void)snipTranslate {
}
// Since ocr detect may be inaccurate, sometimes need to set sourceLanguage manually, so show Fixed window.
- EZWindowType windowType = EZConfiguration.shared.shortcutSelectTranslateWindowType;
+ EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType;
EZBaseQueryWindow *window = [self windowWithType:windowType];
// Wait to close floating window if need.
@@ -755,7 +756,7 @@ - (void)inputTranslate {
return;
}
- EZWindowType windowType = EZConfiguration.shared.shortcutSelectTranslateWindowType;
+ EZWindowType windowType = Configuration.shared.shortcutSelectTranslateWindowType;
if (self.floatingWindowType == windowType) {
[self closeFloatingWindow];
@@ -763,7 +764,7 @@ - (void)inputTranslate {
}
NSString *queryText = nil;
- if ([EZConfiguration.shared clearInput]) {
+ if ([Configuration.shared clearInput]) {
queryText = @"";
}
@@ -775,7 +776,7 @@ - (void)inputTranslate {
- (void)showMiniFloatingWindow {
MMLogInfo(@"showMiniFloatingWindow");
- EZWindowType windowType = EZConfiguration.shared.mouseSelectTranslateWindowType;
+ EZWindowType windowType = Configuration.shared.mouseSelectTranslateWindowType;
if (self.floatingWindowType == windowType) {
[self closeFloatingWindow];
diff --git a/Easydict/NewApp/Configuration/Configuration.swift b/Easydict/NewApp/Configuration/Configuration+Defaults.swift
similarity index 68%
rename from Easydict/NewApp/Configuration/Configuration.swift
rename to Easydict/NewApp/Configuration/Configuration+Defaults.swift
index 5777aa144..2cafce5ed 100644
--- a/Easydict/NewApp/Configuration/Configuration.swift
+++ b/Easydict/NewApp/Configuration/Configuration+Defaults.swift
@@ -1,5 +1,5 @@
//
-// Configuration.swift
+// Configuration+Defaults.swift
// Easydict
//
// Created by 戴藏龙 on 2024/1/12.
@@ -33,7 +33,7 @@ extension Defaults.Keys {
static let autoCopyOCRText = Key("EZConfiguration_kAutoCopyOCRTextKey", default: false)
static let autoCopySelectedText = Key("EZConfiguration_kAutoCopySelectedTextKey", default: false)
static let autoCopyFirstTranslatedText = Key("EZConfiguration_kAutoCopyFirstTranslatedTextKey", default: false)
- static let languageDetectOptimize = Key("EZConfiguration_kLanguageDetectOptimizeTypeKey", default: EZLanguageDetectOptimize.none)
+ static let languageDetectOptimize = Key("EZConfiguration_kLanguageDetectOptimizeTypeKey", default: LanguageDetectOptimize.none)
static let defaultTTSServiceType = Key("EZConfiguration_kDefaultTTSServiceTypeKey", default: TTSServiceType.youdao)
static let showGoogleQuickLink = Key("EZConfiguration_kShowGoogleLinkKey", default: true)
static let showEudicQuickLink = Key("EZConfiguration_kShowEudicLinkKey", default: true)
@@ -54,6 +54,80 @@ extension Defaults.Keys {
static let fontSizeOptionIndex = Key("EZConfiguration_kTranslationControllerFontKey", default: 0)
}
+extension Defaults.Keys {
+ static func intelligentQueryTextType(for serviceType: ServiceType) -> Key {
+ let key = EZConstKey.constkey("IntelligentQueryTextType", serviceType: serviceType)
+ return .init(key, default: EZQueryTextType(rawValue: 7))
+ }
+
+ static func queryTextType(for serviceType: ServiceType) -> Key {
+ let key = EZConstKey.constkey("QueryTextType", serviceType: serviceType)
+ return .init(key, default: EZQueryTextType(rawValue: 0))
+ }
+
+ static func windorFrame(for windowType: EZWindowType) -> Key {
+ let key = "EZConfiguration_kWindowFrameKey_\(windowType)"
+ return .init(key, default: .zero)
+ }
+}
+
+extension EZQueryTextType: Defaults.Serializable {
+ public static var bridge: Bridge = .init()
+
+ public struct Bridge: Defaults.Bridge {
+ public func serialize(_ value: EZQueryTextType?) -> String? {
+ guard let value else { return "7" }
+ return "\(value.rawValue)"
+ }
+
+ public func deserialize(_ object: String?) -> EZQueryTextType? {
+ guard let object else { return nil }
+ return EZQueryTextType(rawValue: UInt(object) ?? 7)
+ }
+
+ public typealias Value = EZQueryTextType
+
+ public typealias Serializable = String
+ }
+}
+
+extension CGRect: Defaults.Serializable {
+ public static var bridge: Bridge = .init()
+
+ public struct Bridge: Defaults.Bridge {
+ public func serialize(_ value: CGRect?) -> String? {
+ let value = value ?? .zero
+ return NSStringFromRect(value)
+ }
+
+ public func deserialize(_ object: String?) -> CGRect? {
+ guard let object else { return nil }
+ return NSRectFromString(object)
+ }
+
+ public typealias Value = CGRect
+
+ public typealias Serializable = String
+ }
+}
+
+@propertyWrapper
+class DefaultsWrapper {
+ var wrappedValue: T {
+ get {
+ Defaults[key]
+ } set {
+ Defaults[key] = newValue
+ }
+ }
+
+ init(_ key: Defaults.Key) {
+ self.key = key
+ }
+
+ let key: Defaults.Key
+}
+
// Service Configuration
extension Defaults.Keys {
// OPENAI
diff --git a/Easydict/NewApp/Model/TTSServiceType.swift b/Easydict/NewApp/Model/TTSServiceType.swift
index c28ef5938..a32efbe17 100644
--- a/Easydict/NewApp/Model/TTSServiceType.swift
+++ b/Easydict/NewApp/Model/TTSServiceType.swift
@@ -10,11 +10,11 @@ import Defaults
import Foundation
enum TTSServiceType: String, CaseIterable {
- case youdao
- case bing
- case google
- case baidu
- case apple
+ case youdao = "Youdao"
+ case bing = "Bing"
+ case google = "Google"
+ case baidu = "Baidu"
+ case apple = "Apple"
}
@available(macOS 13, *)
diff --git a/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift b/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift
index 82ec281a1..2da99bad1 100644
--- a/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift
+++ b/Easydict/NewApp/Utility/Extensions/LanguageDetectOptimizeExtensions.swift
@@ -9,14 +9,14 @@
import Defaults
import Foundation
-extension EZLanguageDetectOptimize: Defaults.Serializable {}
+extension LanguageDetectOptimize: Defaults.Serializable {}
-extension EZLanguageDetectOptimize: CaseIterable {
- public static let allCases: [EZLanguageDetectOptimize] = [.none, .baidu, .google]
+extension LanguageDetectOptimize: CaseIterable {
+ public static let allCases: [LanguageDetectOptimize] = [.none, .baidu, .google]
}
@available(macOS 13, *)
-extension EZLanguageDetectOptimize: CustomLocalizedStringResourceConvertible {
+extension LanguageDetectOptimize: CustomLocalizedStringResourceConvertible {
public var localizedStringResource: LocalizedStringResource {
switch self {
case .none:
diff --git a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
index 852631707..84668e464 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
@@ -26,7 +26,7 @@ struct GeneralTab: View {
Section {
FirstAndSecondLanguageSettingView()
Picker("setting.general.language.language_detect_optimize", selection: $languageDetectOptimize) {
- ForEach(EZLanguageDetectOptimize.allCases, id: \.rawValue) { option in
+ ForEach(LanguageDetectOptimize.allCases, id: \.rawValue) { option in
Text(option.localizedStringResource)
.tag(option)
}
From 6c837d417a9dface038948aaa29686ddb1577038 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Tue, 23 Jan 2024 16:39:31 +0800
Subject: [PATCH 31/72] perf: add new advanced tab
---
Easydict.xcodeproj/project.pbxproj | 4 ++
Easydict/App/Localizable.xcstrings | 19 ++++++++-
.../NewApp/View/SettingView/SettingView.swift | 6 +++
.../View/SettingView/Tabs/AdvancedTab.swift | 42 +++++++++++++++++++
.../View/SettingView/Tabs/GeneralTab.swift | 15 -------
5 files changed, 70 insertions(+), 16 deletions(-)
create mode 100644 Easydict/NewApp/View/SettingView/Tabs/AdvancedTab.swift
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index 43ca6f7b7..0feacd3fa 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -70,6 +70,7 @@
037E006D2B3DC098006491C6 /* EZOpenAIService+EZPromptMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 03CF27FA2B3A787900E19B57 /* EZOpenAIService+EZPromptMessages.m */; };
038030952B4106800009230C /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 038030942B4106800009230C /* CocoaLumberjack */; };
038030972B4106800009230C /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 038030962B4106800009230C /* CocoaLumberjackSwift */; };
+ 03832F542B5F6BE200D0DC64 /* AdvancedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03832F532B5F6BE200D0DC64 /* AdvancedTab.swift */; };
0383914C292FBE120009828C /* Main.strings in Resources */ = {isa = PBXBuildFile; fileRef = 03839140292FBE120009828C /* Main.strings */; };
0383914D292FBE120009828C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03839143292FBE120009828C /* Assets.xcassets */; };
0383914E292FBE120009828C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 03839144292FBE120009828C /* ViewController.m */; };
@@ -421,6 +422,7 @@
037852B529588EDE00D0E2CF /* EZCustomTableRowView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZCustomTableRowView.m; sourceTree = ""; };
037852B7295D49F900D0E2CF /* EZTableRowView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EZTableRowView.h; sourceTree = ""; };
037852B8295D49F900D0E2CF /* EZTableRowView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZTableRowView.m; sourceTree = ""; };
+ 03832F532B5F6BE200D0DC64 /* AdvancedTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedTab.swift; sourceTree = ""; };
03839141292FBE120009828C /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; };
03839142292FBE120009828C /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
03839143292FBE120009828C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
@@ -2099,6 +2101,7 @@
0A8685C72B552A590022534F /* DisabledAppTab.swift */,
276742042B3DC230002A2C75 /* PrivacyTab.swift */,
276742052B3DC230002A2C75 /* AboutTab.swift */,
+ 03832F532B5F6BE200D0DC64 /* AdvancedTab.swift */,
);
path = Tabs;
sourceTree = "";
@@ -2831,6 +2834,7 @@
DC46DF802B4417B900DEAE3E /* Configuration.swift in Sources */,
036E7D7B293F4FC8002675DF /* EZOpenLinkButton.m in Sources */,
EAED41EC2B54AA920005FE0A /* ServiceConfigurationSection.swift in Sources */,
+ 03832F542B5F6BE200D0DC64 /* AdvancedTab.swift in Sources */,
276742092B3DC230002A2C75 /* AboutTab.swift in Sources */,
03008B2E2941956D0062B821 /* EZURLSchemeHandler.m in Sources */,
DC6D9C872B352EBC0055EFFC /* FontSizeHintView.swift in Sources */,
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index aacf0329e..d96c42854 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -2,7 +2,14 @@
"sourceLanguage" : "en",
"strings" : {
"" : {
-
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : ""
+ }
+ }
+ }
},
"about" : {
"comment" : "about",
@@ -37,6 +44,16 @@
}
}
},
+ "advanced" : {
+ "localizations" : {
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "高级"
+ }
+ }
+ }
+ },
"ali_translate" : {
"extractionState" : "manual",
"localizations" : {
diff --git a/Easydict/NewApp/View/SettingView/SettingView.swift b/Easydict/NewApp/View/SettingView/SettingView.swift
index f64f70efc..ec5c395ab 100644
--- a/Easydict/NewApp/View/SettingView/SettingView.swift
+++ b/Easydict/NewApp/View/SettingView/SettingView.swift
@@ -12,6 +12,7 @@ enum SettingTab: Int {
case general
case service
case disabled
+ case advanced
case privacy
case about
}
@@ -34,6 +35,9 @@ struct SettingView: View {
DisabledAppTab()
.tabItem { Label("disabled_app_list", systemImage: "nosign") }
.tag(SettingTab.disabled)
+ AdvancedTab()
+ .tabItem { Label("advanced", systemImage: "wrench.and.screwdriver") }
+ .tag(SettingTab.advanced)
PrivacyTab()
.tabItem { Label("privacy", systemImage: "hand.raised.square") }
@@ -71,6 +75,8 @@ struct SettingView: View {
320
case .about:
450
+ default:
+ 400
}
let newSize = CGSize(width: maxWidth, height: height)
diff --git a/Easydict/NewApp/View/SettingView/Tabs/AdvancedTab.swift b/Easydict/NewApp/View/SettingView/Tabs/AdvancedTab.swift
new file mode 100644
index 000000000..55b9da5c3
--- /dev/null
+++ b/Easydict/NewApp/View/SettingView/Tabs/AdvancedTab.swift
@@ -0,0 +1,42 @@
+//
+// AdvancedTab.swift
+// Easydict
+//
+// Created by tisfeng on 2024/1/23.
+// Copyright © 2024 izual. All rights reserved.
+//
+
+import Defaults
+import SwiftUI
+
+@available(macOS 13, *)
+struct AdvancedTab: View {
+ var body: some View {
+ Form {
+ Section {
+ Picker("setting.general.advance.default_tts_service", selection: $defaultTTSServiceType) {
+ ForEach(TTSServiceType.allCases, id: \.rawValue) { option in
+ Text(option.localizedStringResource)
+ .tag(option)
+ }
+ }
+ Toggle("setting.general.advance.enable_beta_feature", isOn: $enableBetaFeature)
+ Toggle(isOn: $enableBetaNewApp) {
+ Text("enable_beta_new_app")
+ }
+ } header: {
+ Text("setting.general.advance.header")
+ }
+ }
+ .formStyle(.grouped)
+ }
+
+ @Default(.defaultTTSServiceType) private var defaultTTSServiceType
+ @Default(.enableBetaFeature) private var enableBetaFeature
+ @Default(.enableBetaNewApp) private var enableBetaNewApp
+}
+
+@available(macOS 13, *)
+#Preview {
+ AdvancedTab()
+}
diff --git a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
index 84668e464..6471771c1 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
@@ -139,21 +139,6 @@ struct GeneralTab: View {
} header: {
Text("other")
}
-
- Section {
- Picker("setting.general.advance.default_tts_service", selection: $defaultTTSServiceType) {
- ForEach(TTSServiceType.allCases, id: \.rawValue) { option in
- Text(option.localizedStringResource)
- .tag(option)
- }
- }
- Toggle("setting.general.advance.enable_beta_feature", isOn: $enableBetaFeature)
- Toggle(isOn: $enableBetaNewApp) {
- Text("enable_beta_new_app")
- }
- } header: {
- Text("setting.general.advance.header")
- }
}
.formStyle(.grouped)
}
From c673f6ca1f33a259dfb34fe4d4740a5a502b9a0d Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Wed, 24 Jan 2024 11:50:19 +0800
Subject: [PATCH 32/72] fix add disable app drag lag issue (#355)
* fix: add disable app by dragging lag issue
* fix: revert default disable app workaround and disableAutoSelect
* fix: revert workarounds and fix by disableAutoSelect
---
Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
index 7d70402ff..d45ba5a44 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
@@ -12,8 +12,13 @@ import SwiftUI
private class DisabledAppViewModel: ObservableObject {
@Published var appModelList: [EZAppModel] = []
@Published var selectedAppModels: Set = []
- @Published var isImporting = false
@Published var isShowImportErrorAlert = false
+ @Published var isImporting = false {
+ didSet {
+ // https://github.com/tisfeng/Easydict/issues/346
+ Configuration.shared.disabledAutoSelect = isImporting
+ }
+ }
init() {
fetchDisabledApps()
From f0bfe0bd072a81d806c6c233157e647a28e21ab7 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Tue, 23 Jan 2024 23:25:12 +0800
Subject: [PATCH 33/72] docs: update sponsor list
---
README.md | 1 +
README_EN.md | 1 +
2 files changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 2f0a8a952..dfb27f0b8 100644
--- a/README.md
+++ b/README.md
@@ -823,6 +823,7 @@ Easydict 作为一个免费开源的非盈利项目,目前主要是作者个
| 2024-01-09 | ㅤ Jack | 20 | 目前用过最好用的字典软件,谢谢! |
| 2024-01-15 | ㅤ | 20 | 感谢开源,感谢有你:) |
| 2024-01-16 | ㅤ sd | 5 | 大佬牛逼🐂🍺 |
+| 2024-01-23 | ㅤ | 5 | |
diff --git a/README_EN.md b/README_EN.md
index 026b259d3..369d47ec0 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -820,6 +820,7 @@ If you don't want your username to be displayed in the list, please choose anony
| 2024-01-09 | ㅤ Jack | 20 | 目前用过最好用的字典软件,谢谢! |
| 2024-01-15 | ㅤ | 20 | 感谢开源,感谢有你:) |
| 2024-01-16 | ㅤ sd | 5 | 大佬牛逼🐂🍺 |
+| 2024-01-23 | ㅤ | 5 | |
From 27c8eceeb36766031227e4fca82db00b7636348e Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Wed, 24 Jan 2024 11:50:06 +0800
Subject: [PATCH 34/72] chore: update issue templates
---
.github/ISSUE_TEMPLATE/cn_bug_report_zh.yml | 20 +++++++++++++----
.github/ISSUE_TEMPLATE/cn_feature_request.yml | 8 +++++--
.github/ISSUE_TEMPLATE/en_bug_report.yml | 22 ++++++++++++++-----
.github/ISSUE_TEMPLATE/en_feature_request.yml | 8 +++++--
4 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/cn_bug_report_zh.yml b/.github/ISSUE_TEMPLATE/cn_bug_report_zh.yml
index de97b7dfe..c2d0f068a 100644
--- a/.github/ISSUE_TEMPLATE/cn_bug_report_zh.yml
+++ b/.github/ISSUE_TEMPLATE/cn_bug_report_zh.yml
@@ -1,6 +1,6 @@
name: 反馈问题
description: 反馈问题
-title: "🐞 反馈问题:"
+title: "🐞 反馈问题:{{请填写标题,不要留空}}"
labels: ["bug"]
body:
@@ -22,18 +22,30 @@ body:
id: description
attributes:
label: 问题描述
- description: 请详细描述你所遇到的问题,确保开发者能够理解、重现该问题。如果上下文信息不足,开发者无法定位,问题会被降低优先级或忽略。
+ description: 请详细描述你所遇到的问题,确保开发者能够理解、重现该问题。如果上下文信息不足,导致开发者无法定位,问题会被降低优先级或忽略。
placeholder: 问题描述
validations:
required: true
+ - type: dropdown
+ id: reproducible
+ attributes:
+ label: 该问题是否可以稳定重现?
+ multiple: false
+ options:
+ - 可重现
+ - 不可重现
+ validations:
+ required: true
+
- type: textarea
id: reproduce
attributes:
label: 重现步骤
description: |
- 请描述如何重现该问题。如果该问题是偶发性的,或者需要特定的操作步骤才能重现,请一定要详细提供重现步骤,否则开发者无法定位问题。
- (如果遇到一些很奇怪的问题,可以先尝试重启 Easydict,重启电脑,或卸载重装应用,看能否解决问题 🤔)
+ 如果该问题可重现,请一定要详细提供重现步骤,否则开发者无法定位问题。
+ 如果该问题是偶发性的,可以先尝试重启 Easydict,重启电脑,或卸载重装应用,看能否解决问题 🤔
+ 注意:鉴于开发者精力有限,目前只会处理可稳定重现的问题。对于不可重现的问题,当前只会简单记录下来,观察后续。
placeholder: 重现步骤
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/cn_feature_request.yml b/.github/ISSUE_TEMPLATE/cn_feature_request.yml
index 93baaa805..07b4f5297 100644
--- a/.github/ISSUE_TEMPLATE/cn_feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/cn_feature_request.yml
@@ -1,6 +1,6 @@
name: 功能建议
description: 功能建议
-title: "🚀 功能建议:"
+title: "🚀 功能建议:{{请填写标题,不要留空}}"
labels: ["enhancement"]
body:
@@ -15,6 +15,8 @@ body:
required: true
- label: Easydict 已升级到 [最新版本](https://github.com/tisfeng/Easydict/releases)
required: true
+ - label: 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 issue 可能会被无视或直接关闭**
+ required: true
- type: textarea
id: feature_description
@@ -29,7 +31,9 @@ body:
id: feature_usecase
attributes:
label: 使用场景
- description: 请描述你希望功能的使用场景,有无其他类似可供参考的 App 功能等。
+ description: |
+ 请描述你希望功能的使用场景,有无其他类似可供参考的 App 功能等。
+ 如果该功能没有明确的使用场景,或是无法被开发者理解,可能会被降低优先级或忽略,因此请务必清晰描述。
placeholder: 使用场景
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/en_bug_report.yml b/.github/ISSUE_TEMPLATE/en_bug_report.yml
index fc13b2380..2f87973a8 100644
--- a/.github/ISSUE_TEMPLATE/en_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/en_bug_report.yml
@@ -1,6 +1,6 @@
name: Bug report
description: Report an issue
-title: "🐞 Bug Report: "
+title: "🐞 Bug Report: {{Please fill in the title, don't leave it blank}} "
labels: ["bug"]
body:
@@ -27,14 +27,26 @@ body:
validations:
required: true
+ - type: dropdown
+ id: reproducible
+ attributes:
+ label: Is the issue consistently reproducible?
+ multiple: false
+ options:
+ - Reproducible
+ - Non-reproducible
+ validations:
+ required: true
+
- type: textarea
id: reproduce
attributes:
- label: Reproduction steps
+ label: Steps to Reproduce
description: |
- Please describe how to reproduce the problem. If the problem is episodic or requires specific action steps to reproduce, please describe it in as much detail as possible, otherwise the developer will not be able to locate the problem.
- (If the problem is episodic, try restarting Easydict, restarting the device, or uninstalling and reinstalling the app to see if that solves the problem 🤔)
- placeholder: Reproduction steps
+ If the issue is reproducible, please provide detailed steps to reproduce it. Otherwise, the developer may not be able to locate the issue.
+ If the issue is intermittent, you can try restarting Easydict, restarting your computer, or uninstalling and reinstalling the application to see if the problem can be resolved 🤔
+ Note: Given the limited resources of the developer, only issues that can be reliably reproduced will be addressed at this time. For non-reproducible issues, they will be simply recorded for future observation.
+ placeholder: Steps to Reproduce
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/en_feature_request.yml b/.github/ISSUE_TEMPLATE/en_feature_request.yml
index 5b50e36f2..f63be4bf5 100644
--- a/.github/ISSUE_TEMPLATE/en_feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/en_feature_request.yml
@@ -1,6 +1,6 @@
name: Feature request
description: Request a new feature
-title: "🚀 Feature Request: "
+title: "🚀 Feature Request: {{Please fill in the title, don't leave it blank}}"
labels: ["enhancement"]
body:
@@ -15,6 +15,8 @@ body:
required: true
- label: Easydict has been upgraded to the [latest version](https://github.com/tisfeng/Easydict/releases)
required: true
+ - label: I understand and agree to the above, and understand that the project maintainer has limited energy, **issues that do not follow the rules may be ignored or closed directly**
+ required: true
- type: textarea
id: feature_description
@@ -29,7 +31,9 @@ body:
id: feature_usecase
attributes:
label: Use case
- description: Please describe the use case of the feature you're requesting, and whether there are any similar app features for reference.
+ description: |
+ Please describe the use case of the feature you're requesting, and whether there are any similar app features for reference.
+ If the feature has no actual usage scenarios or is not well understood by the developers it might get deprioritized or even ignored, so be sure to describe it clearly.
placeholder: Use case
validations:
required: true
From a2cc86277b9ea15d299afeb1dde8b961982b9908 Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Wed, 24 Jan 2024 16:57:11 +0800
Subject: [PATCH 35/72] fix: General tab other section title (#361)
* fix: general tab other title issue
* fix: other translation
---
Easydict/App/Localizable.xcstrings | 29 ++++++++++++-------
.../View/SettingView/Tabs/GeneralTab.swift | 2 +-
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings
index d96c42854..bb3696775 100644
--- a/Easydict/App/Localizable.xcstrings
+++ b/Easydict/App/Localizable.xcstrings
@@ -1958,16 +1958,6 @@
}
}
},
- "other" : {
- "localizations" : {
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "其他"
- }
- }
- }
- },
"Parameter Error" : {
"comment" : "Error description",
"localizations" : {
@@ -2772,6 +2762,23 @@
}
}
},
+ "setting.general.other.header" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Other"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "其他"
+ }
+ }
+ }
+ },
"setting.general.quick_link.header" : {
"localizations" : {
"en" : {
@@ -3443,4 +3450,4 @@
}
},
"version" : "1.0"
-}
\ No newline at end of file
+}
diff --git a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
index 6471771c1..26e427fee 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift
@@ -137,7 +137,7 @@ struct GeneralTab: View {
Text("hide_menu_bar_icon")
}
} header: {
- Text("other")
+ Text("setting.general.other.header")
}
}
.formStyle(.grouped)
From e0fd57a072ca7b2fd274fda46118761da783adfb Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Wed, 24 Jan 2024 14:06:46 +0800
Subject: [PATCH 36/72] perf: change to use gearshape.2 SF image for Advanced
tab
---
Easydict/NewApp/View/SettingView/SettingView.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Easydict/NewApp/View/SettingView/SettingView.swift b/Easydict/NewApp/View/SettingView/SettingView.swift
index ec5c395ab..81333b8ee 100644
--- a/Easydict/NewApp/View/SettingView/SettingView.swift
+++ b/Easydict/NewApp/View/SettingView/SettingView.swift
@@ -36,7 +36,7 @@ struct SettingView: View {
.tabItem { Label("disabled_app_list", systemImage: "nosign") }
.tag(SettingTab.disabled)
AdvancedTab()
- .tabItem { Label("advanced", systemImage: "wrench.and.screwdriver") }
+ .tabItem { Label("advanced", systemImage: "gearshape.2") }
.tag(SettingTab.advanced)
PrivacyTab()
From 1dcf8d6d9e3ff11a33aac73f9f7f27cf39057120 Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Wed, 24 Jan 2024 18:18:51 +0800
Subject: [PATCH 37/72] fix: do not remove extra line breaks in DeepL
---
Easydict/Feature/Service/DeepL/EZDeepLTranslate.m | 1 -
1 file changed, 1 deletion(-)
diff --git a/Easydict/Feature/Service/DeepL/EZDeepLTranslate.m b/Easydict/Feature/Service/DeepL/EZDeepLTranslate.m
index 44007d5ad..576c5de8c 100644
--- a/Easydict/Feature/Service/DeepL/EZDeepLTranslate.m
+++ b/Easydict/Feature/Service/DeepL/EZDeepLTranslate.m
@@ -390,7 +390,6 @@ - (void)deepLTranslate:(NSString *)text from:(EZLanguage)from to:(EZLanguage)to
}
*/
NSString *translatedText = [responseObject[@"translations"] firstObject][@"text"];
- translatedText = [translatedText.trim removeExtraLineBreaks];
NSArray *translatedTextArray = [translatedText toParagraphs];
return translatedTextArray;
From a7bb7852f2088294602f3e35dab5bd38a94a88fa Mon Sep 17 00:00:00 2001
From: tisfeng
Date: Thu, 25 Jan 2024 10:17:36 +0800
Subject: [PATCH 38/72] perf(ocr): do not check if new paragraph by big line
spacing
---
.../Feature/Service/Apple/EZAppleService.m | 38 ++++++++++---------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/Easydict/Feature/Service/Apple/EZAppleService.m b/Easydict/Feature/Service/Apple/EZAppleService.m
index be76679f2..e2a681b15 100644
--- a/Easydict/Feature/Service/Apple/EZAppleService.m
+++ b/Easydict/Feature/Service/Apple/EZAppleService.m
@@ -24,7 +24,7 @@
static NSArray *const kAllowedCharactersInPoetryList = @[ @"《", @"》", @"〔", @"〕" ];
-static CGFloat const kParagraphLineHeightRatio = 1.2;
+static CGFloat const kParagraphLineHeightRatio = 1.5;
static NSInteger const kShortPoetryCharacterCountOfLine = 12;
@@ -1211,18 +1211,6 @@ - (void)setupOCRResult:(EZOCRResult *)ocrResult
CGFloat deltaY = prevBoundingBox.origin.y - (boundingBox.origin.y + boundingBox.size.height);
CGFloat deltaX = boundingBox.origin.x - (prevBoundingBox.origin.x + prevBoundingBox.size.width);
- // Note that line spacing is inaccurate, sometimes it's too small 😢
- BOOL isNewParagraph = NO;
- if (deltaY > 0) {
- // averageLineSpacing may too small, so deltaY should be much larger than averageLineSpacing
- BOOL isBigLineSpacing = [self isBigSpacingLineOfTextObservation:textObservation
- prevTextObservation:prevTextObservation
- greaterThanLineHeightRatio:kParagraphLineHeightRatio];
- if (isBigLineSpacing) {
- isNewParagraph = YES;
- }
- }
-
// Note that sometimes the line frames will overlap a little, then deltaY will less then 0
BOOL isNewLine = NO;
if (deltaY > 0) {
@@ -1251,10 +1239,9 @@ - (void)setupOCRResult:(EZOCRResult *)ocrResult
if (isNeedRemoveLastDashOfText) {
mergedText = [mergedText substringToIndex:mergedText.length - 1].mutableCopy;
}
- } else if (isNewParagraph || isNewLine) {
+ } else if (isNewLine) {
joinedString = [self joinedStringOfTextObservation:textObservation
- prevTextObservation:prevTextObservation
- isNewParagraph:isNewParagraph];
+ prevTextObservation:prevTextObservation];
} else {
joinedString = @" "; // if the same line, just join two texts
}
@@ -1489,9 +1476,10 @@ - (BOOL)isPoetryOftextObservations:(NSArray *)tex
/// Get joined string of text, according to its last char.
- (NSString *)joinedStringOfTextObservation:(VNRecognizedTextObservation *)textObservation
prevTextObservation:(VNRecognizedTextObservation *)prevTextObservation
- isNewParagraph:(BOOL)isNewParagraph {
+{
NSString *joinedString = @"";
BOOL needLineBreak = NO;
+ BOOL isNewParagraph = NO;
CGRect prevBoundingBox = prevTextObservation.boundingBox;
CGFloat prevLineLength = prevBoundingBox.size.width;
@@ -1509,7 +1497,7 @@ - (NSString *)joinedStringOfTextObservation:(VNRecognizedTextObservation *)textO
BOOL hasPrevIndentation = [self hasIndentationOfTextObservation:prevTextObservation];
BOOL hasIndentation = [self hasIndentationOfTextObservation:textObservation];
-
+
BOOL isPrevLongText = [self isLongTextObservation:prevTextObservation isStrict:NO];
BOOL isEqualChineseText = [self isEqualChineseTextObservation:textObservation prevTextObservation:prevTextObservation];
@@ -1895,6 +1883,20 @@ - (BOOL)hasIndentationOfTextObservation:(VNRecognizedTextObservation *)textObser
return hasIndentation;
}
+- (BOOL)hasIndentationOfTextObservation:(VNRecognizedTextObservation *)textObservation
+ prevTextObservation:(VNRecognizedTextObservation *)prevTextObservation {
+ BOOL isEqualX = [self isEqualXOfTextObservation:textObservation prevTextObservation:prevTextObservation];
+
+ CGFloat lineX = CGRectGetMinX(textObservation.boundingBox);
+ CGFloat prevLineX = CGRectGetMinX(prevTextObservation.boundingBox);
+ CGFloat dx = lineX - prevLineX;
+
+ if (!isEqualX && dx < 0) {
+ return YES;
+ }
+ return NO;
+}
+
- (BOOL)isEqualTextObservation:(VNRecognizedTextObservation *)textObservation
prevTextObservation:(VNRecognizedTextObservation *)prevTextObservation {
// 0.06 - 0.025
From b6974d1f20dc583a4212444622faca45c046103c Mon Sep 17 00:00:00 2001
From: phlpsong <103433299+phlpsong@users.noreply.github.com>
Date: Fri, 26 Jan 2024 09:59:49 +0800
Subject: [PATCH 39/72] fix: setting crash after uninstall app in disabled app
list (#364)
Co-authored-by: Tisfeng
---
.../EZDisableAutoSelectTextViewController.m | 14 +++++++++++++-
.../View/SettingView/Tabs/DisabledAppTab.swift | 7 ++++++-
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m b/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m
index 86a5c8e56..8df326346 100644
--- a/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m
+++ b/Easydict/Feature/PerferenceWindow/DisableAutoSelectTextViewController/EZDisableAutoSelectTextViewController.m
@@ -57,7 +57,7 @@ - (void)viewDidLoad {
}
- (void)setup {
- self.appModelList = [[EZLocalStorage.shared selectTextTypeAppModelList] mutableCopy];
+ [self setupAppModelList];
[self.titleTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.inset(kMargin + 5); // ???: Why is the actual inset is 18?
@@ -78,6 +78,18 @@ - (void)setup {
}];
}
+- (void)setupAppModelList {
+ self.appModelList = [[NSMutableArray alloc] init];
+ NSArray *allAppModelList = [EZLocalStorage.shared selectTextTypeAppModelList];
+ NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
+ for (EZAppModel *appModel in allAppModelList) {
+ NSURL *appURL = [workspace URLForApplicationWithBundleIdentifier:appModel.appBundleID];
+ if (appURL) {
+ [self.appModelList addObject:appModel];
+ }
+ }
+}
+
#pragma mark - Getter && Setter
diff --git a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
index d45ba5a44..ece9cc335 100644
--- a/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
+++ b/Easydict/NewApp/View/SettingView/Tabs/DisabledAppTab.swift
@@ -25,7 +25,12 @@ private class DisabledAppViewModel: ObservableObject {
}
func fetchDisabledApps() {
- appModelList = EZLocalStorage.shared().selectTextTypeAppModelList
+ let allAppModelList = EZLocalStorage.shared().selectTextTypeAppModelList
+
+ appModelList = allAppModelList.compactMap { appModel in
+ let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: appModel.appBundleID)
+ return url == nil ? nil : appModel
+ }
}
func saveDisabledApps() {
From 5d30f34604648cf67850df60a1ac025ff983f28c Mon Sep 17 00:00:00 2001
From: Lava <34743145+CanglongCl@users.noreply.github.com>
Date: Fri, 26 Jan 2024 17:50:34 -0800
Subject: [PATCH 40/72] [Refactor] Introduce `GlobalContext` and migrate the
updater controller into it (#365)
* introduce global context and migrate updater controller into
* refactor: mutually initialize global context
* revert `@Default` in `EasydictApp`
* fix: remove updaterController from Main.storyboard
* style: fix typo
* fix: updaterDelegate was nil
* perf: set updaterHelper to private
* perf: remove sparkle code from AppDelegate
* perf: remove unused #import header
---------
Co-authored-by: Tisfeng
---
Easydict.xcodeproj/project.pbxproj | 4 ++
Easydict/App/AppDelegate.h | 3 --
Easydict/App/AppDelegate.m | 25 +----------
Easydict/App/Base.lproj/Main.storyboard | 14 +-----
.../Feature/Configuration/Configuration.swift | 10 +----
.../Feature/Configuration/EZConfiguration.h | 3 ++
.../Feature/Configuration/EZConfiguration.m | 10 ++---
.../Feature/StatusItem/EZMenuItemManager.m | 7 +++
Easydict/NewApp/EasydictApp.swift | 39 +++--------------
Easydict/NewApp/Utility/GlobalContext.swift | 43 +++++++++++++++++++
Easydict/NewApp/View/MenuItemView.swift | 20 ++++-----
11 files changed, 83 insertions(+), 95 deletions(-)
create mode 100644 Easydict/NewApp/Utility/GlobalContext.swift
diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj
index 0feacd3fa..dd2932c73 100644
--- a/Easydict.xcodeproj/project.pbxproj
+++ b/Easydict.xcodeproj/project.pbxproj
@@ -277,6 +277,7 @@
EA9943EE2B5353AB00EE7B97 /* WindowTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */; };
EA9943F02B5354C400EE7B97 /* ShowWindowPositionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */; };
EA9943F22B5358BF00EE7B97 /* LanguageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */; };
+ EAE3D3502B62E9DE001EE3E3 /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE3D34F2B62E9DE001EE3E3 /* GlobalContext.swift */; };
EAED41EC2B54AA920005FE0A /* ServiceConfigurationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAED41EB2B54AA920005FE0A /* ServiceConfigurationSection.swift */; };
EAED41EF2B54B1430005FE0A /* ConfigurableService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAED41EE2B54B1430005FE0A /* ConfigurableService.swift */; };
EAED41F22B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAED41F12B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift */; };
@@ -772,6 +773,7 @@
EA9943ED2B5353AB00EE7B97 /* WindowTypeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTypeExtensions.swift; sourceTree = ""; };
EA9943EF2B5354C400EE7B97 /* ShowWindowPositionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowWindowPositionExtensions.swift; sourceTree = ""; };
EA9943F12B5358BF00EE7B97 /* LanguageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageExtensions.swift; sourceTree = ""; };
+ EAE3D34F2B62E9DE001EE3E3 /* GlobalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalContext.swift; sourceTree = ""; };
EAED41EB2B54AA920005FE0A /* ServiceConfigurationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceConfigurationSection.swift; sourceTree = ""; };
EAED41EE2B54B1430005FE0A /* ConfigurableService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurableService.swift; sourceTree = ""; };
EAED41F12B54B39D0005FE0A /* OpenAIService+ConfigurableService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAIService+ConfigurableService.swift"; sourceTree = ""; };
@@ -2230,6 +2232,7 @@
EA9943DD2B534BAE00EE7B97 /* Utility */ = {
isa = PBXGroup;
children = (
+ EAE3D34F2B62E9DE001EE3E3 /* GlobalContext.swift */,
EAED41ED2B54B1390005FE0A /* Protocol */,
EA9943E62B534D7C00EE7B97 /* Extensions */,
);
@@ -2795,6 +2798,7 @@
0320C5872B29F35700861B3D /* QueryServiceRecord.swift in Sources */,
03FC699A2B39D13A0035D2DA /* EZOpenAIChatResponse.m in Sources */,
03B022FA29231FA6001C7E63 /* EZServiceTypes.m in Sources */,
+ EAE3D3502B62E9DE001EE3E3 /* GlobalContext.swift in Sources */,
EA9943F02B5354C400EE7B97 /* ShowWindowPositionExtensions.swift in Sources */,
03B0233129231FA6001C7E63 /* MMCrash.m in Sources */,
03B0232629231FA6001C7E63 /* NSAttributedString+MM.m in Sources */,
diff --git a/Easydict/App/AppDelegate.h b/Easydict/App/AppDelegate.h
index d604f4d1b..2ecaff2c6 100644
--- a/Easydict/App/AppDelegate.h
+++ b/Easydict/App/AppDelegate.h
@@ -7,10 +7,7 @@
//
#import
-#import
@interface AppDelegate : NSObject
-@property (weak) IBOutlet SPUStandardUpdaterController *updaterController;
-
@end
diff --git a/Easydict/App/AppDelegate.m b/Easydict/App/AppDelegate.m
index 6e6efd49c..ccec0a435 100644
--- a/Easydict/App/AppDelegate.m
+++ b/Easydict/App/AppDelegate.m
@@ -7,19 +7,12 @@
//
#import "AppDelegate.h"
-#import "EZMenuItemManager.h"
#import "EZShortcut.h"
#import "MMCrash.h"
-#import "EZWindowManager.h"
-#import "EZLanguageManager.h"
-#import "EZLog.h"
-#import "EZSchemeParser.h"
#import "AppDelegate+EZURLScheme.h"
-#import
-#import
#import "Easydict-Swift.h"
-@interface AppDelegate ()
+@interface AppDelegate ()
@end
@@ -99,20 +92,4 @@ - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)applica
return NO;
}
-#pragma mark - SUUpdaterDelegate
-
-- (NSString *)feedURLStringForUpdater:(SPUUpdater *)updater {
- NSString *feedURLString = @"https://raw.githubusercontent.com/tisfeng/Easydict/main/appcast.xml";
-#if DEBUG
- feedURLString = @"http://localhost:8000/appcast.xml";
-#endif
- return feedURLString;
-}
-
-#pragma mark - SPUStandardUserDriverDelegate
-
-- (BOOL)supportsGentleScheduledUpdateReminders {
- return YES;
-}
-
@end
diff --git a/Easydict/App/Base.lproj/Main.storyboard b/Easydict/App/Base.lproj/Main.storyboard
index 9df59d2cb..52009ade9 100644
--- a/Easydict/App/Base.lproj/Main.storyboard
+++ b/Easydict/App/Base.lproj/Main.storyboard
@@ -716,18 +716,8 @@ DQ
-
-
-
-
-
+
-
-
-
-
-
-
@@ -799,7 +789,7 @@ DQ