I'm writing an RPN calculator consisting of a non-editable NSTextFields
to display the top of the stack and an editable NSTextField to enter
commands. I want to perform directly some actions when e.g. [+], [-],
[*], or [/] are hit, without the necessarity to confirm with [return].
To do this I observe the NSDownKey events passed to my application and
perform some actions according to them. To get them I inherited
NSWindow and have now the following two files:
[1] MyWindow.h:
1: #import <Cocoa/Cocoa.h>
2:
3: @interface MyWindow : NSWindow {}
4: @end
5:
6: @interface NSObject (MyWindowDelegate)
7: - (BOOL)handlesEvent:(NSEvent *)event;
8: @end
and
[2] MyWindow.m:
1: #import "MyWindow.h"
2:
3: @implementation MyWindow
4:
5: - (void)sentEvent:(NSEvent *)event
6: {
7: if ([_delegate respondsTo:@selector(handlesEvent:)]
8: && [_delegate handlesEvent:event])
9: return; // don't process event furter
10: [super sentEvent:event];
11: }
12:
13: @end
This passes the events sent to my window to MyWindow's delegate (my
application controller) where they get actually processed. However, I
have some questions on that:
At first, I have two questions concerning Obj-C (hope that's ok, here):
(1) Do I need the empty braces ({}) in [1:3] (read MyWindow.h, line 3)?
(2) Should I use a @protocol to register -handlesEvent: instead of a
category in [1:6]? Or is there any better solution?
And then, I have two questions concerning Cocoa:
(1) Should I use [self delegate] in [2:7f] instead of _delegate or is it
for sure that _delegate will be NSWindow's delegate in future, too?
(2) And this is my main question: Is there any chance to get into the
Event-Loop without inheriting NSWindow (or NSApplication)?
[I somehow think it is unnecessary to have this two extra files in
my project just to redirect the event queue.]
Thanks for any help or advices,
Bastian
The first thing you are doing is subclsssing NSWindow - the braces
enclose the instance variables for your subclass, but you have no
instance variables or methods for the subclass. IOW, that first
interface declaration appears to be doing nothing of value other than
declaring a specific subclass name for NSWindow. The second interface
declaration creates a category method that will be inherited by every
instance of NSObject in your app - basically everything. To create a
category specific to NSWindow, strip out everything from the colon
following NSWindow to the opening parenthesis at the category and add
the category name to the implementation file, as in "@implementation
MyWindow (MyWindowDelegate)". Be aware, however, that every window in
your app will use the method defined in your category, and there is no
way to pass to "super". As far as using a protocol, that will not change
the behavior of any object that does not conform to the protocol and
implement the specific method. Protocols are primarily for defining how
one object expects another object to respond, as in the various dragging
protocols or plugin protocol definitions.
} And then, I have two questions concerning Cocoa:
}
} (1) Should I use [self delegate] in [2:7f] instead of _delegate or is
} it for sure that _delegate will be NSWindow's delegate in future,
} too?
Be safe, always use accessor methods, the cost is minimal.
} (2) And this is my main question: Is there any chance to get into the
} Event-Loop without inheriting NSWindow (or NSApplication)?
} [I somehow think it is unnecessary to have this two extra files in
} my project just to redirect the event queue.]
}
} Thanks for any help or advices,
} Bastian
}
Take a look at NSTextField and its superclasses NSControl and
NSResponder. There are some delegate methods that you might be able to
use to accomplish what you want without subclassing. This would most
likely involve making your display field editable, but with the delegate
methods ( especially -controlTextDidChange, but you might also take a
look at -control:textView:doCommandBySelector: ), you can easily manage
its content.
First, thank you for your answer.
Luddite Wacko <sy...@undernet.org> wrote:
> Bastian Erdnuess wrote:
> } 3: @interface MyWindow : NSWindow {}
> } 4: @end
> } (1) Do I need the empty braces ({}) in [1:3] (read MyWindow.h, line 3)?
> The first thing you are doing is subclsssing NSWindow - the braces
> enclose the instance variables for your subclass, but you have no
> instance variables or methods for the subclass. IOW, that first
> interface declaration appears to be doing nothing of value other than
> declaring a specific subclass name for NSWindow.
So, is it leagal to write just
@interface MyWindow : NSWindow
@end
and omit the '{}' ?
> } 6: @interface NSObject (MyWindowDelegate)
> } 7: - (BOOL)handlesEvent:(NSEvent *)event;
> } 8: @end
> } (2) Should I use a @protocol to register -handlesEvent: instead of a
> } category in [1:6]? Or is there any better solution?
> The second interface
> declaration creates a category method that will be inherited by every
> instance of NSObject in your app - basically everything.
Yeah, that's the point where I feel that I do something pretty "untidy".
> To create a
> category specific to NSWindow, strip out everything from the colon
> following NSWindow to the opening parenthesis at the category and add
> the category name to the implementation file, as in "@implementation
> MyWindow (MyWindowDelegate)".
I didn't got that sentence. Which colon?
But anyway. I don't think that I want the category relative to
MyWindow. 'handlesEvent:' should be a member of my delegates object,
and I do not know of which kind it'll be (in fact, in this case, I know,
it will be an direct NSObject derivat).
> Be aware, however, that every window in
> your app will use the method defined in your category, and there is no
> way to pass to "super". As far as using a protocol, that will not change
> the behavior of any object that does not conform to the protocol and
> implement the specific method. Protocols are primarily for defining how
> one object expects another object to respond, as in the various dragging
> protocols or plugin protocol definitions.
Right. And I have this category for the purpose to tell my
'MyWindow'-method what it has to expect from it's delegates member
'handlesEvent:'. Especially, it needs to know, that it gets a 'BOOL'
back, otherwise it would assume an 'id' and would warn the if-statement
in the following code:
> } 5: - (void)sentEvent:(NSEvent *)event
> } 6: {
> } 7: if ([_delegate respondsTo:@selector(handlesEvent:)]
> } 8: && [_delegate handlesEvent:event])
> } 9: return; // don't process event furter
> } 10: [super sentEvent:event];
> } 11: }
The alternative would be to define a protocol
@protocol MyWindowDelegateImplementsHandlesEvent
- (BOOL)handlesEvent:(NSEvent *)event
@end
and change the if above to
if ([_delegate confirmsToProtocol:
@protocol(MyWindowDelegateImplementsHandlesEvent)]
&& [_delegate handlesEvent:event])
return;
But this has some disadvantages. First, I'd need a own protocol for
each single method my delegate could implement, since I don't know
whether it implements all methods, or not, or which ones. And then, I
need to write all the protocols my delegate implements down in the
header of it's interface.
It's not that this would be a problem. It just feels odd to me, since
I've never seen a class conforming to like 20 protocols. And it somehow
also doesn't match the sense of a protocol to me to use it for a single
declaration.
Perfect would be something like an "optional protocol". That's like a
protocol, but the class confirming to it doesn't neet to implement all
of it, just when it does implement any, it has to implement it
conforming to the types specified in the "optional protocol".
> } (1) Should I use [self delegate] in [2:7f] instead of _delegate or is
> } it for sure that _delegate will be NSWindow's delegate in future,
> } too?
>
> Be safe, always use accessor methods, the cost is minimal.
Well, you're probably right. So I'll go to change this.
> } (2) And this is my main question: Is there any chance to get into the
> } Event-Loop without inheriting NSWindow (or NSApplication)?
> } [I somehow think it is unnecessary to have this two extra files in
> } my project just to redirect the event queue.]
> }
> } Thanks for any help or advices,
> } Bastian
> }
>
> Take a look at NSTextField and its superclasses NSControl and
> NSResponder. There are some delegate methods that you might be able to
> use to accomplish what you want without subclassing. This would most
> likely involve making your display field editable, but with the delegate
> methods ( especially -controlTextDidChange, but you might also take a
> look at -control:textView:doCommandBySelector: ), you can easily manage
> its content.
I thought about getting "-controlTextDidChange" and check for the last
inserted charakter. But it would be somehow just a work-around, and I
don't think that I want to go that way.
Thanks for your help,
I appreciate it.
Bastian
Really, though, you are getting too elaborate. The simplest solution is
to subclass your text view implementing these five methods:
@interface MyDisplay : NSTextView {
}
- (void)keyDown:(NSEvent *)theEvent;
- (void)keyUp:(NSEvent *)theEvent;
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;
- (BOOL)resignFirstResponder;
@end
Then, when you window becomes active, send it
[myWindow makeFirstResponder:theDisplay];
(where "theDisplay" is the MyDisplay object instance).
Then, "theDisplay" will use receive all the keystrokes one by one. This
is especially helpful if you want to manage keydown repeats (look up
NSEvent), you can filter for action keystrokes, arrow keys, anything you
want, and easily discard any meaningless keystrokes.
I am not honestly sure if the "{}" are necessary - just try compiling
without them and see if you get an error.
} statement in the following code:
> Really, though, you are getting too elaborate. The simplest solution is
> to subclass your text view implementing these five methods:
>
> @interface MyDisplay : NSTextView {
>
> }
>
> - (void)keyDown:(NSEvent *)theEvent;
> - (void)keyUp:(NSEvent *)theEvent;
> - (BOOL)acceptsFirstResponder;
> - (BOOL)becomeFirstResponder;
> - (BOOL)resignFirstResponder;
>
> @end
>
> Then, when you window becomes active, send it
>
> [myWindow makeFirstResponder:theDisplay];
>
> (where "theDisplay" is the MyDisplay object instance).
>
> Then, "theDisplay" will use receive all the keystrokes one by one. This
> is especially helpful if you want to manage keydown repeats (look up
> NSEvent), you can filter for action keystrokes, arrow keys, anything you
> want, and easily discard any meaningless keystrokes.
I think you're right. That should be the approach expressing more what
I was intend to do.
> }> } 3: @interface MyWindow : NSWindow {}
> }> } 4: @end
> }
> }> } (1) Do I need the empty braces ({}) in [1:3] (read MyWindow.h, line
> }> } 3)?
>
> I am not honestly sure if the "{}" are necessary - just try compiling
> without them and see if you get an error.
No, I don't. That's why I wonder that I don't see any code without
them. E.g. you also use them above. I think, I'll put them there.
Just because everybody does.
Bastian