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 27 enum 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