Commit 4d8c97a0 authored by deanm@chromium.org's avatar deanm@chromium.org

Implement a mostly working InfoBubble with a shim BookmarkBubble.

This ended up being implemented as a toplevel instead of a popup, to handle
things like virtual desktop switching.  I imagine there are some other problems
we might hit, like the window getting decorated, etc.  Although I think the
shape mask might prevent decorations from being visible.  This is not pixel
perfect with Windows, since we're not anti-aliasing the frame border.

Review URL: http://codereview.chromium.org/100203


git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14941 0039d316-1c4b-4281-b951-d872f2087c98
parent 900e7368
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/gtk/bookmark_bubble_gtk.h"
#include <gtk/gtk.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "chrome/browser/gtk/info_bubble_gtk.h"
// static
void BookmarkBubbleGtk::Show(const gfx::Rect& rect,
Profile* profile,
const GURL& url,
bool newly_bookmarked) {
// TODO(deanm): Implement the real bookmark bubble. For now we just have
// a placeholder for testing that input and focus works correctly.
GtkWidget* content = gtk_vbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(content), gtk_label_new("Hej!"), TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(content), gtk_entry_new(), TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(content), gtk_entry_new(), TRUE, TRUE, 0);
InfoBubbleGtk* bubble = InfoBubbleGtk::Show(rect, content);
DCHECK(bubble);
}
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_GTK_BOOKMARK_BUBBLE_GTK_H_
#define CHROME_BROWSER_GTK_BOOKMARK_BUBBLE_GTK_H_
#include "base/basictypes.h"
#include "googleurl/src/gurl.h"
typedef struct _GtkWidget GtkWidget;
class Profile;
namespace gfx {
class Rect;
}
class BookmarkBubbleGtk {
public:
static void Show(const gfx::Rect& rect,
Profile* profile,
const GURL& url,
bool newly_bookmarked);
};
#endif // CHROME_BROWSER_GTK_BOOKMARK_BUBBLE_GTK_H_
...@@ -487,7 +487,7 @@ void BrowserWindowGtk::ShowBookmarkManager() { ...@@ -487,7 +487,7 @@ void BrowserWindowGtk::ShowBookmarkManager() {
void BrowserWindowGtk::ShowBookmarkBubble(const GURL& url, void BrowserWindowGtk::ShowBookmarkBubble(const GURL& url,
bool already_bookmarked) { bool already_bookmarked) {
NOTIMPLEMENTED(); toolbar_->star()->ShowStarBubble(url, !already_bookmarked);
} }
void BrowserWindowGtk::ShowReportBugDialog() { void BrowserWindowGtk::ShowReportBugDialog() {
......
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/gtk/info_bubble_gtk.h"
#include <gtk/gtk.h>
#include "base/basictypes.h"
#include "base/gfx/gtk_util.h"
#include "base/gfx/rect.h"
#include "base/logging.h"
#include "chrome/common/gfx/path.h"
namespace {
// The height of the arrow, and the width will be about twice the height.
const int kArrowSize = 5;
// Number of pixels to the start of the arrow from the edge of the window.
const int kArrowX = 13;
// Number of pixels between the tip of the arrow and the region we're
// pointing to.
const int kArrowToContentPadding = -6;
// We draw flat diagonal corners, each corner is an NxN square.
const int kCornerSize = 3;
// Margins around the content.
const int kTopMargin = kArrowSize + kCornerSize + 6;
const int kBottomMargin = kCornerSize + 6;
const int kLeftMargin = kCornerSize + 6;
const int kRightMargin = kCornerSize + 6;
const GdkColor kBackgroundColor = GDK_COLOR_RGB(0xff, 0xff, 0xff);
const GdkColor kFrameColor = GDK_COLOR_RGB(0x63, 0x63, 0x63);
// A small convenience since GdkPoint is a POD without a constructor.
GdkPoint MakeGdkPoint(gint x, gint y) {
GdkPoint point = {x, y};
return point;
}
enum FrameType {
FRAME_MASK,
FRAME_STROKE,
};
// Make the points for our polygon frame, either for fill (the mask), or for
// when we stroke the border. NOTE: This seems a bit overcomplicated, but it
// requires a bunch of careful fudging to get the pixels rasterized exactly
// where we want them, the arrow to have a 1 pixel point, etc.
// TODO(deanm): Windows draws with Skia and uses some PNG images for the
// corners. This is a lot more work, but they get anti-aliasing.
std::vector<GdkPoint> MakeFramePolygonPoints(int width,
int height,
FrameType type) {
std::vector<GdkPoint> points;
// If we have a stroke, we have to offset some of our points by 1 pixel.
int off = (type == FRAME_MASK) ? 0 : 1;
// Top left corner.
points.push_back(MakeGdkPoint(0, kArrowSize + kCornerSize - 1));
points.push_back(MakeGdkPoint(kCornerSize - 1, kArrowSize));
// The arrow.
points.push_back(MakeGdkPoint(kArrowX - kArrowSize, kArrowSize));
points.push_back(MakeGdkPoint(kArrowX, 0));
points.push_back(MakeGdkPoint(kArrowX + 1 - off, 0));
points.push_back(MakeGdkPoint(kArrowX + kArrowSize + 1 - off, kArrowSize));
// Top right corner.
points.push_back(MakeGdkPoint(width - kCornerSize + 1 - off, kArrowSize));
points.push_back(MakeGdkPoint(width - off, kArrowSize + kCornerSize - 1));
// Bottom right corner.
points.push_back(MakeGdkPoint(width - off, height - kCornerSize));
points.push_back(MakeGdkPoint(width - kCornerSize, height - off));
// Bottom left corner.
points.push_back(MakeGdkPoint(kCornerSize - off, height - off));
points.push_back(MakeGdkPoint(0, height - kCornerSize));
return points;
}
// When our size is initially allocated or changed, we need to recompute
// and apply our shape mask region.
void HandleSizeAllocate(GtkWidget* widget,
GtkAllocation* allocation,
gpointer unused) {
DCHECK(allocation->x == 0 && allocation->y == 0);
std::vector<GdkPoint> points = MakeFramePolygonPoints(
allocation->width, allocation->height, FRAME_MASK);
GdkRegion* mask_region = gdk_region_polygon(&points[0],
points.size(),
GDK_EVEN_ODD_RULE);
gdk_window_shape_combine_region(widget->window, mask_region, 0, 0);
gdk_region_destroy(mask_region);
}
gboolean HandleExpose(GtkWidget* widget,
GdkEventExpose* event,
gpointer unused) {
GdkDrawable* drawable = GDK_DRAWABLE(event->window);
GdkGC* gc = gdk_gc_new(drawable);
gdk_gc_set_rgb_fg_color(gc, &kFrameColor);
// Stroke the frame border.
std::vector<GdkPoint> points = MakeFramePolygonPoints(
widget->allocation.width, widget->allocation.height, FRAME_STROKE);
gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
g_object_unref(gc);
return FALSE; // Propagate so our children paint, etc.
}
} // namespace
// static
InfoBubbleGtk* InfoBubbleGtk::Show(const gfx::Rect& rect, GtkWidget* content) {
InfoBubbleGtk* bubble = new InfoBubbleGtk();
bubble->Init(rect, content);
return bubble;
}
InfoBubbleGtk::InfoBubbleGtk()
: window_(NULL),
screen_x_(0),
screen_y_(0),
closed_(false) {
}
InfoBubbleGtk::~InfoBubbleGtk() {
}
void InfoBubbleGtk::Init(const gfx::Rect& rect, GtkWidget* content) {
DCHECK(!window_);
screen_x_ = rect.x() + (rect.width() / 2) - kArrowX;
screen_y_ = rect.y() + rect.height() + kArrowToContentPadding;
window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_decorated(GTK_WINDOW(window_), TRUE);
gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
gtk_widget_set_app_paintable(window_, TRUE);
// Have GTK double buffer around the expose signal.
gtk_widget_set_double_buffered(window_, TRUE);
// Set the background color, so we don't need to paint it manually.
gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &kBackgroundColor);
// Make sure that our window can be focused.
GTK_WIDGET_SET_FLAGS(window_, GTK_CAN_FOCUS);
GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
kTopMargin, kBottomMargin,
kLeftMargin, kRightMargin);
gtk_container_add(GTK_CONTAINER(alignment), content);
gtk_container_add(GTK_CONTAINER(window_), alignment);
// GtkWidget only exposes the bitmap mask interface. Use GDK to more
// efficently mask a GdkRegion. Make sure the window is realized during
// HandleSizeAllocate, so the mask can be applied to the GdkWindow.
gtk_widget_realize(window_);
gtk_window_move(GTK_WINDOW(window_), screen_x_, screen_y_);
gtk_widget_add_events(window_, GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK);
g_signal_connect(window_, "size-allocate",
G_CALLBACK(HandleSizeAllocate), NULL);
g_signal_connect(window_, "expose-event",
G_CALLBACK(HandleExpose), NULL);
g_signal_connect(window_, "configure-event",
G_CALLBACK(&HandleConfigureThunk), this);
g_signal_connect(window_, "button-press-event",
G_CALLBACK(&HandleButtonPressThunk), this);
gtk_widget_show_all(window_);
gtk_window_present(GTK_WINDOW(window_));
gtk_grab_add(window_);
}
void InfoBubbleGtk::Close() {
DCHECK(!closed_);
DCHECK(window_);
gtk_widget_destroy(window_);
window_ = NULL;
closed_ = true;
}
gboolean InfoBubbleGtk::HandleConfigure(GdkEventConfigure* event) {
// If the window is moved someplace besides where we want it, move it back.
// TODO(deanm): In the end, I will probably remove this code and just let
// the user move around the bubble like a normal dialog. I want to try
// this for now and see if it causes problems when any window managers.
if (event->x != screen_x_ || event->y != screen_y_)
gtk_window_move(GTK_WINDOW(window_), screen_x_, screen_y_);
return FALSE;
}
gboolean InfoBubbleGtk::HandleButtonPress(GdkEventButton* event) {
// If we got a click in our own window, that's ok.
if (event->window == window_->window)
return FALSE; // Propagate.
// Otherwise we had a click outside of our window, close ourself.
Close();
return TRUE;
}
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_GTK_INFO_BUBBLE_GTK_H_
#define CHROME_BROWSER_GTK_INFO_BUBBLE_GTK_H_
#include "base/basictypes.h"
#include <gtk/gtk.h>
namespace gfx {
class Rect;
}
class InfoBubbleGtk {
public:
// Show an InfoBubble, pointing at the area |rect| (in screen coordinates).
// An infobubble will try to fit on the screen, so it can point to any edge
// of |rect|. The bubble will host |widget| as the content.
static InfoBubbleGtk* Show(const gfx::Rect& rect, GtkWidget* content);
InfoBubbleGtk();
virtual ~InfoBubbleGtk();
void Close();
private:
// Creates the InfoBubble.
void Init(const gfx::Rect& rect, GtkWidget* content);
// Closes the window notifying the delegate. |closed_by_escape| is true if
// the close is the result of pressing escape.
void Close(bool closed_by_escape);
static gboolean HandleConfigureThunk(GtkWidget* widget,
GdkEventConfigure* event,
gpointer user_data) {
return reinterpret_cast<InfoBubbleGtk*>(user_data)->HandleConfigure(event);
}
gboolean HandleConfigure(GdkEventConfigure* event);
static gboolean HandleButtonPressThunk(GtkWidget* widget,
GdkEventButton* event,
gpointer userdata) {
return reinterpret_cast<InfoBubbleGtk*>(userdata)->
HandleButtonPress(event);
}
gboolean HandleButtonPress(GdkEventButton* event);
static gboolean HandleButtonReleaseThunk(GtkWidget* widget,
GdkEventButton* event,
gpointer userdata) {
return reinterpret_cast<InfoBubbleGtk*>(userdata)->
HandleButtonRelease(event);
}
gboolean HandleButtonRelease(GdkEventButton* event);
// Our GtkWindow popup window.
GtkWidget* window_;
// Where we want our window to be positioned on the screen.
int screen_x_;
int screen_y_;
// Have we been closed?
bool closed_;
DISALLOW_COPY_AND_ASSIGN(InfoBubbleGtk);
};
#endif // CHROME_BROWSER_GTK_INFO_BUBBLE_GTK_H_
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "chrome/browser/gtk/toolbar_star_toggle_gtk.h" #include "chrome/browser/gtk/toolbar_star_toggle_gtk.h"
#include "base/gfx/rect.h"
#include "chrome/browser/gtk/bookmark_bubble_gtk.h"
#include "chrome/common/resource_bundle.h" #include "chrome/common/resource_bundle.h"
#include "grit/theme_resources.h" #include "grit/theme_resources.h"
...@@ -27,6 +29,22 @@ ToolbarStarToggleGtk::~ToolbarStarToggleGtk() { ...@@ -27,6 +29,22 @@ ToolbarStarToggleGtk::~ToolbarStarToggleGtk() {
widget_.Destroy(); widget_.Destroy();
} }
void ToolbarStarToggleGtk::ShowStarBubble(const GURL& url,
bool newly_bookmarked) {
GtkWidget* widget = widget_.get();
gint x, y;
gdk_window_get_origin(widget->window, &x, &y);
x += widget->allocation.x;
y += widget->allocation.y;
gint width = widget->allocation.width;
gint height = widget->allocation.height;
BookmarkBubbleGtk::Show(gfx::Rect(x, y, width, height),
NULL,
url,
newly_bookmarked);
}
void ToolbarStarToggleGtk::SetStarred(bool starred) { void ToolbarStarToggleGtk::SetStarred(bool starred) {
is_starred_ = starred; is_starred_ = starred;
gtk_widget_queue_draw(widget_.get()); gtk_widget_queue_draw(widget_.get());
......
...@@ -728,6 +728,8 @@ ...@@ -728,6 +728,8 @@
'browser/gtk/back_forward_menu_model_gtk.h', 'browser/gtk/back_forward_menu_model_gtk.h',
'browser/gtk/bookmark_bar_gtk.cc', 'browser/gtk/bookmark_bar_gtk.cc',
'browser/gtk/bookmark_bar_gtk.h', 'browser/gtk/bookmark_bar_gtk.h',
'browser/gtk/bookmark_bubble_gtk.cc',
'browser/gtk/bookmark_bubble_gtk.h',
'browser/gtk/bookmark_editor_gtk.cc', 'browser/gtk/bookmark_editor_gtk.cc',
'browser/gtk/bookmark_editor_gtk.h', 'browser/gtk/bookmark_editor_gtk.h',
'browser/gtk/browser_toolbar_gtk.cc', 'browser/gtk/browser_toolbar_gtk.cc',
...@@ -746,10 +748,12 @@ ...@@ -746,10 +748,12 @@
'browser/gtk/go_button_gtk.h', 'browser/gtk/go_button_gtk.h',
'browser/gtk/gtk_chrome_button.cc', 'browser/gtk/gtk_chrome_button.cc',
'browser/gtk/gtk_chrome_button.h', 'browser/gtk/gtk_chrome_button.h',
'browser/gtk/infobar_gtk.cc', 'browser/gtk/info_bubble_gtk.cc',
'browser/gtk/infobar_gtk.h', 'browser/gtk/info_bubble_gtk.h',
'browser/gtk/infobar_container_gtk.cc', 'browser/gtk/infobar_container_gtk.cc',
'browser/gtk/infobar_container_gtk.h', 'browser/gtk/infobar_container_gtk.h',
'browser/gtk/infobar_gtk.cc',
'browser/gtk/infobar_gtk.h',
'browser/gtk/find_bar_gtk.cc', 'browser/gtk/find_bar_gtk.cc',
'browser/gtk/find_bar_gtk.h', 'browser/gtk/find_bar_gtk.h',
'browser/gtk/link_button_gtk.cc', 'browser/gtk/link_button_gtk.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment