/AppKit/CPTabView.j
http://github.com/cacaodev/cappuccino · Unknown · 571 lines · 462 code · 109 blank · 0 comment · 0 complexity · 58b8c2e866fdeb3e7fa66c513d284f6b MD5 · raw file
- /*
- * CPTabView.j
- * AppKit
- *
- * Created by Derek Hammer.
- * Copyright 2010, Derek Hammer.
- *
- * 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 "CPBox.j"
- @import "CPSegmentedControl.j"
- @import "CPTabViewItem.j"
- @import "CPView.j"
- @typedef CPTabViewType
- CPTopTabsBezelBorder = 0;
- //CPLeftTabsBezelBorder = 1;
- CPBottomTabsBezelBorder = 2;
- //CPRightTabsBezelBorder = 3;
- CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
- CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
- CPNoTabsNoBorder = 6; //Displays no tabs and no border.
- var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
- CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
- CPTabViewWillSelectTabViewItemSelector = 1 << 3,
- CPTabViewDidChangeNumberOfTabViewItemsSelector = 1 << 4;
- @protocol CPTabViewDelegate <CPObject>
- @optional
- - (BOOL)tabView:(CPTabView)tabView shouldSelectTabViewItem:(CPTabViewItem)tabViewItem;
- - (void)tabView:(CPTabView)tabView didSelectTabViewItem:(CPTabViewItem)tabViewItem;
- - (void)tabView:(CPTabView)tabView willSelectTabViewItem:(CPTabViewItem)tabViewItem;
- - (void)tabViewDidChangeNumberOfTabViewItems:(CPTabView)tabView;
- @end
- /*!
- @ingroup appkit
- @class CPTabView
- A CPTabView object presents a tabbed interface where each page is one a
- complete view hiearchy of its own. The user can navigate between various
- pages by clicking on the tab headers.
- */
- @implementation CPTabView : CPView
- {
- CPArray _items;
- CPSegmentedControl _tabs;
- CPBox _box;
- CPNumber _selectedIndex;
- CPTabViewType _type;
- CPFont _font;
- id <CPTabViewDelegate> _delegate;
- unsigned _delegateSelectors;
- }
- - (id)initWithFrame:(CGRect)aFrame
- {
- if (self = [super initWithFrame:aFrame])
- {
- _items = [CPArray array];
- [self _init];
- [self setTabViewType:CPTopTabsBezelBorder];
- }
- return self;
- }
- - (void)_init
- {
- _selectedIndex = CPNotFound;
- _tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
- [_tabs setHitTests:NO];
- var height = [_tabs valueForThemeAttribute:@"min-size"].height;
- [_tabs setFrameSize:CGSizeMake(0, height)];
- _box = [[CPBox alloc] initWithFrame:[self bounds]];
- [self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
- [self addSubview:_box];
- [self addSubview:_tabs];
- }
- // Adding and Removing Tabs
- /*!
- Adds a CPTabViewItem to the tab view.
- @param aTabViewItem the item to add
- */
- - (void)addTabViewItem:(CPTabViewItem)aTabViewItem
- {
- [self insertTabViewItem:aTabViewItem atIndex:[_items count]];
- }
- /*!
- Inserts a CPTabViewItem into the tab view at the specified index.
- @param aTabViewItem the item to insert
- @param anIndex the index for the item
- */
- - (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPUInteger)anIndex
- {
- [_items insertObject:aTabViewItem atIndex:anIndex];
- [self _updateItems];
- [self _repositionTabs];
- [aTabViewItem _setTabView:self];
- if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
- [_delegate tabViewDidChangeNumberOfTabViewItems:self];
- }
- /*!
- Removes the specified tab view item from the tab view.
- @param aTabViewItem the item to remove
- */
- - (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
- {
- var count = [_items count];
- for (var i = 0; i < count; i++)
- {
- if ([_items objectAtIndex:i] === aTabViewItem)
- {
- [_items removeObjectAtIndex:i];
- break;
- }
- }
- [self _updateItems];
- [self _repositionTabs];
- [aTabViewItem _setTabView:nil];
- if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
- [_delegate tabViewDidChangeNumberOfTabViewItems:self];
- }
- // Accessing Tabs
- /*!
- Returns the index of the specified item
- @param aTabViewItem the item to find the index for
- @return the index of aTabViewItem or CPNotFound
- */
- - (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
- {
- return [_items indexOfObjectIdenticalTo:aTabViewItem];
- }
- /*!
- Returns the index of the CPTabViewItem with the specified identifier.
- @param anIdentifier the identifier of the item
- @return the index of the tab view item identified by anIdentifier, or CPNotFound
- */
- - (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
- {
- for (var index = [_items count]; index >= 0; index--)
- if ([[_items[index] identifier] isEqual:anIdentifier])
- return index;
- return CPNotFound;
- }
- /*!
- Returns the number of items in the tab view.
- @return the number of tab view items in the receiver
- */
- - (unsigned)numberOfTabViewItems
- {
- return [_items count];
- }
- /*!
- Returns the CPTabViewItem at the specified index.
- @return a tab view item, or nil
- */
- - (CPTabViewItem)tabViewItemAtIndex:(CPUInteger)anIndex
- {
- return [_items objectAtIndex:anIndex];
- }
- /*!
- Returns the array of items that backs this tab view.
- @return a copy of the array of items in the receiver
- */
- - (CPArray)tabViewItems
- {
- return [_items copy]; // Copy?
- }
- // Selecting a Tab
- /*!
- Sets the first tab view item in the array to be displayed to the user.
- @param aSender the object making this request
- */
- - (void)selectFirstTabViewItem:(id)aSender
- {
- if ([_items count] === 0)
- return; // throw?
- [self selectTabViewItemAtIndex:0];
- }
- /*!
- Sets the last tab view item in the array to be displayed to the user.
- @param aSender the object making this request
- */
- - (void)selectLastTabViewItem:(id)aSender
- {
- if ([_items count] === 0)
- return; // throw?
- [self selectTabViewItemAtIndex:[_items count] - 1];
- }
- /*!
- Sets the next tab item in the array to be displayed.
- @param aSender the object making this request
- */
- - (void)selectNextTabViewItem:(id)aSender
- {
- if (_selectedIndex === CPNotFound)
- return;
- var nextIndex = _selectedIndex + 1;
- if (nextIndex === [_items count])
- // does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
- return;
- [self selectTabViewItemAtIndex:nextIndex];
- }
- /*!
- Selects the previous item in the array for display.
- @param aSender the object making this request
- */
- - (void)selectPreviousTabViewItem:(id)aSender
- {
- if (_selectedIndex === CPNotFound)
- return;
- var previousIndex = _selectedIndex - 1;
- if (previousIndex < 0)
- return; // does nothing. See above.
- [self selectTabViewItemAtIndex:previousIndex];
- }
- /*!
- Displays the specified item in the tab view.
- @param aTabViewItem the item to display
- */
- - (void)selectTabViewItem:(CPTabViewItem)aTabViewItem
- {
- [self selectTabViewItemAtIndex:[self indexOfTabViewItem:aTabViewItem]];
- }
- /*!
- Selects the item at the specified index.
- @param anIndex the index of the item to display.
- */
- - (BOOL)selectTabViewItemAtIndex:(CPUInteger)anIndex
- {
- if (anIndex === _selectedIndex)
- return;
- var aTabViewItem = [self tabViewItemAtIndex:anIndex];
- if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
- return NO;
- if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
- [_delegate tabView:self willSelectTabViewItem:aTabViewItem];
- [_tabs selectSegmentWithTag:anIndex];
- [self _setSelectedIndex:anIndex];
- if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
- [_delegate tabView:self didSelectTabViewItem:aTabViewItem];
- return YES;
- }
- /*!
- Returns the current item being displayed.
- @return the tab view item currenly being displayed by the receiver
- */
- - (CPTabViewItem)selectedTabViewItem
- {
- if (_selectedIndex != CPNotFound)
- return [_items objectAtIndex:_selectedIndex];
- return nil;
- }
- // Modifying the font
- /*!
- Returns the font for tab label text.
- @return the font for tab label text
- */
- - (CPFont)font
- {
- return _font;
- }
- /*!
- Sets the font for tab label text to font.
- @param font the font the receiver should use for tab label text
- */
- - (void)setFont:(CPFont)font
- {
- if ([_font isEqual:font])
- return;
- _font = font;
- [_tabs setFont:_font];
- }
- //
- /*!
- Sets the tab view type.
- @param aTabViewType the view type
- */
- - (void)setTabViewType:(CPTabViewType)aTabViewType
- {
- if (_type === aTabViewType)
- return;
- _type = aTabViewType;
- if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
- [_tabs removeFromSuperview];
- else
- [self addSubview:_tabs];
- switch (_type)
- {
- case CPTopTabsBezelBorder:
- case CPBottomTabsBezelBorder:
- case CPNoTabsBezelBorder:
- [_box setBorderType:CPBezelBorder];
- break;
- case CPNoTabsLineBorder:
- [_box setBorderType:CPLineBorder];
- break;
- case CPNoTabsNoBorder:
- [_box setBorderType:CPNoBorder];
- break;
- }
- [self setNeedsLayout];
- }
- - (void)layoutSubviews
- {
- // Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
- // This means we can't rely on autoresize masks.
- if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
- {
- [_box setFrame:[self bounds]];
- }
- else
- {
- var aFrame = [self frame],
- segmentedHeight = CGRectGetHeight([_tabs frame]),
- origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
- [_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
- CGRectGetHeight(aFrame) - segmentedHeight / 2)];
- [self _updateItems];
- [self _repositionTabs];
- }
- }
- /*!
- Returns the tab view type.
- @return the tab view type of the receiver
- */
- - (CPTabViewType)tabViewType
- {
- return _type;
- }
- /*!
- Returns the receiver's delegate.
- @return the receiver's delegate
- */
- - (id)delegate
- {
- return _delegate;
- }
- /*!
- Sets the delegate for this tab view.
- @param aDelegate the tab view's delegate
- */
- - (void)setDelegate:(id <CPTabViewDelegate>)aDelegate
- {
- if (_delegate == aDelegate)
- return;
- _delegate = aDelegate;
- _delegateSelectors = 0;
- if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
- _delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector;
- if ([_delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)])
- _delegateSelectors |= CPTabViewWillSelectTabViewItemSelector;
- if ([_delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)])
- _delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
- if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
- _delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
- }
- - (void)setBackgroundColor:(CPColor)aColor
- {
- [_box setBackgroundColor:aColor];
- }
- - (CPColor)backgroundColor
- {
- return [_box backgroundColor];
- }
- - (void)mouseDown:(CPEvent)anEvent
- {
- var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
- if (segmentIndex != CPNotFound && [self selectTabViewItemAtIndex:segmentIndex])
- [_tabs trackSegment:anEvent];
- }
- - (void)_repositionTabs
- {
- var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
- verticalCenterOfTabs = CGRectGetHeight([_tabs bounds]) / 2;
- if (_type === CPBottomTabsBezelBorder)
- [_tabs setCenter:CGPointMake(horizontalCenterOfSelf, CGRectGetHeight([self bounds]) - verticalCenterOfTabs)];
- else
- [_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
- }
- - (void)_setSelectedIndex:(CPNumber)index
- {
- _selectedIndex = index;
- [self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
- }
- - (void)_setContentViewFromItem:(CPTabViewItem)anItem
- {
- [_box setContentView:[anItem view]];
- }
- - (void)_updateItems
- {
- var count = [_items count];
- [_tabs setSegmentCount:count];
- for (var i = 0; i < count; i++)
- {
- [_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
- [_tabs setTag:i forSegment:i];
- }
- if (_selectedIndex === CPNotFound)
- [self selectFirstTabViewItem:self];
- }
- @end
- var CPTabViewItemsKey = "CPTabViewItemsKey",
- CPTabViewSelectedItemKey = "CPTabViewSelectedItemKey",
- CPTabViewTypeKey = "CPTabViewTypeKey",
- CPTabViewFontKey = "CPTabViewFontKey",
- CPTabViewDelegateKey = "CPTabViewDelegateKey";
- @implementation CPTabView (CPCoding)
- - (id)initWithCoder:(CPCoder)aCoder
- {
- if (self = [super initWithCoder:aCoder])
- {
- [self _init];
- _font = [aCoder decodeObjectForKey:CPTabViewFontKey];
- [_tabs setFont:_font];
- _items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
- [_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
- [self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
- self.selectOnAwake = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
- _type = [aCoder decodeIntForKey:CPTabViewTypeKey];
- }
- return self;
- }
- - (void)awakeFromCib
- {
- // This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
- // not safe to call before the views of the tab views items are fully decoded.
- [self _updateItems];
- if (self.selectOnAwake)
- {
- [self selectTabViewItem:self.selectOnAwake];
- delete self.selectOnAwake;
- }
- var type = _type;
- _type = nil;
- [self setTabViewType:type];
- [self setNeedsLayout];
- }
- - (void)encodeWithCoder:(CPCoder)aCoder
- {
- // Don't bother to encode the CPBox. We will recreate it on decode and its content view is already
- // stored by the tab view item. Not encoding _box makes the resulting archive smaller and reduces
- // the surface for decoding bugs (of which we've had many in tab view).
- var subviews = [self subviews];
- [_box removeFromSuperview];
- [super encodeWithCoder:aCoder];
- [self setSubviews:subviews];
- [aCoder encodeObject:_items forKey:CPTabViewItemsKey];
- var selected = [self selectedTabViewItem];
- if (selected)
- [aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
- [aCoder encodeInt:_type forKey:CPTabViewTypeKey];
- [aCoder encodeObject:_font forKey:CPTabViewFontKey];
- [aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
- }
- @end