File: constrained_window_sheet_controller.mm

package info (click to toggle)
chromium-browser 41.0.2272.118-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 2,189,132 kB
  • sloc: cpp: 9,691,462; ansic: 3,341,451; python: 712,689; asm: 518,779; xml: 208,926; java: 169,820; sh: 119,353; perl: 68,907; makefile: 28,311; yacc: 13,305; objc: 11,385; tcl: 3,186; cs: 2,225; sql: 2,217; lex: 2,215; lisp: 1,349; pascal: 1,256; awk: 407; ruby: 155; sed: 53; php: 14; exp: 11
file content (302 lines) | stat: -rw-r--r-- 10,232 bytes parent folder | download
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// Copyright (c) 2012 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.

#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"

#include <map>

#include "base/logging.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
#include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"

namespace {

// Maps parent windows to sheet controllers.
NSMutableDictionary* g_sheetControllers;

// Get a value for the given window that can be used as a key in a dictionary.
NSValue* GetKeyForParentWindow(NSWindow* parent_window) {
  return [NSValue valueWithNonretainedObject:parent_window];
}

}  // namespace

// An invisible overlay window placed on top of the sheet's parent view.
// This window blocks interaction with the underlying view.
@interface CWSheetOverlayWindow : NSWindow {
  base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
}
@end

@interface ConstrainedWindowSheetController ()
- (id)initWithParentWindow:(NSWindow*)parentWindow;
- (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView;
- (ConstrainedWindowSheetInfo*)
    findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet;
- (void)onParentWindowWillClose:(NSNotification*)note;
- (void)onParentWindowSizeDidChange:(NSNotification*)note;
- (void)updateSheetPosition:(NSView*)parentView;
- (NSRect)overlayWindowFrameForParentView:(NSView*)parentView;
- (NSPoint)originForSheetSize:(NSSize)sheetSize
              inContainerRect:(NSRect)containerRect;
- (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow;
- (void)closeSheet:(ConstrainedWindowSheetInfo*)info
     withAnimation:(BOOL)withAnimation;
@end

@implementation CWSheetOverlayWindow

- (id)initWithContentRect:(NSRect)rect
               controller:(ConstrainedWindowSheetController*)controller {
  if ((self = [super initWithContentRect:rect
                               styleMask:NSBorderlessWindowMask
                                 backing:NSBackingStoreBuffered
                                   defer:NO])) {
    [self setOpaque:NO];
    [self setBackgroundColor:[NSColor clearColor]];
    [self setIgnoresMouseEvents:NO];
    [self setReleasedWhenClosed:NO];
    controller_.reset([controller retain]);
  }
  return self;
}

- (void)mouseDown:(NSEvent*)event {
  [controller_ onOverlayWindowMouseDown:self];
}

@end

@implementation ConstrainedWindowSheetController

+ (ConstrainedWindowSheetController*)
    controllerForParentWindow:(NSWindow*)parentWindow {
  DCHECK(parentWindow);
  ConstrainedWindowSheetController* controller =
      [g_sheetControllers objectForKey:GetKeyForParentWindow(parentWindow)];
  if (controller)
    return controller;

  base::scoped_nsobject<ConstrainedWindowSheetController> new_controller(
      [[ConstrainedWindowSheetController alloc]
          initWithParentWindow:parentWindow]);
  if (!g_sheetControllers)
    g_sheetControllers = [[NSMutableDictionary alloc] init];
  [g_sheetControllers setObject:new_controller
                         forKey:GetKeyForParentWindow(parentWindow)];
  return new_controller;
}

+ (ConstrainedWindowSheetController*)
    controllerForSheet:(id<ConstrainedWindowSheet>)sheet {
  for (ConstrainedWindowSheetController* controller in
       [g_sheetControllers objectEnumerator]) {
    if ([controller findSheetInfoForSheet:sheet])
      return controller;
  }
  return nil;
}

+ (id<ConstrainedWindowSheet>)sheetForOverlayWindow:(NSWindow*)overlayWindow {
  for (ConstrainedWindowSheetController* controller in
          [g_sheetControllers objectEnumerator]) {
    for (ConstrainedWindowSheetInfo* info in controller->sheets_.get()) {
      if ([overlayWindow isEqual:[info overlayWindow]])
        return [info sheet];
    }
  }
  return nil;
}

- (id)initWithParentWindow:(NSWindow*)parentWindow {
  if ((self = [super init])) {
    parentWindow_.reset([parentWindow retain]);
    sheets_.reset([[NSMutableArray alloc] init]);

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(onParentWindowWillClose:)
               name:NSWindowWillCloseNotification
             object:parentWindow_];
  }
  return self;
}

- (void)showSheet:(id<ConstrainedWindowSheet>)sheet
    forParentView:(NSView*)parentView {
  DCHECK(sheet);
  DCHECK(parentView);
  if (!activeView_.get())
    activeView_.reset([parentView retain]);

  // Observe the parent window's size.
  [[NSNotificationCenter defaultCenter]
      addObserver:self
         selector:@selector(onParentWindowSizeDidChange:)
             name:NSWindowDidResizeNotification
           object:parentWindow_];

  // Create an invisible overlay window.
  NSRect rect = [self overlayWindowFrameForParentView:parentView];
  base::scoped_nsobject<NSWindow> overlayWindow(
      [[CWSheetOverlayWindow alloc] initWithContentRect:rect controller:self]);
  [parentWindow_ addChildWindow:overlayWindow
                        ordered:NSWindowAbove];

  // Add an entry for the sheet.
  base::scoped_nsobject<ConstrainedWindowSheetInfo> info(
      [[ConstrainedWindowSheetInfo alloc] initWithSheet:sheet
                                             parentView:parentView
                                          overlayWindow:overlayWindow]);
  [sheets_ addObject:info];

  // Show or hide the sheet.
  if ([activeView_ isEqual:parentView])
    [info showSheet];
  else
    [info hideSheet];
}

- (NSPoint)originForSheet:(id<ConstrainedWindowSheet>)sheet
           withWindowSize:(NSSize)size {
  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
  DCHECK(info);
  NSRect containerRect =
      [self overlayWindowFrameForParentView:[info parentView]];
  return [self originForSheetSize:size inContainerRect:containerRect];
}

- (void)closeSheet:(id<ConstrainedWindowSheet>)sheet {
  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
  DCHECK(info);
  [self closeSheet:info withAnimation:YES];
}

- (void)parentViewDidBecomeActive:(NSView*)parentView {
  [[self findSheetInfoForParentView:activeView_] hideSheet];
  activeView_.reset([parentView retain]);
  [self updateSheetPosition:parentView];
  [[self findSheetInfoForParentView:activeView_] showSheet];
}

- (void)pulseSheet:(id<ConstrainedWindowSheet>)sheet {
  ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
  DCHECK(info);
  if ([activeView_ isEqual:[info parentView]])
    [[info sheet] pulseSheet];
}

- (int)sheetCount {
  return [sheets_ count];
}

- (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView {
  for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
    if ([parentView isEqual:[info parentView]])
      return info;
  }
  return NULL;
}

- (ConstrainedWindowSheetInfo*)
    findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet {
  for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
    if ([sheet isEqual:[info sheet]])
      return info;
  }
  return NULL;
}

- (void)onParentWindowWillClose:(NSNotification*)note {
  [[NSNotificationCenter defaultCenter]
      removeObserver:self
                name:NSWindowWillCloseNotification
              object:parentWindow_];

  // Close all sheets.
  NSArray* sheets = [NSArray arrayWithArray:sheets_];
  for (ConstrainedWindowSheetInfo* info in sheets)
    [self closeSheet:info withAnimation:NO];

  // Delete this instance.
  [g_sheetControllers removeObjectForKey:GetKeyForParentWindow(parentWindow_)];
  if (![g_sheetControllers count]) {
    [g_sheetControllers release];
    g_sheetControllers = nil;
  }
}

- (void)onParentWindowSizeDidChange:(NSNotification*)note {
  [self updateSheetPosition:activeView_];
}

- (void)updateSheetPosition:(NSView*)parentView {
  ConstrainedWindowSheetInfo* info =
      [self findSheetInfoForParentView:parentView];
  if (!info)
    return;

  NSRect rect = [self overlayWindowFrameForParentView:parentView];
  [[info overlayWindow] setFrame:rect display:YES];
  [[info sheet] updateSheetPosition];
}

- (NSRect)overlayWindowFrameForParentView:(NSView*)parentView {
  NSRect viewFrame = GetSheetParentBoundsForParentView(parentView);

  id<NSWindowDelegate> delegate = [[parentView window] delegate];
  if ([delegate respondsToSelector:@selector(window:
                                  willPositionSheet:
                                          usingRect:)]) {
    NSRect sheetFrame = NSZeroRect;
    // This API needs Y to be the distance from the bottom of the overlay to
    // the top of the sheet. X, width, and height are ignored.
    sheetFrame.origin.y = NSMaxY(viewFrame);
    NSRect customSheetFrame = [delegate window:[parentView window]
                             willPositionSheet:nil
                                     usingRect:sheetFrame];
    viewFrame.size.height += NSMinY(customSheetFrame) - NSMinY(sheetFrame);
  }

  viewFrame.origin = [[parentView window] convertBaseToScreen:viewFrame.origin];
  return viewFrame;
}

- (NSPoint)originForSheetSize:(NSSize)sheetSize
              inContainerRect:(NSRect)containerRect {
  NSPoint origin;
  origin.x = roundf(NSMinX(containerRect) +
                    (NSWidth(containerRect) - sheetSize.width) / 2.0);
  origin.y = NSMaxY(containerRect) + 5 - sheetSize.height;
  return origin;
}

- (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow {
  for (ConstrainedWindowSheetInfo* curInfo in sheets_.get()) {
    if ([overlayWindow isEqual:[curInfo overlayWindow]]) {
      [self pulseSheet:[curInfo sheet]];
      [[curInfo sheet] makeSheetKeyAndOrderFront];
      break;
    }
  }
}

- (void)closeSheet:(ConstrainedWindowSheetInfo*)info
     withAnimation:(BOOL)withAnimation {
  if (![sheets_ containsObject:info])
    return;

  [[NSNotificationCenter defaultCenter]
      removeObserver:self
                name:NSWindowDidResizeNotification
              object:parentWindow_];

  [parentWindow_ removeChildWindow:[info overlayWindow]];
  [[info sheet] closeSheetWithAnimation:withAnimation];
  [[info overlayWindow] close];
  [sheets_ removeObject:info];
}

@end