diff --git a/AppKit/AppKit.j b/AppKit/AppKit.j index 2a34e76f4..c48b219d3 100644 --- a/AppKit/AppKit.j +++ b/AppKit/AppKit.j @@ -47,6 +47,7 @@ @import "CPColorPanel.j" @import "CPColorSpace.j" @import "CPColorWell.j" +@import "CPComboBox.j" @import "CPCompatibility.j" @import "CPControl.j" @import "CPController.j" diff --git a/AppKit/CPComboBox.j b/AppKit/CPComboBox.j new file mode 100644 index 000000000..1d2af4928 --- /dev/null +++ b/AppKit/CPComboBox.j @@ -0,0 +1,1193 @@ +/* + * CPComboBox.j + * AppKit + * + * Created by Aparajita Fishman. + * Copyright (c) 2012, The Cappuccino Foundation + * + * 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 "CPTextField.j" +@import "_CPPopUpList.j" + + +CPComboBoxSelectionDidChangeNotification = @"CPComboBoxSelectionDidChangeNotification"; +CPComboBoxSelectionIsChangingNotification = @"CPComboBoxSelectionIsChangingNotification"; +CPComboBoxWillDismissNotification = @"CPComboBoxWillDismissNotification"; +CPComboBoxWillPopUpNotification = @"CPComboBoxWillPopUpNotification"; + +CPComboBoxStateButtonBordered = CPThemeState("button-bordered"); + +var CPComboBoxTextSubview = @"text", + CPComboBoxButtonSubview = @"button", + CPComboBoxDefaultNumberOfVisibleItems = 5, + CPComboBoxFocusRingWidth = -1; + + +@implementation CPComboBox : CPTextField +{ + CPArray _items; + _CPPopUpList _listDelegate; + CPComboBoxDataSource _dataSource; + BOOL _usesDataSource; + BOOL _completes; + BOOL _canComplete; + int _numberOfVisibleItems; + BOOL _forceSelection; + BOOL _hasVerticalScroller; + CPString _selectedStringValue; + BOOL _popUpButtonCausedResign; +} + ++ (CPString)defaultThemeClass +{ + return "combobox"; +} + ++ (id)themeAttributes +{ + return [CPDictionary dictionaryWithObjectsAndKeys:_CGSizeMake(21.0, 29.0), @"popup-button-size", _CGInsetMake(3.0, 3.0, 3.0, 3.0), @"border-inset"]; +} + ++ (Class)_binderClassForBinding:(CPString)theBinding +{ + if (theBinding === CPContentBinding || theBinding === CPContentValuesBinding) + return [_CPComboBoxContentBinder class]; + + return [super _binderClassForBinding:theBinding]; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + self = [super initWithFrame:aFrame]; + + if (self) + [self _initComboBox]; + + return self; +} + +- (void)_initComboBox +{ + _items = [CPArray array]; + _listClass = [_CPPopUpList class]; + _usesDataSource = NO; + _completes = NO; + _canComplete = NO; + _numberOfVisibleItems = CPComboBoxDefaultNumberOfVisibleItems; + _forceSelection = NO; + _hasVerticalScroller = YES; + _selectedStringValue = @""; + _popUpButtonCausedResign = NO; + + [self setTheme:[CPTheme defaultTheme]]; + [self setBordered:YES]; + [self setBezeled:YES]; + [self setEditable:YES]; + [self setThemeState:CPComboBoxStateButtonBordered]; +} + +#pragma mark Setting Display Attributes + +- (BOOL)hasVerticalScroller +{ + return _hasVerticalScroller; +} + +- (void)setHasVerticalScroller:(BOOL)flag +{ + flag = !!flag; + + if (_hasVerticalScroller === flag) + return; + + _hasVerticalScroller = flag; + [[_listDelegate scrollView] setHasVerticalScroller:flag]; +} + +- (CGSize)intercellSpacing +{ + return [[_listDelegate tableView] intercellSpacing]; +} + +- (void)setIntercellSpacing:(CGSize)aSize +{ + [[_listDelegate tableView] setIntercellSpacing:aSize]; +} + +- (BOOL)isButtonBordered +{ + return [self hasThemeState:CPComboBoxStateButtonBordered]; +} + +- (void)setButtonBordered:(BOOL)flag +{ + if (!!flag) + [self setThemeState:CPComboBoxStateButtonBordered]; + else + [self unsetThemeState:CPComboBoxStateButtonBordered]; +} + +- (float)itemHeight +{ + return [[_listDelegate tableView] rowHeight]; +} + +- (void)setItemHeight:(float)itemHeight +{ + [[_listDelegate tableView] setRowHeight:itemHeight]; + + // FIXME: This shouldn't be necessary, but CPTableView does not tile after setRowHeight + [[_listDelegate tableView] reloadData]; +} + +- (int)numberOfVisibleItems +{ + return _numberOfVisibleItems; +} + +- (void)setNumberOfVisibleItems:(int)visibleItems +{ + // There should always be at least 1 visible item! + _numberOfVisibleItems = MAX(visibleItems, 1); +} + +#pragma mark Setting a Delegate + +- (id < CPComboBoxDelegate >)delegate +{ + return [super delegate]; +} + +/*! + Sets the CPComboBox delegate. Note that although the Cocoa + docs say that the delegate must conform to the NSComboBoxDelegate + protocol, in actual fact it doesn't. Also note that the same + delegate may conform to the NSTextFieldDelegate protocol. +*/ +- (void)setDelegate:(id < CPComboBoxDelegate >)aDelegate +{ + var delegate = [self delegate]; + + if (aDelegate === delegate) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (delegate) + { + [defaultCenter removeObserver:delegate name:CPComboBoxSelectionIsChangingNotification object:self]; + [defaultCenter removeObserver:delegate name:CPComboBoxSelectionDidChangeNotification object:self]; + [defaultCenter removeObserver:delegate name:CPComboBoxWillDismissNotification object:self]; + [defaultCenter removeObserver:delegate name:CPComboBoxWillPopUpNotification object:self]; + } + + if (aDelegate) + { + if ([aDelegate respondsToSelector:@selector(comboBoxSelectionIsChanging:)]) + [defaultCenter addObserver:delegate + selector:@selector(comboBoxSelectionIsChanging:) + name:CPComboBoxSelectionIsChangingNotification + object:self]; + + if ([aDelegate respondsToSelector:@selector(comboBoxSelectionDidChange:)]) + [defaultCenter addObserver:delegate + selector:@selector(comboBoxSelectionDidChange:) + name:CPComboBoxSelectionDidChangeNotification + object:self]; + + if ([aDelegate respondsToSelector:@selector(comboBoxWillPopUp:)]) + [defaultCenter addObserver:delegate + selector:@selector(comboBoxWillPopUp:) + name:CPComboBoxWillPopUpNotification + object:self]; + + if ([aDelegate respondsToSelector:@selector(comboBoxWillDismiss:)]) + [defaultCenter addObserver:delegate + selector:@selector(comboBoxWillDissmis:) + name:CPComboBoxWillDismissNotification + object:self]; + } + + [super setDelegate:aDelegate]; +} + +#pragma mark Setting a Data Source + +- (id < CPComboBoxDataSource >)dataSource +{ + if (!_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:NO]; + + return _dataSource; +} + +- (void)setDataSource:(id < CPComboBoxDataSource >)aSource +{ + if (!_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:NO]; + else if (_dataSource !== aSource) + { + if (![aSource respondsToSelector:@selector(numberOfItemsInComboBox:)] || + ![aSource respondsToSelector:@selector(comboBox:objectValueForItemAtIndex:)]) + { + CPLog.warn("Illegal %s data source (%s). Must implement numberOfItemsInComboBox: and comboBox:objectValueForItemAtIndex:", [self className], [aSource description]); + } + else + _dataSource = aSource; + } +} + +- (BOOL)usesDataSource +{ + return _usesDataSource; +} + +- (void)setUsesDataSource:(BOOL)flag +{ + flag = !!flag; + + if (_usesDataSource === flag) + return; + + _usesDataSource = flag; + + // Cocoa empties the internal item list if usesDataSource is YES + if (_usesDataSource) + [_items removeAllObjects]; + + [self reloadData]; +} + +#pragma mark Working with an Internal List + +- (void)addItemsWithObjectValues:(CPArray)objects +{ + [_items addObjectsFromArray:objects]; + + [self reloadDataSourceForSelector:_cmd]; +} + +- (void)addItemWithObjectValue:(id)anObject +{ + [_items addObject:anObject]; + + [self reloadDataSourceForSelector:_cmd]; +} + +- (void)insertItemWithObjectValue:(id)anObject atIndex:(int)anIndex +{ + // Issue the warning first, because removeObjectAtIndex may raise + if (_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:YES]; + + [_items insertObject:anObject atIndex:anIndex]; + [self reloadData]; +} + +/*! + Returns the internal array of items. NOTE: Unlike Cocoa the array is mutable, + since all arrays in Objective-J are mutable. But you should treat it as + an immutable array. Do NOT attempt to change the returned array in any way. + + If usesDataSource is YES, a warning is logged and an empty array is returned. +*/ +- (CPArray)objectValues +{ + if (_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:YES]; + + return _items; +} + +- (void)removeAllItems +{ + [_items removeAllObjects]; + + [self reloadDataSourceForSelector:_cmd]; +} + +- (void)removeItemAtIndex:(int)index +{ + // Issue the warning first, because removeObjectAtIndex may raise + if (_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:YES]; + + [_items removeObjectAtIndex:index]; + [self reloadData]; +} + +- (void)removeItemWithObjectValue:(id)anObject +{ + [_items removeObject:anObject]; + + [self reloadDataSourceForSelector:_cmd]; +} + +- (int)numberOfItems +{ + if (_usesDataSource) + return [_dataSource numberOfItemsInComboBox:self]; + else + return _items.length; +} + +#pragma mark Manipulating the Displayed List + +/*! + Returns the delegate to be used when creating the pop up list. +*/ +- (_CPPopUpList)listDelegate +{ + return _listDelegate; +} + +/*! + Sets the delegate to be used when creating the pop up list. + By default this is _CPPopUpList. If you are using a subclass + of _CPPopUpList, call this method with your subclass. +*/ +- (void)setListDelegate:(_CPPopUpList)aDelegate +{ + if (_listDelegate === aDelegate) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + if (_listDelegate) + { + [defaultCenter removeObserver:self name:_CPPopUpListWillPopUpNotification object:_listDelegate]; + [defaultCenter removeObserver:self name:_CPPopUpListWillDismissNotification object:_listDelegate]; + [defaultCenter removeObserver:self name:_CPPopUpListDidDismissNotification object:_listDelegate]; + [defaultCenter removeObserver:self name:_CPPopUpListItemWasClickedNotification object:_listDelegate]; + + var oldTableView = [_listDelegate tableView]; + + if (oldTableView) + { + [defaultCenter removeObserver:self name:CPTableViewSelectionIsChangingNotification object:oldTableView]; + [defaultCenter removeObserver:self name:CPTableViewSelectionDidChangeNotification object:oldTableView]; + } + } + + _listDelegate = aDelegate; + + [defaultCenter addObserver:self + selector:@selector(comboBoxWillPopUp:) + name:_CPPopUpListWillPopUpNotification + object:_listDelegate]; + + [defaultCenter addObserver:self + selector:@selector(comboBoxWillDismiss:) + name:_CPPopUpListWillDismissNotification + object:_listDelegate]; + + [defaultCenter addObserver:self + selector:@selector(listDidDismiss:) + name:_CPPopUpListDidDismissNotification + object:_listDelegate]; + + [defaultCenter addObserver:self + selector:@selector(itemWasClicked:) + name:_CPPopUpListItemWasClickedNotification + object:_listDelegate]; + + [[_listDelegate scrollView] setHasVerticalScroller:_hasVerticalScroller]; + + var tableView = [_listDelegate tableView]; + + [defaultCenter addObserver:self + selector:@selector(comboBoxSelectionIsChanging:) + name:CPTableViewSelectionIsChangingNotification + object:tableView]; + + [defaultCenter addObserver:self + selector:@selector(comboBoxSelectionDidChange:) + name:CPTableViewSelectionDidChangeNotification + object:tableView]; + + // Apply our text style to the list + [_listDelegate setFont:[self font]]; + [_listDelegate setAlignment:[self alignment]]; +} + +- (int)indexOfItemWithObjectValue:(id)anObject +{ + if (_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:YES]; + + return [_items indexOfObject:anObject]; +} + +- (id)itemObjectValueAtIndex:(int)index +{ + if (_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:YES]; + + return [_items objectAtIndex:index]; +} + +- (void)noteNumberOfItemsChanged +{ + [[_listDelegate tableView] noteNumberOfRowsChanged]; +} + +- (void)scrollItemAtIndexToTop:(int)index +{ + [_listDelegate scrollItemAtIndexToTop:index]; +} + +- (void)scrollItemAtIndexToVisible:(int)index +{ + [[_listDelegate tableView] scrollRowToVisible:index]; +} + +- (void)reloadData +{ + [[_listDelegate tableView] reloadData]; +} + +/*! @ignore */ +- (void)popUpList +{ + if (!_listDelegate) + [self setListDelegate:[[_CPPopUpList alloc] initWithDataSource:self]]; + + [self _selectMatchingItem]; + + // Note the offset here is 1 less than the focus ring width because the outer edge + // of the focus ring is very transparent and it looks better if the list is closer. + if (CPComboBoxFocusRingWidth < 0) + { + var inset = [self currentValueForThemeAttribute:@"border-inset"]; + + CPComboBoxFocusRingWidth = inset.bottom; + } + + [_listDelegate popUpRelativeToRect:[self _borderFrame] view:self offset:CPComboBoxFocusRingWidth - 1]; +} + +/*! @ignore */ +- (BOOL)listIsVisible +{ + return _listDelegate ? [_listDelegate isVisible] : NO; +} + +/*! @ignore */ +- (void)reloadDataSourceForSelector:(SEL)cmd +{ + if (_usesDataSource) + [self _dataSourceWarningForMethod:cmd condition:YES] + else + [self reloadData]; +} + +/*! + If the list is non-empty, sets the value of the field from the currently selected value of the list + and returns YES. If the list is empty or the list has no selected item, returns NO. + @ignore +*/ +- (BOOL)takeStringValueFromList +{ + if (_usesDataSource && _dataSource && [_dataSource numberOfItemsInComboBox:self] === 0) + return NO; + + var selectedStringValue = [_listDelegate selectedStringValue]; + + if (selectedStringValue === nil) + return NO; + else + _selectedStringValue = selectedStringValue; + + [self setStringValue:_selectedStringValue]; + [self _reverseSetBinding]; + + return YES; +} + +/*! + The receiver receives this notification when the list is closed. + @ignore +*/ +- (void)listDidDismiss:(CPNotification)aNotification +{ + [[self window] makeFirstResponder:self]; +} + +/*! + The receiver receives this notification when an item in the list is clicked. + @ignore +*/ +- (void)itemWasClicked:(CPNotification)aNotification +{ + [self takeStringValueFromList]; + [self sendAction:[self action] to:[self target]]; +} + +#pragma mark Manipulating the Selection + +- (void)deselectItemAtIndex:(int)index +{ + var table = [_listDelegate tableView], + row = [table selectedRow]; + + if (row !== index) + return; + + [table deselectRow:index]; +} + +- (int)indexOfSelectedItem +{ + return [[_listDelegate tableView] selectedRow]; +} + +- (id)objectValueOfSelectedItem +{ + var row = [[_listDelegate tableView] selectedRow]; + + if (row >= 0) + { + if (_usesDataSource) + [self _dataSourceWarningForMethod:_cmd condition:YES]; + + return _items[row]; + } + + return nil; +} + +- (void)selectItemAtIndex:(int)index +{ + var table = [_listDelegate tableView], + row = [table selectedRow]; + + if (row === index) + return; + + [table selectRowIndexes:[CPIndexSet indexSetWithIndex:index] byExtendingSelection:NO]; +} + +- (void)selectItemWithObjectValue:(id)anObject +{ + var index = [self indexOfItemWithObjectValue:anObject]; + + if (index !== CPNotFound) + [self selectItemAtIndex:index]; +} + +#pragma mark Completing the Text Field + +- (BOOL)completes +{ + return _completes; +} + +- (void)setCompletes:(BOOL)flag +{ + _completes = !!flag; +} + +- (CPString)completedString:(CPString)substring +{ + if (_usesDataSource) + return [self comboBoxCompletedString:substring]; + else + { + var index = [_items indexOfObjectPassingTest:CPComboBoxCompletionTest context:substring]; + + return index !== CPNotFound ? _items[index] : nil; + } +} + +/*! + Returns whether the combo box forces the user to enter or select + an item that is in the item list. +*/ +- (BOOL)forceSelection +{ + return _forceSelection; +} + +/*! + Sets whether the combo box forces the user to enter or select + an item that is in the item list. If \c flag is \c YES and the user enters a value + that is not in the list, when the field loses focus it will revert + to the previous value. If \c flag is \c NO, the user can enter any value they wish. + + Note that this flag is ignored if \ref setStringValue or \ref setObjectValue are + called directly. +*/ +- (void)setForceSelection:(BOOL)flag +{ + _forceSelection = !!flag; +} + +#pragma mark CPTextField Delegate Methods and Overrides + +/*! @ignore */ +- (BOOL)sendAction:(SEL)anAction to:(id)anObject +{ + // When the action is sent, be sure to get the value and close the list. + // This covers the case where the action is triggered by pressing a key + // that triggers the text field action. + + if ([self listIsVisible]) + { + [self takeStringValueFromList]; + [_listDelegate close]; + } + + return [super sendAction:anAction to:anObject]; +} + +/*! @ignore */ +- (void)setObjectValue:(id)object +{ + [super setObjectValue:object]; + + _selectedStringValue = [self stringValue]; +} + +/*! @ignore */ +- (void)interpretKeyEvents:(CPArray)events +{ + var theEvent = events[0]; + + // Only if characters are added at the end of the value can completion occur + _canComplete = NO; + + if (_completes) + { + console.log("%d: %s", [theEvent keyCode], [theEvent _couldBeKeyEquivalent]); + if (![theEvent _couldBeKeyEquivalent] && [theEvent characters].charAt(0) !== CPDeleteCharacter) + { + var value = [self _inputElement].value, + selectedRange = [self selectedRange]; + + _canComplete = CPMaxRange(selectedRange) === value.length; + } + } + + [super interpretKeyEvents:events]; +} + +/*! @ignore */ +- (void)paste:(id)sender +{ + if (_completes) + { + // Completion can occur only if pasting at the end of the value + var value = [self _inputElement].value, + selectedRange = [self selectedRange]; + + _canComplete = CPMaxRange(selectedRange) === value.length; + } + else + _canComplete = NO; + + [super paste:sender]; +} + +/*! @ignore */ +- (void)textDidChange:(CPNotification)aNotification +{ + /* + Completion is attempted iff: + - _completes is YES + - Characters were added at the end of the value + */ + var uncompletedString = [self stringValue], + newString = uncompletedString; + + if (_completes && _canComplete) + { + newString = [self completedString:uncompletedString]; + + if (newString && newString.length > uncompletedString.length) + { + [self setStringValue:newString]; + [self setSelectedRange:CPMakeRange(uncompletedString.length, newString.length - uncompletedString.length)]; + } + } + + [self _selectMatchingItem]; + _canComplete = NO; + + [super textDidChange:aNotification]; +} + +/*! + Override of CPView -performKeyEquivalent + @ignore +*/ +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + if ([[self window] firstResponder] === self) + { + var key = [anEvent charactersIgnoringModifiers]; + + switch (key) + { + case CPDownArrowFunctionKey: + if (![self listIsVisible]) + { + [self popUpList]; + return YES; + } + break; + + case CPEscapeFunctionKey: + if ([self listIsVisible]) + { + // If we are forcing a selection and the user has entered a value which is not + // in the list, revert to the most recent valid value. + if (_forceSelection && ([self _inputElement].value !== _selectedStringValue)) + [self setStringValue:_selectedStringValue]; + } + break; + } + + if ([_listDelegate performKeyEquivalent:anEvent]) + return YES; + } + + return [super performKeyEquivalent:anEvent]; +} + +/*! @ignore */ +- (BOOL)resignFirstResponder +{ + var buttonCausedResign = _popUpButtonCausedResign; + + _popUpButtonCausedResign = NO; + + /* + If the list or popup button is clicked, we lose focus. The list will refuse first responder, + and we refuse to resign. But we still have to manually restore the focus to the input element. + */ + var shouldResign = !buttonCausedResign && (!_listDelegate || [_listDelegate controllingViewShouldResign]); + + if (!shouldResign) + { +#if PLATFORM(DOM) + // In FireFox this needs to be done in setTimeout, otherwise there is no caret + // We have to save the input element now, when we lose focus it will change. + var element = [self _inputElement]; + window.setTimeout(function() { element.focus(); }, 0); +#endif + + return NO; + } + + // The list was not clicked, we need to close it now + [_listDelegate close]; + + // If the field is empty, allow it to remain empty. + // Otherwise restore the most recently selected value if forcing selection. + var value = [self stringValue]; + + if (value) + { + if (_forceSelection && ![value isEqual:_selectedStringValue]) + [self setStringValue:_selectedStringValue]; + } + else + _selectedStringValue = @""; + + return [super resignFirstResponder]; +} + +- (void)setFont:(CPFont)aFont +{ + [super setFont:aFont]; + [_listDelegate setFont:aFont]; +} + +- (void)setAlignment:(CPTextAlignment)alignment +{ + [super setAlignment:alignment]; + [_listDelegate setAlignment:alignment]; +} + +#pragma mark Pop Up Button Layout + +- (CGRect)popupButtonRectForBounds:(CGRect)bounds +{ + var borderInset = [self currentValueForThemeAttribute:@"border-inset"], + buttonSize = [self currentValueForThemeAttribute:@"popup-button-size"]; + + bounds.origin.x = CGRectGetMaxX(bounds) - borderInset.right - buttonSize.width; + bounds.origin.y += borderInset.top; + + bounds.size.width = buttonSize.width; + bounds.size.height = buttonSize.height; + + return bounds; +} + +- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName +{ + if (aName === "popup-button-view") + return [self popupButtonRectForBounds:[self bounds]]; + + return [super rectForEphemeralSubviewNamed:aName]; +} + +- (CPView)createEphemeralSubviewNamed:(CPString)aName +{ + if (aName === "popup-button-view") + { + var view = [[_CPComboBoxPopUpButton alloc] initWithFrame:_CGRectMakeZero() comboBox:self]; + + return view; + } + + return [super createEphemeralSubviewNamed:aName]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + var popupButtonView = [self layoutEphemeralSubviewNamed:@"popup-button-view" + positioned:CPWindowAbove + relativeToEphemeralSubviewNamed:@"content-view"]; +} + +#pragma mark Internal Helpers + +/*! @ignore */ +- (void)_dataSourceWarningForMethod:(SEL)cmd condition:(CPString)flag +{ + CPLog.warn("-[%s %s] should not be called when usesDataSource is set to %s", [self className], cmd, flag ? "YES" : "NO"); +} + +/*! + Select the item that matches the current value of the combobox. + @ignore +*/ +- (void)_selectMatchingItem +{ + var index = CPNotFound, + stringValue = [self stringValue]; + + if (_usesDataSource) + { + if (_dataSource && [_dataSource respondsToSelector:@selector(comboBox:indexOfItemWithStringValue:)]) + index = [_dataSource comboBox:self indexOfItemWithStringValue:stringValue] + } + else + index = [self indexOfItemWithObjectValue:stringValue]; + + [_listDelegate selectRow:index]; + + // selectRow scrolls the row to visible, if a row is selected scroll it to the top + if (index !== CPNotFound) + { + [_listDelegate scrollItemAtIndexToTop:index]; + _selectedStringValue = stringValue; + } +} + +/*! + Calculate the frame in base coordinates that will nestle just below the visible border of the text field. + @ignore +*/ +- (CGRect)_borderFrame +{ + var inset = [self currentValueForThemeAttribute:@"border-inset"], + frame = [self bounds]; + + frame.origin.x += inset.left; + frame.origin.y += inset.top; + frame.size.width -= inset.left + inset.right; + frame.size.height -= inset.top + inset.bottom; + + return frame; +} + +/* @ignore */ +- (void)_popUpButtonWasClicked +{ + if (![self isEnabled]) + return; + + // If we are currently the first responder, we will be asked to resign when the list pops up. + // Set a flag to let resignResponder know that the button was clicked and we should not resign. + var firstResponder = [[self window] firstResponder]; + + _popUpButtonCausedResign = firstResponder === self; + + if ([self listIsVisible]) + [_listDelegate close]; + else + { + if (firstResponder !== self) + [[self window] makeFirstResponder:self]; + + [self popUpList]; + } +} + +@end + +@implementation CPComboBox (CPComboBoxDelegate) + +/*! @ignore */ +- (void)comboBoxSelectionIsChanging:(CPNotification)aNotification +{ + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxSelectionIsChangingNotification object:self]; +} + +/*! @ignore */ +- (void)comboBoxSelectionDidChange:(CPNotification)aNotification +{ + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxSelectionDidChangeNotification object:self]; +} + +/*! @ignore */ +- (void)comboBoxWillPopUp:(CPNotification)aNotification +{ + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxWillPopUpNotification object:self]; +} + +/*! @ignore */ +- (void)comboBoxWillDismiss:(CPNotification)aNotification +{ + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxWillDismissNotification object:self]; +} + +@end + +@implementation CPComboBox (CPComboBoxDataSource) + +/*! @ignore */ +- (CPString)comboBoxCompletedString:(CPString)uncompletedString +{ + if ([_dataSource respondsToSelector:@selector(comboBox:completedString:)]) + return [_dataSource comboBox:self completedString:uncompletedString]; + else + return nil; +} + +@end + +@implementation CPComboBox (_CPPopUpListDataSource) + +- (int)numberOfItemsInList:(_CPPopUpList)aList +{ + return [self numberOfItems]; +} + +- (int)numberOfVisibleItemsInList:(_CPPopUpList)aList +{ + return [self numberOfVisibleItems]; +} + +- (id)list:(_CPPopUpList)aList objectValueForItemAtIndex:(int)index +{ + if (_usesDataSource) + return [_dataSource comboBox:self objectValueForItemAtIndex:index]; + else + return _items[index]; +} + +- (id)list:(_CPPopUpList)aList displayValueForObjectValue:(id)aValue +{ + return aValue || @""; +} + +- (CPString)list:(_CPPopUpList)aList stringValueForObjectValue:(id)aValue +{ + return String(aValue); +} + +@end + +@implementation CPComboBox (Bindings) + +/*! @ignore */ +- (void)setContentValues:(CPArray)anArray +{ + [self setUsesDataSource:NO]; + [self removeAllItems]; + [self addItemsWithObjectValues:anArray]; +} + +/*! @ignore */ +- (void)setContent:(CPArray)anArray +{ + [self setUsesDataSource:NO]; + + // Directly nuke _items, [_items removeAll] will trigger an extra call to setContent + _items = []; + + var values = []; + + [anArray enumerateObjectsUsingBlock:function(object) + { + values.push([object description]); + }]; + + [self addItemsWithObjectValues:values]; +} + +@end + +var CPComboBoxItemsKey = @"CPComboBoxItemsKey", + CPComboBoxListKey = @"CPComboBoxListKey", + CPComboBoxDelegateKey = @"CPComboBoxDelegateKey", + CPComboBoxDataSourceKey = @"CPComboBoxDataSourceKey", + CPComboBoxUsesDataSourceKey = @"CPComboBoxUsesDataSourceKey", + CPComboBoxCompletesKey = @"CPComboBoxCompletesKey", + CPComboBoxNumberOfVisibleItemsKey = @"CPComboBoxNumberOfVisibleItemsKey", + CPComboBoxHasVerticalScrollerKey = @"CPComboBoxHasVerticalScrollerKey", + CPComboBoxButtonBorderedKey = @"CPComboBoxButtonBorderedKey"; + +@implementation CPComboBox (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + [self _initComboBox]; + + _items = [aCoder decodeObjectForKey:CPComboBoxItemsKey]; + _listDelegate = [aCoder decodeObjectForKey:CPComboBoxListKey]; + _delegate = [aCoder decodeObjectForKey:CPComboBoxDelegateKey]; + _dataSource = [aCoder decodeObjectForKey:CPComboBoxDataSourceKey]; + _usesDataSource = [aCoder decodeBoolForKey:CPComboBoxUsesDataSourceKey]; + _completes = [aCoder decodeBoolForKey:CPComboBoxCompletesKey]; + _numberOfVisibleItems = [aCoder decodeIntForKey:CPComboBoxNumberOfVisibleItemsKey]; + _hasVerticalScroller = [aCoder decodeBoolForKey:CPComboBoxHasVerticalScrollerKey]; + [self setButtonBordered:[aCoder decodeBoolForKey:CPComboBoxButtonBorderedKey]]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeObject:_items forKey:CPComboBoxItemsKey]; + [aCoder encodeObject:_listDelegate forKey:CPComboBoxListKey]; + [aCoder encodeObject:_delegate forKey:CPComboBoxDelegateKey]; + [aCoder encodeObject:_dataSource forKey:CPComboBoxDataSourceKey]; + [aCoder encodeBool:_usesDataSource forKey:CPComboBoxUsesDataSourceKey]; + [aCoder encodeBool:_completes forKey:CPComboBoxCompletesKey]; + [aCoder encodeInt:_numberOfVisibleItems forKey:CPComboBoxNumberOfVisibleItemsKey]; + [aCoder encodeBool:_hasVerticalScroller forKey:CPComboBoxHasVerticalScrollerKey]; + [aCoder encodeBool:[self isButtonBordered] forKey:CPComboBoxButtonBorderedKey]; +} + +@end + + +var CPComboBoxCompletionTest = function(object, index, context) +{ + return object.toString().indexOf(context) === 0; +}; + + +/* + This class is only used for CPContentBinding and CPContentValuesBinding. +*/ +@implementation _CPComboBoxContentBinder : CPBinder + +- (void)setValueFor:(CPString)theBinding +{ + var destination = [_info objectForKey:CPObservedObjectKey], + keyPath = [_info objectForKey:CPObservedKeyPathKey], + options = [_info objectForKey:CPOptionsKey], + newValue = [destination valueForKeyPath:keyPath], + isPlaceholder = CPIsControllerMarker(newValue); + + [_source removeAllItems]; + + if (isPlaceholder) + { + // By default the placeholders will all result in an empty list + switch (newValue) + { + case CPMultipleValuesMarker: + newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || []; + break; + + case CPNoSelectionMarker: + newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || []; + break; + + case CPNotApplicableMarker: + if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption]) + [CPException raise:CPGenericException + reason:@"can't transform non applicable key on: " + _source + " value: " + newValue]; + + newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || []; + break; + + case CPNullMarker: + newValue = [options objectForKey:CPNullPlaceholderBindingOption] || []; + break; + } + + if (![newValue isKindOfClass:[CPArray class]]) + newValue = []; + } + else + newValue = [self transformValue:newValue withOptions:options]; + + switch (theBinding) + { + case CPContentBinding: [_source setContent:newValue]; + break; + + case CPContentValuesBinding: [_source setContentValues:newValue]; + break; + } +} + +@end + +@implementation _CPComboBoxPopUpButton : CPView +{ + CPComboBox _comboBox; +} + +- (id)initWithFrame:(CGRect)aFrame comboBox:(CPComboBox)aComboBox +{ + self = [super initWithFrame:aFrame]; + + if (self) + _comboBox = aComboBox; + + return self; +} + +- (void)mouseDown:(CPEvent)theEvent +{ + [_comboBox _popUpButtonWasClicked]; +} + +- (BOOL)acceptsFirstResponder +{ + return NO; +} + +@end diff --git a/AppKit/CPKeyValueBinding.j b/AppKit/CPKeyValueBinding.j index 608f7ae4a..1794085a3 100644 --- a/AppKit/CPKeyValueBinding.j +++ b/AppKit/CPKeyValueBinding.j @@ -558,6 +558,8 @@ CPValueBinding = @"value"; CPValueURLBinding = @"valueURL"; CPValuePathBinding = @"valuePath"; CPDataBinding = @"data"; +CPContentBinding = @"content"; +CPContentValuesBinding = @"contentValues"; //Binding options constants CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection"; diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index bda0c5a1b..60874a91a 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -486,10 +486,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); #if PLATFORM(DOM) var element = [self _inputElement], - font = [self currentValueForThemeAttribute:@"font"]; - - // generate the font metric - [font _getMetrics]; + font = [self currentValueForThemeAttribute:@"font"], + lineHeight = ROUND([font defaultLineHeightForFont]); element.value = _stringValue; element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString]; @@ -511,26 +509,26 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); switch (verticalAlign) { case CPTopVerticalTextAlignment: - var topPoint = (_CGRectGetMinY(contentRect) + 1) + "px"; // for the same reason we have a -1 for the left, we also have a + 1 here + var topPoint = _CGRectGetMinY(contentRect) + "px"; break; case CPCenterVerticalTextAlignment: - var topPoint = (_CGRectGetMidY(contentRect) - (font._lineHeight / 2) + 1) + "px"; + var topPoint = (_CGRectGetMidY(contentRect) - (lineHeight / 2)) + "px"; break; case CPBottomVerticalTextAlignment: - var topPoint = (_CGRectGetMaxY(contentRect) - font._lineHeight) + "px"; + var topPoint = (_CGRectGetMaxY(contentRect) - lineHeight) + "px"; break; default: - var topPoint = (_CGRectGetMinY(contentRect) + 1) + "px"; + var topPoint = _CGRectGetMinY(contentRect) + "px"; break; } element.style.top = topPoint; - element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px"; // why -1? + element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px"; // -1 because input element seems to have 1px left inset element.style.width = _CGRectGetWidth(contentRect) + "px"; - element.style.height = font._lineHeight + "px"; // private ivar for the line height of the DOM text at this particular size + element.style.height = lineHeight + "px"; _DOMElement.appendChild(element); @@ -1315,9 +1313,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); { 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; @@ -1365,7 +1360,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); else { var view = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()]; - //[view setImagePosition:CPNoImage]; [view setHitTests:NO]; diff --git a/AppKit/CPTokenField.j b/AppKit/CPTokenField.j index d306052e9..a0c094816 100755 --- a/AppKit/CPTokenField.j +++ b/AppKit/CPTokenField.j @@ -90,6 +90,11 @@ var CPScrollDestinationNone = 0, return "tokenfield"; } ++ (id)themeAttributes +{ + return [CPDictionary dictionaryWithObject:_CGInsetMakeZero() forKey:@"editor-inset"]; +} + - (id)initWithFrame:(CPRect)frame { if (self = [super initWithFrame:frame]) @@ -179,6 +184,7 @@ var CPScrollDestinationNone = 0, // Give the delegate a chance to confirm, replace or add to the list of tokens being added. var delegateApprovedObjects = [self _shouldAddObjects:[CPArray arrayWithObject:token] atIndex:_selectedRange.location], delegateApprovedObjectsCount = [delegateApprovedObjects count]; + if (delegateApprovedObjects) { for (var i = 0; i < delegateApprovedObjectsCount; i++) @@ -313,11 +319,12 @@ var CPScrollDestinationNone = 0, #if PLATFORM(DOM) var string = [self stringValue], - element = [self _inputElement]; + element = [self _inputElement], + font = [self currentValueForThemeAttribute:@"font"]; element.value = nil; element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString]; - element.style.font = [[self currentValueForThemeAttribute:@"font"] cssString]; + element.style.font = [font cssString]; element.style.zIndex = 1000; switch ([self alignment]) @@ -332,9 +339,9 @@ var CPScrollDestinationNone = 0, var contentRect = [self contentRectForBounds:[self bounds]]; element.style.top = CGRectGetMinY(contentRect) + "px"; - element.style.left = (CGRectGetMinX(contentRect) - 1) + "px"; // why -1? + element.style.left = (CGRectGetMinX(contentRect) - 1) + "px"; // element effectively imposes a 1px left margin element.style.width = CGRectGetWidth(contentRect) + "px"; - element.style.height = CGRectGetHeight(contentRect) + "px"; + element.style.height = ROUND([font defaultLineHeightForFont]) + "px"; [_tokenScrollView documentView]._DOMElement.appendChild(element); @@ -472,6 +479,7 @@ var CPScrollDestinationNone = 0, - (id)objectValue { var objectValue = []; + for (var i = 0, count = [[self _tokens] count]; i < count; i++) { var token = [[self _tokens] objectAtIndex:i]; @@ -570,6 +578,20 @@ var CPScrollDestinationNone = 0, [self setNeedsDisplay:YES]; } +- (void)setEnabled:(BOOL)shouldBeEnabled +{ + [super setEnabled:shouldBeEnabled]; + + // Set the enabled state of the tokens + for (var i = 0, count = [[self _tokens] count]; i < count; i++) + { + var token = [[self _tokens] objectAtIndex:i]; + + if ([token respondsToSelector:@selector(setEnabled:)]) + [token setEnabled:shouldBeEnabled]; + } +} + - (void)sendAction:(SEL)anAction to:(id)anObject { _shouldNotifyTarget = NO; @@ -962,7 +984,10 @@ var CPScrollDestinationNone = 0, offset = CPPointMake(contentOrigin.x, contentOrigin.y), spaceBetweenTokens = CPSizeMake(2.0, 2.0), isEditing = [[self window] firstResponder] == self, - tokenToken = [_CPTokenFieldToken new]; + tokenToken = [_CPTokenFieldToken new], + font = [self currentValueForThemeAttribute:@"font"], + lineHeight = ROUND([font defaultLineHeightForFont]), + editorInset = [self currentValueForThemeAttribute:@"editor-inset"]; // Get the height of a typical token, or a token token if you will. [tokenToken sizeToFit]; @@ -1001,15 +1026,17 @@ var CPScrollDestinationNone = 0, // XXX The "X" here is used to estimate the space needed to fit the next character // without clipping. Since different fonts might have different sizes of "X" this // solution is not ideal, but it works. - textWidth = [(element.value || @"") + "X" sizeWithFont:[self font]].width; + textWidth = [(element.value || @"") + "X" sizeWithFont:font].width; + if (useRemainingWidth) textWidth = MAX(contentSize.width - offset.x - 1, textWidth); } _inputFrame = fitAndFrame(textWidth, tokenHeight); + _inputFrame.size.height = lineHeight; - element.style.left = _inputFrame.origin.x + "px"; - element.style.top = _inputFrame.origin.y + "px"; + element.style.left = (_inputFrame.origin.x + editorInset.left) + "px"; + element.style.top = (_inputFrame.origin.y + editorInset.top) + "px"; element.style.width = _inputFrame.size.width + "px"; element.style.height = _inputFrame.size.height + "px"; diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-center.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-center.png new file mode 100644 index 000000000..8f5171d3c Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-center.png new file mode 100644 index 000000000..584597d70 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-left.png new file mode 100644 index 000000000..5cd37245f Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-right.png new file mode 100644 index 000000000..cad4200ec Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-center.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-center.png new file mode 100644 index 000000000..17a750707 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-left.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-left.png new file mode 100644 index 000000000..cf33abf0d Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-right.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-right.png new file mode 100644 index 000000000..d86d6da36 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-focused-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-left.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-left.png new file mode 100644 index 000000000..7278434b5 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-center.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-center.png new file mode 100644 index 000000000..0d654e92e Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-center.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-center.png new file mode 100644 index 000000000..49cb956b3 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-left.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-left.png new file mode 100644 index 000000000..dc9d6ac33 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-right.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-right.png new file mode 100644 index 000000000..811bbcd91 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-center.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-center.png new file mode 100644 index 000000000..17a750707 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-left.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-left.png new file mode 100644 index 000000000..475f79853 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-right.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-right.png new file mode 100644 index 000000000..6de63cff9 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-focused-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-left.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-left.png new file mode 100644 index 000000000..67d0702e0 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-right.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-right.png new file mode 100644 index 000000000..d033a0e7f Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-no-border-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/combobox-bezel-right.png b/AppKit/Themes/Aristo/Resources/combobox-bezel-right.png new file mode 100644 index 000000000..e796d2f7e Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/combobox-bezel-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-center.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-center.png index 2ffc73c8b..47b21b422 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-center.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-center.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-center.png new file mode 100644 index 000000000..2089c6dc6 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-left.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-left.png new file mode 100644 index 000000000..d4f0f53be Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-right.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-right.png new file mode 100644 index 000000000..e3fa8a303 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-disabled-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-center.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-center.png index b5dad6f9f..e1aad4ee2 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-center.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-center.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-left.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-left.png index b44bf2c1c..66a00a37e 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-left.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-right.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-right.png index aeed86899..53711d5ad 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-right.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-focused-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-left.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-left.png index c4d22bfc2..39a880af9 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-left.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-left.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-right.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-right.png index 8281342b3..8aac94d7b 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-right.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-rounded-right.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-0.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-0.png index 1f0cfabe5..9277864a3 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-0.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-0.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-1.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-1.png index 72c8add06..373b4521a 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-1.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-1.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-2.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-2.png index 3679527d6..8a25d7d56 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-2.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-2.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-3.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-3.png index 1cda75fe5..c64440c21 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-3.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-3.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-4.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-4.png index 6dcbf53ef..b9d310c91 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-4.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-4.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-5.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-5.png index db566f219..fd3d0f353 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-5.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-5.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-6.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-6.png index 8bd048b99..657130254 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-6.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-6.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-7.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-7.png index e96d14560..516547dd3 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-7.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-7.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-8.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-8.png index bf9f2b88d..7e5ee4d11 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-8.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-8.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-0.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-0.png index 86d3a0b3c..9d9c48149 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-0.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-0.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-1.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-1.png index 2dc080f27..69059d7f4 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-1.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-1.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-2.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-2.png index 9a8ffa1bc..c16b43a4c 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-2.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-2.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-3.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-3.png index ee5aff6fa..c66d31b2f 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-3.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-3.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-4.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-4.png index 2e66015ba..b9d310c91 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-4.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-4.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-5.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-5.png index 785335b23..1bbb21e96 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-5.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-5.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-6.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-6.png index cd3848f17..34f8ea207 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-6.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-6.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-7.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-7.png index af73498eb..78e4e0197 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-7.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-7.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-8.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-8.png index 992854957..7cacd441e 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-8.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-8.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-0.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-0.png index de1254128..45338f207 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-0.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-0.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-1.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-1.png index fbd78313e..b5425fd48 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-1.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-1.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-2.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-2.png index bb4be64f4..d0ed18841 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-2.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-2.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-3.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-3.png index 2ad2b9e6e..d91ea7245 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-3.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-3.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-4.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-4.png index 6b4c7b191..b9d310c91 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-4.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-4.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-5.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-5.png index 62394f613..2b90330e9 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-5.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-5.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-6.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-6.png index 22aa9f17e..427e771b2 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-6.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-6.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-7.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-7.png index 474edf7ec..cdc204445 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-7.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-7.png differ diff --git a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-8.png b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-8.png index e77d1c1eb..0bfdb231c 100644 Binary files a/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-8.png and b/AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-8.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-center-disabled.png b/AppKit/Themes/Aristo/Resources/token-center-disabled.png new file mode 100644 index 000000000..0fcdbc47a Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/token-center-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-left-disabled.png b/AppKit/Themes/Aristo/Resources/token-left-disabled.png new file mode 100644 index 000000000..c3ae314d9 Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/token-left-disabled.png differ diff --git a/AppKit/Themes/Aristo/Resources/token-right-disabled.png b/AppKit/Themes/Aristo/Resources/token-right-disabled.png new file mode 100644 index 000000000..00d52832d Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/token-right-disabled.png differ diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 602e8db92..df1eae02c 100755 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -863,46 +863,46 @@ var themedButtonValues = nil, bezelColor = PatternColor( [ - ["textfield-bezel-square-0.png", 3.0, 4.0], - ["textfield-bezel-square-1.png", 1.0, 4.0], - ["textfield-bezel-square-2.png", 3.0, 4.0], - ["textfield-bezel-square-3.png", 3.0, 1.0], + ["textfield-bezel-square-0.png", 6.0, 6.0], + ["textfield-bezel-square-1.png", 1.0, 6.0], + ["textfield-bezel-square-2.png", 6.0, 6.0], + ["textfield-bezel-square-3.png", 6.0, 1.0], ["textfield-bezel-square-4.png", 1.0, 1.0], - ["textfield-bezel-square-5.png", 3.0, 1.0], - ["textfield-bezel-square-6.png", 3.0, 4.0], - ["textfield-bezel-square-7.png", 1.0, 4.0], - ["textfield-bezel-square-8.png", 3.0, 4.0] + ["textfield-bezel-square-5.png", 6.0, 1.0], + ["textfield-bezel-square-6.png", 6.0, 6.0], + ["textfield-bezel-square-7.png", 1.0, 6.0], + ["textfield-bezel-square-8.png", 6.0, 6.0] ]), bezelFocusedColor = PatternColor( [ - ["textfield-bezel-square-focused-0.png", 7.0, 7.0], - ["textfield-bezel-square-focused-1.png", 1.0, 7.0], - ["textfield-bezel-square-focused-2.png", 7.0, 7.0], - ["textfield-bezel-square-focused-3.png", 7.0, 1.0], + ["textfield-bezel-square-focused-0.png", 6.0, 6.0], + ["textfield-bezel-square-focused-1.png", 1.0, 6.0], + ["textfield-bezel-square-focused-2.png", 6.0, 6.0], + ["textfield-bezel-square-focused-3.png", 6.0, 1.0], ["textfield-bezel-square-focused-4.png", 1.0, 1.0], - ["textfield-bezel-square-focused-5.png", 7.0, 1.0], - ["textfield-bezel-square-focused-6.png", 7.0, 7.0], - ["textfield-bezel-square-focused-7.png", 1.0, 7.0], - ["textfield-bezel-square-focused-8.png", 7.0, 7.0] + ["textfield-bezel-square-focused-5.png", 6.0, 1.0], + ["textfield-bezel-square-focused-6.png", 6.0, 6.0], + ["textfield-bezel-square-focused-7.png", 1.0, 6.0], + ["textfield-bezel-square-focused-8.png", 6.0, 6.0] ]), bezelDisabledColor = PatternColor( [ - ["textfield-bezel-square-disabled-0.png", 3.0, 4.0], - ["textfield-bezel-square-disabled-1.png", 1.0, 4.0], - ["textfield-bezel-square-disabled-2.png", 3.0, 4.0], - ["textfield-bezel-square-disabled-3.png", 3.0, 1.0], + ["textfield-bezel-square-disabled-0.png", 6.0, 6.0], + ["textfield-bezel-square-disabled-1.png", 1.0, 6.0], + ["textfield-bezel-square-disabled-2.png", 6.0, 6.0], + ["textfield-bezel-square-disabled-3.png", 6.0, 1.0], ["textfield-bezel-square-disabled-4.png", 1.0, 1.0], - ["textfield-bezel-square-disabled-5.png", 3.0, 1.0], - ["textfield-bezel-square-disabled-6.png", 3.0, 4.0], - ["textfield-bezel-square-disabled-7.png", 1.0, 4.0], - ["textfield-bezel-square-disabled-8.png", 3.0, 4.0] - ]), + ["textfield-bezel-square-disabled-5.png", 6.0, 1.0], + ["textfield-bezel-square-disabled-6.png", 6.0, 6.0], + ["textfield-bezel-square-disabled-7.png", 1.0, 6.0], + ["textfield-bezel-square-disabled-8.png", 6.0, 6.0] + ]); - placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0]; - - // Global for reuse by CPTokenField. + // Global for reuse by subclasses + textDisabledColor = [CPColor colorWithCalibratedWhite:0.60 alpha:1.0]; + placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0]; themedTextFieldValues = [ [@"vertical-alignment", CPTopVerticalTextAlignment, CPThemeStateBezeled], @@ -911,30 +911,35 @@ var themedButtonValues = nil, [@"bezel-color", bezelDisabledColor, CPThemeStateBezeled | CPThemeStateDisabled], [@"font", [CPFont systemFontOfSize:12.0], CPThemeStateBezeled], - [@"content-inset", CGInsetMake(8.0, 7.0, 5.0, 8.0), CPThemeStateBezeled], - [@"content-inset", CGInsetMake(7.0, 7.0, 5.0, 8.0), CPThemeStateBezeled | CPThemeStateEditing], - [@"bezel-inset", CGInsetMake(3.0, 4.0, 3.0, 4.0), CPThemeStateBezeled], - [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled | CPThemeStateEditing], + // no border + [@"bezel-inset", CGInsetMakeZero()], + [@"content-inset", CGInsetMake(2.0, 2.0, 2.0, 2.0)], // as defined in [CPTextField +themeAttributes] + // with border + [@"bezel-inset", CGInsetMakeZero(), CPThemeStateBezeled], + [@"content-inset", CGInsetMake(8.0, 7.0, 7.0, 8.0), CPThemeStateBezeled], + + [@"text-color", textDisabledColor, CPThemeStateBezeled | CPThemeStateDisabled], [@"text-color", placeholderColor, CPTextFieldStatePlaceholder], + [@"text-color", placeholderColor, CPTextFieldStatePlaceholder | CPThemeStateDisabled], [@"line-break-mode", CPLineBreakByTruncatingTail, CPThemeStateTableDataView], [@"vertical-alignment", CPCenterVerticalTextAlignment, CPThemeStateTableDataView], - [@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 5.0), CPThemeStateTableDataView], + [@"content-inset", CGInsetMake(3.0, 3.0, 3.0, 5.0), CPThemeStateTableDataView], [@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0], CPThemeStateTableDataView], [@"text-color", [CPColor whiteColor], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView], [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView], [@"text-color", [CPColor blackColor], CPThemeStateTableDataView | CPThemeStateEditing], - [@"content-inset", CGInsetMake(7.0, 7.0, 5.0, 8.0), CPThemeStateTableDataView | CPThemeStateEditing], + [@"content-inset", CGInsetMake(8.0, 8.0, 7.0, 5.0), CPThemeStateTableDataView | CPThemeStateEditing], [@"font", [CPFont systemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateEditing], - [@"bezel-inset", CGInsetMake(-2.0, -2.0, -2.0, -2.0), CPThemeStateTableDataView | CPThemeStateEditing], + [@"bezel-inset", CGInsetMake(-1.0, -1.0, -1.0, -1.0), CPThemeStateTableDataView | CPThemeStateEditing], [@"text-color", [CPColor colorWithCalibratedWhite:125.0 / 255.0 alpha:1.0], CPThemeStateTableDataView | CPThemeStateGroupRow], [@"text-color", [CPColor colorWithCalibratedWhite:1.0 alpha:1.0], CPThemeStateTableDataView | CPThemeStateGroupRow | CPThemeStateSelectedTableDataView], [@"text-shadow-color", [CPColor whiteColor], CPThemeStateTableDataView | CPThemeStateGroupRow], [@"text-shadow-offset", CGSizeMake(0,1), CPThemeStateTableDataView | CPThemeStateGroupRow], - [@"text-shadow-color", [CPColor colorWithCalibratedWhite:0.0 alpha:0.6], CPThemeStateTableDataView | CPThemeStateGroupRow | CPThemeStateSelectedTableDataView], + [@"text-shadow-color", [CPColor colorWithCalibratedWhite:0.0 alpha:0.6], CPThemeStateTableDataView | CPThemeStateGroupRow | CPThemeStateSelectedTableDataView], [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateGroupRow] ]; @@ -954,35 +959,42 @@ var themedButtonValues = nil, var textfield = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 30.0)], bezelColor = PatternColor( [ - ["textfield-bezel-rounded-left.png", 13.0, 22.0], - ["textfield-bezel-rounded-center.png", 1.0, 22.0], - ["textfield-bezel-rounded-right.png", 13.0, 22.0] + ["textfield-bezel-rounded-left.png", 15.0, 30.0], + ["textfield-bezel-rounded-center.png", 1.0, 30.0], + ["textfield-bezel-rounded-right.png", 15.0, 30.0] ], PatternIsHorizontal), bezelFocusedColor = PatternColor( [ - ["textfield-bezel-rounded-focused-left.png", 17.0, 30.0], + ["textfield-bezel-rounded-focused-left.png", 15.0, 30.0], ["textfield-bezel-rounded-focused-center.png", 1.0, 30.0], - ["textfield-bezel-rounded-focused-right.png", 17.0, 30.0] + ["textfield-bezel-rounded-focused-right.png", 15.0, 30.0] ], PatternIsHorizontal), - placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0]; + bezelDisabledColor = PatternColor( + [ + ["textfield-bezel-rounded-disabled-left.png", 15.0, 30.0], + ["textfield-bezel-rounded-disabled-center.png", 1.0, 30.0], + ["textfield-bezel-rounded-disabled-right.png", 15.0, 30.0] + ], + PatternIsHorizontal); - // Global for reuse by CPSearchField + // Global for reuse by subclasses themedRoundedTextFieldValues = [ - [@"bezel-color", bezelColor, CPTextFieldStateRounded | CPThemeStateBezeled], - [@"bezel-color", bezelFocusedColor, CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing], + [@"bezel-color", bezelColor, CPTextFieldStateRounded | CPThemeStateBezeled], + [@"bezel-color", bezelFocusedColor, CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing], + [@"bezel-color", bezelDisabledColor, CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateDisabled], [@"font", [CPFont systemFontOfSize:12.0]], - [@"content-inset", CGInsetMake(8.0, 14.0, 6.0, 14.0), CPTextFieldStateRounded | CPThemeStateBezeled], - [@"content-inset", CGInsetMake(7.0, 14.0, 6.0, 14.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing], - - [@"bezel-inset", CGInsetMake(4.0, 4.0, 4.0, 4.0), CPTextFieldStateRounded | CPThemeStateBezeled], - [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing], + // The new bezel is one pixel shorter, so we add one extra empty pixel at the bottom + // for size compatibility with an earlier version. + [@"bezel-inset", CGInsetMake(0.0, 0.0, 1.0, 0.0), CPTextFieldStateRounded | CPThemeStateBezeled], + [@"content-inset", CGInsetMake(8.0, 13.0, 7.0, 14.0), CPTextFieldStateRounded | CPThemeStateBezeled], + [@"text-color", textDisabledColor, CPTextFieldStateRounded | CPThemeStateDisabled], [@"text-color", placeholderColor, CPTextFieldStateRounded | CPTextFieldStatePlaceholder], [@"min-size", CGSizeMake(0.0, 30.0), CPTextFieldStateRounded | CPThemeStateBezeled], @@ -1015,11 +1027,20 @@ var themedButtonValues = nil, overrides = [ - [@"content-inset", CGInsetMake(8.0, 0.0, 4.0, 0.0)], - // Placeholder is displayed as regular text, not tokens; requires a different inset. - [@"content-inset", CGInsetMake(9.0, 0.0, 5.0, 2.0), CPTextFieldStatePlaceholder], - [@"content-inset", CGInsetMake(6.0, 5.0, 5.0, 6.0), CPThemeStateBezeled], - [@"content-inset", CGInsetMake(9.0, 7.0, 6.0, 8.0), CPThemeStateBezeled | CPTextFieldStatePlaceholder], + [@"bezel-inset", CGInsetMakeZero()], + [@"editor-inset", CGInsetMake(2.0, 0.0, 0.0, 0.0)], + + // Non-bezeled token field with tokens + [@"content-inset", CGInsetMake(5.0, 8.0, 4.0, 8.0)], + + // Non-bezeled token field with no tokens + [@"content-inset", CGInsetMake(7.0, 8.0, 6.0, 8.0), CPTextFieldStatePlaceholder], + + // Bezeled token field with tokens + [@"content-inset", CGInsetMake(6.0, 8.0, 2.0, 8.0), CPThemeStateBezeled], + + // Bezeled token field with no tokens + [@"content-inset", CGInsetMake(8.0, 8.0, 7.0, 8.0), CPThemeStateBezeled | CPTextFieldStatePlaceholder] ]; [self registerThemeValues:overrides forView:tokenfield inherit:themedTextFieldValues]; @@ -1047,6 +1068,14 @@ var themedButtonValues = nil, ], PatternIsHorizontal), + bezelColorDisabled = PatternColor( + [ + ["token-left-disabled.png", 11.0, 19.0], + ["token-center-disabled.png", 1.0, 19.0], + ["token-right-disabled.png", 11.0, 19.0] + ], + PatternIsHorizontal), + textColor = [CPColor colorWithRed:41.0 / 255.0 green:51.0 / 255.0 blue:64.0 / 255.0 alpha:1.0], textHighlightedColor = [CPColor whiteColor], @@ -1054,18 +1083,19 @@ var themedButtonValues = nil, [ [@"bezel-color", bezelColor, CPThemeStateBezeled], [@"bezel-color", bezelHighlightedColor, CPThemeStateBezeled | CPThemeStateHighlighted], + [@"bezel-color", bezelColorDisabled, CPThemeStateBezeled | CPThemeStateDisabled], [@"text-color", textColor], [@"text-color", textHighlightedColor, CPThemeStateHighlighted], - [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled], - [@"content-inset", CGInsetMake(1.0, 24.0, 2.0, 16.0), CPThemeStateBezeled], + [@"bezel-inset", CGInsetMakeZero(), CPThemeStateBezeled], + [@"content-inset", CGInsetMake(1.0, 22.0, 3.0, 15.0), CPThemeStateBezeled], // Minimum height == maximum height since tokens are fixed height. [@"min-size", CGSizeMake(0.0, 19.0)], [@"max-size", CGSizeMake(-1.0, 19.0)], - [@"vertical-alignment", CPCenterTextAlignment], + [@"vertical-alignment", CPCenterTextAlignment] ]; [self registerThemeValues:themeValues forView:token]; @@ -1091,7 +1121,7 @@ var themedButtonValues = nil, [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered], [@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered | CPThemeStateHighlighted], - [@"offset", CGPointMake(18, 6), CPThemeStateBordered] + [@"offset", CGPointMake(17, 6), CPThemeStateBordered] ]; [self registerThemeValues:themeValues forView:button]; @@ -1099,6 +1129,88 @@ var themedButtonValues = nil, return button; } ++ (CPComboBox)themedComboBox +{ + var combo = [[CPComboBox alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 29.0)], + + bezelColor = PatternColor( + [ + ["combobox-bezel-left.png", 6.0, 29.0], + ["combobox-bezel-center.png", 1.0, 29.0], + ["combobox-bezel-right.png", 24.0, 29.0] + ], + PatternIsHorizontal), + + bezelFocusedColor = PatternColor( + [ + ["combobox-bezel-focused-left.png", 6.0, 29.0], + ["combobox-bezel-focused-center.png", 1.0, 29.0], + ["combobox-bezel-focused-right.png", 24.0, 29.0] + ], + PatternIsHorizontal), + + bezelDisabledColor = PatternColor( + [ + ["combobox-bezel-disabled-left.png", 6.0, 29.0], + ["combobox-bezel-disabled-center.png", 1.0, 29.0], + ["combobox-bezel-disabled-right.png", 24.0, 29.0] + ], + PatternIsHorizontal), + + bezelNoBorderColor = PatternColor( + [ + ["combobox-bezel-no-border-left.png", 6.0, 29.0], + ["combobox-bezel-no-border-center.png", 1.0, 29.0], + ["combobox-bezel-no-border-right.png", 24.0, 29.0] + ], + PatternIsHorizontal), + + bezelNoBorderFocusedColor = PatternColor( + [ + ["combobox-bezel-no-border-focused-left.png", 6.0, 29.0], + ["combobox-bezel-no-border-focused-center.png", 1.0, 29.0], + ["combobox-bezel-no-border-focused-right.png", 24.0, 29.0] + ], + PatternIsHorizontal), + + bezelNoBorderDisabledColor = PatternColor( + [ + ["combobox-bezel-no-border-disabled-left.png", 6.0, 29.0], + ["combobox-bezel-no-border-disabled-center.png", 1.0, 29.0], + ["combobox-bezel-no-border-disabled-right.png", 24.0, 29.0] + ], + PatternIsHorizontal), + + overrides = + [ + [@"bezel-color", bezelColor, CPThemeStateBezeled | CPComboBoxStateButtonBordered], + [@"bezel-color", bezelFocusedColor, CPThemeStateBezeled | CPComboBoxStateButtonBordered | CPThemeStateEditing], + [@"bezel-color", bezelDisabledColor, CPThemeStateBezeled | CPComboBoxStateButtonBordered | CPThemeStateDisabled], + + [@"bezel-color", bezelNoBorderColor, CPThemeStateBezeled], + [@"bezel-color", bezelNoBorderFocusedColor, CPThemeStateBezeled | CPThemeStateEditing], + [@"bezel-color", bezelNoBorderDisabledColor, CPThemeStateBezeled | CPThemeStateDisabled], + + [@"border-inset", CGInsetMake(3.0, 3.0, 3.0, 3.0), CPThemeStateBezeled], + + // The right border inset has to make room for the focus ring and popup button + [@"content-inset", CGInsetMake(8.0, 27.0, 7.0, 8.0), CPThemeStateBezeled | CPComboBoxStateButtonBordered], + [@"content-inset", CGInsetMake(8.0, 24.0, 7.0, 8.0), CPThemeStateBezeled], + [@"content-inset", CGInsetMake(8.0, 24.0, 7.0, 8.0), CPThemeStateBezeled | CPThemeStateEditing], + + [@"popup-button-size", CGSizeMake(21.0, 23.0), CPThemeStateBezeled | CPComboBoxStateButtonBordered], + [@"popup-button-size", CGSizeMake(17.0, 23.0), CPThemeStateBezeled], + + // Because combo box uses a three-part bezel, the height is fixed + [@"min-size", CGSizeMake(0, 29.0)], + [@"max-size", CGSizeMake(-1, 29.0)] + ]; + + [self registerThemeValues:overrides forView:combo inherit:themedTextFieldValues]; + + return combo; +} + + (CPRadioButton)themedRadioButton { var button = [CPRadio radioWithTitle:@"Hello Friend!"], diff --git a/AppKit/_CPPopUpList.j b/AppKit/_CPPopUpList.j new file mode 100644 index 000000000..d42d87a18 --- /dev/null +++ b/AppKit/_CPPopUpList.j @@ -0,0 +1,849 @@ +/* + * _CPPopUpList.j + * AppKit + * + * Created by Aparajita Fishman. + * Copyright (c) 2012, The Cappuccino Foundation + * + * 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 "CPTableView.j" +@import "_CPPopUpListDataSource.j" + + +/*! + Notification sent when the list is about to pop up. \c object is the _CPPopUpList. +*/ +_CPPopUpListWillPopUpNotification = @"_CPPopUpListWillPopUpNotification"; + +/*! + Notification sent when the list is about to be dismissed. \c object is the _CPPopUpList. +*/ +_CPPopUpListWillDismissNotification = @"_CPPopUpListWillDismissNotification"; + +/*! + Notification sent when the list is dismissed. \c object is the _CPPopUpList. +*/ +_CPPopUpListDidDismissNotification = @"_CPPopUpListDidDismissNotification"; + +/*! + Notification sent by when an item is selected. \c object is the _CPPopUpList. + When this is received the list has already been dismissed and the dismiss notification has been sent. +*/ +_CPPopUpListItemWasClickedNotification = @"_CPPopUpListItemWasClickedNotification"; + +/*! + @ignore + + The minimum number of items that must be visible below the related field. + If less than this number would be completely visible, and there is room for this many complete items + above the field, the list is displayed above. +*/ +var ListMinimumItems = 3; + +/*! @ignore */ +var ListColumnIdentifier = @"1"; + + +/*! + This class is a controller for a panel that can pop up and display a scrollable list of items in a CPTableView. + It is used by CPComboBox to display the list of choices. + + This class requires a data source which must conform to the interface of _CPPopUpListDataSource. + + Objects of this class send the following notifications: + + _CPPopUpListWillPopUpNotification + _CPPopUpListWillDismissNotification + _CPPopUpListDidDismissNotification + _CPPopUpListItemWasClickedNotification +*/ +@implementation _CPPopUpList : CPObject +{ + _CPPopUpListDataSource _dataSource; + BOOL _itemWasClicked; + BOOL _listWasClicked; + int _listWidth; + _CPPopUpPanel _panel; + CPScrollView _scrollView; + _CPPopUpTableView _tableView; + CPTableColumn _tableColumn; +} + +#pragma mark Creating and Displaying a List + +/*! + Creates a pop up list of choices that will display in a scrollable CPTableView. + + @param aDataSource A subclass of _CPPopUpListDataSource +*/ +- (id)initWithDataSource:(_CPPopUpListDataSource)aDataSource +{ + self = [super init]; + + if (self) + { + [self setDataSource:aDataSource]; + _itemWasClicked = NO; + _listWasClicked = NO; + _listWidth = 0; + + _tableView = [self makeTableView]; + + // Start with a default size, we will resize it later + frame = CGRectMake(0, 0, 200, 200); + + _tableColumn = [[CPTableColumn alloc] initWithIdentifier:ListColumnIdentifier]; + [_tableColumn setWidth:CGRectGetWidth(frame) - [CPScroller scrollerWidth]]; + [_tableColumn setResizingMask:CPTableColumnAutoresizingMask]; + [_tableView addTableColumn:_tableColumn]; + + _scrollView = [self makeScrollViewWithFrame:CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame))]; + [_scrollView setDocumentView:_tableView]; + + // This has to be done after setDocumentView so that the table knows which scroll view to update + [_tableView setHeaderView:nil]; + + _panel = [self makeListPanelWithFrame:frame]; + [[_panel contentView] addSubview:_scrollView]; + [_panel setInitialFirstResponder:_tableView]; + + if ([_dataSource numberOfItemsInList:self] > 0) + [_tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + else + [_tableView setEnabled:NO]; + + [_scrollView scrollToBeginningOfDocument:nil]; + } + + return self; +} + +/*! @ignore */ +- (CPPanel)makeListPanelWithFrame:(CGRect)aFrame +{ + var panel = [[_CPPopUpPanel alloc] initWithContentRect:aFrame styleMask:CPBorderlessWindowMask]; + + [panel setTitle:@""]; + [panel setFloatingPanel:YES]; + [panel setBecomesKeyOnlyIfNeeded:YES]; + [panel setHasShadow:YES]; + [panel setShadowStyle:CPMenuWindowShadowStyle]; + [panel setDelegate:self]; + + return panel; +} + +/*! @ignore */ +- (_CPPopUpTableView)makeTableView +{ + [self removeTableViewObservers]; + + var table = [[_CPPopUpTableView alloc] initWithFrame:CGRectMakeZero()]; + + [table setDelegate:self]; + [table setDataSource:self]; + [table setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle]; + [table setUsesAlternatingRowBackgroundColors:NO]; + [table setAllowsMultipleSelection:NO]; + [table setIntercellSpacing:CGSizeMake(3, 2)]; + [table setTarget:self]; + [table setDoubleAction:@selector(tableViewClickAction:)]; + [table setAction:@selector(tableViewClickAction:)]; + [table setRowHeight:[self rowHeightForTableView:table]]; + + return table; +} + +/*! @ignore */ +- (void)removeTableViewObservers +{ + if (_tableView) + { + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [defaultCenter removeObserver:self name:CPTableViewSelectionIsChangingNotification object:_tableView]; + [defaultCenter removeObserver:self name:CPTableViewSelectionDidChangeNotification object:_tableView]; + } +} + +/*! @ignore */ +- (CPScrollView)makeScrollViewWithFrame:(CGRect)aFrame +{ + var scroll = [[CPScrollView alloc] initWithFrame:aFrame]; + + [scroll setBorderType:CPLineBorder]; + [scroll setAutohidesScrollers:NO]; + [scroll setHasVerticalScroller:YES]; + [scroll setHasHorizontalScroller:NO]; + [scroll setLineScroll:[_tableView rowHeight]]; + [scroll setVerticalPageScroll:0.0]; + + return scroll; +} + +/*! + Pop up the list if it is not already visible. + If it is not visible, a _CPPopUpListWillPopUpNotification will be sent. + + @param aRect A rect (in \c aView coordinates) to display relative to + @param aView The view whose coordinate system \c aRect is in + @param offset How far to offset the list from \c aRect +*/ +- (void)popUpRelativeToRect:(CGRect)aRect view:(CPView)aView offset:(int)offset +{ + if ([_panel isVisible]) + return; + + var rowRect = [_tableView rectOfRow:[self numberOfRowsInTableView:_tableView] - 1], + frame = CGRectMake(0, 0, MAX(_listWidth, CGRectGetWidth(aRect)), CGRectGetMaxY(rowRect)); + + // Place the frame relative to aRect and constrain it to the screen bounds + frame = [self constrain:frame relativeToRect:aRect view:aView offset:offset]; + + [_panel setFrame:frame]; + [_scrollView setFrameSize:CGSizeMakeCopy(frame.size)]; + [_tableView setEnabled:[_dataSource numberOfItemsInList:self] > 0]; + [self scrollItemAtIndexToTop:[_tableView selectedRow]]; + + [self listWillPopUp]; + + [_panel orderFront:nil]; +} + +#pragma mark Setting Display Attributes + +/*! + Returns the desired width of the list. +*/ +- (int)listWidth +{ + return _listWidth; +} + +/*! + Sets the desired width of the list for the next call to \ref showListForfield:relativeTo:. + Note that the actual display width may be larger if the given width is less than the width of the associated + field. +*/ +- (void)setListWidth:(int)width +{ + _listWidth = width; +} + +- (void)setFont:(CPFont)aFont +{ + var oldDataView = [_tableColumn dataView], + newDataView = [CPTextField new]; + + [newDataView setFont:aFont]; + [newDataView setAlignment:[oldDataView alignment]]; + [_tableColumn setDataView:newDataView]; + + // Force the data view cache to flush + [_tableView reloadData]; +} + +- (void)setAlignment:(CPTextAlignment)alignment +{ + var oldDataView = [_tableColumn dataView], + newDataView = [CPTextField new]; + + [newDataView setAlignment:alignment]; + [newDataView setFont:[oldDataView font]]; + [_tableColumn setDataView:newDataView]; + + // Force the data view cache to flush + [_tableView reloadData]; +} + +/*! + Returns whether the list is currently visible. +*/ +- (BOOL)isVisible +{ + return [_panel isVisible]; +} + +/*! + Returns the desired row height for the table view. + Subclasses should override this if they want something other than the default. +*/ +- (int)rowHeightForTableView:(CPTableView)aTableView +{ + return [aTableView rowHeight]; +} + +/*! + Returns the table view used by the list. +*/ +- (CPTableView)tableView +{ + return _tableView; +} + +/*! + Returns the single table column used by the list. +*/ +- (CPTableColumn)tableColumn +{ + return _tableColumn; +} + +/*! + Returns the scroll view used by the list. +*/ +- (CPScrollView)scrollView +{ + return _scrollView; +} + +/*! + Returns the panel in which the list appears. +*/ +- (CPPanel)panel +{ + return _panel; +} + +#pragma mark Setting a Data Source + +- (void)setDataSource:(_CPPopUpListDataSource)aDataSource +{ + if (_dataSource === aDataSource) + return; + + if (![_CPPopUpListDataSource protocolIsImplementedByObject:aDataSource]) + { + CPLog.warn("Illegal %s data source (%s). Must implement the methods in _CPPopUpListDataSource.", [self className], [aDataSource description]); + } + else + _dataSource = aDataSource; +} + +- (_CPPopUpListDataSource)dataSource +{ + return _dataSource; +} + +#pragma mark Manipulating the Selection + +/*! + Select the next item in the list if there one. If there is currently no selected item, + the first item is selected. Returns YES if the selection changed. +*/ +- (BOOL)selectNextItem +{ + if (![_tableView isEnabled]) + return NO; + + var row = [_tableView selectedRow]; + + if (row < ([_dataSource numberOfItemsInList:self] - 1)) + return [self selectRow:++row]; + else + return NO; +} + +/*! + Select the previous item in the list. If there is currently no selected item, + nothing happens. Returns YES if the selection changed. +*/ +- (BOOL)selectPreviousItem +{ + if (![_tableView isEnabled]) + return NO; + + var row = [_tableView selectedRow]; + + if (row > 0) + return [self selectRow:--row]; + else + return NO; +} + +/*! + Returns the selected object value. If no value is selected, + returns nil. +*/ +- (id)selectedObjectValue +{ + var row = [_tableView selectedRow]; + + return (row >= 0) ? [_dataSource list:self objectValueForItemAtIndex:row] : nil; +} + +/*! + Returns the selected value as a single-line string. If no value is selected, + returns nil. +*/ +- (CPString)selectedStringValue +{ + var value = [self selectedObjectValue]; + + return value !== nil ? [_dataSource list:self stringValueForObjectValue:value] : nil; +} + +/*! + Returns the last selected row in the list. If no row has been selected, returns -1. +*/ +- (int)selectedRow +{ + return [_tableView selectedRow]; +} + +/*! + Selects a row and scrolls it to be visible. Returns YES if the selection actually changed. +*/ +- (BOOL)selectRow:(int)row +{ + if (row === [_tableView selectedRow]) + return NO; + + var validRow = (row >= 0 && row < [self numberOfRowsInTableView:_tableView]), + indexes = validRow ? [CPIndexSet indexSetWithIndex:row] : [CPIndexSet indexSet]; + + [_tableView selectRowIndexes:indexes byExtendingSelection:NO]; + + if (validRow) + { + [_tableView scrollRowToVisible:row]; + return YES; + } + else + return NO; +} + +#pragma mark Manipulating the Displayed List + +/*! + Scroll the list down one page. +*/ +- (void)scrollPageDown +{ + [_scrollView scrollPageDown:nil]; +} + +/*! + Scroll the list up one page. +*/ +- (void)scrollPageUp +{ + [_scrollView scrollPageUp:nil]; +} + +/*! + Scroll to the top of the list. +*/ +- (void)scrollToTop +{ + [_scrollView scrollToBeginningOfDocument:nil]; +} + +/*! + Scroll to the bottom of the list. +*/ +- (void)scrollToBottom +{ + [_scrollView scrollToEndOfDocument:nil]; +} + +- (void)scrollItemAtIndexToTop:(int)row +{ + var rect = [_tableView rectOfRow:row]; + + [[_tableView superview] scrollToPoint:rect.origin]; +} + +/*! + Close the list if it is currently visible. If it is visible, + a CPComboBoxWillDismissNotification will be sent. If the + list is being closed after an item was clicked, the close + is delayed slightly so the user can briefly see the clicked row + get highlighted. +*/ +- (void)close +{ + if (![_panel isVisible]) + return; + + if ([self listWasClicked]) + { + [self setListWasClicked:NO]; + + // Wait until we get through the run loop and delay a little + // so the user can briefly see the clicked row get highlighted. + if ([self itemWasClicked]) + { + [self setItemWasClicked:NO]; + [CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(closeListAfterItemClick) userInfo:nil repeats:NO]; + return; + } + } + + [[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListWillDismissNotification object:self]; + [_panel close]; + [[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListDidDismissNotification object:self]; +} + +/*! + Close the list after an item was clicked. +*/ +- (void)closeListAfterItemClick +{ + [self close]; + [[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListItemWasClickedNotification object:self]; +} + +#pragma mark Handling Events + +/*! + Handles standard key equivalents for moving the selection + and selecting an item. This method should be called by + the -performKeyEquivalent method of the field that is + controlling the list. +*/ +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + var key = [anEvent charactersIgnoringModifiers]; + + switch (key) + { + case CPDownArrowFunctionKey: + if ([self isVisible]) + { + [self selectNextItem]; + return YES; + } + break; + + case CPUpArrowFunctionKey: + if ([self isVisible]) + { + [self selectPreviousItem]; + return YES; + } + break; + + case CPEscapeFunctionKey: + if ([self isVisible]) + { + [self close]; + return YES; + } + break; + + case CPPageUpFunctionKey: + if ([self isVisible]) + { + [self scrollPageUp]; + return YES; + } + break; + + case CPPageDownFunctionKey: + if ([self isVisible]) + { + [self scrollPageDown]; + return YES; + } + break; + + case CPHomeFunctionKey: + if ([self isVisible]) + { + [self scrollToTop]; + return YES; + } + break; + + case CPEndFunctionKey: + if ([self isVisible]) + { + [self scrollToBottom]; + return YES; + } + break; + } + + return NO; +} + +/*! + Returns whether an item in the list was clicked since it was opened. + If there are no items, \ref itemWasClicked will always return NO. +*/ +- (BOOL)itemWasClicked +{ + return _itemWasClicked && ([_dataSource numberOfItemsInList:self] > 0); +} + +/*! + Sets whether an item in the list was clicked since it was opened. + If there are no items, \ref itemWasClicked will always return NO. + + Subclasses will usually want to set this in the mouseDown: + of the control. +*/ +- (void)setItemWasClicked:(BOOL)flag +{ + _itemWasClicked = ([_dataSource numberOfItemsInList:self] > 0) && flag; +} + +/*! + Returns whether any view in the list was clicked since it was opened. + If there are no items, \ref listWasClicked will always return NO. +*/ +- (BOOL)listWasClicked +{ + return _listWasClicked && ([_dataSource numberOfItemsInList:self] > 0); +} + +/*! + Sets whether any view in the list was clicked since it was opened. + If there are no items, \ref listWasClicked will always return NO. + + Subclasses will usually want to use a subclass of CPPanel and override + sendEvent: to set this flag when the event type is CPLeftMouseDown + or CPRightMouseDown. This is distinct from \ref itemWasClicked because, + for example, a scroller in the list may be clicked without clicking an + item in the list. +*/ +- (void)setListWasClicked:(BOOL)flag +{ + _listWasClicked = ([_dataSource numberOfItemsInList:self] > 0) && flag; +} + +/*! + Returns whether a controlling view should resign. This should be called + from the controlling view's resignFirstResponder method. +*/ +- (BOOL)controllingViewShouldResign +{ + if ([self listWasClicked]) + { + /* + If an item was not clicked (probably the scrollbar), clear the click flag so that future + clicks outside the list will allow it to close. + */ + if ([self listWasClicked] && ![self itemWasClicked]) + [self setListWasClicked:NO]; + + return NO; + } + else + return YES; +} +#pragma mark Internal Helpers + +/*! @ignore */ +- (void)listWillPopUp +{ + [[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListWillPopUpNotification object:self]; +} + +/*! + Return a frame in platform window base coordinates such that the list, when displayed, will show at least ListMinimumItems + items completely on screen. Normally the list should be displayed below \c aRect, but if there is not room + for at least ListMinimumItems items, an attempt should be made to display that many + items above \c aRect. If the minimum cannot be displayed on top, whichever direction can display more items + is chosen. + @ignore +*/ +- (CGRect)constrain:(CGRect)aFrame relativeToRect:(CGRect)aRect view:(CPView)aView offset:(int)offset +{ + // Convert from the view's coordinate system to the coordinate system of the primary platform window + var baseOrigin = [aView convertPointToBase:aRect.origin], + windowOrigin = [[aView window] convertBaseToPlatformWindow:baseOrigin], + rowHeight = [self rowHeightForTableView:_tableView] + [_tableView intercellSpacing].height, + + // Be sure to clip the number of displayed rows to what the field wants + numberOfRows = MIN([self numberOfRowsInTableView:_tableView], [_dataSource numberOfVisibleItemsInList:self]), + + // Add 2 to height for border + frame = CGRectMake(windowOrigin.x, windowOrigin.y + CGRectGetHeight(aRect) + offset, MAX(_listWidth, CGRectGetWidth(aFrame)), (rowHeight * numberOfRows) + 2), + + // Get the bottom coordinate of the frame and the platform window + bottomFrame = CGRectMakeCopy(frame), + bottom = CGRectGetMaxY(bottomFrame), + viewRect = [[CPPlatformWindow primaryPlatformWindow] visibleFrame], + visibleBottom = CGRectGetMaxY(viewRect), + bottomVisibleRows = numberOfRows; + + // Make sure it will fit in the screen. If not, reduce the number of items till we reach the minimum. + while (bottom > visibleBottom && bottomVisibleRows >= ListMinimumItems) + { + bottom -= rowHeight; + bottomFrame.size.height -= rowHeight; + --bottomVisibleRows; + } + + if (bottom >= visibleBottom || bottomVisibleRows < ListMinimumItems) + { + // The minimum number of items will not fit, try above + var topFrame = CGRectMakeCopy(frame); + + topFrame.origin.y = windowOrigin.y - offset - CGRectGetHeight(topFrame); + + var visibleTop = CGRectGetMinY(viewRect), + topVisibleRows = numberOfRows; + + while (topFrame.origin.y <= visibleTop && topVisibleRows >= ListMinimumItems) + { + topFrame.origin.y += rowHeight; + topFrame.size.height -= rowHeight; + --topVisibleRows; + } + + // If there is room on the top or it can display more than at the bottom, show it there + if ((topFrame.origin.y > visibleTop && topVisibleRows >= ListMinimumItems) || topVisibleRows > bottomVisibleRows) + frame = topFrame; + else + frame = bottomFrame; + } + else + frame = bottomFrame; + + return frame; +} + +- (void)tableViewClickAction:(id)sender +{ + [self close]; +} + +@end + + +var _CPPopUpListDataSourceKey = @"_CPPopUpListDataSourceKey", + _CPPopUpListListWidthKey = @"_CPPopUpListListWidthKey", + _CPPopUpListListPanelKey = @"_CPPopUpListListPanelKey", + _CPPopUpListScrollViewKey = @"_CPPopUpListScrollViewKey", + _CPPopUpListTableViewKey = @"_CPPopUpListTableViewKey"; + +@implementation _CPPopUpList (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + _listWasClicked = NO; + _itemWasClicked = NO; + + _dataSource = [aCoder decodeObjectForKey:_CPPopUpListDataSourceKey]; + _listWidth = [aCoder decodeIntForKey:_CPPopUpListListWidthKey]; + _panel = [aCoder decodeObjectForKey:_CPPopUpListListPanelKey]; + _scrollView = [aCoder decodeObjectForKey:_CPPopUpListScrollViewKey]; + _tableView = [aCoder decodeObjectForKey:_CPPopUpListTableViewKey]; + _tableColumn = [_tableView tableColumnWithIdentifier:ListColumnIdentifier]; + [_scrollView setDocumentView:_tableView]; + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [super encodeWithCoder:aCoder]; + + [aCoder encodeObject:_dataSource forKey:_CPPopUpListDataSourceKey]; + [aCoder encodeObject:_listWidth forKey:_CPPopUpListListWidthKey]; + [aCoder encodeObject:_panel forKey:_CPPopUpListListPanelKey]; + [aCoder encodeObject:_scrollView forKey:_CPPopUpListScrollViewKey]; + [aCoder encodeObject:_tableView forKey:_CPPopUpListTableViewKey]; +} + +@end + +@implementation _CPPopUpList (CPTableViewDataSource) + +- (int)numberOfRowsInTableView:(id)aTableView +{ + return MAX([_dataSource numberOfItemsInList:self], 1); +} + +- (id)tableView:(id)aTableView objectValueForTableColumn:(CPTableColumn)aColumn row:(int)aRow +{ + return [_dataSource list:self displayValueForObjectValue:[_dataSource list:self objectValueForItemAtIndex:aRow]]; +} + +@end + +@implementation _CPPopUpTableView : CPTableView +{ + BOOL _acceptFirstResponder; +} + +- (id)initWithFrame:(CGRect)aFrame +{ + if (self = [super initWithFrame:aFrame]) + { + // We want the autocomplete to remain first responder until we are clicked. + _acceptFirstResponder = NO; + } + + return self; +} + +- (void)trackMouse:(CPEvent)anEvent +{ + if (![self isEnabled]) + return; + + [[self delegate] setItemWasClicked:YES]; + + // CPTableView will not track the click if it is not first responder + _acceptFirstResponder = YES; + [[self window] makeFirstResponder:self]; + [super trackMouse:anEvent]; +} + +- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp +{ + _acceptFirstResponder = NO; + [super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp]; +} + +- (BOOL)acceptsFirstResponder +{ + return _acceptFirstResponder; +} + +/*! + Return the column used for the list. +*/ +- (CPTableColumn)listColumn +{ + return _tableColumn; +} + +@end + +@implementation _CPPopUpPanel : CPPanel + +- (void)sendEvent:(CPEvent)anEvent +{ + var type = [anEvent type]; + + if (type === CPLeftMouseDown || type === CPRightMouseDown) + [[self delegate] setListWasClicked:YES]; + + return [super sendEvent:anEvent]; +} + +@end diff --git a/AppKit/_CPPopUpListDataSource.j b/AppKit/_CPPopUpListDataSource.j new file mode 100644 index 000000000..cd413779d --- /dev/null +++ b/AppKit/_CPPopUpListDataSource.j @@ -0,0 +1,98 @@ +/* + * _CPPopUpListDataSource.j + * AppKit + * + * Created by Aparajita Fishman. + * Copyright (c) 2012, The Cappuccino Foundation + * + * 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 + */ + + +/*! + This abstract base class defines the methods that delegates of _CPPopUpList must implement. + You may either subclass this class to use the default implementation of + list:objectValueForItemAtIndex: and list:displayValueForObjectValue: or use your + own class and define these methods yourself. +*/ +@implementation _CPPopUpListDataSource : CPObject + +/*! + Returns whether the given object conforms to the minimum protocol defined by this class. +*/ ++ (BOOL)protocolIsImplementedByObject:(id)anObject +{ + return (anObject && + [anObject respondsToSelector:@selector(numberOfItemsInList:)] && + [anObject respondsToSelector:@selector(numberOfVisibleItemsInList:)] && + [anObject respondsToSelector:@selector(list:objectValueForItemAtIndex:)] && + [anObject respondsToSelector:@selector(list:displayValueForObjectValue:)] && + [anObject respondsToSelector:@selector(list:stringValueForObjectValue:)]); +} + +/*! + Returns the number of items managed by the list. +*/ +- (int)numberOfItemsInList:(_CPPopUpList)aList +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Returns the number of items to display at one time. +*/ +- (int)numberOfVisibleItemsInList:(_CPPopUpList)aList +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Returns the data for a given row index. +*/ +- (id)list:(_CPPopUpList)aList objectValueForItemAtIndex:(int)index +{ + _CPRaiseInvalidAbstractInvocation(self, _cmd); +} + +/*! + Returns a value to display for a single row in the list. Subclasses should override + this if the table data needs to be converted or formatted in some way to be displayed. + If your data source use a data representation other than CPStrings, you must override + this method and return the appropriate data when there are no search results. + + If the _CPPopUpList's table uses a custom data view, this method should return a value suitable + for sending to the setObjectValue: method of the data view. + + @param aValue Data for the given row + @return A value to be displayed in the list +*/ +- (id)list:(_CPPopUpList)aList displayValueForObjectValue:(id)aValue +{ + return aValue || @""; +} + +/*! + Returns a single-line string representation for an object value. Subclasses should override + this if the object data is not convertible to a simple single-line string. + + @param aValue Table data to be converted to a string + @return A value to be displayed in the autocomplete field +*/ +- (CPString)list:(_CPPopUpList)aList stringValueForObjectValue:(id)aValue +{ + return String(aValue); +} + +@end diff --git a/Tests/Manual/CPComboBoxTest/AppController.j b/Tests/Manual/CPComboBoxTest/AppController.j new file mode 100644 index 000000000..87a40e1ef --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/AppController.j @@ -0,0 +1,228 @@ +/* + * AppController.j + * CPComboBoxTest + * + * Created by Aparajita Fishman. + * Copyright (c) 2011, Intalio, 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 + + +@implementation Companies : CPObject +{ + CPMutableArray items @accessors; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + items = [CPMutableArray array]; + + var employees = "Tom,Dick,Harry,Ted,Sam,Fred,Ralph,Ed,Tim,John,Bill,Irving,Stan,Rodney".split(","); + + employees = [CPArray arrayWithObjects:employees count:employees.length]; + [items addObject:[CPDictionary dictionaryWithObjectsAndKeys:@"Spacely Sprockets", @"name", employees, @"employees"]]; + + employees = "Jane,Sally,Joan,Sara,Melissa,Beverly,Gillian,Sandra,Samantha,Mary,Kate".split(","); + employees = [CPArray arrayWithObjects:employees count:employees.length]; + [items addObject:[CPDictionary dictionaryWithObjectsAndKeys:@"Cogswell Cogs", @"name", employees, @"employees"]]; + } + + return self; +} + +@end + +@implementation AppController : CPObject +{ + @outlet CPWindow theWindow; + CPWindow testWindow; + CPString employee @accessors; + Companies companies @accessors; + CPArrayController companiesController; + CPArrayController employeesController; + CPComboBox combo; + @outlet CPComboBox cibCombo; + @outlet CPTextField comboTarget; + CPString fontName; + int nextCheckboxY; +} + +- (id)init +{ + if (self = [super init]) + { + fontName = [CPFont systemFontFace]; + companies = [Companies new]; + } + + return self; +} + +- (void)awakeFromCib +{ +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + testWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(30, 50, 500, 400) styleMask:CPTitledWindowMask | CPResizableWindowMask]; + + var contentView = [testWindow contentView], + companiesScrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(30, 30, 200, 100)], + companiesTable = [[CPTableView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], + employeesScrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(250, 30, 200, 200)], + employeesTable = [[CPTableView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)]; + + [testWindow setTitle:@"CPComboBox (from code)"]; + + var column = [[CPTableColumn alloc] initWithIdentifier:@"name"]; + [column setResizingMask:CPTableColumnAutoresizingMask]; + [companiesTable addTableColumn:column]; + [companiesTable setAllowsMultipleSelection:YES]; + [companiesTable setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle]; + [companiesScrollView setHasHorizontalScroller:NO]; + [companiesScrollView setHasVerticalScroller:YES]; + [companiesScrollView setDocumentView:companiesTable]; + [companiesScrollView setBorderType:CPBezelBorder]; + [companiesTable setHeaderView:nil]; + [companiesTable setAllowsEmptySelection:NO]; + + [contentView addSubview:companiesScrollView]; + + column = [[CPTableColumn alloc] initWithIdentifier:@"name"]; + [column setResizingMask:CPTableColumnAutoresizingMask]; + [employeesTable addTableColumn:column]; + [employeesTable setAllowsMultipleSelection:YES]; + [employeesTable setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle]; + [employeesScrollView setHasHorizontalScroller:NO]; + [employeesScrollView setHasVerticalScroller:YES]; + [employeesScrollView setDocumentView:employeesTable]; + [employeesScrollView setBorderType:CPBezelBorder]; + [employeesTable setHeaderView:nil]; + [employeesTable setAllowsEmptySelection:YES]; + + [contentView addSubview:employeesScrollView]; + + combo = [[CPComboBox alloc] initWithFrame:CGRectMake(250, 240, 200, 29)]; + [combo setCompletes:YES]; + [contentView addSubview:combo]; + + var textfield = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:200]; + [textfield setFrameOrigin:CGPointMake(250, 290)]; + [contentView addSubview:textfield]; + + var center = [CPNotificationCenter defaultCenter]; + [center addObserver:self selector:@selector(comboNote:) name:CPComboBoxSelectionDidChangeNotification object:combo]; + [center addObserver:self selector:@selector(comboNote:) name:CPComboBoxSelectionIsChangingNotification object:combo]; + [center addObserver:self selector:@selector(comboNote:) name:CPComboBoxWillDismissNotification object:combo]; + [center addObserver:self selector:@selector(comboNote:) name:CPComboBoxWillPopUpNotification object:combo]; + [center addObserver:self selector:@selector(comboNote:) name:CPControlTextDidEndEditingNotification object:combo]; + + nextCheckboxY = 166; + [self makeCheckBoxWithTitle:@"Enabled" defaultState:CPOnState]; + [self makeCheckBoxWithTitle:@"Button bordered" defaultState:CPOnState]; + [self makeCheckBoxWithTitle:@"Bold" defaultState:CPOffState]; + [self makeCheckBoxWithTitle:@"Completes" defaultState:CPOnState]; + [self makeCheckBoxWithTitle:@"Force selection" defaultState:CPOffState]; + [self makeCheckBoxWithTitle:@"Vertical scrollbar" defaultState:CPOnState]; + [self makeCheckBoxWithTitle:@"Big item height" defaultState:CPOffState]; + [self makeCheckBoxWithTitle:@"More visible items" defaultState:CPOffState]; + + companiesController = [CPArrayController new]; + [companiesController bind:@"contentArray" toObject:companies withKeyPath:@"items" options:nil]; + + employeesController = [CPArrayController new]; + [employeesController bind:@"contentArray" toObject:companiesController withKeyPath:@"selection.employees" options:nil]; + + var employeeController = [CPObjectController new]; + [employeeController bind:@"content" toObject:employeesController withKeyPath:@"selection.self" options:nil]; + + [[companiesTable tableColumnWithIdentifier:@"name"] bind:@"value" toObject:companiesController withKeyPath:@"arrangedObjects.name" options:nil]; + [[employeesTable tableColumnWithIdentifier:@"name"] bind:@"value" toObject:employeesController withKeyPath:@"arrangedObjects" options:nil]; + [combo bind:@"contentValues" toObject:employeesController withKeyPath:@"arrangedObjects" options:nil]; + [combo bind:@"value" toObject:employeeController withKeyPath:@"content" options:nil]; + + [companiesController addObserver:self forKeyPath:@"selection" options:0 context:@"companies.selection"]; + [companiesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"companies.selectionIndexes"]; + [companiesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"companies.arrangedObjects"]; + [employeesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"employees.selectionIndexes"]; + [employeesController addObserver:self forKeyPath:@"selection" options:0 context:@"employees.selection"]; + [employeesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"employees.arrangedObjects"]; + [combo addObserver:self forKeyPath:@"value" options:0 context:@"combo.value"]; + + [testWindow setInitialFirstResponder:combo]; + [testWindow makeKeyAndOrderFront:self]; +} + +- (void)makeCheckBoxWithTitle:(CPString)aTitle defaultState:(BOOL)aState +{ + var checkbox = [CPCheckBox checkBoxWithTitle:aTitle]; + [checkbox setFrameOrigin:CGPointMake(55, nextCheckboxY)]; + [checkbox setState:aState]; + [checkbox setTarget:self]; + [checkbox setAction:@selector(setComboState:)]; + [[testWindow contentView] addSubview:checkbox]; + + nextCheckboxY += 25; +} + +- (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(id)context +{ + console.log("\nkeyPath: %s\ncontext: %s\nnew: %s\nold: %s", keyPath, context, [[change valueForKey:CPKeyValueChangeNewKey] description], [[change valueForKey:CPKeyValueChangeOldKey] description]); +} + +- (void)setComboState:(id)sender +{ + var title = [sender title], + state = [sender state] === CPOnState; + + if (title === @"Enabled") + [combo setEnabled:state]; + else if (title === @"Button bordered") + [combo setButtonBordered:state]; + else if (title === @"Bold") + { + var font = [sender state] === CPOnState ? [CPFont boldFontWithName:fontName size:12] : [CPFont fontWithName:fontName size:12]; + [combo setFont:font]; + } + else if (title === @"Completes") + [combo setCompletes:state]; + else if (title === @"Force selection") + [combo setForceSelection:state]; + else if (title === @"Vertical scroller") + [combo setHasVerticalScroller:state]; + else if (title === @"Big item height") + [combo setItemHeight:state ? 47 : 23]; + else if (title === @"More visible items") + [combo setNumberOfVisibleItems:state ? 10 : 5]; +} + +- (void)comboNote:(CPNotification)aNote +{ + console.log([aNote name]); + + var object = [aNote object]; + + if ([aNote name] === CPComboBoxWillDismissNotification) + console.log("Selected: %d - %s", [object indexOfSelectedItem], [object objectValueOfSelectedItem]); +} + +@end diff --git a/Tests/Manual/CPComboBoxTest/Info.plist b/Tests/Manual/CPComboBoxTest/Info.plist new file mode 100644 index 000000000..eee5519b4 --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/Info.plist @@ -0,0 +1,10 @@ + + + + + Main cib file base name + MainMenu.cib + CPBundleName + CPComboBoxTest + + diff --git a/Tests/Manual/CPComboBoxTest/Jakefile b/Tests/Manual/CPComboBoxTest/Jakefile new file mode 100644 index 000000000..a83c45d07 --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/Jakefile @@ -0,0 +1,94 @@ +/* + * Jakefile + * test + * + * Created by aparajita on August 19, 2011. + * Copyright 2011, Victory-Heart Productions All rights reserved. + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"); + +app ("test", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "test.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("test"); + task.setIdentifier("com.aparajita.test"); + task.setVersion("1.0"); + task.setAuthor("Victory-Heart Productions"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("test"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + task.setNib2CibFlags("-R Resources/"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", ["test"], function() +{ + printResults(configuration); +}); + +task ("build", ["default"]); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", "test", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "test", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "test")); + OS.system(["press", "-f", FILE.join("Build", "Release", "test"), FILE.join("Build", "Deployment", "test")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "test")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "test"), FILE.join("Build", "Desktop", "test", "test.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "test", "test.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "test")); + print("----------------------------"); +} diff --git a/Tests/Manual/CPComboBoxTest/Resources/MainMenu.cib b/Tests/Manual/CPComboBoxTest/Resources/MainMenu.cib new file mode 100644 index 000000000..0f50b6fc6 --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/Resources/MainMenu.cib @@ -0,0 +1 @@ +280NPLIST;1.0;D;K;4;$topD;K;18;CPCibObjectDataKeyD;K;6;CP$UIDd;1;2E;E;K;8;$objectsA;S;5;$nullD;K;10;$classnameS;16;_CPCibObjectDataK;8;$classesA;S;16;_CPCibObjectDataS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;1E;K;28;_CPCibObjectDataNamesKeysKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataNamesValuesKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataClassesKeysKeyD;K;6;CP$UIDd;1;0E;K;32;_CPCibObjectDataClassesValuesKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataConnectionsKeyD;K;6;CP$UIDd;1;4E;K;28;_CPCibObjectDataFrameworkKeyD;K;6;CP$UIDd;1;0E;K;26;_CPCibObjectDataNextOidKeyD;K;6;CP$UIDd;1;5E;K;30;_CPCibObjectDataObjectsKeysKeyD;K;6;CP$UIDd;1;6E;K;32;_CPCibObjectDataObjectsValuesKeyD;K;6;CP$UIDd;1;7E;K;26;_CPCibObjectDataOidKeysKeyD;K;6;CP$UIDd;1;8E;K;28;_CPCibObjectDataOidValuesKeyD;K;6;CP$UIDd;1;9E;K;28;_CPCibObjectDataFileOwnerKeyD;K;6;CP$UIDd;2;11E;K;33;_CPCibObjectDataVisibleWindowsKeyD;K;6;CP$UIDd;2;13E;E;D;K;10;$classnameS;7;CPArrayK;8;$classesA;S;7;CPArrayS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;15E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;17E;D;K;6;CP$UIDd;2;18E;D;K;6;CP$UIDd;2;20E;D;K;6;CP$UIDd;2;22E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;24E;D;K;6;CP$UIDd;2;25E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;28E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;30E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;34E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;38E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;40E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;41E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;42E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;44E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;46E;D;K;6;CP$UIDd;1;0E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;34E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;38E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;40E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;41E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;42E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;44E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;46E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;10;$classnameS;18;_CPCibCustomObjectK;8;$classesA;S;18;_CPCibCustomObjectS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;47E;E;D;K;10;$classnameS;5;CPSetK;8;$classesA;S;5;CPSetS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;12E;K;15;CPSetObjectsKeyD;K;6;CP$UIDd;2;48E;E;D;K;10;$classnameS;20;CPCibOutletConnectorK;8;$classesA;S;20;CPCibOutletConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;11E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;49E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;30E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;38E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;50E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;30E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;46E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;51E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;30E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;34E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;52E;E;D;K;10;$classnameS;21;CPCibControlConnectorK;8;$classesA;S;21;CPCibControlConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;19E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;38E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;46E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;53E;E;D;K;10;$classnameS;21;CPCibBindingConnectorK;8;$classesA;S;21;CPCibBindingConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;44E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;54E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;55E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;56E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;58E;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;43E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;59E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;55E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;60E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;61E;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;42E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;62E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;55E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;63E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;64E;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;41E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;65E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;55E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;66E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;67E;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;32E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;68E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;69E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;70E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;71E;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;40E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;30E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;72E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;55E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;73E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;74E;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;38E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;32E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;75E;K;31;CPCibBindingConnectorBindingKeyD;K;6;CP$UIDd;2;76E;K;31;CPCibBindingConnectorKeyPathKeyD;K;6;CP$UIDd;2;77E;K;31;CPCibBindingConnectorOptionsKeyD;K;6;CP$UIDd;2;78E;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;47E;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;79E;E;D;K;10;$classnameS;18;CPObjectControllerK;8;$classesA;S;18;CPObjectControllerS;12;CPControllerS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;31E;K;28;CPObjectControllerContentKeyD;K;6;CP$UIDd;1;0E;K;36;CPObjectControllerObjectClassNameKeyD;K;6;CP$UIDd;2;80E;K;31;CPObjectControllerIsEditableKeyD;K;6;CP$UIDd;2;81E;K;49;CPObjectControllerAutomaticallyPreparesContentKeyD;K;6;CP$UIDd;2;82E;E;D;K;10;$classnameS;20;_CPCibWindowTemplateK;8;$classesA;S;20;_CPCibWindowTemplateS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;33E;K;30;_CPCibWindowTemplateMaxSizeKeyD;K;6;CP$UIDd;2;83E;K;32;_CPCibWindowTemplateViewClassKeyD;K;6;CP$UIDd;1;0E;K;34;_CPCibWindowTemplateWindowClassKeyD;K;6;CP$UIDd;2;84E;K;33;_CPCibWindowTemplateWindowRectKeyD;K;6;CP$UIDd;2;85E;K;30;_CPCibWindowTempatStyleMaskKeyD;K;6;CP$UIDd;2;86E;K;34;_CPCibWindowTemplateWindowTitleKeyD;K;6;CP$UIDd;2;87E;K;33;_CPCibWindowTemplateWindowViewKeyD;K;6;CP$UIDd;2;36E;E;D;K;10;$classnameS;6;CPViewK;8;$classesA;S;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;35E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;89E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;89E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;90E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;1;0E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;91E;E;D;K;10;$classnameS;10;CPComboBoxK;8;$classesA;S;10;CPComboBoxS;11;CPTextFieldS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;37E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;92E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;93E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;2;95E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;96E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;99E;K;11;$aalignmentD;K;6;CP$UIDd;3;100E;K;35;CPControlSendsActionOnEndEditingKeyD;K;6;CP$UIDd;2;81E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;101E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;102E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;2;81E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;2;81E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;2;81E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;3;104E;K;27;CPTextFieldLineBreakModeKeyD;K;6;CP$UIDd;2;99E;K;23;CPTextFieldAlignmentKeyD;K;6;CP$UIDd;3;100E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;1;0E;K;18;CPComboBoxItemsKeyD;K;6;CP$UIDd;3;105E;K;17;CPComboBoxListKeyD;K;6;CP$UIDd;1;0E;K;21;CPComboBoxDelegateKeyD;K;6;CP$UIDd;1;0E;K;23;CPComboBoxDataSourceKeyD;K;6;CP$UIDd;1;0E;K;27;CPComboBoxUsesDataSourceKeyD;K;6;CP$UIDd;2;82E;K;22;CPComboBoxCompletesKeyD;K;6;CP$UIDd;2;81E;K;33;CPComboBoxNumberOfVisibleItemsKeyD;K;6;CP$UIDd;3;106E;K;32;CPComboBoxHasVerticalScrollerKeyD;K;6;CP$UIDd;2;81E;K;27;CPComboBoxButtonBorderedKeyD;K;6;CP$UIDd;2;81E;E;D;K;10;$classnameS;10;CPCheckBoxK;8;$classesA;S;10;CPCheckBoxS;8;CPButtonS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;39E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;107E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;108E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;3;109E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;110E;K;16;$aimage-positionD;K;6;CP$UIDd;2;99E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;88E;K;11;$aalignmentD;K;6;CP$UIDd;2;88E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;111E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;100E;K;16;CPButtonTitleKeyD;K;6;CP$UIDd;3;112E;K;25;CPButtonAlternateTitleKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonAllowsMixedStateKeyD;K;6;CP$UIDd;2;82E;K;23;CPButtonHighlightsByKeyD;K;6;CP$UIDd;3;111E;K;23;CPButtonShowsStateByKeyD;K;6;CP$UIDd;3;111E;K;32;CPButtonImageDimsWhenDisabledKeyD;K;6;CP$UIDd;2;81E;K;24;CPButtonImagePositionKeyD;K;6;CP$UIDd;2;99E;K;28;CPButtonKeyEquivalentMaskKeyD;K;6;CP$UIDd;2;88E;K;24;CPButtonPeriodicDelayKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonPeriodicIntervalKeyD;K;6;CP$UIDd;1;0E;E;D;K;6;$classD;K;6;CP$UIDd;2;39E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;113E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;114E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;3;109E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;110E;K;16;$aimage-positionD;K;6;CP$UIDd;2;99E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;88E;K;11;$aalignmentD;K;6;CP$UIDd;2;88E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;111E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;100E;K;16;CPButtonTitleKeyD;K;6;CP$UIDd;3;115E;K;25;CPButtonAlternateTitleKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonAllowsMixedStateKeyD;K;6;CP$UIDd;2;82E;K;23;CPButtonHighlightsByKeyD;K;6;CP$UIDd;3;111E;K;23;CPButtonShowsStateByKeyD;K;6;CP$UIDd;3;111E;K;32;CPButtonImageDimsWhenDisabledKeyD;K;6;CP$UIDd;2;81E;K;24;CPButtonImagePositionKeyD;K;6;CP$UIDd;2;99E;K;28;CPButtonKeyEquivalentMaskKeyD;K;6;CP$UIDd;2;88E;K;24;CPButtonPeriodicDelayKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonPeriodicIntervalKeyD;K;6;CP$UIDd;1;0E;E;D;K;6;$classD;K;6;CP$UIDd;2;39E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;116E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;117E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;3;109E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;91E;K;16;$aimage-positionD;K;6;CP$UIDd;2;99E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;88E;K;11;$aalignmentD;K;6;CP$UIDd;2;88E;K;17;CPControlValueKeyD;K;6;CP$UIDd;2;88E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;100E;K;16;CPButtonTitleKeyD;K;6;CP$UIDd;3;118E;K;25;CPButtonAlternateTitleKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonAllowsMixedStateKeyD;K;6;CP$UIDd;2;82E;K;23;CPButtonHighlightsByKeyD;K;6;CP$UIDd;3;111E;K;23;CPButtonShowsStateByKeyD;K;6;CP$UIDd;3;111E;K;32;CPButtonImageDimsWhenDisabledKeyD;K;6;CP$UIDd;2;81E;K;24;CPButtonImagePositionKeyD;K;6;CP$UIDd;2;99E;K;28;CPButtonKeyEquivalentMaskKeyD;K;6;CP$UIDd;2;88E;K;24;CPButtonPeriodicDelayKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonPeriodicIntervalKeyD;K;6;CP$UIDd;1;0E;E;D;K;6;$classD;K;6;CP$UIDd;2;39E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;119E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;120E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;3;109E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;110E;K;16;$aimage-positionD;K;6;CP$UIDd;2;99E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;88E;K;11;$aalignmentD;K;6;CP$UIDd;2;88E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;111E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;100E;K;16;CPButtonTitleKeyD;K;6;CP$UIDd;3;121E;K;25;CPButtonAlternateTitleKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonAllowsMixedStateKeyD;K;6;CP$UIDd;2;82E;K;23;CPButtonHighlightsByKeyD;K;6;CP$UIDd;3;111E;K;23;CPButtonShowsStateByKeyD;K;6;CP$UIDd;3;111E;K;32;CPButtonImageDimsWhenDisabledKeyD;K;6;CP$UIDd;2;81E;K;24;CPButtonImagePositionKeyD;K;6;CP$UIDd;2;99E;K;28;CPButtonKeyEquivalentMaskKeyD;K;6;CP$UIDd;2;88E;K;24;CPButtonPeriodicDelayKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonPeriodicIntervalKeyD;K;6;CP$UIDd;1;0E;E;D;K;6;$classD;K;6;CP$UIDd;2;39E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;122E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;3;109E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;110E;K;16;$aimage-positionD;K;6;CP$UIDd;2;99E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;88E;K;11;$aalignmentD;K;6;CP$UIDd;2;88E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;111E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;100E;K;16;CPButtonTitleKeyD;K;6;CP$UIDd;3;124E;K;25;CPButtonAlternateTitleKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonAllowsMixedStateKeyD;K;6;CP$UIDd;2;82E;K;23;CPButtonHighlightsByKeyD;K;6;CP$UIDd;3;111E;K;23;CPButtonShowsStateByKeyD;K;6;CP$UIDd;3;111E;K;32;CPButtonImageDimsWhenDisabledKeyD;K;6;CP$UIDd;2;81E;K;24;CPButtonImagePositionKeyD;K;6;CP$UIDd;2;99E;K;28;CPButtonKeyEquivalentMaskKeyD;K;6;CP$UIDd;2;88E;K;24;CPButtonPeriodicDelayKeyD;K;6;CP$UIDd;1;0E;K;27;CPButtonPeriodicIntervalKeyD;K;6;CP$UIDd;1;0E;E;D;K;10;$classnameS;11;CPTextFieldK;8;$classesA;S;11;CPTextFieldS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;45E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;36E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;88E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;126E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;36E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;94E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;3;127E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;128E;K;6;$afontD;K;6;CP$UIDd;2;98E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;99E;K;11;$aalignmentD;K;6;CP$UIDd;3;100E;K;35;CPControlSendsActionOnEndEditingKeyD;K;6;CP$UIDd;2;81E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;101E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;102E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;2;81E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;2;81E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;2;81E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;3;104E;K;27;CPTextFieldLineBreakModeKeyD;K;6;CP$UIDd;2;99E;K;23;CPTextFieldAlignmentKeyD;K;6;CP$UIDd;3;100E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;101E;E;S;13;CPApplicationD;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;34E;E;E;S;8;delegateS;8;cibComboS;11;comboTargetS;9;theWindowS;20;takeStringValueFrom:S;30;value: cibCombo.buttonBorderedS;5;valueS;23;cibCombo.buttonBorderedD;K;10;$classnameS;12;CPDictionaryK;8;$classesA;S;12;CPDictionaryS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;35;value: cibCombo.hasVerticalScrollerS;28;cibCombo.hasVerticalScrollerD;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;30;value: cibCombo.forceSelectionS;23;cibCombo.forceSelectionD;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;25;value: cibCombo.completesS;18;cibCombo.completesD;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;20;contentObject: comboS;13;contentObjectS;5;comboD;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;23;value: cibCombo.enabledS;16;cibCombo.enabledD;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;18;enabled: selectionS;7;enabledS;9;selectionD;K;6;$classD;K;6;CP$UIDd;2;57E;K;10;CP.objectsD;E;E;S;13;AppControllerS;10;CPComboBoxT;F;S;32;{10000000000000, 10000000000000}S;8;CPWindowS;23;{{583, 51}, {394, 166}}d;2;15S;21;CPComboBox (from cib)d;1;0S;20;{{0, 0}, {394, 166}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;38E;D;K;6;CP$UIDd;2;40E;D;K;6;CP$UIDd;2;44E;D;K;6;CP$UIDd;2;41E;D;K;6;CP$UIDd;2;42E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;2;46E;E;E;S;6;normalS;21;{{14, 25}, {199, 29}}S;19;{{0, 0}, {199, 29}}d;2;36S;8;comboboxS;35;bezeled+placeholder+button-borderedD;K;10;$classnameS;6;CPFontK;8;$classesA;S;6;CPFontS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;97E;K;13;CPFontNameKeyD;K;6;CP$UIDd;3;129E;K;13;CPFontSizeKeyD;K;6;CP$UIDd;3;130E;K;15;CPFontIsBoldKeyD;K;6;CP$UIDd;2;82E;K;17;CPFontIsItalicKeyD;K;6;CP$UIDd;2;82E;E;d;1;2d;1;4S;0;d;4;3072D;K;10;$classnameS;7;CPColorK;8;$classesA;S;7;CPColorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;103E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;131E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;132E;D;K;6;CP$UIDd;3;133E;D;K;6;CP$UIDd;3;134E;D;K;6;CP$UIDd;3;135E;D;K;6;CP$UIDd;3;136E;D;K;6;CP$UIDd;3;137E;D;K;6;CP$UIDd;3;138E;D;K;6;CP$UIDd;3;139E;D;K;6;CP$UIDd;3;140E;E;E;d;1;7S;21;{{247, 30}, {72, 18}}S;18;{{0, 0}, {72, 18}}S;9;check-boxS;8;selectedd;1;1S;7;EnabledS;21;{{247, 80}, {89, 18}}S;18;{{0, 0}, {89, 18}}S;9;CompletesS;23;{{247, 105}, {117, 18}}S;19;{{0, 0}, {117, 18}}S;15;Force selectionS;23;{{247, 130}, {129, 18}}S;19;{{0, 0}, {129, 18}}S;18;Vertical scrollbarS;22;{{247, 55}, {125, 18}}S;19;{{0, 0}, {125, 18}}S;15;Button borderedS;21;{{14, 74}, {196, 29}}S;19;{{0, 0}, {196, 29}}S;9;textfieldS;19;bezeled+placeholderS;17;Arial, sans-serifd;2;12D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;111E;D;K;6;CP$UIDd;3;111E;D;K;6;CP$UIDd;3;111E;D;K;6;CP$UIDd;3;111E;E;E;S;3;MoeS;5;LarryS;6;CheeseS;5;CurlyS;5;ShempS;7;GrouchoS;5;HarpoS;5;ChicoS;5;ZeppoE;K;9;$archiverS;15;CPKeyedArchiverK;8;$versionS;6;100000E; \ No newline at end of file diff --git a/Tests/Manual/CPComboBoxTest/Resources/MainMenu.xib b/Tests/Manual/CPComboBoxTest/Resources/MainMenu.xib new file mode 100644 index 000000000..265203b64 --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/Resources/MainMenu.xib @@ -0,0 +1,828 @@ + + + + 1050 + 11E53 + 2182 + 1138.47 + 569.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2182 + + + YES + NSView + NSComboBox + NSWindowTemplate + NSObjectController + NSTextField + NSTextFieldCell + NSButtonCell + NSComboBoxCell + NSButton + NSCustomObject + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + AppController + + + NSComboBox + YES + + + + 15 + 2 + {{583, 961}, {394, 166}} + 544735232 + CPComboBox (from cib) + NSWindow + + + + + 256 + + YES + + + 268 + {{20, 115}, {191, 26}} + + + + YES + + 343014976 + 272630784 + + + LucidaGrande + 13 + 1044 + + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + controlTextColor + + 3 + MAA + + + 7 + YES + YES + + YES + Moe + Larry + Cheese + Curly + Shemp + Groucho + Harpo + Chico + Zeppo + + + + + 274 + {13, 189} + + + YES + + YES + + 10 + 10 + 1000 + + 75628032 + 0 + + + LucidaGrande + 12 + 16 + + + 3 + MC4zMzMzMzI5ODU2AA + + + + + 338820672 + 268436480 + + + YES + + 6 + System + controlBackgroundColor + + 3 + MC42NjY2NjY2NjY3AA + + + + + 3 + YES + + + + 3 + 2 + + + 6 + System + gridColor + + 3 + MC41AA + + + 19 + tableViewAction: + -765427712 + + + + 1 + 15 + 0 + YES + 0 + 1 + + + + + + 268 + {{247, 118}, {72, 18}} + + + + YES + + -2080244224 + 0 + Enabled + + + 1211912703 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + + + + 268 + {{247, 93}, {125, 18}} + + + + YES + + -2080244224 + 0 + Button bordered + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{247, 68}, {89, 18}} + + + + YES + + -2080244224 + 0 + Completes + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{247, 43}, {117, 18}} + + + + YES + + 67239424 + 0 + Force selection + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{247, 18}, {129, 18}} + + + + YES + + -2080244224 + 0 + Vertical scrollbar + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{20, 66}, {188, 22}} + + + + YES + + -1804468671 + 272630784 + + + + + YES + + + 6 + System + textColor + + + + + + {394, 166} + + + + + {{0, 0}, {1920, 1178}} + {10000000000000, 10000000000000} + YES + + + + + YES + + + delegate + + + + 451 + + + + theWindow + + + + 464 + + + + cibCombo + + + + 529 + + + + comboTarget + + + + 577 + + + + enabled: selection + + + + + + enabled: selection + enabled + selection + 2 + + + 522 + + + + value: cibCombo.enabled + + + + + + value: cibCombo.enabled + value + cibCombo.enabled + 2 + + + 531 + + + + contentObject: combo + + + + + + contentObject: combo + contentObject + combo + 2 + + + 521 + + + + value: cibCombo.completes + + + + + + value: cibCombo.completes + value + cibCombo.completes + 2 + + + 535 + + + + value: cibCombo.forceSelection + + + + + + value: cibCombo.forceSelection + value + cibCombo.forceSelection + 2 + + + 548 + + + + value: cibCombo.hasVerticalScroller + + + + + + value: cibCombo.hasVerticalScroller + value + cibCombo.hasVerticalScroller + 2 + + + 546 + + + + value: cibCombo.buttonBordered + + + + + + value: cibCombo.buttonBordered + value + cibCombo.buttonBordered + 2 + + + 552 + + + + takeStringValueFrom: + + + + 578 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 450 + + + + + 460 + + + YES + + + + + + 461 + + + YES + + + + + + + + + + + + 462 + + + YES + + + + + + 463 + + + + + 515 + + + YES + + + + + + 516 + + + + + 517 + + + + + 532 + + + YES + + + + + + 533 + + + + + 536 + + + YES + + + + + + 537 + + + + + 539 + + + YES + + + + + + 540 + + + + + 549 + + + YES + + + + + + 550 + + + + + 573 + + + YES + + + + + + 574 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 450.IBPluginDependency + 460.IBPluginDependency + 460.IBWindowTemplateEditedContentRect + 460.NSWindowTemplate.visibleAtLaunch + 461.IBPluginDependency + 462.IBPluginDependency + 463.IBComboBoxObjectValuesKey.objectValues + 463.IBPluginDependency + 515.IBPluginDependency + 516.IBPluginDependency + 517.IBPluginDependency + 532.IBPluginDependency + 533.IBPluginDependency + 536.IBPluginDependency + 537.IBPluginDependency + 539.IBPluginDependency + 540.IBPluginDependency + 549.IBPluginDependency + 550.IBPluginDependency + 573.IBPluginDependency + 574.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{357, 418}, {480, 270}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + YES + Moe + Larry + Cheese + Curly + Shemp + Groucho + Harpo + Chico + Zeppo + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + + + + + YES + + + + + 578 + + + + YES + + AppController + NSObject + + YES + + YES + cibCombo + comboTarget + theWindow + + + YES + NSComboBox + NSTextField + NSWindow + + + + YES + + YES + cibCombo + comboTarget + theWindow + + + YES + + cibCombo + NSComboBox + + + comboTarget + NSTextField + + + theWindow + NSWindow + + + + + IBProjectSource + ./Classes/AppController.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + NSSwitch + {15, 15} + + + diff --git a/Tests/Manual/CPComboBoxTest/Resources/spinner.gif b/Tests/Manual/CPComboBoxTest/Resources/spinner.gif new file mode 100644 index 000000000..06dbc2bc2 Binary files /dev/null and b/Tests/Manual/CPComboBoxTest/Resources/spinner.gif differ diff --git a/Tests/Manual/CPComboBoxTest/index-debug.html b/Tests/Manual/CPComboBoxTest/index-debug.html new file mode 100644 index 000000000..052bcacac --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/index-debug.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + CPComboBoxTest + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + diff --git a/Tests/Manual/CPComboBoxTest/index.html b/Tests/Manual/CPComboBoxTest/index.html new file mode 100644 index 000000000..bf35f355e --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/index.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + CPComboBoxTest + + + + + + + + + + + + +
+
+ + + +
+
+ + + + diff --git a/Tests/Manual/CPComboBoxTest/main.j b/Tests/Manual/CPComboBoxTest/main.j new file mode 100644 index 000000000..e2cfd7a56 --- /dev/null +++ b/Tests/Manual/CPComboBoxTest/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * test + * + * Created by aparajita on August 19, 2011. + * Copyright 2011, Victory-Heart Productions All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} diff --git a/Tests/Manual/NewTextFieldBezel/AppController.j b/Tests/Manual/NewTextFieldBezel/AppController.j new file mode 100644 index 000000000..fb92db10c --- /dev/null +++ b/Tests/Manual/NewTextFieldBezel/AppController.j @@ -0,0 +1,121 @@ +/* + * AppController.j + * NewTextField + * + * Created by Aparajita Fishman. + * Copyright (c) 2011, Intalio, 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 + + +@implementation AppController : CPObject +{ + CPWindow theWindow; + CPMutableArray fields; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask]; + fields = [CPMutableArray array]; + + var field = [CPTextField textFieldWithStringValue:@"" placeholder:@"Text field" width:200]; + [fields addObject:field]; + [self configureField:field at:50]; + + field = [CPTextField roundedTextFieldWithStringValue:@"" placeholder:@"Text field" width:200]; + [fields addObject:field]; + [self configureField:field at:100]; + + field = [CPTextField textFieldWithStringValue:@"" placeholder:@"Big text field" width:200]; + [field setFont:[CPFont systemFontOfSize:16]]; + [fields addObject:field]; + [self configureField:field at:150]; + + field = [[CPSearchField alloc] initWithFrame:CPMakeRect(0, 0, 200, 30)]; + [fields addObject:field]; + [self configureField:field at:200]; + + field = [[CPTokenField alloc] initWithFrame:CPMakeRect(0, 0, 200, 30)]; + [field setEditable:YES]; + [field setPlaceholderString:"Type in a token!"]; + [field setTokenizingCharacterSet:[CPCharacterSet characterSetWithCharactersInString:@" "]]; + [fields addObject:field]; + [self configureField:field at:250]; + + [self makeTableAt:300]; + [theWindow orderFront:self]; +} + +- (void)configureField:(CPTextField)aField at:(int)yCoord +{ + var contentView = [theWindow contentView]; + + [aField setFrameOrigin:CGPointMake(50, yCoord)]; + [aField sizeToFit]; + [contentView addSubview:aField]; + + var enabler = [CPCheckBox checkBoxWithTitle:@"Enabled"]; + + [enabler setFrameOrigin:CGPointMake(50 + 200 + 10, yCoord + 7)]; + [enabler setTarget:self]; + [enabler setAction:@selector(enableField:)]; + [enabler setState:CPOnState]; + [enabler setTag:[fields count] - 1]; + [contentView addSubview:enabler]; +} + +- (void)makeTableAt:(int)yCoord +{ + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(50, yCoord, 200, 200)], + table = [[CPTableView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)], + column = [[CPTableColumn alloc] initWithIdentifier:@"1"]; + + [scrollView setBorderType:CPBezelBorder]; + [table setDataSource:self]; + [table setVerticalMotionCanBeginDrag:NO]; + [column setResizingMask:CPTableColumnAutoresizingMask]; + [column setEditable:YES]; + [table setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle]; + [table addTableColumn:column]; + [[theWindow contentView] addSubview:scrollView]; + [scrollView setDocumentView:table]; +} + +- (void)enableField:(id)sender +{ + var field = [fields objectAtIndex:[sender tag]]; + [field setEnabled:[sender state] === CPOnState]; +} + +- (int)numberOfRowsInTableView:(CPTableView)aTableView +{ + return 7; +} + +- (id)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)column row:(int)row +{ + return "Double-click to edit"; +} + +- (void)tableView:(CPTableView)aTableView setObjectValue:(id)anObject forTableColumn:(CPTableColumn)aTableColumn row:(int)rowIndex +{ + +} + +@end diff --git a/Tests/Manual/NewTextFieldBezel/Info.plist b/Tests/Manual/NewTextFieldBezel/Info.plist new file mode 100644 index 000000000..19de2f81c --- /dev/null +++ b/Tests/Manual/NewTextFieldBezel/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + NewTextField + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/NewTextFieldBezel/Jakefile b/Tests/Manual/NewTextFieldBezel/Jakefile new file mode 100644 index 000000000..684e5113f --- /dev/null +++ b/Tests/Manual/NewTextFieldBezel/Jakefile @@ -0,0 +1,93 @@ +/* + * Jakefile + * NewTextField + * + * Created by aparajita on August 10, 2011. + * Copyright 2011, Victory-Heart Productions All rights reserved. + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"); + +app ("NewTextField", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "NewTextField.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("NewTextField"); + task.setIdentifier("com.aparajita.NewTextField"); + task.setVersion("1.0"); + task.setAuthor("Victory-Heart Productions"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("NewTextField"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", ["NewTextField"], function() +{ + printResults(configuration); +}); + +task ("build", ["default"]); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", "NewTextField", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "NewTextField", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "NewTextField")); + OS.system(["press", "-f", FILE.join("Build", "Release", "NewTextField"), FILE.join("Build", "Deployment", "NewTextField")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "NewTextField")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "NewTextField"), FILE.join("Build", "Desktop", "NewTextField", "NewTextField.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "NewTextField", "NewTextField.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "NewTextField")); + print("----------------------------"); +} diff --git a/Tests/Manual/NewTextFieldBezel/Resources/spinner.gif b/Tests/Manual/NewTextFieldBezel/Resources/spinner.gif new file mode 100644 index 000000000..06dbc2bc2 Binary files /dev/null and b/Tests/Manual/NewTextFieldBezel/Resources/spinner.gif differ diff --git a/Tests/Manual/NewTextFieldBezel/index-debug.html b/Tests/Manual/NewTextFieldBezel/index-debug.html new file mode 100644 index 000000000..4c283399a --- /dev/null +++ b/Tests/Manual/NewTextFieldBezel/index-debug.html @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + NewTextField + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + diff --git a/Tests/Manual/NewTextFieldBezel/index.html b/Tests/Manual/NewTextFieldBezel/index.html new file mode 100644 index 000000000..d288174a1 --- /dev/null +++ b/Tests/Manual/NewTextFieldBezel/index.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + NewTextField + + + + + + + + + + + + +
+
+ + + +
+
+ + + + diff --git a/Tests/Manual/NewTextFieldBezel/main.j b/Tests/Manual/NewTextFieldBezel/main.j new file mode 100644 index 000000000..c5515727b --- /dev/null +++ b/Tests/Manual/NewTextFieldBezel/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * NewTextField + * + * Created by aparajita on August 10, 2011. + * Copyright 2011, Victory-Heart Productions All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} diff --git a/Tools/nib2cib/NSAppKit.j b/Tools/nib2cib/NSAppKit.j index f26db332a..227d47fbd 100644 --- a/Tools/nib2cib/NSAppKit.j +++ b/Tools/nib2cib/NSAppKit.j @@ -31,6 +31,7 @@ @import "NSColorWell.j" @import "NSCollectionView.j" @import "NSCollectionViewItem.j" +@import "NSComboBox.j" @import "NSControl.j" @import "NSCustomObject.j" @import "NSCustomResource.j" diff --git a/Tools/nib2cib/NSComboBox.j b/Tools/nib2cib/NSComboBox.j new file mode 100644 index 000000000..16ed5ba49 --- /dev/null +++ b/Tools/nib2cib/NSComboBox.j @@ -0,0 +1,105 @@ +/* + * NSComboBox.j + * nib2cib + * + * Created by Aparajita Fishman. + * Copyright (c) 2012, The Cappuccino Foundation + * + * 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 +@import + +@import "NSTextField.j" + + +@implementation CPComboBox (NSCoding) + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + self = [super NS_initWithCoder:aCoder]; + + if (self) + { + var cell = [aCoder decodeObjectForKey:@"NSCell"]; + + _items = [cell itemList]; + _usesDataSource = [cell usesDataSource]; + _completes = [cell completes]; + _numberOfVisibleItems = [cell visibleItemCount]; + _hasVerticalScroller = [cell hasVerticalScroller]; + [self setButtonBordered:[cell borderedButton]]; + + // Make sure the height is clipped to the max given by the theme + var maxSize = [[[Converter sharedConverter] themes][0] valueForAttributeWithName:@"max-size" forClass:[CPComboBox class]], + size = [self frameSize]; + + [self setFrameSize:CGSizeMake(size.width, MIN(size.height, maxSize.height))]; + } + + return self; +} + +@end + +@implementation NSComboBox : CPComboBox + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPComboBox class]; +} + +@end + +@implementation NSComboBoxCell : NSTextFieldCell +{ + int _visibleItemCount @accessors(readonly, getter=visibleItemCount); + BOOL _hasVerticalScroller @accessors(readonly, getter=hasVerticalScroller); + BOOL _usesDataSource @accessors(readonly, getter=usesDataSource); + BOOL _completes @accessors(readonly, getter=completes); + CPArray _itemList @accessors(readonly, getter=itemList); + BOOL _borderedButton @accessors(readonly, getter=borderedButton); +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + self = [super initWithCoder:aCoder]; + + if (self) + { + _visibleItemCount = [aCoder decodeIntForKey:@"NSVisibleItemCount"]; + _hasVerticalScroller = [aCoder decodeBoolForKey:@"NSHasVerticalScroller"]; + _usesDataSource = [aCoder decodeBoolForKey:@"NSUsesDataSource"]; + _completes = [aCoder decodeBoolForKey:@"NSCompletes"]; + + if (!_usesDataSource) + _itemList = [aCoder decodeObjectForKey:@"NSPopUpListData"] || []; + else + _itemList = []; + + // NSButtonBordered key is present only if the value is NO, go figure + _borderedButton = [aCoder containsValueForKey:@"NSButtonBordered"] ? [aCoder decodeBoolForKey:@"NSButtonBordered"] : YES; + } + + return self; +} + +@end