1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_UI_COCOA_DRAGGABLE_BUTTON_MIXIN_H_
6#define CHROME_BROWSER_UI_COCOA_DRAGGABLE_BUTTON_MIXIN_H_
7
8#import <Cocoa/Cocoa.h>
9
10// The design of this class is extraordinarily poor. Apologies to all clients in
11// advance. Unfortunately, the lack of multiple inheritance and our desire to
12// avoid runtime hacks makes this convoluted dance necessary.
13//
14// Buttons that want to be draggable should implement the Mixin protocol below
15// and keep an instance of the Impl as an ivar. The button should forward mouse
16// events to the impl, which will tell the button whether or not to call super
17// and let the event be handled normally.
18//
19// If the impl decides to do work on the event, methods of the mixin protocol
20// may be called. Some of the methods declared in that protocol have base
21// implementations. If the method is not implemented by the button, that base
22// implementation will be called. Otherwise, the button's implementation will
23// be called first and the DraggableButtonResult will be used to determine
24// whether the base implementation should be called. This requires the client to
25// understand what the base does.
26
27enum DraggableButtonResult {
28  // Return values for Impl methods.
29  kDraggableButtonImplDidWork,
30  kDraggableButtonMixinCallSuper,
31
32  // Return values for Mixin methods.
33  kDraggableButtonMixinDidWork,
34  kDraggableButtonImplUseBase,
35};
36
37// Mixin Protocol //////////////////////////////////////////////////////////////
38
39// Buttons that make use of the below impl need to conform to this protocol.
40@protocol DraggableButtonMixin
41
42@required
43
44// Called when a drag should start. Implement this to do any pasteboard
45// manipulation and begin the drag, usually with
46// -dragImage:at:offset:event:. Subclasses must call one of the blocking
47// -drag* methods of NSView when implementing this method.
48- (void)beginDrag:(NSEvent*)dragEvent;
49
50@optional
51
52// Called if the actsOnMouseDown property is set. Fires the button's action and
53// tracks the click.
54- (DraggableButtonResult)performMouseDownAction:(NSEvent*)theEvent;
55
56// Implement if you want to do any extra work on mouseUp, after a mouseDown
57// action has already fired.
58- (DraggableButtonResult)secondaryMouseUpAction:(BOOL)wasInside;
59
60// Resets the draggable state of the button after dragging is finished. This is
61// called by DraggableButtonImpl when the beginDrag call returns.
62- (DraggableButtonResult)endDrag;
63
64// Decides whether to treat the click as a cue to start dragging, or to instead
65// call the mouseDown/mouseUp handler as appropriate.  Implement if you want to
66// do something tricky when making the decision.
67- (DraggableButtonResult)deltaIndicatesDragStartWithXDelta:(float)xDelta
68    yDelta:(float)yDelta
69    xHysteresis:(float)xHysteresis
70    yHysteresis:(float)yHysteresis
71    indicates:(BOOL*)result;
72
73// Decides if there is enough information to stop tracking the mouse.
74// It's deltaIndicatesDragStartWithXDelta, however, that decides whether it's a
75// drag or not. Implement if you want to do something tricky when making the
76// decision.
77- (DraggableButtonResult)deltaIndicatesConclusionReachedWithXDelta:(float)xDelta
78    yDelta:(float)yDelta
79    xHysteresis:(float)xHysteresis
80    yHysteresis:(float)yHysteresis
81    indicates:(BOOL*)result;
82
83@end
84
85// Impl Interface //////////////////////////////////////////////////////////////
86
87// Implementation of the drag and drop logic. NSButton Mixin subclasses should
88// forward their mouse events to this, which in turn will call out to the mixin
89// protocol.
90@interface DraggableButtonImpl : NSObject {
91 @private
92  // The button for which this class is implementing stuff.
93  NSButton<DraggableButtonMixin>* button_;
94
95  // Is this a draggable type of button?
96  BOOL draggable_;
97
98  // Has the action already fired for this click?
99  BOOL actionHasFired_;
100
101  // Does button action happen on mouse down when possible?
102  BOOL actsOnMouseDown_;
103
104  NSTimeInterval durationMouseWasDown_;
105  NSTimeInterval whenMouseDown_;
106}
107
108@property(nonatomic) NSTimeInterval durationMouseWasDown;
109
110@property(nonatomic) NSTimeInterval whenMouseDown;
111
112// Whether the action has already fired for this click.
113@property(nonatomic) BOOL actionHasFired;
114
115// Enable or disable dragability for special buttons like "Other Bookmarks".
116@property(nonatomic) BOOL draggable;
117
118// If it has a popup menu, for example, we want to perform the action on mouse
119// down, if possible (as long as user still gets chance to drag, if
120// appropriate).
121@property(nonatomic) BOOL actsOnMouseDown;
122
123// Designated initializer.
124- (id)initWithButton:(NSButton<DraggableButtonMixin>*)button;
125
126// NSResponder implementation. NSButton subclasses should invoke these methods
127// and only call super if the return value indicates such.
128- (DraggableButtonResult)mouseDownImpl:(NSEvent*)event;
129- (DraggableButtonResult)mouseUpImpl:(NSEvent*)event;
130
131@end
132
133#endif  // CHROME_BROWSER_UI_COCOA_DRAGGABLE_BUTTON_MIXIN_H_
134