When clicked NSImageView, Application can't move - cocoa

I create NSToolbar and added NSView on the NSToolbar.
NSView Included NSImageView.
When clicked NSImageView or NSView in NSToolbar, I can't moved application.
How can I move application when I clicked NSView (or NSImageView) on the NSToolbar.

The question isn't very clear to me, but if you want to move the window with the mouse you could subclass the NSView (or NSImageView) and add
- (BOOL)mouseDownCanMoveWindow {
return YES;
}

Related

Blinking cursor in NSTextField causes drawRect: to be called in custom NSView

I'm running into the strangest issue:
I have a Cocoa app with a custom NSView inside an NSScrollView.
When I click into the text field, drawRect: is called on my custom view each time the cursor blinks.
If I move my custom view outside of the scroll view, the issue goes away. Also if I place my test view at the very bottom of its parent view, the issue goes away.
This is not a duplicate of Why does the blinking cursor in textfield cause drawRect to be called?, it's the same issue, but I did not override hitTest:. In fact, my test view implementation looks like this:
class TestView:NSView
{
override func draw(_ dirtyRect: NSRect) {
NSColor.red.set()
dirtyRect.fill()
logWarning("drawRect in custom view called. dirty rect: \(NSStringFromRect(dirtyRect))")
}
}
My view hierarchy:
NSWindow
NSSplitView
NSView
NSVisualEffectView
NSTabView
NSView
NSScrollView
NSView
TestView
It seems to be a combination of the NSVisualEffectView and NSScrollView. I can't reproduce this in a fresh project. I tried reshuffling views in my existing app, but I can't pin this down.
The issue in action:
Note how drawRect: is called as soon as the text field has focus and stops when I click outside. I don't see why an unrelated view has to be redrawn with each caret pulse.

NSView inside an NSPopover: Converting Coordinates

Context
Let P be an NSPopover and view be an NSView that is a subview contained within P.
What I Need
I want to take the frame of view and convert it to the screen's coordinate system. Normally, you would do that like this:
NSRect frameRelativeToWindow = [self convertRect:self.frame relativeToView:nil];
[self.window convertRectToScreen:frameRelativeToWindow];
However, because view is in an NSPopover, its window property is nil and the above method will return NSZeroRect: [0, 0, 0, 0]
So, how can I get my view's frame in screen coordinates if the view is in an NSPopover?
Notes:
The NSPopover IS onscreen and visible.
The view whose frame I am trying to convert to screen coordinates is not a direct subview of the NSPopover or its contentView. The view in question is actually a custom NSView within an NSTableCellView within an NSTableView within an NSScrollView within an NSClipView. The clipView is a direct subview of the NSPopover's contentView.
In a normal NSWindow, you can take any subview and call [self window] to get the containing NSWindow object. Doing that in a popover, however, returns nil.
The following code assumes, that you have
a NSViewController subclass that manages the popover view
the view controller implements NSPopoverDelegate
As hamstergene pointed out, the popover's window won't be available until it is shown.
When the popoverWillShow: delegate method gets called, your popover view's window should be an instance of _NSPopoverWindow (and not nil).
- (void)popoverWillShow:(NSNotification *)notification
{
NSWindow* popOverWindow = self.view.window;
NSRect popoverRectInScreenCoords = [popOverWindow convertRectToScreen:self.view.frame];
NSLog(#"Popover Rect in Screen Coords:%#", NSStringFromRect(popoverRectInScreenCoords));
}
Update
Bryan (the OP) posted a solution to his problem in the comments.
The issue wasn't NSPopover itself. The actual reason for the nil window for some of his subviews was NSTableView, which releases non-visible NSTableCellViews.
As a workaround he implemented viewDidMoveToWindow in his custom NSTableCellView subclass. When viewDidMoveToWindow: gets called, the table cell view already has a window that can be used to perform coordinate conversion.
Your approach for calculating screen coordinates is correct (just don't forget to convert view's frame coordinates to window's base coordinates first using convertRect:self.frame toView:nil).
The problem is that the window is not created until the popover is displayed. Before that, there is no way to find the view's screen coordinates, because it is not on a screen.
If waiting for display is too late for you, catch popoverWillShow: delegate method (or the corresponding notification), which fires right before displaying the popover when the window is already created and positioned.

How NOT to change the background when NSButton clicked

I have a NSButton on a view I subclass the NSView and draw something in the drawRect:, and the view is belong to a NSWindow which
Then when I click the button, I find the view become transparent, which I guess is because the button click changes the button's background. So I subclass the NSButton,
- (void)mouseDown:(NSEvent *)theEvent {
[self.window.contentView setNeedsDisplay:YES];
[super mouseDown:theEvent];
[self.window.contentView setNeedsDisplay:YES];
}
Right now when I mouseDown and mouseUp, the view seems right, except there is a flash transparent when I click, even more, when I mouseDown and move out of the button, the background becomes transparent again.
Can anyone tell me what should I do to make the background of a clicked button do not change?or where to put the redraw code.
THANKS!

cocoa: NSView touch event

I created a simple cocoa project, and added an NSButton in the window.
Then I added an NSScrollView to the window and hided the NSButton.
However, when I click the scroll view , it is strange that the NSButton action responds!
I guess there is something with the touch event chains, but I failed to find it.
For example, I try to use:
- [NSView becomeFirstResponder];
- [NSView setAcceptsTouchEvents:];
SO what I want is the only the front-most view to become the first responder, and the touch event will not be sent to its superview or so.
Thanks.
This is the view hierarchy:
the scroll view and button are both added to the window view, and the scrollview's frame includes the button's frame. In other words, the button is hidden by the scroll view but still receives click events.
You need to add mouseDown: event in NSScrollView or NSCrollView's View. like this:
-(void)mouseDown:(NSEvent *)theEvent {
NSLog(#"MouseDown in NSView");
}

How to pass scroll events to parent NSScrollView

I need fixed-size NSTextViews inside a larger scrolling window. IB requires that the textviews be inside their own NSScrollViews, even though their min/max sizes are fixed so that they won’t actually scroll. When trackpad gestures are made within the textview frames (regardless of whether they have focus), they are captured by the textviews’ scrollviews, so nothing happens.
How do I tell the textviews’ scrollviews to pass scroll events up to the window’s main scrollview? (Or perhaps I should be asking how I tell the window’s main scrollview to handle these events itself and not pass them on to its child scrollviews.)
The IB structure is like this:
window
window’s content view
big scrollview for window (desired target for scroll events)
box
swappable content view in separate xib
scrollview for textview
textview
And, yes, the window does scroll correctly when the textviews do not have focus.
You needn't create a outlet "svActive" to track your super scrollview. Just write this sentence in scrollWheel event:
[[self nextResponder] scrollWheel:event];
this will pass the event to next responder in the responder chain.
IB does not require you have a text view inside a NSScrollView; this is just the default, because most of the time you'll want your view to scroll. Select the NSTextView and choose Layout > Unembed Objects. Note that after this point, you can no longer move or resize your view in IB. This seems to be a bug.
Here's an example of how to put two NSTextViews in a single NSScrollView.
Add two text views next to each other; put some text in them so you can see what's happening.
Select the views; choose Layout > Embed Objects In > Scroll View. This puts them in a generic NSView inside a NSScrollView.
Select the text views; choose Layout > Unembed Objects.
Turn off the springs and struts (autosizing) for each text view, so they don't resize when you shrink the scroll view.
Take note of the height of the document view (here it's 175).
Make the scroll view smaller. This also resizes the document view (NSView).
Restore the document view to its original size (I set the height back to 175).
Done! Scrolling works as you'd expect.
This is really embarrassing. After weeks of putting it off, I made a first attempt to get a subclassed NSScrollView to behave passively — and it turned out to be a no brainer.
Here’s the subclass:
h file:
#import <Cocoa/Cocoa.h>
#interface ScrollViewPassive : NSScrollView {
// This property is assigned a ref to windowController’s main scrollview.
NSScrollView *svActive;
}
#property (nonatomic, retain) NSScrollView *svActive;
#end
m file:
#import "ScrollViewPassive.h"
#implementation ScrollViewPassive
#synthesize svActive;
// Pass any gesture scrolling up to the main, active scrollview.
- (void)scrollWheel:(NSEvent *)event {
[svActive scrollWheel:event];
}
#end
There’s no need to make outlets for these passive scrollviews; I give them their refs to the main scrollview right after their xibs are assigned as content to the NSBox:
[self.boxDisplayingTextViews setContentView:self.subviewCtllr1.view];
// A textview's superview's superview is its scrollview:
((ScrollViewPassive *)[[self.subviewCtllr1.textview1 superview] superview]).svActive = self.scrollviewMain;
That’s it. Works like a charm.
I find that IB3 and Xcode4 both fight you if you try to do this directly, but you can do it indirectly. First, drag the textview out of the scrollview and delete the scrollview. You'll wind up with an orphaned textview. I don't know any way to get IB to allow you to put this into your window, but it'll be in your NIB. Now, attach an IBOutlet to it, and at runtime do a addSubview: and adjust its frame to move it into whatever scrollview you wanted it to be in.
In my experience, NIBs are a good thing, but every complex NIB I've ever worked with needed some final rearranging in code.
Based on #phaibin's answer, here's how you'd do it in Swift 4.2.
First, subclass NSScrollView and override scrollWheel:
class ScrollThingy: NSScrollView{
override func scrollWheel(with event: NSEvent) {
self.nextResponder?.scrollWheel(with: event)
}
}
Then place the ScrollThingy class (or whatever you name it) on the NSScrollView that is wrapped around your NSTextView. The parent NSScrollView will get the scroll event thereafter.

Resources