mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 20:01:11 +00:00
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:
parent
2ebe9f4335
commit
4d5f2cec93
3 changed files with 169 additions and 88 deletions
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue