Tk Source Code

Check-in [2c72a417]
Login

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

Overview
Comment:Fix text clipping when working with the Xft-based renderer.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-5-branch
Files: files | file ages | folders
SHA1: 2c72a41785ba1d51d99aca17a097720528d89260
User & Date: dkf 2012-06-12 08:29:27
Context
2012-06-15
05:43
Backport of [Bug 3532186] fix check-in: 15e3ff22 user: jan.nijtmans tags: core-8-5-branch
2012-06-12
08:34
Fix text clipping when working with the Xft-based renderer. check-in: 66c1f59a user: dkf tags: trunk
08:29
Fix text clipping when working with the Xft-based renderer. check-in: 2c72a417 user: dkf tags: core-8-5-branch
2012-06-11
13:06
fix test failure check-in: 10319218 user: dkf tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ChangeLog.









1
2
3
4
5
6
7








2012-06-11  Donal K. Fellows  <[email protected]>

	* generic/ttk/ttkLabel.c (TextDraw): [Bug 3294450]: Get the clipping
	* generic/ttk/ttkEntry.c (EntryDisplay): of text in Ttk various text
	elements (e.g., buttons, entries, etc.) correct. Stops a whole range
	of visual problems, including loss of the second and subsequent lines
	of a label when the first line doesn't entirely fit, and failing to
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2012-06-12  Donal K. Fellows  <[email protected]>

	* unix/tkUnixRFont.c (Tk_DrawChars, TkUnixSetXftClipRegion): Add some
	* generic/ttk/ttkEntry.c (EntryDisplay): special magic to make the
	* generic/ttk/ttkLabel.c (TextDraw): text clipping work right with the
	Xft-based renderer (which doesn't use the standard Tk GC except to
	supply the color).

2012-06-11  Donal K. Fellows  <[email protected]>

	* generic/ttk/ttkLabel.c (TextDraw): [Bug 3294450]: Get the clipping
	* generic/ttk/ttkEntry.c (EntryDisplay): of text in Ttk various text
	elements (e.g., buttons, entries, etc.) correct. Stops a whole range
	of visual problems, including loss of the second and subsequent lines
	of a label when the first line doesn't entirely fit, and failing to

Changes to generic/tkInt.h.

1182
1183
1184
1185
1186
1187
1188



1189
1190
1191
1192
1193
1194
1195
#ifdef __WIN32__
#define TkParseColor XParseColor
#else
MODULE_SCOPE Status TkParseColor (Display * display,
				Colormap map, CONST char* spec,
				XColor * colorPtr);
#endif




/*
 * Unsupported commands.
 */

MODULE_SCOPE int	TkUnsupported1ObjCmd(ClientData clientData,
			    Tcl_Interp *interp, int objc,







>
>
>







1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
#ifdef __WIN32__
#define TkParseColor XParseColor
#else
MODULE_SCOPE Status TkParseColor (Display * display,
				Colormap map, CONST char* spec,
				XColor * colorPtr);
#endif
#ifdef HAVE_XFT
MODULE_SCOPE void	TkUnixSetXftClipRegion(Region clipRegion);
#endif

/*
 * Unsupported commands.
 */

MODULE_SCOPE int	TkUnsupported1ObjCmd(ClientData clientData,
			    Tcl_Interp *interp, int objc,

Changes to generic/ttk/ttkEntry.c.

1217
1218
1219
1220
1221
1222
1223
1224

1225
1226
1227
1228
1229
1230
1231
1232



1233
1234
1235
1236
1237
1238
1239
		selStartX - borderWidth, entryPtr->entry.layoutY - borderWidth,
		selEndX - selStartX + 2*borderWidth,
		entryPtr->entry.layoutHeight + 2*borderWidth,
		borderWidth, TK_RELIEF_RAISED);
	}
    }

    /* Initialize the clip region:

     */

    rect.x = textarea.x;
    rect.y = textarea.y;
    rect.width = textarea.width;
    rect.height = textarea.height;
    clipRegion = TkCreateRegion();
    TkUnionRectWithRegion(&rect, clipRegion, clipRegion);




    /* Draw cursor:
     */
    if (showCursor) {
	int cursorX = EntryCharPosition(entryPtr, entryPtr->entry.insertPos),
	    cursorY = entryPtr->entry.layoutY,
	    cursorHeight = entryPtr->entry.layoutHeight,







|
>








>
>
>







1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
		selStartX - borderWidth, entryPtr->entry.layoutY - borderWidth,
		selEndX - selStartX + 2*borderWidth,
		entryPtr->entry.layoutHeight + 2*borderWidth,
		borderWidth, TK_RELIEF_RAISED);
	}
    }

    /* Initialize the clip region. Note that Xft does _not_ derive its
     * clipping area from the GC, so we have to supply that by other means.
     */

    rect.x = textarea.x;
    rect.y = textarea.y;
    rect.width = textarea.width;
    rect.height = textarea.height;
    clipRegion = TkCreateRegion();
    TkUnionRectWithRegion(&rect, clipRegion, clipRegion);
#ifdef HAVE_XFT
    TkUnixSetXftClipRegion(clipRegion);
#endif

    /* Draw cursor:
     */
    if (showCursor) {
	int cursorX = EntryCharPosition(entryPtr, entryPtr->entry.insertPos),
	    cursorY = entryPtr->entry.layoutY,
	    cursorHeight = entryPtr->entry.layoutHeight,
1268
1269
1270
1271
1272
1273
1274







1275
1276
1277
1278
1279
1280
1281
	gc = EntryGetGC(entryPtr, es.selForegroundObj, clipRegion);
	Tk_DrawTextLayout(
	    Tk_Display(tkwin), d, gc, entryPtr->entry.textLayout,
	    entryPtr->entry.layoutX, entryPtr->entry.layoutY,
	    selFirst, selLast);
	Tk_FreeGC(Tk_Display(tkwin), gc);
    }







    TkDestroyRegion(clipRegion);
}

/*------------------------------------------------------------------------
 * +++ Widget commands.
 */








>
>
>
>
>
>
>







1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
	gc = EntryGetGC(entryPtr, es.selForegroundObj, clipRegion);
	Tk_DrawTextLayout(
	    Tk_Display(tkwin), d, gc, entryPtr->entry.textLayout,
	    entryPtr->entry.layoutX, entryPtr->entry.layoutY,
	    selFirst, selLast);
	Tk_FreeGC(Tk_Display(tkwin), gc);
    }

    /* Drop the region. Note that we have to manually remove the reference to
     * it from the Xft guts (if they're being used).
     */
#ifdef HAVE_XFT
    TkUnixSetXftClipRegion(None);
#endif
    TkDestroyRegion(clipRegion);
}

/*------------------------------------------------------------------------
 * +++ Widget commands.
 */

Changes to generic/ttk/ttkLabel.c.

127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155
156
157
158
159
160



161

162
163
164
165
166
167
168
static void TextDraw(TextElement *text, Tk_Window tkwin, Drawable d, Ttk_Box b)
{
    XColor *color = Tk_GetColorFromObj(tkwin, text->foregroundObj);
    int underline = -1;
    XGCValues gcValues;
    GC gc1, gc2;
    Tk_Anchor anchor = TK_ANCHOR_CENTER;


    gcValues.font = Tk_FontId(text->tkfont);
    gcValues.foreground = color->pixel;
    gc1 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);
    gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
    gc2 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);

    /* 
     * Place text according to -anchor:
     */
    Tk_GetAnchorFromObj(NULL, text->anchorObj, &anchor);
    b = Ttk_AnchorBox(b, text->width, text->height, anchor);

    /*
     * Clip text if it's too wide:
     */
    if (b.width < text->width) {
	TkRegion clipRegion = TkCreateRegion();
	XRectangle rect;


	rect.x = b.x;
	rect.y = b.y;
	rect.width = b.width + (text->embossed ? 1 : 0);
	rect.height = b.height + (text->embossed ? 1 : 0);
	TkUnionRectWithRegion(&rect, clipRegion, clipRegion);
	TkSetRegion(Tk_Display(tkwin), gc1, clipRegion);
	TkSetRegion(Tk_Display(tkwin), gc2, clipRegion);



	TkDestroyRegion(clipRegion);

    }

    if (text->embossed) {
	Tk_DrawTextLayout(Tk_Display(tkwin), d, gc2,
	    text->textLayout, b.x+1, b.y+1, 0/*firstChar*/, -1/*lastChar*/);
    }
    Tk_DrawTextLayout(Tk_Display(tkwin), d, gc1,







>

















<


>







>
>
>

>







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
static void TextDraw(TextElement *text, Tk_Window tkwin, Drawable d, Ttk_Box b)
{
    XColor *color = Tk_GetColorFromObj(tkwin, text->foregroundObj);
    int underline = -1;
    XGCValues gcValues;
    GC gc1, gc2;
    Tk_Anchor anchor = TK_ANCHOR_CENTER;
    TkRegion clipRegion = NULL;

    gcValues.font = Tk_FontId(text->tkfont);
    gcValues.foreground = color->pixel;
    gc1 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);
    gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
    gc2 = Tk_GetGC(tkwin, GCFont | GCForeground, &gcValues);

    /* 
     * Place text according to -anchor:
     */
    Tk_GetAnchorFromObj(NULL, text->anchorObj, &anchor);
    b = Ttk_AnchorBox(b, text->width, text->height, anchor);

    /*
     * Clip text if it's too wide:
     */
    if (b.width < text->width) {

	XRectangle rect;

	clipRegion = TkCreateRegion();
	rect.x = b.x;
	rect.y = b.y;
	rect.width = b.width + (text->embossed ? 1 : 0);
	rect.height = b.height + (text->embossed ? 1 : 0);
	TkUnionRectWithRegion(&rect, clipRegion, clipRegion);
	TkSetRegion(Tk_Display(tkwin), gc1, clipRegion);
	TkSetRegion(Tk_Display(tkwin), gc2, clipRegion);
#ifdef HAVE_XFT
	TkUnixSetXftClipRegion(clipRegion);
#else
	TkDestroyRegion(clipRegion);
#endif
    }

    if (text->embossed) {
	Tk_DrawTextLayout(Tk_Display(tkwin), d, gc2,
	    text->textLayout, b.x+1, b.y+1, 0/*firstChar*/, -1/*lastChar*/);
    }
    Tk_DrawTextLayout(Tk_Display(tkwin), d, gc1,
176
177
178
179
180
181
182






183
184
185
186
187
188
189
	}
	Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc1,
	    text->textLayout, b.x, b.y, underline);
    }

    Tk_FreeGC(Tk_Display(tkwin), gc1);
    Tk_FreeGC(Tk_Display(tkwin), gc2);






}

static void TextElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    TextElement *text = elementRecord;







>
>
>
>
>
>







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
	}
	Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc1,
	    text->textLayout, b.x, b.y, underline);
    }

    Tk_FreeGC(Tk_Display(tkwin), gc1);
    Tk_FreeGC(Tk_Display(tkwin), gc2);
#ifdef HAVE_XFT
    if (clipRegion != NULL) {
	TkUnixSetXftClipRegion(None);
	TkDestroyRegion(clipRegion);
    }
#endif
}

static void TextElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    TextElement *text = elementRecord;

Changes to unix/tkUnixRFont.c.

29
30
31
32
33
34
35










36
37
38
39
40
41
42
    FcPattern *pattern;

    Display *display;
    int screen;
    XftDraw *ftDraw;
    XftColor color;
} UnixFtFont;











/*
 * Package initialization:
 * 	Nothing to do here except register the fact that we're using Xft in
 * 	the TIP 59 configuration database.
 */








>
>
>
>
>
>
>
>
>
>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    FcPattern *pattern;

    Display *display;
    int screen;
    XftDraw *ftDraw;
    XftColor color;
} UnixFtFont;

/*
 * Used to describe the current clipping box. Can't be passed normally because
 * the information isn't retrievable from the GC.
 */

typedef struct ThreadSpecificData {
    Region clipRegion;		/* The clipping region, or None. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

/*
 * Package initialization:
 * 	Nothing to do here except register the fact that we're using Xft in
 * 	the TIP 59 configuration database.
 */

708
709
710
711
712
713
714


715
716
717
718
719
720
721
    const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */
    UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
    XGCValues values;
    XColor xcolor;
    int clen, nspec, xStart = x;
    XftGlyphFontSpec specs[NUM_SPEC];
    XGlyphInfo metrics;



    if (fontPtr->ftDraw == 0) {
#if DEBUG_FONTSEL
	printf("Switch to drawable 0x%x\n", drawable);
#endif /* DEBUG_FONTSEL */
	fontPtr->ftDraw = XftDrawCreate(display, drawable,
		DefaultVisual(display, fontPtr->screen),







>
>







718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
    const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */
    UnixFtFont *fontPtr = (UnixFtFont *) tkfont;
    XGCValues values;
    XColor xcolor;
    int clen, nspec, xStart = x;
    XftGlyphFontSpec specs[NUM_SPEC];
    XGlyphInfo metrics;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    if (fontPtr->ftDraw == 0) {
#if DEBUG_FONTSEL
	printf("Switch to drawable 0x%x\n", drawable);
#endif /* DEBUG_FONTSEL */
	fontPtr->ftDraw = XftDrawCreate(display, drawable,
		DefaultVisual(display, fontPtr->screen),
734
735
736
737
738
739
740



741
742
743
744
745
746
747
		&xcolor);
	fontPtr->color.color.red = xcolor.red;
	fontPtr->color.color.green = xcolor.green;
	fontPtr->color.color.blue = xcolor.blue;
	fontPtr->color.color.alpha = 0xffff;
	fontPtr->color.pixel = values.foreground;
    }



    nspec = 0;
    while (numBytes > 0 && x <= maxCoord && y <= maxCoord) {
	XftFont *ftFont;
	FcChar32 c;

	clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
	if (clen <= 0) {







>
>
>







746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
		&xcolor);
	fontPtr->color.color.red = xcolor.red;
	fontPtr->color.color.green = xcolor.green;
	fontPtr->color.color.blue = xcolor.blue;
	fontPtr->color.color.alpha = 0xffff;
	fontPtr->color.pixel = values.foreground;
    }
    if (tsdPtr->clipRegion != None) {
	XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion);
    }
    nspec = 0;
    while (numBytes > 0 && x <= maxCoord && y <= maxCoord) {
	XftFont *ftFont;
	FcChar32 c;

	clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes);
	if (clen <= 0) {
771
772
773
774
775
776
777



778
779
780
781
782
783
784
785
786
787
788
789
790
791










		nspec = 0;
	    }
	}
    }
    if (nspec) {
	XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec);
    }




  doUnderlineStrikeout:
    if (fontPtr->font.fa.underline != 0) {
	XFillRectangle(display, drawable, gc, xStart,
		y + fontPtr->font.underlinePos, (unsigned) (x - xStart),
		(unsigned) fontPtr->font.underlineHeight);
    }
    if (fontPtr->font.fa.overstrike != 0) {
	y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10;
	XFillRectangle(display, drawable, gc, xStart, y,
		(unsigned) (x - xStart),
		(unsigned) fontPtr->font.underlineHeight);
    }
}

















>
>
>














>
>
>
>
>
>
>
>
>
>
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
		nspec = 0;
	    }
	}
    }
    if (nspec) {
	XftDrawGlyphFontSpec(fontPtr->ftDraw, &fontPtr->color, specs, nspec);
    }
    if (tsdPtr->clipRegion != None) {
	XftDrawSetClip(fontPtr->ftDraw, None);
    }

  doUnderlineStrikeout:
    if (fontPtr->font.fa.underline != 0) {
	XFillRectangle(display, drawable, gc, xStart,
		y + fontPtr->font.underlinePos, (unsigned) (x - xStart),
		(unsigned) fontPtr->font.underlineHeight);
    }
    if (fontPtr->font.fa.overstrike != 0) {
	y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10;
	XFillRectangle(display, drawable, gc, xStart, y,
		(unsigned) (x - xStart),
		(unsigned) fontPtr->font.underlineHeight);
    }
}

void
TkUnixSetXftClipRegion(
    Region clipRegion)		/* The clipping region to install. */
{
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    tsdPtr->clipRegion = clipRegion;
}