diff --git a/Easydict.xcodeproj/project.pbxproj b/Easydict.xcodeproj/project.pbxproj index 67d3a9090..098e63510 100644 --- a/Easydict.xcodeproj/project.pbxproj +++ b/Easydict.xcodeproj/project.pbxproj @@ -238,6 +238,7 @@ 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 */; }; + 0AC8A84F2B6DFDD4006DA5CC /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 0AC8A84E2B6DFDD4006DA5CC /* SettingsAccess */; }; 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 */; }; @@ -834,6 +835,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0AC8A84F2B6DFDD4006DA5CC /* SettingsAccess in Frameworks */, 03022F192B3591AE00B63209 /* GoogleGenerativeAI in Frameworks */, 2721E4D02AFE920700A059AC /* Alamofire in Frameworks */, 03022F1F2B36CF3100B63209 /* SwiftShell in Frameworks */, @@ -2484,6 +2486,7 @@ EA3B81FB2B52555C004C0E8B /* Defaults */, 967712E92B5B913600105E0F /* KeyHolder */, 03022F182B3591AE00B63209 /* GoogleGenerativeAI */, + 0AC8A84E2B6DFDD4006DA5CC /* SettingsAccess */, ); productName = Bob; productReference = C99EEB182385796700FEE666 /* Easydict-debug.app */; @@ -2545,6 +2548,7 @@ EA3B81FA2B52555C004C0E8B /* XCRemoteSwiftPackageReference "Defaults" */, 967712E82B5B913600105E0F /* XCRemoteSwiftPackageReference "KeyHolder" */, 03022F172B3591AE00B63209 /* XCRemoteSwiftPackageReference "generative-ai-swift" */, + 0AC8A84D2B6DFDD4006DA5CC /* XCRemoteSwiftPackageReference "SettingsAccess" */, ); productRefGroup = C99EEB192385796700FEE666 /* Products */; projectDirPath = ""; @@ -3538,6 +3542,14 @@ minimumVersion = 1.8.0; }; }; + 0AC8A84D2B6DFDD4006DA5CC /* XCRemoteSwiftPackageReference "SettingsAccess" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/orchetect/SettingsAccess"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.4.0; + }; + }; 2721E4CE2AFE920700A059AC /* XCRemoteSwiftPackageReference "Alamofire" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Alamofire/Alamofire.git"; @@ -3640,6 +3652,11 @@ package = 03FD68B92B1DC59600FD388E /* XCRemoteSwiftPackageReference "CryptoSwift" */; productName = CryptoSwift; }; + 0AC8A84E2B6DFDD4006DA5CC /* SettingsAccess */ = { + isa = XCSwiftPackageProductDependency; + package = 0AC8A84D2B6DFDD4006DA5CC /* XCRemoteSwiftPackageReference "SettingsAccess" */; + productName = SettingsAccess; + }; 2721E4CF2AFE920700A059AC /* Alamofire */ = { isa = XCSwiftPackageProductDependency; package = 2721E4CE2AFE920700A059AC /* XCRemoteSwiftPackageReference "Alamofire" */; diff --git a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0d2cecd07..6e22eb216 100644 --- a/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Easydict.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -234,6 +234,15 @@ "version" : "2.4.0" } }, + { + "identity" : "settingsaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/orchetect/SettingsAccess", + "state" : { + "revision" : "0fd73c8b5892e88acb13adb7f36a4ba9293a0061", + "version" : "1.4.0" + } + }, { "identity" : "snapkit", "kind" : "remoteSourceControl", diff --git a/Easydict/App/Localizable.xcstrings b/Easydict/App/Localizable.xcstrings index ded1bbed0..504793858 100644 --- a/Easydict/App/Localizable.xcstrings +++ b/Easydict/App/Localizable.xcstrings @@ -652,22 +652,6 @@ } } }, - "clear_input" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clear Input:" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "清空查询内容:" - } - } - } - }, "clear_input_when_translating" : { "localizations" : { "en" : { @@ -1565,6 +1549,40 @@ } } }, + "keep_prev_result_when_selected_text_is_empty" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keep previous result when selected text is empty" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "划词翻译未选中文本时,保留上次结果" + } + } + } + }, + "keep_result" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keep Result:" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "保留结果:" + } + } + } + }, "language_detect_optimize" : { "localizations" : { "en" : { @@ -2364,6 +2382,23 @@ } } }, + "select_query_text_when_window_activate" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select query text when window activate" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "打开窗口时自动选中查询文本" + } + } + } + }, "select_translate" : { "localizations" : { "en" : { diff --git a/Easydict/Feature/Configuration/Configuration.swift b/Easydict/Feature/Configuration/Configuration.swift index 7f7c6dc53..774f4a193 100644 --- a/Easydict/Feature/Configuration/Configuration.swift +++ b/Easydict/Feature/Configuration/Configuration.swift @@ -141,6 +141,12 @@ let kHideMenuBarIconKey = "EZConfiguration_kHideMenuBarIconKey" @DefaultsWrapper(.clearInput) var clearInput: Bool + @DefaultsWrapper(.keepPrevResultWhenEmpty) + var keepPrevResultWhenEmpty: Bool + + @DefaultsWrapper(.selectQueryTextWhenWindowActivate) + var selectQueryTextWhenWindowActivate: Bool + var disabledAutoSelect: Bool = false var isRecordingSelectTextShortcutKey: Bool = false diff --git a/Easydict/Feature/Configuration/EZConfiguration.h b/Easydict/Feature/Configuration/EZConfiguration.h index 266797563..5cf592d6c 100644 --- a/Easydict/Feature/Configuration/EZConfiguration.h +++ b/Easydict/Feature/Configuration/EZConfiguration.h @@ -73,6 +73,7 @@ typedef NS_ENUM(NSUInteger, EZAppearenceType) { @property (nonatomic, assign) BOOL allowCrashLog; @property (nonatomic, assign) BOOL allowAnalytics; @property (nonatomic, assign) BOOL clearInput; +@property (nonatomic, assign) BOOL keepPrevResult; // TODO: Need to move them. These are read/write properties only and will not be stored locally, only for external use. /// Only use when showing NSOpenPanel to select disabled apps. diff --git a/Easydict/Feature/Configuration/EZConfiguration.m b/Easydict/Feature/Configuration/EZConfiguration.m index a3104745a..407f9883c 100644 --- a/Easydict/Feature/Configuration/EZConfiguration.m +++ b/Easydict/Feature/Configuration/EZConfiguration.m @@ -52,6 +52,7 @@ static NSString *const kAllowCrashLogKey = @"EZConfiguration_kAllowCrashLogKey"; static NSString *const kAllowAnalyticsKey = @"EZConfiguration_kAllowAnalyticsKey"; static NSString *const kClearInputKey = @"EZConfiguration_kClearInputKey"; +static NSString *const kKeepPrevResultKey = @"EZConfiguration_kKeepPrevResultKey"; static NSString *const kTranslationControllerFontKey = @"EZConfiguration_kTranslationControllerFontKey"; static NSString *const kApperanceKey = @"EZConfiguration_kApperanceKey"; @@ -126,6 +127,7 @@ - (void)setup { self.allowCrashLog = [NSUserDefaults mm_readBool:kAllowCrashLogKey defaultValue:YES]; self.allowAnalytics = [NSUserDefaults mm_readBool:kAllowAnalyticsKey defaultValue:YES]; self.clearInput = [NSUserDefaults mm_readBool:kClearInputKey defaultValue:NO]; + self.keepPrevResult = [NSUserDefaults mm_readBool:kKeepPrevResultKey defaultValue:YES]; self.fontSizes = @[@(1), @(1.1), @(1.2), @(1.3), @(1.4)]; [[NSUserDefaults standardUserDefaults]registerDefaults:@{kTranslationControllerFontKey: @(0)}]; @@ -432,6 +434,14 @@ - (void)setClearInput:(BOOL)clearInput { [self logSettings:@{@"clear_input" : @(clearInput)}]; } +- (void)setKeepPrevResult:(BOOL)keepPrevResult { + _keepPrevResult = keepPrevResult; + + [NSUserDefaults mm_write:@(keepPrevResult) forKey:kKeepPrevResultKey]; + + [self logSettings:@{@"keep_prev_result": @(keepPrevResult)}]; +} + - (void)setFontSizeIndex:(NSInteger)fontSizeIndex { NSInteger targetIndex = MIN(_fontSizes.count-1, MAX(fontSizeIndex, 0)); diff --git a/Easydict/Feature/PerferenceWindow/EZSettingViewController.m b/Easydict/Feature/PerferenceWindow/EZSettingViewController.m index 8715ec11e..a2d28b21e 100644 --- a/Easydict/Feature/PerferenceWindow/EZSettingViewController.m +++ b/Easydict/Feature/PerferenceWindow/EZSettingViewController.m @@ -74,8 +74,10 @@ @interface EZSettingViewController () @property (nonatomic, strong) NSTextField *playAudioLabel; @property (nonatomic, strong) NSButton *autoPlayAudioButton; -@property (nonatomic, strong) NSTextField *clearInputLabel; +@property (nonatomic, strong) NSTextField *inputFieldLabel; @property (nonatomic, strong) NSButton *clearInputButton; +@property (nonatomic, strong) NSButton *keepPrevResultButton; +@property (nonatomic, strong) NSButton *selectQueryTextWhenWindowActivateButton; @property (nonatomic, strong) NSTextField *autoQueryLabel; @property (nonatomic, strong) NSButton *autoQueryOCRTextButton; @@ -395,14 +397,23 @@ - (void)setupUI { self.autoPlayAudioButton = [NSButton checkboxWithTitle:autoPlayAudioTitle target:self action:@selector(autoPlayAudioButtonClicked:)]; [self.contentView addSubview:self.autoPlayAudioButton]; - NSTextField *clearInputLabel = [NSTextField labelWithString:NSLocalizedString(@"clear_input", nil)]; - clearInputLabel.font = font; - [self.contentView addSubview:clearInputLabel]; - self.clearInputLabel = clearInputLabel; + NSString *inputFieldLabelTitle = [NSString stringWithFormat:@"%@:", NSLocalizedString(@"setting.general.input.header", nil)]; + NSTextField *inputFieldLabel = [NSTextField labelWithString:inputFieldLabelTitle]; + inputFieldLabel.font = font; + [self.contentView addSubview:inputFieldLabel]; + self.inputFieldLabel = inputFieldLabel; NSString *clearInputTitle = NSLocalizedString(@"clear_input_when_translating", nil); self.clearInputButton = [NSButton checkboxWithTitle:clearInputTitle target:self action:@selector(clearInputButtonClicked:)]; [self.contentView addSubview:self.clearInputButton]; + + NSString *keepPrevResultTitle = NSLocalizedString(@"keep_prev_result_when_selected_text_is_empty", nil); + self.keepPrevResultButton = [NSButton checkboxWithTitle:keepPrevResultTitle target:self action:@selector(keepPrevResultButtonClicked:)]; + [self.contentView addSubview:self.keepPrevResultButton]; + + NSString *selectQueryTextWhenWindowActivateTitle = NSLocalizedString(@"select_query_text_when_window_activate", nil); + self.selectQueryTextWhenWindowActivateButton = [NSButton checkboxWithTitle:selectQueryTextWhenWindowActivateTitle target:self action:@selector(selectQueryTextWhenWindowActivateButtonClicked:)]; + [self.contentView addSubview:self.selectQueryTextWhenWindowActivateButton]; NSTextField *autoQueryLabel = [NSTextField labelWithString:NSLocalizedString(@"auto_query", nil)]; autoQueryLabel.font = font; @@ -546,6 +557,8 @@ - (void)setupUI { self.autoPlayAudioButton.mm_isOn = self.config.autoPlayAudio; self.clearInputButton.mm_isOn = self.config.clearInput; + self.keepPrevResultButton.mm_isOn = self.config.keepPrevResultWhenEmpty; + self.selectQueryTextWhenWindowActivateButton.mm_isOn = self.config.selectQueryTextWhenWindowActivate; self.launchAtStartupButton.mm_isOn = self.config.launchAtStartup; self.hideMainWindowButton.mm_isOn = self.config.hideMainWindow; self.autoQueryOCRTextButton.mm_isOn = self.config.autoQueryOCRText; @@ -747,19 +760,26 @@ - (void)updateViewConstraints { }]; - [self.clearInputLabel mas_remakeConstraints:^(MASConstraintMaker *make) { + [self.inputFieldLabel mas_remakeConstraints:^(MASConstraintMaker *make) { make.right.equalTo(self.autoGetSelectedTextLabel); make.top.equalTo(self.autoPlayAudioButton.mas_bottom).offset(self.verticalPadding); }]; [self.clearInputButton mas_remakeConstraints:^(MASConstraintMaker *make) { - make.left.equalTo(self.clearInputLabel.mas_right).offset(self.horizontalPadding); - make.centerY.equalTo(self.clearInputLabel); + make.left.equalTo(self.inputFieldLabel.mas_right).offset(self.horizontalPadding); + make.centerY.equalTo(self.inputFieldLabel); + }]; + [self.keepPrevResultButton mas_remakeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.clearInputButton); + make.top.equalTo(self.clearInputButton.mas_bottom).offset(self.verticalPadding); + }]; + [self.selectQueryTextWhenWindowActivateButton mas_remakeConstraints:^(MASConstraintMaker *make) { + make.left.equalTo(self.clearInputButton); + make.top.equalTo(self.keepPrevResultButton.mas_bottom).offset(self.verticalPadding); }]; - [self.autoQueryLabel mas_remakeConstraints:^(MASConstraintMaker *make) { make.right.equalTo(self.autoGetSelectedTextLabel); - make.top.equalTo(self.clearInputButton.mas_bottom).offset(self.verticalPadding); + make.top.equalTo(self.selectQueryTextWhenWindowActivateButton.mas_bottom).offset(self.verticalPadding); }]; [self.autoQueryOCRTextButton mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.autoQueryLabel.mas_right).offset(self.horizontalPadding); @@ -967,6 +987,14 @@ - (void)clearInputButtonClicked:(NSButton *)sender { self.config.clearInput = sender.mm_isOn; } +- (void)keepPrevResultButtonClicked:(NSButton *)sender { + self.config.keepPrevResultWhenEmpty = sender.mm_isOn; +} + +- (void)selectQueryTextWhenWindowActivateButtonClicked:(NSButton *)sender { + self.config.selectQueryTextWhenWindowActivate = sender.mm_isOn; +} + - (void)autoCopySelectedTextButtonClicked:(NSButton *)sender { self.config.autoCopySelectedText = sender.mm_isOn; } diff --git a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m index c1bb5cc13..44a5a5e5b 100644 --- a/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m +++ b/Easydict/Feature/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m @@ -527,6 +527,9 @@ - (void)focusInputTextView { [NSApp activateIgnoringOtherApps:YES]; [self.baseQueryWindow makeFirstResponder:self.queryView.textView]; + if (Configuration.shared.selectQueryTextWhenWindowActivate) { + self.queryView.textView.selectedRange = NSMakeRange(0, self.inputText.length); + } } } diff --git a/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m b/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m index dd2c18664..1b098b8d5 100644 --- a/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m +++ b/Easydict/Feature/ViewController/Window/WindowManager/EZWindowManager.m @@ -435,11 +435,6 @@ - (void)showFloatingWindow:(EZBaseQueryWindow *)window atPoint:(CGPoint)point { [window.queryViewController focusInputTextView]; [self updateFloatingWindowType:window.windowType]; - - // mainWindow has been ordered out before, so we need to order back. - if ([EZMainQueryWindow isAlive]) { - [self.mainWindow orderBack:nil]; - } } - (void)updateFloatingWindowType:(EZWindowType)floatingWindowType { @@ -694,9 +689,14 @@ - (void)selectTextTranslate { NSLog(@"selectTextTranslate windowType: %@", @(windowType)); self.eventMonitor.actionType = EZActionTypeShortcutQuery; [self.eventMonitor getSelectedText:^(NSString *_Nullable text) { - // If text is nil, currently, we choose to clear input. - self.selectedText = [text trim] ?: @""; self.actionType = self.eventMonitor.actionType; + + // Clear query if text is nil and user don't want to keep the last result. + if (!text && !Configuration.shared.keepPrevResultWhenEmpty) { + text = @""; + } + self.selectedText = [text trim]; + [self showFloatingWindowType:windowType queryText:self.selectedText]; }]; } diff --git a/Easydict/NewApp/Configuration/Configuration+Defaults.swift b/Easydict/NewApp/Configuration/Configuration+Defaults.swift index 43220d130..4d4b7a334 100644 --- a/Easydict/NewApp/Configuration/Configuration+Defaults.swift +++ b/Easydict/NewApp/Configuration/Configuration+Defaults.swift @@ -53,6 +53,8 @@ extension Defaults.Keys { 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 keepPrevResultWhenEmpty = Key("EZConfiguration_kKeepPrevResultKey", default: true) + static let selectQueryTextWhenWindowActivate = Key("EZConfiguration_kSelectQueryTextWhenWindowActivate", default: false) static let enableBetaNewApp = Key("EZConfiguration_kEnableBetaNewAppKey", default: false) static let enableBetaFeature = Key("EZBetaFeatureKey", default: false) diff --git a/Easydict/NewApp/View/MenuItemView.swift b/Easydict/NewApp/View/MenuItemView.swift index 2c5a6aca4..404b691bb 100644 --- a/Easydict/NewApp/View/MenuItemView.swift +++ b/Easydict/NewApp/View/MenuItemView.swift @@ -6,6 +6,7 @@ // Copyright © 2023 izual. All rights reserved. // +import SettingsAccess import Sparkle import SwiftUI import ZipArchive @@ -92,10 +93,18 @@ struct MenuItemView: View { @ViewBuilder private var settingItem: some View { if #available(macOS 14.0, *) { - SettingsLink() + SettingsLink { + Text("Settings...") + } preAction: { + NSLog("打开设置") + NSApp.activate(ignoringOtherApps: true) + } postAction: { + // nothing to do + } } else { Button("Settings...") { NSLog("打开设置") + NSApp.activate(ignoringOtherApps: true) NSApplication.shared.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) } } diff --git a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift index a1e59e04e..cfbf2f2dd 100644 --- a/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift +++ b/Easydict/NewApp/View/SettingView/Tabs/GeneralTab.swift @@ -78,6 +78,8 @@ struct GeneralTab: View { Section { Toggle("clear_input_when_translating", isOn: $clearInput) + Toggle("keep_prev_result_when_selected_text_is_empty", isOn: $keepPrevResultWhenEmpty) + Toggle("select_query_text_when_window_activate", isOn: $selectQueryTextWhenWindowActivate) } header: { Text("setting.general.input.header") } @@ -161,6 +163,8 @@ struct GeneralTab: View { @Default(.adjustPopButtonOrigin) private var adjustPopButtonOrigin @Default(.clearInput) private var clearInput + @Default(.keepPrevResultWhenEmpty) private var keepPrevResultWhenEmpty + @Default(.selectQueryTextWhenWindowActivate) private var selectQueryTextWhenWindowActivate @Default(.disableEmptyCopyBeep) private var disableEmptyCopyBeep @Default(.autoPlayAudio) private var autoPlayAudio