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
    }
}