mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-24 17:18:54 +00:00
A text container that tracked its text view's size took the text view's full width, while the text started after the text container inset, so lines could run past the visible right edge. It now follows Cocoa: a tracking container fills the text view minus the inset on both sides. * CPTextView/CPTextContainer.j: Subtract the text container inset on both sides when tracking the text view's width and height. * CPTextView/CPTextView.j: Refit the text container when a text view is created and when its text container inset changes.
310 lines
8.2 KiB
Plaintext
310 lines
8.2 KiB
Plaintext
/*
|
|
* CPTextContainer.j
|
|
* AppKit
|
|
*
|
|
* Created by Emmanuel Maillard on 27/02/2010.
|
|
* Copyright Emmanuel Maillard 2010.
|
|
*
|
|
* 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 <Foundation/CPGeometry.j>
|
|
@import "CPLayoutManager.j"
|
|
|
|
@class CPTextView
|
|
@class CPLayoutManager
|
|
|
|
/*
|
|
@global
|
|
@group CPLineSweepDirection
|
|
*/
|
|
CPLineSweepLeft = 0;
|
|
/*
|
|
@global
|
|
@group CPLineSweepDirection
|
|
*/
|
|
CPLineSweepRight = 1;
|
|
/*
|
|
@global
|
|
@group CPLineSweepDirection
|
|
*/
|
|
CPLineSweepDown = 2;
|
|
/*
|
|
@global
|
|
@group CPLineSweepDirection
|
|
*/
|
|
CPLineSweepUp = 3;
|
|
|
|
/*
|
|
@global
|
|
@group CPLineMovementDirection
|
|
*/
|
|
CPLineDoesntMoves = 0;
|
|
/*
|
|
@global
|
|
@group CPLineMovementDirection
|
|
*/
|
|
CPLineMovesLeft = 1;
|
|
/*
|
|
@global
|
|
@group CPLineMovementDirection
|
|
*/
|
|
CPLineMovesRight = 2;
|
|
/*
|
|
@global
|
|
@group CPLineMovementDirection
|
|
*/
|
|
CPLineMovesDown = 3;
|
|
/*
|
|
@global
|
|
@group CPLineMovementDirection
|
|
*/
|
|
CPLineMovesUp = 4;
|
|
|
|
/*!
|
|
@ingroup appkit
|
|
@class CPTextContainer
|
|
*/
|
|
@implementation CPTextContainer : CPObject
|
|
{
|
|
float _lineFragmentPadding @accessors(property=lineFragmentPadding);
|
|
CGSize _size @accessors(property=containerSize);
|
|
CPLayoutManager _layoutManager @accessors(property=layoutManager);
|
|
CPTextView _textView @accessors(property=textView);
|
|
BOOL _inResizing;
|
|
BOOL _widthTracksTextView;
|
|
BOOL _heightTracksTextView;
|
|
}
|
|
|
|
|
|
// MARK: -
|
|
// MARK: Init methods
|
|
|
|
- (id)initWithContainerSize:(CGSize)aSize
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_size = aSize;
|
|
[self _init];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
return [self initWithContainerSize:CPMakeSize(1e7, 1e7)];
|
|
}
|
|
|
|
- (void)_init
|
|
{
|
|
_lineFragmentPadding = 0.0;
|
|
|
|
_layoutManager = [[CPLayoutManager alloc] init];
|
|
[_layoutManager addTextContainer:self];
|
|
}
|
|
|
|
// MARK: -
|
|
// MARK: Setter methods
|
|
|
|
- (void)setContainerSize:(CGSize)someSize
|
|
{
|
|
var oldSize = _size;
|
|
|
|
_size = CGSizeMakeCopy(someSize);
|
|
|
|
if (oldSize.width != _size.width)
|
|
{
|
|
_inResizing = YES;
|
|
[_layoutManager invalidateLayoutForCharacterRange:CPMakeRange(0, [[_layoutManager textStorage] length])
|
|
isSoft:NO
|
|
actualCharacterRange:NULL];
|
|
|
|
[_layoutManager _validateLayoutAndGlyphs];
|
|
[_textView sizeToFit]; // this is necessary to adopt the height of CPTextView in case of rewrapping
|
|
_inResizing = NO;
|
|
}
|
|
}
|
|
|
|
// Controls whether the receiver adjusts the width of its bounding rectangle when its text view is resized.
|
|
- (BOOL)widthTracksTextView
|
|
{
|
|
return _widthTracksTextView;
|
|
}
|
|
|
|
- (void)setWidthTracksTextView:(BOOL)flag
|
|
{
|
|
if (_widthTracksTextView === flag)
|
|
return;
|
|
|
|
_widthTracksTextView = flag;
|
|
[self _updateFrameObserver];
|
|
}
|
|
|
|
// Controls whether the receiver adjusts the height of its bounding rectangle when its text view is resized.
|
|
- (BOOL)heightTracksTextView
|
|
{
|
|
return _heightTracksTextView;
|
|
}
|
|
|
|
- (void)setHeightTracksTextView:(BOOL)flag
|
|
{
|
|
if (_heightTracksTextView === flag)
|
|
return;
|
|
|
|
_heightTracksTextView = flag;
|
|
[self _updateFrameObserver];
|
|
}
|
|
|
|
- (void)_updateFrameObserver
|
|
{
|
|
if (_textView)
|
|
{
|
|
[[CPNotificationCenter defaultCenter] removeObserver:self
|
|
name:CPViewFrameDidChangeNotification
|
|
object:_textView];
|
|
|
|
var flag = _widthTracksTextView || _heightTracksTextView;
|
|
[_textView setPostsFrameChangedNotifications:flag];
|
|
|
|
if (flag)
|
|
{
|
|
[[CPNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(textViewFrameChanged:)
|
|
name:CPViewFrameDidChangeNotification
|
|
object:_textView];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)textViewFrameChanged:(CPNotification)aNotification
|
|
{
|
|
// As in Cocoa, a tracking container fills the text view inside its text container inset on both sides.
|
|
var newSize = CGSizeMake(_size.width, _size.height),
|
|
frameSize = [_textView frame].size,
|
|
inset = [_textView textContainerInset];
|
|
|
|
if (_widthTracksTextView)
|
|
newSize.width = MAX(0.0, frameSize.width - 2.0 * inset.width);
|
|
|
|
if (_heightTracksTextView)
|
|
newSize.height = MAX(0.0, frameSize.height - 2.0 * inset.height);
|
|
|
|
[self setContainerSize:newSize];
|
|
}
|
|
|
|
- (void)setTextView:(CPTextView)aTextView
|
|
{
|
|
if (_textView)
|
|
{
|
|
[[CPNotificationCenter defaultCenter] removeObserver:self
|
|
name:CPViewFrameDidChangeNotification
|
|
object:_textView];
|
|
[_textView setTextContainer:nil];
|
|
}
|
|
|
|
_textView = aTextView;
|
|
|
|
if (_textView)
|
|
{
|
|
[self _updateFrameObserver];
|
|
[_textView setTextContainer:self];
|
|
}
|
|
|
|
[_layoutManager textContainerChangedTextView:self];
|
|
}
|
|
|
|
- (BOOL)containsPoint:(CGPoint)aPoint
|
|
{
|
|
return CGRectContainsPoint(CGRectMake(0, 0, _size.width, _size.height), aPoint);
|
|
}
|
|
|
|
- (BOOL)isSimpleRectangularTextContainer
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect
|
|
sweepDirection:(CPLineSweepDirection)sweep
|
|
movementDirection:(CPLineMovementDirection)movement
|
|
remainingRect:(CGRectPointer)remainingRect
|
|
{
|
|
var resultRect = CGRectCreateCopy(proposedRect);
|
|
|
|
if (sweep != CPLineSweepRight || movement != CPLineMovesDown)
|
|
{
|
|
CPLog.trace(@"FIXME: unsupported sweep (" + sweep + ") or movement (" + movement + ")");
|
|
return CGRectMakeZero();
|
|
}
|
|
|
|
if (resultRect.origin.x + resultRect.size.width > _size.width)
|
|
resultRect.size.width = _size.width - resultRect.origin.x;
|
|
|
|
if (resultRect.size.width < 0)
|
|
resultRect = CGRectMakeZero();
|
|
|
|
if (remainingRect)
|
|
{
|
|
remainingRect.origin.x = resultRect.origin.x + resultRect.size.width;
|
|
remainingRect.origin.y = resultRect.origin.y;
|
|
remainingRect.size.height = resultRect.size.height;
|
|
remainingRect.size.width = _size.width - (resultRect.origin.x + resultRect.size.width);
|
|
}
|
|
|
|
return resultRect;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
var CPTextContainerSizeKey = @"CPTextContainerSizeKey",
|
|
CPTextContainerLayoutManagerKey = @"CPTextContainerLayoutManagerKey",
|
|
CPTextContainerWidthTracksTextViewKey = @"CPTextContainerWidthTracksTextViewKey",
|
|
CPTextContainerHeightTracksTextViewKey = @"CPTextContainerHeightTracksTextViewKey";
|
|
|
|
@implementation CPTextContainer (CPCoding)
|
|
|
|
- (id)initWithCoder:(CPCoder)aCoder
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
[self _init];
|
|
|
|
_size = [aCoder decodeSizeForKey:CPTextContainerSizeKey];
|
|
|
|
_layoutManager = [aCoder decodeObjectForKey:CPTextContainerLayoutManagerKey];
|
|
[_layoutManager addTextContainer:self];
|
|
|
|
_widthTracksTextView = [aCoder decodeBoolForKey:CPTextContainerWidthTracksTextViewKey];
|
|
_heightTracksTextView = [aCoder decodeBoolForKey:CPTextContainerHeightTracksTextViewKey];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
[aCoder encodeSize:_size forKey:CPTextContainerSizeKey];
|
|
[aCoder encodeObject:_layoutManager forKey:CPTextContainerLayoutManagerKey];
|
|
[aCoder encodeBool:_widthTracksTextView forKey:CPTextContainerWidthTracksTextViewKey];
|
|
[aCoder encodeBool:_heightTracksTextView forKey:CPTextContainerHeightTracksTextViewKey];
|
|
}
|
|
|
|
@end
|