Вытащите, чтобы удалить элемент
Вы знаете этот эффект, когда вы перетаскиваете элемент из док-станции и появляется курсор облачного перетаскивания, и когда вы отпускаете его, он исчезает с эффектом пуфа? Аналогично, в Xcode, когда вы перетаскиваете точку останова за пределы канального номера, происходит то же самое.
Я хотел бы реализовать тот же эффект в моем приложении, но не могу найти правильный путь.
У меня есть потомок NSImageView для реализации протоколов NSDraggingSource и NSDraggingDestination. У меня есть несколько примеров этого представления, которые позволяют перетаскивать их содержимое между остальными (операция копирования выполняется в этом сценарии, но это имеет смысл только для того, чтобы показать, что у меня есть drag'n drop, имплантированный и полностью работающий для стандартных задач).
Теперь, когда я перетаскиваю изображение из его вида в любом месте (кроме другого экземпляра представления), я хочу, чтобы операция удаления выполнялась при удалении. Однако операция перетаскивания полностью контролируется целевым представлением. Я мог бы заставить их реагировать так, как я хочу (хотя это будет очень много работы), но он полностью не работает, если я перетаскиваю приложение.
Если бы я мог получить операцию перетаскивания delete, я мог бы легко справиться с этим:
- (void)draggedImage: (NSImage *)image
endedAt: (NSPoint)screenPoint
operation: (NSDragOperation)operation
{
if (operation == NSDragOperationDelete) {
NSRect rect = [self.window convertRectToScreen: [self convertRect: self.frame fromView: nil]];
NSShowAnimationEffect(NSAnimationEffectPoof, rect.origin, self.bounds.size, nil, nil, NULL);
}
}
Я уже пытался установить курсор удаления следующим образом:
- (void)draggingSession: (NSDraggingSession *)session
movedToPoint: (NSPoint)screenPoint
{
if (!NSPointInRect(screenPoint, self.window.frame)) {
[[NSCursor disappearingItemCursor] set];
}
}
(для простоты это для всего ветра в данный момент). Это работает до тех пор, пока я не попаду на рабочий стол или окно поиска. В начале мерцает, вероятно, потому, что Finder одновременно устанавливает свой собственный указатель перетаскивания. Это абсолютно без эффекта, когда я попадал в док. Это также происходит, когда я определяю свой собственный тип данных картона.
Кроме того, любое другое представление с включенной возможностью переадресации в моем приложении по-прежнему будет принимать мои данные перетаскивания (например, NSTextView), которые я не хочу выполнять (я пишу NSURL для перетаскивания картона с пользовательской схемой).
Update:
Я сделал еще несколько шагов. Как уже указывал Питер, важно обрабатывать draggingSession:sourceOperationmaskForDraggingContext:
, который выглядит так в моем коде:
- (NSDragOperation) draggingSession: (NSDraggingSession *)session
sourceOperationMaskForDraggingContext: (NSDraggingContext)context;
{
switch(context) {
case NSDraggingContextOutsideApplication:
return NSDragOperationDelete;
break;
case NSDraggingContextWithinApplication:
default:
return NSDragOperationDelete | NSDragOperationMove;
break;
}
}
Это решает 2 проблемы: 1) вне приложения операция перетаскивания вообще не принимается, 2) она не позволяет всем стандартным представлениям принимать эту операцию (потому что NSOutlineView, NSTextView и т.д. не обрабатывают данные операции перетаскивания), Кроме того, я создал собственный тип данных в виде картона, но это не кажется необходимым. Тем не менее, яснее иметь собственный.
К сожалению, отказ от моего потомка NSImageView (как внутри, так и вне приложения) не дает мне NSDragOperationDelete в draggedImage:endedAt:operation:
(что я указал выше), но NSDragOperationNone. Кроме того, курсор перетаскивания при перемещении мыши вне приложения является недопустимым, а не исчезающим элементом. Итак, если кто-то может решить эти две вещи, я бы принял это как ответ на мой вопрос.
Ответы
Ответ 1
Там может быть менее хакерский способ сделать это, но я могу подумать о одной возможности: после начала перетаскивания создайте прозрачное безграничное окно размером рабочего стола, чтобы стать фиктивным местом назначения. Вам может потребоваться позвонить -setIgnoresMouseEvents:
с помощью NO
, чтобы он мог получать капли, даже если он прозрачен. Вам также необходимо установить уровень окна над панелью меню (NSMainMenuWindowLevel + 1), чтобы убедиться, что перетаскивание в панель меню или док-станция все еще перехватывается вашим окном.
В качестве места назначения перетаскивания это окно должно будет проверить, находится ли какое-либо из ваших изображений под курсором. Вы можете использовать +[NSWindow windowNumberAtPoint:belowWindowWithWindowNumber:]
, чтобы найти окно под окном прозрачного оверлея, которое находится под курсором. Затем используйте -[NSApplication windowWithWindowNumber:]
, чтобы определить, является ли это одним из окон вашего приложения, и если да, вызовите -[NSView hitTest:]
в своем представлении содержимого (конвертируя координаты курсора, если это необходимо), чтобы найти представление. Затем вы можете перенаправить методы NSDraggingDestination
в это представление.
Ответ 2
Я предполагаю, что NSDragOperationDelete касается только перетаскивания/таргетинга на док-хост, и ничего больше.
NSDragOperationGeneric лучше подходит.
Обязательно не смешивайте методы, если вы идете по маршруту 10.7, предпочитайте:
-(void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
Ответ 3
OS X 10.7 или лучше:
- (void) draggingEnded: (id<NSDraggingInfo>) aInfo {
/*! Delete the current leaf if it is no longer represented in the subviews. */
RETURN_IF ( , NSNotFound != [self.visibleLeafs indexOfObject: iSelectedControl.representedObject] );
iIgnoreLeafsObservation = YES;
[self.leafs removeObject: iSelectedControl.representedObject];
iIgnoreLeafsObservation = NO;
}
Это предполагает, что вы уже визуально удалили объект. Вы также можете установить флаг во время draggingEntered: и draggingExited: указать, был ли сеанс перетаскивания последним внутри или вне себя.
В этой программе листья - это фактическая наблюдаемая коллекция, а visibleLeafs - это смоделированная коллекция представленного объекта каждого видимого элемента управления. Другие перегрузки NSDraggingDestination представляют то, что будет происходить визуально, прежде чем это произойдет.
Ответ 4
Вы можете использовать следующую команду, чтобы сделать poof в местоположении мыши:
NSShowAnimationEffect(NSAnimationEffectPoof, [NSEvent mouseLocation], NSZeroSize, NULL, NULL, NULL);
Дополнительную информацию см. в "Справочник по функциям набора приложений" .