diff --git a/Source/NSView.m b/Source/NSView.m index 1d09f9627..d4ed731a1 100644 --- a/Source/NSView.m +++ b/Source/NSView.m @@ -2601,6 +2601,16 @@ - (void) displayIfNeededInRectIgnoringOpacity: (NSRect)aRect isect = [subview convertRect: isect fromView: self]; [subview displayIfNeededInRectIgnoringOpacity: isect]; } + else if (NSIsEmptyRect(subviewFrame)) + { + /* A subview with no area has nothing to draw, and is + never reached by the line above, so it would keep + itself and every view over it waiting for a display + that cannot happen. Let it settle its own flag. + */ + [subview displayIfNeededInRectIgnoringOpacity: + NSZeroRect]; + } if (subview->_rFlags.needs_display) { @@ -2729,6 +2739,17 @@ - (void) displayRectIgnoringOpacity: (NSRect)aRect [subview displayRectIgnoringOpacity: isect inContext: context]; } + else if (NSIsEmptyRect(subviewFrame) + && subview->_rFlags.needs_display == YES) + { + /* A subview with no area has nothing to draw and is never + reached by the line above, so it would keep itself and + every view over it waiting for a display that cannot + happen. Let it settle its own flag. + */ + [subview displayRectIgnoringOpacity: NSZeroRect + inContext: context]; + } /* * Is there still something to draw in the subview? * This keeps the invariant that views further up are marked diff --git a/Tests/gui/NSView/emptyViewNeedsDisplay.m b/Tests/gui/NSView/emptyViewNeedsDisplay.m new file mode 100644 index 000000000..121527d00 --- /dev/null +++ b/Tests/gui/NSView/emptyViewNeedsDisplay.m @@ -0,0 +1,101 @@ +/* A view with no area cannot draw anything. Marking it for display and + then displaying the window has to leave it, and every view above it, + clear again; otherwise nothing in that branch of the tree ever reports + itself as drawn. A view that has an area but falls outside the rectangle + being displayed is a different matter and keeps its mark. Drawing needs + the backend, so the set is skipped without one. +*/ +#include "Testing.h" + +#include +#include + +#include +#include +#include +#include + +int +main(int argc, char **argv) +{ + NSWindow *window; + NSView *content; + NSView *flat; + NSView *aside; + NSBox *box; + + START_SET("NSView empty view needs display") + + NS_DURING + { + [NSApplication sharedApplication]; + } + NS_HANDLER + { + if ([[localException name] isEqualToString: NSInternalInconsistencyException]) + SKIP("It looks like GNUstep backend is not yet installed") + } + NS_ENDHANDLER + + NS_DURING + { + window = AUTORELEASE([[NSWindow alloc] + initWithContentRect: NSMakeRect(0, 0, 200, 200) + styleMask: NSWindowStyleMaskBorderless + backing: NSBackingStoreBuffered + defer: NO]); + content = [window contentView]; + + flat = AUTORELEASE([[NSView alloc] + initWithFrame: NSMakeRect(10, 10, 100, 0)]); + [content addSubview: flat]; + [flat setNeedsDisplay: YES]; + [window orderFront: nil]; + [window display]; + + PASS([flat needsDisplay] == NO, + "a view with no area does not wait for a display"); + PASS([content needsDisplay] == NO, + "the view holding it does not wait either"); + PASS([window viewsNeedDisplay] == NO, + "the window has nothing left to display"); + + /* A box two pixels high gives its content view no room at all, which + is how this turns up in a real window. */ + box = AUTORELEASE([[NSBox alloc] + initWithFrame: NSMakeRect(10, 100, 180, 2)]); + [box setTitlePosition: NSNoTitle]; + [content addSubview: box]; + [window display]; + + PASS([[box contentView] needsDisplay] == NO, + "the content view of a box too short for it is not left waiting"); + PASS([box needsDisplay] == NO, "nor is the box"); + PASS([content needsDisplay] == NO, "nor is the view holding the box"); + + /* A view with an area that the displayed rectangle does not reach + keeps its mark, since it still has something to draw. */ + aside = AUTORELEASE([[NSView alloc] + initWithFrame: NSMakeRect(150, 150, 20, 20)]); + [content addSubview: aside]; + [window display]; + [aside setNeedsDisplay: YES]; + [content displayIfNeededInRect: NSMakeRect(0, 0, 40, 40)]; + + PASS([aside needsDisplay] == YES, + "a view outside the rectangle being displayed still waits"); + } + NS_HANDLER + { + if ([[localException name] isEqualToString: NSInternalInconsistencyException] + || [[localException name] isEqualToString: @"NSWindowServerCommunicationException"]) + SKIP("No display available") + else + [localException raise]; + } + NS_ENDHANDLER + + END_SET("NSView empty view needs display") + + return 0; +}