diff options
author | allanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-06 09:50:32 +0000 |
---|---|---|
committer | allanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-06 09:50:32 +0000 |
commit | 90e7e1e5afd58e5042bc7431c072f9d4bb497621 (patch) | |
tree | 877d3066a4d3bee4f68b6664d612a4caae9f2f9b | |
parent | d7427e04bf79570fe33f4e413cee11903fd906c8 (diff) | |
download | chromium_src-90e7e1e5afd58e5042bc7431c072f9d4bb497621.zip chromium_src-90e7e1e5afd58e5042bc7431c072f9d4bb497621.tar.gz chromium_src-90e7e1e5afd58e5042bc7431c072f9d4bb497621.tar.bz2 |
Fix bug where a speech input bubble can be drawn outside of the viewing window.
On creating the bubble added a check to see if the SpeechInputBubble's target is contained within the viewing window and if it isn't make it point towards the page icon. Also the Controller now informs the Manager if the bubble could not be created by simulating a cancel click (can happen if tab_contents is null).
BUG=84600
TEST=Manual
Review URL: http://codereview.chromium.org/7187008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@91540 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/speech/speech_input_bubble_controller.cc | 11 | ||||
-rw-r--r-- | chrome/browser/speech/speech_input_bubble_gtk.cc | 34 | ||||
-rw-r--r-- | chrome/browser/speech/speech_input_bubble_mac.mm | 21 | ||||
-rw-r--r-- | chrome/browser/speech/speech_input_bubble_views.cc | 36 |
4 files changed, 85 insertions, 17 deletions
diff --git a/chrome/browser/speech/speech_input_bubble_controller.cc b/chrome/browser/speech/speech_input_bubble_controller.cc index 56ea3f0..8cdae9f 100644 --- a/chrome/browser/speech/speech_input_bubble_controller.cc +++ b/chrome/browser/speech/speech_input_bubble_controller.cc @@ -43,8 +43,17 @@ void SpeechInputBubbleController::CreateBubble(int caller_id, DCHECK_EQ(0u, bubbles_.count(caller_id)); SpeechInputBubble* bubble = SpeechInputBubble::Create(tab_contents, this, element_rect); - if (!bubble) // could be null if tab or display rect were invalid. + if (!bubble) { + // Could be null if tab or display rect were invalid. + // Simulate the cancel button being clicked to inform the delegate. + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &SpeechInputBubbleController::InvokeDelegateButtonClicked, + caller_id, SpeechInputBubble::BUTTON_CANCEL)); return; + } bubbles_[caller_id] = bubble; diff --git a/chrome/browser/speech/speech_input_bubble_gtk.cc b/chrome/browser/speech/speech_input_bubble_gtk.cc index d1004b2..c8c5ab0 100644 --- a/chrome/browser/speech/speech_input_bubble_gtk.cc +++ b/chrome/browser/speech/speech_input_bubble_gtk.cc @@ -5,10 +5,14 @@ #include "chrome/browser/speech/speech_input_bubble.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/gtk/browser_toolbar_gtk.h" +#include "chrome/browser/ui/gtk/browser_window_gtk.h" #include "chrome/browser/ui/gtk/bubble/bubble_gtk.h" #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" #include "chrome/browser/ui/gtk/gtk_theme_service.h" #include "chrome/browser/ui/gtk/gtk_util.h" +#include "chrome/browser/ui/gtk/location_bar_view_gtk.h" #include "chrome/browser/ui/gtk/owned_widget_gtk.h" #include "content/browser/tab_contents/tab_contents.h" #include "grit/generated_resources.h" @@ -159,15 +163,31 @@ void SpeechInputBubbleGtk::Show() { GtkThemeService* theme_provider = GtkThemeService::GetFrom( tab_contents()->profile()); - gfx::Rect rect( - element_rect_.x() + element_rect_.width() - kBubbleTargetOffsetX, - element_rect_.y() + element_rect_.height(), 1, 1); - bubble_ = BubbleGtk::Show(tab_contents()->GetNativeView(), - &rect, + GtkWidget* reference_widget = tab_contents()->GetNativeView(); + gfx::Rect container_rect; + tab_contents()->GetContainerBounds(&container_rect); + gfx::Rect target_rect(element_rect_.right() - kBubbleTargetOffsetX, + element_rect_.bottom(), 1, 1); + + if (target_rect.x() < 0 || target_rect.y() < 0 || + target_rect.x() > container_rect.width() || + target_rect.y() > container_rect.height()) { + // Target is not in screen view, so point to wrench. + Browser* browser = + Browser::GetOrCreateTabbedBrowser(tab_contents()->profile()); + BrowserWindowGtk* browser_window = + BrowserWindowGtk::GetBrowserWindowForNativeWindow( + browser->window()->GetNativeHandle()); + reference_widget = browser_window->GetToolbar()->GetLocationBarView() + ->location_icon_widget(); + target_rect = gtk_util::WidgetBounds(reference_widget); + } + bubble_ = BubbleGtk::Show(reference_widget, + &target_rect, content, BubbleGtk::ARROW_LOCATION_TOP_LEFT, - false, // match_system_theme - true, // grab_input + false, // match_system_theme + true, // grab_input theme_provider, this); diff --git a/chrome/browser/speech/speech_input_bubble_mac.mm b/chrome/browser/speech/speech_input_bubble_mac.mm index 0b3ea16..3d7b146 100644 --- a/chrome/browser/speech/speech_input_bubble_mac.mm +++ b/chrome/browser/speech/speech_input_bubble_mac.mm @@ -7,6 +7,10 @@ #include "chrome/browser/speech/speech_input_bubble.h" #import "base/memory/scoped_nsobject.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/cocoa/browser_window_cocoa.h" +#include "chrome/browser/ui/cocoa/browser_window_controller.h" +#include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" #import "chrome/browser/ui/cocoa/speech_input_window_controller.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_view.h" @@ -60,19 +64,30 @@ void SpeechInputBubbleImpl::Show() { // Find the screen coordinates for the given tab and position the bubble's // arrow anchor point inside that to point at the bottom-left of the html - // input element rect. + // input element rect if the position is valid, otherwise point it towards + // the page icon in the omnibox. gfx::NativeView view = tab_contents()->view()->GetNativeView(); + NSWindow* parentWindow = tab_contents()->view()->GetTopLevelNativeWindow(); NSRect tab_bounds = [view bounds]; int anchor_x = tab_bounds.origin.x + element_rect_.x() + element_rect_.width() - kBubbleTargetOffsetX; int anchor_y = tab_bounds.origin.y + tab_bounds.size.height - element_rect_.y() - element_rect_.height(); - NSPoint anchor = NSMakePoint(anchor_x, anchor_y); + NSPoint anchor = NSZeroPoint; + if (anchor_x < 0 || anchor_y < 0 || + anchor_x > NSWidth([parentWindow frame]) || + anchor_y > NSHeight([parentWindow frame])) { + LocationBarViewMac* locationBar = + [[parentWindow windowController] locationBarBridge]; + anchor = locationBar->GetPageInfoBubblePoint(); + } else { + anchor = NSMakePoint(anchor_x, anchor_y); + } anchor = [view convertPoint:anchor toView:nil]; anchor = [[view window] convertBaseToScreen:anchor]; window_.reset([[SpeechInputWindowController alloc] - initWithParentWindow:tab_contents()->view()->GetTopLevelNativeWindow() + initWithParentWindow:parentWindow delegate:delegate_ anchoredAt:anchor]); diff --git a/chrome/browser/speech/speech_input_bubble_views.cc b/chrome/browser/speech/speech_input_bubble_views.cc index 9c3b7115..aa1f847 100644 --- a/chrome/browser/speech/speech_input_bubble_views.cc +++ b/chrome/browser/speech/speech_input_bubble_views.cc @@ -8,8 +8,10 @@ #include "base/message_loop.h" #include "base/utf_string_conversions.h" -#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/views/bubble/bubble.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/toolbar_view.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_view.h" #include "grit/generated_resources.h" @@ -32,6 +34,8 @@ namespace { const int kBubbleHorizMargin = 6; const int kBubbleVertMargin = 4; const int kBubbleHeadingVertMargin = 6; +const int kIconHorizontalOffset = 27; +const int kIconVerticalOffset = -7; // This is the content view which is placed inside a SpeechInputBubble. class ContentView @@ -307,9 +311,8 @@ gfx::Rect SpeechInputBubbleImpl::GetInfoBubbleTarget( gfx::Rect container_rect; tab_contents()->GetContainerBounds(&container_rect); return gfx::Rect( - container_rect.x() + element_rect.x() + element_rect.width() - - kBubbleTargetOffsetX, - container_rect.y() + element_rect.y() + element_rect.height(), 1, 1); + container_rect.x() + element_rect.right() - kBubbleTargetOffsetX, + container_rect.y() + element_rect.bottom(), 1, 1); } void SpeechInputBubbleImpl::BubbleClosing(Bubble* bubble, @@ -339,11 +342,32 @@ void SpeechInputBubbleImpl::Show() { views::Widget::GetTopLevelWidgetForNativeView( tab_contents()->view()->GetNativeView()); if (toplevel_widget) { + gfx::Rect container_rect; + tab_contents()->GetContainerBounds(&container_rect); + gfx::Rect target_rect = GetInfoBubbleTarget(element_rect_); + if (!container_rect.Contains(target_rect.x(), target_rect.y())) { + // Target is not in screen view, so point to page icon in omnibox. + Browser* browser = + Browser::GetOrCreateTabbedBrowser(tab_contents()->profile()); + BrowserView* browser_view = + BrowserView::GetBrowserViewForNativeWindow( + browser->window()->GetNativeHandle()); + gfx::Point point; + if (base::i18n::IsRTL()) { + int width = browser_view->toolbar()->location_bar()->width(); + point = gfx::Point(width - kIconHorizontalOffset, 0); + } + point.Offset(0, kIconVerticalOffset); + views::View::ConvertPointToScreen(browser_view->toolbar()->location_bar(), + &point); + target_rect = browser_view->toolbar()->location_bar()->bounds(); + target_rect.set_origin(point); + target_rect.set_width(kIconHorizontalOffset); + } bubble_ = Bubble::Show(toplevel_widget, - GetInfoBubbleTarget(element_rect_), + target_rect, BubbleBorder::TOP_LEFT, bubble_content_, this); - // We don't want fade outs when closing because it makes speech recognition // appear slower than it is. Also setting it to false allows |Close| to // destroy the bubble immediately instead of waiting for the fade animation |