Fix various issues when drawing controls with edited cells. This

includes a fix for the problem of properly resizing and redrawing the
editor when the cell is resized or moved during editing (bug #22678).


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@29133 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Wolfgang Lux 2009-12-17 01:03:07 +00:00
parent f3c7d70152
commit 786f376f1b
7 changed files with 243 additions and 149 deletions

View file

@ -1,3 +1,32 @@
2009-12-17 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/NSCell.m (-_setupTextWithFrame:inView:editor:delegate:range):
* Source/NSCell.m (-endEditing:):
Properly set up the field editor and its text container to handle
cells whose text is wrapped, cells which truncate their text, and
cells with scrollable contents, respectively, and prepare the
field editor and its enclosing clip view to be resizable. Use a
clip view only when necessary.
* Source/NSCell.m (-_drawEditorWithFrame:inView:): Auxiliary
method to update the frame of a cell's editor when the cell has
been resized or moved.
* Source/NSCell.m (-_setInEditing:): Helper method that allows
control views to flag their edited cell.
* Source/NSCell.m (-drawInteriorWithFrame:inView):
* Source/NSTextFieldCell.m (-drawInteriorWithFrame:inView): Do not
draw the interior of an edited cell.
* Source/NSTableView.m (-moveColumn:toColumn:): Update the column
index of the edited cell if necessary.
* Source/NSControl.m (-drawRect:):
* Source/NSTableView.m (-drawRow:clipRect:):
* Source/NSOutlineView.m (-drawRow:clipRect): Remove obsolete
workarounds to prevent an edited cell from being drawn twice.
The preceding changes also should fix #22678.
2009-12-16 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/NSTextFieldCell.m (-drawInteriorWithFrame:inView:):

View file

@ -491,6 +491,9 @@ enum {
inView: (NSView*)controlView;
- (void) _drawFocusRingWithFrame: (NSRect)cellFrame
inView: (NSView*)controlView;
- (void) _drawEditorWithFrame: (NSRect)cellFrame
inView: (NSView*)controlView;
- (void) _setInEditing: (BOOL)flag;
- (void) _updateFieldEditor: (NSText*)textObject;
@end

View file

@ -1950,23 +1950,26 @@ static NSColor *dtxtCol;
*/
- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
{
cellFrame = [self drawingRectForBounds: cellFrame];
NSRect drawingRect = [self drawingRectForBounds: cellFrame];
//FIXME: Check if this is also neccessary for images,
// Add spacing between border and inside
if (_cell.is_bordered || _cell.is_bezeled)
{
cellFrame.origin.x += 3;
cellFrame.size.width -= 6;
cellFrame.origin.y += 1;
cellFrame.size.height -= 2;
drawingRect.origin.x += 3;
drawingRect.size.width -= 6;
drawingRect.origin.y += 1;
drawingRect.size.height -= 2;
}
switch (_cell.type)
{
case NSTextCellType:
[self _drawAttributedText: [self _drawAttributedString]
inFrame: cellFrame];
if (_cell.in_editing)
[self _drawEditorWithFrame: cellFrame inView: controlView];
else
[self _drawAttributedText: [self _drawAttributedString]
inFrame: drawingRect];
break;
case NSImageCellType:
@ -1976,8 +1979,8 @@ static NSColor *dtxtCol;
NSPoint position;
size = [_cell_image size];
position.x = MAX(NSMidX(cellFrame) - (size.width/2.),0.);
position.y = MAX(NSMidY(cellFrame) - (size.height/2.),0.);
position.x = MAX(NSMidX(drawingRect) - (size.width/2.),0.);
position.y = MAX(NSMidY(drawingRect) - (size.height/2.),0.);
/*
* Images are always drawn with their bottom-left corner
* at the origin so we must adjust the position to take
@ -2105,39 +2108,68 @@ static NSColor *dtxtCol;
delegate: (id)anObject
range: (NSRange)selection
{
BOOL needsClipView;
BOOL wraps = [self wraps];
NSTextContainer *ct;
NSSize maxSize;
NSRect titleRect = [self titleRectForBounds: aRect];
NSClipView *cv = [[NSClipView alloc] initWithFrame: titleRect];
NSTextContainer *ct = [(NSTextView*)textObject textContainer];
NSRect maxRect;
// A clip view should is only created for scrollable text
if ([self isScrollable])
/* We always add a clip view if the cell is editable so that the user
can edit the whole contents even if the cell's contents normally is
clipped. */
needsClipView = [self isScrollable] || [self isEditable];
if (needsClipView)
{
/* See comments in NSStringDrawing.m about the choice of maximum size. */
maxRect = NSMakeRect(0, 0, 1e6, titleRect.size.height);
NSClipView *cv;
cv = [[NSClipView alloc] initWithFrame: titleRect];
[cv setDocumentView: textObject];
[controlView addSubview: cv];
RELEASE(cv);
}
else
{
maxRect = NSMakeRect(0, 0, titleRect.size.width, titleRect.size.height);
}
[controlView addSubview: textObject];
[controlView addSubview: cv];
RELEASE(cv);
[cv setAutoresizesSubviews: NO];
[cv setDocumentView: textObject];
[ct setContainerSize: maxRect.size];
/* Note: The order of statements matters here. We must set the text object's
horizontallyResizable and verticallyResizable attributes before setting
its frame size. Otherwise, the text object's width and/or height might
incorrectly be reduced to zero (since the text object has no contents at
this point) if the text object was resizable by its text container before.
Of course we could also have set the text object's minimum size to the
intended frame size, but then we must update the minimum size whenever
the field editor's frame is changed in -_drawEditorWithFrame:inView:.
Note that the minimum size is not relevant when a clip view is used. */
[textObject setMinSize: NSZeroSize];
[textObject setMaxSize: NSMakeSize(1e6, 1e6)];
[textObject setHorizontallyResizable: needsClipView && !wraps];
[textObject setVerticallyResizable: needsClipView];
[textObject setFrame: titleRect];
if (needsClipView)
[textObject setAutoresizingMask: NSViewWidthSizable + NSViewHeightSizable];
else
[textObject setAutoresizingMask: NSViewNotSizable];
/* Note: Order of statements matters again. The heightTracksTextView
and widthTracksTextView container attributes must be set after setting
the horizontallyResizable and verticallyResizable text view attributes
because NSTextView's setter methods include "safety code", which
always updates the container attributes along with the text view
attributes.
FIXME Fix NSTextView to only reset the text container attributes, but
never set them. However note that this may break some sloppily written
code which forgets to set the text container attributes. */
/* See comments in NSStringDrawing.m about the choice of maximum size. */
ct = [(NSTextView*)textObject textContainer];
if (wraps)
maxSize = NSMakeSize(NSWidth(titleRect), 1e6);
else
maxSize = NSMakeSize(1e6, 1e6);
[ct setContainerSize: maxSize];
[ct setWidthTracksTextView: wraps];
[ct setHeightTracksTextView: NO];
[ct setWidthTracksTextView: NO];
[textObject setFrame: maxRect];
[textObject setHorizontallyResizable: NO];
[textObject setVerticallyResizable: NO];
[textObject setMaxSize: maxRect.size];
[textObject setMinSize: titleRect.size];
[self _updateFieldEditor: textObject];
[textObject sizeToFit];
[textObject setSelectedRange: selection];
[textObject scrollRangeToVisible: selection];
@ -2159,8 +2191,13 @@ static NSColor *dtxtCol;
[textObject setDelegate: nil];
clipView = (NSClipView*)[textObject superview];
[textObject removeFromSuperview];
[clipView removeFromSuperview];
if ([clipView isKindOfClass: [NSClipView class]])
{
[clipView setDocumentView: nil];
[clipView removeFromSuperview];
}
else
[textObject removeFromSuperview];
}
/*
@ -2854,11 +2891,36 @@ static NSColor *dtxtCol;
}
}
- (void) _drawEditorWithFrame: (NSRect)cellFrame
inView: (NSView *)controlView
{
/* Look Ma', no drawing here... */
/* Adjust the text editor's frame to match cell's frame (minus the border) */
NSRect titleRect = [self titleRectForBounds: cellFrame];
NSText *textObject = [(NSControl*)controlView currentEditor];
NSView *clipView = [textObject superview];
if ([clipView isKindOfClass: [NSClipView class]])
{
[clipView setFrame: titleRect];
}
else
{
[textObject setFrame: titleRect];
}
}
- (BOOL) _sendsActionOn:(int)eventTypeMask
{
return (_action_mask & eventTypeMask);
}
- (void) _setInEditing: (BOOL)flag
{
_cell.in_editing = flag;
}
- (void) _updateFieldEditor: (NSText*)textObject
{
if (_formatter != nil)

View file

@ -625,23 +625,6 @@ static NSNotificationCenter *nc;
- (void) drawRect: (NSRect)aRect
{
/* Do nothing if there is already a text editor doing the drawing;
* otherwise, we draw everything twice. That is bad if there are
* any transparency involved (eg, even an anti-alias font!) because
* if the semi-transparent pixels are drawn over themselves they
* become less transparent (eg, an anti-alias font becomes darker
* and gives the impression of being bold).
*/
if ([self currentEditor] != nil)
{
/* Make sure the cell's control view is always set up.
* FIXME Need to draw the cell's border which is not covered by
* the field editor if the cell is bordered.
*/
[_cell setControlView: self];
return;
}
[self drawCell: _cell];
}

View file

@ -867,88 +867,89 @@ static NSImage *unexpandable = nil;
/* Draw the row between startingColumn and endingColumn */
for (i = startingColumn; i <= endingColumn; i++)
{
if (i != _editedColumn || rowIndex != _editedRow)
id item = [self itemAtRow: rowIndex];
tb = [_tableColumns objectAtIndex: i];
cell = [tb dataCellForRow: rowIndex];
if (i == _editedColumn && rowIndex == _editedRow)
[cell _setInEditing: YES];
[self _willDisplayCell: cell
forTableColumn: tb
row: rowIndex];
[cell setObjectValue: [_dataSource outlineView: self
objectValueForTableColumn: tb
byItem: item]];
drawingRect = [self frameOfCellAtColumn: i
row: rowIndex];
if (tb == _outlineTableColumn)
{
id item = [self itemAtRow: rowIndex];
NSImage *image = nil;
int level = 0;
float indentationFactor = 0.0;
// float originalWidth = drawingRect.size.width;
tb = [_tableColumns objectAtIndex: i];
cell = [tb dataCellForRow: rowIndex];
[self _willDisplayCell: cell
forTableColumn: tb
row: rowIndex];
[cell setObjectValue: [_dataSource outlineView: self
objectValueForTableColumn: tb
byItem: item]];
drawingRect = [self frameOfCellAtColumn: i
row: rowIndex];
if (tb == _outlineTableColumn)
// display the correct arrow...
if ([self isItemExpanded: item])
{
NSImage *image = nil;
int level = 0;
float indentationFactor = 0.0;
// float originalWidth = drawingRect.size.width;
// display the correct arrow...
if ([self isItemExpanded: item])
{
image = expanded;
}
else
{
image = collapsed;
}
if (![self isExpandable: item])
{
image = unexpandable;
}
level = [self levelForItem: item];
indentationFactor = _indentationPerLevel * level;
imageCell = [[NSCell alloc] initImageCell: image];
if (_indentationMarkerFollowsCell)
{
imageRect.origin.x = drawingRect.origin.x + indentationFactor;
imageRect.origin.y = drawingRect.origin.y;
}
else
{
imageRect.origin.x = drawingRect.origin.x;
imageRect.origin.y = drawingRect.origin.y;
}
if ([_delegate respondsToSelector: @selector(outlineView:willDisplayOutlineCell:forTableColumn:item:)])
{
[_delegate outlineView: self
willDisplayOutlineCell: imageCell
forTableColumn: tb
item: item];
}
/* Do not indent if the delegate set the image to nil. */
if ([imageCell image])
{
imageRect.size.width = [image size].width;
imageRect.size.height = [image size].height;
[imageCell drawWithFrame: imageRect inView: self];
drawingRect.origin.x
+= indentationFactor + [image size].width + 5;
drawingRect.size.width
-= indentationFactor + [image size].width + 5;
}
else
{
drawingRect.origin.x += indentationFactor;
drawingRect.size.width -= indentationFactor;
}
RELEASE(imageCell);
image = expanded;
}
else
{
image = collapsed;
}
[cell drawWithFrame: drawingRect inView: self];
if (![self isExpandable: item])
{
image = unexpandable;
}
level = [self levelForItem: item];
indentationFactor = _indentationPerLevel * level;
imageCell = [[NSCell alloc] initImageCell: image];
if (_indentationMarkerFollowsCell)
{
imageRect.origin.x = drawingRect.origin.x + indentationFactor;
imageRect.origin.y = drawingRect.origin.y;
}
else
{
imageRect.origin.x = drawingRect.origin.x;
imageRect.origin.y = drawingRect.origin.y;
}
if ([_delegate respondsToSelector: @selector(outlineView:willDisplayOutlineCell:forTableColumn:item:)])
{
[_delegate outlineView: self
willDisplayOutlineCell: imageCell
forTableColumn: tb
item: item];
}
/* Do not indent if the delegate set the image to nil. */
if ([imageCell image])
{
imageRect.size.width = [image size].width;
imageRect.size.height = [image size].height;
[imageCell drawWithFrame: imageRect inView: self];
drawingRect.origin.x
+= indentationFactor + [image size].width + 5;
drawingRect.size.width
-= indentationFactor + [image size].width + 5;
}
else
{
drawingRect.origin.x += indentationFactor;
drawingRect.size.width -= indentationFactor;
}
RELEASE(imageCell);
}
[cell drawWithFrame: drawingRect inView: self];
if (i == _editedColumn && rowIndex == _editedRow)
[cell _setInEditing: NO];
}
}

View file

@ -2178,6 +2178,16 @@ static void computeNewSelection
[_selectedColumns addIndex: newIndex];
}
/* Update edited cell */
if (_editedColumn == columnIndex)
{
_editedColumn = newIndex;
}
else if ((_editedColumn >= minRange) && (_editedColumn <= maxRange))
{
_editedColumn += shift;
}
/* Now really move the column */
if (columnIndex < newIndex)
{
@ -4948,20 +4958,21 @@ static BOOL selectContiguousRegion(NSTableView *self,
/* Draw the row between startingColumn and endingColumn */
for (i = startingColumn; i <= endingColumn; i++)
{
if (i != _editedColumn || rowIndex != _editedRow)
{
tb = [_tableColumns objectAtIndex: i];
cell = [tb dataCellForRow: rowIndex];
[self _willDisplayCell: cell
forTableColumn: tb
row: rowIndex];
[cell setObjectValue: [_dataSource tableView: self
objectValueForTableColumn: tb
row: rowIndex]];
drawingRect = [self frameOfCellAtColumn: i
row: rowIndex];
[cell drawWithFrame: drawingRect inView: self];
}
tb = [_tableColumns objectAtIndex: i];
cell = [tb dataCellForRow: rowIndex];
if (i == _editedColumn && rowIndex == _editedRow)
[cell _setInEditing: YES];
[self _willDisplayCell: cell
forTableColumn: tb
row: rowIndex];
[cell setObjectValue: [_dataSource tableView: self
objectValueForTableColumn: tb
row: rowIndex]];
drawingRect = [self frameOfCellAtColumn: i
row: rowIndex];
[cell drawWithFrame: drawingRect inView: self];
if (i == _editedColumn && rowIndex == _editedRow)
[cell _setInEditing: NO];
}
}

View file

@ -225,15 +225,20 @@
- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
{
NSRect titleRect;
if (_cell.in_editing)
[self _drawEditorWithFrame: cellFrame inView: controlView];
else
{
NSRect titleRect;
/* Make sure we are a text cell; titleRect might return an incorrect
rectangle otherwise. Note that the type could be different if the
user has set an image on us, which we just ignore (OS X does so as
well). */
_cell.type = NSTextCellType;
titleRect = [self titleRectForBounds: cellFrame];
[[self _drawAttributedString] drawInRect: titleRect];
/* Make sure we are a text cell; titleRect might return an incorrect
rectangle otherwise. Note that the type could be different if the
user has set an image on us, which we just ignore (OS X does so as
well). */
_cell.type = NSTextCellType;
titleRect = [self titleRectForBounds: cellFrame];
[[self _drawAttributedString] drawInRect: titleRect];
}
}
/*