Tk Source Code

Check-in [1708ef56]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:All on Tk/Cocoa: Improve view performance during resizing; implement custom drawing of scroller to remove flickering and ghosted appearance during window operations; reduce flickering of menubutton during resizing, but do not completely eliminate ghosted rendering when widget is unmapped
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-5-branch
Files: files | file ages | folders
SHA1: 1708ef569d8ec06584be22668819e086259080f0
User & Date: kevin_walzer 2014-12-24 04:44:16
Context
2014-12-24
04:48
Add method to tkMacOSXButton.c check-in: afb02fa2 user: kevin_walzer tags: core-8-5-branch
04:44
All on Tk/Cocoa: Improve view performance during resizing; implement custom drawing of scroller to remove flickering and ghosted appearance during window operations; reduce flickering of menubutton during resizing, but do not completely eliminate ghosted rendering when widget is unmapped check-in: 1708ef56 user: kevin_walzer tags: core-8-5-branch
2014-12-21
04:14
Minor optimization of drawing code in OSX check-in: 1de71ea9 user: kevin_walzer tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to macosx/tkMacOSXScrlbr.c.

1
2
3
4
5
6
7
8
9


10
11
12
13
14
15
16
/*
 * tkMacOSXScrollbar.c --
 *
 *	This file implements the Macintosh specific portion of the scrollbar
 *	widget.
 *
 * Copyright (c) 1996 by Sun Microsystems, Inc.
 * Copyright 2001-2009, Apple Inc.
 * Copyright (c) 2006-2009 Daniel A. Steffen <[email protected]>


 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tkMacOSXPrivate.h"
#include "tkScrollbar.h"









>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * tkMacOSXScrollbar.c --
 *
 *	This file implements the Macintosh specific portion of the scrollbar
 *	widget.
 *
 * Copyright (c) 1996 by Sun Microsystems, Inc.
 * Copyright 2001-2009, Apple Inc.
 * Copyright (c) 2006-2009 Daniel A. Steffen <[email protected]>
*  Copyright (c) 2014 Marc Culler.
 * Copyright (c) 2014 Kevin Walzer/WordTech Commununications LLC.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tkMacOSXPrivate.h"
#include "tkScrollbar.h"
27
28
29
30
31
32
33





34

35
36
37
38





39
40
41
42
43
44
45
 * A subclass of NSScroller with sanity checking:
 *
 * NSScrollers created by Tk will have their tag set to a pointer to the
 * TkScrollbar which manages the NSScroller.  This allows an NSScroller to be
 * aware of the state of its Tk parent.  This subclass overrides the drawRect
 * method so that it will not draw itself if the widget is completely outside
 * of its container.





 */


@interface TkNSScroller: NSScroller
-(void) drawRect:(NSRect)dirtyRect;






@end

@implementation TkNSScroller

    - (void)drawRect:(NSRect)dirtyRect
    {
	NSInteger tag = [self tag];







>
>
>
>
>

>



|
>
>
>
>
>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 * A subclass of NSScroller with sanity checking:
 *
 * NSScrollers created by Tk will have their tag set to a pointer to the
 * TkScrollbar which manages the NSScroller.  This allows an NSScroller to be
 * aware of the state of its Tk parent.  This subclass overrides the drawRect
 * method so that it will not draw itself if the widget is completely outside
 * of its container.
 *
 * Custom drawing of the knob seems to work around the flickering visible after  
 * private API's were removed. Based on technique outlined at 
 * http://stackoverflow.com/questions/1604682/nsscroller-
 * graphical-glitches-lag. Only supported on 10.7 and above.
 */
 

@interface TkNSScroller: NSScroller
-(void) drawRect:(NSRect)dirtyRect;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
-(BOOL) isHorizontal;
-(void) drawKnob;
- (void)drawArrow:(NSScrollerArrow)arrow highlightPart:(int)flag;
- (void)drawKnobSlotInRect:(NSRect)rect highlight:(BOOL)highlight;
#endif
@end

@implementation TkNSScroller

    - (void)drawRect:(NSRect)dirtyRect
    {
	NSInteger tag = [self tag];
73
74
75
76
77
78
79






































80
81
82
83
84
85
86
		if (x > parent_width || x + widget_width < 0) {
		    return;
		}
	    }
	}
	[super drawRect:dirtyRect];
    }







































@end



/*
 * Declaration of Mac specific scrollbar structure.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
		if (x > parent_width || x + widget_width < 0) {
		    return;
		}
	    }
	}
	[super drawRect:dirtyRect];
    }
    
 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
- (BOOL)isHorizontal {
    NSRect bounds = [self bounds];
    return NSWidth(bounds) < NSHeight (bounds);
}


- (void)drawKnob
{
    NSRect knobRect = [self rectForPart:NSScrollerKnob];
    
    if ([self isHorizontal]) {
	NSRect newRect = NSMakeRect(knobRect.origin.x, knobRect.origin.y, knobRect.size.width - 5, knobRect.size.height);
	NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:newRect xRadius:7 yRadius:7];
 
        [[NSColor lightGrayColor] set];
	[path fill];
    } else {
	NSRect newRect = NSMakeRect(knobRect.origin.x, knobRect.origin.y, knobRect.size.width, knobRect.size.height - 5);
	NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:newRect xRadius:7 yRadius:7];
 
    [[NSColor lightGrayColor] set];
	[path fill];
    }
       
}

- (void)drawArrow:(NSScrollerArrow)arrow highlightPart:(int)flag
{
    // We don't want arrows
}

- (void)drawKnobSlotInRect:(NSRect)rect highlight:(BOOL)highlight
{

}
#endif   

@end



/*
 * Declaration of Mac specific scrollbar structure.

Changes to macosx/tkMacOSXWindowEvent.c.

767
768
769
770
771
772
773


774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
 * during the normal AppKit drawing cycle, we supress drawing of all subviews
 * and instead send Expose events about the subviews that would be redrawn.
 */

@interface TKContentView(TKWindowEvent)
- (void) drawRect: (NSRect) rect;
- (void) generateExposeEvents: (HIMutableShapeRef) shape;


- (void) viewDidEndLiveResize;
- (void) viewWillDraw;
- (BOOL) isOpaque;
- (BOOL) wantsDefaultClipping;
- (BOOL) acceptsFirstResponder;
- (void) keyDown: (NSEvent *) theEvent;
@end

@implementation TKContentView
@end

double drawTime;

/*
 * Set a minimum time for drawing to render. With removal of private NSView API's, default drawing
 * is slower and less responsive. This number, which seems feasible after some experimentatation, skips
 * some drawing to avoid lag. 
 */

#define MAX_DYNAMIC_TIME .000000001

/*Restrict event processing to Expose events.*/
static Tk_RestrictAction
ExposeRestrictProc(
    ClientData arg,
    XEvent *eventPtr)
{







>
>











<
<
<
<
<
<
<
<
<







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786









787
788
789
790
791
792
793
 * during the normal AppKit drawing cycle, we supress drawing of all subviews
 * and instead send Expose events about the subviews that would be redrawn.
 */

@interface TKContentView(TKWindowEvent)
- (void) drawRect: (NSRect) rect;
- (void) generateExposeEvents: (HIMutableShapeRef) shape;
- (BOOL) preservesContentDuringLiveResize;
- (void) viewWillStartLiveResize;
- (void) viewDidEndLiveResize;
- (void) viewWillDraw;
- (BOOL) isOpaque;
- (BOOL) wantsDefaultClipping;
- (BOOL) acceptsFirstResponder;
- (void) keyDown: (NSEvent *) theEvent;
@end

@implementation TKContentView
@end











/*Restrict event processing to Expose events.*/
static Tk_RestrictAction
ExposeRestrictProc(
    ClientData arg,
    XEvent *eventPtr)
{
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844

845

846



847
848
849
850
851
852
853
854
855
856
857


858
859


860

861
862
863
864
865
866
867
#ifdef TK_MAC_DEBUG_DRAWING
    TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(rect));
    [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:.1] setFill];
    NSRectFillListUsingOperation(rectsBeingDrawn, rectsBeingDrawnCount,
	    NSCompositeSourceOver);
#endif

    NSDate *beginTime=[NSDate date];

    /*Skip drawing during live resize if redraw is too slow.*/
    if([self inLiveResize] && drawTime>MAX_DYNAMIC_TIME) return;

    CGFloat height = [self bounds].size.height;
    HIMutableShapeRef drawShape = HIShapeCreateMutable();

    while (rectsBeingDrawnCount--) {
	CGRect r = NSRectToCGRect(*rectsBeingDrawn++);

	r.origin.y = height - (r.origin.y + r.size.height);
	HIShapeUnionWithRect(drawShape, &r);
    }
    if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) {
	[self generateExposeEvents:drawShape];
    } else {
	[self performSelectorOnMainThread:@selector(generateExposeEvents:)
		withObject:(id)drawShape waitUntilDone:NO
		modes:[NSArray arrayWithObjects:NSRunLoopCommonModes,
			NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode,
			nil]];
    }
    CFRelease(drawShape);

    drawTime=-[beginTime timeIntervalSinceNow];

    [super setNeedsDisplayInRect:rect];



}

/*At conclusion of resize event, send notification and set view for redraw if earlier drawing was skipped because of lagginess.*/
- (void)viewDidEndLiveResize
{
    if(drawTime>MAX_DYNAMIC_TIME) {
    [self setNeedsDisplay:YES];
    [super viewDidEndLiveResize];
    }
}



-(void) viewWillDraw  {
	[self setNeedsDisplay:YES];


    } 


- (void) generateExposeEvents: (HIMutableShapeRef) shape
{

    TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
    unsigned long serial;
    CGRect updateBounds;







<
<
<
<
<



















>
|
>
|
>
>
>


<
|

<
<
|
|
|

>
>
|
|
>
>
|
>







807
808
809
810
811
812
813





814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841

842
843


844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
#ifdef TK_MAC_DEBUG_DRAWING
    TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(rect));
    [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:.1] setFill];
    NSRectFillListUsingOperation(rectsBeingDrawn, rectsBeingDrawnCount,
	    NSCompositeSourceOver);
#endif






    CGFloat height = [self bounds].size.height;
    HIMutableShapeRef drawShape = HIShapeCreateMutable();

    while (rectsBeingDrawnCount--) {
	CGRect r = NSRectToCGRect(*rectsBeingDrawn++);

	r.origin.y = height - (r.origin.y + r.size.height);
	HIShapeUnionWithRect(drawShape, &r);
    }
    if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) {
	[self generateExposeEvents:drawShape];
    } else {
	[self performSelectorOnMainThread:@selector(generateExposeEvents:)
		withObject:(id)drawShape waitUntilDone:NO
		modes:[NSArray arrayWithObjects:NSRunLoopCommonModes,
			NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode,
			nil]];
    }
    CFRelease(drawShape);
}

/*Provide more fine-grained control over resizing of content to reduce flicker after removal of private API's.*/

- (BOOL) preservesContentDuringLiveResize
{
    return YES;
}


- (void)viewWillStartLiveResize
{


  [super viewWillStartLiveResize];
}


- (void)viewDidEndLiveResize
{

    [self setNeedsDisplay:YES];
    [super setNeedsDisplay:YES];
    [super viewDidEndLiveResize];
     
}

- (void) generateExposeEvents: (HIMutableShapeRef) shape
{

    TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
    unsigned long serial;
    CGRect updateBounds;