Cocoa:以编程方式显示/隐藏具有自定义NSView的自定义NSWindow

5
首先,我必须告诉您,我是Objective-C和Cocoa的新手。
我已经阅读了一些关于这方面的书籍,并且现在能够构建相当简单的程序。
我已经卡在一个程序上15天了,我正在尝试构建它,但我真的不知道该去哪里寻找更多信息。
我想构建一个可以使用DDC/CI改变我的显示器亮度的程序,并且我想显示/隐藏一个窗口(其中写有亮度级别),就像Leopard或Snow Leopard的苹果亮度簸箕一样,具有完全相同的样式。
使用RegisterEventHotKey和各种IOservices函数,我已经能够在按下F2时增加亮度,在按下F1时降低亮度。
使用自定义NSWindow(TransparentWindow)和自定义NSView(RoundedView),我已经能够获得与苹果亮度簸箕完全相同的窗口。我将其放在awakeFromNib中,它会正确地显示并保留在那里。
我无法实现的是仅在按下F1或F2时显示窗口(我已经实现了一个NSTimer来隐藏它,但现在不是重点)。
我尝试了不同的方式:
1) 从NSObject类中,我实现了RegisterEventHotKey,并创建了一个TransparentWindow实例,然后向该实例发送orderOut。
2) 我使用NSNotificationCenter直接向TransparentWindow类发送通知,并从那里调用orderOut。
3) 还有许多我现在不记得的方式。
现在我正在尝试通过在awakeFromNib中创建窗口(这可以工作),然后使用orderOut将其隐藏(这从未起作用)来使窗口出现。
这些是涉及的类:
TransparentWindow.h:
#import <Cocoa/Cocoa.h>

@interface TransparentWindow : NSWindow
{
IBOutlet NSWindow *window;
}

@property (retain) IBOutlet NSWindow *window;

@end

TransparentWindow.m:

#import "TransparentWindow.h"


@implementation TransparentWindow

@synthesize window;


- (id)initWithContentRect:(NSRect)contentRect 
                styleMask:(unsigned int)aStyle 
                  backing:(NSBackingStoreType)bufferingType 
                    defer:(BOOL)flag {

    window = [super initWithContentRect:contentRect 
                                        styleMask:NSBorderlessWindowMask 
                                          backing:NSBackingStoreBuffered 
                                defer:NO];
    if (window != nil) {

        [window setLevel: NSStatusWindowLevel];
        [window setBackgroundColor: [NSColor clearColor]];
        [window setAlphaValue:1.0];
        [window setOpaque:NO];
        [window setHasShadow:NO];
        [window setIgnoresMouseEvents: YES];
        NSLog(@"%p", window);

    }
    return window;

}


- (BOOL) canBecomeKeyWindow
{
    return YES;
}

- (void)notify {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:self];
}




- (void)handleNotification:(NSNotification*)note {

        [window orderOut:self];

    }


@end

RoundedView.h:

#import <Cocoa/Cocoa.h>

@interface RoundedView : NSView
{
}


@end

RoundedView.m:

#import "RoundedView.h"

@implementation RoundedView


- (void)drawRect:(NSRect)rect
{
        NSColor *bgColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.25];
    NSRect bgRect = rect;
    int minX = NSMinX(bgRect);
    int midX = NSMidX(bgRect);
    int maxX = NSMaxX(bgRect);
    int minY = NSMinY(bgRect);
    int midY = NSMidY(bgRect);
    int maxY = NSMaxY(bgRect);
    float radius = 20.0; // correct value to duplicate Panther's App Switcher
    NSBezierPath *bgPath = [NSBezierPath bezierPath];

    // Bottom edge and bottom-right curve
    [bgPath moveToPoint:NSMakePoint(midX, minY)];
    [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) 
                                     toPoint:NSMakePoint(maxX, midY) 
                                      radius:radius];

    // Right edge and top-right curve
    [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, maxY) 
                                     toPoint:NSMakePoint(midX, maxY) 
                                      radius:radius];

    // Top edge and top-left curve
    [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) 
                                     toPoint:NSMakePoint(minX, midY) 
                                      radius:radius];

    // Left edge and bottom-left curve
    [bgPath appendBezierPathWithArcFromPoint:bgRect.origin 
                                     toPoint:NSMakePoint(midX, minY) 
                                      radius:radius];
    [bgPath closePath];

    [bgColor set];
    [bgPath fill];

}

MainProgram.h:

#import <Cocoa/Cocoa.h>
#import <carbon/carbon.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <IOKit/IOKitLib.h> 
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/i2c/IOI2CInterface.h>

@interface MainProgram : NSObject {

}

@end

MainProgram.m:

#import "MainProgram.h"
#import <Carbon/Carbon.h>
#import "TransparentWindow.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <IOKit/IOKitLib.h> 
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/i2c/IOI2CInterface.h>


static OSStatus OnHotKeyEvent(EventHandlerCallRef nextHandler,EventRef theEvent,void *userData);

extern int level;

@implementation MainProgram


- (void)awakeFromNib
{

    EventHotKeyRef gMyHotKeyRef;
    EventHotKeyID gMyHotKeyID;
    EventTypeSpec eventType;
    eventType.eventClass=kEventClassKeyboard;
    eventType.eventKind=kEventHotKeyPressed;

    InstallApplicationEventHandler(&OnHotKeyEvent, 1, &eventType, NULL, NULL);

    gMyHotKeyID.signature='htk1';
    gMyHotKeyID.id=1;
    //RegisterEventHotKey(122, controlKey, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);
    RegisterEventHotKey(122, NULL, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);

    gMyHotKeyID.signature='htk2';
    gMyHotKeyID.id=2;
    RegisterEventHotKey(120, NULL, gMyHotKeyID, GetApplicationEventTarget(), 0, &gMyHotKeyRef);
}




OSStatus OnHotKeyEvent(EventHandlerCallRef nextHandler,EventRef theEvent,
                         void *userData)
{

    EventHotKeyID hkCom;
    GetEventParameter(theEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,
                      sizeof(hkCom),NULL,&hkCom);
    int l = hkCom.id;

    switch (l) {
        case 1: 
            //NSLog(@"Hotkey 1");
            level = level - 5;

            if (level<0) {
                level = 0;
            }
            else {
                BrightnessMain(level);
            }
            break;
        case 2: 
            //NSLog(@"Hotkey 2");
            level = level + 5;

            if (level>100) {
                level = 100;
            }
            else {
                BrightnessMain(level);

                TransparentWindow *object3 = [[TransparentWindow alloc] init];

                //[object3 orderOut:nil];
                [[NSNotificationCenter defaultCenter] addObserver:object3 selector:@selector(handleNotification:) name:@"MyNotification" object:nil];
                [object3 notify];

            }
            break;
    }

    return noErr;
}


@end

哪里出错了? 我不是一个专业的程序员,但我认为这可能是一个愚蠢而简单的错误,但我无法找出来。

MainProgram.m的更新:

我已经用这个替换了

TransparentWindow *object3 = [[TransparentWindow alloc] init];

用这个代替:

NSRect contentRect;
NSSize contentSize;

contentSize.width  = 210;
contentSize.height = 205;

contentRect.origin = NSMakePoint(855, 140);
contentRect.size  = contentSize;

TransparentWindow *object3 = [[TransparentWindow alloc] initWithContentRect:contentRect 
                                                                                  styleMask:NSBorderlessWindowMask 
                                                                                    backing:NSBackingStoreBuffered 
                                                                                      defer:NO];

我不确定这是否是您的意思...

修正后的TransparentWindow.h:

#import <Cocoa/Cocoa.h>

@interface TransparentWindow : NSWindow
{

}


@end

已更正的TransparentWindow.m文件:

#import "TransparentWindow.h"


@implementation TransparentWindow


- (id)initWithContentRect:(NSRect)contentRect 
                styleMask:(unsigned int)aStyle 
                  backing:(NSBackingStoreType)bufferingType 
                    defer:(BOOL)flag {

    self = [super initWithContentRect:contentRect 
                                        styleMask:NSBorderlessWindowMask 
                                          backing:NSBackingStoreBuffered 
                                defer:NO];
    if (self != nil) {

        [self setLevel: NSStatusWindowLevel];
        [self setBackgroundColor: [NSColor clearColor]];
        [self setAlphaValue:1.0];
        [self setOpaque:NO];
        [self setHasShadow:NO];
        [self setIgnoresMouseEvents: YES];
        NSLog(@"%p", self);

    }
    return self;

}



- (BOOL) canBecomeKeyWindow
{
    return YES;
}

- (void)notify {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:self];
}




- (void)handleNotification:(NSNotification*)note {

    [self orderOut:self];

    }

@end

更新2:

如果我使用makeKeyAndOrderFront而不是orderFront,就会发生一件非常奇怪的事情。

在按下F1或F2按钮的前3或4次之后,什么都不会发生,但当我再次按下F1或F2时,仍然没有窗口出现,但我会在控制台中获得这些奇怪的错误信息:

RoundedFloatingPanel[2346] <Warning>: CGSResolveShmemReference : window.RO.dirtyRegion : Reference offset (38464) exceeds bounds (32768) on shmem obj 0x229
RoundedFloatingPanel[2346] <Warning>: CGSResolveShmemReference : window.RO : Reference offset (38144) exceeds bounds (32768) on shmem obj 0x229
RoundedFloatingPanel[2346] <Error>: kCGErrorFailure: CGSNewWindowWithOpaqueShape: Cannot map window information shmem
RoundedFloatingPanel[2346] <Error>: kCGErrorFailure: Set a breakpoint @ CGErrorBreakpoint() to catch errors as they are logged.
2011-02-17 16:01:42.895 RoundedFloatingPanel[2346:a0f] HIToolbox: ignoring exception 'Error (1000) creating CGSWindow' that raised inside Carbon event dispatch
(
    0   CoreFoundation                      0x91f186ba __raiseError + 410
    1   libobjc.A.dylib                     0x999e3509 objc_exception_throw + 56
    2   CoreFoundation                      0x91f183e8 +[NSException raise:format:arguments:] + 136
    3   CoreFoundation                      0x91f1835a +[NSException raise:format:] + 58
    4   AppKit                              0x93408915 _NXCreateWindow + 316
    5   AppKit                              0x93408720 _NSCreateWindow + 59
    6   AppKit                              0x93407946 -[NSWindow _commonAwake] + 1784
    7   AppKit                              0x9340456e -[NSWindow _commonInitFrame:styleMask:backing:defer:] + 1524
    8   AppKit                              0x934031c1 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1568
    9   AppKit                              0x93402b9b -[NSWindow initWithContentRect:styleMask:backing:defer:] + 71
    10  RoundedFloatingPanel                0x000029a6 -[TransparentWindow initWithContentRect:styleMask:backing:defer:] + 119
    11  RoundedFloatingPanel                0x000031ec OnHotKeyEvent + 447
    12  HIToolbox                           0x95ff0ecf _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 1567
    13  HIToolbox                           0x95ff0196 _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 411
    14  HIToolbox                           0x95fefff5 SendEventToEventTargetWithOptions + 58
    15  HIToolbox                           0x96024c18 _ZL29ToolboxEventDispatcherHandlerP25OpaqueEventHandlerCallRefP14OpaqueEventRefPv + 3006
    16  HIToolbox                           0x95ff1320 _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 2672
    17  HIToolbox                           0x95ff0196 _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 411
    18  HIToolbox                           0x96012a07 SendEventToEventTarget + 52
    19  AppKit                              0x93460c3e -[NSApplication sendEvent:] + 7494
    20  AppKit                              0x933f42a7 -[NSApplication run] + 917
    21  AppKit                              0x933ec2d9 NSApplicationMain + 574
    22  RoundedFloatingPanel                0x00002368 main + 30
    23  RoundedFloatingPanel                0x0000231e start + 54
    24  ???                                 0x00000001 0x0 + 1
1个回答

2

首先,

  TransparentWindow *object3 = [[TransparentWindow alloc] init];

未调用您在中定义的初始化程序

  @implementation TransparentWindow


  - (id)initWithContentRect:(NSRect)contentRect 
                  styleMask:(unsigned int)aStyle 
                    backing:(NSBackingStoreType)
                      defer:(BOOL)flag { ...

相反,调用

  TransparentWindow *object3 = [[TransparentWindow alloc] initWithContentRect: ...
                                                                    styleMask: ... 
                                                                      backing: ... defer: ...];

使用适当的参数来调用你定义的内容!
接下来,不要合成属性“window”。你的“TransparentWindow”是一个“NSWindow”,因为它是一个子类,所以你不必在其中创建“NSWindow * window”。看起来你正在混合“window”和“self”在“TransparentWindow”内部,这是非常糟糕的。
删除属性和实例变量“window”,并将“TransparentWindow”内所有出现的“window”替换为“self”。例如,“init...”方法应该像这样:
 self = [super initWithContentRect:contentRect 
                                    styleMask:NSBorderlessWindowMask 
                                      backing:NSBackingStoreBuffered 
                            defer:NO];
 if (self != nil) { ...

现在,请告诉我这些变化之后情况如何!我不确定它们是否足够,但这些是最明显的问题。

另外,暂时不要使用通知;现在只需使用 order...: 即可。那应该可以工作。 - Yuji
实际上,在Interface Builder中,我已经启用了“Visible at launch”,因此窗口已经显示,我想在按下F1或F2时隐藏它。出于这个原因,我正在使用orderOut。 无论如何,无论是orderOut(隐藏)还是orderFront(显示),都没有起作用。我现在没有使用通知。 orderOut和orderFront的发送者应该是什么? - Andrea3000
那么你为什么在代码中创建object3???你的IBOutlet持有什么窗口???你需要将orderOut:发送到在nib内创建的TransparentWindow类的特定实例。 - Yuji
我使用 [window setLevel: NSScreenSaverWindowLevel][window setCollectionBehavior:NSWindowCollectionBehaviorStationary] 解决了这个问题。 非常感谢! - Andrea3000
很高兴听到这个好消息:) 如果每一个在 SO 上提问的人都像你一样好就好了... :) - Yuji
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接