mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
974 lines
31 KiB
Plaintext
974 lines
31 KiB
Plaintext
/*
|
|
* _CPWindowView.j
|
|
* AppKit
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
@import "CPImageView.j"
|
|
@import "CPView.j"
|
|
@import "CPWindow_Constants.j"
|
|
|
|
@class CPCursor
|
|
@class CPMenu
|
|
@class CPPlatformWindow
|
|
|
|
@global CPApp
|
|
|
|
var _CPWindowViewCornerResizeRectWidth = 10,
|
|
_CPWindowViewMinContentHeight = 2,
|
|
|
|
_CPWindowViewResizeRegionNone = -1,
|
|
_CPWindowViewResizeRegionTopLeft = 0,
|
|
_CPWindowViewResizeRegionTop = 1,
|
|
_CPWindowViewResizeRegionTopRight = 2,
|
|
_CPWindowViewResizeRegionRight = 3,
|
|
_CPWindowViewResizeRegionBottomRight = 4,
|
|
_CPWindowViewResizeRegionBottom = 5,
|
|
_CPWindowViewResizeRegionBottomLeft = 6,
|
|
_CPWindowViewResizeRegionLeft = 7;
|
|
|
|
_CPWindowViewResizeSlop = 3;
|
|
|
|
|
|
@implementation _CPWindowView : CPView
|
|
{
|
|
unsigned _styleMask;
|
|
|
|
CPImageView _resizeIndicator;
|
|
CGSize _resizeIndicatorOffset;
|
|
|
|
CPView _toolbarView;
|
|
CGSize _toolbarOffset;
|
|
// BOOL _isAnimatingToolbar;
|
|
|
|
CGRect _cachedFrame;
|
|
int _resizeRegion;
|
|
CGPoint _mouseDraggedPoint;
|
|
|
|
CGRect _cachedScreenFrame;
|
|
|
|
CPView _sheetShadowView;
|
|
}
|
|
|
|
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
|
{
|
|
return CGRectMakeCopy(aFrameRect);
|
|
}
|
|
|
|
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
|
{
|
|
return CGRectMakeCopy(aContentRect);
|
|
}
|
|
|
|
+ (CPString)defaultThemeClass
|
|
{
|
|
return "window";
|
|
}
|
|
|
|
+ (CPDictionary)themeAttributes
|
|
{
|
|
return @{
|
|
@"title-bar-height": 25,
|
|
@"shadow-inset": CGInsetMakeZero(),
|
|
@"shadow-distance": 5,
|
|
@"window-shadow-color": [CPColor clearColor],
|
|
@"size-indicator": CGSizeMakeZero(),
|
|
@"resize-indicator": [CPNull null],
|
|
@"attached-sheet-shadow-color": [CPColor blackColor],
|
|
@"shadow-height": 8,
|
|
@"close-image-origin": [CPNull null],
|
|
@"close-image-size": [CPNull null],
|
|
@"close-image": [CPNull null],
|
|
@"close-active-image": [CPNull null],
|
|
@"bezel-color": [CPNull null],
|
|
@"title-text-color": [CPColor blackColor],
|
|
@"title-font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
|
|
@"title-text-shadow-color": [CPNull null],
|
|
@"title-text-shadow-offset": CGSizeMakeZero(),
|
|
@"title-alignment": CPCenterTextAlignment,
|
|
@"title-line-break-mode": CPLineBreakByTruncatingTail,
|
|
@"title-vertical-alignment": CPTopVerticalTextAlignment,
|
|
};
|
|
}
|
|
|
|
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
|
{
|
|
var contentRect = [[self class] contentRectForFrameRect:aFrameRect],
|
|
theToolbar = [[self window] toolbar];
|
|
|
|
if ([theToolbar isVisible])
|
|
{
|
|
var toolbarHeight = CGRectGetHeight([[theToolbar _toolbarView] frame]);
|
|
|
|
contentRect.origin.y += toolbarHeight;
|
|
contentRect.size.height -= toolbarHeight;
|
|
}
|
|
|
|
return contentRect;
|
|
}
|
|
|
|
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
|
{
|
|
var frameRect = [[self class] frameRectForContentRect:aContentRect],
|
|
theToolbar = [[self window] toolbar];
|
|
|
|
if ([theToolbar isVisible])
|
|
{
|
|
var toolbarHeight = CGRectGetHeight([[theToolbar _toolbarView] frame]);
|
|
|
|
frameRect.origin.y -= toolbarHeight;
|
|
frameRect.size.height += toolbarHeight;
|
|
}
|
|
|
|
return frameRect;
|
|
}
|
|
|
|
- (id)initWithFrame:(CGRect)aFrame styleMask:(unsigned)aStyleMask
|
|
{
|
|
self = [super initWithFrame:aFrame];
|
|
|
|
if (self)
|
|
{
|
|
_styleMask = aStyleMask;
|
|
_resizeIndicatorOffset = CGSizeMakeZero();
|
|
_toolbarOffset = CGSizeMakeZero();
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setDocumentEdited:(BOOL)isEdited
|
|
{
|
|
}
|
|
|
|
- (void)setTitle:(CPString)aTitle
|
|
{
|
|
}
|
|
|
|
- (CPView)hitTest:(CGPoint)locationInWindow
|
|
{
|
|
var region = [self resizeRegionForPoint:[_window convertBaseToGlobal:locationInWindow]];
|
|
|
|
if (region !== _CPWindowViewResizeRegionNone)
|
|
return self;
|
|
else
|
|
return [super hitTest:locationInWindow];
|
|
}
|
|
|
|
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (void)mouseDown:(CPEvent)anEvent
|
|
{
|
|
var theWindow = [self window],
|
|
couldResize = _styleMask & CPResizableWindowMask && ![theWindow isFullPlatformWindow];
|
|
|
|
couldResize = couldResize && (
|
|
(CPWindowResizeStyle === CPWindowResizeStyleModern) ||
|
|
((CPWindowResizeStyle === CPWindowResizeStyleLegacy) && _resizeIndicator));
|
|
|
|
if (couldResize)
|
|
{
|
|
var theWindow = [self window],
|
|
windowFrame = [theWindow frame],
|
|
shouldResize = NO;
|
|
|
|
if (CPWindowResizeStyle === CPWindowResizeStyleModern)
|
|
{
|
|
var globalPoint = [theWindow convertBaseToGlobal:[anEvent locationInWindow]];
|
|
|
|
_resizeRegion = [self resizeRegionForPoint:globalPoint];
|
|
shouldResize = _resizeRegion !== _CPWindowViewResizeRegionNone;
|
|
}
|
|
else
|
|
{
|
|
// Extend the resize frame to the edge of the window frame
|
|
var resizeFrame = [_resizeIndicator frame];
|
|
resizeFrame.size.width = CGRectGetWidth(windowFrame) - CGRectGetMinX(resizeFrame);
|
|
resizeFrame.size.height = CGRectGetHeight(windowFrame) - CGRectGetMinY(resizeFrame);
|
|
|
|
var localPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
|
shouldResize = CGRectContainsPoint(resizeFrame, localPoint);
|
|
|
|
// When in legacy mode, the only possible resize region is lower right
|
|
_resizeRegion = shouldResize ? _CPWindowViewResizeRegionBottomRight : _CPWindowViewResizeRegionNone;
|
|
}
|
|
|
|
if (shouldResize)
|
|
return [self trackResizeWithEvent:anEvent];
|
|
}
|
|
|
|
if ([self couldBeMoveEvent:anEvent])
|
|
[self trackMoveWithEvent:anEvent];
|
|
else
|
|
[super mouseDown:anEvent];
|
|
}
|
|
|
|
- (BOOL)couldBeMoveEvent:(CPEvent)anEvent
|
|
{
|
|
var theWindow = [self window];
|
|
|
|
return [theWindow isMovable] && [theWindow isMovableByWindowBackground];
|
|
}
|
|
|
|
/*
|
|
aPoint should be in global coordinates
|
|
*/
|
|
- (int)resizeRegionForPoint:(CGPoint)aPoint
|
|
{
|
|
/*
|
|
If the window is fixed width (minSize.width === maxSize.width), there
|
|
are 2 possible resize rects: top and bottom.
|
|
|
|
If the window is fixed height (minSize.height === maxSize.height), there
|
|
are 2 possible resize rects: left and right.
|
|
|
|
Otherwise there are 8 possible resize rects, 1 for each side and 1 for each corner.
|
|
The four corner rects are the same size, and the top/bottom and left/right
|
|
rects are the same size. So to save calculations, we can just create
|
|
3 rects and move them around to do hit testing. Start with the corners
|
|
and then do left/right and top/bottom.
|
|
|
|
NOTE: If the window is a sheet, the top is never eligible for resizing.
|
|
*/
|
|
var wind = [self window],
|
|
frame = [wind frame],
|
|
rect,
|
|
minSize = [wind minSize],
|
|
maxSize = [wind maxSize],
|
|
isFixedWidth = minSize.width === maxSize.width,
|
|
isFixedHeight = minSize.height === maxSize.height,
|
|
isSheet = wind._isSheet;
|
|
|
|
if (isFixedWidth)
|
|
{
|
|
rect = CGRectMake(frame.origin.x - _CPWindowViewResizeSlop,
|
|
frame.origin.y - _CPWindowViewResizeSlop,
|
|
frame.size.width + (_CPWindowViewResizeSlop * 2),
|
|
_CPWindowViewResizeSlop * 2);
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return isSheet ? _CPWindowViewResizeRegionNone : _CPWindowViewResizeRegionTop;
|
|
|
|
rect.origin.y = CGRectGetMaxY(frame) - _CPWindowViewResizeSlop;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionBottom;
|
|
}
|
|
else if (isFixedHeight)
|
|
{
|
|
rect = CGRectMake(frame.origin.x - _CPWindowViewResizeSlop,
|
|
frame.origin.y - _CPWindowViewResizeSlop,
|
|
_CPWindowViewResizeSlop * 2,
|
|
frame.size.height + (_CPWindowViewResizeSlop * 2));
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionLeft;
|
|
|
|
rect.origin.x = CGRectGetMaxX(frame) - _CPWindowViewResizeSlop;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionRight;
|
|
}
|
|
else
|
|
{
|
|
rect = CGRectMake(frame.origin.x - _CPWindowViewResizeSlop,
|
|
frame.origin.y - _CPWindowViewResizeSlop,
|
|
_CPWindowViewCornerResizeRectWidth + _CPWindowViewResizeSlop,
|
|
_CPWindowViewCornerResizeRectWidth + _CPWindowViewResizeSlop);
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return isSheet ? _CPWindowViewResizeRegionNone : _CPWindowViewResizeRegionTopLeft;
|
|
|
|
rect.origin.x = CGRectGetMaxX(frame) - _CPWindowViewCornerResizeRectWidth;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return isSheet ? _CPWindowViewResizeRegionNone : _CPWindowViewResizeRegionTopRight;
|
|
|
|
rect.origin.y = CGRectGetMaxY(frame) - _CPWindowViewCornerResizeRectWidth;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionBottomRight;
|
|
|
|
rect.origin.x = frame.origin.x - _CPWindowViewResizeSlop;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionBottomLeft;
|
|
|
|
rect = CGRectMake(rect.origin.x,
|
|
frame.origin.y + _CPWindowViewCornerResizeRectWidth,
|
|
_CPWindowViewResizeSlop * 2,
|
|
CGRectGetHeight(frame) - (_CPWindowViewCornerResizeRectWidth * 2));
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionLeft;
|
|
|
|
rect.origin.x = CGRectGetMaxX(frame) - _CPWindowViewResizeSlop;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionRight;
|
|
|
|
rect = CGRectMake(frame.origin.x + _CPWindowViewCornerResizeRectWidth,
|
|
frame.origin.y - _CPWindowViewResizeSlop,
|
|
CGRectGetWidth(frame) - (_CPWindowViewCornerResizeRectWidth * 2),
|
|
_CPWindowViewResizeSlop * 2);
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return isSheet ? _CPWindowViewResizeRegionNone : _CPWindowViewResizeRegionTop;
|
|
|
|
rect.origin.y = CGRectGetMaxY(frame) - _CPWindowViewResizeSlop;
|
|
|
|
if (CGRectContainsPoint(rect, aPoint))
|
|
return _CPWindowViewResizeRegionBottom;
|
|
}
|
|
|
|
return _CPWindowViewResizeRegionNone;
|
|
}
|
|
|
|
/*
|
|
aPoint is in window coordinates
|
|
*/
|
|
- (void)setCursorForLocation:(CGPoint)aPoint resizing:(BOOL)isResizing
|
|
{
|
|
var theWindow = [self window];
|
|
|
|
if ([theWindow isFullPlatformWindow] ||
|
|
!(_styleMask & CPResizableWindowMask) ||
|
|
(CPWindowResizeStyle !== CPWindowResizeStyleModern))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var globalPoint = [theWindow convertBaseToGlobal:aPoint],
|
|
resizeRegion = isResizing ? _resizeRegion : [self resizeRegionForPoint:globalPoint],
|
|
minSize = nil,
|
|
maxSize = nil,
|
|
frameSize;
|
|
|
|
if (resizeRegion !== _CPWindowViewResizeRegionNone)
|
|
{
|
|
minSize = [theWindow minSize];
|
|
maxSize = [theWindow maxSize];
|
|
frameSize = [theWindow frame].size;
|
|
}
|
|
|
|
switch (resizeRegion)
|
|
{
|
|
case _CPWindowViewResizeRegionTopLeft:
|
|
if (minSize && CGSizeEqualToSize(frameSize, minSize))
|
|
[[CPCursor resizeNorthwestCursor] set];
|
|
else if (maxSize && CGSizeEqualToSize(frameSize, maxSize))
|
|
[[CPCursor resizeSoutheastCursor] set];
|
|
else
|
|
[[CPCursor resizeNorthwestSoutheastCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionTop:
|
|
if (minSize && (frameSize.height === minSize.height))
|
|
[[CPCursor resizeUpCursor] set];
|
|
else if (maxSize && (frameSize.height === maxSize.height))
|
|
[[CPCursor resizeDownCursor] set];
|
|
else if ([CPMenu menuBarVisible] && (CGRectGetMinY([theWindow frame]) <= [CPMenu menuBarHeight]))
|
|
[[CPCursor resizeDownCursor] set];
|
|
else
|
|
[[CPCursor resizeNorthSouthCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionTopRight:
|
|
if (minSize && CGSizeEqualToSize(frameSize, minSize))
|
|
[[CPCursor resizeNortheastCursor] set];
|
|
else if (maxSize && CGSizeEqualToSize(frameSize, maxSize))
|
|
[[CPCursor resizeSouthwestCursor] set];
|
|
else
|
|
[[CPCursor resizeNortheastSouthwestCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionRight:
|
|
if (minSize && (frameSize.width === minSize.width))
|
|
[[CPCursor resizeRightCursor] set];
|
|
else if (maxSize && (frameSize.width === maxSize.width))
|
|
[[CPCursor resizeLeftCursor] set];
|
|
else
|
|
[[CPCursor resizeEastWestCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionBottomRight:
|
|
if (minSize && CGSizeEqualToSize(frameSize, minSize))
|
|
[[CPCursor resizeSoutheastCursor] set];
|
|
else if (maxSize && CGSizeEqualToSize(frameSize, maxSize))
|
|
[[CPCursor resizeNorthwestCursor] set];
|
|
else
|
|
[[CPCursor resizeNorthwestSoutheastCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionBottom:
|
|
if (minSize && (frameSize.height === minSize.height))
|
|
[[CPCursor resizeDownCursor] set];
|
|
else if (maxSize && (frameSize.height === maxSize.height))
|
|
[[CPCursor resizeUpCursor] set];
|
|
else
|
|
[[CPCursor resizeNorthSouthCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionBottomLeft:
|
|
if (minSize && CGSizeEqualToSize(frameSize, minSize))
|
|
[[CPCursor resizeSouthwestCursor] set];
|
|
else if (maxSize && CGSizeEqualToSize(frameSize, maxSize))
|
|
[[CPCursor resizeNortheastCursor] set];
|
|
else
|
|
[[CPCursor resizeNortheastSouthwestCursor] set];
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionLeft:
|
|
if (minSize && (frameSize.width === minSize.width))
|
|
[[CPCursor resizeLeftCursor] set];
|
|
else if (maxSize && (frameSize.width === maxSize.width))
|
|
[[CPCursor resizeRightCursor] set];
|
|
else
|
|
[[CPCursor resizeEastWestCursor] set];
|
|
break;
|
|
|
|
default:
|
|
[[CPCursor arrowCursor] set];
|
|
}
|
|
}
|
|
|
|
- (void)trackResizeWithEvent:(CPEvent)anEvent
|
|
{
|
|
var type = [anEvent type];
|
|
|
|
if (type === CPLeftMouseUp)
|
|
{
|
|
_cachedScreenFrame = nil;
|
|
return;
|
|
}
|
|
|
|
var location = [anEvent locationInWindow],
|
|
theWindow = [self window],
|
|
globalLocation = [theWindow convertBaseToGlobal:location],
|
|
frame = [theWindow frame];
|
|
|
|
if (type === CPLeftMouseDown)
|
|
{
|
|
_mouseDraggedPoint = CGPointMake(globalLocation.x, globalLocation.y);
|
|
_cachedFrame = CGRectMakeCopy(frame);
|
|
_cachedScreenFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
|
}
|
|
else if (type === CPLeftMouseDragged)
|
|
{
|
|
var deltaX = globalLocation.x - _mouseDraggedPoint.x,
|
|
deltaY = globalLocation.y - _mouseDraggedPoint.y,
|
|
startX = CGRectGetMinX(_cachedFrame),
|
|
startY = CGRectGetMinY(_cachedFrame),
|
|
startWidth = CGRectGetWidth(_cachedFrame),
|
|
startHeight = CGRectGetHeight(_cachedFrame),
|
|
newX,
|
|
newY,
|
|
newWidth,
|
|
newHeight,
|
|
resizeMinSize = [self _minimumResizeSize],
|
|
minSize = [theWindow minSize],
|
|
maxSize = [theWindow maxSize];
|
|
|
|
minSize = CGSizeMake(MAX(minSize.width, resizeMinSize.width), MAX(minSize.height, resizeMinSize.height));
|
|
|
|
// If it's a sheet, horizontal resizing is symmetrical, so the movement is effectively doubled
|
|
if (theWindow._isSheet)
|
|
deltaX *= 2;
|
|
|
|
// Calculate x and width first
|
|
switch (_resizeRegion)
|
|
{
|
|
case _CPWindowViewResizeRegionTopLeft:
|
|
case _CPWindowViewResizeRegionLeft:
|
|
case _CPWindowViewResizeRegionBottomLeft:
|
|
// If it is shrinking from the left edge, the maximum distance it can move
|
|
// is the distance between the original width and the min width.
|
|
if (deltaX > 0)
|
|
deltaX = MIN(startWidth - minSize.width, deltaX);
|
|
|
|
// If it is growing from the left edge, the maximum distance it can move
|
|
// is the distance between the original width and the max width.
|
|
else if (deltaX < 0)
|
|
deltaX = -MIN(maxSize.width - startWidth, ABS(deltaX));
|
|
|
|
if (theWindow._isSheet)
|
|
deltaX = FLOOR(deltaX / 2);
|
|
|
|
newX = startX + deltaX;
|
|
|
|
// Pin the left edge to the usable screen left edge
|
|
var pinnedX = MAX(newX, CGRectGetMinX(_cachedScreenFrame));
|
|
|
|
if (pinnedX !== newX)
|
|
{
|
|
deltaX += pinnedX - newX;
|
|
newX = pinnedX;
|
|
}
|
|
|
|
// When resizing from the left, we change the origin and the width
|
|
if (theWindow._isSheet)
|
|
deltaX *= 2;
|
|
|
|
newWidth = startWidth - deltaX;
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionTopRight:
|
|
case _CPWindowViewResizeRegionRight:
|
|
case _CPWindowViewResizeRegionBottomRight:
|
|
// If it is growing from the right edge, the maximum distance it can move
|
|
// is the distance between the original width and the max width.
|
|
if (deltaX > 0)
|
|
deltaX = MIN(maxSize.width - startWidth, deltaX);
|
|
|
|
// If it is shrinking from the right edge, the maximum distance it can move
|
|
// is the distance between the original width and the min width.
|
|
else if (deltaX < 0)
|
|
deltaX = -MIN(startWidth - minSize.width, ABS(deltaX));
|
|
|
|
if (theWindow._isSheet)
|
|
deltaX = FLOOR(deltaX / 2);
|
|
|
|
// Pin the right edge to the usable screen right edge
|
|
var newMaxX = startX + startWidth + deltaX,
|
|
pinnedX = MIN(newMaxX, CGRectGetMaxX(_cachedScreenFrame));
|
|
|
|
if (pinnedX !== newMaxX)
|
|
deltaX += pinnedX - newMaxX;
|
|
|
|
if (theWindow._isSheet)
|
|
{
|
|
newWidth = startWidth + (deltaX * 2);
|
|
newX = startX - FLOOR((newWidth - startWidth) / 2);
|
|
}
|
|
else
|
|
{
|
|
newWidth = startWidth + deltaX;
|
|
newX = startX;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
newX = startX;
|
|
newWidth = startWidth;
|
|
}
|
|
|
|
// Now calculate y and height
|
|
switch (_resizeRegion)
|
|
{
|
|
case _CPWindowViewResizeRegionTopLeft:
|
|
case _CPWindowViewResizeRegionTop:
|
|
case _CPWindowViewResizeRegionTopRight:
|
|
// If it is shrinking from the top edge, the maximum distance it can move
|
|
// is the distance between the original height and the min height.
|
|
if (deltaY > 0)
|
|
deltaY = MIN(startHeight - minSize.height, deltaY);
|
|
|
|
// If it is growing from the top edge, the maximum distance it can move
|
|
// is the distance between the original height and the max height.
|
|
else if (deltaY < 0)
|
|
deltaY = -MIN(maxSize.height - startHeight, ABS(deltaY));
|
|
|
|
newY = startY + deltaY;
|
|
|
|
// Pin the top edge to the usable screen top edge
|
|
var pinnedY = MAX(newY, CGRectGetMinY(_cachedScreenFrame));
|
|
|
|
if (pinnedY !== newY)
|
|
{
|
|
deltaY += pinnedY - newY;
|
|
newY = pinnedY;
|
|
}
|
|
|
|
// When resizing from the top, change the origin and the height
|
|
newHeight = startHeight - deltaY;
|
|
break;
|
|
|
|
case _CPWindowViewResizeRegionBottomLeft:
|
|
case _CPWindowViewResizeRegionBottom:
|
|
case _CPWindowViewResizeRegionBottomRight:
|
|
// If it is growing from the bottom edge, the maximum distance it can move
|
|
// is the distance between the original height and the max height.
|
|
if (deltaY > 0)
|
|
deltaY = MIN(maxSize.height - startHeight, deltaY);
|
|
|
|
// If it is shrinking from the bottom edge, the maximum distance it can move
|
|
// is the distance between the original height and the min height.
|
|
else if (deltaY < 0)
|
|
deltaY = -MIN(startHeight - minSize.height, ABS(deltaY));
|
|
|
|
newY = startY;
|
|
|
|
// Pin the bottom edge to the usable screen bottom edge
|
|
var newMaxY = startY + startHeight + deltaY,
|
|
pinnedY = MIN(newMaxY, CGRectGetMaxY(_cachedScreenFrame));
|
|
|
|
if (pinnedY !== newMaxY)
|
|
deltaY += pinnedY - newMaxY;
|
|
|
|
// When resizing from the bottom, change only the height
|
|
newHeight = startHeight + deltaY;
|
|
break;
|
|
|
|
default:
|
|
newY = startY;
|
|
newHeight = startHeight;
|
|
}
|
|
|
|
// When resizing, we always constrain to the usable screen.
|
|
frame = CGRectMake(newX, newY, newWidth, newHeight);
|
|
|
|
var constrainedFrame = [theWindow _constrainOriginOfFrame:frame],
|
|
dx = constrainedFrame.origin.x - frame.origin.x,
|
|
dy = constrainedFrame.origin.y - frame.origin.y;
|
|
|
|
// When resizing from the left or top, we adjust the origin and size.
|
|
switch (_resizeRegion)
|
|
{
|
|
case _CPWindowViewResizeRegionBottomLeft:
|
|
case _CPWindowViewResizeRegionLeft:
|
|
case _CPWindowViewResizeRegionTopLeft:
|
|
case _CPWindowViewResizeRegionTop:
|
|
case _CPWindowViewResizeRegionTopRight:
|
|
frame.origin = constrainedFrame.origin;
|
|
frame.size.width -= dx;
|
|
frame.size.height -= dy;
|
|
}
|
|
|
|
// When resizing from the right or bottom, we only adjust the size.
|
|
switch (_resizeRegion)
|
|
{
|
|
case _CPWindowViewResizeRegionTopRight:
|
|
case _CPWindowViewResizeRegionRight:
|
|
case _CPWindowViewResizeRegionBottomRight:
|
|
case _CPWindowViewResizeRegionBottom:
|
|
frame.size.width += dx;
|
|
frame.size.height += dy;
|
|
}
|
|
|
|
[theWindow _setFrame:frame display:YES animate:NO constrainWidth:NO constrainHeight:NO];
|
|
[self setCursorForLocation:location resizing:YES];
|
|
}
|
|
|
|
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
|
}
|
|
|
|
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
|
{
|
|
var theWindow = [self window];
|
|
|
|
if (![theWindow isMovable])
|
|
return;
|
|
|
|
var type = [anEvent type];
|
|
|
|
if (type === CPLeftMouseUp)
|
|
{
|
|
return;
|
|
}
|
|
else if (type === CPLeftMouseDown)
|
|
{
|
|
_mouseDraggedPoint = [theWindow convertBaseToGlobal:[anEvent locationInWindow]];
|
|
_cachedFrame = CGRectMakeCopy([theWindow frame]);
|
|
}
|
|
else if (type === CPLeftMouseDragged)
|
|
{
|
|
var theWindow = [self window],
|
|
location = [theWindow convertBaseToGlobal:[anEvent locationInWindow]],
|
|
deltaX = location.x - _mouseDraggedPoint.x,
|
|
deltaY = location.y - _mouseDraggedPoint.y,
|
|
origin = CGPointMake(_cachedFrame.origin.x + deltaX, _cachedFrame.origin.y + deltaY);
|
|
|
|
[theWindow setFrameOrigin:origin];
|
|
}
|
|
|
|
[CPApp setTarget:self selector:@selector(trackMoveWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
|
}
|
|
|
|
- (void)setFrameSize:(CGSize)newSize
|
|
{
|
|
[super setFrameSize:newSize];
|
|
|
|
// reposition sheet if the parent window resizes or moves
|
|
var theWindow = [self window],
|
|
sheet = [theWindow attachedSheet];
|
|
|
|
if (sheet)
|
|
{
|
|
[theWindow _setAttachedSheetFrameOrigin];
|
|
[sheet._windowView _adjustShadowViewSize];
|
|
}
|
|
else if (theWindow && theWindow._isSheet)
|
|
[self _adjustShadowViewSize];
|
|
}
|
|
|
|
- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
|
|
{
|
|
if (shouldShowResizeIndicator && CPWindowResizeStyle === CPWindowResizeStyleLegacy)
|
|
{
|
|
_resizeIndicator = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
|
[_resizeIndicator setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin];
|
|
|
|
[self addSubview:_resizeIndicator];
|
|
}
|
|
else
|
|
{
|
|
[_resizeIndicator removeFromSuperview];
|
|
|
|
_resizeIndicator = nil;
|
|
}
|
|
|
|
[self setNeedsLayout];
|
|
}
|
|
|
|
- (BOOL)showsResizeIndicator
|
|
{
|
|
return _resizeIndicator !== nil;
|
|
}
|
|
|
|
- (void)setResizeIndicatorOffset:(CGSize)anOffset
|
|
{
|
|
if (CGSizeEqualToSize(_resizeIndicatorOffset, anOffset))
|
|
return;
|
|
|
|
_resizeIndicatorOffset = anOffset;
|
|
|
|
if (!_resizeIndicator)
|
|
return;
|
|
|
|
var size = [_resizeIndicator frame].size,
|
|
boundsSize = [self frame].size;
|
|
|
|
[_resizeIndicator setFrameOrigin:CGPointMake(boundsSize.width - size.width - anOffset.width, boundsSize.height - size.height - anOffset.height)];
|
|
}
|
|
|
|
- (CGSize)resizeIndicatorOffset
|
|
{
|
|
return _resizeIndicatorOffset;
|
|
}
|
|
|
|
- (void)windowDidChangeDocumentEdited
|
|
{
|
|
}
|
|
|
|
- (void)windowDidChangeDocumentSaving
|
|
{
|
|
}
|
|
|
|
- (BOOL)showsToolbar
|
|
{
|
|
var styleMaskWindow = [[self window] styleMask];
|
|
|
|
return styleMaskWindow & CPBorderlessWindowMask || styleMaskWindow & CPTitledWindowMask || styleMaskWindow & CPHUDBackgroundWindowMask || styleMaskWindow & CPBorderlessBridgeWindowMask;
|
|
}
|
|
|
|
- (CGSize)toolbarOffset
|
|
{
|
|
return _toolbarOffset;
|
|
}
|
|
|
|
- (CPColor)toolbarLabelColor
|
|
{
|
|
return [CPColor blackColor];
|
|
}
|
|
|
|
- (float)toolbarMaxY
|
|
{
|
|
if (!_toolbarView || [_toolbarView isHidden])
|
|
return [self toolbarOffset].height;
|
|
|
|
return CGRectGetMaxY([_toolbarView frame]);
|
|
}
|
|
|
|
- (_CPToolbarView)toolbarView
|
|
{
|
|
return _toolbarView;
|
|
}
|
|
|
|
- (void)tile
|
|
{
|
|
var theWindow = [self window],
|
|
bounds = [self bounds],
|
|
width = CGRectGetWidth(bounds);
|
|
|
|
if ([[theWindow toolbar] isVisible])
|
|
{
|
|
var toolbarView = [self toolbarView],
|
|
toolbarOffset = [self toolbarOffset];
|
|
|
|
[toolbarView setFrame:CGRectMake(toolbarOffset.width, toolbarOffset.height, width, CGRectGetHeight([toolbarView frame]))];
|
|
}
|
|
|
|
if ([self showsResizeIndicator])
|
|
{
|
|
var size = [_resizeIndicator frame].size,
|
|
boundsSize = [self bounds].size;
|
|
|
|
[_resizeIndicator setFrameOrigin:CGPointMake(boundsSize.width - size.width - _resizeIndicatorOffset.width, boundsSize.height - size.height - _resizeIndicatorOffset.height)];
|
|
}
|
|
}
|
|
|
|
- (void)noteToolbarChanged
|
|
{
|
|
var theWindow = [self window],
|
|
toolbar = [theWindow toolbar],
|
|
toolbarView = [toolbar _toolbarView];
|
|
|
|
if (_toolbarView !== toolbarView)
|
|
{
|
|
[_toolbarView removeFromSuperview];
|
|
|
|
if (toolbarView)
|
|
{
|
|
[toolbarView removeFromSuperview];
|
|
[toolbarView FIXME_setIsHUD:_styleMask & CPHUDBackgroundWindowMask];
|
|
|
|
[self addSubview:toolbarView];
|
|
}
|
|
|
|
_toolbarView = toolbarView;
|
|
}
|
|
|
|
[toolbarView setHidden:![self showsToolbar] || ![toolbar isVisible]];
|
|
|
|
if (theWindow)
|
|
{
|
|
var contentRect = [self convertRect:[[theWindow contentView] frame] toView:nil];
|
|
|
|
contentRect.origin = [theWindow convertBaseToGlobal:contentRect.origin];
|
|
|
|
[self setAutoresizesSubviews:NO];
|
|
[theWindow setFrame:[theWindow frameRectForContentRect:contentRect]];
|
|
[self setAutoresizesSubviews:YES];
|
|
}
|
|
|
|
[self tile];
|
|
}
|
|
|
|
- (void)noteKeyWindowStateChanged
|
|
{
|
|
if ([[self window] isKeyWindow])
|
|
[self setThemeState:CPThemeStateKeyWindow];
|
|
else
|
|
[self unsetThemeState:CPThemeStateKeyWindow];
|
|
}
|
|
|
|
- (void)noteMainWindowStateChanged
|
|
{
|
|
if ([[self window] isMainWindow])
|
|
[self setThemeState:CPThemeStateMainWindow];
|
|
else
|
|
[self unsetThemeState:CPThemeStateMainWindow];
|
|
}
|
|
|
|
/*
|
|
- (void)setAnimatingToolbar:(BOOL)isAnimatingToolbar
|
|
{
|
|
_isAnimatingToolbar = isAnimatingToolbar;
|
|
}
|
|
|
|
- (BOOL)isAnimatingToolbar
|
|
{
|
|
return _isAnimatingToolbar;
|
|
}
|
|
*/
|
|
|
|
- (void)didAddSubview:(CPView)aView
|
|
{
|
|
if (!_resizeIndicator || aView === _resizeIndicator)
|
|
return;
|
|
|
|
[self addSubview:_resizeIndicator];
|
|
}
|
|
|
|
- (void)_enableSheet:(BOOL)enable inWindow:(CPWindow)parentWindow
|
|
{
|
|
if (enable)
|
|
{
|
|
// Clip the shadow view width to the parent's content view
|
|
var myWidth = [self bounds].size.width,
|
|
shadowWidth = [self _shadowViewWidthForParentWindow:parentWindow],
|
|
shadowHeight = [self currentValueForThemeAttribute:@"shadow-height"];
|
|
|
|
_sheetShadowView = [[CPView alloc] initWithFrame:CGRectMake(FLOOR((myWidth - shadowWidth) / 2), 0, shadowWidth, shadowHeight)];
|
|
[_sheetShadowView setAutoresizingMask:CPViewWidthSizable];
|
|
[self addSubview:_sheetShadowView];
|
|
}
|
|
else
|
|
{
|
|
[_sheetShadowView removeFromSuperview];
|
|
_sheetShadowView = nil;
|
|
}
|
|
|
|
[self setNeedsLayout];
|
|
}
|
|
|
|
- (CGRect)_shadowViewWidthForParentWindow:(CPWindow)parentWindow
|
|
{
|
|
var myWidth = [self bounds].size.width,
|
|
parentWidth = [[parentWindow contentView] bounds].size.width;
|
|
|
|
return MIN(myWidth, parentWidth);
|
|
}
|
|
|
|
- (void)layoutSubviews
|
|
{
|
|
[super layoutSubviews];
|
|
|
|
[_sheetShadowView setBackgroundColor:[self valueForThemeAttribute:@"attached-sheet-shadow-color"]];
|
|
|
|
if (_resizeIndicator)
|
|
{
|
|
var size = [self valueForThemeAttribute:@"size-indicator"],
|
|
boundsSize = [self frame].size;
|
|
|
|
[_resizeIndicator setFrame:CGRectMake(boundsSize.width - size.width - _resizeIndicatorOffset.width, boundsSize.height - size.height - _resizeIndicatorOffset.height, size.width, size.height)];
|
|
[_resizeIndicator setImage:[self valueForThemeAttribute:@"resize-indicator"]];
|
|
}
|
|
}
|
|
|
|
- (void)_adjustShadowViewSize
|
|
{
|
|
if (!_sheetShadowView)
|
|
return;
|
|
|
|
var myWidth = [self frame].size.width,
|
|
shadowFrame = [_sheetShadowView frame],
|
|
shadowWidth = [self _shadowViewWidthForParentWindow:_window._parentView];
|
|
|
|
shadowFrame.origin.x = FLOOR((myWidth - shadowWidth) / 2);
|
|
shadowFrame.size.width = shadowWidth;
|
|
[_sheetShadowView setFrame:shadowFrame];
|
|
}
|
|
|
|
- (CGSize)_minimumResizeSize
|
|
{
|
|
// Leave at least 4px so there is something visible.
|
|
return CGSizeMake(4, _CPWindowViewMinContentHeight);
|
|
}
|
|
|
|
- (int)bodyOffset
|
|
{
|
|
return [self frame].origin.y;
|
|
}
|
|
|
|
@end
|