From df3fb86c8aedf3f02e9ccf7ffc78a063b04c9992 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Tue, 23 Sep 2014 13:08:44 +0000 Subject: Fix display of scrollbars when their window is not mapped in Tk-Cocoa --- macosx/tkMacOSXScrlbr.c | 124 +++++++++++++++++++++++++++++++++++++++---- macosx/tkMacOSXWindowEvent.c | 35 ++++++------ 2 files changed, 131 insertions(+), 28 deletions(-) diff --git a/macosx/tkMacOSXScrlbr.c b/macosx/tkMacOSXScrlbr.c index 0b4fa61..e0a583d 100644 --- a/macosx/tkMacOSXScrlbr.c +++ b/macosx/tkMacOSXScrlbr.c @@ -21,13 +21,74 @@ #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 { TkScrollbar info; - NSScroller *scroller; + TkNSScroller *scroller; int variant; } MacScrollbar; @@ -50,6 +111,7 @@ static void UpdateScrollbarMetrics(void); static void ScrollbarEventProc(ClientData clientData, XEvent *eventPtr); + /* * The class procedure table for the scrollbar widget. */ @@ -66,7 +128,7 @@ Tk_ClassProcs tkpScrollbarProcs = { #define NSAppleAquaScrollBarVariantChanged @"AppleAquaScrollBarVariantChanged" @implementation TKApplication(TKScrlbr) -- (void) tkScroller: (NSScroller *) scroller +- (void) tkScroller: (TkNSScroller *) scroller { NSScrollerPart hitPart = [scroller hitPart]; TkScrollbar *scrollPtr = (TkScrollbar *)[scroller tag]; @@ -254,8 +316,8 @@ TkpDestroyScrollbar( TkScrollbar *scrollPtr) { MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr; - NSScroller *scroller = macScrollPtr->scroller; - [scroller setTag:(NSInteger)0]; + TkNSScroller *scroller = macScrollPtr->scroller; + [scroller setTag:(NSInteger)-1]; TkMacOSXMakeCollectableAndRelease(macScrollPtr->scroller); } @@ -284,7 +346,7 @@ TkpDisplayScrollbar( { TkScrollbar *scrollPtr = (TkScrollbar *) clientData; MacScrollbar *macScrollPtr = (MacScrollbar *) clientData; - NSScroller *scroller = macScrollPtr->scroller; + TkNSScroller *scroller = macScrollPtr->scroller; Tk_Window tkwin = scrollPtr->tkwin; TkWindow *winPtr = (TkWindow *) tkwin; MacDrawable *macWin = (MacDrawable *) winPtr->window; @@ -337,15 +399,16 @@ TkpDisplayScrollbar( [scroller setEnabled:(knobProportion < 1.0 && (scrollPtr->vertical ? frame.size.height : frame.size.width) > metrics[macScrollPtr->variant].minHeight)]; + // [scroller setEnabled: YES]; [scroller setDoubleValue:scrollPtr->firstFraction / (1.0 - knobProportion)]; [scroller setKnobProportion:knobProportion]; [scroller displayRectIgnoringOpacity:[scroller bounds]]; TkMacOSXRestoreDrawingContext(&dc); -#ifdef TK_MAC_DEBUG_SCROLLBAR + #ifdef TK_MAC_DEBUG_SCROLLBAR TKLog(@"scroller %s frame %@ width %d height %d", ((TkWindow *)scrollPtr->tkwin)->pathName, NSStringFromRect(frame), Tk_Width(tkwin), Tk_Height(tkwin)); -#endif + #endif } /* @@ -373,7 +436,7 @@ TkpComputeScrollbarGeometry( * changed. */ { MacScrollbar *macScrollPtr = (MacScrollbar *) scrollPtr; - NSScroller *scroller = macScrollPtr->scroller; + TkNSScroller *scroller = macScrollPtr->scroller; int width, height, variant, fieldLength; if (scrollPtr->highlightWidth < 0) { @@ -401,7 +464,7 @@ TkpComputeScrollbarGeometry( } if (!scroller) { if ((width > height) ^ !scrollPtr->vertical) { - /* -[NSScroller initWithFrame:] determines horizonalness for the + /* -[NSScroller initWithFrame:] determines horizontalness for the * lifetime of the scroller via isHoriz = (width > height) */ if (scrollPtr->vertical) { width = height; @@ -412,7 +475,7 @@ TkpComputeScrollbarGeometry( width = 2; } } - scroller = [[NSScroller alloc] initWithFrame: + scroller = [[TkNSScroller alloc] initWithFrame: NSMakeRect(0, 0, width, height)]; macScrollPtr->scroller = TkMacOSXMakeUncollectable(scroller); [scroller setAction:@selector(tkScroller:)]; @@ -533,7 +596,7 @@ TkpScrollbarPosition( /* Scrollbar widget record. */ int x, int y) /* Coordinates within scrollPtr's window. */ { - NSScroller *scroller = ((MacScrollbar *) scrollPtr)->scroller; + TkNSScroller *scroller = ((MacScrollbar *) scrollPtr)->scroller; MacDrawable *macWin = (MacDrawable *) ((TkWindow *) scrollPtr->tkwin)->window; NSView *view = TkMacOSXDrawableView(macWin); @@ -594,6 +657,45 @@ ScrollbarEventProc( TkScrollbarEventProc(clientData, eventPtr); } } + + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXGetScrollFrame -- + * + * Computes a frame for an NSScroller that will correspond to where + * Tk thinks the scroller is located. + * + * Results: + * Returns an NSRect describing a frame for an NSScrollbar. + * + * Side effects: + * None + * + *---------------------------------------------------------------------- + */ +NSRect TkMacOSXGetScrollFrame( + TkScrollbar *scrlPtr) +{ + MacScrollbar *macscrlPtr = (MacScrollbar *) scrlPtr; + Tk_Window tkwin = scrlPtr->tkwin; + TkWindow *winPtr = (TkWindow *) tkwin; + if (tkwin) { + MacDrawable *macWin = (MacDrawable *) winPtr->window; + NSView *view = TkMacOSXDrawableView(macWin); + CGFloat viewHeight = [view bounds].size.height; + NSRect frame = NSMakeRect(macWin->xOff, macWin->yOff, + Tk_Width(tkwin), Tk_Height(tkwin)); + + frame.origin.y = viewHeight - (frame.origin.y + frame.size.height); + return frame; + } else { + return NSZeroRect; + } +} + + /* * Local Variables: diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index c458a89..319bc70 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -837,28 +837,29 @@ ExposeRestrictProc( HIShapeGetBounds(shape, &updateBounds); serial = LastKnownRequestProcessed(Tk_Display(winPtr)); if (GenerateUpdates(shape, &updateBounds, winPtr) && - ![[NSRunLoop currentRunLoop] currentMode] && - Tcl_GetServiceMode() != TCL_SERVICE_NONE) { - /* - * Ensure there are no pending idle-time redraws that could prevent the - * just posted Expose events from generating new redraws. - */ + ![[NSRunLoop currentRunLoop] currentMode] && + Tcl_GetServiceMode() != TCL_SERVICE_NONE) { + /* + * Ensure there are no pending idle-time redraws that could prevent the + * just posted Expose events from generating new redraws. + */ - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} - /* - * For smoother drawing, process Expose events and resulting redraws - * immediately instead of at idle time. - */ + /* + * For smoother drawing, process Expose events and resulting redraws + * immediately instead of at idle time. + */ - ClientData oldArg; - Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, - UINT2PTR(serial), &oldArg); + ClientData oldArg; + Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, + UINT2PTR(serial), &oldArg); - while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} - Tk_RestrictEvents(oldProc, oldArg, &oldArg); - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} + Tk_RestrictEvents(oldProc, oldArg, &oldArg); + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} } + } - (void) tkToolbarButton: (id) sender -- cgit v0.12 From e11d1bc0cb52ca2873204e1e8db7b67ed5fff7be Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Tue, 23 Sep 2014 14:25:17 +0000 Subject: Fine-tune display of buttons on Tk/Cocoa on horizontal scroll --- macosx/tkMacOSXButton.c | 7 +++++++ macosx/tkMacOSXSubwindows.c | 1 + macosx/tkMacOSXWindowEvent.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c index 95611d0..58383ca 100644 --- a/macosx/tkMacOSXButton.c +++ b/macosx/tkMacOSXButton.c @@ -67,6 +67,13 @@ static NSRect TkMacOSXGetButtonFrame(TkButton *butPtr); return; } + int parent_width = Tk_Width(Tk_Parent(tkwin)); + int widget_width = Tk_Width(tkwin); + int x = Tk_X(tkwin); + if (x > parent_width - 30 || x + widget_width < 0) { + return; + } + } } [super drawRect:dirtyRect]; diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index d557db3..22012f9 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -455,6 +455,7 @@ MoveResizeWindow( macParent = macWin->winPtr->parentPtr->privatePtr; parentBorderwidth = macWin->winPtr->parentPtr->changes.border_width; + UpdateOffsets(macWin->winPtr, deltaX, deltaY);//todo? } if (macParent) { diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 319bc70..2e5e204 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -779,6 +779,8 @@ Tk_MacOSXIsAppInFront(void) @implementation TKContentView @end +#define TK_MAC_DEBUG_DRAWING + static Tk_RestrictAction ExposeRestrictProc( ClientData arg, -- cgit v0.12 From 4d3938584a1fe900082df0182583d83cf3480be3 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Tue, 23 Sep 2014 14:27:16 +0000 Subject: Remove debug message from window event code --- macosx/tkMacOSXWindowEvent.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 2e5e204..319bc70 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -779,8 +779,6 @@ Tk_MacOSXIsAppInFront(void) @implementation TKContentView @end -#define TK_MAC_DEBUG_DRAWING - static Tk_RestrictAction ExposeRestrictProc( ClientData arg, -- cgit v0.12 From 17496cdc52a1eddf082c45994393bc3d2649e0aa Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Wed, 24 Sep 2014 23:34:52 +0000 Subject: Fixes to tkMacOSXWindowEvent.c to improve drawing performance after removal of private NSView API's --- macosx/tkMacOSXSubwindows.c | 1 - macosx/tkMacOSXWindowEvent.c | 66 ++++++++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 22012f9..d557db3 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -455,7 +455,6 @@ MoveResizeWindow( macParent = macWin->winPtr->parentPtr->privatePtr; parentBorderwidth = macWin->winPtr->parentPtr->changes.border_width; - UpdateOffsets(macWin->winPtr, deltaX, deltaY);//todo? } if (macParent) { diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 319bc70..c4780c8 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -357,11 +357,12 @@ GenerateUpdates( event.xexpose.width = damageBounds.size.width; event.xexpose.height = damageBounds.size.height; event.xexpose.count = 0; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); -#ifdef TK_MAC_DEBUG_DRAWING - TKLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, + Tk_HandleEvent(&event); + + #ifdef TK_MAC_DEBUG_DRAWING + NSLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); -#endif + #endif /* * Generate updates for the children of this window @@ -388,7 +389,7 @@ GenerateUpdates( /* * TODO: Here we should handle out of process embedding. */ - } + } return 1; } @@ -770,6 +771,8 @@ Tk_MacOSXIsAppInFront(void) @interface TKContentView(TKWindowEvent) - (void) drawRect: (NSRect) rect; - (void) generateExposeEvents: (HIMutableShapeRef) shape; +- (void) viewDidEndLiveResize; +- (void) viewWillDraw; - (BOOL) isOpaque; - (BOOL) wantsDefaultClipping; - (BOOL) acceptsFirstResponder; @@ -779,6 +782,17 @@ Tk_MacOSXIsAppInFront(void) @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, @@ -788,6 +802,7 @@ ExposeRestrictProc( ? TK_PROCESS_EVENT : TK_DEFER_EVENT); } + @implementation TKContentView(TKWindowEvent) - (void) drawRect: (NSRect) rect @@ -803,6 +818,10 @@ ExposeRestrictProc( 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(); @@ -823,28 +842,44 @@ ExposeRestrictProc( nil]]; } CFRelease(drawShape); + drawTime=-[beginTime timeIntervalSinceNow]; } +/*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; if (!winPtr) { - return; + return; } + HIShapeGetBounds(shape, &updateBounds); serial = LastKnownRequestProcessed(Tk_Display(winPtr)); if (GenerateUpdates(shape, &updateBounds, winPtr) && - ![[NSRunLoop currentRunLoop] currentMode] && - Tcl_GetServiceMode() != TCL_SERVICE_NONE) { + ![[NSRunLoop currentRunLoop] currentMode] && + Tcl_GetServiceMode() != TCL_SERVICE_NONE) { /* * Ensure there are no pending idle-time redraws that could prevent the * just posted Expose events from generating new redraws. */ - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} /* * For smoother drawing, process Expose events and resulting redraws @@ -853,15 +888,18 @@ ExposeRestrictProc( ClientData oldArg; Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, - UINT2PTR(serial), &oldArg); + UINT2PTR(serial), &oldArg); while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} + Tk_RestrictEvents(oldProc, oldArg, &oldArg); while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} - } + } + } +/*This is no-op on 10.7 and up because Apple has removed this widget, but leaving here for backwards compatibility.*/ - (void) tkToolbarButton: (id) sender { #ifdef TK_MAC_DEBUG_EVENTS @@ -889,21 +927,15 @@ ExposeRestrictProc( Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); } -#ifdef TK_MAC_DEBUG_DRAWING - (void) setFrameSize: (NSSize) newSize { - TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, - NSStringFromSize(newSize)); [super setFrameSize:newSize]; } - (void) setNeedsDisplayInRect: (NSRect) invalidRect { - TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, - NSStringFromRect(invalidRect)); [super setNeedsDisplayInRect:invalidRect]; } -#endif - (BOOL) isOpaque { -- cgit v0.12 From d2485d93167c4fe26531ba4d406998356c912675 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 1 Oct 2014 18:41:52 +0000 Subject: Ticket [9e487e9f15]: Fix for tkWinButton to avoid problems in plugin --- win/tkWinButton.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/win/tkWinButton.c b/win/tkWinButton.c index f4d2d05..2da8e03 100644 --- a/win/tkWinButton.c +++ b/win/tkWinButton.c @@ -1265,20 +1265,28 @@ ButtonProc( return 0; } case BN_CLICKED: { - int code; - Tcl_Interp *interp = butPtr->info.interp; - - if (butPtr->info.state != STATE_DISABLED) { - Tcl_Preserve((ClientData)interp); - code = TkInvokeButton((TkButton*)butPtr); - if (code != TCL_OK && code != TCL_CONTINUE - && code != TCL_BREAK) { - Tcl_AddErrorInfo(interp, "\n (button invoke)"); - Tcl_BackgroundError(interp); + /* + * OOPS: chromium fires WM_NULL regularly to ping if plugin is still + * alive. When using an external window (i.e. via the tcl plugin), this + * causes all buttons to fire once a second, so we need to make sure + * that we are not dealing with the chromium life check. + */ + if (wParam != 0 || lParam != 0) { + int code; + Tcl_Interp *interp = butPtr->info.interp; + + if (butPtr->info.state != STATE_DISABLED) { + Tcl_Preserve((ClientData)interp); + code = TkInvokeButton((TkButton*)butPtr); + if (code != TCL_OK && code != TCL_CONTINUE + && code != TCL_BREAK) { + Tcl_AddErrorInfo(interp, "\n (button invoke)"); + Tcl_BackgroundError(interp); + } + Tcl_Release((ClientData)interp); } - Tcl_Release((ClientData)interp); + Tcl_ServiceAll(); } - Tcl_ServiceAll(); return 0; } -- cgit v0.12