diff options
author | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-03 00:38:41 +0000 |
---|---|---|
committer | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-03 00:38:41 +0000 |
commit | a0117df788373dfcbbe4ee11aff5ea56558bf4a1 (patch) | |
tree | c536a98be4e82efbd83adf740011d230c49dde97 /remoting | |
parent | 391a22068d38281032cf73a50e18df4f10bfafbc (diff) | |
download | chromium_src-a0117df788373dfcbbe4ee11aff5ea56558bf4a1.zip chromium_src-a0117df788373dfcbbe4ee11aff5ea56558bf4a1.tar.gz chromium_src-a0117df788373dfcbbe4ee11aff5ea56558bf4a1.tar.bz2 |
New-style Disconnect window for Linux Chromoting host.
BUG=None
TEST=Manual
Review URL: http://codereview.chromium.org/8733014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112831 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/disconnect_window_linux.cc | 227 |
1 files changed, 189 insertions, 38 deletions
diff --git a/remoting/host/disconnect_window_linux.cc b/remoting/host/disconnect_window_linux.cc index b7292d6..ba4693d 100644 --- a/remoting/host/disconnect_window_linux.cc +++ b/remoting/host/disconnect_window_linux.cc @@ -5,6 +5,7 @@ #include "remoting/host/disconnect_window.h" #include <gtk/gtk.h> +#include <math.h> #include "base/compiler_specific.h" #include "base/logging.h" @@ -14,16 +15,6 @@ #include "remoting/host/ui_strings.h" #include "ui/base/gtk/gtk_signal.h" -namespace { -// The width in pixels at which the message will wrap. Given that the message -// contains an un-splittable email address, it's unlikely that a fixed width -// is going to look aesthetically pleasing in all languages. -// TODO(jamiewalch): Replace this with a layout that only uses a single line, -// and which is docked at the top or bottom of the host screen, as in our -// UI mocks. -const int kMessageWidth = 300; -} - namespace remoting { class DisconnectWindowLinux : public DisconnectWindow { @@ -36,20 +27,33 @@ class DisconnectWindowLinux : public DisconnectWindow { virtual void Hide() OVERRIDE; private: - CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, void, OnResponse, int); + CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, gboolean, OnDelete, GdkEvent*); + CHROMEGTK_CALLBACK_0(DisconnectWindowLinux, void, OnClicked); + CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, gboolean, OnConfigure, + GdkEventConfigure*); + CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, gboolean, OnButtonPress, + GdkEventButton*); void CreateWindow(const UiStrings& ui_strings); ChromotingHost* host_; GtkWidget* disconnect_window_; GtkWidget* message_; + GtkWidget* button_; + + // Used to distinguish resize events from other types of "configure-event" + // notifications. + int current_width_; + int current_height_; DISALLOW_COPY_AND_ASSIGN(DisconnectWindowLinux); }; DisconnectWindowLinux::DisconnectWindowLinux() : host_(NULL), - disconnect_window_(NULL) { + disconnect_window_(NULL), + current_width_(0), + current_height_(0) { } DisconnectWindowLinux::~DisconnectWindowLinux() { @@ -58,42 +62,71 @@ DisconnectWindowLinux::~DisconnectWindowLinux() { void DisconnectWindowLinux::CreateWindow(const UiStrings& ui_strings) { if (disconnect_window_) return; - disconnect_window_ = gtk_dialog_new_with_buttons( - UTF16ToUTF8(ui_strings.product_name).c_str(), - NULL, - GTK_DIALOG_NO_SEPARATOR, - UTF16ToUTF8(ui_strings.disconnect_button_text_plus_shortcut).c_str(), - GTK_RESPONSE_OK, - NULL); - + disconnect_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); GtkWindow* window = GTK_WINDOW(disconnect_window_); + + g_signal_connect(disconnect_window_, "delete-event", + G_CALLBACK(OnDeleteThunk), this); + gtk_window_set_title(window, UTF16ToUTF8(ui_strings.product_name).c_str()); gtk_window_set_resizable(window, FALSE); + // Try to keep the window always visible. gtk_window_stick(window); gtk_window_set_keep_above(window, TRUE); + + // Remove window titlebar. + gtk_window_set_decorated(window, FALSE); + + // In case the titlebar is still there, try to remove some of the buttons. // Utility windows have no minimize button or taskbar presence. gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY); gtk_window_set_deletable(window, FALSE); - g_signal_connect(disconnect_window_, "response", - G_CALLBACK(OnResponseThunk), this); + // Allow custom rendering of the background pixmap. + gtk_widget_set_app_paintable(disconnect_window_, TRUE); + + // Handle window resizing, to regenerate the background pixmap and window + // shape bitmap. The stored width & height need to be initialized here + // in case the window is created a second time (the size of the previous + // window would be remembered, preventing the generation of bitmaps for the + // new window). + current_height_ = current_width_ = 0; + g_signal_connect(disconnect_window_, "configure-event", + G_CALLBACK(OnConfigureThunk), this); + + // Handle mouse events to allow the user to drag the window around. + gtk_widget_set_events(disconnect_window_, GDK_BUTTON_PRESS_MASK); + g_signal_connect(disconnect_window_, "button-press-event", + G_CALLBACK(OnButtonPressThunk), this); - GtkWidget* content_area = - gtk_dialog_get_content_area(GTK_DIALOG(disconnect_window_)); + // All magic numbers taken from screen shots provided by UX. + // The alignment sets narrow margins at the top and bottom, compared with + // left and right. The left margin is made larger to accommodate the + // window movement gripper. + GtkWidget* align = gtk_alignment_new(0, 0, 1, 1); + gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12); + gtk_container_add(GTK_CONTAINER(window), align); - GtkWidget* message_row = gtk_hbox_new(FALSE, 0); - // TODO(lambroslambrou): Replace the magic number with an appropriate - // constant from a header file (such as chrome/browser/ui/gtk/gtk_util.h - // but check_deps disallows its #inclusion here). - gtk_container_set_border_width(GTK_CONTAINER(message_row), 12); - gtk_container_add(GTK_CONTAINER(content_area), message_row); + GtkWidget* button_row = gtk_hbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(align), button_row); + + button_ = gtk_button_new_with_label( + UTF16ToUTF8(ui_strings.disconnect_button_text_plus_shortcut).c_str()); + gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0); + + g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this); message_ = gtk_label_new(NULL); - gtk_widget_set_size_request(message_, kMessageWidth, -1); - gtk_label_set_line_wrap(GTK_LABEL(message_), true); - gtk_container_add(GTK_CONTAINER(message_row), message_); + gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0); + + // Override any theme setting for the text color, so that the text is + // readable against the window's background pixmap. + PangoAttrList* attributes = pango_attr_list_new(); + PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0); + pango_attr_list_insert(attributes, text_color); + gtk_label_set_attributes(GTK_LABEL(message_), attributes); - gtk_widget_show_all(content_area); + gtk_widget_show_all(disconnect_window_); } void DisconnectWindowLinux::Show(ChromotingHost* host, @@ -114,16 +147,134 @@ void DisconnectWindowLinux::Hide() { } } -void DisconnectWindowLinux::OnResponse(GtkWidget* dialog, int response_id) { - // |response_id| is ignored, because there is only one button, and pressing - // it should have the same effect as closing the window (if the window Close - // button were visible). +void DisconnectWindowLinux::OnClicked(GtkWidget* button) { CHECK(host_); host_->Shutdown(base::Closure()); Hide(); } +gboolean DisconnectWindowLinux::OnDelete(GtkWidget* window, GdkEvent* event) { + CHECK(host_); + + host_->Shutdown(base::Closure()); + Hide(); + + return TRUE; +} + +namespace { +// Helper function for creating a rectangular path with rounded corners, as +// Cairo doesn't have this facility. |radius| is the arc-radius of each +// corner. The bounding rectangle extends from (0, 0) to (width, height). +void AddRoundRectPath(cairo_t* cairo_context, int width, int height, + int radius) { + cairo_new_sub_path(cairo_context); + cairo_arc(cairo_context, width - radius, radius, radius, -M_PI_2, 0); + cairo_arc(cairo_context, width - radius, height - radius, radius, 0, M_PI_2); + cairo_arc(cairo_context, radius, height - radius, radius, M_PI_2, 2 * M_PI_2); + cairo_arc(cairo_context, radius, radius, radius, 2 * M_PI_2, 3 * M_PI_2); + cairo_close_path(cairo_context); +} + +} // namespace + +gboolean DisconnectWindowLinux::OnConfigure(GtkWidget* widget, + GdkEventConfigure* event) { + // Only generate bitmaps if the size has actually changed. + if (event->width == current_width_ && event->height == current_height_) + return FALSE; + + current_width_ = event->width; + current_height_ = event->height; + + // Create the depth 1 pixmap for the window shape. + GdkPixmap* shape_mask = gdk_pixmap_new(NULL, current_width_, current_height_, + 1); + cairo_t* cairo_context = gdk_cairo_create(shape_mask); + + // Set the arc radius for the corners. + const int kCornerRadius = 6; + + // Initialize the whole bitmap to be transparent. + cairo_set_source_rgba(cairo_context, 0, 0, 0, 0); + cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); + cairo_paint(cairo_context); + + // Paint an opaque round rect covering the whole area (leaving the extreme + // corners transparent). + cairo_set_source_rgba(cairo_context, 1, 1, 1, 1); + cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); + AddRoundRectPath(cairo_context, current_width_, current_height_, + kCornerRadius); + cairo_fill(cairo_context); + + cairo_destroy(cairo_context); + gdk_window_shape_combine_mask(widget->window, shape_mask, 0, 0); + g_object_unref(shape_mask); + + // Create a full-color pixmap for the window background image. + GdkPixmap* background = gdk_pixmap_new(NULL, current_width_, current_height_, + 24); + cairo_context = gdk_cairo_create(background); + + // Paint the whole bitmap one color. + cairo_set_source_rgb(cairo_context, 0.91, 0.91, 0.91); + cairo_paint(cairo_context); + + // Paint the round-rectangle edge. + cairo_set_source_rgb(cairo_context, 0.13, 0.69, 0.11); + cairo_set_line_width(cairo_context, 6); + AddRoundRectPath(cairo_context, current_width_, current_height_, + kCornerRadius); + cairo_stroke(cairo_context); + + // Render the window-gripper. In order for a straight line to light up + // single pixels, Cairo requires the coordinates to have fractional + // components of 0.5 (so the "/ 2" is a deliberate integer division). + double gripper_top = current_height_ / 2 - 10.5; + double gripper_bottom = current_height_ / 2 + 10.5; + cairo_set_line_width(cairo_context, 1); + + double x = 12.5; + cairo_set_source_rgb(cairo_context, 0.70, 0.70, 0.70); + cairo_move_to(cairo_context, x, gripper_top); + cairo_line_to(cairo_context, x, gripper_bottom); + cairo_stroke(cairo_context); + x += 3; + cairo_move_to(cairo_context, x, gripper_top); + cairo_line_to(cairo_context, x, gripper_bottom); + cairo_stroke(cairo_context); + + x -= 2; + cairo_set_source_rgb(cairo_context, 0.97, 0.97, 0.97); + cairo_move_to(cairo_context, x, gripper_top); + cairo_line_to(cairo_context, x, gripper_bottom); + cairo_stroke(cairo_context); + x += 3; + cairo_move_to(cairo_context, x, gripper_top); + cairo_line_to(cairo_context, x, gripper_bottom); + cairo_stroke(cairo_context); + + cairo_destroy(cairo_context); + + gdk_window_set_back_pixmap(widget->window, background, FALSE); + g_object_unref(background); + gdk_window_invalidate_rect(widget->window, NULL, TRUE); + + return FALSE; +} + +gboolean DisconnectWindowLinux::OnButtonPress(GtkWidget* widget, + GdkEventButton* event) { + gtk_window_begin_move_drag(GTK_WINDOW(disconnect_window_), + event->button, + event->x_root, + event->y_root, + event->time); + return FALSE; +} + DisconnectWindow* DisconnectWindow::Create() { return new DisconnectWindowLinux; } |