app_window_frame_view.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Copyright 2014 The Chromium Authors. All rights reserved.
2227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Use of this source code is governed by a BSD-style license that can be
3227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// found in the LICENSE file.
4227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
5227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "apps/ui/views/app_window_frame_view.h"
6227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
7227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "base/strings/utf_string_conversions.h"
8227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "extensions/common/draggable_region.h"
9227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "grit/theme_resources.h"
10227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "grit/ui_strings.h"  // Accessibility names
11227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "third_party/skia/include/core/SkPaint.h"
12227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "third_party/skia/include/core/SkRegion.h"
13227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/base/hit_test.h"
14227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/base/l10n/l10n_util.h"
15227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/base/resource/resource_bundle.h"
16227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/gfx/canvas.h"
17227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/gfx/image/image.h"
18227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/gfx/path.h"
19227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/views/controls/button/image_button.h"
20227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/views/layout/grid_layout.h"
21227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/views/views_delegate.h"
22227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/views/widget/widget.h"
23227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/views/widget/widget_delegate.h"
24227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
25227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#if defined(USE_AURA)
26227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/aura/env.h"
27227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#include "ui/aura/window.h"
28227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks#endif
29227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
30227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksnamespace {
31227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks// Height of the chrome-style caption, in pixels.
32227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksconst int kCaptionHeight = 25;
33227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks}  // namespace
34227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
35227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksnamespace apps {
36227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
37227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksconst char AppWindowFrameView::kViewClassName[] =
38227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    "browser/ui/views/extensions/AppWindowFrameView";
39227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
40227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin HendricksAppWindowFrameView::AppWindowFrameView()
41227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    : widget_(NULL),
42227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      draggable_region_(NULL),
43227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      close_button_(NULL),
44227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      maximize_button_(NULL),
45227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      restore_button_(NULL),
46227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      minimize_button_(NULL),
47227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      resize_inside_bounds_size_(0),
48227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      resize_outside_bounds_size_(0),
49227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks      resize_area_corner_size_(0) {}
50227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
51227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin HendricksAppWindowFrameView::~AppWindowFrameView() {}
52227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
53227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksvoid AppWindowFrameView::Init(views::Widget* widget,
54227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              bool draw_frame,
55227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              const SkColor& frame_color,
56227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              const SkRegion* draggable_region,
57227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              int resize_inside_bounds_size,
58227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              int resize_outside_bounds_size,
59227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              int resize_outside_scale_for_touch,
60227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks                              int resize_area_corner_size) {
61227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  widget_ = widget;
62227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  draw_frame_ = draw_frame;
63227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  frame_color_ = frame_color;
64227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  draggable_region_ = draggable_region;
65227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  resize_inside_bounds_size_ = resize_inside_bounds_size;
66227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  resize_outside_bounds_size_ = resize_outside_bounds_size;
67227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  resize_area_corner_size_ = resize_area_corner_size;
68227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks
69227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks  if (draw_frame) {
70227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
71227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    close_button_ = new views::ImageButton(this);
72227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    close_button_->SetImage(
73227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_NORMAL,
74227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE).ToImageSkia());
75227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    close_button_->SetImage(
76227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_HOVERED,
77227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_H).ToImageSkia());
78227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    close_button_->SetImage(
79227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_PRESSED,
80227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_P).ToImageSkia());
81227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    close_button_->SetAccessibleName(
82227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
83227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    AddChildView(close_button_);
84227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    maximize_button_ = new views::ImageButton(this);
85227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    maximize_button_->SetImage(
86227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_NORMAL,
87227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE).ToImageSkia());
88227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    maximize_button_->SetImage(
89227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_HOVERED,
90227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_H).ToImageSkia());
91227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    maximize_button_->SetImage(
92227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_PRESSED,
93227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_P).ToImageSkia());
94227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    maximize_button_->SetImage(
95227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_DISABLED,
96227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_D).ToImageSkia());
97227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    maximize_button_->SetAccessibleName(
98227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
99227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    AddChildView(maximize_button_);
100227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    restore_button_ = new views::ImageButton(this);
101227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    restore_button_->SetImage(
102227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_NORMAL,
103227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE).ToImageSkia());
104227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    restore_button_->SetImage(
105227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_HOVERED,
106227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_H).ToImageSkia());
107227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    restore_button_->SetImage(
108227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_PRESSED,
109227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_P).ToImageSkia());
110227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    restore_button_->SetAccessibleName(
111227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        l10n_util::GetStringUTF16(IDS_APP_ACCNAME_RESTORE));
112227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    AddChildView(restore_button_);
113227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    minimize_button_ = new views::ImageButton(this);
114227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    minimize_button_->SetImage(
115227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        views::CustomButton::STATE_NORMAL,
116227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks        rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE).ToImageSkia());
117227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks    minimize_button_->SetImage(
118        views::CustomButton::STATE_HOVERED,
119        rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_H).ToImageSkia());
120    minimize_button_->SetImage(
121        views::CustomButton::STATE_PRESSED,
122        rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_P).ToImageSkia());
123    minimize_button_->SetAccessibleName(
124        l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
125    AddChildView(minimize_button_);
126  }
127}
128
129// views::NonClientFrameView implementation.
130
131gfx::Rect AppWindowFrameView::GetBoundsForClientView() const {
132  if (!draw_frame_ || widget_->IsFullscreen())
133    return bounds();
134  return gfx::Rect(
135      0, kCaptionHeight, width(), std::max(0, height() - kCaptionHeight));
136}
137
138gfx::Rect AppWindowFrameView::GetWindowBoundsForClientBounds(
139    const gfx::Rect& client_bounds) const {
140  gfx::Rect window_bounds = client_bounds;
141#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
142  // Get the difference between the widget's client area bounds and window
143  // bounds, and grow |window_bounds| by that amount.
144  gfx::Insets native_frame_insets =
145      widget_->GetClientAreaBoundsInScreen().InsetsFrom(
146          widget_->GetWindowBoundsInScreen());
147  window_bounds.Inset(native_frame_insets);
148#endif
149  if (!draw_frame_) {
150    // Enforce minimum size (1, 1) in case that client_bounds is passed with
151    // empty size. This could occur when the frameless window is being
152    // initialized.
153    if (window_bounds.IsEmpty()) {
154      window_bounds.set_width(1);
155      window_bounds.set_height(1);
156    }
157    return window_bounds;
158  }
159
160  int closeButtonOffsetX = (kCaptionHeight - close_button_->height()) / 2;
161  int header_width = close_button_->width() + closeButtonOffsetX * 2;
162  return gfx::Rect(window_bounds.x(),
163                   window_bounds.y() - kCaptionHeight,
164                   std::max(header_width, window_bounds.width()),
165                   window_bounds.height() + kCaptionHeight);
166}
167
168int AppWindowFrameView::NonClientHitTest(const gfx::Point& point) {
169  if (widget_->IsFullscreen())
170    return HTCLIENT;
171
172  gfx::Rect expanded_bounds = bounds();
173  if (resize_outside_bounds_size_) {
174    expanded_bounds.Inset(gfx::Insets(-resize_outside_bounds_size_,
175                                      -resize_outside_bounds_size_,
176                                      -resize_outside_bounds_size_,
177                                      -resize_outside_bounds_size_));
178  }
179  // Points outside the (possibly expanded) bounds can be discarded.
180  if (!expanded_bounds.Contains(point))
181    return HTNOWHERE;
182
183  // Check the frame first, as we allow a small area overlapping the contents
184  // to be used for resize handles.
185  bool can_ever_resize = widget_->widget_delegate()
186                             ? widget_->widget_delegate()->CanResize()
187                             : false;
188  // Don't allow overlapping resize handles when the window is maximized or
189  // fullscreen, as it can't be resized in those states.
190  int resize_border = (widget_->IsMaximized() || widget_->IsFullscreen())
191                          ? 0
192                          : resize_inside_bounds_size_;
193  int frame_component = GetHTComponentForFrame(point,
194                                               resize_border,
195                                               resize_border,
196                                               resize_area_corner_size_,
197                                               resize_area_corner_size_,
198                                               can_ever_resize);
199  if (frame_component != HTNOWHERE)
200    return frame_component;
201
202  // Check for possible draggable region in the client area for the frameless
203  // window.
204  if (draggable_region_ && draggable_region_->contains(point.x(), point.y()))
205    return HTCAPTION;
206
207  int client_component = widget_->client_view()->NonClientHitTest(point);
208  if (client_component != HTNOWHERE)
209    return client_component;
210
211  // Then see if the point is within any of the window controls.
212  if (close_button_ && close_button_->visible() &&
213      close_button_->GetMirroredBounds().Contains(point)) {
214    return HTCLOSE;
215  }
216  if ((maximize_button_ && maximize_button_->visible() &&
217       maximize_button_->GetMirroredBounds().Contains(point)) ||
218      (restore_button_ && restore_button_->visible() &&
219       restore_button_->GetMirroredBounds().Contains(point))) {
220    return HTMAXBUTTON;
221  }
222  if (minimize_button_ && minimize_button_->visible() &&
223      minimize_button_->GetMirroredBounds().Contains(point)) {
224    return HTMINBUTTON;
225  }
226
227  // Caption is a safe default.
228  return HTCAPTION;
229}
230
231void AppWindowFrameView::GetWindowMask(const gfx::Size& size,
232                                       gfx::Path* window_mask) {
233  // We got nothing to say about no window mask.
234}
235
236// views::View implementation.
237
238gfx::Size AppWindowFrameView::GetPreferredSize() {
239  gfx::Size pref = widget_->client_view()->GetPreferredSize();
240  gfx::Rect bounds(0, 0, pref.width(), pref.height());
241  return widget_->non_client_view()
242      ->GetWindowBoundsForClientBounds(bounds)
243      .size();
244}
245
246void AppWindowFrameView::Layout() {
247  if (!draw_frame_)
248    return;
249  gfx::Size close_size = close_button_->GetPreferredSize();
250  const int kButtonOffsetY = 0;
251  const int kButtonSpacing = 1;
252  const int kRightMargin = 3;
253
254  close_button_->SetBounds(width() - kRightMargin - close_size.width(),
255                           kButtonOffsetY,
256                           close_size.width(),
257                           close_size.height());
258
259  bool can_ever_resize = widget_->widget_delegate()
260                             ? widget_->widget_delegate()->CanResize()
261                             : false;
262  maximize_button_->SetEnabled(can_ever_resize);
263  gfx::Size maximize_size = maximize_button_->GetPreferredSize();
264  maximize_button_->SetBounds(
265      close_button_->x() - kButtonSpacing - maximize_size.width(),
266      kButtonOffsetY,
267      maximize_size.width(),
268      maximize_size.height());
269  gfx::Size restore_size = restore_button_->GetPreferredSize();
270  restore_button_->SetBounds(
271      close_button_->x() - kButtonSpacing - restore_size.width(),
272      kButtonOffsetY,
273      restore_size.width(),
274      restore_size.height());
275
276  bool maximized = widget_->IsMaximized();
277  maximize_button_->SetVisible(!maximized);
278  restore_button_->SetVisible(maximized);
279  if (maximized)
280    maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
281  else
282    restore_button_->SetState(views::CustomButton::STATE_NORMAL);
283
284  gfx::Size minimize_size = minimize_button_->GetPreferredSize();
285  minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
286  minimize_button_->SetBounds(
287      maximize_button_->x() - kButtonSpacing - minimize_size.width(),
288      kButtonOffsetY,
289      minimize_size.width(),
290      minimize_size.height());
291}
292
293void AppWindowFrameView::OnPaint(gfx::Canvas* canvas) {
294  if (!draw_frame_)
295    return;
296
297  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
298  if (ShouldPaintAsActive()) {
299    close_button_->SetImage(
300        views::CustomButton::STATE_NORMAL,
301        rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE).ToImageSkia());
302  } else {
303    close_button_->SetImage(
304        views::CustomButton::STATE_NORMAL,
305        rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_U).ToImageSkia());
306  }
307
308  // TODO(jeremya): different look for inactive?
309  SkPaint paint;
310  paint.setAntiAlias(false);
311  paint.setStyle(SkPaint::kFill_Style);
312  paint.setColor(frame_color_);
313  gfx::Path path;
314  path.moveTo(0, 0);
315  path.lineTo(width(), 0);
316  path.lineTo(width(), kCaptionHeight);
317  path.lineTo(0, kCaptionHeight);
318  path.close();
319  canvas->DrawPath(path, paint);
320}
321
322const char* AppWindowFrameView::GetClassName() const { return kViewClassName; }
323
324gfx::Size AppWindowFrameView::GetMinimumSize() {
325  gfx::Size min_size = widget_->client_view()->GetMinimumSize();
326  if (!draw_frame_)
327    return min_size;
328
329  // Ensure we can display the top of the caption area.
330  gfx::Rect client_bounds = GetBoundsForClientView();
331  min_size.Enlarge(0, client_bounds.y());
332  // Ensure we have enough space for the window icon and buttons.  We allow
333  // the title string to collapse to zero width.
334  int closeButtonOffsetX = (kCaptionHeight - close_button_->height()) / 2;
335  int header_width = close_button_->width() + closeButtonOffsetX * 2;
336  if (header_width > min_size.width())
337    min_size.set_width(header_width);
338  return min_size;
339}
340
341gfx::Size AppWindowFrameView::GetMaximumSize() {
342  gfx::Size max_size = widget_->client_view()->GetMaximumSize();
343
344  // Add to the client maximum size the height of any title bar and borders.
345  gfx::Size client_size = GetBoundsForClientView().size();
346  if (max_size.width())
347    max_size.Enlarge(width() - client_size.width(), 0);
348  if (max_size.height())
349    max_size.Enlarge(0, height() - client_size.height());
350
351  return max_size;
352}
353
354// views::ButtonListener implementation.
355
356void AppWindowFrameView::ButtonPressed(views::Button* sender,
357                                       const ui::Event& event) {
358  DCHECK(draw_frame_);
359  if (sender == close_button_)
360    widget_->Close();
361  else if (sender == maximize_button_)
362    widget_->Maximize();
363  else if (sender == restore_button_)
364    widget_->Restore();
365  else if (sender == minimize_button_)
366    widget_->Minimize();
367}
368
369}  // namespace apps
370