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 + + + + + + + + + + + + + + + + + + + + + + JavaScript is required for this site to work correctly but is either disabled or not supported by your browser. + Show me how to enable JavaScript + You may want to upgrade to a newer browser while you're at it: + + Safari + Firefox + Chrome + Opera + Internet Explorer + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + JavaScript is required for this site to work correctly but is either disabled or not supported by your browser. + Show me how to enable JavaScript + You may want to upgrade to a newer browser while you're at it: + + Safari + Firefox + Chrome + Opera + Internet Explorer + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + JavaScript is required for this site to work correctly but is either disabled or not supported by your browser. + Show me how to enable JavaScript + You may want to upgrade to a newer browser while you're at it: + + Safari + Firefox + Chrome + Opera + Internet Explorer + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + JavaScript is required for this site to work correctly but is either disabled or not supported by your browser. + Show me how to enable JavaScript + You may want to upgrade to a newer browser while you're at it: + + Safari + Firefox + Chrome + Opera + Internet Explorer + + + + + + + + + + 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
JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.
Show me how to enable JavaScript
You may want to upgrade to a newer browser while you're at it: