Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Easydict/Swift/Feature/Configuration/Configuration+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ extension Defaults.Keys {
let key = "EZConfiguration_kWindowFrameKey_\(windowType)"
return .init(key, default: .zero)
}

static func inputFieldCollapsed(for windowType: EZWindowType) -> Key<Bool> {
let key = "EZConfiguration_kInputFieldCollapsedKey_\(windowType.rawValue)"
return .init(key, default: false)
}
}

// MARK: - EZQueryTextType + Defaults.Serializable
Expand Down
6 changes: 6 additions & 0 deletions Easydict/objc/ViewController/View/QueryView/EZQueryView.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand Down
133 changes: 120 additions & 13 deletions Easydict/objc/ViewController/View/QueryView/EZQueryView.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,6 +31,7 @@ @interface EZQueryView () <NSTextViewDelegate, NSTextStorageDelegate>

@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;
Expand All @@ -39,6 +41,11 @@ @interface EZQueryView () <NSTextViewDelegate, NSTextStorageDelegate>
@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<MASConstraint *> *loadingViewExpandedConstraints;
@property (nonatomic, copy) NSArray<MASConstraint *> *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
Expand Down Expand Up @@ -124,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;
Expand Down Expand Up @@ -205,6 +206,45 @@ - (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];
}];

[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 {
Expand All @@ -230,6 +270,9 @@ - (NSTextField *)alertTextField {
#pragma mark - Public Methods

- (CGFloat)heightOfQueryView {
if (self.collapsed) {
return EZQueryViewExceptInputViewHeight;
}
return [self heightOfTextView] + EZQueryViewExceptInputViewHeight;
}

Expand Down Expand Up @@ -266,7 +309,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];
}
Expand Down Expand Up @@ -297,19 +340,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];
}

Expand Down Expand Up @@ -338,6 +394,16 @@ - (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;
Comment thread
nbasov marked this conversation as resolved.
Resolved
[self updateLoadingAnimationViewConstraints];
[self updateArrowButton];
[self setNeedsUpdateConstraints:YES];
}

- (void)setAssociatedWindowType:(EZWindowType)windowType {
[super setAssociatedWindowType:windowType];

Expand Down Expand Up @@ -501,6 +567,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];

Expand Down Expand Up @@ -572,8 +643,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]);
}
}

Expand Down Expand Up @@ -621,12 +691,49 @@ - (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);
}
}

/// 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<MASConstraint *> *activeConstraints = self.collapsed ? self.loadingViewCollapsedConstraints : self.loadingViewExpandedConstraints;
NSArray<MASConstraint *> *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);

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;

self.detectButton.showAutoLanguage = self.queryModel.showAutoLanguage;
self.detectButton.detectedLanguage = self.queryModel.detectedLanguage;
[self updateLoadingAnimationViewConstraints];

CGFloat height = 20;
self.detectButton.cornerRadius = height / 2;
Expand Down
Loading
Loading