summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/cocoa/draggable_button_mixin.h
blob: 07d89f2a083e018d0cfa6451bd22a4f66f78b83e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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_

#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_