| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/gtk/status_bubble_gtk.h" | 5 #include "chrome/browser/gtk/status_bubble_gtk.h" |
| 6 | 6 |
| 7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
| 8 | 8 |
| 9 #include "app/gfx/gtk_util.h" | 9 #include "app/gfx/gtk_util.h" |
| 10 #include "app/gfx/text_elider.h" | 10 #include "app/gfx/text_elider.h" |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 // Inner padding between the border and the text label. | 23 // Inner padding between the border and the text label. |
| 24 const int kInternalTopBottomPadding = 1; | 24 const int kInternalTopBottomPadding = 1; |
| 25 const int kInternalLeftRightPadding = 2; | 25 const int kInternalLeftRightPadding = 2; |
| 26 | 26 |
| 27 // The radius of the edges of our bubble. | 27 // The radius of the edges of our bubble. |
| 28 const int kCornerSize = 3; | 28 const int kCornerSize = 3; |
| 29 | 29 |
| 30 // Milliseconds before we hide the status bubble widget when you mouseout. | 30 // Milliseconds before we hide the status bubble widget when you mouseout. |
| 31 const int kHideDelay = 250; | 31 const int kHideDelay = 250; |
| 32 | 32 |
| 33 // How close the mouse can get to the infobubble before it starts sliding |
| 34 // off-screen. |
| 35 const int kMousePadding = 20; |
| 36 |
| 33 } // namespace | 37 } // namespace |
| 34 | 38 |
| 35 StatusBubbleGtk::StatusBubbleGtk(Profile* profile) | 39 StatusBubbleGtk::StatusBubbleGtk(Profile* profile) |
| 36 : theme_provider_(GtkThemeProvider::GetFrom(profile)), | 40 : theme_provider_(GtkThemeProvider::GetFrom(profile)), |
| 37 timer_factory_(this) { | 41 padding_(NULL), |
| 42 label_(NULL), |
| 43 timer_factory_(this), |
| 44 flip_horizontally_(false), |
| 45 y_offset_(0), |
| 46 download_shelf_is_visible_(false) { |
| 38 InitWidgets(); | 47 InitWidgets(); |
| 39 | 48 |
| 40 theme_provider_->InitThemesFor(this); | 49 theme_provider_->InitThemesFor(this); |
| 41 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, | 50 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, |
| 42 NotificationService::AllSources()); | 51 NotificationService::AllSources()); |
| 43 } | 52 } |
| 44 | 53 |
| 45 StatusBubbleGtk::~StatusBubbleGtk() { | 54 StatusBubbleGtk::~StatusBubbleGtk() { |
| 46 container_.Destroy(); | 55 container_.Destroy(); |
| 47 } | 56 } |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 | 116 |
| 108 void StatusBubbleGtk::HideInASecond() { | 117 void StatusBubbleGtk::HideInASecond() { |
| 109 if (!timer_factory_.empty()) | 118 if (!timer_factory_.empty()) |
| 110 timer_factory_.RevokeAll(); | 119 timer_factory_.RevokeAll(); |
| 111 | 120 |
| 112 MessageLoop::current()->PostDelayedTask(FROM_HERE, | 121 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 113 timer_factory_.NewRunnableMethod(&StatusBubbleGtk::Hide), | 122 timer_factory_.NewRunnableMethod(&StatusBubbleGtk::Hide), |
| 114 kHideDelay); | 123 kHideDelay); |
| 115 } | 124 } |
| 116 | 125 |
| 117 void StatusBubbleGtk::MouseMoved() { | 126 void StatusBubbleGtk::MouseMoved( |
| 118 // We can't do that fancy sliding behaviour where the status bubble slides | 127 const gfx::Point& location, bool left_content) { |
| 119 // out of the window because the window manager gets in the way. So totally | 128 if (!GTK_WIDGET_REALIZED(container_.get())) |
| 120 // ignore this message for now. | 129 return; |
| 121 // | 130 |
| 122 // TODO(erg): At least get some sliding behaviour so that it slides out of | 131 GtkWidget* parent = gtk_widget_get_parent(container_.get()); |
| 123 // the way to hide the status bubble on mouseover. | 132 if (!parent || !GTK_WIDGET_REALIZED(parent)) |
| 133 return; |
| 134 |
| 135 int old_y_offset = y_offset_; |
| 136 bool old_flip_horizontally = flip_horizontally_; |
| 137 |
| 138 if (left_content) { |
| 139 SetFlipHorizontally(false); |
| 140 y_offset_ = 0; |
| 141 } else { |
| 142 GtkWidget* toplevel = gtk_widget_get_toplevel(container_.get()); |
| 143 if (!toplevel || !GTK_WIDGET_REALIZED(toplevel)) |
| 144 return; |
| 145 |
| 146 bool ltr = (l10n_util::GetTextDirection() == l10n_util::LEFT_TO_RIGHT); |
| 147 |
| 148 GtkRequisition requisition; |
| 149 gtk_widget_size_request(container_.get(), &requisition); |
| 150 |
| 151 // Get our base position (that is, not including the current offset) |
| 152 // relative to the origin of the root window. |
| 153 gint toplevel_x = 0, toplevel_y = 0; |
| 154 gdk_window_get_position(toplevel->window, &toplevel_x, &toplevel_y); |
| 155 gfx::Rect parent_rect = |
| 156 gtk_util::GetWidgetRectRelativeToToplevel(parent); |
| 157 gfx::Rect bubble_rect( |
| 158 toplevel_x + parent_rect.x() + |
| 159 (ltr ? 0 : parent->allocation.width - requisition.width), |
| 160 toplevel_y + parent_rect.y() + |
| 161 parent->allocation.height - requisition.height, |
| 162 requisition.width, |
| 163 requisition.height); |
| 164 |
| 165 int left_threshold = |
| 166 bubble_rect.x() - bubble_rect.height() - kMousePadding; |
| 167 int right_threshold = |
| 168 bubble_rect.right() + bubble_rect.height() + kMousePadding; |
| 169 int top_threshold = bubble_rect.y() - kMousePadding; |
| 170 |
| 171 if (((ltr && location.x() < right_threshold) || |
| 172 (!ltr && location.x() > left_threshold)) && |
| 173 location.y() > top_threshold) { |
| 174 if (download_shelf_is_visible_) { |
| 175 SetFlipHorizontally(true); |
| 176 y_offset_ = 0; |
| 177 } else { |
| 178 SetFlipHorizontally(false); |
| 179 int distance = std::max(ltr ? |
| 180 location.x() - right_threshold : |
| 181 left_threshold - location.x(), |
| 182 top_threshold - location.y()); |
| 183 y_offset_ = std::min(-1 * distance, requisition.height); |
| 184 } |
| 185 } else { |
| 186 SetFlipHorizontally(false); |
| 187 y_offset_ = 0; |
| 188 } |
| 189 } |
| 190 |
| 191 if (y_offset_ != old_y_offset || flip_horizontally_ != old_flip_horizontally) |
| 192 gtk_widget_queue_resize_no_redraw(parent); |
| 193 } |
| 194 |
| 195 void StatusBubbleGtk::UpdateDownloadShelfVisibility(bool visible) { |
| 196 download_shelf_is_visible_ = visible; |
| 124 } | 197 } |
| 125 | 198 |
| 126 void StatusBubbleGtk::Observe(NotificationType type, | 199 void StatusBubbleGtk::Observe(NotificationType type, |
| 127 const NotificationSource& source, | 200 const NotificationSource& source, |
| 128 const NotificationDetails& details) { | 201 const NotificationDetails& details) { |
| 129 if (type == NotificationType::BROWSER_THEME_CHANGED) { | 202 if (type == NotificationType::BROWSER_THEME_CHANGED) { |
| 130 UserChangedTheme(); | 203 UserChangedTheme(); |
| 131 } | 204 } |
| 132 } | 205 } |
| 133 | 206 |
| 134 void StatusBubbleGtk::InitWidgets() { | 207 void StatusBubbleGtk::InitWidgets() { |
| 208 bool ltr = (l10n_util::GetTextDirection() == l10n_util::LEFT_TO_RIGHT); |
| 209 |
| 135 label_ = gtk_label_new(NULL); | 210 label_ = gtk_label_new(NULL); |
| 136 | 211 |
| 137 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1); | 212 padding_ = gtk_alignment_new(0, 0, 1, 1); |
| 138 gtk_alignment_set_padding(GTK_ALIGNMENT(padding), | 213 gtk_alignment_set_padding(GTK_ALIGNMENT(padding_), |
| 139 kInternalTopBottomPadding, kInternalTopBottomPadding, | 214 kInternalTopBottomPadding, kInternalTopBottomPadding, |
| 140 kInternalLeftRightPadding, | 215 kInternalLeftRightPadding + (ltr ? 0 : kCornerSize), |
| 141 kInternalLeftRightPadding + kCornerSize); | 216 kInternalLeftRightPadding + (ltr ? kCornerSize : 0)); |
| 142 gtk_container_add(GTK_CONTAINER(padding), label_); | 217 gtk_container_add(GTK_CONTAINER(padding_), label_); |
| 143 | 218 |
| 144 container_.Own(gtk_event_box_new()); | 219 container_.Own(gtk_event_box_new()); |
| 145 gtk_util::ActAsRoundedWindow( | 220 gtk_util::ActAsRoundedWindow( |
| 146 container_.get(), gfx::kGdkWhite, kCornerSize, | 221 container_.get(), gfx::kGdkWhite, kCornerSize, |
| 147 gtk_util::ROUNDED_TOP_RIGHT, | 222 gtk_util::ROUNDED_TOP_RIGHT, |
| 148 gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT); | 223 gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT); |
| 149 gtk_widget_set_name(container_.get(), "status-bubble"); | 224 gtk_widget_set_name(container_.get(), "status-bubble"); |
| 150 gtk_container_add(GTK_CONTAINER(container_.get()), padding); | 225 gtk_container_add(GTK_CONTAINER(container_.get()), padding_); |
| 226 |
| 227 // We need to listen for mouse motion events, since a fast-moving pointer may |
| 228 // enter our window without us getting any motion events on the browser near |
| 229 // enough for us to run away. |
| 230 gtk_widget_add_events(container_.get(), GDK_POINTER_MOTION_MASK); |
| 231 g_signal_connect(container_.get(), "motion-notify-event", |
| 232 G_CALLBACK(HandleMotionNotifyThunk), this); |
| 151 | 233 |
| 152 UserChangedTheme(); | 234 UserChangedTheme(); |
| 153 } | 235 } |
| 154 | 236 |
| 155 void StatusBubbleGtk::UserChangedTheme() { | 237 void StatusBubbleGtk::UserChangedTheme() { |
| 156 if (theme_provider_->UseGtkTheme()) { | 238 if (theme_provider_->UseGtkTheme()) { |
| 157 gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, NULL); | 239 gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, NULL); |
| 158 gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, NULL); | 240 gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, NULL); |
| 159 } else { | 241 } else { |
| 160 // TODO(erg): This is the closest to "text that will look good on a | 242 // TODO(erg): This is the closest to "text that will look good on a |
| 161 // toolbar" that I can find. Maybe in later iterations of the theme system, | 243 // toolbar" that I can find. Maybe in later iterations of the theme system, |
| 162 // there will be a better color to pick. | 244 // there will be a better color to pick. |
| 163 GdkColor bookmark_text = | 245 GdkColor bookmark_text = |
| 164 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT); | 246 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT); |
| 165 gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, &bookmark_text); | 247 gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, &bookmark_text); |
| 166 | 248 |
| 167 GdkColor toolbar_color = | 249 GdkColor toolbar_color = |
| 168 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR); | 250 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR); |
| 169 gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, &toolbar_color); | 251 gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, &toolbar_color); |
| 170 } | 252 } |
| 171 | 253 |
| 172 gtk_util::SetRoundedWindowBorderColor(container_.get(), | 254 gtk_util::SetRoundedWindowBorderColor(container_.get(), |
| 173 theme_provider_->GetBorderColor()); | 255 theme_provider_->GetBorderColor()); |
| 174 } | 256 } |
| 257 |
| 258 void StatusBubbleGtk::SetFlipHorizontally(bool flip_horizontally) { |
| 259 if (flip_horizontally == flip_horizontally_) |
| 260 return; |
| 261 |
| 262 flip_horizontally_ = flip_horizontally; |
| 263 |
| 264 bool ltr = (l10n_util::GetTextDirection() == l10n_util::LEFT_TO_RIGHT); |
| 265 bool on_left = (ltr && !flip_horizontally) || (!ltr && flip_horizontally); |
| 266 |
| 267 gtk_alignment_set_padding(GTK_ALIGNMENT(padding_), |
| 268 kInternalTopBottomPadding, kInternalTopBottomPadding, |
| 269 kInternalLeftRightPadding + (on_left ? 0 : kCornerSize), |
| 270 kInternalLeftRightPadding + (on_left ? kCornerSize : 0)); |
| 271 // The rounded window code flips these arguments if we're RTL. |
| 272 gtk_util::SetRoundedWindowEdgesAndBorders( |
| 273 container_.get(), |
| 274 kCornerSize, |
| 275 flip_horizontally ? |
| 276 gtk_util::ROUNDED_TOP_LEFT : |
| 277 gtk_util::ROUNDED_TOP_RIGHT, |
| 278 gtk_util::BORDER_TOP | |
| 279 (flip_horizontally ? gtk_util::BORDER_LEFT : gtk_util::BORDER_RIGHT)); |
| 280 gtk_widget_queue_draw(container_.get()); |
| 281 } |
| 282 |
| 283 gboolean StatusBubbleGtk::HandleMotionNotify(GdkEventMotion* event) { |
| 284 MouseMoved(gfx::Point(event->x_root, event->y_root), false); |
| 285 return FALSE; |
| 286 } |
| OLD | NEW |