blob: 5a5eb08fa0754ef42a69e4aeb0af7225075e035a [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_DRAGGABLE_BUTTON_MIXIN_H_
#define CHROME_BROWSER_UI_COCOA_DRAGGABLE_BUTTON_MIXIN_H_
#pragma once
#import <Cocoa/Cocoa.h>
// The design of this class is extraordinarily poor. Apologies to all clients in
// advance. Unfortunately, the lack of multiple inheritance and our desire to
// avoid runtime hacks makes this convoluted dance necessary.
//
// Buttons that want to be draggable should implement the Mixin protocol below
// and keep an instance of the Impl as an ivar. The button should forward mouse
// events to the impl, which will tell the button whether or not to call super
// and let the event be handled normally.
//
// If the impl decides to do work on the event, methods of the mixin protocol
// may be called. Some of the methods declared in that protocol have base
// implementations. If the method is not implemented by the button, that base
// implementation will be called. Otherwise, the button's implementation will
// be called first and the DraggableButtonResult will be used to determine
// whether the base implementation should be called. This requires the client to
// understand what the base does.
enum DraggableButtonResult {
// Return values for Impl methods.
kDraggableButtonImplDidWork,
kDraggableButtonMixinCallSuper,
// Return values for Mixin methods.
kDraggableButtonMixinDidWork,
kDraggableButtonImplUseBase,
};
// Mixin Protocol //////////////////////////////////////////////////////////////
// Buttons that make use of the below impl need to conform to this protocol.
@protocol DraggableButtonMixin
@required
// Called when a drag should start. Implement this to do any pasteboard
// manipulation and begin the drag, usually with
// -dragImage:at:offset:event:. Subclasses must call one of the blocking
// -drag* methods of NSView when implementing this method.
- (void)beginDrag:(NSEvent*)dragEvent;
@optional
// Called if the actsOnMouseDown property is set. Fires the button's action and
// tracks the click.
- (DraggableButtonResult)performMouseDownAction:(NSEvent*)theEvent;
// Implement if you want to do any extra work on mouseUp, after a mouseDown
// action has already fired.
- (DraggableButtonResult)secondaryMouseUpAction:(BOOL)wasInside;
// Resets the draggable state of the button after dragging is finished. This is
// called by DraggableButtonImpl when the beginDrag call returns.
- (DraggableButtonResult)endDrag;
// Decides whether to treat the click as a cue to start dragging, or to instead
// call the mouseDown/mouseUp handler as appropriate. Implement if you want to
// do something tricky when making the decision.
- (DraggableButtonResult)deltaIndicatesDragStartWithXDelta:(float)xDelta
yDelta:(float)yDelta
xHysteresis:(float)xHysteresis
yHysteresis:(float)yHysteresis
indicates:(BOOL*)result;
// Decides if there is enough information to stop tracking the mouse.
// It's deltaIndicatesDragStartWithXDelta, however, that decides whether it's a
// drag or not. Implement if you want to do something tricky when making the
// decision.
- (DraggableButtonResult)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta
yDelta:(float)yDelta
xHysteresis:(float)xHysteresis
yHysteresis:(float)yHysteresis
indicates:(BOOL*)result;
@end
// Impl Interface //////////////////////////////////////////////////////////////
// Implementation of the drag and drop logic. NSButton Mixin subclasses should
// forward their mouse events to this, which in turn will call out to the mixin
// protocol.
@interface DraggableButtonImpl : NSObject {
@private
// The button for which this class is implementing stuff.
NSButton<DraggableButtonMixin>* button_;
// Is this a draggable type of button?
BOOL draggable_;
// Has the action already fired for this click?
BOOL actionHasFired_;
// Does button action happen on mouse down when possible?
BOOL actsOnMouseDown_;
NSTimeInterval durationMouseWasDown_;
NSTimeInterval whenMouseDown_;
}
@property(nonatomic) NSTimeInterval durationMouseWasDown;
@property(nonatomic) NSTimeInterval whenMouseDown;
// Whether the action has already fired for this click.
@property(nonatomic) BOOL actionHasFired;
// Enable or disable dragability for special buttons like "Other Bookmarks".
@property(nonatomic) BOOL draggable;
// If it has a popup menu, for example, we want to perform the action on mouse
// down, if possible (as long as user still gets chance to drag, if
// appropriate).
@property(nonatomic) BOOL actsOnMouseDown;
// Designated initializer.
- (id)initWithButton:(NSButton<DraggableButtonMixin>*)button;
// NSResponder implementation. NSButton subclasses should invoke these methods
// and only call super if the return value indicates such.
- (DraggableButtonResult)mouseDownImpl:(NSEvent*)event;
- (DraggableButtonResult)mouseUpImpl:(NSEvent*)event;
@end
#endif // CHROME_BROWSER_UI_COCOA_DRAGGABLE_BUTTON_MIXIN_H_