I want to shrink a NSWindow, by changing the height of the frame and having that come off of the top of the window.
I tried:
NSRect frame = [mainWindow frame];
frame.origin.y += 71;
frame.size.height -= 71;
[mainWindow setFrame:frame display:YES animate:YES];
But it made the window smaller from the bottom, not the top.
In cocoa on OS X, the origin is in the bottom left corner of the screen. This means that increasing the y position of a window will move it up the screen. Since you want to change the top of your window, you want the bottom corner to stay in place, which means you should not change your origin. Simply changing the height will cause your window to shrink from the top.
NSRect frame = [mainWindow frame];
frame.size.height -= 71;
[mainWindow setFrame:frame display:YES animate:YES];
Related
This is the constructor of my NSWindow subclass called FullScreenWindow :
- (id)initWithScreen:(NSScreen *)s {
NSRect contentRect = [s frame];
self = [super initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO
screen:s];
if (self) {
[self setCollectionBehavior:NSWindowCollectionBehaviorStationary|
NSWindowCollectionBehaviorCanJoinAllSpaces|
NSWindowCollectionBehaviorIgnoresCycle];
[self setReleasedWhenClosed:YES];
[self setBackgroundColor:[NSColor greenColor]];
[self setAlphaValue:1.0];
[self setOpaque:NO];
[self setLevel:NSMainMenuWindowLevel-1];
}
return self;
}
I wanna add such an NSWindow to every display in [NSScreen screens] but when I connect a second display, the windows only display the right way if I set origin.x of contentRect to -1440 for the first display (and 0 for the second one). When I get origin.x values of the frames of the NSScreen instances it returns 0 for the first display and 1440 for the second one. Why are these coordinates shifted?
One of the [NSScreen screens] will have (0, 0) as origin.
Now imagine 2 axes: Y goes up from (0, 0) and X goes to the right.
All other screens will have coordinates with this coordinate system and screen.frame.origin will represent bottom left corner.
I could not find this in the documentation, so I found this experimenting with displays arrangement.
I had this picture with two monitors: main one 1366x768, secondary 1680x1050, aligned to the top.
I tried also different arrangements, moving #1 around #0, and my hypothesis was always correct.
I'm drawing a rectangle on a custom subclass of NSView which can then be dragged within the borders of the view:
The code for doing this is:
// Get the starting location of the mouse down event.
NSPoint location = [self convertPoint: [event locationInWindow] fromView: nil];
// Break out if this is not within the bounds of the rect.
if (!NSPointInRect(location, [self boundsOfAllControlPoints])) {
return;
}
while (YES) {
// Begin modal mouse tracking, looking for mouse dragged and mouse up events
NSEvent *trackingEvent = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
// Get tracking location and convert it to point in the view.
NSPoint trackingLocation = [self convertPoint:[trackingEvent locationInWindow] fromView:nil];
// Calculate the delta's of x and y compared to the previous point.
long dX = location.x - trackingLocation.x;
long dY = location.y - trackingLocation.y;
// Update all points in the rect
for (int i = 0; i < 4; i++) {
NSPoint newPoint = NSMakePoint(points[i].x - dX, points[i].y - dY);
points[i] = newPoint;
}
NSLog(#"Tracking location x: %f y: %f", trackingLocation.x, trackingLocation.y);
// Set current location as previous location.
location = trackingLocation;
// Ask for a redraw.
[self setNeedsDisplay:YES];
// Stop mouse tracking if a mouse up is received.
if ([trackingEvent type] == NSLeftMouseUp) {
break;
}
}
I basically catch a mouse down event and check whether its location is inside the draggable rect. If it is, I start tracking the movement of the mouse in trackingEvent. I calculate the delta's for x and y coordinates, create new points for the draggable rect, and request a refresh of the views display.
Although it works, it looks a bit "amateurish" as during the drag, the mouse pointer will catch up with the shape being dragged and will eventually cross its borders. In other drag operations one will see the mouse pointer fixed to the position of the object being dragged, from start to finish of the drag operation.
What is causing this effect?
EDIT:
I've changed my approach following Rob's answer and adopted the three method approach:
- (void) mouseDown: (NSEvent*) event {
// There was a mouse down event which might be in the thumbnail rect.
[self setDragStartPoint: [self convertPoint: [event locationInWindow] fromView: nil]];
// Indicate we have a valid start of a drag.
if (NSPointInRect([self dragStartPoint], [self boundsOfAllControlPoints])) {
[self setValidDrag: YES];
}
}
- (void) mouseDragged: (NSEvent *) anEvent {
// Return if a valid drag was not detected during a mouse down event.
if (![self validDrag]) {
return;
}
NSLog(#"Tracking a drag.");
// Get tracking location and convert it to point in the view.
NSPoint trackingLocation = [self convertPoint: [anEvent locationInWindow] fromView: nil];
// Calculate the delta's of x and y compared to the previous point.
long dX = [self dragStartPoint].x - trackingLocation.x;
long dY = [self dragStartPoint].y - trackingLocation.y;
// Update all points in the rect
for (int i = 0; i < 4; i++) {
NSPoint newPoint = NSMakePoint(points[i].x - dX, points[i].y - dY);
points[i] = newPoint;
}
// Ask for a redraw.
[self setNeedsDisplay:YES];
NSLog(#"Tracking location x: %f y: %f", trackingLocation.x, trackingLocation.y);
// Set current location as previous location.
[self setDragStartPoint: trackingLocation];
NSLog(#"Completed mouseDragged method. Allow for repaint.");
}
- (void) mouseUp: (NSEvent *) anEvent {
// End the drag.
[self setValidDrag: NO];
[self setNeedsDisplay: YES];
}
Although the effect is slightly better, there's still a noticeable delay with the rect eventually dragging behind the direction of movement of the mouse pointer. This is especially noticeable when I move the mouse slowly during a drag.
EDIT 2:
Got it. The problem was with calculating the deltas. I used long for that while I should use float. Works great now.
You're holding onto the event loop during the draw, which means that the square never gets to redraw. Your call to setNeedsDisplay: doesn't draw anything. It just flags the view to be redrawn. It can't redraw until this routine returns, which you don't do until the mouse button is released.
Read Handling Mouse Dragging Operations for a full discussion on how to implement dragging in Cocoa. You either need to return from mouseDown: and override mouseDragged: and mouseUp:, or you need to pump the event loop yourself so that the drawing cycle can process.
I tend to recommend the first approach, even though it requires multiple methods. Pumping the event loop can create very surprising bugs and should be used with caution. The most common bugs in my experience are due to delayed selectors firing when you pump the event loop, causing "extra" code to run in the middle of your dragging routine. In some cases, this can cause reentrance and deadlock. (I've had this happen....)
1.Create a cocoa application (not document-based)
2.Create a new class "StretchView"(subclass NSView)
3.Open the Interface builder and drag a "Scroll view" to the main window
4.Choose the "Scroll view" and set the class "StretchView" (in class identity window)
The size of the contentview is 500*500 and the size of the strechview is also 500*500
(horizontal Scroll is enabled).
Then I start to draw some numbers(1,2,3,4......) horizontally one after the other.
When the number is out of ranger(the x pos is larger than 500) I increase the width
of the StretchView. (Everything works fine up till this point)
Then I tried to make the horizontal scroller to automatically scroll to the end so
that everytime I increase the width of the StretchView the last number coulde be
seen.
Here's the code:
//The timer is called every sec
-(void)myTimerAction:(NSTimer *) timer
{
NSLog(#"myTimerAction");
//......
int i = _myArray.count;
NSRect rect = [self frame];
int width = rect.size.width;
//The width between two number is 10
//When the x pos of current num is bigger then the scroll's width
if((i * 10) > width) {
//reset the width
width = i * 10;
[self setFrameSize:CGSizeMake(width, rect.size.height)];
//How to make it autoscroll???
//...............................
}
//......
[self setNeedsDisplay:YES];
}
Try this:
NSView *contentView = [[self enclosingScrollView] contentView];
CGFloat newXPosition = width - NSWidth([contentView bounds]);
if (newXPosition > 0.0) [self scrollPoint:NSMakePoint(newXPosition, 0.0)];
contentView is the clipping view in the enclosing scroll view. You want to scroll your current view to the x point in your view such that this x point + the clipping view width give the last clipping view width points of your view.
So, in reference to : Kiosk Applications - OS X programming - Multiple monitors
I have an 800x600 NSView and I need to scale this proportionally when I enter full screen mode using NSView's enterFullScreenModeWithOptions.
Right now, what's happening is the UI elements are stationary at where they were initially defined and the view is occupying the full screen mode. To top this off, my view has a background image which occupies the top left 800x600 square on the monitor too. So how do I scale everything in the view proportionally to fit the screen when in full screen mode?
Thanks again,
Teja
I implemented a viewer, which inherits form NSView. Before my image gets drawn, I do the resize [image setSize:size] to fit my image to the fullscreen.
if([myviewer isInFullScreenMode]){
int width = [[NSScreen mainScreen] frame].size.width;
int height = [[NSScreen mainScreen] frame].size.height;
NSSize size;
size.width = width;
size.height = height;
[image setScalesWhenResized:YES];
[image setSize:size];
}
Hope, this can help anyone.
I've got an NSView (myView) wrapped in an NSScrollView (myScrollView). Using zoom-in/out buttons, the user can alter the scale of myView. If the user is currently scrolled to a particular spot in myView, I'd like to keep that part of the view on-screen after the zooming has taken place.
I've got code that looks like this:
// preserve current position in scrollview
NSRect oldVisibleRect = [[myScrollView contentView] documentVisibleRect];
NSPoint oldCenter = NSPointFromCGPoint(CGPointMake(oldVisibleRect.origin.x + (oldVisibleRect.size.width / 2.0),
oldVisibleRect.origin.y + (oldVisibleRect.size.height / 2.0)));
// adjust my zoom
++displayZoom;
[self scaleUnitSquareToSize:NSSizeFromCGSize(CGSizeMake(0.5, 0.5))];
[self calculateBounds]; // make sure my frame & bounds are at least as big as the visible content view
[self display];
// Adjust scroll view to keep the same position.
NSRect newVisibleRect = [[myScrollView contentView] documentVisibleRect];
NSPoint newOffset = NSPointFromCGPoint(CGPointMake((oldCenter.x * 0.5) - (newVisibleRect.size.width / 2.0),
(oldCenter.y * 0.5) - (newVisibleRect.size.height / 2.0)));
if (newOffset.x < 0)
newOffset.x = 0;
if (newOffset.y < 0)
newOffset.y = 0;
[[myScrollView contentView] scrollToPoint: newOffset];
[myScrollView reflectScrolledClipView: [myScrollView contentView]];
And it seems sort of close, but it's not quite right and I can't figure out what I'm doing wrong. My two questions are:
1) Is there not a built-in something along the lines of:
[myView adjustScaleBy: 0.5 whilePreservingLocationInScrollview:myScrollView];
2) If not, can anyone see what I'm doing wrong in my "long way around" approach, above?
Thanks!
Keeping the same scroll position after scaling isn't easy. One thing you need to decide is what you mean by "the same" - do you want the top, middle, or bottom of the visible area before scaling to stay in place after scaling?
Or, more intuitively, do you want the position that stays in place a percentage down the visible rect equal to the percentage that you are scrolled down the document when you start (eg, so the center of the scroller's thumb doesn't move up or down during a scale, the thumb just grows or shrinks).
If you want the latter effect, one way to do it is get the NSScrollView's verticalScroller and horizontalScroller, and then read their 'floatValue's. These are normalized from 0 to 1, where '0' means you're at the top of the document and 1 means you're at the end. The nice thing about asking the scroller for this is that if the document is shorter than the NSScrollView, the scroller still returns a sane answer in all cases for 'floatValue,' so you don't have to special-case this.
After you resize, set the NSScrollView's scroll position to be the same percentage it was before the scale - but, sadly, here's where I wave my hands a little bit. I haven't done this in a while in my code, but as I recall you can't just set the NSScrollers' 'floatValue's directly - they'll LOOK scrolled, but they won't actually affect the NSScrollView.
So, you'll have to write some math to calculate the new top-left point in your document based on the percentage you want to be through it - on the y axis, for instance, it'll look like, "If the document is now shorter than the scrollView's contentView, scroll to point 0, otherwise scroll to a point that's ((height of contentView - height of documentView) * oldVerticalPercentage) down the document." X axis is of course similar.
Also, I'm almost positive you don't need a call to -display here, and in general shouldn't ever call it, ever. (-setNeedsDisplay: at most.)
-Wil
Me thinks you like to type too much… ;-)
// instead of this:
NSPoint oldCenter = NSPointFromCGPoint(CGPointMake(oldVisibleRect.origin.x +
(oldVisibleRect.size.width / 2.0),
// use this:
NSPoint oldCenter = NSMakePoint(NSMidX(oldVisibleRect), NSMaxY(oldVisibleRect));
// likewise instead of this:
[self scaleUnitSquareToSize:NSSizeFromCGSize(CGSizeMake(0.5, 0.5))];
// use this:
[self scaleUnitSquareToSize:NSMakeSize(0.5, 0.5)];
// and instead of this
NSPoint newOffset = NSPointFromCGPoint(CGPointMake(
(oldCenter.x * 0.5) - (newVisibleRect.size.width / 2.0),
(oldCenter.y * 0.5) - (newVisibleRect.size.height / 2.0)));
// use this:
NSPoint newOffset NSMakePoint(
(oldCenter.x - NSWidth(newVisibleRect)) / 2.f,
(oldCenter.y - NSHeight(newVisibleRect)) / 2.f);
This is an old question, but I hope someone looking for this finds my answer useful...
float zoomFactor = 1.3;
-(void)zoomIn
{
NSRect visible = [scrollView documentVisibleRect];
NSRect newrect = NSInsetRect(visible, NSWidth(visible)*(1 - 1/zoomFactor)/2.0, NSHeight(visible)*(1 - 1/zoomFactor)/2.0);
NSRect frame = [scrollView.documentView frame];
[scrollView.documentView scaleUnitSquareToSize:NSMakeSize(zoomFactor, zoomFactor)];
[scrollView.documentView setFrame:NSMakeRect(0, 0, frame.size.width * zoomFactor, frame.size.height * zoomFactor)];
[[scrollView documentView] scrollPoint:newrect.origin];
}
-(void)zoomOut
{
NSRect visible = [scrollView documentVisibleRect];
NSRect newrect = NSOffsetRect(visible, -NSWidth(visible)*(zoomFactor - 1)/2.0, -NSHeight(visible)*(zoomFactor - 1)/2.0);
NSRect frame = [scrollView.documentView frame];
[scrollView.documentView scaleUnitSquareToSize:NSMakeSize(1/zoomFactor, 1/zoomFactor)];
[scrollView.documentView setFrame:NSMakeRect(0, 0, frame.size.width / zoomFactor, frame.size.height / zoomFactor)];
[[scrollView documentView] scrollPoint:newrect.origin];
}