Skip to content

Instantly share code, notes, and snippets.

@saikat
Created July 29, 2009 01:57
Show Gist options
  • Save saikat/157815 to your computer and use it in GitHub Desktop.
Save saikat/157815 to your computer and use it in GitHub Desktop.
// Attempt at a themeable CPTextView
@import <AppKit/CPView.j>
@import <AppKit/CPTextField.j>
// Code extended from code at http://gist.github.com/9848
@implementation CPTextView : CPControl
{
DOMElement FIXME_textArea;
id _delegate;
CPScrollView _scrollView;
CPView _contentView;
JSObject _existingSelectStart;
BOOL _alreadyFired;
CPTextFieldBezelStyle _bezelStyle;
}
+ (id)themeAttributes
{
return [CPDictionary dictionaryWithObjects:[CGInsetMakeZero(), CGInsetMake(2.0, 2.0, 2.0, 2.0), nil]
forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
}
-(id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame: aFrame];
FIXME_textArea = document.createElement("textarea");
FIXME_textArea.style.width = "100%";
FIXME_textArea.style.height = "100%";
if(document.selection)
FIXME_textArea.style.overflow = "auto";
else
FIXME_textArea.style.overflow = "hidden";
FIXME_textArea.style.position = "absolute";
FIXME_textArea.style.left = "0";
FIXME_textArea.style.top = "0";
FIXME_textArea.style.border = "0";
FIXME_textArea.style.margin = "0";
FIXME_textArea.style.padding = "0";
FIXME_textArea.style.backgroundColor = "rgb(255, 255, 255)";
FIXME_textArea.style.fontSize = "14px";
FIXME_textArea.style.fontFamily = "Helvetica";
FIXME_textArea.style.resize = "none";
FIXME_textArea.style.outlineStyle = "none";
FIXME_textArea.onkeydown = function() {
[self textDidChange: self];
};
FIXME_textArea.onkeypress = function() {
[self textDidChange: self];
};
FIXME_textArea.onkeyup = function() {
[self textDidChange: self];
};
if(document.attachEvent)
{
FIXME_textArea.onfocus = function() {
_existingSelectStart = document.body.onselectstart;
document.body.onselectstart = function() { };
};
FIXME_textArea.onblur = function() {
if(_existingSelectStart)
document.body.onselectstart = _existingSelectStart;
};
}
_DOMElement.appendChild(FIXME_textArea);
return self;
}
- (void)setDelegate:(id)delegate
{
_delegate = delegate;
}
- (id)delegate
{
return _delegate;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)mouseDown:(CPEvent)anEvent
{
// Don't track! (ever?)
return [[self window] makeFirstResponder:self];
}
- (void)textDidChange:(id)sender
{
if ([_delegate respondsToSelector:@selector(textViewDidChange:)])
[_delegate textViewDidChange: self];
if (!_contentView)
return;
var bounds = [_contentView bounds];
FIXME_textArea.style.height = CGRectGetHeight(bounds) + "px";
FIXME_textArea.style.height = MAX(CGRectGetHeight(bounds), FIXME_textArea.scrollHeight) + "px";
[self setFrameSize:CGSizeMake(CGRectGetWidth(bounds), parseInt(FIXME_textArea.style.height, 10))];
[self scrollToCaret];
[[CPRunLoop currentRunLoop] performSelectors];
}
- (void)scrollToCaret
{
if(![_scrollView verticalScroller] || [[_scrollView verticalScroller] isHidden])
return;
if(FIXME_textArea.selectionStart)
{
var start = FIXME_textArea.selectionStart,
end = FIXME_textArea.selectionEnd;
var imposter = document.createElement('div'),
referenceSpan = document.createElement('span'),
stringValue = FIXME_textArea.value;
imposter.style.overflow = "hidden";
imposter.style.fontSize = "14px";
imposter.style.padding = "0";
imposter.style.margin = "0";
imposter.style.height = FIXME_textArea.style.height;
imposter.style.width = getComputedStyle(FIXME_textArea, "").getPropertyValue('width');
imposter.style.fontFamily = getComputedStyle(FIXME_textArea, "").getPropertyValue('font-family');
for(var i=0; i<start; i++)
{
referenceSpan.innerHTML = stringValue.charAt(i).replace("\n", "<br />");
imposter.appendChild(referenceSpan.cloneNode(true));
}
while (imposter.childNodes[start - 1] && imposter.childNodes[start - 1].innerHTML == " ")
start--;
document.body.appendChild(imposter);
var caretOffsetTop = imposter.childNodes[start - 1].offsetTop - imposter.offsetTop,
caretHeight = imposter.childNodes[start-1].offsetHeight;
document.body.removeChild(imposter);
}
else if(document.selection)
{
FIXME_textArea.focus();
var range = document.selection.createRange();
window.range = range;
if(range.parentElement() != FIXME_textArea)
return;
var caretOffsetTop = range.offsetTop + _DOMElement.offsetTop - 18,
caretHeight = 18;
}
else
return;
[self scrollRectToVisible:CGRectMake(1, caretOffsetTop, 1, caretHeight)];
}
- (CPString)stringValue
{
return FIXME_textArea.value;
}
- (void)setStringValue:(CPString)aString
{
if(aString)
FIXME_textArea.value = aString;
else
FIXME_textArea.value = "";
[self textDidChange: self];
}
- (void)focus
{
window.setTimeout(function(){
FIXME_textArea.focus();
}, 0);
}
- (void)viewDidMoveToSuperview
{
_scrollView = [self enclosingScrollView];
_contentView = [_scrollView contentView];
}
- (void)setFrameSize:(CGSize)aSize
{
[super setFrameSize:aSize];
[self scrollToCaret];
}
- (void)setBezeled:(BOOL)shouldBeBezeled
{
if (shouldBeBezeled)
[self setThemeState:CPThemeStateBezeled];
else
[self unsetThemeState:CPThemeStateBezeled];
}
- (BOOL)isBezeled
{
return [self hasThemeState:CPThemeStateBezeled];
}
- (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle
{
var shouldBeRounded = aBezelStyle === CPTextFieldRoundedBezel;
if (shouldBeRounded)
[self setThemeState:CPTextFieldStateRounded];
else
[self unsetThemeState:CPTextFieldStateRounded];
}
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
if (!contentInset)
return bounds;
bounds.origin.x += contentInset.left;
bounds.origin.y += contentInset.top;
bounds.size.width -= contentInset.left + contentInset.right;
bounds.size.height -= contentInset.top + contentInset.bottom;
return bounds;
}
- (CGRect)bezelRectForBounds:(CFRect)bounds
{
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
if (CGInsetIsEmpty(bezelInset))
return bounds;
bounds.origin.x += bezelInset.left;
bounds.origin.y += bezelInset.top;
bounds.size.width -= bezelInset.left + bezelInset.right;
bounds.size.height -= bezelInset.top + bezelInset.bottom;
return bounds;
}
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
{
if (aName === "bezel-view")
return [self bezelRectForBounds:[self bounds]];
else if (aName === "content-view")
return [self contentRectForBounds:[self bounds]];
return [super rectForEphemeralSubviewNamed:aName];
}
- (CPView)createEphemeralSubviewNamed:(CPString)aName
{
if (aName === "bezel-view") {
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[view setHitTests:NO];
return view;
}
else {
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
//[view setImagePosition:CPNoImage];
return view;
}
return [super createEphemeralSubviewNamed:aName];
}
- (void)layoutSubviews
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
if (bezelView)
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
if (contentView) {
[contentView setHidden:[self hasThemeState:CPThemeStateEditing]];
var string = "";
if ([self hasThemeState:CPTextFieldStatePlaceholder])
string = [self placeholderString];
else {
string = [self stringValue];
}
[contentView setText:string];
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
[contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
[contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
[contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
[contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
[contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
}
}
- (CPTextFieldBezelStyle)bezelStyle
{
if ([self hasThemeState:CPTextFieldStateRounded])
return CPTextFieldRoundedBezel;
return CPTextFieldSquareBezel;
}
- (void)selectText:(id)sender
{
var element = FIXME_textArea;
[self focus];
window.setTimeout(function() { element.select(); }, 0);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment