SelectText из NSTextField в фокусе
Может ли кто-нибудь предложить метод для выбора всего текста NSTextField
, когда пользователь нажимает на него?
Я нашел предложения для подкласса NSTextField
, а затем использовал mouseDown
или firstResponder
, `, но теперь он выше моего мастерства. Поэтому я надеялся, что будет более простое решение, или кто-то может быть достаточно любезен, чтобы подробно описать необходимые шаги.
Ответы
Ответ 1
Нет более простого решения, вам нужно подклассом NSTextField
делать то, что вы хотите. Вам нужно будет научиться обрабатывать подклассы, если вы хотите сделать что-нибудь полезное в Cocoa.
Текстовые поля могут быть относительно сложными для подкласса, потому что NSTextField
использует отдельный объект NSTextView
, называемый Редактором полей, для обработки фактического редактирования. Это текстовое представление возвращается объектом NSWindow
для NSTextField
и повторно используется для всех текстовых полей на странице.
Как и любой подкласс NSResponder
, NSTextField
отвечает методам -acceptsFirstResponder
и -becomeFirstResponder
. Они вызывается, когда окно хочет сосредоточиться на конкретном элементе управления или представлении. Если вы возвращаете YES
из обоих этих методов, ваш элемент управления/представления будет иметь статус первого ответчика, что означает его активное управление. Однако, как уже упоминалось, NSTextField фактически дает статус первого ответчика редактора полей при щелчке, поэтому вам нужно сделать что-то подобное в своем подклассе NSTextField
:
@implementation MCTextField
- (BOOL)becomeFirstResponder
{
BOOL result = [super becomeFirstResponder];
if(result)
[self performSelector:@selector(selectText:) withObject:self afterDelay:0];
return result;
}
@end
Это сначала вызывает реализацию суперкласса -becomeFirstResponder
, которая будет выполнять тяжелую работу по управлению редактором полей. Затем он вызывает -selectText:
, который выбирает весь текст в поле, но делает это после задержки 0 секунд, которая будет задерживаться до следующего прогона цикла события. Это означает, что выбор будет происходить после полной настройки редактора полей.
Ответ 2
Ответ от @Rob предположительно работал в какой-то момент, но, как заметил @Daniel, он больше не работает. Похоже, что Cocoa хочет отследить мышь и вытащить выделение в ответ на щелчок, и попытка выбрать текст в ответ на becomeFirstResponder
не очень хорошо работает с этим.
Событие мыши необходимо перехватить, чтобы предотвратить это отслеживание. Более или менее методом проб и ошибок я нашел решение, которое, похоже, работает на OS X 10.10:
@interface MyAutoselectTextField : NSTextField
@end
@implementation MyAutoselectTextField
- (void)mouseDown:(NSEvent *)theEvent
{
[[self currentEditor] selectAll:nil];
}
@end
Насколько я могу судить, к моменту появления mouseDown:
редактор полей уже настроен, возможно, как побочный эффект becomeFirstResponder
. Вызов selectAll:
затем выбирает содержимое редактора полей. Вызов selectText:
on self
вместо этого не работает, по-видимому, потому, что редактор поля настроен. Обратите внимание, что переопределение mouseDown:
здесь не вызывает super
; super
будет запускать цикл отслеживания, который будет вытягивать выделение, и мы не хотим этого поведения. Обратите внимание, что это переопределение mouseDown:
не влияет на выбор после того, как текстовое поле стало первым ответчиком, потому что в этот момент он вызывается редактором полей mouseDown:
.
Я понятия не имею, какой диапазон версий OS X это работает; если вам все равно, вам нужно протестировать его. К сожалению, работа с NSTextField
всегда немного хрупка, потому что работа редактора полей настолько странна и поэтому зависит от деталей реализации в super
.
Ответ 3
Некоторые обновления с Swift:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectAll(self)
}
}
}
Или для точного выбора:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectedRange = NSMakeRange(location, length)
}
}
}
Быстрая версия, работает для меня:
import Cocoa
class TextFieldSubclass: NSTextField {
override func becomeFirstResponder() -> Bool {
let source = CGEventSourceCreate(CGEventSourceStateID.HIDSystemState)
let tapLocation = CGEventTapLocation.CGHIDEventTap
let cmdA = CGEventCreateKeyboardEvent(source, 0x00, true)
CGEventSetFlags(cmdA, CGEventFlags.MaskCommand)
CGEventPost(tapLocation, cmdA)
return true
}
}
Ответ 4
Мне нравится решение Konstantin, но оно будет выбираться на каждой мышке. Здесь вариант, который я использую для выбора в методе mouseDown
, но только если он только стал первым ответчиком:
class SelectingTextField: NSTextField {
var wantsSelectAll = false
override func becomeFirstResponder() -> Bool {
wantsSelectAll = true
return super.becomeFirstResponder()
}
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if wantsSelectAll {
selectText(self)
wantsSelectAll = false
}
}
}
Ответ 5
Я знаю, что это старый вопрос, но здесь обновление для тех, кто может споткнуться об этом.
Переопределите NSTextField и подключитесь к intoFirstResponder(). Вам не придется беспокоиться об управлении делегатами или кликами. Самое сложное - сначала найти редактор полей для сфокусированного текстового поля, а затем попросить его выделить весь текст.
Objective-C
// in AutoselectOnFocusTextField.h
@interface AutoselectOnFocusTextField : NSTextField
@end
// in AutoselectOnFocusTextField.m
@implementation AutoselectOnFocusTextField
- (BOOL)becomeFirstResponder {
if (![super becomeFirstResponder]) {
return NO;
}
NSText* fieldEditor = [self currentEditor];
if (fieldEditor != nil) {
[fieldEditor performSelector:@selector(selectAll:) withObject:self afterDelay:0.0];
}
return YES;
}
@end
Swift
class AutoselectOnFocusTextField: NSTextField {
override func becomeFirstResponder() -> Bool {
guard super.becomeFirstResponder() else {
return false
}
if let editor = self.currentEditor() {
editor.perform(#selector(selectAll(_:)), with: self, afterDelay: 0)
}
return true
}
}