172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/dropdown_bar_host.h"
672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/view_ids.h"
1021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/dropdown_bar_view.h"
1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/frame/browser_view.h"
123f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/slide_animation.h"
1372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/keycodes/keyboard_codes.h"
1472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/path.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/scrollbar_size.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/focus/external_focus_tracker.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/focus/view_storage.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#if defined(OS_WIN)
213f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/win/scoped_gdi_object.h"
223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#elif defined(OS_LINUX)
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/gtk/scoped_handle_gtk.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
263f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsennamespace {
273f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#if defined(OS_WIN)
293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsentypedef base::win::ScopedRegion ScopedPlatformRegion;
303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#elif defined(OS_LINUX)
3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsentypedef ui::ScopedRegion ScopedPlatformRegion;
323f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#endif
333f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
343f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}  // namespace
353f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing gfx::Path;
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool DropdownBarHost::disable_animations_during_testing_ = false;
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// DropdownBarHost, public:
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDropdownBarHost::DropdownBarHost(BrowserView* browser_view)
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : browser_view_(browser_view),
46dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      view_(NULL),
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      animation_offset_(0),
48dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      focus_manager_(NULL),
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      esc_accel_target_registered_(false),
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_visible_(false) {
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::Init(DropdownBarView* view) {
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_ = view;
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initialize the host.
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host_.reset(CreateHost());
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host_->InitWithWidget(browser_view_->GetWidget(), gfx::Rect());
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host_->SetContentsView(view_);
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Start listening to focus changes, so we can register and unregister our
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // own handler for Escape.
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_manager_ =
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::FocusManager::GetFocusManagerForNativeView(host_->GetNativeView());
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (focus_manager_) {
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    focus_manager_->AddFocusChangeListener(this);
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // In some cases (see bug http://crbug.com/17056) it seems we may not have
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // a focus manager.  Please reopen the bug if you hit this.
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Start the process of animating the opening of the widget.
743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  animation_.reset(new ui::SlideAnimation(this));
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDropdownBarHost::~DropdownBarHost() {
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_manager_->RemoveFocusChangeListener(this);
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_tracker_.reset(NULL);
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::Show(bool animate) {
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Stores the currently focused view, and tracks focus changes so that we can
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // restore focus when the dropdown widget is closed.
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_tracker_.reset(new views::ExternalFocusTracker(view_, focus_manager_));
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!animate || disable_animations_during_testing_) {
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    is_visible_ = true;
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    animation_->Reset(1);
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AnimationProgressed(animation_.get());
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!is_visible_) {
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Don't re-start the animation.
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_visible_ = true;
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      animation_->Reset();
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      animation_->Show();
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::SetFocusAndSelection() {
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->SetFocusAndSelection(true);
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool DropdownBarHost::IsAnimating() const {
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return animation_->is_animating();
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::Hide(bool animate) {
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsVisible())
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (animate && !disable_animations_during_testing_) {
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    animation_->Reset(1.0);
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    animation_->Hide();
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StopAnimation();
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    is_visible_ = false;
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    host_->Hide();
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::StopAnimation() {
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  animation_->End();
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool DropdownBarHost::IsVisible() const {
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return is_visible_;
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// DropdownBarHost, views::FocusChangeListener implementation:
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::FocusWillChange(views::View* focused_before,
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      views::View* focused_now) {
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // First we need to determine if one or both of the views passed in are child
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // views of our view.
13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool our_view_before = focused_before && view_->Contains(focused_before);
13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool our_view_now = focused_now && view_->Contains(focused_now);
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When both our_view_before and our_view_now are false, it means focus is
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // changing hands elsewhere in the application (and we shouldn't do anything).
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Similarly, when both are true, focus is changing hands within the dropdown
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // widget (and again, we should not do anything). We therefore only need to
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // look at when we gain initial focus and when we loose it.
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!our_view_before && our_view_now) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We are gaining focus from outside the dropdown widget so we must register
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // a handler for Escape.
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    RegisterAccelerators();
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (our_view_before && !our_view_now) {
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We are losing focus to something outside our widget so we restore the
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // original handler for Escape.
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UnregisterAccelerators();
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
1563f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// DropdownBarHost, ui::AnimationDelegate implementation:
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1583f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid DropdownBarHost::AnimationProgressed(const ui::Animation* animation) {
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // First, we calculate how many pixels to slide the widget.
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size pref_size = view_->GetPreferredSize();
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  animation_offset_ = static_cast<int>((1.0 - animation_->GetCurrentValue()) *
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       pref_size.height());
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This call makes sure it appears in the right location, the size and shape
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // is correct and that it slides in the right direction.
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect dlg_rect = GetDialogPosition(gfx::Rect());
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDialogPosition(dlg_rect, false);
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Let the view know if we are animating, and at which offset to draw the
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // edges.
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->set_animation_offset(animation_offset_);
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  view_->SchedulePaint();
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid DropdownBarHost::AnimationEnded(const ui::Animation* animation) {
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Place the dropdown widget in its fully opened state.
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  animation_offset_ = 0;
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!animation_->IsShowing()) {
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Animation has finished closing.
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    host_->Hide();
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    is_visible_ = false;
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Animation has finished opening.
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// DropdownBarHost protected:
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::ResetFocusTracker() {
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_tracker_.reset(NULL);
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::GetWidgetBounds(gfx::Rect* bounds) {
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(bounds);
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *bounds = browser_view_->bounds();
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::UpdateWindowEdges(const gfx::Rect& new_pos) {
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |w| is used to make it easier to create the part of the polygon that curves
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the right side of the Find window. It essentially keeps track of the
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // x-pixel position of the right-most background image inside the view.
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(finnur): Let the view tell us how to draw the curves or convert
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // this to a CustomFrameWindow.
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int w = new_pos.width() - 6;  // -6 positions us at the left edge of the
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                // rightmost background image of the view.
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int h = new_pos.height();
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This polygon array represents the outline of the background image for the
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // window. Basically, it encompasses only the visible pixels of the
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // concatenated find_dlg_LMR_bg images (where LMR = [left | middle | right]).
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const Path::Point polygon[] = {
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      {0, 0}, {0, 1}, {2, 3}, {2, h - 3}, {4, h - 1},
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        {4, h}, {w+0, h},
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      {w+0, h - 1}, {w+1, h - 1}, {w+3, h - 3}, {w+3, 3}, {w+6, 0}
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Find the largest x and y value in the polygon.
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int max_x = 0, max_y = 0;
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < arraysize(polygon); i++) {
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    max_x = std::max(max_x, static_cast<int>(polygon[i].x));
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    max_y = std::max(max_y, static_cast<int>(polygon[i].y));
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We then create the polygon and use SetWindowRgn to force the window to draw
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // only within that area. This region may get reduced in size below.
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Path path(polygon, arraysize(polygon));
2293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  ScopedPlatformRegion region(path.CreateNativeRegion());
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Are we animating?
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (animation_offset() > 0) {
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The animation happens in two steps: First, we clip the window and then in
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // GetWidgetPosition we offset the window position so that it still looks
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // attached to the toolbar as it grows. We clip the window by creating a
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // rectangle region (that gradually increases as the animation progresses)
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // and find the intersection between the two regions using CombineRgn.
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // |y| shrinks as the animation progresses from the height of the view down
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // to 0 (and reverses when closing).
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int y = animation_offset();
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // |y| shrinking means the animation (visible) region gets larger. In other
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // words: the rectangle grows upward (when the widget is opening).
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Path animation_path;
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SkRect animation_rect = { SkIntToScalar(0), SkIntToScalar(y),
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              SkIntToScalar(max_x), SkIntToScalar(max_y) };
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    animation_path.addRect(animation_rect);
2473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    ScopedPlatformRegion animation_region(
2483f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        animation_path.CreateNativeRegion());
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    region.Set(Path::IntersectRegions(animation_region.Get(), region.Get()));
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Next, we need to increase the region a little bit to account for the
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // curved edges that the view will draw to make it look like grows out of
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the toolbar.
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Path::Point left_curve[] = {
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      {0, y+0}, {0, y+1}, {2, y+3}, {2, y+0}, {0, y+0}
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    };
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Path::Point right_curve[] = {
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      {w+3, y+3}, {w+6, y+0}, {w+3, y+0}, {w+3, y+3}
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    };
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Combine the region for the curve on the left with our main region.
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Path left_path(left_curve, arraysize(left_curve));
2633f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    ScopedPlatformRegion r(left_path.CreateNativeRegion());
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    region.Set(Path::CombineRegions(r.Get(), region.Get()));
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Combine the region for the curve on the right with our main region.
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Path right_path(right_curve, arraysize(right_curve));
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    region.Set(Path::CombineRegions(r.Get(), region.Get()));
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Now see if we need to truncate the region because parts of it obscures
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the main window border.
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect widget_bounds;
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetWidgetBounds(&widget_bounds);
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Calculate how much our current position overlaps our boundaries. If we
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // overlap, it means we have too little space to draw the whole widget and
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we allow overwriting the scrollbar before we start truncating our widget.
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(brettw) this constant is evil. This is the amount of room we've added
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // to the window size, when we set the region, it can change the size.
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static const int kAddedWidth = 7;
2833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int difference = new_pos.right() - kAddedWidth - widget_bounds.right() -
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gfx::scrollbar_size() + 1;
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (difference > 0) {
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Path::Point exclude[4];
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[0].x = max_x - difference;  // Top left corner.
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[0].y = 0;
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[1].x = max_x;               // Top right corner.
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[1].y = 0;
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[2].x = max_x;               // Bottom right corner.
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[2].y = max_y;
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[3].x = max_x - difference;  // Bottom left corner.
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    exclude[3].y = max_y;
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Subtract this region from the original region.
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Path exclude_path(exclude, arraysize(exclude));
3013f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    ScopedPlatformRegion exclude_region(exclude_path.CreateNativeRegion());
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    region.Set(Path::SubtractRegion(region.Get(), exclude_region.Get()));
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Window takes ownership of the region.
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host()->SetShape(region.release());
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::RegisterAccelerators() {
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!esc_accel_target_registered_);
31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  views::Accelerator escape(ui::VKEY_ESCAPE, false, false, false);
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_manager_->RegisterAccelerator(escape, this);
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  esc_accel_target_registered_ = true;
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DropdownBarHost::UnregisterAccelerators() {
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(esc_accel_target_registered_);
31872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  views::Accelerator escape(ui::VKEY_ESCAPE, false, false, false);
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  focus_manager_->UnregisterAccelerator(escape, this);
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  esc_accel_target_registered_ = false;
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
322