Why does my clickable box needs a double click to have a clickcount that is equal to 1? - macos

I have an NSStatusItem that has an NSMenuItem which contains a custom NSView. this NSView contains 3 Subviews of NSBox which are clickable (implemented the mouseDown event). Strangely, when I run the application and I enter the statusItem right after I pressed the 'Play'-Button I have to click the NSBox just one time (this returns clickCount = 1). When I enter another window or view and I go back to the statusItem and I try to click one of the NSBoxes nothing happens. When I double click the items, the actions happen and the clickCount is also equal to 1.
-(void)mouseDown:(NSEvent *)theEvent {
NSLog(#"ClickCount: %ld", theEvent.clickCount);
if ([delegate respondsToSelector:#selector(boxClicked:)]) {
[delegate boxClicked:self];
}
}
Does anyone have any idea why this is happening and how I can solve this?
EDIT (full m-file code):
#import "ClickableBox.h"
#implementation ClickableBox
#synthesize delegate;
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
- (void)viewDidMoveToWindow {
[self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO];
}
-(void)mouseEntered:(NSEvent *)theEvent {
if ([delegate respondsToSelector:#selector(boxRolledOver:)]) {
[delegate boxRolledOver:self];
}
}
-(void)mouseExited:(NSEvent *)theEvent {
if ([delegate respondsToSelector:#selector(boxExited:)]) {
[delegate boxExited:self];
}
}
-(void)mouseDown:(NSEvent *)theEvent {
NSLog(#"ClickCount: %ld", theEvent.clickCount);
if ([delegate respondsToSelector:#selector(boxClicked:)]) {
[delegate boxClicked:self];
}
}
- (void)dealloc {
[delegate release];
[super dealloc];
}
#end

It does sound like you also need to do this:
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
so that first mouse-click events are accepted in your custom NSView.
There may be other issues, but can't tell from the code-snippet as-is.

This could be because your view is not key. You can set a delegate for your NSMenu and on -menuWillOpen: you can call [boxView.window makeFirstResponder:boxView]; to make it key.

Related

traffic buttons not responding in MacOS application

I am creating an application on MacOS and I am having this problem. I am using custom traffic buttons. When the window is not active if I click they do not respond, what I get is the window becoming active.
How could I fix that so the buttons will respond even if the window is not the active one.
I have them added to a custom view, here is what I have:
My View:
#implementation TrafficView
- (id)init {
if ((self = [super init])) {
trackingArea_.reset([[NSTrackingArea alloc]
initWithRect:[self bounds]
options:(NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways |
NSTrackingInVisibleRect)
owner:self
userInfo:nil]);
[self addTrackingArea:trackingArea_.get()];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
- (BOOL)acceptsFirstResponder {
return YES;
}
- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent {
return YES;
}
- (void)mouseEntered:(NSEvent*)event {
mouseInside_ = YES;
for (NSView* view in self.subviews)
[view setNeedsDisplay:YES];
}
- (void)mouseExited:(NSEvent*)event {
mouseInside_ = NO;
for (NSView* view in self.subviews)
[view setNeedsDisplay:YES];
}
- (BOOL)_mouseInGroup:(NSButton *)button {
return mouseInside_;
}
#end
Now to add the buttons I do this in the window implementation (I will post the code for only one button)
trafficView_.reset([[TrafficView alloc] init]);
[self.frameView addSubview:trafficView_];
// close button is declared as NSButton
closeButton_ =
[NSWindow standardWindowButton:NSWindowCloseButton
forStyleMask:NSTitledWindowMask];
[trafficView_ addSubview:closeButton_];
Note that my custom view (trafficView_) is added to the NSThemeFrame class, self.frameView
- (NSView*)frameView {
return [[self contentView] superview];
}
I do not why the buttons do not respond.
Could someone help on this?
Thanks in advance

Undo makeFirstResponder

I want an NSTextField that prevents empty strings so I subclassed the NSTextField and implemented this method
-(void) textDidEndEditing:(NSNotification *)aNotification
{
if([[self stringValue] isEqualToString:#""])
{
NSBeep();
[[self window] makeFirstResponder:self];
}
else
{
//what goes here
}
}
This works when my new text field is the second control in the window but not the first. In those cases I can't tab out of the Subclassed textfield even when its text is non-empty
So, how do I undo the makeFirstResponder method? Or is there a way of making the new textfield the current responder
Thanks in advance
stupot.
As you're overriding the base function, you should only need to make a call back to the super to make normal behaviour continue.
-(void) textDidEndEditing:(NSNotification *)aNotification
{
if([[self stringValue] isEqualToString:#""])
{
NSBeep();
[[self window] makeFirstResponder:self];
}
else
{
//what goes here - this:
[super textDidEndEditing:aNotification];
}
}

NSView subview right click not working over nsimageview

I have to two subviews associated with a view. One is a transparent view that handles a right click, the other a nsview with an nsimageview subview. For some reason the right click works over any part of the superview except the part within the nsimageview. The transparent view is on top of the other view yet the right mouse down event is not firing.
I finally solved it by subclassing the image view and overriding the hit test method to return nil. The full implementation is below :
#implementation TTBaseImageView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[super drawRect:dirtyRect];
}
-(BOOL)isFlipped
{
return YES;
}
-(BOOL)acceptsFirstResponder
{
return NO;
}
-(BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return NO;
}
-(NSView *)hitTest:(NSPoint)aPoint
{
return nil;
}
#end

draggingEntered not called

I have an NSBox subclass called dragBox. I want to be able to drag it around a canvas. The code is as follows:
-(void) awakeFromNib
{
[[self superview] registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
}
-(void) mouseDown:(NSEvent *)theEvent
{
[self dragImage:[[NSImage alloc] initWithContentsOfFile:#"/Users/bruce/Desktop/Untitled-1.png"] at:NSMakePoint(32, 32) offset:NSMakeSize(0,0) event:theEvent pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] source:self slideBack:YES];
}
-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender // validate
{
NSLog(#"Updated");
return [sender draggingSourceOperationMask];
}
-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSLog(#"Drag Entered");
return [sender draggingSourceOperationMask];
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSLog(#"Move Box");
[self setFrameOrigin:[sender draggingLocation]];
return YES;
}
-(BOOL) prepareForDragOperation:(id<NSDraggingInfo>)sender
{NSLog(#"Prepared");
return YES;
}
Why isn't dragEntered being called? I have tried to use all the pboard types and such. Nothing seems to work. I have also changed the registerForDraggedTypes to just work off of the [self] view. The box is a subview of a canvas.
Bruce
I found that awakeFromNib was the wrong place to put my registerForDragTypes call since I am programmatically adding my view (i.e. not adding it via a Nib). I had to put the call into initWithFrame:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes: [NSArray arrayWithObjects:NSTIFFPboardType,NSFilenamesPboardType,nil]];
}
return self;
}
Bruce,
Your Code needs to be changed in the below way. I believe that view should be registered for drag types to make the method draggingEntered to get called.
#interface NSModifiedBox : NSBox
#end
#implementation NSModifiedBox
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[self registerForDraggedTypes:
[NSArray arrayWithObjects:NSTIFFPboardType,NSFilenamesPboardType,nil]];
[super drawRect:dirtyRect];
}
- (NSDragOperation)draggingEntered:(id )sender
{
if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
== NSDragOperationGeneric)
{
return NSDragOperationGeneric;
} // end if
// not a drag we can use
return NSDragOperationNone;
}
- (BOOL)prepareForDragOperation:(id )sender
{
return YES;
}
#end
Now Drag and Drop a NSBox on the Xib and the Modify the class of NSBox to NSModifiedBox.
Set a break point to the method "draggingEntered".
Now Drag a ".png" or ".gif" file and drop on the NSModifiedBox and you see the "draggingEntered" will get invoked
Or you can check by using NSLog as well inside a "draggingEntered".
Hope my answer will help you :)

Cocoa: Right Click NSStatusItem

I am a .Net developer who need to port a small project into Mac, so I know next to nothing about Objective C. In fact the code below was just a bunch of grasping at straws and shooting in the dark.
Trying to build a Status Menu program that opens one or another window depending on if it is a left-click or a right-click/ctrl+click. Here is what I have, and it works for left-click only (obviously):
-(void) awakeFromNib{
NSBundle *bundle = [NSbundle mainBundle];
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
[statusImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"icon" ofType:#"png"]];
[statusItem setImage:statusImage];
[statusItem setToolTip:#"Program Name"];
[statusItem setHighlightMode:YES];
[statusItem setAction:#selector(openWin:)];
[statusItem setTarget: self];
}
-(void)openWin:(id)sender{
[self openLeftWindow:sender];
}
-(IBAction)openLeftWindow:(id)sender{
//Code to populate Left Click Window
[leftWindow makeKeyAndorderFront:nil];
}
-(IBAction)openRightWindow:(id)sender{
//Code to populate Right Click Window
[rightWindow makeKeyAndorderFront:nil];
}
It seems to me that the solution would be either an if statement in the openWin() function to determine which button is clicked (or if ctrl was held down) then run the appropriate code or to make the menu aware of both the left and right clicks. But neither of these have worked when I attempted to do so.
Thanks In Advance.
If you will be satisfied with detecting control-click and not right click, then the first block of code will do what you want. If you really need the right click detection, you will have to use a custom view instead of an image in your NSStatusItem, and the second block of code will work.
Simple Method:
- (void)openWin:(id)sender {
NSEvent *event = [NSApp currentEvent];
if([event modifierFlags] & NSControlKeyMask) {
[self openRightWindow:nil];
} else {
[self openLeftWindow:nil];
}
}
Custom view method:
- (void)awakeFromNib {
...
statusImage = ...
MyView *view = [MyView new];
view.image = statusImage;
[statusItem setView:view];
[statusItem setToolTip:#"Program Name"];
view target = self;
view action = #selector(openLeftWindow:);
view rightAction = #selector(openRightWindow:);
[view release];
//[statusImage release]; //If you are not using it anymore, you should release it.
}
MyView.h
#import <Cocoa/Cocoa.h>
#interface MyView : NSControl {
NSImage *image;
id target;
SEL action, rightAction;
}
#property (retain) NSImage *image;
#property (assign) id target;
#property (assign) SEL action, rightAction;
#end
MyView.m
#import "MyView.h"
#implementation MyView
#synthesize image, target, action, rightAction;
- (void)mouseUp:(NSEvent *)event {
if([event modifierFlags] & NSControlKeyMask) {
[NSApp sendAction:self.rightAction to:self.target from:self];
} else {
[NSApp sendAction:self.action to:self.target from:self];
}
}
- (void)rightMouseUp:(NSEvent *)event {
[NSApp sendAction:self.rightAction to:self.target from:self];
}
- (void)dealloc {
self.image = nil;
[super dealloc];
}
- (void)drawRect:(NSRect)rect {
[self.image drawInRect:self.bounds fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
}
#end
I would create a view and use the status items method.
-setView:
Then in the subclassed view you can detect ctrl+LMB using the following
- (void)mouseDown:(NSEvent *)theEvent
{
[super mouseDown:theEvent];
//Respond to the mouse click
if ([theEvent modifierFlags] & NSCommandKeyMask) //Command + LMB
{
//Do something
}
}
I think you can figure out the rest.
A more simplified response (Note, only works with control + click)
Properties:
#property (strong, nonatomic) NSStatusItem *statusItem;
#property (weak) IBOutlet NSMenu *statusMenu;
In Your Application Did Load:
[self.statusItem setAction:#selector(itemClicked:)];
Clicked Function:
- (void)itemClicked:(id)sender
{
NSEvent *event = [NSApp currentEvent];
if([event modifierFlags] & NSControlKeyMask) {
NSLog(#"Right Click Pressed");
[self.statusItem popUpStatusItemMenu:self.statusMenu];
} else {
// Do Nothing
NSLog(#"Left Click Pressed");
}
}

Resources