Add -insertionPointRectForCharacterIndex:inTextContainer: method to NSLayoutManager and use it in NSTextView.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@15863 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Alexander Malmberg 2003-02-03 01:52:27 +00:00
parent c5227a8144
commit 20ab4fa2df
4 changed files with 142 additions and 130 deletions

View file

@ -1,3 +1,11 @@
2003-02-03 02:49 Alexander Malmberg <alexander@malmberg.org>
* Headers/gnustep/gui/NSLayoutManager.h, Source/NSLayoutManager.m:
Add -insertionPointRectForCharacterIndex:inTextContainer: method.
* Source/NSTextView.m (-updateInsertionPointStateAndRestartTimer:):
Use it.
2003-02-02 Fred Kiefer <FredKiefer@gmx.de>
* Source/NSTextField.m

View file

@ -110,6 +110,20 @@
fractionOfDistanceThroughGlyph: (float *)partialFraction;
/*
Returns a rectangle suitable for drawing an insertion point in if the
insertion point is placed before the given character. The character index
may be any character in the text (it will handle positions "inside" a
ligature), and (unlike other methods) it may be one past the end of the
text (ie. cindex==[[_textStorage string] length]).
If the character isn't in the text container, returns NSZeroRect.
GNUstep extension.
*/
-(NSRect) insertionPointRectForCharacterIndex: (unsigned int)cindex
inTextContainer: (NSTextContainer *)textContainer;
@end

View file

@ -38,6 +38,8 @@ points inside line frag rects.
http://wiki.gnustep.org/index.php/NominallySpacedGlyphs
*/
#include <math.h>
#include "AppKit/NSLayoutManager.h"
#include "AppKit/GSLayoutManager_internal.h"
@ -126,7 +128,9 @@ container? necessary? */
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
if (tc->textContainer == container)
break;
//printf("container %i %@, %i+%i\n",i,tc->textContainer,tc->pos,tc->length);
[self _doLayoutToGlyph: last - 1];
//printf(" now %i+%i\n",tc->pos,tc->length);
if (i == num_textcontainers ||
tc->pos + tc->length < last ||
tc->pos > glyphRange.location)
@ -173,9 +177,11 @@ container? necessary? */
{
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
r->glyphs[i].g != NSControlGlyph)
x0 += [r->font advancementForGlyph: r->glyphs[i].g].width;
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
{
x0 += [r->font advancementForGlyph: r->glyphs[i].g].width;
}
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
}
}
else
x0 = NSMinX(lf->rect);
@ -202,9 +208,11 @@ container? necessary? */
{
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
r->glyphs[i].g != NSControlGlyph)
x1 += [r->font advancementForGlyph: r->glyphs[i].g].width;
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
{
x1 += [r->font advancementForGlyph: r->glyphs[i].g].width;
}
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
}
}
else
x1 = NSMaxX(lf->rect);
@ -536,6 +544,106 @@ anything visible
}
}
-(NSRect) insertionPointRectForCharacterIndex: (unsigned int)cindex
inTextContainer: (NSTextContainer *)textContainer
{
int i;
textcontainer_t *tc;
linefrag_t *lf;
float x0, x1;
NSRect r;
unsigned int glyph_index;
float fraction_through;
if (cindex == [[_textStorage string] length])
{ /* TODO: use extra line frag, etc. */
if (!cindex)
{
return NSMakeRect(1,1,1,13); /* TODO! */
}
glyph_index = [self numberOfGlyphs] - 1;
fraction_through = 1.0;
}
else
{
NSRange glyphRange, charRange;
glyphRange = [self glyphRangeForCharacterRange: NSMakeRange(cindex, 1)
actualCharacterRange: &charRange];
/* Magic to deal with composite characters and ligatures. */
fraction_through = (cindex - charRange.location) / (float)charRange.length;
fraction_through *= glyphRange.length;
glyph_index = glyphRange.location + floor(fraction_through);
fraction_through -= floor(fraction_through);
}
for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
if (tc->textContainer == textContainer)
break;
[self _doLayoutToGlyph: glyph_index - 1];
if (i == num_textcontainers)
{
NSLog(@"%s: invalid text container", __PRETTY_FUNCTION__);
return NSZeroRect;
}
if (glyph_index < tc->pos || glyph_index >= tc->pos + tc->length)
{
return NSZeroRect;
}
for (lf = tc->linefrags, i = 0; i < tc->num_linefrags; i++, lf++)
if (lf->pos + lf->length > glyph_index)
break;
{
int i, j;
linefrag_point_t *lp;
glyph_run_t *r;
unsigned int gpos, cpos;
for (j = 0, lp = lf->points; j < lf->num_points; j++)
if (lp->pos + lp->length > glyph_index)
break;
x0 = lp->p.x + lf->rect.origin.x;
r = run_for_glyph_index(lp->pos, glyphs, &gpos, &cpos);
i = lp->pos - gpos;
while (i + gpos < glyph_index)
{
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
r->glyphs[i].g != NSControlGlyph)
{
x0 += [r->font advancementForGlyph: r->glyphs[i].g].width;
}
GLYPH_STEP_FORWARD(r, i, gpos, cpos)
}
x1 = x0;
if (!r->glyphs[i].isNotShown && r->glyphs[i].g &&
r->glyphs[i].g != NSControlGlyph)
{
x1 += [r->font advancementForGlyph: r->glyphs[i].g].width;
}
}
r = lf->rect;
r.origin.x = x0 + (x1 - x0) * fraction_through;
r.size.width = 1;
r.origin.y++;
r.size.height -= 2;
return r;
}
@end

View file

@ -2856,9 +2856,6 @@ Figure out how the additional layout stuff is supposed to work.
/* TODO: this is a basic stopgap implementation; should work fine, but no
blinking. need to do a proper one once I know how */
unsigned int l;
BOOL after;
NSRange gr;
NSRect new;
if (!_layoutManager)
@ -2870,47 +2867,17 @@ Figure out how the additional layout stuff is supposed to work.
if (_layoutManager->_selected_range.length > 0 ||
_layoutManager->_selected_range.location == NSNotFound)
{
[self setNeedsDisplayInRect: _insertionPointRect];
_insertionPointRect = NSZeroRect;
return;
}
l = _layoutManager->_selected_range.location;
if (l == [self textLength])
{
if (l == 0)
{
/* TODO */
new = NSZeroRect;
new.size.width = 1;
new.size.height = 14;
new.origin.y = 0;
goto adjust;
}
l--;
after = YES;
new = NSZeroRect;
}
else
after = NO;
gr = [_layoutManager glyphRangeForCharacterRange: NSMakeRange(l,1)
actualCharacterRange: NULL];
new = [_layoutManager boundingRectForGlyphRange: gr
inTextContainer: _textContainer];
if (after)
{
new.origin.x += new.size.width - 1;
new = [_layoutManager
insertionPointRectForCharacterIndex: _layoutManager->_selected_range.location
inTextContainer: _textContainer];
new.origin.x += _textContainerOrigin.x;
new.origin.y += _textContainerOrigin.y;
}
new.size.width = 1;
adjust:
new.origin.y++;
new.size.height -= 2;
new.origin.x += _textContainerOrigin.x;
new.origin.y += _textContainerOrigin.y;
if (!NSEqualRects(new, _insertionPointRect))
{
@ -2919,92 +2886,7 @@ adjust:
[self setNeedsDisplayInRect: _insertionPointRect];
}
#if 0 /* TODO */
/* Update insertion point rect */
NSRange charRange;
NSRange glyphRange;
unsigned glyphIndex;
NSRect rect;
NSRect oldInsertionPointRect;
/* Simple case - no insertion point */
if ((_layoutManager->_selected_range.length > 0) || _layoutManager->_selected_range.location == NSNotFound)
{
if (_insertionPointTimer != nil)
{
[_insertionPointTimer invalidate];
DESTROY (_insertionPointTimer);
}
/* TODO: horizontal position of insertion point */
// _originalInsertPoint = 0; TODO
return;
}
if (_layoutManager->_selected_range.location == [[[_layoutManager textStorage] string] length])
{
rect = NSZeroRect;
goto ugly_hack_done;
}
charRange = NSMakeRange (_layoutManager->_selected_range.location, 0);
glyphRange = [_layoutManager glyphRangeForCharacterRange: charRange
actualCharacterRange: NULL];
glyphIndex = glyphRange.location;
rect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex: glyphIndex
effectiveRange: NULL];
rect.origin.x += _textContainerOrigin.x;
rect.origin.y += _textContainerOrigin.y;
if ([self selectionAffinity] != NSSelectionAffinityUpstream)
{
/* Standard case - draw the insertion point just before the
associated glyph index */
NSPoint loc = [_layoutManager locationForGlyphAtIndex: glyphIndex];
rect.origin.x += loc.x;
}
else /* _affinity == NSSelectionAffinityUpstream - non standard */
{
/* TODO - THIS DOES NOT WORK - as a consequence,
NSSelectionAffinityUpstream DOES NOT WORK */
/* Check - if the previous glyph is on another line */
/* TODO: Don't know how to do this check, this is a hack and
DOES NOT WORK - clearly this code should be inside the layout
manager anyway */
NSRect rect2;
rect2 = [_layoutManager lineFragmentRectForGlyphAtIndex: glyphIndex - 1
effectiveRange: NULL];
if (NSMinY (rect2) < NSMinY (rect))
{
/* Then we need to draw the insertion point just after the
previous glyph - DOES NOT WORK */
glyphRange = NSMakeRange (glyphIndex - 1, 1);
rect = [_layoutManager boundingRectForGlyphRange: glyphRange
inTextContainer:_textContainer];
rect.origin.x = NSMaxX (rect) - 1;
}
else /* Else, standard case again */
{
NSPoint loc = [_layoutManager locationForGlyphAtIndex: glyphIndex];
rect.origin.x += loc.x;
}
}
ugly_hack_done:
rect.size.width = 1;
oldInsertionPointRect = _insertionPointRect;
_insertionPointRect = rect;
/* Remember horizontal position of insertion point */
// _originalInsertPoint = _insertionPointRect.origin.x; TODO
#if 0 /* TODO: old code for insertion point blinking. might be useful */
if (restartFlag)
{
/* Start blinking timer if not yet started */