Заставить окно перерисовать себя с помощью Core Graphics? - PullRequest
6 голосов
/ 04 октября 2011

Я разработал систему впрыска и подключил некоторые кварцевые API для создания приятных эффектов с окнами в Mac OS X. Например, когда пользователь устанавливает в окне красный цвет - это красный глянцевый красный.

Но, когда я внедряю приложение, которое уже запущено, я не могу дать ему желаемый эффект, так как окно уже нарисовано. Итак, я ищу что-то в графике кварц / ядро, которая может позволить мне перерисовать все окно или какую-то технику, которая может позволить мне послать какое-то событие / вызов некоторой функции, которая заставит систему перекрасить все окно снова.

Я имею в виду, что каждая вещь в окне должна быть нарисована снова, чтобы мои подключенные API-интерфейсы выполнялись для создания правильных эффектов, оттенков и цветов. Здесь важен порядок, в котором окно создается и окрашивается.

Я использую технику, аналогичную инжектировать и вставлять , а код инъекции - это код C / C ++.

У кого-нибудь есть идеи, как мне этого добиться?

Ответы [ 3 ]

5 голосов
/ 10 октября 2011

Если вы запрашиваете способ заставить окно перерисовать себя, используя API более низкого уровня, чем Какао, то, насколько я знаю, это невозможно.Окно перерисовывается, когда вызывается метод drawRect: метод его содержимого.Он передает CGContextRef в окно, которое метод затем использует для перерисовки окна.CoreGraphics не несет ответственности за перерисовку окна.Какао использует CoreGraphics для перерисовки окон.

Можно получить графический контекст окна вне drawRect: и затем рисовать к нему в любое время (см., Например, здесь ), но этозвучит как то, что вы действительно хотите сделать, это перехватить результаты обычной процедуры рисования окна и сделать некоторые свои собственные вещи сверху.Возможно, вы могли бы сделать это, переключив класс представления содержимого окна и переопределив drawRect.Вспомогательная функция для обработки инъекции выглядела бы примерно так:

typedef void (^InjectedBlock)(CGContextRef, CGRect);

void InjectIntoView(NSView* view, InjectedBlock aBlock)
{
    Class viewClass = [view class];
    InjectedBlock injectedBlock = [aBlock copy];

    void(^drawRect)(id, SEL, NSRect) = ^(id self, SEL _cmd, NSRect rect)
    {
        struct objc_super superId = { self, viewClass };
        objc_msgSendSuper(superId, @selector(drawRect:), rect);

        injectedBlock([[NSGraphicsContext currentContext] graphicsPort], CGRectFromNSRect(rect));
    };

    NSString* subclassName = [NSString stringWithFormat:"%s_injected", class_getName(viewClass)]
    Class subclass objc_allocateClassPair(viewClass, [subclassName UTF8String], 0);
    objc_registerClassPair(subclass);

    Method overriddenMethod = class_getInstanceMethod([NSView class], @selector(drawRect:));
    IMP imp = imp_implementationWithBlock(drawRect);

    class_addMethod(subclass, @selector(drawRect:), imp, method_getTypeEncoding(overriddenMethod))
}

Редактировать:

Ааа, вас интересует все окно.Кадр и т. Д. Также являются экземплярами NSView, но они являются частными подклассами NSView, к которым у вас нет прямого доступа.Вы можете заставить их перерисовать, вызвав display в окне, но это, вероятно, перезапишет все, что вы сделали с окном, так как оно будет использовать существующие процедуры рисования этих классов.

Так что вы можете такжехочу рассмотреть возможность использования drawRect: метод этих представлений (вызов [[NSGraphicsContext currentContext] graphicsPort] в drawRect: даст вам CGContextRef, который вы можете использовать с API-интерфейсами Quartz).Вы можете получить представление кадров, вызвав superview в представлении содержимого окна.

Обратите внимание, что расположение представлений кадра окна не имеет документов и может изменяться при обновлении системы.

Звучит какво всяком случае, интересный проект!

5 голосов
/ 06 октября 2011

-[NSView setNeedsDisplayInRect:] и -[NSView setNeedsDisplay:] являются прямыми эквивалентами invalidateRect.

Я не знаю, что вы подразумеваете под тем, что вам нужно в Quartz / CoreGraphics.Какао уже использует их для рисования.

Если вы хотите вызвать некую волшебную функцию CGxxx (), которая сделает окно нарисованным заново, это невозможно сделать.Заголовок и рамка окна рисуются системой, но что касается контента, для низкоуровневых API нет никакого способа узнать, что там должно быть нарисовано.Единственный, кто знает, как нарисовать вид, это сам вид.(Может быть, что-то кешируется в хранилище бэк-окна, но я не знаю каких-либо открытых или недокументированных API для доступа к нему.)объект перерисовать свои взгляды.Если вы уже внедрены в процесс, он может включать следующие шаги:

  • определение времени выполнения obj-c (вам потребуется не менее objc_msgSend функция)
  • определение NSApplicationкласс
  • с использованием +[NSApplication sharedApplication] и -[NSApplication windows] для поиска NSWindow* указателя объекта
  • с использованием contentView, display и т. д. для перерисовки
3 голосов
/ 13 октября 2011

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

При аннулировании вы сообщаете системе, что часть вашего взгляда недействительна. В следующий раз, когда ваша система выделит время для рисования (обычно сразу после объявления об аннулировании), она перерисовает прямоугольник, который вы аннулировали.

setNeedsDisplay делает то же самое, за исключением всего представления, а не конкретного прямоугольника внутри этого представления. В вашем вопросе это не имеет значения, так как вы хотите обновить все окно. Он соединяется от UIView до Quartz во внутреннюю систему, так что ваш рисунок Quartz будет также использоваться, пока вы обрабатываете его через drawRect, который вызывается при рисовании.

Так что просто вызовите [yourWindow setNeedsDisplay]; и система будет знать, что ваше окно необходимо перерисовать как можно скорее.

...