From b6ce7c756f63de03ea2d87aaa733751610ee2dee Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Tue, 21 Jul 2009 21:43:13 +0000 Subject: - Improve the TextAndLinkCell (foreign key link arrows) to make the arrows behave like proper buttons, allowing clicking and dragging out to cancel the click, and highlight states --- .../Images/link-arrow-highlighted-clicked.png | Bin 0 -> 249 bytes Resources/Images/link-arrow-highlighted.png | Bin 249 -> 271 bytes Source/SPTextAndLinkCell.h | 1 - Source/SPTextAndLinkCell.m | 71 ++++++++++++++++++++- sequel-pro.xcodeproj/project.pbxproj | 16 +++-- 5 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 Resources/Images/link-arrow-highlighted-clicked.png diff --git a/Resources/Images/link-arrow-highlighted-clicked.png b/Resources/Images/link-arrow-highlighted-clicked.png new file mode 100644 index 00000000..5ce39b58 Binary files /dev/null and b/Resources/Images/link-arrow-highlighted-clicked.png differ diff --git a/Resources/Images/link-arrow-highlighted.png b/Resources/Images/link-arrow-highlighted.png index 5ce39b58..ce1d6b14 100644 Binary files a/Resources/Images/link-arrow-highlighted.png and b/Resources/Images/link-arrow-highlighted.png differ diff --git a/Source/SPTextAndLinkCell.h b/Source/SPTextAndLinkCell.h index 48d6bb18..7cd34c3f 100644 --- a/Source/SPTextAndLinkCell.h +++ b/Source/SPTextAndLinkCell.h @@ -39,7 +39,6 @@ enum sptextandlinkcell_drawstates id linkTarget; SEL linkAction; - NSRect linkRect; int lastLinkColumn; int lastLinkRow; int drawState; diff --git a/Source/SPTextAndLinkCell.m b/Source/SPTextAndLinkCell.m index 2a57c8b3..d1e8338e 100644 --- a/Source/SPTextAndLinkCell.m +++ b/Source/SPTextAndLinkCell.m @@ -28,6 +28,13 @@ @implementation SPTextAndLinkCell +/** + * Provide a method to derive the link rect from a cell rect. + */ +static inline NSRect SPTextLinkRectFromCellRect(NSRect inRect) { + return NSMakeRect(inRect.origin.x + inRect.size.width - 15, inRect.origin.y - 1, 12, inRect.size.height); +} + #pragma mark - #pragma mark Setup and teardown @@ -99,6 +106,7 @@ [linkButton setBordered:NO]; [linkButton setShowsBorderOnlyWhileMouseInside:YES]; [linkButton setImage:[NSImage imageNamed:@"link-arrow"]]; + [linkButton setAlternateImage:[NSImage imageNamed:@"link-arrow-clicked"]]; } } @@ -120,7 +128,7 @@ // Set up new rects NSRect textRect = NSMakeRect(aRect.origin.x, aRect.origin.y, aRect.size.width - 18, aRect.size.height); - linkRect = NSMakeRect(aRect.origin.x + aRect.size.width - 15, aRect.origin.y - 1, 12, aRect.size.height); + NSRect linkRect = SPTextLinkRectFromCellRect(aRect); // Draw the text [super drawInteriorWithFrame:textRect inView:controlView]; @@ -138,12 +146,15 @@ switch (drawState) { case SP_LINKDRAWSTATE_NORMAL: [linkButton setImage:[NSImage imageNamed:@"link-arrow"]]; + [linkButton setAlternateImage:[NSImage imageNamed:@"link-arrow-clicked"]]; break; case SP_LINKDRAWSTATE_HIGHLIGHT: [linkButton setImage:[NSImage imageNamed:@"link-arrow-highlighted"]]; + [linkButton setAlternateImage:[NSImage imageNamed:@"link-arrow-highlighted-clicked"]]; break; case SP_LINKDRAWSTATE_BACKGROUNDHIGHLIGHT: [linkButton setImage:[NSImage imageNamed:@"link-arrow-clicked"]]; + [linkButton setAlternateImage:[NSImage imageNamed:@"link-arrow"]]; break; } } @@ -161,6 +172,7 @@ if (!hasLink) return NSCellHitContentArea | NSCellHitEditableTextArea; NSPoint p = [[[NSApp mainWindow] contentView] convertPoint:[event locationInWindow] toView:controlView]; + NSRect linkRect = SPTextLinkRectFromCellRect(cellFrame); // Hit the link if it falls within the link rectangle for this cell, set when drawing if (p.x > linkRect.origin.x && p.x < (linkRect.origin.x + linkRect.size.width)) { @@ -171,8 +183,8 @@ lastLinkColumn = [tableView columnAtPoint:p]; lastLinkRow = [tableView rowAtPoint:p]; - [linkTarget performSelector:linkAction withObject:self]; - return NSCellHitContentArea; + // Return a trackable hit + return NSCellHitContentArea | NSCellHitTrackableArea; // Otherwise return an editable hit - this allows the entire cell to be clicked to edit the contents. } else { @@ -180,6 +192,59 @@ } } +/** + * Allow mouse tracking within the button cell, to support expected click + * behaviour in the button cell. + */ +- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp +{ + + // Fast case for no link + if (!hasLink) return [super trackMouse:theEvent inRect:cellFrame ofView:controlView untilMouseUp:untilMouseUp]; + + NSPoint p = [controlView convertPoint:[theEvent locationInWindow] fromView:nil]; + NSRect linkRect = SPTextLinkRectFromCellRect(cellFrame); + + // Fast path for if not in button rect - just pass to super + if (!NSMouseInRect(p, linkRect, [controlView isFlipped])) + return [super trackMouse:theEvent inRect:cellFrame ofView:controlView untilMouseUp:untilMouseUp]; + + // Continue tracking the mouse while it's down, updating the state as it enters and leaves the cell, + // until it is released; if still within the cell, follow the link. + BOOL mouseInButton = YES; + while (1) { + if (mouseInButton) { + + // Highlight the button + [linkButton highlight:YES withFrame:linkRect inView:controlView]; + + // Continue to track until mouse completes a click or exits the cell while still down + BOOL mouseClicked = [linkButton trackMouse:theEvent inRect:linkRect ofView:controlView untilMouseUp:NO]; + if (mouseClicked) { + + // Remove highlight, and follow the link + [linkButton highlight:NO withFrame:linkRect inView:controlView]; + [linkTarget performSelector:linkAction withObject:self]; + return YES; + } + + // Mouse has exited the cell. Remove highlight. + mouseInButton = NO; + [linkButton highlight:NO withFrame:linkRect inView:controlView]; + } + + // Keep tracking the mouse outside the button, until the mouse button is released or it reenters the button + theEvent = [[controlView window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask]; + p = [controlView convertPoint:[theEvent locationInWindow] fromView:nil]; + mouseInButton = NSMouseInRect(p, linkRect, [controlView isFlipped]); + + // If the event is a mouse release, break the loop. + if ([theEvent type] == NSLeftMouseUp) break; + } + + return YES; +} + #pragma mark - #pragma mark Information getters diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index 4b1e79c2..729133f9 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -127,6 +127,7 @@ 4DECC3370EC2A170008D359E /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DECC3340EC2A170008D359E /* Growl.framework */; }; 4DECC48F0EC2B436008D359E /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3320EC2A170008D359E /* Sparkle.framework */; }; 4DECC4910EC2B436008D359E /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4DECC3340EC2A170008D359E /* Growl.framework */; }; + 581068B61015411B0068C6E2 /* link-arrow-highlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = 581068B51015411B0068C6E2 /* link-arrow-highlighted.png */; }; 58186D210F4CB38900851FE9 /* ConnectionErrorDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58186D1F0F4CB38900851FE9 /* ConnectionErrorDialog.xib */; }; 5822C9B51000DB2400DCC3D6 /* SPConnectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5822C9B41000DB2400DCC3D6 /* SPConnectionController.m */; }; 5822CAE110011C8000DCC3D6 /* ConnectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5822CADF10011C8000DCC3D6 /* ConnectionView.xib */; }; @@ -146,7 +147,7 @@ 58CDB3420FCE142500F8ACA3 /* KeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E641740EF01F80001BC333 /* KeyChain.m */; }; 58D2E229101222670063EF1D /* SPTextAndLinkCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 58D2E227101222670063EF1D /* SPTextAndLinkCell.m */; }; 58D2E22E101222870063EF1D /* link-arrow-clicked.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22B101222870063EF1D /* link-arrow-clicked.png */; }; - 58D2E22F101222870063EF1D /* link-arrow-highlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22C101222870063EF1D /* link-arrow-highlighted.png */; }; + 58D2E22F101222870063EF1D /* link-arrow-highlighted-clicked.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */; }; 58D2E230101222870063EF1D /* link-arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22D101222870063EF1D /* link-arrow.png */; }; 58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF16C0F23D66600518E8E /* SPSQLParser.m */; }; 58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; }; @@ -220,7 +221,7 @@ isa = PBXContainerItemProxy; containerPortal = 2A37F4A9FDCFA73011CA2CEA /* Project object */; proxyType = 1; - remoteGlobalIDString = 17B7B5611016012700F057DE /* MCPKit */; + remoteGlobalIDString = 17B7B5611016012700F057DE; remoteInfo = MCPKit; }; 380F4EDF0FC0B51D00B0BFD7 /* PBXContainerItemProxy */ = { @@ -471,6 +472,7 @@ 387BBBA70FBCB6CB00B31746 /* SPTableRelations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableRelations.m; sourceTree = ""; }; 4DECC3320EC2A170008D359E /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Sparkle.framework; sourceTree = ""; }; 4DECC3340EC2A170008D359E /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = Frameworks/Growl.framework; sourceTree = ""; }; + 581068B51015411B0068C6E2 /* link-arrow-highlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow-highlighted.png"; sourceTree = ""; }; 58186D200F4CB38900851FE9 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Interfaces/English.lproj/ConnectionErrorDialog.xib; sourceTree = ""; }; 5822C9B31000DB2400DCC3D6 /* SPConnectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPConnectionController.h; sourceTree = ""; }; 5822C9B41000DB2400DCC3D6 /* SPConnectionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPConnectionController.m; sourceTree = ""; }; @@ -494,7 +496,7 @@ 58D2E227101222670063EF1D /* SPTextAndLinkCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTextAndLinkCell.m; sourceTree = ""; }; 58D2E228101222670063EF1D /* SPTextAndLinkCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTextAndLinkCell.h; sourceTree = ""; }; 58D2E22B101222870063EF1D /* link-arrow-clicked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow-clicked.png"; sourceTree = ""; }; - 58D2E22C101222870063EF1D /* link-arrow-highlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow-highlighted.png"; sourceTree = ""; }; + 58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow-highlighted-clicked.png"; sourceTree = ""; }; 58D2E22D101222870063EF1D /* link-arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow.png"; sourceTree = ""; }; 58FEF16B0F23D66600518E8E /* SPSQLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLParser.h; sourceTree = ""; }; 58FEF16C0F23D66600518E8E /* SPSQLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSQLParser.m; sourceTree = ""; }; @@ -967,8 +969,8 @@ 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */, BC1847E80FE6EC8400094BFB /* SPEditSheetTextView.h */, BC1847E90FE6EC8400094BFB /* SPEditSheetTextView.m */, - 58D2E227101222670063EF1D /* SPTextAndLinkCell.m */, 58D2E228101222670063EF1D /* SPTextAndLinkCell.h */, + 58D2E227101222670063EF1D /* SPTextAndLinkCell.m */, ); name = GUI; sourceTree = ""; @@ -1018,7 +1020,8 @@ 17E6419F0EF02036001BC333 /* hideconsole.tiff */, 58D2E22D101222870063EF1D /* link-arrow.png */, 58D2E22B101222870063EF1D /* link-arrow-clicked.png */, - 58D2E22C101222870063EF1D /* link-arrow-highlighted.png */, + 581068B51015411B0068C6E2 /* link-arrow-highlighted.png */, + 58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */, 17E641A20EF02036001BC333 /* logo-48.png */, 17E641AE0EF02036001BC333 /* selectall.tiff */, 17E641AF0EF02036001BC333 /* selectnone.tiff */, @@ -1440,9 +1443,10 @@ BC1E55C4100DC92200AAE9F0 /* table-view-small-square.tiff in Resources */, BCA6F631100FA7D700E80253 /* FieldEditorSheet.xib in Resources */, 58D2E22E101222870063EF1D /* link-arrow-clicked.png in Resources */, - 58D2E22F101222870063EF1D /* link-arrow-highlighted.png in Resources */, + 58D2E22F101222870063EF1D /* link-arrow-highlighted-clicked.png in Resources */, 58D2E230101222870063EF1D /* link-arrow.png in Resources */, BC688D811012462600D35128 /* borderlessbackground.png in Resources */, + 581068B61015411B0068C6E2 /* link-arrow-highlighted.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; -- cgit v1.2.3