Подклассы NSScrollView drawRect: Метод - PullRequest
4 голосов
/ 09 апреля 2011

Я настраиваю пользовательский интерфейс для одного из моих приложений, и идея заключается в том, что текстовая область изначально имеет серый оттенок, когда не в фокусе, а когда она попадает в фокус, рамка становится ярко-белой.Мое приложение использует темную тему, и для однострочного NSTextField это прекрасно работает.

Однако у меня возникают проблемы с подклассами NSTextView.Для того, чтобы правильно изменить границу, мне пришлось фактически разделить родительский класс на NSScrollView, но я все еще вижу странное поведение. (см. Скриншот ниже.) I хочу красное поле, чтобы заполнить весь вид прокрутки, поскольку это позволило бы мне обвести (а не заполнять, что только для тестирования) путь, создавая красивую границу.Вместо этого красное поле, кажется, заполняет только внутреннее дочернее представление.

Следующий фрагмент кода для подкласса NSScrollView:

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    NSRect borderRect = self.bounds;
    borderRect.origin.y += 1;
    borderRect.size.width -= 1;
    borderRect.size.height -= 4;

    BOOL inFocus = ([[self window] firstResponder] == self);

    if (!inFocus) {
        inFocus = [self anySubviewHasFocus:self];
    }

    if (inFocus) {
        [[NSColor colorWithDeviceRed:.8 green:.2 blue:0 alpha:1] set];
    } else {
        [[NSColor colorWithDeviceRed:.1 green:.8 blue:0 alpha:1] set];
    }

    [NSGraphicsContext saveGraphicsState];
    [[NSGraphicsContext currentContext] setShouldAntialias:NO];
    [NSBezierPath fillRect:borderRect];
    [NSGraphicsContext restoreGraphicsState];

    NSLog(@"My bounds: %@", NSStringFromRect(borderRect));
    NSLog(@"Super (%@) bounds: %@", [self superview], NSStringFromRect(borderRect));
}

Создает снимок экрана, как виднониже.Кроме того, посмотрите вывод в журнале, который предлагает , что весь вид должен быть заполнен.Это единственный вывод, который когда-либо показывался, независимо от размера текста внутри.Ввод возвратов каретки увеличивает высоту поля red , но не дает другого выхода.(И я бы хотел, чтобы красная коробка заполняла все границы.)

2011-04-08 21:30:29.789 MyApp[6515:903] My bounds: {{0, 1}, {196, 87}}
2011-04-08 21:30:29.789 MyApp[6515:903] Super (<EditTaskView: 0x3a0b150>) bounds: {{0, 1}, {196, 87}}

Drawing Issue

Редактировать: Спасибо Джош Касвелл за ответ.См. Ниже для правильного поведения, когда не в фокусе, а когда в фокусе.

No Focus

Focus

1 Ответ

7 голосов
/ 09 апреля 2011

Как заметил ughoavgfhw, NSScrollView обычно не рисует, и, вероятно, странным образом взаимодействует со своими дочерними представлениями.Я бы предложил добавить что-то вроде следующего в код рисования вашего текстового представления, чтобы нарисовать желаемое кольцо фокуса *:

// We're going to be modifying the state for this, 
// so allow it to be restored later
[NSGraphicsContext saveGraphicsState];

// Choose the correct color; isFirstResponder is a custom     
// ivar set in becomeFirstResponder and resignFirstResponder
if( isFirstResponder && [[self window] isKeyWindow]){
    [myFocusedColor set];
} 
else {
    [myNotFocusedColor set];
}

// Create two rects, one slightly outset from the bounds,
// one slightly inset
NSRect bounds = [self bounds];
NSRect innerRect = NSInsetRect(bounds, 2, 2);
NSRect outerRect = NSMakeRect(bounds.origin.x - 2, 
                              bounds.origin.y - 2,
                              bounds.size.width + 4,
                              bounds.size.height + 4);

// Create a bezier path using those two rects; this will
// become the clipping path of the context
NSBezierPath * clipPath = [NSBezierPath bezierPathWithRect:outerRect];
[clipPath appendBezierPath:[NSBezierPath bezierPathWithRect:innerRect]];

// Change the current clipping path of the context to 
// the enclosed area of clipPath; "enclosed" defined by 
// winding rule. Drawing will be restricted to this area.
// N.B. that the winding rule makes the order that the
// rects were added to the path important.
[clipPath setWindingRule:NSEvenOddWindingRule];
[clipPath setClip];
// Fill the rect; drawing is clipped and the inner rect
// is not drawn in
[[NSBezierPath bezierPathWithRect:outerRect] fill];
[NSGraphicsContext restoreGraphicsState];

Это должно быть разумным приближением того, что делает AppKit, когда он рисует фокускольцо.Конечно, AppKit в некотором роде позволяет рисовать за пределами представления - я не могу гарантировать, что это полностью безопасно, но вы, кажется, получаете запас в 3 пикселя для игры.Вы можете нарисовать кольцо полностью внутри границ, если хотите.В любом случае, истинное кольцо фокусировки немного расширяется (2 пикселя) внутри вида (как я сделал здесь).

Документы Apple на Настройка области отсечения .

РЕДАКТИРОВАТЬ: Перечитав ваши комментарии по этому вопросу, я понимаю, что, возможно, долго скучал над реальным ответом.Попробуйте либо создать подкласс NSClipView и переключить представление клипа для этого вида прокрутки, либо использовать пользовательское представление для вида документа.


*: Вы также можете поместить это в код рисованияпользовательский подкласс представления, который устанавливается как представление документа NSScrollView;тогда ваш текстовый просмотр может быть подвидом этого.Или замените пользовательский подкласс NSClipView.

...