mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-30 03:48:49 +00:00
CPSplitView initial commit
Reviewed by Francisco [#46 state:resolved responsible:tlrobinson]
This commit is contained in:
1 parent
53d4a714e2
commit
dbc0999f66
3 files changed
+407
No files matched your search
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* CPSplitView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Thomas Robinson.
|
||||
* 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 "CPImage.j"
|
||||
import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||
|
||||
CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
|
||||
CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
|
||||
|
||||
var CPSplitViewHorizontalImage = nil,
|
||||
CPSplitViewVerticalImage = nil;
|
||||
|
||||
@implementation CPSplitView : CPView
|
||||
{
|
||||
id _delegate;
|
||||
BOOL _isVertical;
|
||||
BOOL _isPaneSplitter;
|
||||
|
||||
int _currentDivider;
|
||||
float _initialOffset;
|
||||
|
||||
CPString _originComponent;
|
||||
CPString _sizeComponent;
|
||||
|
||||
CPArray _DOMDividerElements;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
//+ (void)initialize
|
||||
//{
|
||||
// if (self != [CPSplitView class])
|
||||
// return;
|
||||
//
|
||||
// var bundle = [CPBundle bundleForClass:self];
|
||||
//
|
||||
// CPSplitViewHorizontalImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSplitView/CPSplitViewHorizontal.png"] size:CPSizeMake(5.0, 10.0)];
|
||||
// CPSplitViewVerticalImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSplitView/CPSplitViewVertical.png"] size:CPSizeMake(10.0, 5.0)];
|
||||
//}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_currentDivider = CPNotFound;
|
||||
|
||||
_DOMDividerElements = [];
|
||||
|
||||
[self setVertical:YES];
|
||||
[self setIsPaneSplitter:YES];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (float)dividerThickness
|
||||
{
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
- (BOOL)isVertical
|
||||
{
|
||||
return _isVertical;
|
||||
}
|
||||
- (void)setVertical:(BOOL)flag
|
||||
{
|
||||
_isVertical = flag;
|
||||
|
||||
_originComponent = [self isVertical] ? "x" : "y";
|
||||
_sizeComponent = [self isVertical] ? "width" : "height";
|
||||
|
||||
[self adjustSubviews];
|
||||
}
|
||||
|
||||
- (BOOL)isPaneSplitter
|
||||
{
|
||||
return _isPaneSplitter;
|
||||
}
|
||||
- (void)setIsPaneSplitter:(BOOL)flag
|
||||
{
|
||||
_isPaneSplitter = flag;
|
||||
}
|
||||
|
||||
- (void)didAddSubview:(CPView)subview
|
||||
{
|
||||
[self adjustSubviews];
|
||||
}
|
||||
|
||||
- (void)adjustSubviews
|
||||
{
|
||||
var frame = [self frame],
|
||||
dividerThickness = [self dividerThickness];
|
||||
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
var eachSize = (frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length;
|
||||
|
||||
for (var i = 0; i < [_subviews count]; i++)
|
||||
{
|
||||
if ([self isVertical])
|
||||
[_subviews[i] setFrame:CGRectMake(Math.round((eachSize + dividerThickness) * i), 0, Math.round(eachSize), frame.size.height)];
|
||||
else
|
||||
[_subviews[i] setFrame:CGRectMake(0, Math.round((eachSize + dividerThickness) * i), frame.size.width, Math.round(eachSize))];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
[self _postNotificationDidResize];
|
||||
}
|
||||
|
||||
- (BOOL)isSubviewCollapsed:(CPView)subview
|
||||
{
|
||||
return [subview frame].size[_sizeComponent] < 1 ? YES : NO;
|
||||
}
|
||||
|
||||
- (CGRect)rectOfDividerAtIndex:(int)aDivider
|
||||
{
|
||||
var frame = [_subviews[aDivider] frame];
|
||||
|
||||
var rect = CGRectMakeZero();
|
||||
rect.size = CPSizeCreateCopy([self frame].size);
|
||||
|
||||
rect.size[_sizeComponent] = [self dividerThickness];
|
||||
rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
|
||||
{
|
||||
var realRect = [self rectOfDividerAtIndex:aDivider];
|
||||
|
||||
realRect.size[_sizeComponent] += 4;
|
||||
realRect.origin[_originComponent] -= 2;
|
||||
|
||||
return realRect;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var dividerPath = [self isVertical] ? [CPSplitViewVerticalImage filename] : [CPSplitViewHorizontalImage filename];
|
||||
|
||||
var count = [_subviews count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (!_DOMDividerElements[i])
|
||||
{
|
||||
_DOMDividerElements[i] = document.createElement("div");
|
||||
_DOMDividerElements[i].style.cursor = "move";
|
||||
_DOMDividerElements[i].style.position = "absolute";
|
||||
_DOMDividerElements[i].style.backgroundRepeat = "repeat";
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[i]);
|
||||
}
|
||||
|
||||
var frame = [self rectOfDividerAtIndex:i];
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[i], NULL, _CGRectGetMinX(frame), _CGRectGetMinY(frame));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[i], _CGRectGetWidth(frame), _CGRectGetHeight(frame));
|
||||
|
||||
_DOMDividerElements[i].style.backgroundImage = "url('"+dividerPath+"')";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)drawDividerInRect:(CGRect)aRect
|
||||
{
|
||||
// TODO: figure out a way to "draw" dividers using DOM element
|
||||
}
|
||||
|
||||
- (BOOL)cursorAtPoint:(CPPoint)aPoint hitDividerAtIndex:(int)anIndex
|
||||
{
|
||||
var frame = [_subviews[anIndex] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
|
||||
effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
|
||||
additionalRect = null;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
|
||||
effectiveRect = [_delegate splitView:self effectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
|
||||
additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
|
||||
|
||||
return CGRectContainsPoint(effectiveRect, aPoint) || (additionalRect && CGRectContainsPoint(additionalRect, aPoint));
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)theEvent
|
||||
{
|
||||
var location = [theEvent locationInWindow],
|
||||
point = [self convertPoint:location fromView:nil];
|
||||
|
||||
for (var i = 0; i < [_subviews count] - 1; i++)
|
||||
{
|
||||
var frame = [_subviews[i] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
|
||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||
{
|
||||
if ([theEvent clickCount] == 2 &&
|
||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
||||
[_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
|
||||
{
|
||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i];
|
||||
|
||||
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i]])
|
||||
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:minPosition ofDividerAtIndex:i];
|
||||
}
|
||||
else if ([_delegate splitView:self canCollapseSubview:_subviews[i+1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i+1] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i+1]])
|
||||
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:maxPosition ofDividerAtIndex:i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentDivider = i;
|
||||
_initialOffset = startPosition - point[_originComponent];
|
||||
|
||||
[self _postNotificationWillResize];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
_currentDivider = CPNotFound;
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)theEvent
|
||||
{
|
||||
if (_currentDivider != CPNotFound)
|
||||
{
|
||||
var location = [theEvent locationInWindow],
|
||||
point = [self convertPoint:location fromView:nil];
|
||||
|
||||
[self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)theEvent
|
||||
{
|
||||
if (_currentDivider != CPNotFound)
|
||||
{
|
||||
_currentDivider = CPNotFound;
|
||||
[self _postNotificationDidResize];
|
||||
}
|
||||
}
|
||||
|
||||
- (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
var frame = [_subviews[dividerIndex + 1] frame];
|
||||
|
||||
if (dividerIndex + 1 < [_subviews count] - 1)
|
||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
|
||||
else
|
||||
return [self frame].size[_sizeComponent] - [self dividerThickness];
|
||||
}
|
||||
|
||||
- (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
var frame = [_subviews[dividerIndex - 1] frame];
|
||||
|
||||
if (dividerIndex > 0)
|
||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
// not sure where this should override other positions?
|
||||
if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
|
||||
position = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
|
||||
|
||||
var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||
proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||
actualMax = proposedMax,
|
||||
actualMin = proposedMin;
|
||||
|
||||
if([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
||||
actualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
|
||||
|
||||
if([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
||||
actualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
||||
|
||||
var frame = [self frame],
|
||||
viewA = _subviews[dividerIndex],
|
||||
frameA = CGRectCreateCopy([viewA frame]),
|
||||
viewB = _subviews[dividerIndex+1],
|
||||
frameB = CGRectCreateCopy([viewB frame]);
|
||||
|
||||
var realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||
|
||||
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
||||
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
||||
if ([_delegate splitView:self canCollapseSubview:viewA])
|
||||
realPosition = proposedMin;
|
||||
|
||||
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
|
||||
[_subviews[dividerIndex] setFrame:frameA];
|
||||
|
||||
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
|
||||
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
|
||||
[_subviews[dividerIndex+1] setFrame:frameB];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CPSize)oldSize
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
|
||||
{
|
||||
[_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
|
||||
return;
|
||||
}
|
||||
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
var frame = CGRectCreateCopy([self frame]);
|
||||
|
||||
for (var i = 0; i < [_subviews count]; i++)
|
||||
{
|
||||
var view = [_subviews objectAtIndex:i],
|
||||
newFrame = CGRectCreateCopy(frame);
|
||||
|
||||
if(i + 1 == [_subviews count])
|
||||
newFrame.size[_sizeComponent] = frame.size[_sizeComponent] - newFrame.origin[_originComponent];
|
||||
else
|
||||
newFrame.size[_sizeComponent] = frame.size[_sizeComponent] * ([view frame].size[_sizeComponent] / oldSize[_sizeComponent]);
|
||||
|
||||
frame.origin[_originComponent] += newFrame.size[_sizeComponent];
|
||||
frame.origin[_originComponent] += [self dividerThickness];
|
||||
|
||||
[view setFrame:newFrame];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
[self _postNotificationDidResize];
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)delegate
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||
|
||||
_delegate = delegate;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] addObserver:_delegate
|
||||
selector:@selector(splitViewDidResizeSubviews:)
|
||||
name:CPSplitViewDidResizeSubviewsNotification
|
||||
object:self];
|
||||
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] addObserver:_delegate
|
||||
selector:@selector(splitViewWillResizeSubviews:)
|
||||
name:CPSplitViewWillResizeSubviewsNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)_postNotificationWillResize
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||
}
|
||||
|
||||
- (void)_postNotificationDidResize
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// FIXME: once +initialize is fixed for superclasses, remove this and uncomment +initialize
|
||||
var bundle = [CPBundle bundleForClass:CPSplitView];
|
||||
CPSplitViewHorizontalImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSplitView/CPSplitViewHorizontal.png"] size:CPSizeMake(5.0, 10.0)];
|
||||
CPSplitViewVerticalImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSplitView/CPSplitViewVertical.png"] size:CPSizeMake(10.0, 5.0)];
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 144 B |
Binary file not shown.
|
After Width: | Height: | Size: 133 B |
Reference in new issue
Block a user