discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Doubleclicking NSTextView


From: Wolfgang Lux
Subject: Re: Doubleclicking NSTextView
Date: Sun, 2 Sep 2007 17:40:46 +0200

Andreas Höschler wrote:

- (void)mouseDown:(NSEvent *)theEvent
{
...
+  if (startIndex != (unsigned)-1 && startIndex > 0)
+     {
+     NSString *string = [self string];
+ if ([string characterAtIndex:startIndex - 1] == '\n') startIndex--;
+     }
...
}

Unfortunately, this change is not correct (if you double click on the beginning of a line the previous line will be selected). In fact, it merely tries to work around the problem that -characterIndexForPoint: returns a wrong result in the first place. A better fix for the whole problem is below.

- (NSRange)selectionRangeForProposedRange:(NSRange) proposedCharRange granularity:(NSSelectionGranularity)gr
{
...
+      switch (gr)
+        {
+        case NSSelectByCharacter:
+            break;
+        default:
+            return [string lineRangeForRange:proposedCharRange];
+            break;
+        }
...
)

I do not really understand this change and could not find an example where it makes a difference.

Okay, now for a better solution of the problem. First of all, NSLayoutManager's - glyphIndexForPoint:inTextContainer:fractionOfDistanceThroughGlyph: is returning wrong information here because it ignores characters which are not shown and control characters when computing the position even though such characters are relevant for layout. In particular, if you click after the last character of a line, the method returns the index of the last character in the line and fraction 1 rather than the index of the newline character (and fraction 1). The following patch corrects this. Incidentally, the same applies to tab characters. Without this fix, if you click in the space of a tab character, the method returns the index of the character *before* the tab character (unless it is the first character in the line) and not that of the tab character.

--- Source/NSLayoutManager.m    (Revision 25440)
+++ Source/NSLayoutManager.m    (Arbeitskopie)
@@ -696,11 +696,10 @@
       last_visible = lf->pos;
for (j = lp->pos - glyph_pos; j + glyph_pos < lp->pos + lp- >length;)
        {
-         if (r->glyphs[j].isNotShown || r->glyphs[j].g == NSControlGlyph ||
-             !r->glyphs[j].g)
+         if (!r->glyphs[j].g)
            {
              GLYPH_STEP_FORWARD(r, j, glyph_pos, char_pos)
-               continue;
+             continue;
            }
          last_visible = j + glyph_pos;

The other part of the problem is that we do not want NSTextView's - characterIndexForPoint: method to increment the position even if fraction is greater than 0.5 if the new position is in the next line. This is fixed by the patch below. In addition, - characterIndexForPoint: is not correct according to the Mac OS X documentation (and the implementation) because - characterIndexForPoint: expects a point in screen coordinates rather than view coordinates. Therefore, I have take the liberty to introduce a new private method _characterIndexForPoint:respectFraction: to compute the index in view coordinates. Unless respectFraction is NO, the index is incremented to match the closest character on the same line. Note that - characterIndexForPoint: passes NO for this parameter, which is compatible with the Mac OS X implementation of this method.

--- NSSource/NSTextView.m       (Revision 25440)
+++ NSSource/NSTextView.m       (Arbeitskopie)
@@ -1793,7 +1793,8 @@
TODO: make sure this is only called when _layoutManager is known non- nil,
or add guards
*/
-- (unsigned int) characterIndexForPoint: (NSPoint)point
+- (unsigned int) _characterIndexForPoint: (NSPoint)point
+                        respectFraction: (BOOL)respectFraction
{
   unsigned     index;
   float                fraction;
@@ -1808,13 +1809,21 @@
     return (unsigned int)-1;
   index = [_layoutManager characterIndexForGlyphAtIndex: index];
-  if (fraction > 0.5 && index < [_textStorage length])
+ if (respectFraction && fraction > 0.5 && index < [_textStorage length] &&
+      [[_textStorage string] characterAtIndex:index] != '\n')
     {
       index++;
     }
   return index;
}
+- (unsigned int) characterIndexForPoint: (NSPoint)point
+{
+  point = [[self window] convertScreenToBase:point];
+  point = [self convertPoint:point fromView:nil];
+  return [self _characterIndexForPoint:point respectFraction:NO];
+}
+
- (NSRange) markedRange
{
   return NSMakeRange(NSNotFound, 0);
@@ -4267,7 +4276,8 @@
       dragPoint = [sender draggingLocation];
       dragPoint = [self convertPoint: dragPoint fromView: nil];
-      dragIndex = [self characterIndexForPoint: dragPoint];
+      dragIndex = [self _characterIndexForPoint: dragPoint
+                               respectFraction: YES];
       dragRange = NSMakeRange (dragIndex, 0);
       range = [self selectionRangeForProposedRange: dragRange
@@ -4302,7 +4312,8 @@
       dragPoint = [sender draggingLocation];
       dragPoint = [self convertPoint: dragPoint fromView: nil];
-      dragIndex = [self characterIndexForPoint: dragPoint];
+      dragIndex = [self _characterIndexForPoint: dragPoint
+                               respectFraction: YES];
       dragRange = NSMakeRange (dragIndex, 0);
       range = [self selectionRangeForProposedRange: dragRange
@@ -4416,7 +4427,8 @@
      possible) */
startPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
-  startIndex = [self characterIndexForPoint: startPoint];
+  startIndex = [self _characterIndexForPoint: startPoint
+                            respectFraction: [theEvent clickCount] == 1];
   if (startIndex == (unsigned int)-1)
     {
@@ -4620,7 +4632,8 @@
        point = [self convertPoint: [lastEvent locationInWindow]
                  fromView: nil];
-       proposedRange = MakeRangeFromAbs([self characterIndexForPoint: point],
+       proposedRange = MakeRangeFromAbs([self _characterIndexForPoint: point
+                                                      respectFraction: YES],
                                         startIndex);
        chosenRange = [self selectionRangeForProposedRange: proposedRange
                        granularity: granularity];

Regards
Wolfgang






reply via email to

[Prev in Thread] Current Thread [Next in Thread]