From 229822ddbe8957ef78520ce28e4d6aece518dffc Mon Sep 17 00:00:00 2001 From: sbron Date: Thu, 22 Aug 2024 08:59:25 +0000 Subject: [PATCH 01/10] Make sure that the background doesn't overlap the border's left edge when the widget is scrolled horizontally. --- generic/ttk/ttkTreeview.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 725d80e61..bb15f3489 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1787,7 +1787,8 @@ static void DrawItem( if (itemWidth > tv->tree.treeArea.width) { itemWidth = tv->tree.treeArea.width; } - Ttk_Box rowBox = Ttk_MakeBox(x, y, itemWidth, rowHeight); + Ttk_Box rowBox = Ttk_MakeBox(tv->tree.treeArea.x, y, + itemWidth, rowHeight); DisplayLayout(tv->tree.rowLayout, &displayItem, state, rowBox, d); } From cfc94c1dcd75ae2764143f535ed42ebf66d4da5c Mon Sep 17 00:00:00 2001 From: sbron Date: Thu, 22 Aug 2024 09:42:08 +0000 Subject: [PATCH 02/10] Fix distortion of the bottom row when it is only partly visible. --- generic/ttk/ttkTreeview.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index bb15f3489..9a8f12975 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1768,12 +1768,6 @@ static void DrawItem( int x = tv->tree.treeArea.x - tv->tree.xscroll.first; int y = tv->tree.treeArea.y + rowHeight * (row - tv->tree.yscroll.first); - /* Make sure that the item won't overlap the border's bottom edge: - */ - if (y + rowHeight > tv->tree.treeArea.y + tv->tree.treeArea.height) { - rowHeight = tv->tree.treeArea.y + tv->tree.treeArea.height - y; - } - if (row % 2) state |= TTK_STATE_ALTERNATE; PrepareItem(tv, item, &displayItem); @@ -1782,13 +1776,19 @@ static void DrawItem( */ { int itemWidth = TreeWidth(tv); + int itemHeight = rowHeight; /* Make sure that the background won't overlap the border's right edge: */ if (itemWidth > tv->tree.treeArea.width) { itemWidth = tv->tree.treeArea.width; } + /* Make sure that the item won't overlap the border's bottom edge: + */ + if (y + itemHeight > tv->tree.treeArea.y + tv->tree.treeArea.height) { + itemHeight = tv->tree.treeArea.y + tv->tree.treeArea.height - y; + } Ttk_Box rowBox = Ttk_MakeBox(tv->tree.treeArea.x, y, - itemWidth, rowHeight); + itemWidth, itemHeight); DisplayLayout(tv->tree.rowLayout, &displayItem, state, rowBox, d); } From c6fb8f7211f21d148783533870aa960c18ae2c01 Mon Sep 17 00:00:00 2001 From: sbron Date: Thu, 22 Aug 2024 14:20:03 +0000 Subject: [PATCH 03/10] Make the ttk::treeview 'see' command produce the expected result when it is issued before the widget is first mapped. --- generic/ttk/ttkTreeview.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 9a8f12975..975d76f94 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -2874,12 +2874,17 @@ static int TreeviewSeeCommand( /* Make sure item is visible: */ rowNumber = RowNumber(tv, item); - if (rowNumber < tv->tree.yscroll.first) { - TtkScrollTo(tv->tree.yscrollHandle, rowNumber, 1); - } else if (rowNumber >= tv->tree.yscroll.last) { + if (rowNumber >= tv->tree.yscroll.last) { TtkScrollTo(tv->tree.yscrollHandle, tv->tree.yscroll.first + (1+rowNumber - tv->tree.yscroll.last), 1); } + /* On small widgets (shorter than one row high, which is also the case + * before the widget is initially mapped) the above command will have + * scrolled down too far. This is why both conditions must be checked. + */ + if (rowNumber < tv->tree.yscroll.first) { + TtkScrollTo(tv->tree.yscrollHandle, rowNumber, 1); + } return TCL_OK; } From 39cd7480717661d38f5c179d0d986e883865e8e0 Mon Sep 17 00:00:00 2001 From: sbron Date: Thu, 22 Aug 2024 14:44:05 +0000 Subject: [PATCH 04/10] Make sure the scroll information for a ttk::treeview widget is current before use. --- generic/ttk/ttkTreeview.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 975d76f94..469d4f2f2 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1457,6 +1457,10 @@ static int BoundingBox( int row = ItemRow(tv, item); Ttk_Box bbox = tv->tree.treeArea; + /* Make sure the scroll information is current before use */ + TtkUpdateScrollInfo(tv->tree.xscrollHandle); + TtkUpdateScrollInfo(tv->tree.yscrollHandle); + if (row < tv->tree.yscroll.first || row > tv->tree.yscroll.last) { /* not viewable, or off-screen */ return 0; @@ -2319,6 +2323,10 @@ static int TreeviewIdentifyCommand( return TCL_ERROR; } + /* Make sure the scroll information is current before use */ + TtkUpdateScrollInfo(tv->tree.xscrollHandle); + TtkUpdateScrollInfo(tv->tree.yscrollHandle); + region = IdentifyRegion(tv, x, y); item = IdentifyItem(tv, y); colno = IdentifyDisplayColumn(tv, x, &x1); From 87ca573767e17d324b25ca7ec918247fcec654c7 Mon Sep 17 00:00:00 2001 From: sbron Date: Thu, 22 Aug 2024 17:54:48 +0000 Subject: [PATCH 05/10] Don't change the view when the ttk::treeview's 'see' command is executed for a detached item. --- generic/ttk/ttkTreeview.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 469d4f2f2..66cb03bb6 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -2882,6 +2882,10 @@ static int TreeviewSeeCommand( /* Make sure item is visible: */ rowNumber = RowNumber(tv, item); + if (rowNumber < 0) { + /* The item cannot be moved into view because it is detached */ + return TCL_OK; + } if (rowNumber >= tv->tree.yscroll.last) { TtkScrollTo(tv->tree.yscrollHandle, tv->tree.yscroll.first + (1+rowNumber - tv->tree.yscroll.last), 1); From 54e086606e620b22d5015453eb5909c66187945e Mon Sep 17 00:00:00 2001 From: sbron Date: Thu, 22 Aug 2024 18:01:50 +0000 Subject: [PATCH 06/10] Backout a previous change that causes distortion of the bottom row of non-tree columns. --- generic/ttk/ttkTreeview.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 66cb03bb6..84203755b 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1737,11 +1737,6 @@ static void DrawCells( return; } - /* Make sure that the cells won't overlap the border's bottom edge */ - if (y + rowHeight > tv->tree.treeArea.y + tv->tree.treeArea.height) { - rowHeight = tv->tree.treeArea.y + tv->tree.treeArea.height - y; - } - Tcl_ListObjGetElements(NULL, item->valuesObj, &nValues, &values); for (i = 0; i < tv->tree.nColumns; ++i) { tv->tree.columns[i].data = (i < nValues) ? values[i] : 0; From 61bd2be6e92ce9fa6928c53daaa0450f5dea88b6 Mon Sep 17 00:00:00 2001 From: sbron Date: Sat, 24 Aug 2024 14:05:35 +0000 Subject: [PATCH 07/10] Clip the ttk::treeview tree area if it runs the risk of drawing outside its borders. --- generic/ttk/ttkTreeview.c | 99 +++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 19 deletions(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 84203755b..e156cf3f3 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1763,9 +1763,16 @@ static void DrawItem( { Ttk_State state = ItemState(tv, item); DisplayItem displayItem; - int rowHeight = tv->tree.rowHeight; - int x = tv->tree.treeArea.x - tv->tree.xscroll.first; - int y = tv->tree.treeArea.y + rowHeight * (row - tv->tree.yscroll.first); + int x, y, h, rowHeight; + + rowHeight = tv->tree.rowHeight; + h = rowHeight * (row - tv->tree.yscroll.first); + if (h >= tv->tree.treeArea.height) { + /* The item is outside the visible area */ + return; + } + x = tv->tree.treeArea.x - tv->tree.xscroll.first; + y = tv->tree.treeArea.y + h; if (row % 2) state |= TTK_STATE_ALTERNATE; @@ -1774,20 +1781,8 @@ static void DrawItem( /* Draw row background: */ { - int itemWidth = TreeWidth(tv); - int itemHeight = rowHeight; - /* Make sure that the background won't overlap the border's right edge: - */ - if (itemWidth > tv->tree.treeArea.width) { - itemWidth = tv->tree.treeArea.width; - } - /* Make sure that the item won't overlap the border's bottom edge: - */ - if (y + itemHeight > tv->tree.treeArea.y + tv->tree.treeArea.height) { - itemHeight = tv->tree.treeArea.y + tv->tree.treeArea.height - y; - } Ttk_Box rowBox = Ttk_MakeBox(tv->tree.treeArea.x, y, - itemWidth, itemHeight); + TreeWidth(tv), rowHeight); DisplayLayout(tv->tree.rowLayout, &displayItem, state, rowBox, d); } @@ -1850,18 +1845,84 @@ static int DrawForest( return row; } +/* + DrawTreeArea -- + * Draw the tree area including the headings, if any + */ +static void DrawTreeArea(Treeview *tv, Drawable d) { + if (tv->tree.showFlags & SHOW_HEADINGS) { + DrawHeadings(tv, d); + } + DrawForest(tv, tv->tree.root->children, d, 0, 0); +} + /* + TreeviewDisplay -- * Display() widget hook. Draw the widget contents. */ static void TreeviewDisplay(void *clientData, Drawable d) { Treeview *tv = (Treeview *)clientData; + Tk_Window tkwin = tv->core.tkwin; + int x, y, width, height, winWidth, winHeight; + Drawable p = d; + XGCValues gcValues; + GC gc; + /* Draw the general layout of the treeview widget */ Ttk_DrawLayout(tv->core.layout, tv->core.state, d); - if (tv->tree.showFlags & SHOW_HEADINGS) { - DrawHeadings(tv, d); + + /* When the tree area does not fit in the available space, there is a + * risk that it will be drawn over other areas of the layout. To prevent + * that, a helper drawable is used to make sure it doesn't color outside + * the lines. + */ + + winWidth = Tk_Width(tkwin); + winHeight = Tk_Height(tkwin); + width = tv->tree.treeArea.width; + height = tv->tree.headingArea.height + tv->tree.treeArea.height; + + if ((width == winWidth && height == winHeight) + || (tv->tree.treeArea.height % tv->tree.rowHeight == 0 + && TreeWidth(tv) <= width)) { + /* No protection is needed; either the tree area fills the entire + * widget, or everything fits within the available area. + */ + DrawTreeArea(tv, d); + } else { + /* The tree area will need to be clipped after it is drawn + */ + + x = tv->tree.treeArea.x; + if (tv->tree.showFlags & SHOW_HEADINGS) { + y = tv->tree.headingArea.y; + } else { + y = tv->tree.treeArea.y; + } + + /* Create the temporary helper drawable */ + p = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), + winWidth, winHeight, Tk_Depth(tkwin)); + + /* Get a graphics context for copying the drawable content */ + gcValues.function = GXcopy; + gcValues.graphics_exposures = False; + gc = Tk_GetGC(tkwin, GCFunction|GCGraphicsExposures, &gcValues); + + /* Copy the widget background into the helper */ + XCopyArea(Tk_Display(tkwin), d, p, gc, 0, 0, + (unsigned) winWidth, (unsigned) winHeight, 0, 0); + + /* Draw the tree onto the helper without regard for borders */ + DrawTreeArea(tv, p); + + /* Copy only the tree area inside the borders back */ + XCopyArea(Tk_Display(tkwin), p, d, gc, x, y, + (unsigned) width, (unsigned) height, x, y); + + /* Clean up the temporary resources */ + Tk_FreePixmap(Tk_Display(tkwin), p); + Tk_FreeGC(Tk_Display(tkwin), gc); } - DrawForest(tv, tv->tree.root->children, d, 0, 0); } /*------------------------------------------------------------------------ From 7edc6f3c8b805a48f29544ce37e6989b8dd20707 Mon Sep 17 00:00:00 2001 From: sbron Date: Mon, 26 Aug 2024 09:47:27 +0000 Subject: [PATCH 08/10] Use a different clipping method for macOS. --- generic/ttk/ttkTreeview.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index e156cf3f3..ca24ecd74 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1871,9 +1871,7 @@ static void TreeviewDisplay(void *clientData, Drawable d) Ttk_DrawLayout(tv->core.layout, tv->core.state, d); /* When the tree area does not fit in the available space, there is a - * risk that it will be drawn over other areas of the layout. To prevent - * that, a helper drawable is used to make sure it doesn't color outside - * the lines. + * risk that it will be drawn over other areas of the layout. */ winWidth = Tk_Width(tkwin); @@ -1889,39 +1887,45 @@ static void TreeviewDisplay(void *clientData, Drawable d) */ DrawTreeArea(tv, d); } else { - /* The tree area will need to be clipped after it is drawn + /* The tree area needs to be clipped */ - + x = tv->tree.treeArea.x; if (tv->tree.showFlags & SHOW_HEADINGS) { y = tv->tree.headingArea.y; } else { y = tv->tree.treeArea.y; } - - /* Create the temporary helper drawable */ + +#ifndef TK_NO_DOUBLE_BUFFERING + /* Create a temporary helper drawable */ p = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), winWidth, winHeight, Tk_Depth(tkwin)); - + /* Get a graphics context for copying the drawable content */ gcValues.function = GXcopy; gcValues.graphics_exposures = False; gc = Tk_GetGC(tkwin, GCFunction|GCGraphicsExposures, &gcValues); - + /* Copy the widget background into the helper */ XCopyArea(Tk_Display(tkwin), d, p, gc, 0, 0, (unsigned) winWidth, (unsigned) winHeight, 0, 0); - + /* Draw the tree onto the helper without regard for borders */ DrawTreeArea(tv, p); /* Copy only the tree area inside the borders back */ XCopyArea(Tk_Display(tkwin), p, d, gc, x, y, (unsigned) width, (unsigned) height, x, y); - + /* Clean up the temporary resources */ Tk_FreePixmap(Tk_Display(tkwin), p); Tk_FreeGC(Tk_Display(tkwin), gc); +#else + TkpClipDrawableToRect(Tk_Display(tkwin), d, x, y, width, height); + DrawTreeArea(tv, d); + TkpClipDrawableToRect(Tk_Display(tkwin), d, 0, 0, -1, -1); +#endif } } From 5fc5f4fee60e77efde2c304d830667c8af97871c Mon Sep 17 00:00:00 2001 From: sbron Date: Mon, 26 Aug 2024 10:34:43 +0000 Subject: [PATCH 09/10] Hide unused variables on macOS. --- generic/ttk/ttkTreeview.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index ca24ecd74..1763a0d79 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -1863,9 +1863,11 @@ static void TreeviewDisplay(void *clientData, Drawable d) Treeview *tv = (Treeview *)clientData; Tk_Window tkwin = tv->core.tkwin; int x, y, width, height, winWidth, winHeight; +#ifndef TK_NO_DOUBLE_BUFFERING Drawable p = d; XGCValues gcValues; GC gc; +#endif /* Draw the general layout of the treeview widget */ Ttk_DrawLayout(tv->core.layout, tv->core.state, d); From e8bf789b5fc2fcb5997c8fc4d98d06c3c2d442dd Mon Sep 17 00:00:00 2001 From: sbron Date: Tue, 27 Aug 2024 07:55:39 +0000 Subject: [PATCH 10/10] Additional changes for ttk::treeview on macOS. --- generic/ttk/ttkTreeview.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/generic/ttk/ttkTreeview.c b/generic/ttk/ttkTreeview.c index 1763a0d79..02a2f6d71 100644 --- a/generic/ttk/ttkTreeview.c +++ b/generic/ttk/ttkTreeview.c @@ -10,6 +10,8 @@ #ifdef _WIN32 #include "tkWinInt.h" +#elif defined(MAC_OSX_TK) +#include "tkMacOSXPrivate.h" #endif #define DEF_TREE_ROWS "10" @@ -25,8 +27,6 @@ static const int HALO = 4; /* heading separator */ #define STATE_CHANGED (0x100) /* item state option changed */ -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - /*------------------------------------------------------------------------ * +++ Tree items. * @@ -1601,7 +1601,9 @@ static Ttk_Layout TreeviewGetLayout( tv->tree.indent = DEFAULT_INDENT; if ((objPtr = Ttk_QueryOption(treeLayout, "-rowheight", 0))) { (void)Tk_GetPixelsFromObj(NULL, tv->core.tkwin, objPtr, &tv->tree.rowHeight); - tv->tree.rowHeight = MAX(tv->tree.rowHeight, 1); + if (tv->tree.rowHeight < 1) { + tv->tree.rowHeight = 1; + } } if ((objPtr = Ttk_QueryOption(treeLayout, "-indent", 0))) { (void)Tk_GetPixelsFromObj(NULL, tv->core.tkwin, objPtr, &tv->tree.indent); @@ -1862,12 +1864,7 @@ static void TreeviewDisplay(void *clientData, Drawable d) { Treeview *tv = (Treeview *)clientData; Tk_Window tkwin = tv->core.tkwin; - int x, y, width, height, winWidth, winHeight; -#ifndef TK_NO_DOUBLE_BUFFERING - Drawable p = d; - XGCValues gcValues; - GC gc; -#endif + int width, height, winWidth, winHeight; /* Draw the general layout of the treeview widget */ Ttk_DrawLayout(tv->core.layout, tv->core.state, d); @@ -1892,6 +1889,8 @@ static void TreeviewDisplay(void *clientData, Drawable d) /* The tree area needs to be clipped */ + int x, y; + x = tv->tree.treeArea.x; if (tv->tree.showFlags & SHOW_HEADINGS) { y = tv->tree.headingArea.y; @@ -1900,6 +1899,10 @@ static void TreeviewDisplay(void *clientData, Drawable d) } #ifndef TK_NO_DOUBLE_BUFFERING + Drawable p; + XGCValues gcValues; + GC gc; + /* Create a temporary helper drawable */ p = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), winWidth, winHeight, Tk_Depth(tkwin)); @@ -1924,6 +1927,13 @@ static void TreeviewDisplay(void *clientData, Drawable d) Tk_FreePixmap(Tk_Display(tkwin), p); Tk_FreeGC(Tk_Display(tkwin), gc); #else + Ttk_Theme currentTheme = Ttk_GetCurrentTheme(tv->core.interp); + Ttk_Theme aquaTheme = Ttk_GetTheme(tv->core.interp, "aqua"); + if (currentTheme == aquaTheme && [NSApp macOSVersion] > 100800) { + y -= 4; + height += 4; + } + TkpClipDrawableToRect(Tk_Display(tkwin), d, x, y, width, height); DrawTreeArea(tv, d); TkpClipDrawableToRect(Tk_Display(tkwin), d, 0, 0, -1, -1);