diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index f607581b0..c82daf67d 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -178,6 +178,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; unsigned _gridStyleMask; unsigned _numberOfRows; + CPIndexSet _groupRows; CPTableHeaderView _headerView; @@ -318,6 +319,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; _exposedColumns = [CPIndexSet indexSet]; _cachedDataViews = { }; + _groupRows = [CPIndexSet indexSet]; + _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self]; [_tableDrawView setBackgroundColor:[CPColor clearColor]]; [self addSubview:_tableDrawView]; @@ -2361,6 +2364,22 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; else [dataView unsetThemeState:CPThemeStateSelectedDataView]; + if (_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_) + { + if([_delegate tableView:self isGroupRow:row]) + { + [_groupRows addIndex:row]; + [dataView setThemeState:CPThemeStateGroupRow]; + } + else + { + [_groupRows removeIndexesInRange:CPMakeRange(row, 1)]; + [dataView unsetThemeState:CPThemeStateGroupRow]; + } + + [self setNeedsDisplay:YES] + } + if (_implementedDelegateMethods & CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_) [_delegate tableView:self willDisplayView:dataView forTableColumn:tableColumn row:row]; @@ -2516,18 +2535,24 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; firstRow = exposedRows.location, lastRow = CPMaxRange(exposedRows) - 1, colorIndex = MIN(exposedRows.length, colorCount), - heightFilled = 0.0; + heightFilled = 0.0, + groupRowRects = [ ]; while (colorIndex--) { var row = firstRow - firstRow % colorCount + colorIndex, - fillRect = nil; + fillRect = _CGRectMakeZero(); CGContextBeginPath(context); for (; row <= lastRow; row += colorCount) if (row >= firstRow) - CGContextAddRect(context, CGRectIntersection(aRect, fillRect = [self rectOfRow:row])); + { + if (![_groupRows containsIndex:row] && _selectionHighlightStyle !== CPTableViewSelectionHighlightStyleSourceList) + CGContextAddRect(context, CGRectIntersection(aRect, fillRect = [self rectOfRow:row])); + else + groupRowRects.push(CGRectIntersection(aRect, [self rectOfRow:row])); + } if (row - colorCount === lastRow) heightFilled = _CGRectGetMaxY(fillRect); @@ -2539,22 +2564,33 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; } // console.profileEnd("row-paint"); + + + // FIX ME: this is really terrible, it seems like such a hack... var totalHeight = _CGRectGetMaxY(aRect); if (heightFilled >= totalHeight || _rowHeight <= 0.0) + { + [self _drawGroupRowsForRects:groupRowRects]; return; + } var rowHeight = _rowHeight + _intercellSpacing.height, fillRect = _CGRectMake(_CGRectGetMinX(aRect), _CGRectGetMinY(aRect) + heightFilled, _CGRectGetWidth(aRect), rowHeight); for (row = lastRow + 1; heightFilled < totalHeight; ++row) { - CGContextSetFillColor(context, rowColors[row % colorCount]); - CGContextFillRect(context, fillRect); + if(![_groupRows containsIndex:row] && _selectionHighlightStyle !== CPTableViewSelectionHighlightStyleSourceList) + { + CGContextSetFillColor(context, rowColors[row % colorCount]); + CGContextFillRect(context, fillRect); + } heightFilled += rowHeight; fillRect.origin.y += rowHeight; } + + [self _drawGroupRowsForRects:groupRowRects]; } - (void)drawGridInClipRect:(CGRect)aRect @@ -2672,46 +2708,73 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; var gradientCache = [self selectionGradientColors], topLineColor = [gradientCache objectForKey:CPSourceListTopLineColor], bottomLineColor = [gradientCache objectForKey:CPSourceListBottomLineColor], - gradientColor = [gradientCache objectForKey:CPSourceListGradient]; + gradientColor = [gradientCache objectForKey:CPSourceListGradient], + normalSelectionHighlightColor = [self selectionHighlightColor]; + + // dont do these lookups if there are no group rows + if ([_groupRows count]) + { + var topGroupLineColor = [CPColor colorWithCalibratedWhite:212.0 / 255.0 alpha:1.0], + bottomGroupLineColor = [CPColor colorWithCalibratedWhite:185.0 / 255.0 alpha:1.0], + gradientGroupColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [212.0 / 255.0, 212.0 / 255.0, 212.0 / 255.0,1.0, 197.0 / 255.0, 197.0 / 255.0, 197.0 / 255.0,1.0], [0,1], 2); + } while (count--) - { - var rowRect = CGRectIntersection(objj_msgSend(self, rectSelector, indexes[count]), aRect); + { + var currentIndex = indexes[count], + rowRect = CGRectIntersection(objj_msgSend(self, rectSelector, currentIndex), aRect); CGContextAddRect(context, rowRect); - if (drawGradient) + var shouldUseGroupGradient = [_groupRows containsIndex:currentIndex]; + console.log(currentIndex, [_groupRows description], shouldUseGroupGradient); + + if (drawGradient || shouldUseGroupGradient) { + //console.log("gradiant fill for row: " + currentIndex); var minX = _CGRectGetMinX(rowRect), minY = _CGRectGetMinY(rowRect), maxX = _CGRectGetMaxX(rowRect), maxY = _CGRectGetMaxY(rowRect) - deltaHeight; - CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0); + // use a special selection gradient for group rows + + + CGContextDrawLinearGradient(context, (shouldUseGroupGradient) ? gradientGroupColor : gradientColor, rowRect.origin, _CGPointMake(minX, maxY), 0); CGContextClosePath(context); CGContextBeginPath(context); CGContextMoveToPoint(context, minX, minY); CGContextAddLineToPoint(context, maxX, minY); CGContextClosePath(context); - CGContextSetStrokeColor(context, topLineColor); + CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? topGroupLineColor : topLineColor); CGContextStrokePath(context); CGContextBeginPath(context); CGContextMoveToPoint(context, minX, maxY); CGContextAddLineToPoint(context, maxX, maxY - 1); CGContextClosePath(context); - CGContextSetStrokeColor(context, bottomLineColor); + CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? bottomGroupLineColor : bottomLineColor); CGContextStrokePath(context); + CGContextClosePath(context); + } + else + { + CGContextClosePath(context); + //console.log("fill 1 for row:" + currentIndex); + [normalSelectionHighlightColor setFill]; + CGContextFillPath(context); + } } CGContextClosePath(context); - if (!drawGradient) + /*if (!drawGradient) { - [[self selectionHighlightColor] setFill]; + console.log("fill 2 for row: " + currentIndex); + [normalSelectionHighlightColor setFill]; CGContextFillPath(context); - } + }*/ CGContextBeginPath(context); var gridStyleMask = [self gridStyleMask]; @@ -2755,6 +2818,59 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; CGContextStrokePath(context); } +- (void)_drawGroupRowsForRects:(CPArray)rects +{ + if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList || !rects.length) + return; + + var context = [[CPGraphicsContext currentContext] graphicsPort], + i = rects.length; + + CGContextBeginPath(context); + + var gradientCache = [self selectionGradientColors], + topLineColor = [CPColor colorWithHexString:"d3d3d3"], + bottomLineColor = [CPColor colorWithHexString:"bebebd"], + gradientColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0,1.0, + 199.0 / 255.0, 199.0 / 255.0, 199.0 / 255.0,1.0], [0,1], 2), + drawGradient = YES; + + while (i--) + { + var rowRect = rects[i]; + + CGContextAddRect(context, rowRect); + + if (drawGradient) + { + var minX = CGRectGetMinX(rowRect), + minY = CGRectGetMinY(rowRect), + maxX = CGRectGetMaxX(rowRect), + maxY = CGRectGetMaxY(rowRect); + + CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0); + CGContextClosePath(context); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, minX, minY); + CGContextAddLineToPoint(context, maxX, minY); + CGContextClosePath(context); + CGContextSetStrokeColor(context, topLineColor); + CGContextStrokePath(context); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, minX, maxY); + CGContextAddLineToPoint(context, maxX, maxY - 1); + CGContextClosePath(context); + CGContextSetStrokeColor(context, bottomLineColor); + CGContextStrokePath(context); + } + } + + CGContextClosePath(context); +} + + - (void)_drawRows:(CPIndexSet)rowsIndexes clipRect:(CGRect)clipRect { var row = [rowsIndexes firstIndex]; @@ -2769,6 +2885,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; - (void)drawRow:(CPInteger)row clipRect:(CGRect)rect { // This method does currently nothing in cappuccino. Can be overriden by subclasses. + } - (void)layoutSubviews diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index a375b4f62..f930cb1ed 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -403,6 +403,7 @@ CPThemeStateHighlighted = CPThemeState("highlighted"); CPThemeStateSelected = CPThemeState("selected"); CPThemeStateTableDataView = CPThemeState("tableDataView"); CPThemeStateSelectedDataView = CPThemeStateSelectedTableDataView = CPThemeState("selectedTableDataView"); +CPThemeStateGroupRow = CPThemeState("CPThemeStateGroupRow"); CPThemeStateBezeled = CPThemeState("bezeled"); CPThemeStateBordered = CPThemeState("bordered"); CPThemeStateEditable = CPThemeState("editable"); diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 76cc69b53..c74a23bee 100755 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -738,6 +738,13 @@ var themedButtonValues = nil, [@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0], CPThemeStateTableDataView], [@"text-color", [CPColor whiteColor], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView], [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView], + + [@"text-color", [CPColor colorWithCalibratedWhite:125.0 / 255.0 alpha:1.0], CPThemeStateTableDataView|CPThemeStateGroupRow], + [@"text-color", [CPColor colorWithCalibratedWhite:1.0 alpha:1.0], CPThemeStateTableDataView|CPThemeStateGroupRow|CPThemeStateSelectedTableDataView], + [@"text-shadow-color", [CPColor whiteColor], CPThemeStateTableDataView|CPThemeStateGroupRow], + [@"text-shadow-offset", CGSizeMake(0,1), CPThemeStateTableDataView|CPThemeStateGroupRow], + [@"text-shadow-color", [CPColor colorWithCalibratedWhite:0.0 alpha:0.6], CPThemeStateTableDataView|CPThemeStateGroupRow|CPThemeStateSelectedTableDataView], + [@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView|CPThemeStateGroupRow] ]; [self registerThemeValues:themedTextFieldValues forView:textfield]; diff --git a/Tests/Manual/TableTest/GroupRowTest/AppController.j b/Tests/Manual/TableTest/GroupRowTest/AppController.j new file mode 100644 index 000000000..3c3f55c10 --- /dev/null +++ b/Tests/Manual/TableTest/GroupRowTest/AppController.j @@ -0,0 +1,144 @@ +/* + * AppController.j + * ScrollPerformanceTest + * + * Created by You on August 27, 2010. + * Copyright 2010, Your Company All rights reserved. + */ + +@import + + +@implementation AppController : CPObject +{ +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; + + tableView = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 400.0, 400.0)]; + + [tableView setAllowsMultipleSelection:YES]; + [tableView setAllowsColumnSelection:YES]; + [tableView setUsesAlternatingRowBackgroundColors:YES]; + + [tableView setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle]; + [tableView setDelegate:self]; + [tableView setDataSource:self]; + + + var iconView = [[CPImageView alloc] initWithFrame:CGRectMake(16,16,0,0)]; + [iconView setImageScaling:CPScaleNone]; + var iconColumn = [[CPTableColumn alloc] initWithIdentifier:"icons"]; + [iconColumn setWidth:32.0]; + [iconColumn setMinWidth:32.0]; + [iconColumn setDataView:iconView]; + //[tableView addTableColumn:iconColumn]; + + iconImage = [[CPImage alloc] initWithContentsOfFile:"http://cappuccino.org/images/favicon.png" size:CGSizeMake(16,16)]; + + + for (var i = 1; i <= 5; i++) + { + var column = [[CPTableColumn alloc] initWithIdentifier:String(i)]; + + [[column headerView] setStringValue:"Number " + i]; + + [column setMaxWidth:500.0]; + [column setWidth:200.0]; + + [tableView addTableColumn:column]; + } + + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([contentView bounds]), CGRectGetHeight([contentView bounds]))]; + + [scrollView setDocumentView:tableView]; + [scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + [contentView addSubview:scrollView]; + + [theWindow orderFront:self]; + +} + +- (int)numberOfRowsInTableView:(CPTableView)atableView +{ + return 500; +} + +- (id)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aColumn row:(int)aRow +{ + if ([aColumn identifier] === "icons") + return iconImage; + + return aRow; +} + +- (BOOL)tableView:(CPTableView)aTableView isGroupRow:(int)aRow +{ + console.log("called: "+aRow); + var groups = []; + + for(var i = 0; i < 100; i+=5) + groups.push(i); + + return [groups containsObject:aRow]; +} + +@end + +@implementation CPTableView (foo) +- (void)_drawGroupRowsForRects:(CPArray)rects +{ + if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList || !rects.length) + return; + + var context = [[CPGraphicsContext currentContext] graphicsPort], + i = rects.length; + + CGContextBeginPath(context); + + var gradientCache = [self selectionGradientColors], + topLineColor = [CPColor colorWithHexString:"d3d3d3"], + bottomLineColor = [CPColor colorWithHexString:"bebebd"], + gradientColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0,1.0, + 199.0 / 255.0, 199.0 / 255.0, 199.0 / 255.0,1.0], [0,1], 2), + drawGradient = YES; + + while (i--) + { + var rowRect = rects[i]; + + CGContextAddRect(context, rowRect); + + if (drawGradient) + { + var minX = CGRectGetMinX(rowRect), + minY = CGRectGetMinY(rowRect), + maxX = CGRectGetMaxX(rowRect), + maxY = CGRectGetMaxY(rowRect); + + CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0); + CGContextClosePath(context); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, minX, minY); + CGContextAddLineToPoint(context, maxX, minY); + CGContextClosePath(context); + CGContextSetStrokeColor(context, topLineColor); + CGContextStrokePath(context); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, minX, maxY); + CGContextAddLineToPoint(context, maxX, maxY - 1); + CGContextClosePath(context); + CGContextSetStrokeColor(context, bottomLineColor); + CGContextStrokePath(context); + } + } + + CGContextClosePath(context); +} +@end \ No newline at end of file diff --git a/Tests/Manual/TableTest/GroupRowTest/Info.plist b/Tests/Manual/TableTest/GroupRowTest/Info.plist new file mode 100644 index 000000000..481d76e99 --- /dev/null +++ b/Tests/Manual/TableTest/GroupRowTest/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + ScrollPerformanceTest + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/TableTest/GroupRowTest/Jakefile b/Tests/Manual/TableTest/GroupRowTest/Jakefile new file mode 100644 index 000000000..6ba1b7c8b --- /dev/null +++ b/Tests/Manual/TableTest/GroupRowTest/Jakefile @@ -0,0 +1,93 @@ +/* + * Jakefile + * ScrollPerformanceTest + * + * Created by You on August 27, 2010. + * Copyright 2010, Your Company All rights reserved. + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"); + +app ("ScrollPerformanceTest", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "ScrollPerformanceTest.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("ScrollPerformanceTest"); + task.setIdentifier("com.yourcompany.ScrollPerformanceTest"); + task.setVersion("1.0"); + task.setAuthor("Your Company"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("ScrollPerformanceTest"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", ["ScrollPerformanceTest"], function() +{ + printResults(configuration); +}); + +task ("build", ["default"]); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", "ScrollPerformanceTest", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "ScrollPerformanceTest", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "ScrollPerformanceTest")); + OS.system(["press", "-f", FILE.join("Build", "Release", "ScrollPerformanceTest"), FILE.join("Build", "Deployment", "ScrollPerformanceTest")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "ScrollPerformanceTest")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "ScrollPerformanceTest"), FILE.join("Build", "Desktop", "ScrollPerformanceTest", "ScrollPerformanceTest.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "ScrollPerformanceTest", "ScrollPerformanceTest.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "ScrollPerformanceTest")); + print("----------------------------"); +} diff --git a/Tests/Manual/TableTest/GroupRowTest/Resources/spinner.gif b/Tests/Manual/TableTest/GroupRowTest/Resources/spinner.gif new file mode 100644 index 000000000..06dbc2bc2 Binary files /dev/null and b/Tests/Manual/TableTest/GroupRowTest/Resources/spinner.gif differ diff --git a/Tests/Manual/TableTest/GroupRowTest/index-debug.html b/Tests/Manual/TableTest/GroupRowTest/index-debug.html new file mode 100644 index 000000000..a5adac35c --- /dev/null +++ b/Tests/Manual/TableTest/GroupRowTest/index-debug.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + ScrollPerformanceTest + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + diff --git a/Tests/Manual/TableTest/GroupRowTest/index.html b/Tests/Manual/TableTest/GroupRowTest/index.html new file mode 100644 index 000000000..3324fc6fd --- /dev/null +++ b/Tests/Manual/TableTest/GroupRowTest/index.html @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + ScrollPerformanceTest + + + + + + + + + + + + +
+
+ + + +
+
+ + + + diff --git a/Tests/Manual/TableTest/GroupRowTest/main.j b/Tests/Manual/TableTest/GroupRowTest/main.j new file mode 100644 index 000000000..48ff040f7 --- /dev/null +++ b/Tests/Manual/TableTest/GroupRowTest/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * ScrollPerformanceTest + * + * Created by You on August 27, 2010. + * Copyright 2010, Your Company All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +}