Open on side away from the edge of the screen if there is not enough room

This commit is contained in:
Gregory John Casamento 2023-01-03 06:50:15 -05:00
parent e573b5ce09
commit 3f1e8af9c4

View file

@ -42,27 +42,32 @@
#import "AppKit/NSView.h" #import "AppKit/NSView.h"
#import "AppKit/NSDrawer.h" #import "AppKit/NSDrawer.h"
#import "AppKit/NSGraphics.h" #import "AppKit/NSGraphics.h"
#import "AppKit/NSScreen.h"
static NSNotificationCenter *nc = nil; static NSNotificationCenter *nc = nil;
@interface GSDrawerWindow : NSWindow @interface GSDrawerWindow : NSWindow
{ {
NSWindow *_parentWindow; NSWindow *_parentWindow;
NSWindow *_pendingParentWindow; NSWindow *_pendingParentWindow;
NSDrawer *_drawer; NSDrawer *_drawer;
id _container; id _container;
NSBox *_borderBox; NSBox *_borderBox;
NSSize _borderSize; NSSize _borderSize;
NSTimer *_timer; NSTimer *_timer;
NSRect _latestParentFrame; NSRect _latestParentFrame;
BOOL _wasOpen; BOOL _wasOpen;
NSRectEdge _currentEdge;
} }
- (NSRect) frameFromParentWindowFrameInState:(NSInteger)state; - (NSRect) frameFromParentWindowFrameInState:(NSInteger)state;
// open/close // open/close
- (void) openOnEdge; - (void) openOnEdge: (NSRectEdge)edge;
- (void) closeOnEdge: (NSRectEdge)edge;
- (void) closeOnEdge; - (void) closeOnEdge;
- (void) slideOpen:(BOOL)opening; - (void) slideOpen: (BOOL)opening onEdge: (NSRectEdge)edge;
- (void) slideOpen: (BOOL)opening;
- (void) startTimer; - (void) startTimer;
- (void) stopTimer; - (void) stopTimer;
@ -80,9 +85,11 @@ static NSNotificationCenter *nc = nil;
- (void) handleWindowMiniaturize: (NSNotification *)notification; - (void) handleWindowMiniaturize: (NSNotification *)notification;
- (void) handleWindowDeminiaturize: (NSNotification *)notification; - (void) handleWindowDeminiaturize: (NSNotification *)notification;
- (void) handleWindowMove: (NSNotification *)notification; - (void) handleWindowMove: (NSNotification *)notification;
@end @end
@implementation GSDrawerWindow @implementation GSDrawerWindow
+ (void) initialize + (void) initialize
{ {
if (self == [GSDrawerWindow class]) if (self == [GSDrawerWindow class])
@ -151,15 +158,16 @@ static NSNotificationCenter *nc = nil;
return _container; return _container;
} }
- (NSRect) frameFromParentWindowFrameInState:(NSInteger)state - (NSRect) frameFromParentWindowFrameInState: (NSInteger)state
{ {
NSRect newFrame = [_parentWindow frame]; NSRect newFrame = [_parentWindow frame];
CGFloat totalOffset = [_drawer leadingOffset] + [_drawer trailingOffset]; CGFloat totalOffset = [_drawer leadingOffset] + [_drawer trailingOffset];
NSRectEdge edge = [_drawer preferredEdge]; NSRectEdge edge = _currentEdge; // [_drawer preferredEdge];
BOOL opened = (state == NSDrawerOpenState || state == NSDrawerOpeningState); BOOL opened = (state == NSDrawerOpenState || state == NSDrawerOpeningState);
NSSize size = [_parentWindow frame].size; // [_drawer maxContentSize]; NSSize size = [_parentWindow frame].size;
NSRect windowContentRect = [[_parentWindow contentView] frame]; NSRect windowContentRect = [[_parentWindow contentView] frame];
CGFloat windowHeightWithoutTitleBar = windowContentRect.origin.y + windowContentRect.size.height; // FIXME: This should probably add the toolbar height too, if the window has a toolbar CGFloat windowHeightWithoutTitleBar = windowContentRect.origin.y + windowContentRect.size.height;
// FIXME: This should probably add the toolbar height too, if the window has a toolbar
if (edge == NSMinXEdge) // left if (edge == NSMinXEdge) // left
{ {
@ -215,8 +223,6 @@ static NSNotificationCenter *nc = nil;
return newFrame; return newFrame;
} }
- (BOOL) canBecomeKeyWindow - (BOOL) canBecomeKeyWindow
{ {
return YES; return YES;
@ -229,42 +235,18 @@ static NSNotificationCenter *nc = nil;
- (void) becomeKeyWindow - (void) becomeKeyWindow
{ {
[_parentWindow orderFrontRegardless]; // so clicking on the drawer will bring the parent to the front [_parentWindow orderFrontRegardless]; // so clicking on the drawer will bring the parent to the front
[super becomeKeyWindow]; [super becomeKeyWindow];
} }
/*
- (void) orderFront: (id)sender
{
NSPoint holdOrigin = [self frame].origin;
NSPoint newOrigin = NSMakePoint(-10000,-10000);
NSRect tempFrame = [self frame];
// order the window under the parent...
tempFrame.origin = newOrigin;
[self setFrame: tempFrame display: NO];
[super orderFront: sender];
// [_parentWindow orderWindow: NSWindowAbove relativeTo: [self windowNumber]];
tempFrame.origin = holdOrigin;
// [self setFrame: tempFrame display: YES];
}
*/
/*
- (void) orderFront: (id)sender
{
[super orderWindow:NSWindowBelow relativeTo:[_parentWindow windowNumber]];
}
*/
- (void) startTimer - (void) startTimer
{ {
NSTimeInterval time = 0.1; NSTimeInterval time = 0.1;
_timer = [NSTimer scheduledTimerWithTimeInterval: time _timer = [NSTimer scheduledTimerWithTimeInterval: time
target: self target: self
selector: @selector(_timedWindowReset) selector: @selector(_timedWindowReset)
userInfo: nil userInfo: nil
repeats: YES]; repeats: YES];
} }
- (void) stopTimer - (void) stopTimer
@ -283,19 +265,13 @@ static NSNotificationCenter *nc = nil;
[super orderOut: sender]; [super orderOut: sender];
} }
/* // set the _borderBox to not resize during the slide,
- (void) orderWindow: (NSWindowOrderingMode)place relativeTo: (int)windowNum // and attach it to the appropriate edge instead
{
NSLog(@"Ordering window....");
[super orderWindow: place relativeTo: windowNum];
}
*/
- (void) lockBorderBoxForSliding - (void) lockBorderBoxForSliding
{ {
// set the _borderBox to not resize during the slide, and attach it to the appropriate edge instead
NSRectEdge edge = [_drawer preferredEdge]; NSRectEdge edge = [_drawer preferredEdge];
NSUInteger resizeMask = 0; NSUInteger resizeMask = 0;
if (edge == NSMinXEdge) // left if (edge == NSMinXEdge) // left
{ {
resizeMask = NSViewMaxXMargin; resizeMask = NSViewMaxXMargin;
@ -312,7 +288,9 @@ static NSNotificationCenter *nc = nil;
{ {
resizeMask = NSViewMinYMargin; resizeMask = NSViewMinYMargin;
} }
[_borderBox setAutoresizingMask: resizeMask]; // don't resize -- just give appearance of sliding
// don't resize -- just give appearance of sliding
[_borderBox setAutoresizingMask: resizeMask];
} }
- (void) unlockBorderBoxAfterSliding - (void) unlockBorderBoxAfterSliding
@ -321,28 +299,34 @@ static NSNotificationCenter *nc = nil;
[_borderBox setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; [_borderBox setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
} }
- (void) openOnEdge // prepare drawer contents before sliding...
- (void) openOnEdge: (NSRectEdge)edge
{ {
// prepare drawer contents before sliding...
NSRect frame = [self frameFromParentWindowFrameInState:NSDrawerOpenState]; NSRect frame = [self frameFromParentWindowFrameInState:NSDrawerOpenState];
[self setFrame:frame display: YES]; // make sure it's the full (open) size before locking the borderBox [self setFrame:frame display: YES]; // make sure it's the full (open) size before locking the borderBox
if ([_parentWindow isVisible]) // don't order front until parent window is visible if ([_parentWindow isVisible]) // don't order front until parent window is visible
{ {
[self lockBorderBoxForSliding]; [self lockBorderBoxForSliding];
[self slideOpen: YES onEdge: edge];
[self orderFront: self]; [self orderFront: self];
[self slideOpen:YES];
[self performSelector:@selector(unlockBorderBoxAfterSliding) withObject:nil afterDelay:0.01]; [self performSelector:@selector(unlockBorderBoxAfterSliding) withObject:nil afterDelay:0.01];
} }
[self startTimer]; [self startTimer];
} }
- (void) closeOnEdge - (void) openOnEdge
{
[self openOnEdge: _currentEdge];
}
- (void) closeOnEdge: (NSRectEdge)edge
{ {
NSRect frame; NSRect frame;
[self stopTimer]; [self stopTimer];
[self lockBorderBoxForSliding]; [self lockBorderBoxForSliding];
[self slideOpen:NO]; [self slideOpen: NO onEdge: edge];
[self orderOut: self]; [self orderOut: self];
frame = [self frameFromParentWindowFrameInState:NSDrawerOpenState]; frame = [self frameFromParentWindowFrameInState:NSDrawerOpenState];
@ -353,21 +337,30 @@ static NSNotificationCenter *nc = nil;
&& _pendingParentWindow != _parentWindow) && _pendingParentWindow != _parentWindow)
{ {
[self setParentWindow: _pendingParentWindow]; [self setParentWindow: _pendingParentWindow];
ASSIGN(_pendingParentWindow, nil); _pendingParentWindow = nil;
} }
} }
- (void) slideOpen:(BOOL)opening - (void) closeOnEdge
{ {
NSRect frame = [self frameFromParentWindowFrameInState:(opening?NSDrawerClosedState:NSDrawerOpenState)]; [self closeOnEdge: _currentEdge];
NSRect newFrame = [self frameFromParentWindowFrameInState:(opening?NSDrawerOpenState:NSDrawerClosedState)]; }
- (void) slideOpen: (BOOL)opening onEdge: (NSRectEdge)edge
{
NSUInteger count = 10;
NSRect frame = [self frameFromParentWindowFrameInState:
(opening?NSDrawerClosedState:NSDrawerOpenState)];
NSRect newFrame = [self frameFromParentWindowFrameInState:
(opening?NSDrawerOpenState:NSDrawerClosedState)];
NSTimeInterval slideDelay = 0.03; NSTimeInterval slideDelay = 0.03;
NSDate *nextStop = [NSDate dateWithTimeIntervalSinceNow:slideDelay]; NSDate *nextStop = [NSDate dateWithTimeIntervalSinceNow:slideDelay];
int count = 10;
CGFloat deltaX = (newFrame.origin.x - frame.origin.x) / count; CGFloat deltaX = (newFrame.origin.x - frame.origin.x) / count;
CGFloat deltaY = (newFrame.origin.y - frame.origin.y) / count; CGFloat deltaY = (newFrame.origin.y - frame.origin.y) / count;
CGFloat deltaW = (newFrame.size.width - frame.size.width) / count; CGFloat deltaW = (newFrame.size.width - frame.size.width) / count;
CGFloat deltaH = (newFrame.size.height - frame.size.height) / count; CGFloat deltaH = (newFrame.size.height - frame.size.height) / count;
_currentEdge = edge;
while (count--) while (count--)
{ {
frame.origin.x += deltaX; frame.origin.x += deltaX;
@ -376,11 +369,17 @@ static NSNotificationCenter *nc = nil;
frame.size.height += deltaH; frame.size.height += deltaH;
[self setFrame: frame display: YES]; [self setFrame: frame display: YES];
[NSThread sleepUntilDate:nextStop]; [NSThread sleepUntilDate:nextStop];
nextStop = [nextStop addTimeInterval:slideDelay]; nextStop = [nextStop addTimeInterval: slideDelay];
} }
[self setFrame:newFrame display: YES]; [self setFrame:newFrame display: YES];
} }
- (void) slideOpen: (BOOL)opening
{
[self slideOpen: opening onEdge: _currentEdge];
}
- (void) _resetWindowPosition - (void) _resetWindowPosition
{ {
if ([_parentWindow isVisible]) // don't set our frame until parent window is visible if ([_parentWindow isVisible]) // don't set our frame until parent window is visible
@ -552,6 +551,10 @@ static NSNotificationCenter *nc = nil;
preferredEdge: (NSRectEdge)edge preferredEdge: (NSRectEdge)edge
{ {
self = [super init]; self = [super init];
if (self == nil)
return nil;
// initialize the drawer window... // initialize the drawer window...
_drawerWindow = [[GSDrawerWindow alloc] _drawerWindow = [[GSDrawerWindow alloc]
initWithContentRect: NSMakeRect(0, 0, contentSize.width, initWithContentRect: NSMakeRect(0, 0, contentSize.width,
@ -565,14 +568,19 @@ static NSNotificationCenter *nc = nil;
_preferredEdge = edge; _preferredEdge = edge;
_currentEdge = edge; _currentEdge = edge;
_maxContentSize = NSMakeSize(200,200); _maxContentSize = NSMakeSize(200,200);
_minContentSize = contentSize; //NSMakeSize(50,50); _minContentSize = contentSize;
if (edge == NSMinXEdge || edge == NSMaxXEdge) {
_leadingOffset = 0.0; // for side drawers, top of drawer is immediately below the title bar // for side drawers, top of drawer is immediately below the title bar
_trailingOffset = 10.0; if (edge == NSMinXEdge || edge == NSMaxXEdge)
} else { {
_leadingOffset = 10.0; _leadingOffset = 0.0;
_trailingOffset = 10.0; _trailingOffset = 10.0;
} }
else
{
_leadingOffset = 10.0;
_trailingOffset = 10.0;
}
_state = NSDrawerClosedState; _state = NSDrawerClosedState;
return self; return self;
@ -590,6 +598,46 @@ static NSNotificationCenter *nc = nil;
} }
// Opening and Closing // Opening and Closing
- (NSRectEdge) _computeEdge
{
NSRectEdge result = _preferredEdge;
NSRect windowFrame = [[self parentWindow] frame];
NSRect screenRect = [[NSScreen mainScreen] frame];
switch (_preferredEdge)
{
case NSMinXEdge:
if (windowFrame.origin.x < _maxContentSize.width)
{
result = NSMaxXEdge;
}
break;
case NSMinYEdge:
if (windowFrame.origin.y < _maxContentSize.height)
{
result = NSMaxYEdge;
}
break;
case NSMaxXEdge:
if (windowFrame.origin.x + windowFrame.size.height > screenRect.size.height)
{
result = NSMinXEdge;
}
break;
case NSMaxYEdge:
if (windowFrame.origin.y + windowFrame.size.width > screenRect.size.width)
{
result = NSMinYEdge;
}
break;
}
return result;
}
- (void) close - (void) close
{ {
if (_state != NSDrawerOpenState) if (_state != NSDrawerOpenState)
@ -616,7 +664,8 @@ static NSNotificationCenter *nc = nil;
- (void) open - (void) open
{ {
[self openOnEdge: _preferredEdge]; NSRectEdge edge = [self _computeEdge];
[self openOnEdge: edge];
} }
- (void) open: (id)sender - (void) open: (id)sender
@ -639,7 +688,7 @@ static NSNotificationCenter *nc = nil;
[nc postNotificationName: NSDrawerWillOpenNotification object: self]; [nc postNotificationName: NSDrawerWillOpenNotification object: self];
_currentEdge = edge; _currentEdge = edge;
[_drawerWindow openOnEdge]; [_drawerWindow openOnEdge: edge];
_state = NSDrawerOpenState; _state = NSDrawerOpenState;
[nc postNotificationName: NSDrawerDidOpenNotification object: self]; [nc postNotificationName: NSDrawerDidOpenNotification object: self];