├── .gitignore ├── ENTabBarView.png ├── ENTabBarView ├── ENTabBarView.h ├── ENTabBarView.m ├── ENTabCell.h ├── ENTabCell.m ├── ENTabImage.h └── ENTabImage.m ├── Readme.md ├── TabBarDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── TabBarDemo.xccheckout │ └── xcuserdata │ │ └── aaron.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── aaron.xcuserdatad │ └── xcschemes │ ├── TabBarDemo.xcscheme │ └── xcschememanagement.plist ├── TabBarDemo ├── ENAppDelegate.h ├── ENAppDelegate.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── TabBarDemo-Info.plist ├── TabBarDemo-Prefix.pch ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ └── MainMenu.xib └── main.m └── TabBarDemoTests ├── TabBarDemoTests-Info.plist ├── TabBarDemoTests.m └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | DerivedData/* -------------------------------------------------------------------------------- /ENTabBarView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orklann/ENTabBarView/f0675e740569d96d425b80affe6d8291d4659cf3/ENTabBarView.png -------------------------------------------------------------------------------- /ENTabBarView/ENTabBarView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ENTabBarView.h 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ENTabCell.h" 11 | #import "ENTabImage.h" 12 | 13 | @protocol ENTabBarViewDelegate; 14 | 15 | @interface ENTabBarView : NSView{ 16 | NSMutableArray *tabs; 17 | NSBezierPath *tabListControlPath; 18 | NSTrackingArea *trackingArea; 19 | NSMenu *menu; 20 | 21 | BOOL isDragging; 22 | NSInteger destinationIndex; 23 | NSInteger sourceIndex; 24 | ENTabCell *draggingTab; 25 | ENTabImage *draggingImage; 26 | } 27 | 28 | 29 | @property (readwrite) NSFont *tabFont; 30 | @property (readwrite) NSMutableArray *tabs; 31 | @property (readwrite) ENTabCell *selectedTab; 32 | @property (readwrite) NSColor *bgColor; 33 | @property (readwrite) NSColor *tabBGColor; 34 | @property (readwrite) NSColor *tabActivedBGColor; 35 | @property (readwrite) NSColor *tabBorderColor; 36 | @property (readwrite) NSColor *tabTitleColor; 37 | @property (readwrite) NSColor *tabActivedTitleColor; 38 | @property (readwrite) NSColor *smallControlColor; 39 | @property (readwrite) id delegate; 40 | 41 | - (id)addTabViewWithTitle:(NSString *)title; 42 | - (void)redraw; 43 | - (void)removeTabCell:(ENTabCell*)tabCell; 44 | @end 45 | 46 | // ENTabBarView Delegates methods 47 | // Implement these methods to intercept your code 48 | // 49 | @protocol ENTabBarViewDelegate 50 | 51 | @optional 52 | 53 | /* ============================================================= 54 | * Usually we store tabs (ENTabCell*) as a key in a NSDitionary, 55 | * And other object as a value to identify which tab in the foll- 56 | * owing events. 57 | * If you need to know the tab index(order), you can get it like 58 | * this: 59 | * NSInteger index = [[tabBarView tabs] indexOfObject:tab]]; 60 | * Here **tabBarView** is an instance of ENTabBarView 61 | * ============================================================*/ 62 | 63 | - (void)tabWillActive:(ENTabCell*)tab; 64 | - (void)tabDidActived:(ENTabCell*)tab; 65 | 66 | - (void)tabWillClose:(ENTabCell*)tab; 67 | - (void)tabDidClosed:(ENTabCell*)tab; 68 | 69 | - (void)tabWillBeCreated:(ENTabCell*)tab; 70 | - (void)tabDidBeCreated:(ENTabCell*)tab; 71 | @end 72 | -------------------------------------------------------------------------------- /ENTabBarView/ENTabBarView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ENTabBarView.m 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import "ENTabBarView.h" 10 | #import "ENTabImage.h" 11 | 12 | #define kTabBarViewHeight 32 13 | #define kWidthOfTabList 24 14 | #define kHeightOfTabList 28 15 | #define kMaxTabCellWidth 180 16 | #define kMinTabCellWidth 100 17 | #define kTabCellHeight 28 18 | 19 | @interface ENTabBarView (Expose) 20 | - (NSRect)tabRectFromIndex:(NSUInteger)index; 21 | - (NSRect)rectForTabListControl; 22 | - (BOOL)isBlankAreaOfTabBarViewInPoint:(NSPoint)p; 23 | - (NSMenu *)tabsMenu; 24 | - (void)popupMenuDidChoosed:(NSMenuItem*)item; 25 | - (BOOL)validateMenuItem:(NSMenuItem*)menuItem; 26 | - (ENTabCell*)tabCellInPoint:(NSPoint)p; 27 | - (NSInteger)destinationCellIndexFromPoint:(NSPoint)p; 28 | - (void)exchangeTabWithIndex:(NSUInteger)One withTabIndex:(NSUInteger)two; 29 | @end 30 | 31 | @implementation ENTabBarView (Expose) 32 | - (NSRect)tabRectFromIndex:(NSUInteger)index{ 33 | NSUInteger totalWidthOfTabBarView = [self frame].size.width - kWidthOfTabList - 12; 34 | NSUInteger averageWidth = (NSUInteger)(totalWidthOfTabBarView / [tabs count]); 35 | 36 | averageWidth = averageWidth <= kMinTabCellWidth ? kMinTabCellWidth : averageWidth; 37 | averageWidth = averageWidth >= kMaxTabCellWidth ? kMaxTabCellWidth : averageWidth; 38 | 39 | CGFloat x = kWidthOfTabList + (index * averageWidth); 40 | CGFloat y = 0; 41 | CGFloat width = averageWidth; 42 | CGFloat height = kTabCellHeight; 43 | 44 | NSRect rect = NSMakeRect(x, y, width, height); 45 | return rect; 46 | } 47 | 48 | // Rect for left most tab list control 49 | - (NSRect)rectForTabListControl{ 50 | NSRect rect = NSMakeRect(0, 0, kWidthOfTabList, kHeightOfTabList); 51 | rect = CGRectInset(rect, 6, 10); 52 | return rect; 53 | } 54 | 55 | - (BOOL)isBlankAreaOfTabBarViewInPoint:(NSPoint)p{ 56 | // Check tab list control 57 | NSRect rect = [self rectForTabListControl]; 58 | if (NSPointInRect(p, rect)) { 59 | return NO; 60 | } 61 | 62 | // Check all tabs path 63 | NSUInteger index = 0; 64 | for (index = 0; index < [tabs count]; ++index){ 65 | ENTabCell *tab = [tabs objectAtIndex:index]; 66 | NSBezierPath *path = [tab path]; 67 | if ([path containsPoint:p]) { 68 | return NO; 69 | } 70 | } 71 | 72 | // Else return YES 73 | return YES; 74 | } 75 | 76 | - (NSMenu *)tabsMenu { 77 | menu = [[NSMenu alloc] initWithTitle:@"Contextual Menu"]; 78 | NSUInteger index = 0; 79 | for (index = 0; index < [tabs count]; index++) { 80 | ENTabCell *tab = [tabs objectAtIndex:index]; 81 | [menu insertItemWithTitle:[tab title] action:@selector(popupMenuDidChoosed:) keyEquivalent:@"" atIndex:index]; 82 | } 83 | return menu; 84 | } 85 | 86 | - (void)popupMenuDidChoosed:(NSMenuItem*)item{ 87 | NSUInteger index = [menu indexOfItem:item]; 88 | if (index != -1) { 89 | ENTabCell *tab = [tabs objectAtIndex:index]; 90 | NSRect tabRect = [tab frame]; 91 | NSRect tabBarViewRect = [self bounds]; 92 | // If the selected tab is not fully shown in the tabbar view, we 93 | // then exchange it with first(0 index) tab, and then active it. 94 | if (!CGRectContainsRect(tabBarViewRect, tabRect)) { 95 | NSUInteger tabIndex = [[self tabs] indexOfObject:tab]; 96 | [self exchangeTabWithIndex:tabIndex withTab:0]; 97 | } 98 | [tab setAsActiveTab]; 99 | } 100 | } 101 | 102 | - (BOOL)validateMenuItem:(NSMenuItem*)menuItem{ 103 | return YES; 104 | } 105 | 106 | - (ENTabCell*)tabCellInPoint:(NSPoint)p{ 107 | NSUInteger index = 0; 108 | for (index = 0; index < [tabs count]; index++) { 109 | ENTabCell *tab = [tabs objectAtIndex:index]; 110 | NSRect rect = [tab frame]; 111 | if (NSPointInRect(p, rect)) { 112 | return tab; 113 | } 114 | } 115 | return nil; 116 | } 117 | 118 | - (NSInteger)destinationCellIndexFromPoint:(NSPoint)p{ 119 | NSInteger index = 0; 120 | for (index = 0; index < [tabs count]; index++) { 121 | ENTabCell *tab = [tabs objectAtIndex:index]; 122 | NSRect rect = [tab frame]; 123 | CGFloat midX = NSMidX(rect); 124 | CGFloat minX = NSMinX(rect); 125 | CGFloat maxX = NSMaxX(rect); 126 | CGFloat minY = NSMinY(rect); 127 | CGFloat w = rect.size.width; 128 | CGFloat h = rect.size.height; 129 | 130 | NSInteger ret; 131 | NSRect firstRect = NSMakeRect(minX, minY, w/2, h); 132 | NSRect secondeRect = NSMakeRect(midX, minY, w/2, h); 133 | if (NSPointInRect(p, firstRect)) { 134 | ret = index; // don't substract by 1, fix bugs 135 | ret = ret >= 0 ? ret : 0; 136 | if(destinationIndex != -1 && index == destinationIndex){ 137 | return destinationIndex; 138 | } 139 | return ret; 140 | }else if(NSPointInRect(p, secondeRect)){ 141 | ret = index + 1; 142 | if (destinationIndex != -1 && index > destinationIndex) { 143 | ret -= 1; 144 | }else if(destinationIndex != -1 && index == destinationIndex){ 145 | return destinationIndex; 146 | } 147 | return ret; 148 | } 149 | 150 | if (p.x > maxX && index == [tabs count] - 1) { 151 | if (destinationIndex != -1 && index >= destinationIndex) { 152 | return index; 153 | }else{ 154 | return index + 1; 155 | } 156 | } 157 | } 158 | return -1; 159 | } 160 | 161 | - (void)exchangeTabWithIndex:(NSUInteger)one withTab:(NSUInteger)two{ 162 | NSMutableArray *allTabs = [self tabs]; 163 | [allTabs exchangeObjectAtIndex:one withObjectAtIndex:two]; 164 | } 165 | @end 166 | 167 | @implementation ENTabBarView 168 | 169 | @synthesize tabs; 170 | @synthesize bgColor; 171 | @synthesize tabBGColor; 172 | @synthesize tabActivedBGColor; 173 | @synthesize tabBorderColor; 174 | @synthesize tabTitleColor; 175 | @synthesize tabActivedTitleColor; 176 | @synthesize smallControlColor; 177 | @synthesize tabFont; 178 | @synthesize delegate; 179 | 180 | #pragma mark - - - - - - - - - 181 | - (id)initWithFrame:(NSRect)frame 182 | { 183 | self = [super initWithFrame:frame]; 184 | if (self) { 185 | tabs = [NSMutableArray array]; 186 | 187 | // Give all colors a default value if none given 188 | bgColor = [NSColor colorWithCalibratedRed: 0.6 green:0.6 blue:0.6 alpha:1.0]; 189 | tabBGColor = [NSColor colorWithCalibratedRed: 0.68 green: 0.68 blue: 0.68 alpha:1.0]; 190 | tabActivedBGColor = [NSColor whiteColor]; 191 | tabBorderColor = [NSColor colorWithCalibratedRed: 0.53 green:0.53 blue:0.53 alpha:1.0]; 192 | tabTitleColor = [NSColor blackColor]; 193 | tabActivedTitleColor = [NSColor blackColor]; 194 | smallControlColor = [NSColor colorWithCalibratedRed:0.53 green:0.53 blue:0.53 alpha:1.0]; 195 | 196 | // Font 197 | tabFont = [NSFont fontWithName:@"Lucida Grande" size:11]; 198 | 199 | destinationIndex = -1; 200 | sourceIndex = -1; 201 | isDragging = NO; 202 | } 203 | return self; 204 | } 205 | 206 | 207 | -(void)awakeFromNib{ 208 | tabs = [NSMutableArray array]; 209 | } 210 | 211 | /* Overriding this method fix the setFrame issue as well */ 212 | /*- (void)resizeSubviewsWithOldSize:(NSSize)oldSize{ 213 | NSRect rect = [[self superview] bounds]; 214 | rect.origin = NSMakePoint(0, rect.size.height - kTabBarViewHeight); 215 | rect.size.height = kTabBarViewHeight; 216 | [self setFrame:rect]; 217 | [self setNeedsDisplay:YES]; 218 | NSLog(@"[*]%@", NSStringFromRect(rect)); 219 | [self setNeedsLayout:YES]; 220 | }*/ 221 | 222 | - (void)resizeWithOldSuperviewSize:(NSSize)oldSize{ 223 | NSRect rect = [[self superview] bounds]; 224 | rect.origin = NSMakePoint(0, rect.size.height - kTabBarViewHeight); 225 | rect.size.height = kTabBarViewHeight; 226 | [self setFrame:rect]; 227 | [self setNeedsDisplay:YES]; 228 | [self setNeedsLayout:YES]; 229 | } 230 | 231 | - (void)redraw{ 232 | [self setNeedsDisplay:YES]; 233 | 234 | /* Call this method here, and we don't need to setup layout in Interface Builder 235 | * And we need to call [tabBarView redraw] after launch to keep layout, so that this method 236 | * would be called here. 237 | */ 238 | [self resizeWithOldSuperviewSize:NSZeroSize]; 239 | } 240 | 241 | - (void)removeTabCell:(ENTabCell*)tabCell{ 242 | NSUInteger index = [[self tabs] indexOfObject:tabCell]; 243 | if (index > 0) { 244 | index--; 245 | } 246 | [[self tabs] removeObject:tabCell]; 247 | if ([tabs count] > 0) { 248 | ENTabCell *nextTab = [tabs objectAtIndex:index]; 249 | [nextTab setAsActiveTab]; 250 | } 251 | [self redraw]; 252 | } 253 | 254 | - (void)mouseUp:(NSEvent*)event { 255 | if (event.clickCount == 2) { // We capture user double click on tabbar view 256 | NSPoint p =[event locationInWindow]; 257 | p = [self convertPoint:p fromView:[[self window] contentView]]; 258 | if ([self isBlankAreaOfTabBarViewInPoint:p]) { 259 | ENTabCell *tab = [self addTabViewWithTitle:@"Untitled"]; 260 | [tab setAsActiveTab]; 261 | [self redraw]; 262 | } 263 | } 264 | } 265 | 266 | - (void)drawRect:(NSRect)dirtyRect 267 | { 268 | [super drawRect:dirtyRect]; 269 | 270 | // Drawing background color of Tab bar view. 271 | [bgColor set]; 272 | NSRect rect = [self frame]; 273 | rect.origin = NSZeroPoint; 274 | NSRectFill(rect); 275 | 276 | 277 | // Draw tab list control 278 | tabListControlPath = [NSBezierPath bezierPath]; 279 | NSRect tabListRect = [self rectForTabListControl]; 280 | tabListRect = NSIntegralRect(tabListRect); 281 | int maxY = NSMaxY(tabListRect); 282 | int minY = NSMinY(tabListRect); 283 | int minX = NSMinX(tabListRect); 284 | int maxX = NSMaxX(tabListRect); 285 | int midX = NSMidX(tabListRect); 286 | 287 | NSPoint p1 = NSMakePoint(midX, minY); 288 | NSPoint p2 = NSMakePoint(minX, maxY); 289 | NSPoint p3 = NSMakePoint(maxX, maxY); 290 | 291 | [tabListControlPath moveToPoint:p1]; 292 | [tabListControlPath lineToPoint:p2]; 293 | [tabListControlPath lineToPoint:p3]; 294 | [tabListControlPath lineToPoint:p1]; 295 | //[[self smallControlColor] set]; 296 | // Use tab active back ground color to set tab list triangle 297 | [[self tabActivedBGColor] set]; 298 | [tabListControlPath fill]; 299 | 300 | // Drawing bottom border line 301 | NSPoint start = NSMakePoint(0, 1); 302 | NSPoint end = NSMakePoint(NSMaxX(rect), 1); 303 | [NSBezierPath setDefaultLineWidth:2.0]; 304 | [tabBorderColor set]; 305 | [NSBezierPath strokeLineFromPoint:start toPoint:end]; 306 | 307 | /* 308 | * Draw all tab cells 309 | * (*) All are not subclass of NSView, but NSObject 310 | */ 311 | // Reset all tool tips 312 | [self removeAllToolTips]; 313 | NSInteger index = 0; 314 | for(index = 0; index < [tabs count]; ++index){ 315 | NSRect rect = [self tabRectFromIndex:index]; 316 | ENTabCell *tab = [tabs objectAtIndex:index]; 317 | [tab setFrame:rect]; 318 | [self setToolTip:[tab title]]; 319 | [self addToolTipRect:[tab frame] owner:[tab title] userData:nil]; 320 | [tab draw]; 321 | } 322 | } 323 | 324 | - (id)addTabViewWithTitle:(NSString *)title{ 325 | ENTabCell *tab = [ENTabCell tabCellWithTabBarView:self title:title]; 326 | 327 | if ([[self delegate] respondsToSelector:@selector(tabWillBeCreated:)]) { 328 | [[self delegate] tabWillBeCreated:tab]; 329 | } 330 | 331 | [tabs addObject:tab]; 332 | 333 | // If the new tab(add it to last) is not fully shown in the tabbar view, we 334 | // then exchange it with first(0 index) tab, and then active it. 335 | NSUInteger tabIndex = [[self tabs] indexOfObject:tab]; 336 | NSRect tabBarViewRect = [self bounds]; 337 | NSRect tabRect = [self tabRectFromIndex:tabIndex]; 338 | if (!CGRectContainsRect(tabBarViewRect, tabRect)) { 339 | [self exchangeTabWithIndex:tabIndex withTab:0]; 340 | } 341 | 342 | [tab setAsActiveTab]; 343 | 344 | if ([[self delegate] respondsToSelector:@selector(tabDidBeCreated:)]) { 345 | [[self delegate] tabDidBeCreated:tab]; 346 | } 347 | 348 | [self redraw]; 349 | return tab; 350 | } 351 | 352 | - (void)mouseDown:(NSEvent *)theEvent{ 353 | NSPoint p = [theEvent locationInWindow]; 354 | p = [self convertPoint:p fromView:[[self window] contentView]]; 355 | 356 | /* Check if tabs list control clicked */ 357 | NSRect rectOfTabList = NSMakeRect(0, 0, kWidthOfTabList, kHeightOfTabList); 358 | if (NSPointInRect(p, rectOfTabList)) { 359 | [NSMenu popUpContextMenu:[self tabsMenu] withEvent:theEvent forView:self]; 360 | } 361 | 362 | 363 | /* Switch active tab */ 364 | NSUInteger index = 0; 365 | for(index = 0; index < [tabs count]; ++ index){ 366 | ENTabCell *tab = [tabs objectAtIndex:index]; 367 | if([[tab path] containsPoint:p]){ 368 | [tab setAsActiveTab]; 369 | //[self setSelectedTab:tab]; 370 | //[[self selectedTab] setAsActiveTab]; 371 | } 372 | 373 | // forwar mouse down to tab cell 374 | [tab mouseDown:theEvent]; 375 | }; 376 | 377 | [self redraw]; 378 | 379 | // Draging stuff 380 | isDragging = NO; 381 | } 382 | 383 | - (void)mouseMoved:(NSEvent *)theEvent{ 384 | NSPoint p = [theEvent locationInWindow]; 385 | p = [self convertPoint:p fromView:[[self window] contentView]]; 386 | 387 | /* Check if tabs list control clicked 388 | * Commented out so that not used 389 | */ 390 | if (NSPointInRect(p, [self rectForTabListControl])) { 391 | //self.smallControlColor = [self tabActivedBGColor]; 392 | }else{ 393 | //self.smallControlColor = [self tabActivedBGColor]; //oldSmallControlColor; 394 | } 395 | 396 | /* Switch active tab */ 397 | NSUInteger index = 0; 398 | for(index = 0; index < [tabs count]; ++ index){ 399 | ENTabCell *tab = [tabs objectAtIndex:index]; 400 | // forwar mouse moved to tab cell 401 | [tab mouseMoved:theEvent]; 402 | }; 403 | 404 | [self redraw]; 405 | } 406 | 407 | - (void)setFrame:(NSRect)frame { 408 | [super setFrame:frame]; 409 | [self removeTrackingArea:trackingArea]; 410 | 411 | NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved); trackingArea = [[NSTrackingArea alloc] initWithRect:[self frame] options:options owner:self userInfo:nil]; 412 | [self addTrackingArea:trackingArea]; 413 | } 414 | 415 | - (void)setBounds:(NSRect)bounds { 416 | [super setBounds:bounds]; 417 | [self removeTrackingArea:trackingArea]; 418 | 419 | NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved); trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; 420 | [self addTrackingArea:trackingArea]; 421 | } 422 | 423 | #pragma mark Drag & Drop 424 | - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag{ 425 | return NSDragOperationNone; 426 | } 427 | 428 | - (void)mouseDragged:(NSEvent *)theEvent{ 429 | NSPoint p = [theEvent locationInWindow]; 430 | p = [self convertPoint:p fromView:[[self window] contentView]]; 431 | if (!isDragging) { 432 | draggingTab = [self tabCellInPoint:p]; 433 | isDragging = YES; 434 | if (draggingTab != nil) { 435 | draggingImage = [ENTabImage imageWithENTabCell:draggingTab]; 436 | [draggingTab setIsDraggingTab:YES]; 437 | // Save source index 438 | sourceIndex = [tabs indexOfObject:draggingTab]; 439 | [tabs removeObject:draggingTab]; 440 | [self redraw]; 441 | } 442 | } 443 | 444 | if (draggingTab == nil) { 445 | return ; 446 | } 447 | 448 | NSSize offset = NSMakeSize(0.0, 0.0); 449 | 450 | p.x -= (draggingTab.frame.size.width / 2); 451 | p.y -= (draggingTab.frame.size.height / 2); 452 | [self dragImage:draggingImage at:p offset:offset event:theEvent pasteboard:nil source:self slideBack:NO]; 453 | } 454 | 455 | - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint{ 456 | 457 | screenPoint.x += (draggingTab.frame.size.width / 2); 458 | screenPoint.y += (draggingTab.frame.size.height / 2); 459 | NSPoint windowLocation = [[self window] convertScreenToBase:screenPoint]; 460 | NSPoint viewPoint = [self convertPoint:windowLocation fromView:nil]; 461 | 462 | NSRect tabBarViewRect = [self bounds]; 463 | //tabBarViewRect = NSInsetRect(tabBarViewRect, 0, -40); 464 | 465 | if (!NSPointInRect(viewPoint, tabBarViewRect)) { 466 | //NSLog(@"Moved drag out"); 467 | [tabs removeObject:draggingTab]; 468 | destinationIndex = -1; 469 | [self redraw]; 470 | return ; 471 | } 472 | 473 | destinationIndex = [self destinationCellIndexFromPoint:viewPoint]; 474 | if (destinationIndex == -1) return ; 475 | //NSLog(@"Moved Dest index: %d", (int)destinationIndex); 476 | [tabs removeObject:draggingTab]; 477 | [tabs insertObject:draggingTab atIndex:destinationIndex]; 478 | [self redraw]; 479 | 480 | } 481 | 482 | - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation{ 483 | 484 | screenPoint.x += (draggingTab.frame.size.width / 2); 485 | screenPoint.y += (draggingTab.frame.size.height / 2); 486 | NSPoint windowLocation = [[self window] convertScreenToBase:screenPoint]; 487 | NSPoint viewPoint = [self convertPoint:windowLocation fromView:nil]; 488 | 489 | NSRect tabBarViewRect = [self bounds]; 490 | if (!NSPointInRect(viewPoint, tabBarViewRect)) { 491 | [draggingTab setIsDraggingTab:NO]; // Not used? Yes, not used. Just let it be here 492 | [tabs removeObject:draggingTab]; 493 | [tabs insertObject:draggingTab atIndex:sourceIndex]; 494 | [self redraw]; 495 | sourceIndex = -1; 496 | destinationIndex = -1; 497 | return ; 498 | } 499 | 500 | destinationIndex = [self destinationCellIndexFromPoint:viewPoint]; 501 | if (destinationIndex == -1) { 502 | return ; 503 | } 504 | [draggingTab setIsDraggingTab:NO]; 505 | [tabs removeObject:draggingTab]; 506 | [tabs insertObject:draggingTab atIndex:destinationIndex]; 507 | [self redraw]; 508 | 509 | destinationIndex = -1; 510 | sourceIndex = -1; 511 | isDragging = NO; 512 | } 513 | @end 514 | -------------------------------------------------------------------------------- /ENTabBarView/ENTabCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ENTabView.h 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ENTabBarView; 12 | 13 | @interface ENTabCell : NSObject{ 14 | NSBezierPath *path; 15 | } 16 | 17 | @property (readwrite) BOOL canDrawCloseButton; 18 | @property (readwrite) NSString *title; 19 | @property (readwrite) NSMutableAttributedString *titleAttributedString; 20 | @property (readonly) NSBezierPath *path; 21 | @property (readwrite) NSRect frame; 22 | @property (readwrite) ENTabBarView *tabBarView; 23 | @property (readwrite) BOOL isActived; 24 | @property (readwrite) BOOL isDraggingTab; // We did not use this in draw, just leave it here 25 | 26 | + (id)tabCellWithTabBarView:(ENTabBarView*)tabBarView title:(NSString *)aTittle; 27 | - (void)setAsActiveTab; 28 | - (void)draw; 29 | 30 | /* forward mouse event to tab */ 31 | - (void)mouseDown:(NSEvent *)theEvent; 32 | - (void)mouseMoved:(NSEvent *)theEvent; 33 | @end 34 | -------------------------------------------------------------------------------- /ENTabBarView/ENTabCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ENTabView.m 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import "ENTabCell.h" 10 | #import "ENTabBarView.h" 11 | 12 | #define kBorderWidth 2 13 | 14 | #define kCloseButtonWidth 8 15 | 16 | #define deltaXfromLeftAndRight 2 17 | 18 | @implementation ENTabCell 19 | 20 | @synthesize path; 21 | @synthesize frame; 22 | @synthesize isActived; 23 | @synthesize tabBarView; 24 | @synthesize title; 25 | @synthesize titleAttributedString; 26 | @synthesize canDrawCloseButton; 27 | @synthesize isDraggingTab; 28 | 29 | + (id)tabCellWithTabBarView:(ENTabBarView*)tabBarView title:(NSString *)aTittle{ 30 | ENTabCell *tabCell = [[ENTabCell alloc] init]; 31 | [tabCell setTabBarView:tabBarView]; 32 | [tabCell setIsActived:NO]; 33 | [tabCell setTitle:aTittle]; 34 | [tabCell setCanDrawCloseButton:NO]; 35 | [tabCell setIsDraggingTab:NO]; 36 | return tabCell; 37 | } 38 | 39 | - (NSRect)closeButtonRect{ 40 | NSRect tabRect = [self frame]; 41 | CGFloat maxX = NSMaxX(tabRect); 42 | NSRect rect = NSMakeRect(maxX - tabRect.size.height - deltaXfromLeftAndRight - 1, 0, tabRect.size.height, tabRect.size.height); 43 | rect = CGRectInset(rect, kCloseButtonWidth + 2, kCloseButtonWidth + 2); 44 | return rect; 45 | } 46 | 47 | - (void)drawCloseButton{ 48 | NSRect closeButtonRect = [self closeButtonRect]; 49 | NSBezierPath *closeButtonPath = [NSBezierPath bezierPath]; 50 | CGFloat minX = NSMinX(closeButtonRect); 51 | CGFloat maxX = NSMaxX(closeButtonRect); 52 | CGFloat minY = NSMinY(closeButtonRect); 53 | CGFloat maxY = NSMaxY(closeButtonRect); 54 | 55 | NSPoint leftBottomPoint = NSMakePoint(minX, minY); 56 | NSPoint leftTopPoint = NSMakePoint(minX, maxY); 57 | NSPoint rightBottomPoint = NSMakePoint(maxX, minY); 58 | NSPoint rightTopPoint = NSMakePoint(maxX, maxY); 59 | 60 | [closeButtonPath moveToPoint:leftBottomPoint]; 61 | [closeButtonPath lineToPoint:rightTopPoint]; 62 | 63 | [closeButtonPath moveToPoint:leftTopPoint]; 64 | [closeButtonPath lineToPoint:rightBottomPoint]; 65 | 66 | [closeButtonPath setLineWidth:2.0]; 67 | [[[self tabBarView] smallControlColor] set]; 68 | 69 | [closeButtonPath stroke]; 70 | } 71 | 72 | // tab cell draw itself in this method, called in TabBarView's drawRect method 73 | // - :> 74 | - (void)draw{ 75 | if (isDraggingTab) { 76 | //return ; 77 | } 78 | 79 | NSRect rect = [self frame]; 80 | rect = NSInsetRect(rect, kBorderWidth / 2.0, kBorderWidth / 2.0); 81 | rect = NSIntegralRect(rect); 82 | rect.origin.x -= 0.5; 83 | rect.origin.y -= 0.5; // Fix: get rid of 1 pixel bottom line in cell 84 | 85 | float radius = 4.0; 86 | path = [NSBezierPath bezierPath]; 87 | 88 | int minX = NSMinX(rect); 89 | int midX = NSMidX(rect); 90 | int maxX = NSMaxX(rect); 91 | int minY = NSMinY(rect); 92 | int midY = NSMidY(rect); 93 | int maxY = NSMaxY(rect); 94 | 95 | NSPoint leftBottomPoint = NSMakePoint(minX, minY); 96 | NSPoint leftMiddlePoint = NSMakePoint(minX + deltaXfromLeftAndRight, midY); 97 | NSPoint topMiddlePoint = NSMakePoint(midX, maxY); 98 | NSPoint rightMiddlePoint = NSMakePoint(maxX - deltaXfromLeftAndRight, midY); 99 | NSPoint rightBottomPoint = NSMakePoint(maxX, minY); 100 | 101 | 102 | // Start to construct border path 103 | 104 | // move path to left bottom point 105 | [path moveToPoint:leftBottomPoint]; 106 | 107 | // left bottom to left middle 108 | [path appendBezierPathWithArcFromPoint:NSMakePoint(minX + deltaXfromLeftAndRight, minY) toPoint:leftMiddlePoint radius:radius]; 109 | 110 | // left middle to top middle 111 | [path appendBezierPathWithArcFromPoint:NSMakePoint(minX + deltaXfromLeftAndRight, maxY) toPoint:topMiddlePoint radius:radius]; 112 | 113 | // top middle to right middle 114 | [path appendBezierPathWithArcFromPoint:NSMakePoint(maxX - deltaXfromLeftAndRight, maxY) toPoint:rightMiddlePoint radius:radius]; 115 | 116 | // right middle to right bottom 117 | [path appendBezierPathWithArcFromPoint:NSMakePoint(maxX - deltaXfromLeftAndRight, minY) toPoint:rightBottomPoint radius:radius]; 118 | 119 | //left bottom to right bottom -- line 120 | //[path lineToPoint:leftBottomPoint]; 121 | 122 | [path setLineWidth:kBorderWidth]; 123 | 124 | // Draw tab background 125 | 126 | if ([self isActived]) { 127 | [[[self tabBarView] tabActivedBGColor] set]; 128 | }else{ 129 | [[[self tabBarView] tabBGColor] set]; 130 | } 131 | 132 | [path fill]; 133 | 134 | // 135 | // Start here |---------> 136 | // Draw outline border 137 | // 138 | //[path closePath]; Comment out this line to prevent stroke cell bottom line 139 | [[[self tabBarView] tabBorderColor] set]; 140 | [path stroke]; 141 | 142 | 143 | // If finally not a active tab, draw bottom line 144 | if (![self isActived]) { 145 | NSBezierPath *linePath = [NSBezierPath bezierPath]; 146 | [linePath moveToPoint:leftBottomPoint]; 147 | [linePath lineToPoint:rightBottomPoint]; 148 | 149 | // Why plus 1? Coz we operated on points before, so shift 1 pixel width 150 | // *_* 151 | [linePath setLineWidth:kBorderWidth+1]; 152 | [[[self tabBarView] tabBorderColor] set]; 153 | [linePath stroke]; 154 | } 155 | 156 | // Draw title 157 | /* Setup title's attributed string */ 158 | NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; 159 | 160 | NSColor *fontColor = [self isActived]?[[self tabBarView] tabActivedTitleColor] : [[self tabBarView] tabTitleColor]; 161 | NSMutableParagraphStyle* p = [[NSMutableParagraphStyle alloc] init]; 162 | p.alignment = kCTTextAlignmentCenter; 163 | p.lineBreakMode = NSLineBreakByTruncatingTail; 164 | [attrs setObject:[[self tabBarView] tabFont] forKey:NSFontAttributeName]; 165 | [attrs setObject:fontColor forKey:NSForegroundColorAttributeName]; 166 | [attrs setObject:p forKey:NSParagraphStyleAttributeName]; 167 | NSMutableAttributedString *mas = [[NSMutableAttributedString alloc] initWithString:self.title attributes:attrs]; 168 | 169 | [self setTitleAttributedString:mas]; 170 | 171 | 172 | // Fix text layout: vertically center 173 | // [Done]Fix: Close button by shifting rect 174 | NSRect titleRect = [self frame]; 175 | CGFloat fontHeight = self.titleAttributedString.size.height; 176 | int yOffset = (titleRect.size.height - fontHeight) / 2.0; 177 | 178 | titleRect.size.height = fontHeight; 179 | titleRect.origin.y += yOffset; 180 | titleRect = NSInsetRect(titleRect, deltaXfromLeftAndRight + 26, 0); 181 | [self.titleAttributedString drawInRect:titleRect]; 182 | 183 | // Draw close button if mouse on close button rect 184 | if ([self canDrawCloseButton]) { 185 | [self drawCloseButton]; 186 | } 187 | } 188 | 189 | #pragma mark -- Set as active tab -- 190 | - (void)setAsActiveTab{ 191 | if ([[self tabBarView] selectedTab] == self) { 192 | return ; 193 | } 194 | 195 | NSUInteger index = 0; 196 | NSMutableArray *tabs = [[self tabBarView] tabs]; 197 | for(index = 0; index < [tabs count]; ++ index){ 198 | ENTabCell *tab = [tabs objectAtIndex:index]; 199 | [tab setIsActived:NO]; 200 | } 201 | 202 | // Call delegate protocol methods 203 | if([[[self tabBarView] delegate] respondsToSelector:@selector(tabWillActive:)]){ 204 | [[[self tabBarView] delegate] tabWillActive:self]; 205 | } 206 | 207 | [self setIsActived:YES]; 208 | 209 | if([[[self tabBarView] delegate] respondsToSelector:@selector(tabDidActived:)]){ 210 | [[[self tabBarView] delegate] tabDidActived:self]; 211 | } 212 | [[self tabBarView] setSelectedTab:self]; 213 | } 214 | 215 | #pragma mark == Forwar mouse event to tab == 216 | - (void)mouseDown:(NSEvent *)theEvent{ 217 | NSPoint p = [theEvent locationInWindow]; 218 | p = [[self tabBarView] convertPoint:p fromView:[[[self tabBarView] window] contentView]]; 219 | 220 | if (NSPointInRect(p ,[self closeButtonRect])) { 221 | // Delete this tab cell 222 | id delegate = [[self tabBarView] delegate]; 223 | if ([delegate respondsToSelector:@selector(tabWillClose:)]) { 224 | [delegate tabWillClose:self]; 225 | } 226 | [[self tabBarView] removeTabCell:self]; 227 | if ([delegate respondsToSelector:@selector(tabDidClosed:)]) { 228 | [delegate tabDidClosed:self]; 229 | } 230 | }else{ 231 | // Do nothing 232 | } 233 | 234 | } 235 | 236 | - (void)mouseMoved:(NSEvent *)theEvent{ 237 | NSPoint p = [theEvent locationInWindow]; 238 | p = [[self tabBarView] convertPoint:p fromView:[[[self tabBarView] window] contentView]]; 239 | 240 | if (NSPointInRect(p ,[self closeButtonRect])) { 241 | canDrawCloseButton = YES; 242 | }else{ 243 | canDrawCloseButton = NO; 244 | } 245 | } 246 | 247 | @end 248 | -------------------------------------------------------------------------------- /ENTabBarView/ENTabImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // ENTabImage.h 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/29/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ENTabCell.h" 11 | 12 | @interface ENTabImage : NSImage{ 13 | ENTabCell *tab; 14 | } 15 | 16 | @property (readwrite) ENTabCell *tab; 17 | + (id)imageWithENTabCell:(ENTabCell*)tabCell; 18 | @end 19 | -------------------------------------------------------------------------------- /ENTabBarView/ENTabImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // ENTabImage.m 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/29/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import "ENTabImage.h" 10 | 11 | @implementation ENTabImage 12 | @synthesize tab; 13 | + (id)imageWithENTabCell:(ENTabCell*)tabCell{ 14 | NSRect rect = NSMakeRect(0, 0, tabCell.frame.size.width, tabCell.frame.size.height); 15 | ENTabImage *image = [[ENTabImage alloc] initWithSize:rect.size]; 16 | [image setTab:tabCell]; 17 | 18 | // Reset tab cell's frame 19 | [[image tab] setFrame:rect]; 20 | 21 | [image lockFocus]; 22 | 23 | // Transparent 24 | [[NSColor clearColor] set]; 25 | NSRectFill(rect); 26 | 27 | // ---------------------------- Copy from ENTabCell ---------------------------- 28 | [[image tab] draw]; 29 | // ---------------------------- End of copying from ENTabCell ---------------------------- 30 | 31 | [image unlockFocus]; 32 | return image; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## ENTabBarView 2 | 3 | ENTabBarView is a clean and simple TabBarView for Cocoa with ARC enabled and Objective-C. 4 | 5 | ### How to use 6 | 7 | * Drag ENTabBarView into your project. 8 | * Drag a NSView object into Interface Builder, and set its class as ENTabBarView 9 | * In your App delegate's 10 | 11 | ``` 12 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 13 | { 14 | [tabBarView setDelegate:self]; 15 | 16 | [tabBarView addTabViewWithTitle:@"Elk Developer's Note.txt"]; 17 | [tabBarView addTabViewWithTitle:@"index.html.rjs"]; 18 | } 19 | ``` 20 | 21 | Here we assume that `tabBarView` is a IBOutlet to that ENTabBarView in Interface Builder. 22 | 23 | 24 | ### Callbacks 25 | 26 | After `[tabBarView setDelegate:self]`, and set your App delegate like this: 27 | 28 | `@interface ENAppDelegate : NSObject {` 29 | 30 | You can implement these methods in your `tabBarView`'s delegate to get notified while these events are sent as the method name suggested. 31 | 32 | `- (void)tabWillActive:(ENTabCell*)tab;` 33 | 34 | `- (void)tabDidActived:(ENTabCell*)tab;` 35 | 36 | `- (void)tabWillClose:(ENTabCell*)tab;` 37 | 38 | `- (void)tabDidClosed:(ENTabCell*)tab;` 39 | 40 | `- (void)tabWillBeCreated:(ENTabCell*)tab;` 41 | 42 | `- (void)tabDidBeCreated:(ENTabCell*)tab;` 43 | 44 | ### ScreenShots 45 | 46 | ![image](https://raw.githubusercontent.com/aaron-elkins/ENTabBarView/master/ENTabBarView.png) 47 | 48 | ### Authors 49 | 50 | [Aaron Elkins](http://blog.pixelegg.me) 51 | 52 | [Email Me](mailto:threcius@yahoo.com) 53 | 54 | ### Buy me a cup of tea 55 | 56 | If you like this chunk of code, please consider [buying me a cup of tea](https://www.pixelegg.me/buy_me_tea). 57 | 58 | ### License 59 | 60 | ENTabBarView is licensed under [MIT](http://opensource.org/licenses/MIT) license. 61 | -------------------------------------------------------------------------------- /TabBarDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 933AC7E11989DA3E00338178 /* ENTabBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 933AC7DC1989DA3E00338178 /* ENTabBarView.m */; }; 11 | 933AC7E21989DA3E00338178 /* ENTabCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 933AC7DE1989DA3E00338178 /* ENTabCell.m */; }; 12 | 933AC7E31989DA3E00338178 /* ENTabImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 933AC7E01989DA3E00338178 /* ENTabImage.m */; }; 13 | 93E1D8A31982298F00CB9756 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93E1D8A21982298F00CB9756 /* Cocoa.framework */; }; 14 | 93E1D8AD1982298F00CB9756 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 93E1D8AB1982298F00CB9756 /* InfoPlist.strings */; }; 15 | 93E1D8AF1982298F00CB9756 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E1D8AE1982298F00CB9756 /* main.m */; }; 16 | 93E1D8B31982298F00CB9756 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 93E1D8B11982298F00CB9756 /* Credits.rtf */; }; 17 | 93E1D8B61982298F00CB9756 /* ENAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E1D8B51982298F00CB9756 /* ENAppDelegate.m */; }; 18 | 93E1D8B91982298F00CB9756 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93E1D8B71982298F00CB9756 /* MainMenu.xib */; }; 19 | 93E1D8BB1982298F00CB9756 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93E1D8BA1982298F00CB9756 /* Images.xcassets */; }; 20 | 93E1D8C21982298F00CB9756 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93E1D8C11982298F00CB9756 /* XCTest.framework */; }; 21 | 93E1D8C31982298F00CB9756 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93E1D8A21982298F00CB9756 /* Cocoa.framework */; }; 22 | 93E1D8CB1982298F00CB9756 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 93E1D8C91982298F00CB9756 /* InfoPlist.strings */; }; 23 | 93E1D8CD1982298F00CB9756 /* TabBarDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 93E1D8CC1982298F00CB9756 /* TabBarDemoTests.m */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 93E1D8C41982298F00CB9756 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 93E1D8971982298F00CB9756 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 93E1D89E1982298F00CB9756; 32 | remoteInfo = TabBarDemo; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 93073D53198535C6005B31C1 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; 38 | 933AC7DB1989DA3E00338178 /* ENTabBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENTabBarView.h; sourceTree = ""; }; 39 | 933AC7DC1989DA3E00338178 /* ENTabBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ENTabBarView.m; sourceTree = ""; }; 40 | 933AC7DD1989DA3E00338178 /* ENTabCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENTabCell.h; sourceTree = ""; }; 41 | 933AC7DE1989DA3E00338178 /* ENTabCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ENTabCell.m; sourceTree = ""; }; 42 | 933AC7DF1989DA3E00338178 /* ENTabImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENTabImage.h; sourceTree = ""; }; 43 | 933AC7E01989DA3E00338178 /* ENTabImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ENTabImage.m; sourceTree = ""; }; 44 | 93E1D89F1982298F00CB9756 /* TabBarDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabBarDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 93E1D8A21982298F00CB9756 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 46 | 93E1D8A51982298F00CB9756 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 47 | 93E1D8A61982298F00CB9756 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 48 | 93E1D8A71982298F00CB9756 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 49 | 93E1D8AA1982298F00CB9756 /* TabBarDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TabBarDemo-Info.plist"; sourceTree = ""; }; 50 | 93E1D8AC1982298F00CB9756 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 51 | 93E1D8AE1982298F00CB9756 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 52 | 93E1D8B01982298F00CB9756 /* TabBarDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TabBarDemo-Prefix.pch"; sourceTree = ""; }; 53 | 93E1D8B21982298F00CB9756 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 54 | 93E1D8B41982298F00CB9756 /* ENAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ENAppDelegate.h; sourceTree = ""; }; 55 | 93E1D8B51982298F00CB9756 /* ENAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ENAppDelegate.m; sourceTree = ""; }; 56 | 93E1D8BA1982298F00CB9756 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 57 | 93E1D8C01982298F00CB9756 /* TabBarDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TabBarDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 93E1D8C11982298F00CB9756 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 59 | 93E1D8C81982298F00CB9756 /* TabBarDemoTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TabBarDemoTests-Info.plist"; sourceTree = ""; }; 60 | 93E1D8CA1982298F00CB9756 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 61 | 93E1D8CC1982298F00CB9756 /* TabBarDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TabBarDemoTests.m; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | 93E1D89C1982298F00CB9756 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | 93E1D8A31982298F00CB9756 /* Cocoa.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | 93E1D8BD1982298F00CB9756 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | 93E1D8C31982298F00CB9756 /* Cocoa.framework in Frameworks */, 78 | 93E1D8C21982298F00CB9756 /* XCTest.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 933AC7DA1989DA3E00338178 /* ENTabBarView */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 933AC7DB1989DA3E00338178 /* ENTabBarView.h */, 89 | 933AC7DC1989DA3E00338178 /* ENTabBarView.m */, 90 | 933AC7DD1989DA3E00338178 /* ENTabCell.h */, 91 | 933AC7DE1989DA3E00338178 /* ENTabCell.m */, 92 | 933AC7DF1989DA3E00338178 /* ENTabImage.h */, 93 | 933AC7E01989DA3E00338178 /* ENTabImage.m */, 94 | ); 95 | path = ENTabBarView; 96 | sourceTree = ""; 97 | }; 98 | 93E1D8961982298F00CB9756 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 933AC7DA1989DA3E00338178 /* ENTabBarView */, 102 | 93E1D8A81982298F00CB9756 /* TabBarDemo */, 103 | 93E1D8C61982298F00CB9756 /* TabBarDemoTests */, 104 | 93E1D8A11982298F00CB9756 /* Frameworks */, 105 | 93E1D8A01982298F00CB9756 /* Products */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | 93E1D8A01982298F00CB9756 /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 93E1D89F1982298F00CB9756 /* TabBarDemo.app */, 113 | 93E1D8C01982298F00CB9756 /* TabBarDemoTests.xctest */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | 93E1D8A11982298F00CB9756 /* Frameworks */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 93E1D8A21982298F00CB9756 /* Cocoa.framework */, 122 | 93E1D8C11982298F00CB9756 /* XCTest.framework */, 123 | 93E1D8A41982298F00CB9756 /* Other Frameworks */, 124 | ); 125 | name = Frameworks; 126 | sourceTree = ""; 127 | }; 128 | 93E1D8A41982298F00CB9756 /* Other Frameworks */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 93E1D8A51982298F00CB9756 /* AppKit.framework */, 132 | 93E1D8A61982298F00CB9756 /* CoreData.framework */, 133 | 93E1D8A71982298F00CB9756 /* Foundation.framework */, 134 | ); 135 | name = "Other Frameworks"; 136 | sourceTree = ""; 137 | }; 138 | 93E1D8A81982298F00CB9756 /* TabBarDemo */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 93E1D8B41982298F00CB9756 /* ENAppDelegate.h */, 142 | 93E1D8B51982298F00CB9756 /* ENAppDelegate.m */, 143 | 93E1D8B71982298F00CB9756 /* MainMenu.xib */, 144 | 93E1D8BA1982298F00CB9756 /* Images.xcassets */, 145 | 93E1D8A91982298F00CB9756 /* Supporting Files */, 146 | ); 147 | path = TabBarDemo; 148 | sourceTree = ""; 149 | }; 150 | 93E1D8A91982298F00CB9756 /* Supporting Files */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 93E1D8AA1982298F00CB9756 /* TabBarDemo-Info.plist */, 154 | 93E1D8AB1982298F00CB9756 /* InfoPlist.strings */, 155 | 93E1D8AE1982298F00CB9756 /* main.m */, 156 | 93E1D8B01982298F00CB9756 /* TabBarDemo-Prefix.pch */, 157 | 93E1D8B11982298F00CB9756 /* Credits.rtf */, 158 | ); 159 | name = "Supporting Files"; 160 | sourceTree = ""; 161 | }; 162 | 93E1D8C61982298F00CB9756 /* TabBarDemoTests */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 93E1D8CC1982298F00CB9756 /* TabBarDemoTests.m */, 166 | 93E1D8C71982298F00CB9756 /* Supporting Files */, 167 | ); 168 | path = TabBarDemoTests; 169 | sourceTree = ""; 170 | }; 171 | 93E1D8C71982298F00CB9756 /* Supporting Files */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 93E1D8C81982298F00CB9756 /* TabBarDemoTests-Info.plist */, 175 | 93E1D8C91982298F00CB9756 /* InfoPlist.strings */, 176 | ); 177 | name = "Supporting Files"; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXGroup section */ 181 | 182 | /* Begin PBXNativeTarget section */ 183 | 93E1D89E1982298F00CB9756 /* TabBarDemo */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 93E1D8D01982298F00CB9756 /* Build configuration list for PBXNativeTarget "TabBarDemo" */; 186 | buildPhases = ( 187 | 93E1D89B1982298F00CB9756 /* Sources */, 188 | 93E1D89C1982298F00CB9756 /* Frameworks */, 189 | 93E1D89D1982298F00CB9756 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = TabBarDemo; 196 | productName = TabBarDemo; 197 | productReference = 93E1D89F1982298F00CB9756 /* TabBarDemo.app */; 198 | productType = "com.apple.product-type.application"; 199 | }; 200 | 93E1D8BF1982298F00CB9756 /* TabBarDemoTests */ = { 201 | isa = PBXNativeTarget; 202 | buildConfigurationList = 93E1D8D31982298F00CB9756 /* Build configuration list for PBXNativeTarget "TabBarDemoTests" */; 203 | buildPhases = ( 204 | 93E1D8BC1982298F00CB9756 /* Sources */, 205 | 93E1D8BD1982298F00CB9756 /* Frameworks */, 206 | 93E1D8BE1982298F00CB9756 /* Resources */, 207 | ); 208 | buildRules = ( 209 | ); 210 | dependencies = ( 211 | 93E1D8C51982298F00CB9756 /* PBXTargetDependency */, 212 | ); 213 | name = TabBarDemoTests; 214 | productName = TabBarDemoTests; 215 | productReference = 93E1D8C01982298F00CB9756 /* TabBarDemoTests.xctest */; 216 | productType = "com.apple.product-type.bundle.unit-test"; 217 | }; 218 | /* End PBXNativeTarget section */ 219 | 220 | /* Begin PBXProject section */ 221 | 93E1D8971982298F00CB9756 /* Project object */ = { 222 | isa = PBXProject; 223 | attributes = { 224 | CLASSPREFIX = EN; 225 | LastUpgradeCheck = 0510; 226 | ORGANIZATIONNAME = PixelEgg; 227 | TargetAttributes = { 228 | 93E1D8BF1982298F00CB9756 = { 229 | TestTargetID = 93E1D89E1982298F00CB9756; 230 | }; 231 | }; 232 | }; 233 | buildConfigurationList = 93E1D89A1982298F00CB9756 /* Build configuration list for PBXProject "TabBarDemo" */; 234 | compatibilityVersion = "Xcode 3.2"; 235 | developmentRegion = English; 236 | hasScannedForEncodings = 0; 237 | knownRegions = ( 238 | en, 239 | Base, 240 | ); 241 | mainGroup = 93E1D8961982298F00CB9756; 242 | productRefGroup = 93E1D8A01982298F00CB9756 /* Products */; 243 | projectDirPath = ""; 244 | projectRoot = ""; 245 | targets = ( 246 | 93E1D89E1982298F00CB9756 /* TabBarDemo */, 247 | 93E1D8BF1982298F00CB9756 /* TabBarDemoTests */, 248 | ); 249 | }; 250 | /* End PBXProject section */ 251 | 252 | /* Begin PBXResourcesBuildPhase section */ 253 | 93E1D89D1982298F00CB9756 /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 93E1D8AD1982298F00CB9756 /* InfoPlist.strings in Resources */, 258 | 93E1D8BB1982298F00CB9756 /* Images.xcassets in Resources */, 259 | 93E1D8B31982298F00CB9756 /* Credits.rtf in Resources */, 260 | 93E1D8B91982298F00CB9756 /* MainMenu.xib in Resources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | 93E1D8BE1982298F00CB9756 /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | 93E1D8CB1982298F00CB9756 /* InfoPlist.strings in Resources */, 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | /* End PBXResourcesBuildPhase section */ 273 | 274 | /* Begin PBXSourcesBuildPhase section */ 275 | 93E1D89B1982298F00CB9756 /* Sources */ = { 276 | isa = PBXSourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | 933AC7E21989DA3E00338178 /* ENTabCell.m in Sources */, 280 | 933AC7E11989DA3E00338178 /* ENTabBarView.m in Sources */, 281 | 933AC7E31989DA3E00338178 /* ENTabImage.m in Sources */, 282 | 93E1D8AF1982298F00CB9756 /* main.m in Sources */, 283 | 93E1D8B61982298F00CB9756 /* ENAppDelegate.m in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | 93E1D8BC1982298F00CB9756 /* Sources */ = { 288 | isa = PBXSourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | 93E1D8CD1982298F00CB9756 /* TabBarDemoTests.m in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXSourcesBuildPhase section */ 296 | 297 | /* Begin PBXTargetDependency section */ 298 | 93E1D8C51982298F00CB9756 /* PBXTargetDependency */ = { 299 | isa = PBXTargetDependency; 300 | target = 93E1D89E1982298F00CB9756 /* TabBarDemo */; 301 | targetProxy = 93E1D8C41982298F00CB9756 /* PBXContainerItemProxy */; 302 | }; 303 | /* End PBXTargetDependency section */ 304 | 305 | /* Begin PBXVariantGroup section */ 306 | 93E1D8AB1982298F00CB9756 /* InfoPlist.strings */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | 93E1D8AC1982298F00CB9756 /* en */, 310 | ); 311 | name = InfoPlist.strings; 312 | sourceTree = ""; 313 | }; 314 | 93E1D8B11982298F00CB9756 /* Credits.rtf */ = { 315 | isa = PBXVariantGroup; 316 | children = ( 317 | 93E1D8B21982298F00CB9756 /* en */, 318 | ); 319 | name = Credits.rtf; 320 | sourceTree = ""; 321 | }; 322 | 93E1D8B71982298F00CB9756 /* MainMenu.xib */ = { 323 | isa = PBXVariantGroup; 324 | children = ( 325 | 93073D53198535C6005B31C1 /* en */, 326 | ); 327 | name = MainMenu.xib; 328 | sourceTree = ""; 329 | }; 330 | 93E1D8C91982298F00CB9756 /* InfoPlist.strings */ = { 331 | isa = PBXVariantGroup; 332 | children = ( 333 | 93E1D8CA1982298F00CB9756 /* en */, 334 | ); 335 | name = InfoPlist.strings; 336 | sourceTree = ""; 337 | }; 338 | /* End PBXVariantGroup section */ 339 | 340 | /* Begin XCBuildConfiguration section */ 341 | 93E1D8CE1982298F00CB9756 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ALWAYS_SEARCH_USER_PATHS = NO; 345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 346 | CLANG_CXX_LIBRARY = "libc++"; 347 | CLANG_ENABLE_MODULES = YES; 348 | CLANG_ENABLE_OBJC_ARC = YES; 349 | CLANG_WARN_BOOL_CONVERSION = YES; 350 | CLANG_WARN_CONSTANT_CONVERSION = YES; 351 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 352 | CLANG_WARN_EMPTY_BODY = YES; 353 | CLANG_WARN_ENUM_CONVERSION = YES; 354 | CLANG_WARN_INT_CONVERSION = YES; 355 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 356 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 357 | COPY_PHASE_STRIP = NO; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_DYNAMIC_NO_PIC = NO; 360 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 361 | GCC_OPTIMIZATION_LEVEL = 0; 362 | GCC_PREPROCESSOR_DEFINITIONS = ( 363 | "DEBUG=1", 364 | "$(inherited)", 365 | ); 366 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 367 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 368 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 369 | GCC_WARN_UNDECLARED_SELECTOR = YES; 370 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 371 | GCC_WARN_UNUSED_FUNCTION = YES; 372 | GCC_WARN_UNUSED_VARIABLE = YES; 373 | MACOSX_DEPLOYMENT_TARGET = 10.7; 374 | ONLY_ACTIVE_ARCH = YES; 375 | SDKROOT = macosx; 376 | }; 377 | name = Debug; 378 | }; 379 | 93E1D8CF1982298F00CB9756 /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ALWAYS_SEARCH_USER_PATHS = NO; 383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 384 | CLANG_CXX_LIBRARY = "libc++"; 385 | CLANG_ENABLE_MODULES = YES; 386 | CLANG_ENABLE_OBJC_ARC = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_CONSTANT_CONVERSION = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_EMPTY_BODY = YES; 391 | CLANG_WARN_ENUM_CONVERSION = YES; 392 | CLANG_WARN_INT_CONVERSION = YES; 393 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 | COPY_PHASE_STRIP = YES; 396 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 397 | ENABLE_NS_ASSERTIONS = NO; 398 | GCC_C_LANGUAGE_STANDARD = gnu99; 399 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | MACOSX_DEPLOYMENT_TARGET = 10.7; 407 | SDKROOT = macosx; 408 | }; 409 | name = Release; 410 | }; 411 | 93E1D8D11982298F00CB9756 /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 415 | COMBINE_HIDPI_IMAGES = YES; 416 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 417 | GCC_PREFIX_HEADER = "TabBarDemo/TabBarDemo-Prefix.pch"; 418 | INFOPLIST_FILE = "TabBarDemo/TabBarDemo-Info.plist"; 419 | MACOSX_DEPLOYMENT_TARGET = 10.7; 420 | PRODUCT_NAME = "$(TARGET_NAME)"; 421 | WRAPPER_EXTENSION = app; 422 | }; 423 | name = Debug; 424 | }; 425 | 93E1D8D21982298F00CB9756 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 429 | COMBINE_HIDPI_IMAGES = YES; 430 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 431 | GCC_PREFIX_HEADER = "TabBarDemo/TabBarDemo-Prefix.pch"; 432 | INFOPLIST_FILE = "TabBarDemo/TabBarDemo-Info.plist"; 433 | MACOSX_DEPLOYMENT_TARGET = 10.7; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | WRAPPER_EXTENSION = app; 436 | }; 437 | name = Release; 438 | }; 439 | 93E1D8D41982298F00CB9756 /* Debug */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TabBarDemo.app/Contents/MacOS/TabBarDemo"; 443 | COMBINE_HIDPI_IMAGES = YES; 444 | FRAMEWORK_SEARCH_PATHS = ( 445 | "$(DEVELOPER_FRAMEWORKS_DIR)", 446 | "$(inherited)", 447 | ); 448 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 449 | GCC_PREFIX_HEADER = "TabBarDemo/TabBarDemo-Prefix.pch"; 450 | GCC_PREPROCESSOR_DEFINITIONS = ( 451 | "DEBUG=1", 452 | "$(inherited)", 453 | ); 454 | INFOPLIST_FILE = "TabBarDemoTests/TabBarDemoTests-Info.plist"; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | TEST_HOST = "$(BUNDLE_LOADER)"; 457 | WRAPPER_EXTENSION = xctest; 458 | }; 459 | name = Debug; 460 | }; 461 | 93E1D8D51982298F00CB9756 /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TabBarDemo.app/Contents/MacOS/TabBarDemo"; 465 | COMBINE_HIDPI_IMAGES = YES; 466 | FRAMEWORK_SEARCH_PATHS = ( 467 | "$(DEVELOPER_FRAMEWORKS_DIR)", 468 | "$(inherited)", 469 | ); 470 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 471 | GCC_PREFIX_HEADER = "TabBarDemo/TabBarDemo-Prefix.pch"; 472 | INFOPLIST_FILE = "TabBarDemoTests/TabBarDemoTests-Info.plist"; 473 | PRODUCT_NAME = "$(TARGET_NAME)"; 474 | TEST_HOST = "$(BUNDLE_LOADER)"; 475 | WRAPPER_EXTENSION = xctest; 476 | }; 477 | name = Release; 478 | }; 479 | /* End XCBuildConfiguration section */ 480 | 481 | /* Begin XCConfigurationList section */ 482 | 93E1D89A1982298F00CB9756 /* Build configuration list for PBXProject "TabBarDemo" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 93E1D8CE1982298F00CB9756 /* Debug */, 486 | 93E1D8CF1982298F00CB9756 /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | 93E1D8D01982298F00CB9756 /* Build configuration list for PBXNativeTarget "TabBarDemo" */ = { 492 | isa = XCConfigurationList; 493 | buildConfigurations = ( 494 | 93E1D8D11982298F00CB9756 /* Debug */, 495 | 93E1D8D21982298F00CB9756 /* Release */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | 93E1D8D31982298F00CB9756 /* Build configuration list for PBXNativeTarget "TabBarDemoTests" */ = { 501 | isa = XCConfigurationList; 502 | buildConfigurations = ( 503 | 93E1D8D41982298F00CB9756 /* Debug */, 504 | 93E1D8D51982298F00CB9756 /* Release */, 505 | ); 506 | defaultConfigurationIsVisible = 0; 507 | defaultConfigurationName = Release; 508 | }; 509 | /* End XCConfigurationList section */ 510 | }; 511 | rootObject = 93E1D8971982298F00CB9756 /* Project object */; 512 | } 513 | -------------------------------------------------------------------------------- /TabBarDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TabBarDemo.xcodeproj/project.xcworkspace/xcshareddata/TabBarDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 3C64F0E4-EE7D-4D07-AC62-A1A2C93D7E9C 9 | IDESourceControlProjectName 10 | TabBarDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | E04CC744-6AA7-423F-943A-A38AE9C5491D 14 | https://github.com/aaron-elkins/ENTabBarView.git 15 | 16 | IDESourceControlProjectPath 17 | TabBarDemo.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | E04CC744-6AA7-423F-943A-A38AE9C5491D 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/aaron-elkins/ENTabBarView.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | E04CC744-6AA7-423F-943A-A38AE9C5491D 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | E04CC744-6AA7-423F-943A-A38AE9C5491D 36 | IDESourceControlWCCName 37 | TabBarDemo 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TabBarDemo.xcodeproj/project.xcworkspace/xcuserdata/aaron.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orklann/ENTabBarView/f0675e740569d96d425b80affe6d8291d4659cf3/TabBarDemo.xcodeproj/project.xcworkspace/xcuserdata/aaron.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TabBarDemo.xcodeproj/xcuserdata/aaron.xcuserdatad/xcschemes/TabBarDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /TabBarDemo.xcodeproj/xcuserdata/aaron.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TabBarDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 93E1D89E1982298F00CB9756 16 | 17 | primary 18 | 19 | 20 | 93E1D8BF1982298F00CB9756 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TabBarDemo/ENAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ENAppDelegate.h 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ENTabBarView.h" 11 | 12 | @interface ENAppDelegate : NSObject { 13 | IBOutlet ENTabBarView *tabBarView; 14 | IBOutlet NSTextView *textView; 15 | } 16 | 17 | @property (assign) IBOutlet NSWindow *window; 18 | 19 | - (IBAction)newTab:(id)sender; 20 | - (IBAction)setDefaultTheme:(id)sender; 21 | - (IBAction)setZenTheme:(id)sender; 22 | @end 23 | -------------------------------------------------------------------------------- /TabBarDemo/ENAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ENAppDelegate.m 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import "ENAppDelegate.h" 10 | 11 | @implementation ENAppDelegate 12 | 13 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 14 | { 15 | [[textView enclosingScrollView] setBorderType:NSNoBorder]; 16 | [textView setDrawsBackground:YES]; 17 | 18 | [tabBarView setDelegate:self]; 19 | 20 | /* Create two tab with titles */ 21 | [tabBarView addTabViewWithTitle:@"Elk Developer's Note.txt"]; 22 | [tabBarView addTabViewWithTitle:@"index.html.rjs"]; 23 | } 24 | 25 | - (IBAction)newTab:(id)sender{ 26 | [tabBarView addTabViewWithTitle:@"New Tab"]; 27 | [tabBarView redraw]; 28 | } 29 | 30 | #pragma mark ENTabBarView Delegate methods 31 | - (void)tabWillActive:(ENTabCell *)tab{ 32 | NSLog(@"Tab will active with title: %@", [tab title]); 33 | } 34 | 35 | - (void)tabDidActived:(ENTabCell *)tab{ 36 | NSLog(@"Tab did actived with title: %@", [tab title]); 37 | } 38 | 39 | - (void)tabWillClose:(ENTabCell *)tab{ 40 | NSLog(@"Tab will close with title: %@", [tab title]); 41 | } 42 | 43 | - (void)tabDidClosed:(ENTabCell *)tab{ 44 | NSLog(@"Tab did closed with title: %@", [tab title]); 45 | } 46 | 47 | #pragma mark Theme actions 48 | - (IBAction)setDefaultTheme:(id)sender{ 49 | // Give all colors a default value if none given 50 | tabBarView.bgColor = [NSColor colorWithCalibratedRed: 0.6 green:0.6 blue:0.6 alpha:1.0]; 51 | tabBarView.tabBGColor = [NSColor colorWithCalibratedRed: 0.68 green: 0.68 blue: 0.68 alpha:1.0]; 52 | tabBarView.tabActivedBGColor = [NSColor whiteColor]; 53 | tabBarView.tabBorderColor = [NSColor colorWithCalibratedRed: 0.53 green:0.53 blue:0.53 alpha:1.0]; 54 | tabBarView.tabTitleColor = [NSColor blackColor]; 55 | tabBarView.tabActivedTitleColor = [NSColor blackColor]; 56 | tabBarView.smallControlColor = [NSColor colorWithCalibratedRed:0.53 green:0.53 blue:0.53 alpha:1.0]; 57 | 58 | [tabBarView redraw]; 59 | 60 | [textView setBackgroundColor:[NSColor whiteColor]]; 61 | [textView setNeedsDisplay:YES]; 62 | } 63 | 64 | - (IBAction)setZenTheme:(id)sender{ 65 | // Give all colors a default value if none given 66 | tabBarView.bgColor = [NSColor colorWithCalibratedRed: 0.11 green:0.11 blue:0.11 alpha:1.0]; 67 | tabBarView.tabBGColor = [NSColor colorWithCalibratedRed:0.22 green:0.22 blue:0.22 alpha:1.0]; 68 | tabBarView.tabActivedBGColor = [NSColor colorWithCalibratedRed: 0.19 green: 0.19 blue: 0.19 alpha:1.0]; 69 | tabBarView.tabBorderColor = [NSColor colorWithCalibratedRed:0.36 green:0.36 blue:0.36 alpha:1.0]; 70 | tabBarView.tabTitleColor = [NSColor colorWithCalibratedRed:0.56 green:0.56 blue:0.47 alpha:1.0]; 71 | tabBarView.tabActivedTitleColor = [NSColor whiteColor]; 72 | tabBarView.smallControlColor = [NSColor colorWithCalibratedRed:0.53 green:0.53 blue:0.53 alpha:1.0]; 73 | 74 | [tabBarView redraw]; 75 | 76 | [textView setBackgroundColor:[tabBarView tabActivedBGColor]]; 77 | [textView setNeedsDisplay:YES]; 78 | } 79 | @end 80 | -------------------------------------------------------------------------------- /TabBarDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /TabBarDemo/TabBarDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | PixelEgg.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2014 PixelEgg. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /TabBarDemo/TabBarDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /TabBarDemo/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /TabBarDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /TabBarDemo/en.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 416 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | -------------------------------------------------------------------------------- /TabBarDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TabBarDemo 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) 12 | { 13 | return NSApplicationMain(argc, argv); 14 | } 15 | -------------------------------------------------------------------------------- /TabBarDemoTests/TabBarDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | PixelEgg.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TabBarDemoTests/TabBarDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarDemoTests.m 3 | // TabBarDemoTests 4 | // 5 | // Created by Aaron Elkins on 7/25/14. 6 | // Copyright (c) 2014 PixelEgg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TabBarDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TabBarDemoTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /TabBarDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------