// // KBToolBar.m // CustomKeyboard // // Created by Mac on 2025/10/28. // #import "KBToolBar.h" #import "KBResponderUtils.h" // 查找 UIInputViewController,用于系统切换输入法 #import "KBBackspaceUndoManager.h" @interface KBToolBar () @property (nonatomic, strong) UIView *leftContainer; @property (nonatomic, strong) NSArray *leftButtonsInternal; //@property (nonatomic, strong) UIButton *settingsButtonInternal; @property (nonatomic, strong) UIButton *globeButtonInternal; // 可选:系统“切换输入法”键 @property (nonatomic, strong) UIButton *undoButtonInternal; // 右侧撤销删除 @property (nonatomic, assign) BOOL kbNeedsInputModeSwitchKey; @property (nonatomic, assign) BOOL kbUndoVisible; @end @implementation KBToolBar - (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor clearColor]; _leftButtonTitles = @[KBLocalized(@"Recharge Now"), @"AI"]; // 默认标题 [self setupUI]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_undoStateChanged) name:KBBackspaceUndoStateDidChangeNotification object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - Public - (NSArray *)leftButtons { return self.leftButtonsInternal; } - (UIButton *)undoButton { return self.undoButtonInternal; } //- (UIButton *)settingsButton { // return self.settingsButtonInternal; //} - (void)setLeftButtonTitles:(NSArray *)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]; } }]; } #pragma mark - 视图搭建 - (void)setupUI { [self addSubview:self.leftContainer]; // [self addSubview:self.settingsButtonInternal]; [self addSubview:self.globeButtonInternal]; [self addSubview:self.undoButtonInternal]; // 右侧设置按钮 // [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); // }]; // 左侧地球键(按需显示) [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); }]; // 右侧撤销按钮 [self.undoButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) { make.right.equalTo(self.mas_right).offset(-12); make.centerY.equalTo(self.mas_centerY); make.height.mas_equalTo(32); }]; [self kb_updateLeftContainerConstraints]; // 在左侧容器中创建按钮(数量 = 标题数量) NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:_leftButtonTitles.count]; UIView *previous = nil; for (NSInteger i = 0; i < _leftButtonTitles.count; i++) { UIButton *btn = [self buildActionButtonAtIndex:i]; [self.leftContainer addSubview:btn]; [buttons addObject:btn]; [btn mas_makeConstraints:^(MASConstraintMaker *make) { make.top.bottom.equalTo(self.leftContainer); if (previous) { make.left.equalTo(previous.mas_right).offset(8); } else { make.left.equalTo(self.leftContainer.mas_left); } // 不再设置宽度约束,交给 intrinsicContentSize }]; previous = btn; } // 可选:最后一个不要一定贴右边,避免被拉伸 // 如果你希望它最多到右边,可以这样: if (previous) { [previous mas_makeConstraints:^(MASConstraintMaker *make) { make.right.lessThanOrEqualTo(self.leftContainer.mas_right); }]; } self.leftButtonsInternal = buttons.copy; // 初始刷新地球键的可见性与事件绑定 [self kb_refreshGlobeVisibility]; [self kb_updateUndoVisibilityAnimated:NO]; } - (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] : @""; [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]; btn.tag = idx; [btn addTarget:self action:@selector(onLeftAction:) forControlEvents:UIControlEventTouchUpInside]; return btn; } #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]; } } - (void)onUndo { if ([self.delegate respondsToSelector:@selector(toolBarDidTapUndo:)]) { [self.delegate toolBarDidTapUndo:self]; } } #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; //} - (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; } - (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; } #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; self.kbNeedsInputModeSwitchKey = needSwitchKey; [self kb_updateLeftContainerConstraints]; // 绑定系统提供的输入法切换处理(点按切换、长按弹出列表) // 仅在需要时绑定,避免多余的事件转发 [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]; } } } - (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); make.height.mas_equalTo(32); }]; } - (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); } } - (void)didMoveToWindow { [super didMoveToWindow]; [self kb_refreshGlobeVisibility]; } @end