Tk Source Code

Check-in [6d14dd47]
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 | trunk
Files: files | file ages | folders
SHA1: 6d14dd4709b1e5321df079c4b93c6d4ec16c8344
User & Date: kevin_walzer 2014-12-24 04:43:40
Context
2014-12-24
04:47
Revert change from ttk Mac theme check-in: b06f91b0 user: kevin_walzer tags: trunk
04:43
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: 6d14dd47 user: kevin_walzer tags: trunk
2014-12-21
04:16
Revert unintended commit of menubutton file check-in: 399c1c0b user: kevin_walzer tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to macosx/tkMacOSXButton.c.

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 * so that it will not draw itself unless the NSButton frame matches
 * the frame which was installed by DisplayButton, and the TkButton is
 * mapped.  Also, it will not draw anything if the widget is completely
 * outside of its container.
 */

@interface TkNSButton: NSButton

@end

@implementation TkNSButton

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







|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 * so that it will not draw itself unless the NSButton frame matches
 * the frame which was installed by DisplayButton, and the TkButton is
 * mapped.  Also, it will not draw anything if the widget is completely
 * outside of its container.
 */

@interface TkNSButton: NSButton
- (void)drawRect:(NSRect)dirtyRect;
@end

@implementation TkNSButton

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

Changes to macosx/tkMacOSXMenubutton.c.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * 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 "tkMenubutton.h"
#include "tkMacOSXFont.h"
#include "tkMacOSXDebug.h"

/*
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_MENUBUTTON
#endif
*/








|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * 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 "tkMenubutton.h"
#include "tkMacOSXFont.h"
#include "tkMacOSXDebug.h" 

/*
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_MENUBUTTON
#endif
*/

Changes to macosx/tkMacOSXScrlbr.c.

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
22

23
24
25
26
27
28
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80




































81
82

83
84
85
86
87
88
89
/*
 * 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"

/*
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_SCROLLBAR
#endif
*/


NSRect                  TkMacOSXGetScrollFrame(TkScrollbar *scrlPtr);

/*
 * 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];
	if ( tag != -1) {
	    TkScrollbar *scrollPtr = (TkScrollbar *)tag;
	    MacDrawable* macWin = (MacDrawable *)scrollPtr;
	    Tk_Window tkwin = scrollPtr->tkwin;
	    NSRect Tkframe = TkMacOSXGetScrollFrame(scrollPtr);
	    /* Do not draw if the widget is misplaced or unmapped. */
	    if ( NSIsEmptyRect(Tkframe) ||
		 ! macWin->winPtr->flags & TK_MAPPED ||
		 ! NSEqualRects(Tkframe, [self frame])
		 ) {
		return;
	    }

	    /*
	     * Do not draw if the widget is completely outside of its parent.
	     */
	    if (tkwin) {
		int parent_height = Tk_Height(Tk_Parent(tkwin));
		int widget_height = Tk_Height(tkwin);
		int y = Tk_Y(tkwin);
		if ( y > parent_height || y + widget_height < 0 ) {
		    return;
		}

		int parent_width = Tk_Width(Tk_Parent(tkwin));
		int widget_width = Tk_Width(tkwin);
		int x = Tk_X(tkwin);
		if (x > parent_width || x + widget_width < 0) {
		    return;
		}
	    }
	}
	[super drawRect:dirtyRect];
    }





































@end




/*
 * Declaration of Mac specific scrollbar structure.
 */

typedef struct MacScrollbar {









|
>












>











>
>
>
>




|
>
>
>
>
>




>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
138
/*
 * 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"

/*
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_SCROLLBAR
#endif
*/


NSRect                  TkMacOSXGetScrollFrame(TkScrollbar *scrlPtr);

/*
 * 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];
    if ( tag != -1) {
	TkScrollbar *scrollPtr = (TkScrollbar *)tag;
	MacDrawable* macWin = (MacDrawable *)scrollPtr;
	Tk_Window tkwin = scrollPtr->tkwin;
	NSRect Tkframe = TkMacOSXGetScrollFrame(scrollPtr);
	/* Do not draw if the widget is misplaced or unmapped. */
	if ( NSIsEmptyRect(Tkframe) ||
	     ! macWin->winPtr->flags & TK_MAPPED ||
	     ! NSEqualRects(Tkframe, [self frame])
	     ) {
	    return;
	}

	/*
	 * Do not draw if the widget is completely outside of its parent.
	 */
	if (tkwin) {
	    int parent_height = Tk_Height(Tk_Parent(tkwin));
	    int widget_height = Tk_Height(tkwin);
	    int y = Tk_Y(tkwin);
	    if ( y > parent_height || y + widget_height < 0 ) {
		return;
	    }

	    int parent_width = Tk_Width(Tk_Parent(tkwin));
	    int widget_width = Tk_Width(tkwin);
	    int x = Tk_X(tkwin);
	    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.
 */

typedef struct MacScrollbar {

Changes to macosx/tkMacOSXWindowEvent.c.

765
766
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
801
802
803
804
805
806
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
863
864
865
866
867
868
 * 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)
{
    return (eventPtr->type==Expose && eventPtr->xany.serial==PTR2UINT(arg)
	    ? TK_PROCESS_EVENT : TK_DEFER_EVENT);
}


@implementation TKContentView(TKWindowEvent)

- (void) drawRect: (NSRect) rect
{

    const NSRect *rectsBeingDrawn;
    NSInteger rectsBeingDrawnCount;

    [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount];

#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;








>
>











<
<
<
<
<
<
<
<
<
















>




>







<
|
<
<
<




















<
<



|
>
|
>

>
>
|
>
>
>
|
<
|
|

>
>
|
|
>
>
|
|
>
>
>







765
766
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
801
802
803
804
805
806
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
863
864
865
866
867
868
869
870
 * 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)
{
    return (eventPtr->type==Expose && eventPtr->xany.serial==PTR2UINT(arg)
	    ? TK_PROCESS_EVENT : TK_DEFER_EVENT);
}


@implementation TKContentView(TKWindowEvent)

- (void) drawRect: (NSRect) rect
{

    const NSRect *rectsBeingDrawn;
    NSInteger rectsBeingDrawnCount;

    [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount];

#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];
  [self setNeedsDisplay:NO];

}


- (void)viewDidEndLiveResize
{

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


/*Core function of this class, generates expose events for redrawing.*/
- (void) generateExposeEvents: (HIMutableShapeRef) shape
{

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

Changes to macosx/ttkMacOSXTheme.c.

523
524
525
526
527
528
529




530
531
532
533
534
535
536
static Ttk_ElementSpec ComboboxElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    ComboboxElementSize,
    ComboboxElementDraw
};





/*----------------------------------------------------------------------
 * +++ Spinbuttons.
 *
 * From Apple HIG, part III, section "Controls", "The Stepper Control":
 * there should be 2 pixels of space between the stepper control
 * (AKA IncDecButton, AKA "little arrows") and the text field it modifies.







>
>
>
>







523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
static Ttk_ElementSpec ComboboxElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    ComboboxElementSize,
    ComboboxElementDraw
};





/*----------------------------------------------------------------------
 * +++ Spinbuttons.
 *
 * From Apple HIG, part III, section "Controls", "The Stepper Control":
 * there should be 2 pixels of space between the stepper control
 * (AKA IncDecButton, AKA "little arrows") and the text field it modifies.
595
596
597
598
599
600
601





602
603
604
605
606
607
608
    ThemeTrackKind	kind;
    SInt32		thicknessMetric;
} TrackElementData;

static TrackElementData ScaleData = {
    kThemeSlider, kThemeMetricHSliderHeight
};






typedef struct {
    Tcl_Obj *fromObj;		/* minimum value */
    Tcl_Obj *toObj;		/* maximum value */
    Tcl_Obj *valueObj;		/* current value */
    Tcl_Obj *orientObj;		/* horizontal / vertical */
} TrackElement;







>
>
>
>
>







599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
    ThemeTrackKind	kind;
    SInt32		thicknessMetric;
} TrackElementData;

static TrackElementData ScaleData = {
    kThemeSlider, kThemeMetricHSliderHeight
};

static TrackElementData ScrollData = {
    kThemeScrollBarMedium
};


typedef struct {
    Tcl_Obj *fromObj;		/* minimum value */
    Tcl_Obj *toObj;		/* maximum value */
    Tcl_Obj *valueObj;		/* current value */
    Tcl_Obj *orientObj;		/* horizontal / vertical */
} TrackElement;
656
657
658
659
660
661
662

663
664
665
666
667
668
669
    };

    if (info.kind == kThemeSlider) {
	info.trackInfo.slider.pressState = state & TTK_STATE_PRESSED ?
		kThemeThumbPressed : 0;
	info.trackInfo.slider.thumbDir = kThemeThumbPlain;
    }


    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
    END_DRAWING
}

static Ttk_ElementSpec TrackElementSpec = {







>







665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
    };

    if (info.kind == kThemeSlider) {
	info.trackInfo.slider.pressState = state & TTK_STATE_PRESSED ?
		kThemeThumbPressed : 0;
	info.trackInfo.slider.thumbDir = kThemeThumbPlain;
    }


    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
    END_DRAWING
}

static Ttk_ElementSpec TrackElementSpec = {