NGScopeBar

This commit is contained in:
German Laullon
2010-09-15 17:00:07 -07:00
parent da97c25a6a
commit 84ea632031
14 changed files with 4962 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
//
// AppController.h
// MGScopeBar
//
// Created by Matt Gemmell on 16/03/2008.
//
#import <Cocoa/Cocoa.h>
#import "MGScopeBarDelegateProtocol.h"
@interface AppController : NSObject <MGScopeBarDelegate> {
IBOutlet NSTextField *labelField;
IBOutlet MGScopeBar *scopeBar;
IBOutlet NSView *accessoryView;
NSMutableArray *groups;
}
@property(retain) NSMutableArray *groups;
@end
+220
View File
@@ -0,0 +1,220 @@
//
// AppController.m
// MGScopeBar
//
// Created by Matt Gemmell on 16/03/2008.
//
#import "AppController.h"
#import "MGScopeBar.h"
// Keys for our sample data.
#define GROUP_LABEL @"Label" // string
#define GROUP_SEPARATOR @"HasSeparator" // BOOL as NSNumber
#define GROUP_SELECTION_MODE @"SelectionMode" // MGScopeBarGroupSelectionMode (int) as NSNumber
#define GROUP_ITEMS @"Items" // array of dictionaries, each containing the following keys:
#define ITEM_IDENTIFIER @"Identifier" // string
#define ITEM_NAME @"Name" // string
@implementation AppController
#pragma mark Setup and teardown
- (void)awakeFromNib
{
// In this method we basically just set up some sample data for the scope bar,
// so that we can respond to the MGScopeBarDelegate methods easily.
self.groups = [NSMutableArray arrayWithCapacity:0];
scopeBar.delegate = self;
// Add first group of items.
NSArray *items = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
@"HereItem", ITEM_IDENTIFIER,
@"Here", ITEM_NAME,
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
@"ThereItem", ITEM_IDENTIFIER,
@"There", ITEM_NAME,
nil],
nil];
[self.groups addObject:[NSDictionary dictionaryWithObjectsAndKeys:
@"Search:", GROUP_LABEL,
[NSNumber numberWithBool:NO], GROUP_SEPARATOR,
[NSNumber numberWithInt:MGRadioSelectionMode], GROUP_SELECTION_MODE, // single selection group.
items, GROUP_ITEMS,
nil]];
// Add second group of items.
items = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
@"ContentsItem", ITEM_IDENTIFIER,
@"Contents", ITEM_NAME,
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
@"FileNamesItem", ITEM_IDENTIFIER,
@"File Names", ITEM_NAME,
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
@"MetadataItem", ITEM_IDENTIFIER,
@"Metadata", ITEM_NAME,
nil],
nil];
[self.groups addObject:[NSDictionary dictionaryWithObjectsAndKeys:
// deliberately not specifying a label
[NSNumber numberWithBool:YES], GROUP_SEPARATOR,
[NSNumber numberWithInt:MGMultipleSelectionMode], GROUP_SELECTION_MODE, // multiple selection group.
items, GROUP_ITEMS,
nil]];
// Add third group of items.
items = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
@"AllFilesItem", ITEM_IDENTIFIER,
@"All Files", ITEM_NAME,
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
@"ImagesOnlyItem", ITEM_IDENTIFIER,
@"Images Only", ITEM_NAME,
nil],
nil];
[self.groups addObject:[NSDictionary dictionaryWithObjectsAndKeys:
@"Kind:", GROUP_LABEL,
[NSNumber numberWithBool:YES], GROUP_SEPARATOR,
[NSNumber numberWithInt:MGRadioSelectionMode], GROUP_SELECTION_MODE, // single selection group.
items, GROUP_ITEMS,
nil]];
// Tell the scope bar to ask us for data (since we're the scope-bar's delegate).
[scopeBar reloadData];
// Since our first group is a radio-mode group, the scope bar will automatically select its first item.
// The scope bar will take care of deselecting other items when you select a new item in a radio-mode group.
// We'll also select the first item in our second group, which is a multiple-selection group.
// You can (and must) use this method to programmatically select/deselect items in the bar.
[scopeBar setSelected:YES forItem:@"ContentsItem" inGroup:1]; // remember that group-numbers are zero-based.
// Clear out the label field below the scope bar.
[labelField setStringValue:@""];
}
- (void)dealloc
{
self.groups = nil;
[super dealloc];
}
#pragma mark MGScopeBarDelegate methods
- (int)numberOfGroupsInScopeBar:(MGScopeBar *)theScopeBar
{
return [self.groups count];
}
- (NSArray *)scopeBar:(MGScopeBar *)theScopeBar itemIdentifiersForGroup:(int)groupNumber
{
NSString *res=[[self.groups objectAtIndex:groupNumber] valueForKeyPath:[NSString stringWithFormat:@"%@.%@", GROUP_ITEMS, ITEM_IDENTIFIER]];
NSLog(@"-itemIdentifiersForGroup- %@",res);
return res;
}
- (NSString *)scopeBar:(MGScopeBar *)theScopeBar labelForGroup:(int)groupNumber
{
NSString *res= [[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_LABEL]; // might be nil, which is fine (nil means no label).
NSLog(@"-labelForGroup- %@",res);
return res;
}
- (NSString *)scopeBar:(MGScopeBar *)theScopeBar titleOfItem:(NSString *)identifier inGroup:(int)groupNumber
{
NSArray *items = [[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_ITEMS];
if (items) {
// We'll iterate here, since this is just a demo. This avoids having to keep an NSDictionary of identifiers
// for each group as well as an array for ordering. In a more realistic scenario, you'd probably want to be
// able to look-up an item by its identifier in constant time.
for (NSDictionary *item in items) {
if ([[item objectForKey:ITEM_IDENTIFIER] isEqualToString:identifier]) {
NSLog(@"-titleOfItem- %@=%@",identifier,[item objectForKey:ITEM_NAME]);
return [item objectForKey:ITEM_NAME];
break;
}
}
}
return nil;
}
- (MGScopeBarGroupSelectionMode)scopeBar:(MGScopeBar *)theScopeBar selectionModeForGroup:(int)groupNumber
{
return [[[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_SELECTION_MODE] intValue];
}
- (BOOL)scopeBar:(MGScopeBar *)theScopeBar showSeparatorBeforeGroup:(int)groupNumber
{
// Optional method. If not implemented, all groups except the first will have a separator before them.
return [[[self.groups objectAtIndex:groupNumber] objectForKey:GROUP_SEPARATOR] boolValue];
}
- (NSImage *)scopeBar:(MGScopeBar *)scopeBar imageForItem:(NSString *)identifier inGroup:(int)groupNumber
{
// Optional method. If not implemented (or if you return nil), items will not have an image.
if (groupNumber == 0) {
return [NSImage imageNamed:@"NSComputer"];
} else if (groupNumber == 2) {
if ([identifier isEqualToString:@"AllFilesItem"]) {
return [NSImage imageNamed:@"NSGenericDocument"];
} else if ([identifier isEqualToString:@"ImagesOnlyItem"]) {
return [[NSWorkspace sharedWorkspace] iconForFileType:@"png"];
}
}
return nil;
}
- (NSView *)accessoryViewForScopeBar:(MGScopeBar *)scopeBar
{
// Optional method. If not implemented (or if you return nil), the scope-bar will not have an accessory view.
return accessoryView;
}
- (void)scopeBar:(MGScopeBar *)theScopeBar selectedStateChanged:(BOOL)selected
forItem:(NSString *)identifier inGroup:(int)groupNumber
{
// Display some text showing what just happened.
NSString *displayString = [NSString stringWithFormat:@"\"%@\" %@ in group %d.",
identifier,
(selected) ? @"selected" : @"deselected",
groupNumber];
[labelField setStringValue:displayString];
//NSLog(@"%@", displayString);
}
#pragma mark Accessors and properties
@synthesize groups;
@end
Binary file not shown.
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
+21
View File
@@ -0,0 +1,21 @@
//
// MGRecessedPopUpButtonCell.h
// MGScopeBar
//
// Created by Matt Gemmell on 20/03/2008.
// Copyright 2008 Instinctive Code.
//
#import <Cocoa/Cocoa.h>
/*
This cell class is used only for NSPopUpButtons which do NOT automatically
get their titles from their selected menu-items, since such popup-buttons
are weirdly broken when using the recessed bezel-style.
*/
@interface MGRecessedPopUpButtonCell : NSPopUpButtonCell {
NSButton *recessedButton; // we use a separate NSButton to do the bezel-drawing.
}
@end
+54
View File
@@ -0,0 +1,54 @@
//
// MGRecessedPopUpButtonCell.m
// MGScopeBar
//
// Created by Matt Gemmell on 20/03/2008.
// Copyright 2008 Instinctive Code.
//
#import "MGRecessedPopUpButtonCell.h"
@implementation MGRecessedPopUpButtonCell
- (id)initTextCell:(NSString *)title pullsDown:(BOOL)pullsDown
{
if ((self = [super initTextCell:title pullsDown:pullsDown])) {
recessedButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 30, 20)]; // arbitrary frame.
[recessedButton setTitle:@""];
[recessedButton setBezelStyle:NSRecessedBezelStyle];
[recessedButton setButtonType:NSPushOnPushOffButton];
[[recessedButton cell] setHighlightsBy:NSCellIsBordered | NSCellIsInsetButton];
[recessedButton setShowsBorderOnlyWhileMouseInside:NO];
[recessedButton setState:NSOnState]; // ensures it looks pushed-in.
}
return self;
}
- (void)dealloc
{
[recessedButton release];
[super dealloc];
}
- (void)drawTitleWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
// Inset title rect since its position is broken when NSPopUpButton
// isn't using its selected item as its title.
NSRect titleFrame = cellFrame;
titleFrame.origin.y += 1.0;
[super drawTitleWithFrame:titleFrame inView:controlView];
}
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView
{
[recessedButton setFrame:frame];
[recessedButton drawRect:frame];
}
@end
+51
View File
@@ -0,0 +1,51 @@
//
// MGScopeBar.h
// MGScopeBar
//
// Created by Matt Gemmell on 15/03/2008.
// Copyright 2008 Instinctive Code.
//
#import <Cocoa/Cocoa.h>
#import "MGScopeBarDelegateProtocol.h"
@interface MGScopeBar : NSView {
@private
IBOutlet id <MGScopeBarDelegate, NSObject> delegate; // weak ref.
NSMutableArray *_separatorPositions; // x-coords of separators, indexed by their group-number.
NSMutableArray *_groups; // groups of items.
NSView *_accessoryView; // weak ref since it's a subview.
NSMutableDictionary *_identifiers; // map of identifiers to items.
NSMutableArray *_selectedItems; // all selected items in all groups; see note below.
float _lastWidth; // previous width of view from when we last resized.
NSInteger _firstCollapsedGroup; // index of first group collapsed into a popup.
float _totalGroupsWidthForPopups; // total width needed to show all groups expanded (excluding padding and accessory).
float _totalGroupsWidth; // total width needed to show all groups as native-width popups (excluding padding and accessory).
BOOL _smartResizeEnabled; // whether to do our clever collapsing/expanding of buttons when resizing (Smart Resizing).
}
@property(assign) id delegate; // should implement the MGScopeBarDelegate protocol.
- (void)reloadData; // causes the scope-bar to reload all groups/items from its delegate.
- (void)sizeToFit; // only resizes vertically to optimum height; does not affect width.
- (void)adjustSubviews; // performs Smart Resizing if enabled. You should only need to call this yourself if you change the width of the accessoryView.
// Smart Resize is the intelligent conversion of button-groups into NSPopUpButtons and vice-versa, based on available space.
// This functionality is enabled (YES) by default. Changing this setting will automatically call -reloadData.
- (BOOL)smartResizeEnabled;
- (void)setSmartResizeEnabled:(BOOL)enabled;
// The following method must be used to manage selections in the scope-bar; do not attempt to manipulate buttons etc directly.
- (void)setSelected:(BOOL)selected forItem:(NSString *)identifier inGroup:(int)groupNumber;
- (NSArray *)selectedItems;
/*
Note: The -selectedItems method returns an array of arrays.
Each index in the returned array represents the group of items at that index.
The contents of each sub-array are the identifiers of each selected item in that group.
Sub-arrays may be empty, but will always be present (i.e. you will always find an NSArray).
Depending on the group's selection-mode, sub-arrays may contain zero, one or many identifiers.
The identifiers in each sub-array are not in any particular order.
*/
@end
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
//
// MGScopeBarDelegateProtocol.h
// MGScopeBar
//
// Created by Matt Gemmell on 15/03/2008.
// Copyright 2008 Instinctive Code.
//
#import <Cocoa/Cocoa.h>
// Selection modes for the buttons within a group.
typedef enum _MGScopeBarGroupSelectionMode {
MGRadioSelectionMode = 0, // Exactly one item in the group will be selected at a time (no more, and no less).
MGMultipleSelectionMode = 1 // Any number of items in the group (including none) may be selected at a time.
} MGScopeBarGroupSelectionMode;
@class MGScopeBar;
@protocol MGScopeBarDelegate
// Methods used to configure the scope bar.
// Note: all groupNumber parameters are zero-based.
@required
- (int)numberOfGroupsInScopeBar:(MGScopeBar *)theScopeBar;
- (NSArray *)scopeBar:(MGScopeBar *)theScopeBar itemIdentifiersForGroup:(int)groupNumber;
- (NSString *)scopeBar:(MGScopeBar *)theScopeBar labelForGroup:(int)groupNumber; // return nil or an empty string for no label.
- (MGScopeBarGroupSelectionMode)scopeBar:(MGScopeBar *)theScopeBar selectionModeForGroup:(int)groupNumber;
- (NSString *)scopeBar:(MGScopeBar *)theScopeBar titleOfItem:(NSString *)identifier inGroup:(int)groupNumber;
@optional
// If the following method is not implemented, all groups except the first will have a separator before them.
- (BOOL)scopeBar:(MGScopeBar *)theScopeBar showSeparatorBeforeGroup:(int)groupNumber;
- (NSImage *)scopeBar:(MGScopeBar *)theScopeBar imageForItem:(NSString *)identifier inGroup:(int)groupNumber; // default is no image. Will be shown at 16x16.
- (NSView *)accessoryViewForScopeBar:(MGScopeBar *)theScopeBar; // default is no accessory view.
// Notification methods.
@optional
- (void)scopeBar:(MGScopeBar *)theScopeBar selectedStateChanged:(BOOL)selected forItem:(NSString *)identifier inGroup:(int)groupNumber;
@end
+7
View File
@@ -0,0 +1,7 @@
//
// Prefix header for all source files of the 'MGScopeBar' target in the 'MGScopeBar' project
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
+75
View File
@@ -0,0 +1,75 @@
MGScopeBar
By Matt Legend Gemmell
http://mattgemmell.com/
http://instinctivecode.com/
What is MGScopeBar?
-------------------
MGScopeBar is a control which provides a "scope bar" or "filter bar", much like that found in iTunes, the Finder (in the Find/Spotlight window), and Mail.
What platforms does it support?
-------------------------------
MGScopeBar supports Mac OS X 10.5 (Leopard) or later.
What are the licensing requirements?
------------------------------------
A license documented is included with the source code, but essentially it's a BSD-like license but requiring attribution. You're free to use the code in any kind of project, commercial or otherwise. You're also free to redistribute it, either modified or as-is. Read the license document for more details, and contact me if you have questions.
How do I use it in my project?
------------------------------
Just copy the five files whose names start with "MG" into your project, and you're good to go. You can also use the AppController class as a handy reference, since it provides a demo of how MGScopeBar works.
What can it do?
---------------
MGScopeBar gives you a scope bar control which gets its data from a delegate; it's very like NSTableView and other similar controls, so you should find it easy to use.
You can specify multiple "groups" of buttons, each of which can have:
1. An optional separator before the group.
2. An optional label to the left of the first button (and after the separator).
3. A series of buttons, each of which has a title, unique identifier string, and optional icon.
4. A selection-mode for the group; either radio-mode (only one item selected at a time), or multiple-selection (zero or more items can be selected at a time).
Scope bars also support an optional accessory view, displayed at the right side of the bar.
You can choose whether to use the Smart Resize feature (which is on by default). Smart Resize causes the scope bar to automatically collapse button-groups into popup-menus to better fit the available space.
You should read the MGScopeBarDelegateProtocol.h file to see a list of the delegate methods your delegate object will need to implement.
How do I know which buttons are selected in which groups?
---------------------------------------------------------
There's a delegate method which will be called whenever the user interacts with the scope bar in such a way as to change the selection in any group; see the delegate protocol (and the example code in AppController) for more details.
You can also call the -selectedItems method on MGScopeBar to find out exactly what's selected at any time. See the MGScopeBar.h file for an explanation of the data returned from this method.
Getting in touch
----------------
Whilst I can't provide specific help with integrating MGScopeBar into your application, I always welcome feedback, suggestions and bug reports. Feel free to get in touch with me via my gmail address (matt.gemmell) anytime.
I hope you enjoy using MGScopeBar.
-Matt Legend Gemmell
+104
View File
@@ -0,0 +1,104 @@
{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red51\green51\blue51;\red0\green180\blue128;\red255\green0\blue0;
\red31\green105\blue199;\red119\green119\blue119;}
{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{decimal\}.}{\leveltext\leveltemplateid0\'02\'05.;}{\levelnumbers\'01;}}{\listname ;}\listid1}}
{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
\deftab720
\pard\pardeftab720\ql\qnatural
\f0\b\fs24 \cf2 Matt Gemmell / Instinctive Code Source Code License\
\b0\fs22 Last updated: 19th May 2008
\fs24 \
\
\
Thanks for downloading some of our source code!\
\
This is the license agreement for the source code which this document accompanies (don\'92t worry: you\'92re allowed to use it in your own products, commercial or otherwise).\
\
The full license text is further down this page, and you should only use the source code if you agree to the terms in that text. For convenience, though, we\'92ve put together a human-readable
\b non-authoritative
\b0 interpretation of the license which will hopefully answer any questions you have.\
\
\
\b \cf3 Green
\b0 \cf2 text shows
\b \cf3 what you can do with the code
\b0 \cf2 .\
\b \cf4 Red
\b0 \cf2 text means
\b \cf4 restrictions you must abide by
\b0 \cf2 .\
\
Basically, the license says that:\
\
\pard\tx220\tx720\pardeftab720\li720\fi-720\ql\qnatural
\ls1\ilvl0\cf2 {\listtext 1. }You can
\b \cf3 use the code in your own products, including commercial and/or closed-source products
\b0 \cf2 .\
{\listtext 2. }You can
\b \cf3 modify the code
\b0 \cf0 as you wish\cf2 , and
\b \cf3 use the modified code in your products
\b0 \cf2 .\
{\listtext 3. }You can
\b \cf3 redistribute the original, unmodified code
\b0 \cf2 , but you
\b \cf4 have to include the full license text below
\b0 \cf2 .\
{\listtext 4. }You can
\b \cf3 redistribute the modified code
\b0 \cf2 as you wish (
\b \cf4 without the full license text below
\b0 \cf2 ).\
{\listtext 5. }In all cases, you
\b \cf4 must include a credit mentioning Matt Gemmell
\b0 \cf2 as the original author of the source.\
{\listtext 6. }Matt Gemmell is \cf0 not liable for anything you do with the code\cf2 , no matter what. So be sensible.\
{\listtext 7. }You
\b \cf4 can\'92t use the name Matt Gemmell, the name Instinctive Code, the Instinctive Code logo or any other related marks to promote your products
\b0 \cf2 based on the code.\
{\listtext 8. }If you agree to all of that, go ahead and use the source. Otherwise, don\'92t!\
\pard\pardeftab720\ql\qnatural
\cf2 \
\b \
\
Suggested Attribution Format\
\b0 \
The license requires that you give credit to Matt Gemmell, as the original author of any of our source that you use. The placement and format of the credit is up to you, but we prefer the credit to be in the software\'92s \'93About\'94 window. Alternatively, you could put the credit in a list of acknowledgements within the software, in the software\'92s documentation, or on the web page for the software. The suggested format for the attribution is:\
\
\pard\pardeftab720\ql\qnatural
\b \cf0 Includes <Name of Code> code by {\field{\*\fldinst{HYPERLINK "http://mattgemmell.com/"}}{\fldrslt \cf5 Matt Gemmell}}\cf6 .
\b0 \
\pard\pardeftab720\ql\qnatural
\cf2 \
where <Name of Code> would be replaced by the name of the specific source-code package you made use of. Where possible, please link the text \'93Matt Gemmell\'94 to the following URL, or include the URL as plain text: {\field{\*\fldinst{HYPERLINK "http://mattgemmell.com/"}}{\fldrslt \cf5 http://mattgemmell.com/}}\
\
\
\b Full Source Code License Text\
\
\b0 Below you can find the actual text of the license agreement.
\b \
\
\pard\pardeftab720\ql\qnatural
\cf6 \
License Agreement for Source Code provided by Matt Gemmell
\b0 \
\
This software is supplied to you by Matt Gemmell in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this software.\
\
In consideration of your agreement to abide by the following terms, and subject to these terms, Matt Gemmell grants you a personal, non-exclusive license, to use, reproduce, modify and redistribute the software, with or without modifications, in source and/or binary forms; provided that if you redistribute the software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the software, and that in all cases attribution of Matt Gemmell as the original author of the source code shall be included in all such resulting software products or distributions.\uc0\u8232 \
Neither the name, trademarks, service marks or logos of Matt Gemmell or Instinctive Code may be used to endorse or promote products derived from the software without specific prior written permission from Matt Gemmell. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Matt Gemmell herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the software may be incorporated.\
\
The software is provided by Matt Gemmell on an "AS IS" basis. MATT GEMMELL AND INSTINCTIVE CODE MAKE NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.\
\
IN NO EVENT SHALL MATT GEMMELL OR INSTINCTIVE CODE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF MATT GEMMELL OR INSTINCTIVE CODE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\
}
+9
View File
@@ -0,0 +1,9 @@
MGScopeBar TODO
- Maybe properly support a blue gradient appearance (like Mail in Tiger)?
- Easy to change background gradient, but buttons/popups still highlight gray.
- Accessibility support
- The control needs to indicate which groups it contains, what selection-mode those groups support, etc.
- Otherwise it's just an opaque group of buttons to VoiceOver.