summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/tab_view.mm
blob: 5fee1d54851ed9ae68b93566ca39fd9f935c280a (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Copyright (c) 2009 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.

#include "chrome/browser/cocoa/tab_view.h"

#include "chrome/browser/cocoa/tab_window_controller.h"

@implementation TabView

- (id)initWithFrame:(NSRect)frame {
  self = [super initWithFrame:frame];
  if (self) {
    // TODO(alcor): register for theming, either here or the cell
    // [self gtm_registerForThemeNotifications];
  }
  return self;
}

- (void)dealloc {
  // [self gtm_unregisterForThemeNotifications];
  [super dealloc];
}

// Overridden so that mouse clicks come to this view (the parent of the
// hierarchy) first. We want to handle clicks and drags in this class and
// leave the background button for display purposes only.
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
  return YES;
}

// Determines which view a click in our frame actually hit. It's always this
// view, never a child.
// TODO(alcor): Figure out what to do with the close button. Are we using a
// NSButton for it, or drawing it ourselves with a cell?
- (NSView *)hitTest:(NSPoint)aPoint {
  if (NSPointInRect(aPoint, [self frame])) return self;
  return nil;
}

// Handle clicks and drags in this button. We get here because we have
// overridden acceptsFirstMouse: and the click is within our bounds.
// TODO(pinkerton/alcor): This routine needs *a lot* of work to marry Cole's
// ideas of dragging cocoa views between windows and how the Browser and
// TabStrip models want to manage tabs.
- (void)mouseDown:(NSEvent *)theEvent {
  // Make sure the controller doesn't go away while we're doing this.
  // TODO(pinkerton): cole had this, not sure why it's necessary.
  [[controller_ retain] autorelease];

  // Fire the action to select the tab.
  if ([[controller_ target] respondsToSelector:[controller_ action]])
    [[controller_ target] performSelector:[controller_ action]
                               withObject:self];

  // TODO(pinkerton): necessary to pre-arrange the tabs here?

  // Resolve overlay back to original window.
  NSWindow* sourceWindow = [self window];
  if ([sourceWindow isKindOfClass:[NSPanel class]]) {
    sourceWindow = [sourceWindow parentWindow];
  }
  TabWindowController* sourceController = [sourceWindow windowController];
  TabWindowController* draggedController = nil;
  TabWindowController* targetController = nil;

  BOOL isLastTab = NO;  // TODO(alcor)

  NSWindow* dragWindow = nil;
  NSWindow* dragOverlay = nil;
  BOOL dragging = YES;
  BOOL moved = NO;

  NSDate* targetDwellDate = nil; // The date this target was first chosen
  NSMutableArray* targets = [NSMutableArray array];

  NSPoint lastPoint =
      [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]];

  while (dragging) {
    theEvent =
        [NSApp nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask
                           untilDate:[NSDate distantFuture]
                              inMode:NSDefaultRunLoopMode dequeue:YES];
    NSPoint thisPoint = [NSEvent mouseLocation];

    // Find all the windows that could be a target. It has to be of the
    // appropriate class, and visible (obviously).
    if (![targets count]) {
      for (NSWindow* window in [NSApp windows]) {
        if (window == sourceWindow && isLastTab) continue;
        if (window == dragWindow) continue;
        if (![window isVisible]) continue;
        NSWindowController *controller = [window windowController];
        if ([controller isKindOfClass:[TabWindowController class]]) {
          [targets addObject:controller];
        }
      }
    }

    // Iterate over possible targets checking for the one the mouse is in.
    // The mouse can be in either the tab or window frame.
    TabWindowController* newTarget = nil;
    for (TabWindowController* target in targets) {
      NSRect windowFrame = [[target window] frame];
      if (NSPointInRect(thisPoint, windowFrame)) {
        if (NSPointInRect(thisPoint, [[target tabStripView] frame])) {
          newTarget = target;
        }
        break;
      }
    }

    // If we're now targeting a new window, re-layout the tabs in the old
    // target and reset how long we've been hovering over this new one.
    if (targetController != newTarget) {
      targetDwellDate = [NSDate date];
      [targetController arrangeTabs];
      targetController = newTarget;
    }

    NSEventType type = [theEvent type];
    if (type == NSLeftMouseDragged) {
#if 0
      // TODO(alcor): get this working...
      moved = YES;
      if (!draggedController) {
        if (isLastTab) {
          draggedController = sourceController;
          dragWindow = [draggedController window];
        } else {
          // Detach from the current window.
          // TODO(pinkerton): Create a new window, probably with
          // Browser::CreateNewStripWithContents(), but that requires that
          // we make some changes to return the new Browser object.
          draggedController = [sourceController detachTabToNewWindow:self];
          dragWindow = [draggedController window];

          // TODO(pinkerton): reconcile how the view moves from one window
          // to the other with the TabStrip model wanting to create the tabs.
          // [[sourceController tabController] removeTab:tab_];
          // [[sourceController tabController] addTab:tab_];
          [dragWindow setAlphaValue:0.0];
        }

        // Bring the target window to the front and make sure it has a border.
        [[draggedController window] setLevel:NSFloatingWindowLevel];
        [dragWindow orderFront:nil];
        [dragWindow makeMainWindow];
        [draggedController showOverlay];
        dragOverlay = [draggedController overlayWindow];

        if (![targets count])
          [dragOverlay setHasShadow:NO];
      } else {
        NSPoint origin = [dragWindow frame].origin;
        origin.x += thisPoint.x - lastPoint.x;
        origin.y += thisPoint.y - lastPoint.y;
        [dragWindow setFrameOrigin:NSMakePoint(origin.x, origin.y)];
      }

      // If we're not hovering over any window, make the window is fully
      // opaque. Otherwise, find where the tab might be dropped and insert
      // a placeholder so it appears like it's part of that window.
      if (!targetContoller) {
        [[dragWindow animator] setAlphaValue:1.0];
      } else {
        if (![[targetController window] isKeyWindow]) {
          // && ([targetDwellDate timeIntervalSinceNow] < -REQUIRED_DWELL)) {
          [[targetController window] makeKeyAndOrderFront:nil];
          [targets removeAllObjects];
          targetDwellDate = nil;
        }

        // Compute where placeholder should go and insert it into the
        // destination tab strip.
        NSRect dropTabFrame = [[targetController tabStripView] frame];
        NSPoint point =
            [sourceWindow convertBaseToScreen:
                [self convertPointToBase:NSZeroPoint]];
        int x = NSWidth([self bounds]) / 2 + point.x - dropTabFrame.origin.x;
        [targetController insertPlaceholderForTab:tab_ atLocation:x];
        [targetController arrangeTabs];

        if (!targetController)
          [dragWindow makeKeyAndOrderFront:nil];
        [[dragWindow animator] setAlphaValue:targetController ? 0.0 : 0.333];

        [[[draggedController overlayWindow] animator]
            setAlphaValue:targetController ? 0.85 : 1.0];
        // [setAlphaValue:targetController ? 0.0 : 0.6];
      }
#endif
    } else if (type == NSLeftMouseUp) {
      // Mouse up, break out of the drag event tracking loop
      dragging = NO;
    }
    lastPoint = thisPoint;
  }  // while tracking mouse

  // The drag/click is done. If the user dragged the mouse, finalize the drag
  // and clean up.
  if (moved) {
    TabWindowController *dropController = targetController;
    if (dropController) {
      NSRect adjustedFrame = [self bounds];
      NSRect dropTabFrame =  [[dropController tabStripView] frame];
      adjustedFrame.origin = [self convertPointToBase:NSZeroPoint];
      adjustedFrame.origin =
          [sourceWindow convertBaseToScreen:adjustedFrame.origin];
      adjustedFrame.origin.x = adjustedFrame.origin.x - dropTabFrame.origin.x;
//    adjustedFrame.origin.y = adjustedFrame.origin.y - dropTabFrame.origin.y;
//    adjustedFrame.size.height += adjustedFrame.origin.y;
      adjustedFrame.origin.y = 0;

      // TODO(alcor): get add tab stuff working
      // [dropController addTab:tab_];

      [self setFrame:adjustedFrame];
      [dropController arrangeTabs];
      [draggedController close];
      [dropController showWindow:nil];
    } else {
      [[dragWindow animator] setAlphaValue:1.0];
      [dragOverlay setHasShadow:NO];
      [draggedController removeOverlayAfterDelay:
      [[NSAnimationContext currentContext] duration]];
      [dragWindow makeKeyAndOrderFront:nil];

      [[draggedController window] setLevel:NSNormalWindowLevel];
      [draggedController arrangeTabs];
    }
    [sourceController arrangeTabs];
  }
}

@end