alexrozanski / PXSourceList

Source List control for the Mac
Other
638 stars 76 forks source link

Custom background redraws oddly during show/hide #29

Open mindersw opened 11 years ago

mindersw commented 11 years ago

I've added the below method to PXSourceList.m to customize the background. When clicking Show/Hide on a parent row, the background has some redraw issues while collapsing or expanding. Before and after the Show/Hide operation the view is rendered fine, the problem just happens during the operation's animation.

- (void)drawBackgroundInClipRect:(NSRect)dirtyRect {

    // Added this to customize outline view background

    //// Color Declarations
    NSColor* lightColor = [NSColor colorWithCalibratedRed: 0.701 green: 0.784 blue: 0.811 alpha: 1];
    NSColor* transWhite = [NSColor colorWithCalibratedRed: 1 green: 1 blue: 1 alpha: 0.571];
    NSColor* dkGray = [NSColor colorWithCalibratedRed: 0.4 green: 0.4 blue: 0.4 alpha: 1];

    //// Gradient Declarations
    NSGradient* transWhiteGradient = [[NSGradient alloc] initWithColorsAndLocations:
                                      transWhite, 0.01,
                                      [NSColor colorWithCalibratedRed: 0.799 green: 0.913 blue: 0.95 alpha: 0.785], 0.05,
                                      lightColor, 0.37, nil];

    //// Shadow Declarations
    NSShadow* shadow = [[NSShadow alloc] init];
    [shadow setShadowColor: dkGray];
    [shadow setShadowOffset: NSMakeSize(0.1, -3.1)];
    [shadow setShadowBlurRadius: 5.5];

    //// Frames
    //NSRect dirtyRect = NSMakeRect(0, 0, 214, 727);

    //// baseRect Drawing
    NSBezierPath* baseRectPath = [NSBezierPath bezierPathWithRect: NSMakeRect(NSMinX(dirtyRect), NSMinY(dirtyRect), NSWidth(dirtyRect), NSHeight(dirtyRect))];
    [lightColor setFill];
    [baseRectPath fill];

    ////// baseRect Inner Shadow
    NSRect baseRectBorderRect = NSInsetRect([baseRectPath bounds], -shadow.shadowBlurRadius, -shadow.shadowBlurRadius);
    baseRectBorderRect = NSOffsetRect(baseRectBorderRect, -shadow.shadowOffset.width, shadow.shadowOffset.height);
    baseRectBorderRect = NSInsetRect(NSUnionRect(baseRectBorderRect, [baseRectPath bounds]), -1, -1);

    NSBezierPath* baseRectNegativePath = [NSBezierPath bezierPathWithRect: baseRectBorderRect];
    [baseRectNegativePath appendBezierPath: baseRectPath];
    [baseRectNegativePath setWindingRule: NSEvenOddWindingRule];

    [NSGraphicsContext saveGraphicsState];
    {
        NSShadow* shadowWithOffset = [shadow copy];
        CGFloat xOffset = shadowWithOffset.shadowOffset.width + round(baseRectBorderRect.size.width);
        CGFloat yOffset = shadowWithOffset.shadowOffset.height;
        shadowWithOffset.shadowOffset = NSMakeSize(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset));
        [shadowWithOffset set];
        [[NSColor grayColor] setFill];
        [baseRectPath addClip];
        NSAffineTransform* transform = [NSAffineTransform transform];
        [transform translateXBy: -round(baseRectBorderRect.size.width) yBy: 0];
        [[transform transformBezierPath: baseRectNegativePath] fill];
    }
    [NSGraphicsContext restoreGraphicsState];

    [dkGray setStroke];
    [baseRectPath setLineWidth: 1];
    [baseRectPath stroke];

    //// topRect Drawing
    NSBezierPath* topRectPath = [NSBezierPath bezierPathWithRect: NSMakeRect(NSMinX(dirtyRect), NSMinY(dirtyRect) - 1, NSWidth(dirtyRect), NSHeight(dirtyRect) + 2)];
    [transWhiteGradient drawInBezierPath: topRectPath angle: 90];

}
alexrozanski commented 11 years ago

Do you have a sample project or video showing what happens?

mindersw commented 11 years ago

Just used the attached class in place of the existing one in the example project. Note the drawBackgroundInClipRect method in the Drawing section, used to customize the background. I realize this isn't part of your release class, but I think it's important to get working correctly as people do more advanced things with their UIs.


Michael A. LaMorte Mac Developer Minder Softworks

On Oct 25, 2013, at 7:05 AM, Alex Rozanski notifications@github.com wrote:

Do you have a sample project or video showing what happens?

— Reply to this email directly or view it on GitHub.

alexrozanski commented 11 years ago

It looks like the issue here is that the area you're drawing into is based on the dirtyRect parameter that is passed in. The drawing issues are occurring because this method is often called to redraw small sections of the background (which are passed in as the dirtyRect), which you're drawing into as if you were drawing the background of the entire view.

If you instead always redraw the entire background in this method then you don't get (most of) these drawing issues (as a quick test I just set dirtyRect to self.enclosingScrollView.bounds in the first line of the method). It's less efficient, but since you're working with gradients and shadows it would be tricky to do this more efficiently when only redrawing small areas.

For some reason, there is still a small flash of incorrect drawing when expanding and collapsing a section which I couldn't quite figure out. Let me know if you make progress with that.