Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(456)

Side by Side Diff: chrome/browser/ui/views/compact_nav/compact_location_bar_view_host.cc

Issue 6913026: Compact Navigation prototype (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Clang build fix. After commit/revert. Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/compact_nav/compact_location_bar_view_host.h"
6
7 #if defined(TOOLKIT_USES_GTK)
8 #include <gtk/gtk.h>
9 #endif
10
11 #include <algorithm>
12
13 #include "base/i18n/rtl.h"
14 #include "base/compiler_specific.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/platform_util.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
19 #include "chrome/browser/ui/view_ids.h"
20 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
21 #include "chrome/browser/ui/views/compact_nav/compact_location_bar_view.h"
22 #include "chrome/browser/ui/views/frame/browser_view.h"
23 #include "chrome/browser/ui/views/tabs/base_tab_strip.h"
24 #include "content/browser/renderer_host/render_view_host.h"
25 #include "content/browser/tab_contents/tab_contents.h"
26 #include "content/browser/tab_contents/tab_contents_view.h"
27 #include "content/common/notification_source.h"
28 #include "ui/base/animation/slide_animation.h"
29 #include "ui/base/keycodes/keyboard_codes.h"
30 #include "ui/gfx/rect.h"
31 #include "views/controls/scrollbar/native_scroll_bar.h"
32 #include "views/events/event.h"
33 #include "views/focus/external_focus_tracker.h"
34 #include "views/focus/view_storage.h"
35 #include "views/widget/root_view.h"
36 #include "views/widget/widget.h"
37
38 namespace {
39
40 const int kHideTimeoutInSeconds = 2;
41 // TODO(stevet): Share this with CompactLocationBarView. This is the actual
42 // height, without the overlap added.
43 const int kCompactNavbarSpacerHeight = 4;
44 const int kBookmarkBarLocationBarOverlap = 2;
45 const int kSpacerLocationbarOverlap = 1;
46
47 } // namespace
48
49 // An mouse event observer to detect a mouse click on
50 // BrowserView's content area and hide the location bar.
51 class MouseObserver : public MessageLoopForUI::Observer {
52 public:
53 MouseObserver(CompactLocationBarViewHost* host, BrowserView* view);
54 ~MouseObserver();
55
56 // MessageLoopForUI::Observer overrides.
57 #if defined(OS_WIN)
58 virtual void WillProcessMessage(const MSG& native_event) OVERRIDE;
59 virtual void DidProcessMessage(const MSG& native_event) OVERRIDE;
60 #elif defined(OS_LINUX)
61 virtual void WillProcessEvent(GdkEvent* native_event) OVERRIDE;
62 virtual void DidProcessEvent(GdkEvent* native_event) OVERRIDE;
63 #endif
64
65 void Observe(MessageLoopForUI* loop);
66 void StopObserving(MessageLoopForUI* loop);
67
68 private:
69 // TODO(mad): would be nice to have this on the views::Event class.
70 bool IsMouseEvent(const views::NativeEvent& native_event);
71
72 bool IsSameTopLevelWindow(views::NativeEvent native_event);
73
74 // Tests if the event occurred on the content area, using
75 // root window's coordinates.
76 bool HitContentArea(int x, int y);
77
78 // Tests if |p| in the root window's coordinate is within the |view|'s bound.
79 bool HitOnScreen(const views::View* view, const gfx::Point& p);
80
81 CompactLocationBarViewHost* host_;
82 BrowserView* browser_view_;
83 gfx::NativeView top_level_window_;
84 bool observing_;
85
86 DISALLOW_COPY_AND_ASSIGN(MouseObserver);
87 };
88
89 MouseObserver::MouseObserver(CompactLocationBarViewHost* host,
90 BrowserView* view)
91 : host_(host),
92 browser_view_(view),
93 observing_(false) {
94 top_level_window_ = browser_view_->GetWidget()->GetNativeView();
95 }
96
97 MouseObserver::~MouseObserver() {
98 StopObserving(MessageLoopForUI::current());
99 }
100
101 #if defined(OS_WIN)
102 void MouseObserver::WillProcessMessage(const MSG& native_event) {}
103 void MouseObserver::DidProcessMessage(const MSG& native_event) {
104 #elif defined(OS_LINUX)
105 void MouseObserver::WillProcessEvent(GdkEvent* native_event) {}
106 void MouseObserver::DidProcessEvent(GdkEvent* native_event) {
107 #endif
108 // Hide the location bar iff the mouse is pressed on the
109 // BrowserView's content area.
110 if (!IsMouseEvent(native_event))
111 return;
112 views::MouseEvent event(native_event);
113 if (event.type() == ui::ET_MOUSE_PRESSED &&
114 IsSameTopLevelWindow(native_event) &&
115 HitContentArea(event.x(), event.y())) {
116 host_->Hide(true);
117 }
118 }
119
120 void MouseObserver::Observe(MessageLoopForUI* loop) {
121 if (!observing_) {
122 loop->AddObserver(this);
123 observing_ = true;
124 }
125 }
126
127 void MouseObserver::StopObserving(MessageLoopForUI* loop) {
128 if (observing_) {
129 loop->RemoveObserver(this);
130 observing_ = false;
131 }
132 }
133
134 bool MouseObserver::IsMouseEvent(const views::NativeEvent& native_event) {
135 #if defined(OS_WIN)
136 return views::IsClientMouseEvent(native_event) ||
137 views::IsNonClientMouseEvent(native_event);
138 #elif defined(OS_LINUX)
139 return native_event->type == GDK_MOTION_NOTIFY ||
140 native_event->type == GDK_BUTTON_PRESS ||
141 native_event->type == GDK_2BUTTON_PRESS ||
142 native_event->type == GDK_3BUTTON_PRESS ||
143 native_event->type == GDK_BUTTON_RELEASE;
144 #endif
145 }
146
147 // TODO(mad): Would be nice to have a NativeEvent -> NativeWindow mapping.
148 // Then, with a GetTopLevel receiving a NativeWindow, we could do this in a
149 // platform independent way.
150 bool MouseObserver::IsSameTopLevelWindow(views::NativeEvent native_event) {
151 #if defined(OS_WIN)
152 return platform_util::GetTopLevel(native_event.hwnd) == top_level_window_;
153 #elif defined(OS_LINUX)
154 return gdk_window_get_toplevel(
155 reinterpret_cast<GdkEventAny*>(native_event)->window) ==
156 top_level_window_->window;
157 #endif
158 }
159
160 bool MouseObserver::HitContentArea(int x, int y) {
161 gfx::Point p(x, y);
162 // First, exclude the location bar as it's shown on top of
163 // content area.
164 if (HitOnScreen(host_->view(), p)) {
165 return false;
166 }
167 // Treat the bookmark as a content area when it in detached mode.
168 if (browser_view_->GetBookmarkBarView()->IsDetached() &&
169 browser_view_->IsBookmarkBarVisible() &&
170 HitOnScreen(browser_view_->GetBookmarkBarView(), p)) {
171 return true;
172 }
173 if (HitOnScreen(browser_view_->GetContentsView(), p)) {
174 return true;
175 }
176 return false;
177 }
178
179 bool MouseObserver::HitOnScreen(const views::View* view, const gfx::Point& p) {
180 gfx::Point origin(0, 0);
181 views::View::ConvertPointToScreen(view, &origin);
182 gfx::Rect new_bounds(origin, view->size());
183 return new_bounds.Contains(p);
184 }
185
186 ////////////////////////////////////////////////////////////////////////////////
187 // CompactLocationBarViewHost, public:
188
189 CompactLocationBarViewHost::CompactLocationBarViewHost(
190 BrowserView* browser_view) : DropdownBarHost(browser_view),
191 current_tab_model_index_(-1),
192 is_observing_(false) {
193 auto_hide_timer_.reset(new base::OneShotTimer<CompactLocationBarViewHost>());
194 mouse_observer_.reset(new MouseObserver(this, browser_view));
195 CompactLocationBarView* clbv = new CompactLocationBarView(this);
196 Init(clbv, clbv);
197 }
198
199 CompactLocationBarViewHost::~CompactLocationBarViewHost() {
200 // This may happen if we are destroyed during cleanup.
201 if (browser_view() && browser_view()->browser() &&
202 browser_view()->browser()->tabstrip_model()) {
203 browser_view()->browser()->tabstrip_model()->RemoveObserver(this);
204 }
205 }
206
207 CompactLocationBarView* CompactLocationBarViewHost::
208 GetCompactLocationBarView() {
209 return static_cast<CompactLocationBarView*>(view());
210 }
211
212 void CompactLocationBarViewHost::MoveWindowIfNecessary(
213 const gfx::Rect& selection_rect,
214 bool no_redraw) {
215 // We only move the window if one is currently shown. If we don't check
216 // this, then SetWidgetPosition below will end up making the Location Bar
217 // visible.
218 if (!IsVisible())
219 return;
220
221 gfx::Rect new_pos = GetDialogPosition(selection_rect);
222 SetDialogPosition(new_pos, no_redraw);
223
224 // May need to redraw our frame to accommodate bookmark bar styles.
225 view()->SchedulePaint();
226 }
227
228 ////////////////////////////////////////////////////////////////////////////////
229 // LocationBarView::Delegate implementation:
230 TabContentsWrapper* CompactLocationBarViewHost::GetTabContentsWrapper() const {
231 return browser_view()->browser()->GetSelectedTabContentsWrapper();
232 }
233
234 InstantController* CompactLocationBarViewHost::GetInstant() {
235 // TODO(stevet): Re-enable instant for compact nav.
236 // return browser_view()->browser()->instant();
237 return NULL;
238 }
239
240 void CompactLocationBarViewHost::OnInputInProgress(bool in_progress) {
241 }
242
243 ////////////////////////////////////////////////////////////////////////////////
244 // CompactLocationBarViewHost, views::AcceleratorTarget implementation:
245
246 bool CompactLocationBarViewHost::AcceleratorPressed(
247 const views::Accelerator& accelerator) {
248 if (HasFocus()) {
249 DCHECK(view() != NULL);
250 views::FocusManager* focus_manager = view()->GetFocusManager();
251 if (focus_manager)
252 focus_manager->ClearFocus();
253 }
254 Hide(true);
255 return false;
256 }
257
258 ////////////////////////////////////////////////////////////////////////////////
259 // CompactLocationBarViewHost, views::DropdownBarHost implementation:
260
261 gfx::Rect CompactLocationBarViewHost::GetDialogPosition(
262 gfx::Rect avoid_overlapping_rect) {
263 if (!browser_view() || !browser_view()->browser() ||
264 !browser_view()->browser()->tabstrip_model()) {
265 return gfx::Rect();
266 }
267 DCHECK_GE(current_tab_model_index_, 0);
268 if (!browser_view()->browser()->tabstrip_model()->ContainsIndex(
269 current_tab_model_index_)) {
270 return gfx::Rect();
271 }
272
273 gfx::Rect new_pos = GetBoundsUnderTab(current_tab_model_index_);
274
275 if (animation_offset() > 0)
276 new_pos.Offset(0, std::min(0, -animation_offset()));
277 return new_pos;
278 }
279
280 void CompactLocationBarViewHost::SetDialogPosition(const gfx::Rect& new_pos,
281 bool no_redraw) {
282 if (new_pos.IsEmpty())
283 return;
284
285 // Make sure the window edges are clipped to just the visible region. We need
286 // to do this before changing position, so that when we animate the closure
287 // of it it doesn't look like the window crumbles into the toolbar.
288 UpdateWindowEdges(new_pos);
289
290 // TODO(oshima): Animate the window clipping like find-bar.
291 SetWidgetPositionNative(new_pos, no_redraw);
292 }
293
294 ////////////////////////////////////////////////////////////////////////////////
295 // CompactLocationBarViewHost, views::TabStripModelObserver implementation:
296
297 void CompactLocationBarViewHost::TabClosingAt(TabStripModel* tab_strip_model,
298 TabContentsWrapper* contents,
299 int index) {
300 // TODO(stevet): We need to relocate the compact navigation bar if the
301 // removed tab is not the one we are currently under but the tabstrip does
302 // not have the ideal location yet because the tabs are animating at this
303 // time. Need to investigate the best way to handle this case.
304 Hide(false);
305 }
306
307 void CompactLocationBarViewHost::TabSelectedAt(TabContentsWrapper* old_contents,
308 TabContentsWrapper* new_contents,
309 int index,
310 bool user_gesture) {
311 current_tab_model_index_ = index;
312 if (new_contents && new_contents->tab_contents()->is_loading()) {
313 Show(false);
314 } else {
315 Hide(false);
316 }
317 }
318
319 void CompactLocationBarViewHost::TabMoved(TabContentsWrapper* contents,
320 int from_index,
321 int to_index) {
322 if (from_index == current_tab_model_index_) {
323 UpdateOnTabChange(to_index, false);
324 StartAutoHideTimer();
325 }
326 }
327
328 void CompactLocationBarViewHost::TabChangedAt(TabContentsWrapper* contents,
329 int index,
330 TabChangeType change_type) {
331 if (IsCurrentTabIndex(index) && change_type ==
332 TabStripModelObserver::LOADING_ONLY) {
333 bool was_not_visible = !IsVisible();
334 TabContents* tab_contents = contents->tab_contents();
335 Update(tab_contents, false);
336 if (was_not_visible) {
337 if (tab_contents->is_loading()) {
338 // Register to NavigationController LOAD_STOP so that we can autohide
339 // when loading is done.
340 if (!registrar_.IsRegistered(this, NotificationType::LOAD_STOP,
341 Source<NavigationController>(&tab_contents->controller()))) {
342 registrar_.Add(this, NotificationType::LOAD_STOP,
343 Source<NavigationController>(&tab_contents->controller()));
344 }
345 } else {
346 StartAutoHideTimer();
347 }
348 }
349 }
350 }
351
352 void CompactLocationBarViewHost::ActiveTabClicked(int index) {
353 UpdateOnTabChange(index, true);
354 }
355
356 ////////////////////////////////////////////////////////////////////////////////
357 // CompactLocationBarViewHost, NotificationObserver implementation:
358
359 void CompactLocationBarViewHost::Observe(NotificationType type,
360 const NotificationSource& source,
361 const NotificationDetails& details) {
362 switch (type.value) {
363 case NotificationType::LOAD_STOP: {
364 StartAutoHideTimer();
365 // This is one shot deal...
366 registrar_.Remove(this, NotificationType::LOAD_STOP, source);
367 break;
368 }
369 default:
370 NOTREACHED();
371 }
372 }
373
374 ////////////////////////////////////////////////////////////////////////////////
375 // CompactLocationBarViewHost public:
376
377 gfx::Rect CompactLocationBarViewHost::GetBoundsUnderTab(int model_index) const {
378 DCHECK(!browser_view()->UseVerticalTabs());
379
380 // Get the position of the left-bottom corner of the tab on the
381 // widget. The widget of the tab is same as the widget of the
382 // BrowserView which is the parent of the host.
383 BaseTabStrip* tabstrip =
384 static_cast<BaseTabStrip*>(browser_view()->tabstrip());
385 gfx::Rect tab_bounds =
386 tabstrip->ideal_bounds(tabstrip->ModelIndexToTabIndex(model_index));
387 gfx::Rect navbar_bounds(gfx::Point(tab_bounds.x(), tab_bounds.height()),
388 view()->GetPreferredSize());
389
390 // Convert our point to be relative to the widget, since the native code that
391 // draws the dropdown is not in the BrowserView coordinate system.
392 gfx::Point origin = navbar_bounds.origin();
393 views::View::ConvertPointToWidget(browser_view(), &origin);
394 navbar_bounds.set_origin(origin);
395
396 // For RTL case x() defines tab right corner.
397 if (base::i18n::IsRTL())
398 navbar_bounds.Offset(tab_bounds.width(), 0);
399 navbar_bounds.Offset(tabstrip->x(), tabstrip->y());
400
401 // The compact location bar must be smaller than browser_width.
402 int width = std::min(browser_view()->width(),
403 view()->GetPreferredSize().width());
404
405 // Try to center around the tab.
406 navbar_bounds.set_x(browser_view()->GetMirroredXInView(
407 navbar_bounds.x()) - ((width - tab_bounds.width()) / 2));
408
409 // Adjust the location to create the illusion that the compact location bar
410 // is a part of the spacer or bookmark bar, depending on which is showing.
411 if (browser_view()->IsBookmarkBarVisible() &&
412 !browser_view()->GetBookmarkBarView()->IsDetached()) {
413 // TODO(stevet): Compact location bar does not have right background image
414 // yet, so kBookmarkBarLocationBarOverlap is tentative. Fix this once UI is
415 // settled. This may be entirely replaced by a popup CLB, anyway.
416 navbar_bounds.Offset(0,
417 browser_view()->GetBookmarkBarView()->bounds().height() +
418 kCompactNavbarSpacerHeight - kBookmarkBarLocationBarOverlap);
419 } else {
420 // TODO(stevet): kSpacerLocationbarOverlap is tentative as well, as above.
421 navbar_bounds.Offset(0, kCompactNavbarSpacerHeight -
422 kSpacerLocationbarOverlap);
423 }
424
425 // TODO(stevet): Adjust to the right if there is an info bar visible.
426 return navbar_bounds.AdjustToFit(browser_view()->bounds());
427 }
428
429 void CompactLocationBarViewHost::Update(TabContents* contents, bool animate) {
430 // Don't animate if the bar is already shown.
431 bool showing_in_same_tab = animation()->IsShowing() && IsCurrentTab(contents);
432 current_tab_model_index_ = browser_view()->browser()->active_index();
433 Hide(false);
434 GetCompactLocationBarView()->Update(contents);
435 Show(animate && !showing_in_same_tab);
436 // If the tab is loading, we must wait for the notification that it is done.
437 if (contents && !contents->is_loading()) {
438 // This will be a NOOP if we have focus.
439 // We never want to stay up, unless we have focus.
440 StartAutoHideTimer();
441 }
442 }
443
444 void CompactLocationBarViewHost::UpdateOnTabChange(int model_index,
445 bool animate) {
446 DCHECK_GE(model_index, 0);
447 if (IsCurrentTabIndex(model_index) && animation()->IsShowing()) {
448 return;
449 }
450 current_tab_model_index_ = model_index;
451 Update(browser_view()->browser()->tabstrip_model()->
452 GetTabContentsAt(model_index)->tab_contents(), animate);
453 }
454
455 void CompactLocationBarViewHost::StartAutoHideTimer() {
456 if (!IsVisible() || HasFocus())
457 return;
458
459 if (auto_hide_timer_->IsRunning()) {
460 // Restart the timer.
461 auto_hide_timer_->Reset();
462 } else {
463 auto_hide_timer_->Start(base::TimeDelta::FromSeconds(kHideTimeoutInSeconds),
464 this, &CompactLocationBarViewHost::HideCallback);
465 }
466 }
467
468 void CompactLocationBarViewHost::CancelAutoHideTimer() {
469 auto_hide_timer_->Stop();
470 }
471
472 void CompactLocationBarViewHost::SetEnabled(bool enabled) {
473 if (enabled && !is_observing_) {
474 browser_view()->browser()->tabstrip_model()->AddObserver(this);
475 is_observing_ = true;
476 } else {
477 browser_view()->browser()->tabstrip_model()->RemoveObserver(this);
478 is_observing_ = false;
479 }
480 }
481
482 void CompactLocationBarViewHost::Show(bool animate) {
483 CancelAutoHideTimer();
484 mouse_observer_->Observe(MessageLoopForUI::current());
485 DropdownBarHost::Show(animate);
486 host()->Show();
487 }
488
489 void CompactLocationBarViewHost::Hide(bool animate) {
490 CancelAutoHideTimer();
491 mouse_observer_->StopObserving(MessageLoopForUI::current());
492 host()->Hide();
493 DropdownBarHost::Hide(animate);
494 }
495
496 ////////////////////////////////////////////////////////////////////////////////
497 // CompactLocationBarViewHost private:
498 bool CompactLocationBarViewHost::HasFocus() {
499 DCHECK(view() != NULL);
500 views::FocusManager* focus_manager = view()->GetFocusManager();
501 return focus_manager && view()->Contains(focus_manager->GetFocusedView());
502 }
503
504 void CompactLocationBarViewHost::HideCallback() {
505 if (IsVisible() && !HasFocus())
506 Hide(true);
507 }
508
509 bool CompactLocationBarViewHost::IsCurrentTabIndex(int index) {
510 return current_tab_model_index_ == index;
511 }
512
513 bool CompactLocationBarViewHost::IsCurrentTab(TabContents* contents) {
514 TabStripModel* tab_strip_model = browser_view()->browser()->tabstrip_model();
515 return tab_strip_model->ContainsIndex(current_tab_model_index_) &&
516 tab_strip_model->GetTabContentsAt(current_tab_model_index_)->
517 tab_contents() == contents;
518 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698