From 7d3f0423f928218c3bd3780670414f2a7fddc86e Mon Sep 17 00:00:00 2001 From: Nikolay Basov <187539+nbasov@users.noreply.github.com> Date: Sat, 10 Oct 2026 16:51:39 +0200 Subject: [PATCH 1/3] feat(query): support collapsing the input text field Add a collapse arrow to the bottom-right of the input field's button bar (the clear button sits to its left), matching the result view's arrow. When collapsed, the text area is hidden and only the button bar stays visible; typing or pasting while collapsed expands it again. The collapsed state is persisted per window type, but only for select-text translation: auto select, shortcut select, and invoke queries from PopClip / macOS Services via URL scheme. Other actions (input, OCR, pasteboard, etc.) always start expanded, and toggling there only lasts for the current session. - EZQueryView reports its height via heightOfQueryView so a collapsed field no longer grows when a new window is shown with selected text, and the scroll view is not pinned to the top while collapsed to avoid constraint conflicts with the button bar. - EZWindowManager updates the action type before computing the window location, so a collapsed-state height change does not shift the window's top edge. Co-Authored-By: Claude Opus 5.5 --- .../Configuration+Extension.swift | 12 +++ .../Defaults.Keys+Extension.swift | 5 ++ .../View/QueryView/EZQueryView.h | 6 ++ .../View/QueryView/EZQueryView.m | 76 +++++++++++++++++-- .../EZBaseQueryViewController.m | 47 ++++++++++++ .../Window/WindowManager/EZWindowManager.m | 7 +- .../2026-10-10-collapsible-input-field.md | 58 ++++++++++++++ 7 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 docs/histories/2026-10/2026-10-10-collapsible-input-field.md diff --git a/Easydict/Swift/Feature/Configuration/Configuration+Extension.swift b/Easydict/Swift/Feature/Configuration/Configuration+Extension.swift index fa81e440c2..a3431cf1f1 100644 --- a/Easydict/Swift/Feature/Configuration/Configuration+Extension.swift +++ b/Easydict/Swift/Feature/Configuration/Configuration+Extension.swift @@ -75,3 +75,15 @@ extension MyConfiguration { Defaults[windowConfigurationKey(key, windowType: windowType, defaultValue: true)] } } + +// MARK: Input Field Collapsed State + +extension MyConfiguration { + func inputFieldCollapsed(windowType: EZWindowType) -> Bool { + Defaults[.inputFieldCollapsed(for: windowType)] + } + + func setInputFieldCollapsed(_ collapsed: Bool, windowType: EZWindowType) { + Defaults[.inputFieldCollapsed(for: windowType)] = collapsed + } +} diff --git a/Easydict/Swift/Feature/Configuration/Defaults.Keys+Extension.swift b/Easydict/Swift/Feature/Configuration/Defaults.Keys+Extension.swift index 1712e2ce4b..c8dc509e1f 100644 --- a/Easydict/Swift/Feature/Configuration/Defaults.Keys+Extension.swift +++ b/Easydict/Swift/Feature/Configuration/Defaults.Keys+Extension.swift @@ -261,6 +261,11 @@ extension Defaults.Keys { let key = "EZConfiguration_kWindowFrameKey_\(windowType)" return .init(key, default: .zero) } + + static func inputFieldCollapsed(for windowType: EZWindowType) -> Key { + let key = "EZConfiguration_kInputFieldCollapsedKey_\(windowType.rawValue)" + return .init(key, default: false) + } } // MARK: - EZQueryTextType + Defaults.Serializable diff --git a/Easydict/objc/ViewController/View/QueryView/EZQueryView.h b/Easydict/objc/ViewController/View/QueryView/EZQueryView.h index de81412016..aa43828827 100644 --- a/Easydict/objc/ViewController/View/QueryView/EZQueryView.h +++ b/Easydict/objc/ViewController/View/QueryView/EZQueryView.h @@ -33,6 +33,9 @@ static NSTimeInterval const EZDelayDetectTextLanguageInterval = 1.0; @property (nonatomic, assign) BOOL clearButtonHidden; @property (nonatomic, assign) BOOL isTypingChinese; +/// Whether the input text area is collapsed, only the bottom button bar is visible. +@property (nonatomic, assign) BOOL collapsed; + @property (nonatomic, copy) void (^enterActionBlock)(NSString *text); @property (nonatomic, copy) void (^playAudioBlock)(NSString *text); @@ -44,6 +47,9 @@ static NSTimeInterval const EZDelayDetectTextLanguageInterval = 1.0; @property (nonatomic, copy) void (^updateInputTextBlock)(NSString *text, CGFloat queryViewHeight); @property (nonatomic, copy) void (^selectedLanguageBlock)(EZLanguage language); +/// Called when user toggles the collapsed state, by clicking arrow button or typing in collapsed state. +@property (nonatomic, copy) void (^toggleCollapsedBlock)(BOOL collapsed); + - (CGFloat)heightOfQueryView; diff --git a/Easydict/objc/ViewController/View/QueryView/EZQueryView.m b/Easydict/objc/ViewController/View/QueryView/EZQueryView.m index 01b3508acf..eea5384395 100644 --- a/Easydict/objc/ViewController/View/QueryView/EZQueryView.m +++ b/Easydict/objc/ViewController/View/QueryView/EZQueryView.m @@ -17,6 +17,7 @@ #import "EZSchemeParser.h" #import "EZCopyButton.h" #import "NSImage+EZSymbolmage.h" +#import "NSObject+EZDarkMode.h" /// Debounce delay before auto querying while the user keeps typing. /// Kept relatively long since each query is heavy, especially for @@ -30,6 +31,7 @@ @interface EZQueryView () @property (nonatomic, strong) EZDetectLanguageButton *detectButton; @property (nonatomic, strong) EZHoverButton *clearButton; +@property (nonatomic, strong) EZHoverButton *arrowButton; @property (nonatomic, strong) NSTextField *alertTextField; @property (nonatomic, assign) CGFloat textViewMinHeight; @@ -205,6 +207,20 @@ - (void)setup { self.clearBlock(self.copiedText); } }]; + + EZHoverButton *arrowButton = [[EZHoverButton alloc] init]; + [self addSubview:arrowButton]; + self.arrowButton = arrowButton; + arrowButton.mas_key = @"queryView_arrowButton"; + + [self executeOnAppearanceChange:^(EZQueryView *view, BOOL isDarkMode) { + [view updateArrowButton]; + }]; + + [arrowButton setClickBlock:^(EZButton *_Nonnull button) { + mm_strongify(self); + [self toggleCollapsed]; + }]; } - (NSTextField *)alertTextField { @@ -230,6 +246,9 @@ - (NSTextField *)alertTextField { #pragma mark - Public Methods - (CGFloat)heightOfQueryView { + if (self.collapsed) { + return EZQueryViewExceptInputViewHeight; + } return [self heightOfTextView] + EZQueryViewExceptInputViewHeight; } @@ -266,7 +285,7 @@ - (void)setAlertTextHidden:(BOOL)hidden { self.textView.editable = hidden; }); - self.alertTextField.hidden = hidden; + self.alertTextField.hidden = hidden || self.collapsed; self.detectButton.showAutoLanguage = NO; [self updateDetectButton]; } @@ -297,19 +316,32 @@ - (void)updateConstraints { [self.scrollView mas_remakeConstraints:^(MASConstraintMaker *make) { - make.top.left.right.inset(0); + make.left.right.inset(0); + // Do not pin top in collapsed state, avoid conflicting with audio button bottom constraint when cell is taller than button bar. + if (!self.collapsed) { + make.top.inset(0); + } // Add a padding to audio button, avoid making users feel that there is still text below that has not been fully displayed. make.bottom.equalTo(self.audioButton.mas_top).offset(-EZAudioButtonInputViewTopPadding_4); - CGFloat textViewHeight = [self heightOfTextView]; - make.height.mas_greaterThanOrEqualTo(textViewHeight); + if (self.collapsed) { + make.height.mas_equalTo(0); + } else { + CGFloat textViewHeight = [self heightOfTextView]; + make.height.mas_greaterThanOrEqualTo(textViewHeight); + } }]; - [self.clearButton mas_remakeConstraints:^(MASConstraintMaker *make) { + [self.arrowButton mas_remakeConstraints:^(MASConstraintMaker *make) { make.right.bottom.equalTo(self).offset(-4); make.width.height.mas_equalTo(24); }]; + [self.clearButton mas_remakeConstraints:^(MASConstraintMaker *make) { + make.right.equalTo(self.arrowButton.mas_left).offset(-2); + make.bottom.width.height.equalTo(self.arrowButton); + }]; + [super updateConstraints]; } @@ -338,6 +370,15 @@ - (void)setQueryModel:(EZQueryModel *)model { [self updateButtonsDisplayState:queryText]; } +- (void)setCollapsed:(BOOL)collapsed { + _collapsed = collapsed; + + self.scrollView.hidden = collapsed; + self.alertTextField.hidden = collapsed || self.alertText.length == 0; + [self updateArrowButton]; + [self setNeedsUpdateConstraints:YES]; +} + - (void)setAssociatedWindowType:(EZWindowType)windowType { [super setAssociatedWindowType:windowType]; @@ -501,6 +542,11 @@ - (void)textDidChange:(NSNotification *)notification { self.queryModel.actionType = EZActionTypeInputQuery; self.queryModel.needDetectLanguage = YES; + // Expand input text area when user types in collapsed state, otherwise the typed text is invisible. + if (self.collapsed) { + [self toggleCollapsed]; + } + // textView.string has been changed, we don't need to update it again. [self updateInputText:nil]; @@ -572,8 +618,7 @@ - (void)updateInputText:(nullable NSString *)text { [self updateButtonsDisplayState:text]; if (self.updateInputTextBlock) { - CGFloat textViewHeight = [self heightOfTextView]; - self.updateInputTextBlock(text, textViewHeight + EZQueryViewExceptInputViewHeight); + self.updateInputTextBlock(text, [self heightOfQueryView]); } } @@ -621,6 +666,23 @@ - (void)updatePlaceholderTextField { self.alertTextField.hidden = hidden; } +- (void)toggleCollapsed { + self.collapsed = !self.collapsed; + MMLogInfo(@"toggle query view collapsed: %@", @(self.collapsed)); + + if (self.toggleCollapsedBlock) { + self.toggleCollapsedBlock(self.collapsed); + } +} + +- (void)updateArrowButton { + self.arrowButton.toolTip = self.collapsed ? NSLocalizedString(@"show", nil) : NSLocalizedString(@"hide", nil); + + NSImage *baseImage = self.collapsed ? [NSImage imageNamed:@"arrow-left"] : [NSImage imageNamed:@"arrow-down"]; + NSColor *tintColor = self.arrowButton.isDarkMode ? [NSColor ez_imageTintDarkColor] : [NSColor ez_imageTintLightColor]; + self.arrowButton.image = [baseImage imageWithTintColor:tintColor]; +} + - (void)updateDetectButton { // If user has designated source language, there is no meaning to detect language. self.detectButton.enabled = !self.queryModel.hasUserSourceLanguage; diff --git a/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m b/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m index e27a866d97..747d2d8d43 100644 --- a/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m +++ b/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m @@ -80,6 +80,9 @@ @interface EZBaseQueryViewController () `, + 切换时更新行高与窗口高度。 +- 仅划词翻译(`EZActionTypeAutoSelectQuery`、`EZActionTypeShortcutQuery`,以及 PopClip、系统“服务” + 等经 URL scheme 调用的 `EZActionTypeInvokeQuery`)读取并保存折叠状态; + 输入翻译、OCR、剪贴板等其他动作一律展开,期间手动折叠只在当前会话生效、不写入偏好。 + `EZBaseQueryViewController` 在 `updateActionType:` / `updateQueryTextAndParagraphStyle:actionType:` + 中按动作类型应用状态;`EZWindowManager` 在计算窗口位置前先更新动作类型,避免窗口高度变化导致 + 顶部位置偏移。 +- 折叠状态下用户直接输入或粘贴时自动展开,避免输入内容不可见;此时动作类型已变为输入查询, + 不会覆盖已保存的划词折叠偏好。 +- 修复:新窗口带入选中文本时,`updateInputText:` 按文本高度上报行高,导致折叠的输入区块被撑高且 + 按钮错位;改为上报 `heightOfQueryView`,并在折叠时不再固定滚动视图顶部,避免与按钮栏约束冲突。 + +### 设计意图 + +沿用结果区块的箭头交互和既有窗口高度更新链路,只在输入视图与查询控制器之间增加折叠状态, +不改变窗口配置开关的语义。按窗口类型保存与其他窗口级布局偏好保持一致。 + +### 验证 + +- `xcodebuild build -workspace Easydict.xcworkspace -scheme Easydict -derivedDataPath `: + 默认配置在 actool 的 `.icon` 图标导出阶段因本机环境失败(`Icon export exited with status 255`), + 与本次改动无关;附加 `'EXCLUDED_SOURCE_FILE_NAMES=*.icon' ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon` + 后构建成功,改动文件无新增警告。 +- `git diff --check`:通过。 +- 手动检查:用户在本地构建中发现新窗口折叠区块被撑高的问题,已修复并重新构建成功,待用户复测。 + +### 受影响文件 + +- `Easydict/Swift/Feature/Configuration/Configuration+Extension.swift` +- `Easydict/Swift/Feature/Configuration/Defaults.Keys+Extension.swift` +- `Easydict/objc/ViewController/View/QueryView/EZQueryView.h` +- `Easydict/objc/ViewController/View/QueryView/EZQueryView.m` +- `Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m` +- `Easydict/objc/ViewController/Window/WindowManager/EZWindowManager.m` + +### 后续事项 + +- 折叠状态下 OCR 加载动画仍以文本视图为锚点,位置可能与底部按钮重叠,可按需优化。 From 5af4fa9dd87a25ce2c8d41e290e5f1e41b903438 Mon Sep 17 00:00:00 2001 From: Nikolay Basov <187539+nbasov@users.noreply.github.com> Date: Tue, 13 Oct 2026 22:19:34 +0200 Subject: [PATCH 2/3] fix(query): sync input collapsed state with OCR and direct query starts Query in App on the current or a pinned window calls startQueryText:actionType: directly, so an invoke query did not restore the saved collapsed state, and swapping translation languages re-queried as input query without expanding the field. Apply the collapsed state there when the action type changes, keeping session-only toggles for same-type re-queries. OCR actions keep the current collapsed state, both when the window is shown and after OCR finishes. The OCR loading view keeps two constraint sets: anchored to the text view when expanded, and placed in the button bar right after the copy button, or the detect language button when it is visible, when collapsed. Co-Authored-By: Claude Opus 5.5 --- .../View/QueryView/EZQueryView.m | 57 +++++++++++++++++-- .../EZBaseQueryViewController.m | 28 ++++++++- .../2026-10-10-collapsible-input-field.md | 32 ++++++++++- 3 files changed, 107 insertions(+), 10 deletions(-) diff --git a/Easydict/objc/ViewController/View/QueryView/EZQueryView.m b/Easydict/objc/ViewController/View/QueryView/EZQueryView.m index eea5384395..0ce3c1d552 100644 --- a/Easydict/objc/ViewController/View/QueryView/EZQueryView.m +++ b/Easydict/objc/ViewController/View/QueryView/EZQueryView.m @@ -41,6 +41,11 @@ @interface EZQueryView () @property (nonatomic, assign) NSTimeInterval lastRecordTimestamp; @property (nonatomic, strong) EZSchemeParser *schemeParser; +/// Loading view is placed in the input text area when expanded, and in the button bar when collapsed. +@property (nonatomic, copy) NSArray *loadingViewExpandedConstraints; +@property (nonatomic, copy) NSArray *loadingViewCollapsedConstraints; +/// Keep loading view right of detect button in collapsed state, only active when detect button is visible. +@property (nonatomic, strong) MASConstraint *loadingViewAfterDetectButtonConstraint; @property (nonatomic, strong) id fontSizeObserver; @end @@ -126,12 +131,6 @@ - (void)setup { [self addSubview:loadingAnimationView]; self.loadingAnimationView = loadingAnimationView; - [self.loadingAnimationView mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.equalTo(textView).offset(5); - make.left.equalTo(textView).offset(10); - make.height.mas_equalTo(30); - }]; - EZAudioButton *audioButton = [[EZAudioButton alloc] init]; [self addSubview:audioButton]; self.audioButton = audioButton; @@ -221,6 +220,31 @@ - (void)setup { mm_strongify(self); [self toggleCollapsed]; }]; + + [self setupLoadingAnimationViewConstraints]; +} + +/// Do not remake loading view constraints, it would remove the internal width constraint of loading view. +- (void)setupLoadingAnimationViewConstraints { + self.loadingViewExpandedConstraints = [self.loadingAnimationView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(self.textView).offset(5); + make.left.equalTo(self.textView).offset(10); + make.height.mas_equalTo(30); + }]; + + // Place loading view right after the left buttons of button bar. + CGFloat leftPadding = 6; + self.loadingViewCollapsedConstraints = [self.loadingAnimationView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.greaterThanOrEqualTo(self.textCopyButton.mas_right).offset(leftPadding); + make.left.equalTo(self.textCopyButton.mas_right).offset(leftPadding).priorityLow(); + make.centerY.height.equalTo(self.textCopyButton); + }]; + + [self.loadingAnimationView mas_makeConstraints:^(MASConstraintMaker *make) { + self.loadingViewAfterDetectButtonConstraint = make.left.greaterThanOrEqualTo(self.detectButton.mas_right).offset(leftPadding); + }]; + + [self updateLoadingAnimationViewConstraints]; } - (NSTextField *)alertTextField { @@ -375,6 +399,7 @@ - (void)setCollapsed:(BOOL)collapsed { self.scrollView.hidden = collapsed; self.alertTextField.hidden = collapsed || self.alertText.length == 0; + [self updateLoadingAnimationViewConstraints]; [self updateArrowButton]; [self setNeedsUpdateConstraints:YES]; } @@ -675,6 +700,25 @@ - (void)toggleCollapsed { } } +/// Move loading view to the button bar while collapsed, right after the left buttons, so loading progress is visible without expanding input text area. +- (void)updateLoadingAnimationViewConstraints { + NSArray *activeConstraints = self.collapsed ? self.loadingViewCollapsedConstraints : self.loadingViewExpandedConstraints; + NSArray *inactiveConstraints = self.collapsed ? self.loadingViewExpandedConstraints : self.loadingViewCollapsedConstraints; + for (MASConstraint *constraint in inactiveConstraints) { + [constraint deactivate]; + } + for (MASConstraint *constraint in activeConstraints) { + [constraint activate]; + } + + BOOL isDetectButtonVisible = self.detectButton.showAutoLanguage || ![self.detectButton.detectedLanguage isEqualToString:EZLanguageAuto]; + if (self.collapsed && isDetectButtonVisible) { + [self.loadingViewAfterDetectButtonConstraint activate]; + } else { + [self.loadingViewAfterDetectButtonConstraint deactivate]; + } +} + - (void)updateArrowButton { self.arrowButton.toolTip = self.collapsed ? NSLocalizedString(@"show", nil) : NSLocalizedString(@"hide", nil); @@ -689,6 +733,7 @@ - (void)updateDetectButton { self.detectButton.showAutoLanguage = self.queryModel.showAutoLanguage; self.detectButton.detectedLanguage = self.queryModel.detectedLanguage; + [self updateLoadingAnimationViewConstraints]; CGFloat height = 20; self.detectButton.cornerRadius = height / 2; diff --git a/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m b/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m index 747d2d8d43..40a4a6223a 100644 --- a/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m +++ b/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m @@ -472,7 +472,7 @@ - (void)startQueryText:(NSString *)text actionType:(EZActionType)actionType { } self.inputText = text; - self.queryModel.actionType = actionType; + [self switchActionType:actionType]; self.queryView.isTypingChinese = NO; if ([self handleEasydictScheme:text]) { @@ -530,6 +530,7 @@ - (void)startOCRImage:(NSImage *)image self.queryModel.ocrImage = image; self.queryView.isTypingChinese = NO; + // Keep collapsed state while OCR is loading, the loading view is shown in the button bar of collapsed input field. [self.queryView startLoadingAnimation:YES]; mm_weakify(self); @@ -709,7 +710,7 @@ - (void)toggleTranslationLanguages { self.queryModel.userTargetLanguage = toLanguage; self.inputText = translatedText; self.queryModel.ocrImage = nil; - self.queryModel.actionType = EZActionTypeInputQuery; + [self switchActionType:EZActionTypeInputQuery]; } } @@ -784,6 +785,16 @@ - (void)updateActionType:(EZActionType)actionType { [self updateInputFieldCollapsedWithActionType:actionType]; } +/// Update action type in current window, only apply input field collapsed state when action type changes, +/// so that re-querying with the same action type keeps the collapsed state toggled in current session. +- (void)switchActionType:(EZActionType)actionType { + BOOL isChanged = ![self.queryModel.actionType isEqualToString:actionType]; + self.queryModel.actionType = actionType; + if (isChanged) { + [self updateInputFieldCollapsedWithActionType:actionType]; + } +} + #pragma mark - Input Field Collapsed State /// Select text translation, including invoke query from PopClip or macOS Services via URL scheme. @@ -793,8 +804,19 @@ - (BOOL)isSelectTextActionType:(EZActionType)actionType { || actionType == EZActionTypeInvokeQuery; } -/// Restore saved collapsed state for select text translation, other actions always expand input field. +/// OCR translation, the input text is recognized from image, so users may not need to see it. +- (BOOL)isOCRActionType:(EZActionType)actionType { + return actionType == EZActionTypeOCRQuery + || actionType == EZActionTypeScreenshotOCR + || actionType == EZActionTypePasteboardOCR; +} + +/// Restore saved collapsed state for select text translation, keep current state for OCR, other actions always expand input field. - (void)updateInputFieldCollapsedWithActionType:(EZActionType)actionType { + if ([self isOCRActionType:actionType]) { + return; + } + BOOL collapsed = NO; if ([self isSelectTextActionType:actionType]) { collapsed = [self.config inputFieldCollapsedWithWindowType:self.windowType]; diff --git a/docs/histories/2026-10/2026-10-10-collapsible-input-field.md b/docs/histories/2026-10/2026-10-10-collapsible-input-field.md index f9daebc424..0da24848a8 100644 --- a/docs/histories/2026-10/2026-10-10-collapsible-input-field.md +++ b/docs/histories/2026-10/2026-10-10-collapsible-input-field.md @@ -29,6 +29,26 @@ 不会覆盖已保存的划词折叠偏好。 - 修复:新窗口带入选中文本时,`updateInputText:` 按文本高度上报行高,导致折叠的输入区块被撑高且 按钮错位;改为上报 `heightOfQueryView`,并在折叠时不再固定滚动视图顶部,避免与按钮栏约束冲突。 +- 修复(Review 反馈):OCR 加载动画是 `EZQueryView` 的子视图并锚定在文本视图上,折叠时不会随 + 滚动视图隐藏,其计时器还会反复将其设为可见,导致点状动画覆盖底部按钮或溢出到下一单元格。 + `startOCRImage:actionType:autoQuery:` 在开始加载前先按动作类型展开输入框;`EZQueryView` 记录 + 加载状态,折叠时停止动画,加载仍在进行时展开则恢复动画。 +- 调整(用户反馈):OCR 不再为显示加载动画而展开折叠的输入框。`EZQueryView` 为加载视图维护两组 + 约束:展开时锚定在文本视图左上角,折叠时位于按钮栏中清除按钮左侧并与箭头按钮垂直居中, + `setCollapsed:` 切换约束;不使用 `mas_remakeConstraints`,以免移除加载视图内部的宽度约束。 + OCR 完成后仍由 `updateQueryTextAndParagraphStyle:actionType:` 按动作类型应用折叠状态。 +- 调整(用户反馈):OCR 完成后也保持折叠。新增 `isOCRActionType:`(`EZActionTypeOCRQuery`、 + `EZActionTypeScreenshotOCR`、`EZActionTypePasteboardOCR`),`updateInputFieldCollapsedWithActionType:` + 对 OCR 动作不改变当前折叠状态(既不展开也不读取偏好),手动切换仍只在当前会话生效。 +- 调整(用户反馈):折叠时加载视图改为位于按钮栏左侧按钮之后:默认紧跟复制按钮,语言检测按钮 + 可见时(`showAutoLanguage` 或检测语言非 auto)通过单独约束排在其右侧;`updateDetectButton` + 更新检测语言后同步该约束。 +- 修复(Review 反馈):结果区右键 “Query in App” 命中当前窗口或已固定窗口时直接调用 + `startQueryText:actionType:`,不经过折叠状态同步,`EZActionTypeInvokeQuery` 不会恢复已保存的 + 折叠状态;互换翻译语言以 `EZActionTypeInputQuery` 重新查询时,折叠的输入框也不会展开。 + 新增 `switchActionType:`,在 `startQueryText:actionType:` 与 `toggleTranslationLanguages` 中使用, + 仅在动作类型变化时应用折叠状态,同类型重新查询(重试、切换语言、点击结果中的单词)保留当前会话 + 中手动切换的状态。 ### 设计意图 @@ -43,6 +63,16 @@ 后构建成功,改动文件无新增警告。 - `git diff --check`:通过。 - 手动检查:用户在本地构建中发现新窗口折叠区块被撑高的问题,已修复并重新构建成功,待用户复测。 +- OCR 加载动画修复:同一构建命令(附加上述图标参数)构建成功;`git diff --check` 通过; + 未进行手动 UI 验证。 +- OCR 加载视图移至折叠按钮栏:同一构建命令(附加上述图标参数)构建成功,改动文件无新增警告; + `git diff --check` 通过;未进行手动 UI 验证。 +- OCR 保持折叠状态:同一构建命令(附加上述图标参数)构建成功,改动文件无新增警告; + `git diff --check` 通过;未进行手动 UI 验证。 +- 加载视图移至左侧按钮之后:同一构建命令(附加上述图标参数)构建成功,改动文件无新增警告; + `git diff --check` 通过;未进行手动 UI 验证。 +- Query in App / 互换语言修复:同一构建命令(附加上述图标参数)构建成功;`git diff --check` 通过; + 未进行手动 UI 验证。 ### 受影响文件 @@ -55,4 +85,4 @@ ### 后续事项 -- 折叠状态下 OCR 加载动画仍以文本视图为锚点,位置可能与底部按钮重叠,可按需优化。 +- None From f4dcce1dfd48d8f36ca419f6b729f8ce150e8849 Mon Sep 17 00:00:00 2001 From: Nikolay Basov <187539+nbasov@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:21:14 +0100 Subject: [PATCH 3/3] fix(query): replace removed updateQueryCellWithAnimation: calls The dev merge brought in #1327, which dropped the animated row-update API in favor of immediate row layout with window-only animation. Use updateQueryCell for both input-field collapse paths. Co-Authored-By: Claude Opus 5.5 --- .../Window/BaseQueryWindow/EZBaseQueryViewController.m | 4 ++-- docs/histories/2026-10/2026-10-10-collapsible-input-field.md | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m b/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m index f3117935e2..8c15740916 100644 --- a/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m +++ b/Easydict/objc/ViewController/Window/BaseQueryWindow/EZBaseQueryViewController.m @@ -845,7 +845,7 @@ - (void)updateInputFieldCollapsedWithActionType:(EZActionType)actionType { self.isInputFieldCollapsed = collapsed; self.queryView.collapsed = collapsed; self.queryModel.queryViewHeight = self.queryView ? [self.queryView heightOfQueryView] : [self miniQueryViewHeight]; - [self updateQueryCellWithAnimation:NO]; + [self updateQueryCell]; } - (void)scrollToEndOfTextView { @@ -1534,7 +1534,7 @@ - (EZQueryView *)createQueryView { } self.queryModel.queryViewHeight = [self.queryView heightOfQueryView]; - [self updateQueryCellWithAnimation:YES]; + [self updateQueryCell]; }]; [queryView setUpdateInputTextBlock:^(NSString *text, CGFloat queryViewHeight) { diff --git a/docs/histories/2026-10/2026-10-10-collapsible-input-field.md b/docs/histories/2026-10/2026-10-10-collapsible-input-field.md index 0da24848a8..cbc98691c9 100644 --- a/docs/histories/2026-10/2026-10-10-collapsible-input-field.md +++ b/docs/histories/2026-10/2026-10-10-collapsible-input-field.md @@ -49,6 +49,8 @@ 新增 `switchActionType:`,在 `startQueryText:actionType:` 与 `toggleTranslationLanguages` 中使用, 仅在动作类型变化时应用折叠状态,同类型重新查询(重试、切换语言、点击结果中的单词)保留当前会话 中手动切换的状态。 +- 修复(合并 `dev` 后的 Review 反馈):`dev` 中 #1327 移除了 `updateQueryCellWithAnimation:`, + 行高改为立即提交、仅窗口动画;折叠切换的两处调用改为 `updateQueryCell`。 ### 设计意图 @@ -73,6 +75,8 @@ `git diff --check` 通过;未进行手动 UI 验证。 - Query in App / 互换语言修复:同一构建命令(附加上述图标参数)构建成功;`git diff --check` 通过; 未进行手动 UI 验证。 +- 合并 `dev` 后替换已移除的 `updateQueryCellWithAnimation:`:默认构建命令构建成功(无需附加图标参数); + `git diff --check` 通过;未进行手动 UI 验证。 ### 受影响文件