diff options
-rw-r--r-- | generic/tkText.c | 53 | ||||
-rw-r--r-- | generic/tkText.h | 3 | ||||
-rw-r--r-- | generic/tkTextBTree.c | 3 | ||||
-rw-r--r-- | generic/tkTextDisp.c | 567 | ||||
-rw-r--r-- | generic/tkTextIndex.c | 87 | ||||
-rw-r--r-- | tests/text.test | 60 | ||||
-rw-r--r-- | tests/textDisp.test | 322 |
7 files changed, 882 insertions, 213 deletions
diff --git a/generic/tkText.c b/generic/tkText.c index 386dac5..0d06233 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -881,7 +881,7 @@ TextWidgetObjCmd( } else if (c == 'd' && (length > 8) && !strncmp("-displaylines", option, (unsigned) length)) { TkTextLine *fromPtr, *lastPtr; - TkTextIndex index; + TkTextIndex index, index2; int compare = TkTextIndexCmp(indexFromPtr, indexToPtr); value = 0; @@ -916,35 +916,44 @@ TextWidgetObjCmd( /* * We're going to count up all display lines in the logical * line of 'indexFromPtr' up to, but not including the logical - * line of 'indexToPtr', and then subtract off what we didn't - * want from 'from' and add on what we didn't count from 'to. + * line of 'indexToPtr' (except if this line is elided), and + * then subtract off what came in too much from elided lines, + * also subtract off what we didn't want from 'from' and add + * on what we didn't count from 'to'. */ - while (index.linePtr != indexToPtr->linePtr) { - value += TkTextUpdateOneLine(textPtr, fromPtr,0,&index,0); - - /* - * We might have skipped past indexToPtr, if we have - * multiple logical lines in a single display line. - */ - if (TkTextIndexCmp(&index,indexToPtr) > 0) { - break; - } + while (TkTextIndexCmp(&index,indexToPtr) < 0) { + value += TkTextUpdateOneLine(textPtr, index.linePtr, + 0, &index, 0); } - /* - * Now we need to adjust the count to add on the number of - * display lines in the last logical line, and subtract off - * the number of display lines overcounted in the first - * logical line. This logic is still ok if both indices are in - * the same logical line. - */ + index2 = index; + + /* + * Now we need to adjust the count to: + * - subtract off the number of display lines between + * indexToPtr and index2, since we might have skipped past + * indexToPtr, if we have several logical lines in a + * single display line + * - subtract off the number of display lines overcounted + * in the first logical line + * - add on the number of display lines in the last logical + * line + * This logic is still ok if both indexFromPtr and indexToPtr + * are in the same logical line. + */ + index = *indexToPtr; + index.byteIndex = 0; + while (TkTextIndexCmp(&index,&index2) < 0) { + value -= TkTextUpdateOneLine(textPtr, index.linePtr, + 0, &index, 0); + } index.linePtr = indexFromPtr->linePtr; index.byteIndex = 0; while (1) { TkTextFindDisplayLineEnd(textPtr, &index, 1, NULL); - if (index.byteIndex >= indexFromPtr->byteIndex) { + if (TkTextIndexCmp(&index,indexFromPtr) >= 0) { break; } TkTextIndexForwBytes(textPtr, &index, 1, &index); @@ -956,7 +965,7 @@ TextWidgetObjCmd( index.byteIndex = 0; while (1) { TkTextFindDisplayLineEnd(textPtr, &index, 1, NULL); - if (index.byteIndex >= indexToPtr->byteIndex) { + if (TkTextIndexCmp(&index,indexToPtr) >= 0) { break; } TkTextIndexForwBytes(textPtr, &index, 1, &index); diff --git a/generic/tkText.h b/generic/tkText.h index fb9eab2..99a4888 100644 --- a/generic/tkText.h +++ b/generic/tkText.h @@ -1066,6 +1066,9 @@ MODULE_SCOPE void TkTextIndexBackChars(const TkText *textPtr, TkTextIndex *dstPtr, TkTextCountType type); MODULE_SCOPE int TkTextIndexCmp(const TkTextIndex *index1Ptr, const TkTextIndex *index2Ptr); +MODULE_SCOPE int TkTextIndexCountBytes(const TkText *textPtr, + const TkTextIndex *index1Ptr, + const TkTextIndex *index2Ptr); MODULE_SCOPE int TkTextIndexCount(const TkText *textPtr, const TkTextIndex *index1Ptr, const TkTextIndex *index2Ptr, diff --git a/generic/tkTextBTree.c b/generic/tkTextBTree.c index a06d7e9..eaab652 100644 --- a/generic/tkTextBTree.c +++ b/generic/tkTextBTree.c @@ -1879,8 +1879,7 @@ TkBTreePreviousLine( * number of pixels in the widget. * * Results: - * The result is the index of linePtr within the tree, where 0 - * corresponds to the first line in the tree. + * The result is the pixel height of the top of the given line. * * Side effects: * None. diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index b75a152..3ab9fde 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -243,7 +243,8 @@ typedef struct DLine { * top to bottom. Note: the next DLine doesn't * always correspond to the next line of text: * (a) can have multiple DLines for one text - * line, and (b) can have gaps where DLine's + * line (wrapping), (b) can have elided newlines, + * and (c) can have gaps where DLine's * have been deleted because they're out of * date. */ int flags; /* Various flag bits: see below for values. */ @@ -543,7 +544,8 @@ static void DisplayDLine(TkText *textPtr, DLine *dlPtr, static void DisplayLineBackground(TkText *textPtr, DLine *dlPtr, DLine *prevPtr, Pixmap pixmap); static void DisplayText(ClientData clientData); -static DLine * FindDLine(DLine *dlPtr, const TkTextIndex *indexPtr); +static DLine * FindDLine(TkText *textPtr, DLine *dlPtr, + const TkTextIndex *indexPtr); static void FreeDLines(TkText *textPtr, DLine *firstPtr, DLine *lastPtr, int action); static void FreeStyle(TkText *textPtr, TextStyle *stylePtr); @@ -590,6 +592,8 @@ static int TextGetScrollInfoObj(Tcl_Interp *interp, int *intPtr); static void AsyncUpdateLineMetrics(ClientData clientData); static void AsyncUpdateYScrollbar(ClientData clientData); +static int IsStartOfNotMergedLine(TkText *textPtr, + CONST TkTextIndex *indexPtr); /* * Result values returned by TextGetScrollInfoObj: @@ -1759,7 +1763,7 @@ UpdateDisplayInfo( */ index = textPtr->topIndex; - dlPtr = FindDLine(dInfoPtr->dLinePtr, &index); + dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &index); if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) { FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, DLINE_UNLINK); } @@ -3385,7 +3389,7 @@ TkTextFindDisplayLineEnd( { TkTextIndex index; - if (!end && indexPtr->byteIndex == 0) { + if (!end && IsStartOfNotMergedLine(textPtr, indexPtr)) { /* * Nothing to do. */ @@ -3463,7 +3467,8 @@ TkTextFindDisplayLineEnd( */ *xOffset = DlineXOfIndex(textPtr, dlPtr, - indexPtr->byteIndex - dlPtr->index.byteIndex); + TkTextIndexCountBytes(textPtr, &dlPtr->index, + indexPtr)); } if (end) { /* @@ -3533,6 +3538,27 @@ CalculateDisplayLineHeight( DLine *dlPtr; int pixelHeight; + if (tkTextDebug) { + int oldtkTextDebug = tkTextDebug; + /* + * Check that the indexPtr we are given really is at the start of a + * display line. The gymnastics with tkTextDebug is to prevent + * failure of a test suite test, that checks that lines are rendered + * exactly once. TkTextFindDisplayLineEnd is used here for checking + * indexPtr but it calls LayoutDLine/FreeDLine which makes the + * counting wrong. The debug mode shall therefore be switched off + * when calling TkTextFindDisplayLineEnd. + */ + + TkTextIndex indexPtr2 = *indexPtr; + tkTextDebug = 0; + TkTextFindDisplayLineEnd(textPtr, &indexPtr2, 0, NULL); + tkTextDebug = oldtkTextDebug; + if (TkTextIndexCmp(&indexPtr2,indexPtr) != 0) { + Tcl_Panic("CalculateDisplayLineHeight called with bad indexPtr"); + } + } + /* * Special case for artificial last line. May be better to move this * inside LayoutDLine. @@ -3599,26 +3625,44 @@ TkTextIndexYPixels( { int pixelHeight; TkTextIndex index; + int alreadyStartOfLine = 1; - pixelHeight = TkBTreePixelsTo(textPtr, indexPtr->linePtr); + /* + * Find the index denoting the closest position being at the same time + * the start of a logical line above indexPtr and the start of a display + * line. + */ + + index = *indexPtr; + while (1) { + TkTextFindDisplayLineEnd(textPtr, &index, 0, NULL); + if (index.byteIndex == 0) { + break; + } + TkTextIndexBackBytes(textPtr, &index, 1, &index); + alreadyStartOfLine = 0; + } + + pixelHeight = TkBTreePixelsTo(textPtr, index.linePtr); /* - * Iterate through all display-lines corresponding to the single logical - * line belonging to indexPtr, adding up the pixel height of each such - * display line as we go along, until we go past 'indexPtr'. + * Shortcut to avoid layout of a superfluous display line. We know there + * is nothing more to add up to the height if the index we were given was + * already on the first display line of a logical line. */ - if (indexPtr->byteIndex == 0) { - return pixelHeight; + if (alreadyStartOfLine) { + return pixelHeight; } - index.tree = textPtr->sharedTextPtr->tree; - index.linePtr = indexPtr->linePtr; - index.byteIndex = 0; - index.textPtr = NULL; + /* + * Iterate through display lines, starting at the logical line belonging + * to index, adding up the pixel height of each such display line as we + * go along, until we go past 'indexPtr'. + */ while (1) { - int bytes, height; + int bytes, height, compare; /* * Currently this call doesn't have many side-effects. However, if in @@ -3630,9 +3674,10 @@ TkTextIndexYPixels( height = CalculateDisplayLineHeight(textPtr, &index, &bytes, NULL); - index.byteIndex += bytes; + TkTextIndexForwBytes(textPtr, &index, bytes, &index); - if (index.byteIndex > indexPtr->byteIndex) { + compare = TkTextIndexCmp(&index,indexPtr); + if (compare > 0) { return pixelHeight; } @@ -3640,7 +3685,7 @@ TkTextIndexYPixels( pixelHeight += height; } - if (index.byteIndex == indexPtr->byteIndex) { + if (compare == 0) { return pixelHeight; } } @@ -3704,10 +3749,26 @@ TkTextUpdateOneLine( } /* + * CalculateDisplayLineHeight _must_ be called (below) with an index at + * the beginning of a display line. Force this to happen. This is needed + * when TkTextUpdateOneLine is called with a line that is merged with its + * previous line: the number of merged logical lines in a display line is + * calculated correctly only when CalculateDisplayLineHeight receives + * an index at the beginning of a display line. In turn this causes the + * merged lines to receive their correct zero pixel height in + * TkBTreeAdjustPixelHeight. + */ + + TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL); + linePtr = indexPtr->linePtr; + + /* * Iterate through all display-lines corresponding to the single logical - * line 'linePtr', adding up the pixel height of each such display line as - * we go along. The final total is, therefore, the height of the logical - * line. + * line 'linePtr' (and lines merged into this line due to eol elision), + * adding up the pixel height of each such display line as we go along. + * The final total is, therefore, the total height of all display lines + * made up by the logical line 'linePtr' and subsequent logical lines + * merged into this line. */ displayLines = 0; @@ -3724,7 +3785,7 @@ TkTextUpdateOneLine( * test below this while loop. */ - height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes, + height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes, &logicalLines); if (height > 0) { @@ -3738,44 +3799,31 @@ TkTextUpdateOneLine( break; } - if (logicalLines == 0) { - if (indexPtr->linePtr != linePtr) { - /* - * If we reached the end of the logical line, then either way - * we don't have a partial calculation. - */ - - partialCalc = 0; - break; - } - } else if (indexPtr->byteIndex != 0) { - /* - * We must still be on the same wrapped line. - */ - } else { - /* - * Must check if indexPtr is really a new logical line which is - * not merged with the previous line. The only code that would - * really know this is LayoutDLine, which doesn't pass the - * information on, so we have to check manually here. - */ - - TkTextIndex idx; + if (mergedLines == 0) { + if (indexPtr->linePtr != linePtr) { + /* + * If we reached the end of the logical line, then either way + * we don't have a partial calculation. + */ - TkTextIndexBackChars(textPtr, indexPtr, 1, &idx, COUNT_INDICES); - if (!TkTextIsElided(textPtr, &idx, NULL)) { - /* - * We've ended a logical line. - */ - - partialCalc = 0; - break; - } + partialCalc = 0; + break; + } + } else { + if (IsStartOfNotMergedLine(textPtr, indexPtr)) { + /* + * We've ended a logical line. + */ + + partialCalc = 0; + break; + } - /* - * We must still be on the same wrapped line. - */ - } + /* + * We must still be on the same wrapped line, on a new logical + * line merged with the logical line 'linePtr'. + */ + } if (partialCalc && displayLines > 50 && mergedLines == 0) { /* * Only calculate 50 display lines at a time, to avoid huge @@ -4582,6 +4630,8 @@ TextChanged( TextDInfo *dInfoPtr = textPtr->dInfoPtr; DLine *firstPtr, *lastPtr; TkTextIndex rounded; + TkTextLine *linePtr; + int notBegin; /* * Schedule both a redisplay and a recomputation of display information. @@ -4607,23 +4657,78 @@ TextChanged( /* * Find the DLines corresponding to index1Ptr and index2Ptr. There is one * tricky thing here, which is that we have to relayout in units of whole - * text lines: round index1Ptr back to the beginning of its text line, and - * include all the display lines after index2, up to the end of its text - * line. This is necessary because the indices stored in the display lines - * will no longer be valid. It's also needed because any edit could change - * the way lines wrap. + * text lines: This is necessary because the indices stored in the display + * lines will no longer be valid. It's also needed because any edit could + * change the way lines wrap. + * To relayout in units of whole text (logical) lines, round index1Ptr + * back to the beginning of its text line (or, if this line start is + * elided, to the beginning of the text line that starts the display line + * it is included in), and include all the display lines after index2Ptr, + * up to the end of its text line (or, if this line end is elided, up to + * the end of the first non elided text line after this line end). */ rounded = *index1Ptr; rounded.byteIndex = 0; - firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded); + notBegin = 0; + while (!IsStartOfNotMergedLine(textPtr, &rounded) && notBegin) { + notBegin = !TkTextIndexBackBytes(textPtr, &rounded, 1, &rounded); + rounded.byteIndex = 0; + } + + /* + * 'rounded' now points to the start of a display line as well as the + * real (non elided) start of a logical line, and this index is the + * closest before index1Ptr. + */ + + firstPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &rounded); + if (firstPtr == NULL) { + /* + * index1Ptr pertains to no display line, i.e this index is after + * the last display line. Since index2Ptr is after index1Ptr, there + * is no display line to free/redisplay and we can return early. + */ + return; } - lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr); - while ((lastPtr != NULL) - && (lastPtr->index.linePtr == index2Ptr->linePtr)) { - lastPtr = lastPtr->nextPtr; + + rounded = *index2Ptr; + linePtr = index2Ptr->linePtr; + do { + linePtr = TkBTreeNextLine(textPtr, linePtr); + if (linePtr == NULL) { + break; + } + rounded.linePtr = linePtr; + rounded.byteIndex = 0; + } while (!IsStartOfNotMergedLine(textPtr, &rounded)); + + if (linePtr == NULL) { + lastPtr = NULL; + } else { + /* + * 'rounded' now points to the start of a display line as well as the + * start of a logical line not merged with its previous line, and + * this index is the closest after index2Ptr. + */ + + lastPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &rounded); + + /* + * At least one display line is supposed to change. This makes the + * redisplay OK in case the display line we expect to get here was + * unlinked by a previous call to TkTextChanged and the text widget + * did not update before reaching this point. This happens for + * instance when moving the cursor up one line. + * Note that lastPtr != NULL here, otherwise we would have returned + * earlier when we tested for firstPtr being NULL. + */ + + if (lastPtr == firstPtr) { + lastPtr = lastPtr->nextPtr; + } } /* @@ -4803,14 +4908,13 @@ TextRedrawTag( * the line containing the previous character. */ - if (curIndexPtr->byteIndex == 0) { - dlPtr = FindDLine(dlPtr, curIndexPtr); + if (IsStartOfNotMergedLine(textPtr, curIndexPtr)) { + dlPtr = FindDLine(textPtr, dlPtr, curIndexPtr); } else { - TkTextIndex tmp; + TkTextIndex tmp = *curIndexPtr; - tmp = *curIndexPtr; - tmp.byteIndex -= 1; - dlPtr = FindDLine(dlPtr, &tmp); + TkTextIndexBackBytes(textPtr, &tmp, 1, &tmp); + dlPtr = FindDLine(textPtr, dlPtr, &tmp); } if (dlPtr == NULL) { break; @@ -4826,9 +4930,9 @@ TextRedrawTag( curIndexPtr = &search.curIndex; endIndexPtr = curIndexPtr; } - endPtr = FindDLine(dlPtr, endIndexPtr); - if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr) - && (endPtr->index.byteIndex < endIndexPtr->byteIndex)) { + endPtr = FindDLine(textPtr, dlPtr, endIndexPtr); + if ((endPtr != NULL) + && (TkTextIndexCmp(&endPtr->index,endIndexPtr) < 0)) { endPtr = endPtr->nextPtr; } @@ -4946,7 +5050,7 @@ TkTextRelayoutWindow( * could change the way lines wrap. */ - if (textPtr->topIndex.byteIndex != 0) { + if (!IsStartOfNotMergedLine(textPtr, &textPtr->topIndex)) { TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL); } @@ -5059,9 +5163,9 @@ TkTextSetYView( */ textPtr->topIndex = *indexPtr; - if (indexPtr->byteIndex != 0) { - TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL); - } + if (!IsStartOfNotMergedLine(textPtr, indexPtr)) { + TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL); + } dInfoPtr->newTopPixelOffset = pickPlace; goto scheduleUpdate; } @@ -5075,7 +5179,7 @@ TkTextSetYView( if (dInfoPtr->flags & DINFO_OUT_OF_DATE) { UpdateDisplayInfo(textPtr); } - dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr); + dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, indexPtr); if (dlPtr != NULL) { if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) { /* @@ -5084,19 +5188,20 @@ TkTextSetYView( */ dlPtr = NULL; - } else if ((dlPtr->index.linePtr == indexPtr->linePtr) - && (dlPtr->index.byteIndex <= indexPtr->byteIndex)) { - if (dInfoPtr->dLinePtr == dlPtr && dInfoPtr->topPixelOffset != 0) { - /* - * It is on the top line, but that line is hanging off the top - * of the screen. Change the top overlap to zero and update. - */ - - dInfoPtr->newTopPixelOffset = 0; - goto scheduleUpdate; - } - return; - } + } else { + if (TkTextIndexCmp(&dlPtr->index, indexPtr) <= 0) { + if (dInfoPtr->dLinePtr == dlPtr && dInfoPtr->topPixelOffset != 0) { + /* + * It is on the top line, but that line is hanging off the top + * of the screen. Change the top overlap to zero and update. + */ + + dInfoPtr->newTopPixelOffset = 0; + goto scheduleUpdate; + } + return; + } + } } /* @@ -5107,7 +5212,9 @@ TkTextSetYView( * If the line is not close, place it in the center of the window. */ - lineHeight = CalculateDisplayLineHeight(textPtr, indexPtr, NULL, NULL); + tmpIndex = *indexPtr; + TkTextFindDisplayLineEnd(textPtr, &tmpIndex, 0, NULL); + lineHeight = CalculateDisplayLineHeight(textPtr, &tmpIndex, NULL, NULL); /* * It would be better if 'bottomY' were calculated using the actual height @@ -5148,7 +5255,7 @@ TkTextSetYView( MeasureUp(textPtr, indexPtr, close + lineHeight - textPtr->charHeight/2, &tmpIndex, &overlap); - if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) { + if (FindDLine(textPtr, dInfoPtr->dLinePtr, &tmpIndex) != NULL) { bottomY = dInfoPtr->maxY - dInfoPtr->y; } } @@ -5286,6 +5393,8 @@ MeasureUp( index.linePtr = TkBTreeFindLine(srcPtr->tree, textPtr, lineNum); index.byteIndex = 0; + TkTextFindDisplayLineEnd(textPtr, &index, 0, NULL); + lineNum = TkBTreeLinesTo(textPtr, index.linePtr); lowestPtr = NULL; do { dlPtr = LayoutDLine(textPtr, &index); @@ -5306,8 +5415,21 @@ MeasureUp( for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) { distance -= dlPtr->height; if (distance <= 0) { - *dstPtr = dlPtr->index; - if (overlap != NULL) { + *dstPtr = dlPtr->index; + + /* + * dstPtr is the start of a display line that is or is not + * the start of a logical line. If it is the start of a + * logical line, we must check whether this line is merged + * with the previous logical line, and if so we must adjust + * dstPtr to the start of the display line since a display + * line start needs to be returned. + */ + if (!IsStartOfNotMergedLine(textPtr, dstPtr)) { + TkTextFindDisplayLineEnd(textPtr, dstPtr, 0, NULL); + } + + if (overlap != NULL) { *overlap = -distance; } break; @@ -5407,16 +5529,24 @@ TkTextSeeCmd( } /* - * Find the chunk that contains the desired index. dlPtr may be NULL if - * the widget is not mapped. [Bug #641778] + * Find the display line containing the desired index. dlPtr may be NULL + * if the widget is not mapped. [Bug #641778] */ - dlPtr = FindDLine(dInfoPtr->dLinePtr, &index); + dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &index); if (dlPtr == NULL) { return TCL_OK; } - byteCount = index.byteIndex - dlPtr->index.byteIndex; + /* + * Find the chunk within the display line that contains the desired + * index. The chunks making the display line are skipped up to but not + * including the one crossing index. Skipping is done based on a + * byteCount offset possibly spanning several logical lines in case + * they are elided. + */ + + byteCount = TkTextIndexCountBytes(textPtr, &dlPtr->index, &index); for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL ; chunkPtr = chunkPtr->nextPtr) { if (byteCount < chunkPtr->numBytes) { @@ -5427,34 +5557,32 @@ TkTextSeeCmd( /* * Call a chunk-specific function to find the horizontal range of the - * character within the chunk. chunkPtr is NULL if trying to see in elided - * region. + * character within the chunk. */ - if (chunkPtr != NULL) { - chunkPtr->bboxProc(textPtr, chunkPtr, byteCount, - dlPtr->y + dlPtr->spaceAbove, - dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow, - dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width, - &height); - delta = x - dInfoPtr->curXPixelOffset; - oneThird = lineWidth/3; - if (delta < 0) { - if (delta < -oneThird) { - dInfoPtr->newXPixelOffset = x - lineWidth/2; - } else { - dInfoPtr->newXPixelOffset += delta; - } - } else { - delta -= lineWidth - width; - if (delta <= 0) { - return TCL_OK; - } else if (delta > oneThird) { - dInfoPtr->newXPixelOffset = x - lineWidth/2; - } else { - dInfoPtr->newXPixelOffset += delta; - } + chunkPtr->bboxProc(textPtr, chunkPtr, byteCount, + dlPtr->y + dlPtr->spaceAbove, + dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow, + dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width, + &height); + delta = x - dInfoPtr->curXPixelOffset; + oneThird = lineWidth/3; + if (delta < 0) { + if (delta < -oneThird) { + dInfoPtr->newXPixelOffset = x - lineWidth/2; + } else { + dInfoPtr->newXPixelOffset += delta; + } + } else { + delta -= lineWidth - width; + if (delta <= 0) { + return TCL_OK; } + if (delta > oneThird) { + dInfoPtr->newXPixelOffset = x - lineWidth/2; + } else { + dInfoPtr->newXPixelOffset += delta; + } } dInfoPtr->flags |= DINFO_OUT_OF_DATE; if (!(dInfoPtr->flags & REDRAW_PENDING)) { @@ -5691,7 +5819,25 @@ YScrollByLines( offset++; if (offset == 0) { textPtr->topIndex = dlPtr->index; - break; + + /* + * topIndex is the start of a logical line. However, if + * the eol of the previous logical line is elided, then + * topIndex may be elsewhere than the first character of + * a display line, which is unwanted. Adjust to the start + * of the display line, if needed. + * topIndex is the start of a display line that is or is + * not the start of a logical line. If it is the start of + * a logical line, we must check whether this line is + * merged with the previous logical line, and if so we + * must adjust topIndex to the start of the display line. + */ + if (!IsStartOfNotMergedLine(textPtr, &textPtr->topIndex)) { + TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, + 0, NULL); + } + + break; } } @@ -6178,13 +6324,10 @@ GetYPixelCount( /* * For the common case where this dlPtr is also the start of the logical - * line, we can return right away. Note the implicit assumption here that - * the start of a logical line is always the start of a display line (if - * the 'elide won't elide first newline' bug is fixed, this will no longer - * necessarily be true). + * line, we can return right away. */ - if (dlPtr->index.byteIndex == 0) { + if (IsStartOfNotMergedLine(textPtr, &dlPtr->index)) { return count; } @@ -6471,11 +6614,12 @@ AsyncUpdateYScrollbar( static DLine * FindDLine( + TkText *textPtr, /* Widget record for text widget. */ register DLine *dlPtr, /* Pointer to first in list of DLines to * search. */ const TkTextIndex *indexPtr)/* Index of desired character. */ { - TkTextLine *linePtr; + DLine *dlPtrPrev; if (dlPtr == NULL) { return NULL; @@ -6490,43 +6634,89 @@ FindDLine( } /* - * Find the first display line that covers the desired text line. + * The display line containing the desired index is such that the index + * of the first character of this display line is at or before the + * desired index, and the index of the first character of the next + * display line is after the desired index. */ - linePtr = dlPtr->index.linePtr; - while (linePtr != indexPtr->linePtr) { - while (dlPtr->index.linePtr == linePtr) { - dlPtr = dlPtr->nextPtr; - if (dlPtr == NULL) { - return NULL; - } - } + while (TkTextIndexCmp(&dlPtr->index,indexPtr) < 0) { + dlPtrPrev = dlPtr; + dlPtr = dlPtr->nextPtr; + if (dlPtr == NULL) { + TkTextIndex indexPtr2; + /* + * We're past the last display line, either because the desired + * index lies past the visible text, or because the desired index + * is on the last display line showing the last logical line. + */ + indexPtr2 = dlPtrPrev->index; + TkTextIndexForwBytes(textPtr, &indexPtr2, dlPtrPrev->byteCount, + &indexPtr2); + if (TkTextIndexCmp(&indexPtr2,indexPtr) > 0) { + dlPtr = dlPtrPrev; + break; + } else { + return NULL; + } + } + if (TkTextIndexCmp(&dlPtr->index,indexPtr) > 0) { + dlPtr = dlPtrPrev; + break; + } + } - /* - * VMD: some concern here as to whether this logic, or the caller's - * logic will work well with partial peer widgets. - */ + return dlPtr; +} + +/* + *---------------------------------------------------------------------- + * + * IsStartOfNotMergedLine -- + * + * This function checks whether the given index is the start of a + * logical line that is not merged with the previous logical line + * (due to elision of the eol of the previous line). + * + * Results: + * Returns whether the given index denotes the first index of a +* logical line not merged with its previous line. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ - linePtr = TkBTreeNextLine(NULL, linePtr); - if (linePtr == NULL) { - Tcl_Panic("FindDLine reached end of text"); - } - } - if (indexPtr->linePtr != dlPtr->index.linePtr) { - return dlPtr; +static int +IsStartOfNotMergedLine( + TkText *textPtr, /* Widget record for text widget. */ + CONST TkTextIndex *indexPtr) /* Index to check. */ +{ + TkTextIndex indexPtr2; + + if (indexPtr->byteIndex != 0) { + /* + * Not the start of a logical line. + */ + return 0; } - /* - * Now get to the right position within the text line. - */ + if (TkTextIndexBackBytes(textPtr, indexPtr, 1, &indexPtr2)) { + /* + * indexPtr is the first index of the text widget. + */ + return 1; + } - while (indexPtr->byteIndex >= (dlPtr->index.byteIndex+dlPtr->byteCount)) { - dlPtr = dlPtr->nextPtr; - if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) { - break; - } + if (!TkTextIsElided(textPtr, &indexPtr2, NULL)) { + /* + * The eol of the line just before indexPtr is elided. + */ + return 1; } - return dlPtr; + + return 0; } /* @@ -6697,10 +6887,15 @@ DlineIndexOfX( * We've reached the end of the text. */ + TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES); return; } if (chunkPtr->nextPtr == NULL) { - TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES); + /* + * We've reached the end of the display line. + */ + + TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES); return; } chunkPtr = chunkPtr->nextPtr; @@ -6856,7 +7051,7 @@ TkTextIndexBbox( TextDInfo *dInfoPtr = textPtr->dInfoPtr; DLine *dlPtr; register TkTextDispChunk *chunkPtr; - int byteIndex; + int byteCount; /* * Make sure that all of the screen layout information is up to date. @@ -6870,24 +7065,37 @@ TkTextIndexBbox( * Find the display line containing the desired index. */ - dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr); + dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, indexPtr); + + /* + * Two cases shall be trapped here because the logic later really + * needs dlPtr to be the display line containing indexPtr: + * 1. if no display line contains the desired index (NULL dlPtr) + * 2. if indexPtr is before the first display line, in which case + * dlPtr currently points to the first display line + */ + if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) { return -1; } /* - * Find the chunk within the line that contains the desired index. + * Find the chunk within the display line that contains the desired + * index. The chunks making the display line are skipped up to but not + * including the one crossing indexPtr. Skipping is done based on + * a byteCount offset possibly spanning several logical lines in case + * they are elided. */ - byteIndex = indexPtr->byteIndex - dlPtr->index.byteIndex; + byteCount = TkTextIndexCountBytes(textPtr, &dlPtr->index, indexPtr); for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) { if (chunkPtr == NULL) { return -1; } - if (byteIndex < chunkPtr->numBytes) { + if (byteCount < chunkPtr->numBytes) { break; } - byteIndex -= chunkPtr->numBytes; + byteCount -= chunkPtr->numBytes; } /* @@ -6897,13 +7105,13 @@ TkTextIndexBbox( * coordinate on the screen. Translate it to reflect horizontal scrolling. */ - chunkPtr->bboxProc(textPtr, chunkPtr, byteIndex, + chunkPtr->bboxProc(textPtr, chunkPtr, byteCount, dlPtr->y + dlPtr->spaceAbove, dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow, dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr, heightPtr); *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curXPixelOffset; - if ((byteIndex == chunkPtr->numBytes-1) && (chunkPtr->nextPtr == NULL)) { + if ((byteCount == chunkPtr->numBytes-1) && (chunkPtr->nextPtr == NULL)) { /* * Last character in display line. Give it all the space up to the * line. @@ -7001,7 +7209,16 @@ TkTextDLineInfo( * Find the display line containing the desired index. */ - dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr); + dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, indexPtr); + + /* + * Two cases shall be trapped here because the logic later really + * needs dlPtr to be the display line containing indexPtr: + * 1. if no display line contains the desired index (NULL dlPtr) + * 2. if indexPtr is before the first display line, in which case + * dlPtr currently points to the first display line + */ + if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) { return -1; } diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index 9914ea6..70f74fb 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -40,6 +40,9 @@ static const char * StartEnd(TkText *textPtr, const char *string, static int GetIndex(Tcl_Interp *interp, TkSharedText *sharedPtr, TkText *textPtr, const char *string, TkTextIndex *indexPtr, int *canCachePtr); +static int IndexCountBytesOrdered(CONST TkText *textPtr, + CONST TkTextIndex *indexPtr1, + CONST TkTextIndex *indexPtr2); /* * The "textindex" Tcl_Obj definition: @@ -1616,6 +1619,90 @@ TkTextIndexForwChars( /* *--------------------------------------------------------------------------- * + * TkTextIndexCountBytes -- + * + * Given a pair of indices in a text widget, this function counts how + * many bytes are between the two indices. The two indices do not need + * to be ordered. + * + * Results: + * The number of bytes in the given range. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkTextIndexCountBytes( + CONST TkText *textPtr, + CONST TkTextIndex *indexPtr1, /* Index describing one location. */ + CONST TkTextIndex *indexPtr2) /* Index describing second location. */ +{ + int compare = TkTextIndexCmp(indexPtr1, indexPtr2); + + if (compare == 0) { + return 0; + } else if (compare > 0) { + return IndexCountBytesOrdered(textPtr, indexPtr2, indexPtr1); + } else { + return IndexCountBytesOrdered(textPtr, indexPtr1, indexPtr2); + } +} + +static int +IndexCountBytesOrdered( + CONST TkText *textPtr, + CONST TkTextIndex *indexPtr1, + /* Index describing location of character from + * which to count. */ + CONST TkTextIndex *indexPtr2) + /* Index describing location of last character + * at which to stop the count. */ +{ + int byteCount, offset; + TkTextSegment *segPtr, *segPtr1; + TkTextLine *linePtr; + + if (indexPtr1->linePtr == indexPtr2->linePtr) { + return indexPtr2->byteIndex - indexPtr1->byteIndex; + } + + /* + * indexPtr2 is on a line strictly after the line containing indexPtr1. + * Add up: + * bytes between indexPtr1 and end of its line + * bytes in lines strictly between indexPtr1 and indexPtr2 + * bytes between start of the indexPtr2 line and indexPtr2 + */ + + segPtr1 = TkTextIndexToSeg(indexPtr1, &offset); + byteCount = -offset; + for (segPtr = segPtr1; segPtr != NULL; segPtr = segPtr->nextPtr) { + byteCount += segPtr->size; + } + + linePtr = indexPtr1->linePtr->nextPtr; + while (linePtr != indexPtr2->linePtr) { + for (segPtr = linePtr->segPtr; segPtr != NULL; + segPtr = segPtr->nextPtr) { + byteCount += segPtr->size; + } + linePtr = TkBTreeNextLine(textPtr, linePtr); + if (linePtr == NULL) { + Tcl_Panic("TextIndexCountBytesOrdered ran out of lines"); + } + } + + byteCount += indexPtr2->byteIndex; + + return byteCount; +} + +/* + *--------------------------------------------------------------------------- + * * TkTextIndexCount -- * * Given an ordered pair of indices in a text widget, this function diff --git a/tests/text.test b/tests/text.test index 5089bb1..d8d01a0 100644 --- a/tests/text.test +++ b/tests/text.test @@ -2632,7 +2632,65 @@ test text-10.39 {TextWidgetCmd procedure, "count" option} -setup { lappend res [.t count -displaylines 1.19 3.24] [.t count -displaylines 1.0 end] } -cleanup { destroy .t -} -result {2 6 2 5} +} -result {2 6 1 5} +test text-9.2.45 {TextWidgetCmd procedure, "count" option} -setup { + text .t + pack .t + update + set res {} +} -body { + for {set i 1} {$i < 5} {incr i} { + .t insert end "Line $i+++Line $i---Line $i///Line $i - This is Line [format %c [expr 64+$i]]\n" + } + .t tag configure hidden -elide true + .t tag add hidden 2.15 3.10 + .t configure -wrap none + set res [.t count -displaylines 2.0 3.0] +} -cleanup { + destroy .t +} -result {0} +test text-9.2.46 {TextWidgetCmd procedure, "count" option} -setup { + toplevel .mytop + pack [text .mytop.t] + wm geometry .mytop 100x300+0+0 + .mytop.t delete 1.0 end + update + set res {} +} -body { + for {set i 1} {$i < 5} {incr i} { + # 0 1 2 3 4 + # 012345 678901234 567890123 456789012 34567890123456789 + .mytop.t insert end "Line $i+++Line $i---Line $i///Line $i - This is Line [format %c [expr 64+$i]]\n" + } + .mytop.t tag configure hidden -elide true + .mytop.t tag add hidden 2.15 3.10 + .mytop.t configure -wrap char + lappend res [.mytop.t count -displaylines 2.0 3.0] + lappend res [.mytop.t count -displaylines 2.0 3.40] +} -cleanup { + destroy .mytop +} -result {1 3} +test text-9.2.47 {TextWidgetCmd procedure, "count" option} -setup { + text .t + pack .t + update + set res {} +} -body { + for {set i 1} {$i < 25} {incr i} { + .t insert end "Line $i\n" + } + .t tag configure hidden -elide true + .t tag add hidden 5.7 11.0 + update + set y1 [lindex [.t yview] 1] + .t count -displaylines 5.0 11.0 + set y2 [lindex [.t yview] 1] + .t count -displaylines 5.0 12.0 + set y3 [lindex [.t yview] 1] + list [expr {$y1 == $y2}] [expr {$y1 == $y3}] +} -cleanup { + destroy .t +} -result {1 1} test text-11.1 {counting with tag priority eliding} -setup { diff --git a/tests/textDisp.test b/tests/textDisp.test index 70c7208..12a20c6 100644 --- a/tests/textDisp.test +++ b/tests/textDisp.test @@ -537,20 +537,24 @@ test textDisp-4.1 {UpdateDisplayInfo, basic} {textfonts} { .t insert end "Line 1\nLine 2\nLine 3\n" update .t delete 2.0 2.end + update + set res $tk_textRelayout .t insert 2.0 "New Line 2" update - list [.t bbox 1.0] [.t bbox 2.0] [.t bbox 3.0] $tk_textRelayout -} [list [list 5 5 7 $fixedHeight] [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 5 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] 2.0] + lappend res [.t bbox 1.0] [.t bbox 2.0] [.t bbox 3.0] $tk_textRelayout +} [list 2.0 [list 5 5 7 $fixedHeight] [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 5 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] 2.0] test textDisp-4.2 {UpdateDisplayInfo, re-use tail of text line} {textfonts} { .t delete 1.0 end .t insert end "Line 1\nLine 2 is so long that it wraps around\nLine 3" update .t mark set x 2.21 .t delete 2.2 + update + set res $tk_textRelayout .t insert 2.0 X update - list [.t bbox 2.0] [.t bbox x] [.t bbox 3.0] $tk_textRelayout -} [list [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 12 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] [list 5 [expr {3*$fixedDiff + 44}] 7 $fixedHeight] {2.0 2.20}] + lappend res [.t bbox 2.0] [.t bbox x] [.t bbox 3.0] $tk_textRelayout +} [list 2.0 2.20 [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 12 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] [list 5 [expr {3*$fixedDiff + 44}] 7 $fixedHeight] {2.0 2.20}] test textDisp-4.3 {UpdateDisplayInfo, tail of text line shifts} {textfonts} { .t delete 1.0 end .t insert end "Line 1\nLine 2 is so long that it wraps around\nLine 3" @@ -1148,6 +1152,41 @@ test textDisp-8.11 {TkTextChanged, scrollbar notification when changes are off-s .t configure -yscrollcommand "" set scrollInfo } {0.0 0.625} +test textDisp-8.12 {TkTextChanged, moving the insert cursor redraws only past and new lines} { + .t delete 1.0 end + .t configure -wrap none + for {set i 1} {$i < 25} {incr i} { + .t insert end "Line $i Line $i\n" + } + .t tag add hidden 5.0 8.0 + .t tag configure hidden -elide true + .t mark set insert 9.0 + update + .t mark set insert 8.0 ; # up one line + update + set res [list $tk_textRedraw] + .t mark set insert 12.2 ; # in the visible text + update + lappend res $tk_textRedraw + .t mark set insert 6.5 ; # in the hidden text + update + lappend res $tk_textRedraw + .t mark set insert 3.5 ; # in the visible text again + update + lappend res $tk_textRedraw + .t mark set insert 3.8 ; # within the same line + update + lappend res $tk_textRedraw + # This last one is tricky: correct result really is {2.0 3.0} when + # calling .t mark set insert, two calls to TkTextChanged are done: + # (a) to redraw the line of the past position of the cursor + # (b) to redraw the line of the new position of the cursor + # During (a) the display line showing the cursor gets unlinked, + # which leads TkTextChanged in (b) to schedule a redraw starting + # one line _before_ the line containing the insert cursor. This is + # because during (b) findDLine cannot return the display line the + # cursor is in since this display line was just unlinked in (a). +} {{8.0 9.0} {8.0 12.0} {8.0 12.0} {3.0 8.0} {2.0 3.0}} test textDisp-9.1 {TkTextRedrawTag} { .t configure -wrap char @@ -1173,40 +1212,44 @@ test textDisp-9.3 {TkTextRedrawTag} { .t insert 1.0 "Line 1\nLine 2 is long enough to wrap around\nLine 3\nLine 4" update .t tag add big 2.2 2.4 + update .t tag remove big 1.0 end update list $tk_textRelayout $tk_textRedraw -} {2.0 2.0} +} {{2.0 2.20} {2.0 2.20 eof}} test textDisp-9.4 {TkTextRedrawTag} { .t configure -wrap char .t delete 1.0 end .t insert 1.0 "Line 1\nLine 2 is long enough to wrap around\nLine 3\nLine 4" update .t tag add big 2.2 2.20 + update .t tag remove big 1.0 end update list $tk_textRelayout $tk_textRedraw -} {2.0 2.0} +} {{2.0 2.20} {2.0 2.20 eof}} test textDisp-9.5 {TkTextRedrawTag} { .t configure -wrap char .t delete 1.0 end .t insert 1.0 "Line 1\nLine 2 is long enough to wrap around\nLine 3\nLine 4" update .t tag add big 2.2 2.end + update .t tag remove big 1.0 end update list $tk_textRelayout $tk_textRedraw -} {{2.0 2.20} {2.0 2.20}} +} {{2.0 2.20} {2.0 2.20 eof}} test textDisp-9.6 {TkTextRedrawTag} { .t configure -wrap char .t delete 1.0 end - .t insert 1.0 "Line 1\nLine 2 is long enough to wrap\nLine 3 is also long enough to wrap\nLine 4" + .t insert 1.0 "Line 1\nLine 2 is long enough to wrap\nLine 3 is also long enough to wrap" update .t tag add big 2.2 3.5 + update .t tag remove big 1.0 end update list $tk_textRelayout $tk_textRedraw -} {{2.0 2.20 3.0} {2.0 2.20 3.0}} +} {{2.0 2.20 3.0 3.20} {2.0 2.20 3.0 3.20 eof}} test textDisp-9.7 {TkTextRedrawTag} { .t configure -wrap char .t delete 1.0 end @@ -1258,6 +1301,34 @@ test textDisp-9.11 {TkTextRedrawTag} { update set tk_textRedraw } {} +test textDisp-9.12 {TkTextRedrawTag} { + .t configure -wrap char + .t delete 1.0 end + for {set i 1} {$i < 5} {incr i} { + .t insert end "Line $i+++Line $i\n" + } + .t tag configure hidden -elide true + .t tag add hidden 2.6 3.6 + update + .t tag add hidden 3.11 4.6 + update + list $tk_textRelayout $tk_textRedraw +} {2.0 {2.0 eof}} +test textDisp-9.13 {TkTextRedrawTag} { + .t configure -wrap none + .t delete 1.0 end + for {set i 1} {$i < 10} {incr i} { + .t insert end "Line $i - This is Line [format %c [expr 64+$i]]\n" + } + .t tag add hidden 2.8 2.17 + .t tag add hidden 6.8 7.17 + .t tag configure hidden -background red + .t tag configure hidden -elide true + update + .t tag configure hidden -elide false + update + list $tk_textRelayout $tk_textRedraw +} {{2.0 6.0 7.0} {2.0 6.0 7.0}} test textDisp-10.1 {TkTextRelayoutWindow} { .t configure -wrap char @@ -1426,7 +1497,7 @@ test textDisp-11.15 {TkTextSetYView, only a few lines visible} { update .top.t see 11.0 .top.t index @0,0 - # Thie index 9.0 should be just visible by a couple of pixels + # The index 9.0 should be just visible by a couple of pixels } {9.0} test textDisp-11.16 {TkTextSetYView, only a few lines visible} { .top.t yview 8.0 @@ -1439,9 +1510,54 @@ test textDisp-11.17 {TkTextSetYView, only a few lines visible} { update .top.t see 4.0 .top.t index @0,0 - # Thie index 2.0 should be just visible by a couple of pixels + # The index 2.0 should be just visible by a couple of pixels } {2.0} -destroy .top +test textDisp-11.18 {TkTextSetYView, see in elided lines} { + .top.t delete 1.0 end + for {set i 1} {$i < 20} {incr i} { + .top.t insert end [string repeat "Line $i" 10] + .top.t insert end "\n" + } + .top.t yview 4.0 + .top.t tag add hidden 4.10 "4.10 lineend" + .top.t tag add hidden 5.15 10.3 + .top.t tag configure hidden -elide true + update + .top.t see "8.0 lineend" + # The index "8.0 lineend" is on screen despite elided -> no scroll + .top.t index @0,0 +} {4.0} +test textDisp-11.19 {TkTextSetYView, see in elided lines} { + .top.t delete 1.0 end + for {set i 1} {$i < 50} {incr i} { + .top.t insert end "Line $i\n" + } + # button just for having a line with a larger height + button .top.t.b -text "Test" -bd 2 -highlightthickness 2 + .top.t window create 21.0 -window .top.t.b + .top.t tag add hidden 15.36 21.0 + .top.t tag configure hidden -elide true + .top.t configure -height 15 + wm geometry .top 300x200+0+0 + # Indices 21.0, 17.0 and 15.0 are all on the same display line + # therefore index @0,0 shall be the same for all of them + .top.t see end + update + .top.t see 21.0 + update + set ind1 [.top.t index @0,0] + .top.t see end + update + .top.t see 17.0 + update + set ind2 [.top.t index @0,0] + .top.t see end + update + .top.t see 15.0 + update + set ind3 [.top.t index @0,0] + list [expr {$ind1 == $ind2}] [expr {$ind1 == $ind3}] +} {1 1} .t configure -wrap word .t delete 50.0 51.0 @@ -1583,6 +1699,29 @@ test textDisp-13.10 {TkTextSeeCmd procedure} {} { destroy $w set res } {} +test textDisp-13.11 {TkTextSeeCmd procedure} {} { + # insertion of a character at end of a line containing multi-byte + # characters and calling see at the line end shall actually show + # this character + toplevel .top2 + pack [text .top2.t2 -wrap none] + for {set i 1} {$i < 5} {incr i} { + .top2.t2 insert end [string repeat "Line $i: éèàçù" 5]\n + + } + wm geometry .top2 300x200+0+0 + update + .top2.t2 see "1.0 lineend" + update + set ref [.top2.t2 index @0,0] + .top2.t2 insert "1.0 lineend" ç + .top2.t2 see "1.0 lineend" + update + set new [.top2.t2 index @0,0] + set res [.top2.t2 compare $ref == $new] + destroy .top2 + set res +} {0} wm geom . {} .t configure -wrap none @@ -2019,6 +2158,70 @@ test textDisp-16.40 {text count -xpixels} { [.t count -xpixels 1.0 "1.0 displaylineend"] \ [.t count -xpixels 1.0 end] } {35 -35 0 42 42 42 0} +test textDisp-16.41 {text count -xpixels with indices in elided lines} { + set res {} + .t delete 1.0 end + for {set i 1} {$i < 40} {incr i} { + .t insert end [string repeat "Line $i" 20] + .t insert end "\n" + } + .t configure -wrap none + .t tag add hidden 5.15 20.15 + .t tag configure hidden -elide true + lappend res [.t count -xpixels 5.15 6.0] \ + [.t count -xpixels 5.15 6.1] \ + [.t count -xpixels 6.0 6.1] \ + [.t count -xpixels 6.1 6.2] \ + [.t count -xpixels 6.1 6.0] \ + [.t count -xpixels 6.0 7.0] \ + [.t count -xpixels 6.1 7.1] \ + [.t count -xpixels 15.0 20.15] \ + [.t count -xpixels 20.15 20.16] \ + [.t count -xpixels 20.16 20.15] + .t tag remove hidden 20.0 20.15 + lappend res [expr {[.t count -xpixels 5.0 20.0] != 0}] +} [list 0 0 0 0 0 0 0 0 $fixedWidth -$fixedWidth 1] +test textDisp-16.42 {TkTextYviewCmd procedure with indices in elided lines} { + .t configure -wrap none + .t delete 1.0 end + for {set i 1} {$i < 100} {incr i} { + .t insert end [string repeat "Line $i" 20] + .t insert end "\n" + } + .t tag add hidden 5.15 20.15 + .t tag configure hidden -elide true + .t yview 35.0 + .t yview scroll [expr {- 15 * $fixedHeight}] pixels + update + .t index @0,0 +} {5.0} +test textDisp-16.43 {TkTextYviewCmd procedure with indices in elided lines} { + .t configure -wrap none + .t delete 1.0 end + for {set i 1} {$i < 100} {incr i} { + .t insert end [string repeat "Line $i" 20] + .t insert end "\n" + } + .t tag add hidden 5.15 20.15 + .t tag configure hidden -elide true + .t yview 35.0 + .t yview scroll -15 units + update + .t index @0,0 +} {5.0} +test textDisp-16.44 {TkTextYviewCmd procedure, scroll down, with elided lines} { + .t configure -wrap none + .t delete 1.0 end + foreach x [list 0 1 2 3 4 5 6 7 8 9 0] { + .t insert end "$x aaa1\n$x bbb2\n$x ccc3\n$x ddd4\n$x eee5\n$x fff6" + .t insert end "$x 1111\n$x 2222\n$x 3333\n$x 4444\n$x 5555\n$x 6666" hidden + } + .t tag configure hidden -elide true ; # 5 hidden lines + update + .t see [expr {5 + [winfo height .t] / $fixedHeight} + 1].0 + update + .t index @0,0 +} {2.0} .t delete 1.0 end foreach i {a b c d e f g h i j k l m n o p q r s t u v w x y z} { @@ -2444,7 +2647,7 @@ test textDisp-19.11.23 {TextWidgetCmd procedure, "index +displaylines"} { [.t index "12.0 +2d lines"] [.t index "11.0 +2d lines"] \ [.t index "13.0 +2d lines"] [.t index "13.0 +3d lines"] \ [.t index "13.0 +4d lines"] -} {16.17 16.33 16.28 16.46 16.28 16.49 16.65 17.0} +} {16.17 16.33 16.28 16.46 16.28 16.49 16.65 16.72} .t tag remove elide 1.0 end test textDisp-19.11.24 {TextWidgetCmd procedure, "index +/-displaylines"} { list [.t index "11.5 + -1 display lines"] \ @@ -2553,6 +2756,63 @@ test textDisp-19.16 {count -ypixels} { [.t count -ypixels 16.0 "16.0 displaylineend +1c"] \ [.t count -ypixels "16.0 +1 displaylines" "16.0 +4 displaylines +3c"] } [list [expr {260 + 20 * $fixedDiff}] [expr {260 + 20 * $fixedDiff}] $fixedHeight [expr {2*$fixedHeight}] $fixedHeight [expr {3*$fixedHeight}]] +test textDisp-19.17 {count -ypixels with indices in elided lines} { + .t configure -wrap none + .t delete 1.0 end + for {set i 1} {$i < 100} {incr i} { + .t insert end [string repeat "Line $i" 20] + .t insert end "\n" + } + .t tag add hidden 5.15 20.15 + .t tag configure hidden -elide true + set res {} + update + lappend res \ + [.t count -ypixels 1.0 6.0] \ + [.t count -ypixels 2.0 7.5] \ + [.t count -ypixels 5.0 8.5] \ + [.t count -ypixels 6.1 6.2] \ + [.t count -ypixels 6.1 18.8] \ + [.t count -ypixels 18.0 20.50] \ + [.t count -ypixels 5.2 20.60] \ + [.t count -ypixels 20.60 20.70] \ + [.t count -ypixels 5.0 25.0] \ + [.t count -ypixels 25.0 5.0] \ + [.t count -ypixels 25.4 27.50] \ + [.t count -ypixels 35.0 38.0] + .t yview 35.0 + lappend res [.t count -ypixels 5.0 25.0] +} [list [expr {4 * $fixedHeight}] [expr {3 * $fixedHeight}] 0 0 0 0 0 0 [expr {5 * $fixedHeight}] [expr {- 5 * $fixedHeight}] [expr {2 * $fixedHeight}] [expr {3 * $fixedHeight}] [expr {5 * $fixedHeight}]] +test textDisp-19.18 {count -ypixels with indices in elided lines} { + .t configure -wrap none + .t delete 1.0 end + for {set i 1} {$i < 100} {incr i} { + .t insert end [string repeat "Line $i" 20] + .t insert end "\n" + } + .t tag add hidden 5.15 20.15 + .t tag configure hidden -elide true + .t yview 35.0 + set res {} + update + lappend res [.t count -ypixels 5.0 25.0] + .t yview scroll [expr {- 15 * $fixedHeight}] pixels + update + lappend res [.t count -ypixels 5.0 25.0] +} [list [expr {5 * $fixedHeight}] [expr {5 * $fixedHeight}]] +test textDisp-19.19 {count -ypixels with indices in elided lines} { + .t configure -wrap char + .t delete 1.0 end + for {set i 1} {$i < 25} {incr i} { + .t insert end [string repeat "Line $i -" 6] + .t insert end "\n" + } + .t tag add hidden 5.27 11.0 + .t tag configure hidden -elide true + .t yview 5.0 + update + set res [list [.t count -ypixels 5.0 11.0] [.t count -ypixels 5.0 11.20]] +} [list [expr {1 * $fixedHeight}] [expr {2 * $fixedHeight}]] .t delete 1.0 end .t insert end "Line 1" for {set i 2} {$i <= 200} {incr i} { @@ -2722,6 +2982,42 @@ test textDisp-22.9 {TkTextCharBbox, handling of spacing} {textfonts} { [.t bbox 1.1] [.t bbox 2.9] } [list [list 24 11 10 4] [list 55 [expr {$fixedDiff/2 + 15}] 10 4] [list 10 [expr {2*$fixedDiff + 43}] 10 4] [list 76 [expr {2*$fixedDiff + 40}] 10 4] [list 10 11 7 $fixedHeight] [list 69 [expr {$fixedDiff + 34}] 7 $fixedHeight]] .t tag delete spacing +test textDisp-22.10 {TkTextCharBbox, handling of elided lines} {textfonts} { + .t configure -wrap char + .t delete 1.0 end + for {set i 1} {$i < 10} {incr i} { + .t insert end "Line $i - Line [format %c [expr 64+$i]]\n" + } + .t tag add hidden 2.8 2.13 + .t tag add hidden 6.8 7.13 + .t tag configure hidden -elide true + update + list \ + [expr {[lindex [.t bbox 2.9] 0] - [lindex [.t bbox 2.8] 0]}] \ + [expr {[lindex [.t bbox 2.10] 0] - [lindex [.t bbox 2.8] 0]}] \ + [expr {[lindex [.t bbox 2.13] 0] - [lindex [.t bbox 2.8] 0]}] \ + [expr {[lindex [.t bbox 6.9] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 6.10] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 6.13] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 6.14] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 6.15] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 7.0] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 7.1] 0] - [lindex [.t bbox 6.8] 0]}] \ + [expr {[lindex [.t bbox 7.12] 0] - [lindex [.t bbox 6.8] 0]}] +} [list 0 0 0 0 0 0 0 0 0 0 0] +test textDisp-22.11 {TkTextCharBbox, handling of wrapped elided lines} {textfonts} { + .t configure -wrap char + .t delete 1.0 end + for {set i 1} {$i < 10} {incr i} { + .t insert end "Line $i - Line _$i - Lines .$i - Line [format %c [expr 64+$i]]\n" + } + .t tag add hidden 1.30 2.5 + .t tag configure hidden -elide true + update + list \ + [expr {[lindex [.t bbox 1.30] 0] - [lindex [.t bbox 2.4] 0]}] \ + [expr {[lindex [.t bbox 1.30] 0] - [lindex [.t bbox 2.5] 0]}] +} [list 0 0] .t delete 1.0 end .t insert end "Line 1" |