diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 570a5e965..50d8900b5 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -2359,13 +2359,11 @@ setBoundsOrigin: */ - (BOOL)scrollRectToVisible:(CGRect)aRect { - var visibleRect = [self visibleRect]; - // Make sure we have a rect that exists. aRect = CGRectIntersection(aRect, _bounds); - // If aRect is empty or is already visible then no scrolling required. - if (_CGRectIsEmpty(aRect) || CGRectContainsRect(visibleRect, aRect)) + // If aRect is empty no scrolling required. + if (CGRectIsEmpty(aRect)) return NO; var enclosingClipView = [self _enclosingClipView]; @@ -2374,20 +2372,34 @@ setBoundsOrigin: if (!enclosingClipView) return NO; - var scrollPoint = _CGPointMakeCopy(visibleRect.origin); + var documentView = [enclosingClipView documentView]; + + // If the clip view doesn't have a document view, then there isn't much we can do. + if (!documentView) + return NO; + + // Get the document view visible rect and convert aRect to the document view's coordinate system + var documentViewVisibleRect = [documentView visibleRect], + rectInDocumentView = [self convertRect:aRect toView:documentView]; + + // If already visible then no scrolling required. + if (CGRectContainsRect(documentViewVisibleRect, rectInDocumentView)) + return NO; + + var scrollPoint = CGPointMakeCopy(documentViewVisibleRect.origin); // One of the following has to be true since our current visible rect didn't contain aRect. - if (_CGRectGetMinX(aRect) <= _CGRectGetMinX(visibleRect)) - scrollPoint.x = _CGRectGetMinX(aRect); - else if (_CGRectGetMaxX(aRect) > _CGRectGetMaxX(visibleRect)) - scrollPoint.x += _CGRectGetMaxX(aRect) - _CGRectGetMaxX(visibleRect); + if (_CGRectGetMinX(rectInDocumentView) < _CGRectGetMinX(documentViewVisibleRect)) + scrollPoint.x = _CGRectGetMinX(rectInDocumentView); + else if (_CGRectGetMaxX(rectInDocumentView) > _CGRectGetMaxX(documentViewVisibleRect)) + scrollPoint.x += _CGRectGetMaxX(rectInDocumentView) - _CGRectGetMaxX(documentViewVisibleRect); - if (_CGRectGetMinY(aRect) <= _CGRectGetMinY(visibleRect)) - scrollPoint.y = CGRectGetMinY(aRect); - else if (_CGRectGetMaxY(aRect) > _CGRectGetMaxY(visibleRect)) - scrollPoint.y += _CGRectGetMaxY(aRect) - _CGRectGetMaxY(visibleRect); + if (_CGRectGetMinY(rectInDocumentView) < _CGRectGetMinY(documentViewVisibleRect)) + scrollPoint.y = _CGRectGetMinY(rectInDocumentView); + else if (_CGRectGetMaxY(rectInDocumentView) > _CGRectGetMaxY(documentViewVisibleRect)) + scrollPoint.y += _CGRectGetMaxY(rectInDocumentView) - _CGRectGetMaxY(documentViewVisibleRect); - [enclosingClipView scrollToPoint:CGPointMake(scrollPoint.x, scrollPoint.y)]; + [enclosingClipView scrollToPoint:scrollPoint]; return YES; } diff --git a/Tests/AppKit/CPScrollViewTest.j b/Tests/AppKit/CPScrollViewTest.j index 4098f4dac..d95839438 100644 --- a/Tests/AppKit/CPScrollViewTest.j +++ b/Tests/AppKit/CPScrollViewTest.j @@ -214,5 +214,53 @@ [self assert:replacementDocumentView equals:[scrollView documentView] message:@"documentView was not replaced properly"]; } +- (void)testScrollRectToVisible +{ + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], + documentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 1000, 1000)], + textField1 = [CPTextField textFieldWithStringValue:@"Martin" placeholder:@"" width:10], + textField2 = [CPTextField textFieldWithStringValue:@"Malte" placeholder:@"" width:10], + textField1Size = CGSizeMakeCopy([textField1 bounds].size), + textField2Size = CGSizeMakeCopy([textField2 bounds].size); + + [scrollView setDocumentView:documentView]; + + [textField1 setFrameOrigin:CGPointMake(0, 0)]; + [textField2 setFrameOrigin:CGPointMake(500, 500)]; + + [documentView addSubview:textField1]; + [documentView addSubview:textField2]; + + var visibleRect = [documentView visibleRect], + originalVisibleSize = CGSizeMakeCopy(visibleRect.size); + + // Make sure we are at the top left corner + [self assertPoint:CGPointMake(0, 0) equals:visibleRect.origin message:@"VisibleRect origin not at top left corner"]; + + // Make the second text field visible + [textField2 scrollRectToVisible:[textField2 bounds]]; + + var visibleRectOriginShouldBeAt = CGPointMake(500 - originalVisibleSize.width + textField2Size.width, 500 -originalVisibleSize.height + textField2Size.height); + + visibleRect = [documentView visibleRect]; + + // We should now have the text field in the lower right corner + [self assertPoint:visibleRectOriginShouldBeAt equals:visibleRect.origin message:@"Second text field not at lower right corner in visible rect"]; + + // Make the first text field visible again + [textField1 scrollRectToVisible:[textField2 bounds]]; + + visibleRect = [documentView visibleRect]; + + // We should now be back at top left corner + [self assertPoint:CGPointMake(0, 0) equals:visibleRect.origin message:@"VisibleRect origin not at top left corner again"]; +} + +- (void)assertPoint:(CGPoint)expected equals:(CGPoint)actual message:(CPString)message +{ + [self assert:expected.x equals:actual.x message:@"X: " + message]; + [self assert:expected.y equals:actual.y message:@"Y: " + message]; +} + @end