First hack at expand/collapse as we drag over expandable items

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@29082 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2009-11-29 18:02:06 +00:00
parent 2ebe9f4335
commit 4d5f2cec93
3 changed files with 169 additions and 88 deletions

View file

@ -9,6 +9,7 @@
2009-11-29 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSOutlineView.m: Improve display of insertion point for DnD
Add auto-expand/collapse while dragging over expandable items.
2009-11-28 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -52,30 +52,30 @@
}
// Instance methods
- (BOOL)autoResizesOutlineColumn;
- (BOOL)autosaveExpandedItems;
- (void)collapseItem: (id)item;
- (void)collapseItem: (id)item collapseChildren: (BOOL)collapseChildren;
- (void)expandItem: (id)item;
- (void)expandItem:(id)item expandChildren:(BOOL)expandChildren;
- (BOOL)indentationMarkerFollowsCell;
- (float)indentationPerLevel;
- (BOOL)isExpandable: (id)item;
- (BOOL)isItemExpanded: (id)item;
- (id)itemAtRow: (int)row;
- (int)levelForItem: (id)item;
- (int)levelForRow:(int)row;
- (NSTableColumn *)outlineTableColumn;
- (void)reloadItem: (id)item;
- (void)reloadItem: (id)item reloadChildren: (BOOL)reloadChildren;
- (int)rowForItem: (id)item;
- (void)setAutoresizesOutlineColumn: (BOOL)resize;
- (void)setAutosaveExpandedItems: (BOOL)flag;
- (void)setDropItem:(id)item dropChildIndex: (int)childIndex;
- (void)setIndentationMarkerFollowsCell: (BOOL)followsCell;
- (void)setIndentationPerLevel: (float)newIndentLevel;
- (void)setOutlineTableColumn: (NSTableColumn *)outlineTableColumn;
- (BOOL)shouldCollapseAutoExpandedItemsForDeposited: (BOOL)deposited;
- (BOOL) autoResizesOutlineColumn;
- (BOOL) autosaveExpandedItems;
- (void) collapseItem: (id)item;
- (void) collapseItem: (id)item collapseChildren: (BOOL)collapseChildren;
- (void) expandItem: (id)item;
- (void) expandItem: (id)item expandChildren: (BOOL)expandChildren;
- (BOOL) indentationMarkerFollowsCell;
- (float) indentationPerLevel;
- (BOOL) isExpandable: (id)item;
- (BOOL) isItemExpanded: (id)item;
- (id) itemAtRow: (int)row;
- (int) levelForItem: (id)item;
- (int) levelForRow: (int)row;
- (NSTableColumn *) outlineTableColumn;
- (void) reloadItem: (id)item;
- (void) reloadItem: (id)item reloadChildren: (BOOL)reloadChildren;
- (int) rowForItem: (id)item;
- (void) setAutoresizesOutlineColumn: (BOOL)resize;
- (void) setAutosaveExpandedItems: (BOOL)flag;
- (void) setDropItem: (id)item dropChildIndex: (int)childIndex;
- (void) setIndentationMarkerFollowsCell: (BOOL)followsCell;
- (void) setIndentationPerLevel: (float)newIndentLevel;
- (void) setOutlineTableColumn: (NSTableColumn *)outlineTableColumn;
- (BOOL) shouldCollapseAutoExpandedItemsForDeposited: (BOOL)deposited;
@end /* interface of NSOutlineView */
@ -87,82 +87,81 @@
* Called to perform drop operation and returns YES if successful,
* and NO otherwise.
*/
- (BOOL)outlineView: (NSOutlineView *)outlineView
acceptDrop: (id <NSDraggingInfo>)info
item: (id)item
childIndex: (int)index;
- (BOOL) outlineView: (NSOutlineView *)outlineView
acceptDrop: (id <NSDraggingInfo>)info
item: (id)item
childIndex: (int)index;
/**
* Implementation of this method is required. Returns the child at
* the specified index for the given item.
*/
- (id)outlineView: (NSOutlineView *)outlineView
child: (int)index
ofItem: (id)item;
- (id) outlineView: (NSOutlineView *)outlineView
child: (int)index
ofItem: (id)item;
/**
* This is a required method. Returns whether or not the outline view
* item specified is expandable or not.
*/
- (BOOL)outlineView: (NSOutlineView *)outlineView
isItemExpandable: (id)item;
- (BOOL) outlineView: (NSOutlineView *)outlineView
isItemExpandable: (id)item;
/**
* Returns the item for the given persistent object.
*/
- (id)outlineView: (NSOutlineView *)outlineView
itemForPersistentObject:(id)object;
- (id) outlineView: (NSOutlineView *)outlineView
itemForPersistentObject: (id)object;
/*
* This is a required method. Returns the number of children of
* the given item.
*/
- (int)outlineView: (NSOutlineView *)outlineView
numberOfChildrenOfItem: (id)item;
- (int) outlineView: (NSOutlineView *)outlineView
numberOfChildrenOfItem: (id)item;
/**
* This is a required method. Returns the object corresponding to the
* item representing it in the outline view.
*/
- (id)outlineView: (NSOutlineView *)outlineView
objectValueForTableColumn:(NSTableColumn *)tableColumn
byItem:(id)item;
- (id) outlineView: (NSOutlineView *)outlineView
objectValueForTableColumn: (NSTableColumn *)tableColumn
byItem: (id)item;
/**
* Returns the persistent object for the item specified.
*/
- (id)outlineView: (NSOutlineView *)outlineView
persistentObjectForItem: (id)item;
- (id) outlineView: (NSOutlineView *)outlineView
persistentObjectForItem: (id)item;
/**
* Sets the object value of the given item in the given table column to the object provided.
* Sets the object value of the given item in the given table column
* to the object provided.
*/
- (void)outlineView: (NSOutlineView *)outlineView
setObjectValue: (id)object
forTableColumn: (NSTableColumn *)tableColumn
byItem: (id)item;
- (void) outlineView: (NSOutlineView *)outlineView
setObjectValue: (id)object
forTableColumn: (NSTableColumn *)tableColumn
byItem: (id)item;
/**
* Used by the Drag and Drop system. Returns the drag operation which should
* be used when -outlineView:acceptDrop:item:childIndex: is called.
*/
- (NSDragOperation)outlineView: (NSOutlineView*)outlineView
validateDrop: (id <NSDraggingInfo>)info
proposedItem: (id)item
proposedChildIndex: (int)index;
- (NSDragOperation) outlineView: (NSOutlineView*)outlineView
validateDrop: (id <NSDraggingInfo>)info
proposedItem: (id)item
proposedChildIndex: (int)index;
/**
* Causes the outline view to write the specified items to the pastboard.
*/
- (BOOL)outlineView: (NSOutlineView *)outlineView
writeItems: (NSArray*)items
toPasteboard: (NSPasteboard*)pboard;
- (BOOL) outlineView: (NSOutlineView *)outlineView
writeItems: (NSArray*)items
toPasteboard: (NSPasteboard*)pboard;
@end
/*
* Constants
*/
extern int NSOutlineViewDropOnItemIndex;
//enum { NSOutlineViewDropOnItemIndex = -1 };
extern const int NSOutlineViewDropOnItemIndex;
/*
* Notifications

View file

@ -60,7 +60,7 @@
static NSNotificationCenter *nc = nil;
static const int current_version = 1;
int NSOutlineViewDropOnItemIndex = -1;
const int NSOutlineViewDropOnItemIndex = -1;
static int lastVerticalQuarterPosition;
static int lastHorizontalHalfPosition;
@ -71,6 +71,9 @@ static int oldProposedDropRow;
static int currentDropRow;
static int oldDropLevel;
static int currentDropLevel;
static NSMutableSet *autoExpanded = nil;
static NSDate *lastDragUpdate = nil;
static NSDate *lastDragChange = nil;
// Cache the arrow images...
@ -118,6 +121,10 @@ static NSImage *unexpandable = nil;
- (void) _removeChildren: (id)startitem;
@end
@interface NSOutlineView (Private)
- (void) _autoCollapse;
@end
@implementation NSOutlineView
// Initialize the class when it is loaded
@ -130,6 +137,7 @@ static NSImage *unexpandable = nil;
collapsed = [NSImage imageNamed: @"common_outlineCollapsed"];
expanded = [NSImage imageNamed: @"common_outlineExpanded"];
unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable"];
autoExpanded = [NSMutableSet new];
}
}
@ -277,11 +285,11 @@ static NSImage *unexpandable = nil;
}
/**
* Expands the specified item. If expandChildren is set to YES, then all of the
* expandable children of this item all also expanded in a recursive fashion (i.e.
* all children, grandchildren and etc).
* Expands the specified item. If expandChildren is set to YES, then all
* of the expandable children of this item all also expanded in a recursive
* fashion (i.e. all children, grandchildren and etc).
*/
- (void)expandItem: (id)item expandChildren: (BOOL)expandChildren
- (void) expandItem: (id)item expandChildren: (BOOL)expandChildren
{
const SEL shouldExpandSelector = @selector(outlineView:shouldExpandItem:);
BOOL canExpand = YES;
@ -350,7 +358,7 @@ static NSImage *unexpandable = nil;
* Returns whether or not the indentation marker or "knob" is indented
* along with the content inside the cell.
*/
- (BOOL)indentationMarkerFollowsCell
- (BOOL) indentationMarkerFollowsCell
{
return _indentationMarkerFollowsCell;
}
@ -359,7 +367,7 @@ static NSImage *unexpandable = nil;
* Returns the amount of indentation, in points, for each level
* of the tree represented by the outline view.
*/
- (float)indentationPerLevel
- (float) indentationPerLevel
{
return _indentationPerLevel;
}
@ -367,7 +375,7 @@ static NSImage *unexpandable = nil;
/**
* Returns YES, if the item is able to be expanded, NO otherwise.
*/
- (BOOL)isExpandable: (id)item
- (BOOL) isExpandable: (id)item
{
return [_dataSource outlineView: self isItemExpandable: item];
}
@ -375,11 +383,12 @@ static NSImage *unexpandable = nil;
/**
* Returns YES if the item is expanded or open, NO otherwise.
*/
- (BOOL)isItemExpanded: (id)item
- (BOOL) isItemExpanded: (id)item
{
if (item == nil)
{
return YES;
}
// Check the array to determine if it is expanded.
return([_expandedItems containsObject: item]);
}
@ -388,7 +397,7 @@ static NSImage *unexpandable = nil;
* Returns the item at a given row. If no item exists for the given row,
* returns nil.
*/
- (id)itemAtRow: (int)row
- (id) itemAtRow: (int)row
{
if ((row >= [_items count]) || (row < 0))
{
@ -400,7 +409,7 @@ static NSImage *unexpandable = nil;
/**
* Returns the level for a given item.
*/
- (int)levelForItem: (id)item
- (int) levelForItem: (id)item
{
if (item != nil)
{
@ -414,7 +423,7 @@ static NSImage *unexpandable = nil;
/**
* Returns the level for the given row.
*/
- (int)levelForRow: (int)row
- (int) levelForRow: (int)row
{
return [self levelForItem: [self itemAtRow: row]];
}
@ -422,7 +431,7 @@ static NSImage *unexpandable = nil;
/**
* Returns the outline table column.
*/
- (NSTableColumn *)outlineTableColumn
- (NSTableColumn *) outlineTableColumn
{
return _outlineTableColumn;
}
@ -441,7 +450,7 @@ static NSImage *unexpandable = nil;
* set to YES, if it's set to NO, then only the item itself is refreshed
* from the datasource.
*/
- (void)reloadItem: (id)item reloadChildren: (BOOL)reloadChildren
- (void) reloadItem: (id)item reloadChildren: (BOOL)reloadChildren
{
int index;
id parent;
@ -561,7 +570,7 @@ static NSImage *unexpandable = nil;
* Otherwise, the indentation marker will remain at the left most position of
* the view regardless of how many levels in the content is indented.
*/
- (void)setIndentationMarkerFollowsCell: (BOOL)followsCell
- (void) setIndentationMarkerFollowsCell: (BOOL)followsCell
{
_indentationMarkerFollowsCell = followsCell;
}
@ -990,31 +999,42 @@ static NSImage *unexpandable = nil;
- (void) draggingExited: (id <NSDraggingInfo>) sender
{
[self setNeedsDisplayInRect: oldDraggingRect];
[self _autoCollapse];
[self displayIfNeeded];
DESTROY(lastDragUpdate);
DESTROY(lastDragChange);
}
- (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>) sender
{
NSPoint p = [sender draggingLocation];
NSRect newRect;
id item;
int row;
int verticalQuarterPosition;
int horizontalHalfPosition;
int levelBefore;
int levelAfter;
int level;
BOOL dropOn = NO;
NSDragOperation dragOperation = [sender draggingSourceOperationMask];
ASSIGN(lastDragUpdate, [NSDate date]);
//NSLog(@"draggingUpdated");
p = [self convertPoint: p fromView: nil];
verticalQuarterPosition =
(p.y - _bounds.origin.y) / _rowHeight * 4.;
((p.y - _bounds.origin.y) / _rowHeight) * 4.;
horizontalHalfPosition =
(p.x - _bounds.origin.y) / _indentationPerLevel * 2.;
((p.x - _bounds.origin.y) / _indentationPerLevel) * 2.;
if ((verticalQuarterPosition - oldProposedDropRow * 4 <= 2) &&
(verticalQuarterPosition - oldProposedDropRow * 4 >= -3))
row = verticalQuarterPosition;
row = row % 4;
if (row == 1 || row == 2) dropOn = YES;
if ((verticalQuarterPosition - oldProposedDropRow * 4 <= 2)
&& (verticalQuarterPosition - oldProposedDropRow * 4 >= -3))
{
row = oldProposedDropRow;
}
@ -1057,7 +1077,6 @@ static NSImage *unexpandable = nil;
if ((lastVerticalQuarterPosition != verticalQuarterPosition)
|| (lastHorizontalHalfPosition != horizontalHalfPosition))
{
id item;
int childIndex;
if (horizontalHalfPosition / 2 < levelAfter)
@ -1101,21 +1120,26 @@ static NSImage *unexpandable = nil;
childIndex = j;
}
if (YES == dropOn)
{
childIndex = NSOutlineViewDropOnItemIndex;
}
oldProposedDropRow = currentDropRow;
if ([_dataSource respondsToSelector:
@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
{
dragOperation = [_dataSource outlineView: self
validateDrop: sender
proposedItem: item
proposedChildIndex: childIndex];
validateDrop: sender
proposedItem: item
proposedChildIndex: childIndex];
}
if ((currentDropRow != oldDropRow) || (currentDropLevel != oldDropLevel))
{
NSBezierPath *path;
ASSIGN(lastDragChange, lastDragUpdate);
[self lockFocus];
[self setNeedsDisplayInRect: oldDraggingRect];
@ -1223,14 +1247,53 @@ static NSImage *unexpandable = nil;
oldDropRow = currentDropRow;
oldDropLevel = currentDropLevel;
}
else
{
if (YES == dropOn)
{
item = [_items objectAtIndex: currentDropRow];
if ([self isExpandable: item] && ![self isItemExpanded: item])
{
[self expandItem: item expandChildren: NO];
if ([self isItemExpanded: item])
{
[autoExpanded addObject: item];
}
}
}
}
}
else
{
/* If we have been hovering over an item for more than half a second,
* we should expand it.
*/
if (YES == dropOn
&& [lastDragUpdate timeIntervalSinceDate: lastDragChange] >= 0.5)
{
item = [_items objectAtIndex: currentDropRow];
if ([self isExpandable: item] && ![self isItemExpanded: item])
{
[self expandItem: item expandChildren: NO];
if ([self isItemExpanded: item])
{
[autoExpanded addObject: item];
}
}
/* Set the change date even if we didn't actually expand ... so
* we don't keep trying to expand the same item unnecessarily.
*/
ASSIGN(lastDragChange, lastDragUpdate);
}
}
return dragOperation;
}
- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender
{
BOOL result = NO;
if ([_dataSource
respondsToSelector:
@selector(outlineView:acceptDrop:item:childIndex:)])
@ -1267,14 +1330,15 @@ static NSImage *unexpandable = nil;
childIndex = j;
}
return [_dataSource
outlineView: self
acceptDrop: sender
item: item
childIndex: childIndex];
result = [_dataSource outlineView: self
acceptDrop: sender
item: item
childIndex: childIndex];
}
return NO;
[self _autoCollapse];
return result;
}
- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
@ -1875,3 +1939,20 @@ static NSImage *unexpandable = nil;
}
@end
@implementation NSOutlineView (Private)
/* Collapse all the items which were automatically expanded to allow drop.
*/
- (void) _autoCollapse
{
NSEnumerator *e;
id item;
e = [autoExpanded objectEnumerator];
while ((item = [e nextObject]) != nil)
{
[self collapseItem: item collapseChildren: YES];
}
[autoExpanded removeAllObjects];
}
@end