Files
keyboard/CustomKeyboard/View/KBToolBar.m

368 lines
14 KiB
Mathematica
Raw Normal View History

2025-10-27 19:42:27 +08:00
//
// KBToolBar.m
// CustomKeyboard
//
2025-10-28 14:30:03 +08:00
// Created by Mac on 2025/10/28.
2025-10-27 19:42:27 +08:00
//
#import "KBToolBar.h"
2025-11-05 20:11:10 +08:00
#import "KBResponderUtils.h" // UIInputViewController
2025-12-19 19:21:08 +08:00
#import "KBBackspaceUndoManager.h"
#import "KBSkinManager.h"
2025-10-27 19:42:27 +08:00
2025-10-28 10:18:10 +08:00
@interface KBToolBar ()
@property (nonatomic, strong) UIView *leftContainer;
@property (nonatomic, strong) NSArray<UIButton *> *leftButtonsInternal;
//@property (nonatomic, strong) UIButton *settingsButtonInternal;
2025-11-05 20:11:10 +08:00
@property (nonatomic, strong) UIButton *globeButtonInternal; //
2025-12-19 19:21:08 +08:00
@property (nonatomic, strong) UIButton *undoButtonInternal; //
@property (nonatomic, assign) BOOL kbNeedsInputModeSwitchKey;
@property (nonatomic, assign) BOOL kbUndoVisible;
2025-10-28 10:18:10 +08:00
@end
2025-10-27 19:42:27 +08:00
@implementation KBToolBar
static NSString * const kKBAIKeyIdentifier = @"ai";
static const CGFloat kKBAIButtonWidth = 36.0f;
static const CGFloat kKBAIButtonHeight = 36.0f;
2025-10-27 21:11:28 +08:00
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
2025-10-28 10:18:10 +08:00
self.backgroundColor = [UIColor clearColor];
2025-12-19 21:36:11 +08:00
_leftButtonTitles = @[@"AI"]; //
2025-10-28 10:18:10 +08:00
[self setupUI];
2025-12-19 19:21:08 +08:00
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(kb_undoStateChanged)
name:KBBackspaceUndoStateDidChangeNotification
object:nil];
2025-10-27 21:11:28 +08:00
}
return self;
2025-10-27 19:42:27 +08:00
}
2025-10-27 21:11:28 +08:00
2025-12-19 19:21:08 +08:00
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
2025-10-27 21:11:28 +08:00
2025-10-28 10:18:10 +08:00
#pragma mark - Public
- (NSArray<UIButton *> *)leftButtons {
return self.leftButtonsInternal;
}
2025-12-19 19:21:08 +08:00
- (UIButton *)undoButton {
return self.undoButtonInternal;
}
//- (UIButton *)settingsButton {
// return self.settingsButtonInternal;
//}
2025-10-28 10:18:10 +08:00
- (void)setLeftButtonTitles:(NSArray<NSString *> *)leftButtonTitles {
_leftButtonTitles = [leftButtonTitles copy];
// Update titles if buttons already exist
[self.leftButtonsInternal enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx < self.leftButtonTitles.count) {
[obj setTitle:self.leftButtonTitles[idx] forState:UIControlStateNormal];
}
}];
[self kb_updateAIButtonAppearance];
2025-10-28 10:18:10 +08:00
}
#pragma mark -
- (void)setupUI {
[self addSubview:self.leftContainer];
// [self addSubview:self.settingsButtonInternal];
2025-11-05 20:11:10 +08:00
[self addSubview:self.globeButtonInternal];
2025-12-19 19:21:08 +08:00
[self addSubview:self.undoButtonInternal];
2025-10-28 10:18:10 +08:00
//
// [self.settingsButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
// make.right.equalTo(self.mas_right).offset(-12);
// make.centerY.equalTo(self.mas_centerY);
// make.width.height.mas_equalTo(32);
// }];
2025-10-28 10:18:10 +08:00
2025-11-05 20:11:10 +08:00
//
[self.globeButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.mas_left).offset(12);
make.centerY.equalTo(self.mas_centerY);
make.width.height.mas_equalTo(32);
}];
2025-12-19 19:21:08 +08:00
//
[self.undoButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.mas_right).offset(-12);
2025-10-28 10:18:10 +08:00
make.centerY.equalTo(self.mas_centerY);
make.height.mas_equalTo(32);
}];
2025-12-19 19:21:08 +08:00
[self kb_updateLeftContainerConstraints];
// =
NSMutableArray<UIButton *> *buttons = [NSMutableArray arrayWithCapacity:_leftButtonTitles.count];
2025-10-28 10:18:10 +08:00
UIView *previous = nil;
for (NSInteger i = 0; i < _leftButtonTitles.count; i++) {
2025-10-28 10:18:10 +08:00
UIButton *btn = [self buildActionButtonAtIndex:i];
[self.leftContainer addSubview:btn];
[buttons addObject:btn];
2025-10-28 10:18:10 +08:00
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
2025-12-19 21:36:11 +08:00
if (i == 0) {
make.centerY.equalTo(self.leftContainer);
make.width.mas_equalTo(kKBAIButtonWidth);
make.height.mas_equalTo(kKBAIButtonHeight);
} else {
make.top.bottom.equalTo(self.leftContainer);
}
2025-10-28 10:18:10 +08:00
if (previous) {
make.left.equalTo(previous.mas_right).offset(8);
} else {
make.left.equalTo(self.leftContainer.mas_left);
}
// intrinsicContentSize
2025-10-28 10:18:10 +08:00
}];
2025-10-28 10:18:10 +08:00
previous = btn;
}
//
//
if (previous) {
[previous mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.lessThanOrEqualTo(self.leftContainer.mas_right);
}];
}
2025-10-28 10:18:10 +08:00
self.leftButtonsInternal = buttons.copy;
2025-11-05 20:11:10 +08:00
//
[self kb_refreshGlobeVisibility];
2025-12-19 19:21:08 +08:00
[self kb_updateUndoVisibilityAnimated:NO];
[self kb_applyTheme];
2025-10-28 10:18:10 +08:00
}
- (UIButton *)buildActionButtonAtIndex:(NSInteger)idx {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.layer.cornerRadius = 16;
btn.layer.masksToBounds = YES;
btn.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
btn.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
NSString *title = (idx < self.leftButtonTitles.count) ? self.leftButtonTitles[idx] : @"";
2025-10-28 10:18:10 +08:00
[btn setTitle:title forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 10 = + 20
btn.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10);
[btn setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[btn setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
2025-10-28 10:18:10 +08:00
btn.tag = idx;
[btn addTarget:self action:@selector(onLeftAction:) forControlEvents:UIControlEventTouchUpInside];
return btn;
}
#pragma mark - Theme
- (void)kb_applyTheme {
[self kb_updateAIButtonAppearance];
}
- (void)kb_updateAIButtonAppearance {
UIButton *aiButton = [self kb_aiButton];
if (!aiButton) { return; }
KBSkinManager *skinManager = [KBSkinManager shared];
UIImage *icon = [skinManager iconImageForKeyIdentifier:kKBAIKeyIdentifier caseVariant:0];
NSString *skinId = skinManager.current.skinId ?: @"";
BOOL usingDefaultSkin = (skinId.length == 0 || [skinId isEqualToString:@"default"]);
if (!icon && usingDefaultSkin) {
icon = [UIImage imageNamed:@"ai_key_icon"];
}
if (icon) {
[aiButton setBackgroundImage:icon forState:UIControlStateNormal];
[aiButton setBackgroundImage:icon forState:UIControlStateHighlighted];
[aiButton setBackgroundImage:icon forState:UIControlStateSelected];
[aiButton setTitle:@"" forState:UIControlStateNormal];
aiButton.backgroundColor = [UIColor clearColor];
aiButton.layer.cornerRadius = 0;
aiButton.layer.masksToBounds = NO;
aiButton.contentEdgeInsets = UIEdgeInsetsZero;
} else {
[aiButton setBackgroundImage:nil forState:UIControlStateNormal];
[aiButton setBackgroundImage:nil forState:UIControlStateHighlighted];
[aiButton setBackgroundImage:nil forState:UIControlStateSelected];
2025-12-19 21:36:11 +08:00
NSString *title = (self.leftButtonTitles.count > 0) ? self.leftButtonTitles[0] : @"AI";
[aiButton setTitle:title forState:UIControlStateNormal];
aiButton.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
aiButton.layer.cornerRadius = 16;
aiButton.layer.masksToBounds = YES;
aiButton.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10);
}
}
2025-10-28 10:18:10 +08:00
#pragma mark - Actions
- (void)onLeftAction:(UIButton *)sender {
if ([self.delegate respondsToSelector:@selector(toolBar:didTapActionAtIndex:)]) {
[self.delegate toolBar:self didTapActionAtIndex:sender.tag];
}
}
- (void)onSettings {
if ([self.delegate respondsToSelector:@selector(toolBarDidTapSettings:)]) {
[self.delegate toolBarDidTapSettings:self];
}
}
2025-12-19 19:21:08 +08:00
- (void)onUndo {
if ([self.delegate respondsToSelector:@selector(toolBarDidTapUndo:)]) {
[self.delegate toolBarDidTapUndo:self];
}
}
2025-10-28 10:18:10 +08:00
#pragma mark - Lazy
- (UIView *)leftContainer {
if (!_leftContainer) {
_leftContainer = [[UIView alloc] init];
_leftContainer.backgroundColor = [UIColor clearColor];
}
return _leftContainer;
}
//- (UIButton *)settingsButtonInternal {
// if (!_settingsButtonInternal) {
// _settingsButtonInternal = [UIButton buttonWithType:UIButtonTypeSystem];
// _settingsButtonInternal.layer.cornerRadius = 16;
// _settingsButtonInternal.layer.masksToBounds = YES;
// _settingsButtonInternal.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
// [_settingsButtonInternal setTitle:@"⚙︎" forState:UIControlStateNormal]; // 齿
// [_settingsButtonInternal setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// [_settingsButtonInternal addTarget:self action:@selector(onSettings) forControlEvents:UIControlEventTouchUpInside];
// }
// return _settingsButtonInternal;
//}
2025-10-27 19:42:27 +08:00
2025-11-05 20:11:10 +08:00
- (UIButton *)globeButtonInternal {
if (!_globeButtonInternal) {
_globeButtonInternal = [UIButton buttonWithType:UIButtonTypeSystem];
_globeButtonInternal.layer.cornerRadius = 16;
_globeButtonInternal.layer.masksToBounds = YES;
_globeButtonInternal.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
[_globeButtonInternal setTitle:@"🌐" forState:UIControlStateNormal];
[_globeButtonInternal setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
return _globeButtonInternal;
}
2025-12-19 19:21:08 +08:00
- (UIButton *)undoButtonInternal {
if (!_undoButtonInternal) {
_undoButtonInternal = [UIButton buttonWithType:UIButtonTypeSystem];
_undoButtonInternal.layer.cornerRadius = 16;
_undoButtonInternal.layer.masksToBounds = YES;
_undoButtonInternal.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
_undoButtonInternal.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
[_undoButtonInternal setTitle:@"撤销删除" forState:UIControlStateNormal];
[_undoButtonInternal setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
_undoButtonInternal.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10);
_undoButtonInternal.hidden = YES;
_undoButtonInternal.alpha = 0.0;
[_undoButtonInternal addTarget:self action:@selector(onUndo) forControlEvents:UIControlEventTouchUpInside];
}
return _undoButtonInternal;
}
- (UIButton *)kb_aiButton {
2025-12-19 21:36:11 +08:00
if (self.leftButtonsInternal.count == 0) { return nil; }
return self.leftButtonsInternal[0];
}
2025-11-05 20:11:10 +08:00
#pragma mark - Globe (Input Mode Switch)
// 宿
- (void)kb_refreshGlobeVisibility {
UIInputViewController *ivc = KBFindInputViewController(self);
BOOL needSwitchKey = YES;
if (ivc && [ivc respondsToSelector:@selector(needsInputModeSwitchKey)]) {
needSwitchKey = ivc.needsInputModeSwitchKey; // YES
}
self.globeButtonInternal.hidden = !needSwitchKey;
2025-12-19 19:21:08 +08:00
self.kbNeedsInputModeSwitchKey = needSwitchKey;
2025-11-05 20:11:10 +08:00
2025-12-19 19:21:08 +08:00
[self kb_updateLeftContainerConstraints];
2025-11-05 20:11:10 +08:00
//
//
[self.globeButtonInternal removeTarget:nil action:NULL forControlEvents:UIControlEventAllEvents];
if (needSwitchKey && ivc) {
SEL sel = NSSelectorFromString(@"handleInputModeListFromView:withEvent:");
if ([ivc respondsToSelector:sel]) {
[self.globeButtonInternal addTarget:ivc action:sel forControlEvents:UIControlEventAllTouchEvents];
} else {
// 退
[self.globeButtonInternal addTarget:ivc action:@selector(advanceToNextInputMode) forControlEvents:UIControlEventTouchUpInside];
}
}
}
2025-12-19 19:21:08 +08:00
- (void)kb_updateLeftContainerConstraints {
[self.leftContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
if (self.kbNeedsInputModeSwitchKey) {
make.left.equalTo(self.globeButtonInternal.mas_right).offset(8);
} else {
make.left.equalTo(self.mas_left).offset(12);
}
if (self.kbUndoVisible) {
make.right.equalTo(self.undoButtonInternal.mas_left).offset(-8);
} else {
make.right.equalTo(self).offset(-12);
}
make.centerY.equalTo(self.mas_centerY);
2025-12-19 21:36:11 +08:00
make.height.mas_equalTo(MAX(32.0f, kKBAIButtonHeight));
2025-12-19 19:21:08 +08:00
}];
}
- (void)kb_undoStateChanged {
[self kb_updateUndoVisibilityAnimated:YES];
}
- (void)kb_updateUndoVisibilityAnimated:(BOOL)animated {
BOOL visible = [KBBackspaceUndoManager shared].hasUndo;
if (self.kbUndoVisible == visible) { return; }
self.kbUndoVisible = visible;
self.undoButtonInternal.hidden = NO;
[self kb_updateLeftContainerConstraints];
void (^changes)(void) = ^{
self.undoButtonInternal.alpha = visible ? 1.0 : 0.0;
[self layoutIfNeeded];
};
void (^finish)(BOOL) = ^(BOOL finished) {
self.undoButtonInternal.hidden = !visible;
};
if (animated) {
[UIView animateWithDuration:0.18
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:changes
completion:finish];
} else {
changes();
finish(YES);
}
}
2025-11-05 20:11:10 +08:00
- (void)didMoveToWindow {
[super didMoveToWindow];
[self kb_refreshGlobeVisibility];
}
2025-10-27 19:42:27 +08:00
@end