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

Side by Side Diff: remoting/host/disconnect_window_linux.cc

Issue 8733014: New-style Disconnect window for Linux Chromoting host. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 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 "remoting/host/disconnect_window.h" 5 #include "remoting/host/disconnect_window.h"
6 6
7 #include <gtk/gtk.h> 7 #include <gtk/gtk.h>
8 #include <math.h>
8 9
9 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "base/string_util.h" 12 #include "base/string_util.h"
12 #include "base/utf_string_conversions.h" 13 #include "base/utf_string_conversions.h"
13 #include "remoting/host/chromoting_host.h" 14 #include "remoting/host/chromoting_host.h"
14 #include "remoting/host/ui_strings.h" 15 #include "remoting/host/ui_strings.h"
15 #include "ui/base/gtk/gtk_signal.h" 16 #include "ui/base/gtk/gtk_signal.h"
16 17
17 namespace {
18 // The width in pixels at which the message will wrap. Given that the message
19 // contains an un-splittable email address, it's unlikely that a fixed width
20 // is going to look aesthetically pleasing in all languages.
21 // TODO(jamiewalch): Replace this with a layout that only uses a single line,
22 // and which is docked at the top or bottom of the host screen, as in our
23 // UI mocks.
24 const int kMessageWidth = 300;
25 }
26
27 namespace remoting { 18 namespace remoting {
28 19
29 class DisconnectWindowLinux : public DisconnectWindow { 20 class DisconnectWindowLinux : public DisconnectWindow {
30 public: 21 public:
31 DisconnectWindowLinux(); 22 DisconnectWindowLinux();
32 virtual ~DisconnectWindowLinux(); 23 virtual ~DisconnectWindowLinux();
33 24
34 virtual void Show(ChromotingHost* host, 25 virtual void Show(ChromotingHost* host,
35 const std::string& username) OVERRIDE; 26 const std::string& username) OVERRIDE;
36 virtual void Hide() OVERRIDE; 27 virtual void Hide() OVERRIDE;
37 28
38 private: 29 private:
39 CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, void, OnResponse, int); 30 CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, gboolean, OnDelete, GdkEvent*);
31 CHROMEGTK_CALLBACK_0(DisconnectWindowLinux, void, OnClicked);
32 CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, gboolean, OnConfigure,
33 GdkEventConfigure*);
34 CHROMEGTK_CALLBACK_1(DisconnectWindowLinux, gboolean, OnButtonPress,
35 GdkEventButton*);
40 36
41 void CreateWindow(const UiStrings& ui_strings); 37 void CreateWindow(const UiStrings& ui_strings);
42 38
43 ChromotingHost* host_; 39 ChromotingHost* host_;
44 GtkWidget* disconnect_window_; 40 GtkWidget* disconnect_window_;
45 GtkWidget* message_; 41 GtkWidget* message_;
42 GtkWidget* button_;
43
44 // Used to distinguish resize events from other types of "configure-event"
45 // notifications.
46 int current_width_;
47 int current_height_;
46 48
47 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowLinux); 49 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowLinux);
48 }; 50 };
49 51
50 DisconnectWindowLinux::DisconnectWindowLinux() 52 DisconnectWindowLinux::DisconnectWindowLinux()
51 : host_(NULL), 53 : host_(NULL),
52 disconnect_window_(NULL) { 54 disconnect_window_(NULL),
55 current_width_(0),
56 current_height_(0) {
53 } 57 }
54 58
55 DisconnectWindowLinux::~DisconnectWindowLinux() { 59 DisconnectWindowLinux::~DisconnectWindowLinux() {
56 } 60 }
57 61
58 void DisconnectWindowLinux::CreateWindow(const UiStrings& ui_strings) { 62 void DisconnectWindowLinux::CreateWindow(const UiStrings& ui_strings) {
59 if (disconnect_window_) return; 63 if (disconnect_window_) return;
60 64
61 disconnect_window_ = gtk_dialog_new_with_buttons( 65 disconnect_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
62 UTF16ToUTF8(ui_strings.product_name).c_str(), 66 GtkWindow* window = GTK_WINDOW(disconnect_window_);
Jamie 2011/11/30 02:48:56 Do you ever not want to see disconnect_window_ as
Lambros 2011/12/02 01:41:55 The Chrome examples I saw elsewhere used GtkWidget
63 NULL,
64 GTK_DIALOG_NO_SEPARATOR,
65 UTF16ToUTF8(ui_strings.disconnect_button_text_plus_shortcut).c_str(),
66 GTK_RESPONSE_OK,
67 NULL);
68 67
69 GtkWindow* window = GTK_WINDOW(disconnect_window_); 68 g_signal_connect(disconnect_window_, "delete-event",
Jamie 2011/11/30 02:48:56 ...For example, will this still compile if disconn
Lambros 2011/12/02 01:41:55 That particular line would still compile (g_signal
69 G_CALLBACK(OnDeleteThunk), this);
70 gtk_window_set_title(window, UTF16ToUTF8(ui_strings.product_name).c_str());
Jamie 2011/11/30 02:48:56 What's the purpose of setting the title?
Lambros 2011/12/02 01:41:55 Might as well keep it. Theoretically, a user migh
70 gtk_window_set_resizable(window, FALSE); 71 gtk_window_set_resizable(window, FALSE);
72
71 // Try to keep the window always visible. 73 // Try to keep the window always visible.
72 gtk_window_stick(window); 74 gtk_window_stick(window);
73 gtk_window_set_keep_above(window, TRUE); 75 gtk_window_set_keep_above(window, TRUE);
76
77 // Remove window titlebar.
78 gtk_window_set_decorated(window, FALSE);
79
80 // In case the titlebar is still there, try to remove some of the buttons.
74 // Utility windows have no minimize button or taskbar presence. 81 // Utility windows have no minimize button or taskbar presence.
75 gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY); 82 gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY);
76 gtk_window_set_deletable(window, FALSE); 83 gtk_window_set_deletable(window, FALSE);
77 84
78 g_signal_connect(disconnect_window_, "response", 85 // Allow custom rendering of the background pixmap.
79 G_CALLBACK(OnResponseThunk), this); 86 gtk_widget_set_app_paintable(disconnect_window_, TRUE);
80 87
81 GtkWidget* content_area = 88 // Handle window resizing, to regenerate the background pixmap and window
82 gtk_dialog_get_content_area(GTK_DIALOG(disconnect_window_)); 89 // shape bitmap. The stored width & height need to be initialized here
90 // in case the window is created a second time (the size of the previous
91 // window would be remembered, preventing the generation of bitmaps for the
92 // new window).
93 current_height_ = current_width_ = 0;
Jamie 2011/11/30 02:48:56 I don't think this is necessary, as we regenerate
Lambros 2011/12/02 01:41:55 Actually, we were leaking the bitmaps! I've fixed
94 g_signal_connect(disconnect_window_, "configure-event",
95 G_CALLBACK(OnConfigureThunk), this);
83 96
84 GtkWidget* message_row = gtk_hbox_new(FALSE, 0); 97 // Handle mouse events to allow the user to drag the window around.
85 // TODO(lambroslambrou): Replace the magic number with an appropriate 98 gtk_widget_set_events(disconnect_window_, GDK_BUTTON_PRESS_MASK);
86 // constant from a header file (such as chrome/browser/ui/gtk/gtk_util.h 99 g_signal_connect(disconnect_window_, "button-press-event",
87 // but check_deps disallows its #inclusion here). 100 G_CALLBACK(OnButtonPressThunk), this);
88 gtk_container_set_border_width(GTK_CONTAINER(message_row), 12); 101
89 gtk_container_add(GTK_CONTAINER(content_area), message_row); 102 // All magic numbers taken from screen shots provided by UX.
103 // The alignment sets narrow margins at the top and bottom, compared with
104 // left and right. The left margin is made larger to accommodate the
105 // window movement gripper.
Jamie 2011/11/30 02:48:56 Will this work for bidi languages? If not, then th
Lambros 2011/12/02 01:41:55 Haven't tried it. I expect GTK will display the c
106 GtkWidget* align = gtk_alignment_new(0, 0, 1, 1);
107 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12);
108 gtk_container_add(GTK_CONTAINER(window), align);
109
110 GtkWidget* button_row = gtk_hbox_new(FALSE, 12);
111 gtk_container_add(GTK_CONTAINER(align), button_row);
112
113 button_ = gtk_button_new_with_label(
114 UTF16ToUTF8(ui_strings.disconnect_button_text_plus_shortcut).c_str());
115 gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0);
116
117 g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this);
90 118
91 message_ = gtk_label_new(NULL); 119 message_ = gtk_label_new(NULL);
92 gtk_widget_set_size_request(message_, kMessageWidth, -1); 120 gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0);
93 gtk_label_set_line_wrap(GTK_LABEL(message_), true);
94 gtk_container_add(GTK_CONTAINER(message_row), message_);
95 121
96 gtk_widget_show_all(content_area); 122 // Override any theme setting for the text color, so that the text is
123 // readable against the window's background pixmap.
124 PangoAttrList* attributes = pango_attr_list_new();
125 PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0);
126 pango_attr_list_insert(attributes, text_color);
127 gtk_label_set_attributes(GTK_LABEL(message_), attributes);
128
129 gtk_widget_show_all(disconnect_window_);
97 } 130 }
98 131
99 void DisconnectWindowLinux::Show(ChromotingHost* host, 132 void DisconnectWindowLinux::Show(ChromotingHost* host,
100 const std::string& username) { 133 const std::string& username) {
101 host_ = host; 134 host_ = host;
102 CreateWindow(host->ui_strings()); 135 CreateWindow(host->ui_strings());
103 136
104 string16 text = ReplaceStringPlaceholders( 137 string16 text = ReplaceStringPlaceholders(
105 host->ui_strings().disconnect_message, UTF8ToUTF16(username), NULL); 138 host->ui_strings().disconnect_message, UTF8ToUTF16(username), NULL);
106 gtk_label_set_text(GTK_LABEL(message_), UTF16ToUTF8(text).c_str()); 139 gtk_label_set_text(GTK_LABEL(message_), UTF16ToUTF8(text).c_str());
107 gtk_window_present(GTK_WINDOW(disconnect_window_)); 140 gtk_window_present(GTK_WINDOW(disconnect_window_));
108 } 141 }
109 142
110 void DisconnectWindowLinux::Hide() { 143 void DisconnectWindowLinux::Hide() {
111 if (disconnect_window_) { 144 if (disconnect_window_) {
112 gtk_widget_destroy(disconnect_window_); 145 gtk_widget_destroy(disconnect_window_);
113 disconnect_window_ = NULL; 146 disconnect_window_ = NULL;
114 } 147 }
115 } 148 }
116 149
117 void DisconnectWindowLinux::OnResponse(GtkWidget* dialog, int response_id) { 150 void DisconnectWindowLinux::OnClicked(GtkWidget* button) {
118 // |response_id| is ignored, because there is only one button, and pressing
119 // it should have the same effect as closing the window (if the window Close
120 // button were visible).
121 CHECK(host_); 151 CHECK(host_);
122 152
123 host_->Shutdown(base::Closure()); 153 host_->Shutdown(base::Closure());
124 Hide(); 154 Hide();
125 } 155 }
126 156
157 gboolean DisconnectWindowLinux::OnDelete(GtkWidget* window, GdkEvent* event) {
158 CHECK(host_);
159
160 host_->Shutdown(base::Closure());
161 Hide();
162
163 return TRUE;
164 }
165
166 namespace {
167 // Helper function for creating a rectangular path with rounded corners, as
168 // Cairo doesn't have this facility. |radius| is the arc-radius of each
169 // corner. The bounding rectangle extends from (0, 0) to (width, height).
170 void AddRoundRectPath(cairo_t* cr, int width, int height, int radius) {
171 cairo_new_sub_path(cr);
172 cairo_arc(cr, width - radius, radius, radius, -M_PI_2, 0);
173 cairo_arc(cr, width - radius, height - radius, radius, 0, M_PI_2);
174 cairo_arc(cr, radius, height - radius, radius, M_PI_2, 2 * M_PI_2);
175 cairo_arc(cr, radius, radius, radius, 2 * M_PI_2, 3 * M_PI_2);
176 cairo_close_path(cr);
177 }
178
179 } // namespace
180
181 gboolean DisconnectWindowLinux::OnConfigure(GtkWidget* widget,
182 GdkEventConfigure* event) {
183 // Only generate bitmaps if the size has actually changed.
184 if (event->width == current_width_ && event->height == current_height_)
185 return FALSE;
186
187 current_width_ = event->width;
188 current_height_ = event->height;
189
190 // Create the depth 1 pixmap for the window shape.
191 GdkPixmap* shape_mask = gdk_pixmap_new(NULL, current_width_, current_height_,
Jamie 2011/11/30 02:48:56 I assume that cairo_destroy also destroys this pix
Lambros 2011/12/02 01:41:55 I'm pretty sure cairo_destroy() doesn't destroy th
192 1);
193 cairo_t* cr = gdk_cairo_create(shape_mask);
Jamie 2011/11/30 02:48:56 Can this have a more descriptive name, please?
Lambros 2011/12/02 01:41:55 Done (groan!).
Jamie 2011/12/02 02:23:41 I think |context| would be sufficient if you think
194
195 // Set the arc radius for the corners. It is theoretically possible that a
196 // user might be able to resize the window (the Window Manager might not
197 // honor the fixed-size request), so make allowances for extremely thin
198 // sizes just in case.
Jamie 2011/11/30 02:48:56 What happens if we don't? If it just looks crappy
Lambros 2011/12/02 01:41:55 Yeah, it just looks very slightly strange at the c
199 int radius = std::min(6, std::min(current_width_ / 2, current_height_ / 2));
200
201 // Initialize the whole bitmap to be transparent.
202 cairo_set_source_rgba(cr, 0, 0, 0, 0);
203 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
204 cairo_paint(cr);
205
206 // Paint an opaque round rect covering the whole area (leaving the extreme
207 // corners transparent).
208 cairo_set_source_rgba(cr, 1, 1, 1, 1);
209 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
210 AddRoundRectPath(cr, current_width_, current_height_, radius);
211 cairo_fill(cr);
212
213 cairo_destroy(cr);
214 gdk_window_shape_combine_mask(widget->window, shape_mask, 0, 0);
215
216 // Create a full-color pixmap for the window background image.
217 GdkPixmap* background = gdk_pixmap_new(NULL, current_width_, current_height_,
218 24);
219 cr = gdk_cairo_create(background);
220
221 // Paint the whole bitmap one color.
222 cairo_set_source_rgb(cr, 233 / 256.0, 233 / 256.0, 233 / 256.0);
Jamie 2011/11/30 02:48:56 Seems an odd way of specifying the colour. I think
Lambros 2011/12/02 01:41:55 Done.
223 cairo_paint(cr);
224
225 // Paint the round-rectangle edge.
226 cairo_set_source_rgb(cr, 32 / 256.0, 176 / 256.0, 29 / 256.0);
227 cairo_set_line_width(cr, 6);
228 AddRoundRectPath(cr, current_width_, current_height_, radius);
229 cairo_stroke(cr);
230
231 // Render the window-gripper. In order for a straight line to light up
232 // single pixels, Cairo requires the coordinates to have fractional
233 // components of 0.5 (so the "/ 2" is a deliberate integer division).
234 double gripper_top = current_height_ / 2 - 10.5;
235 int gripper_bottom = current_height_ / 2 + 10.5;
Jamie 2011/11/30 02:48:56 I don't understand this. Why is one of these a dou
Lambros 2011/12/02 01:41:55 Doh! They should both be doubles. Fixed.
236 cairo_set_line_width(cr, 1);
237
238 double x = 12.5;
239 cairo_set_source_rgb(cr, 194 / 256.0, 194 / 256.0, 194 / 256.0);
Jamie 2011/11/30 02:48:56 Seems an odd way of specifying the colour. I think
Lambros 2011/12/02 01:41:55 Done.
240 cairo_move_to(cr, x, gripper_top);
241 cairo_line_to(cr, x, gripper_bottom);
242 cairo_stroke(cr);
243 x += 3;
244 cairo_move_to(cr, x, gripper_top);
245 cairo_line_to(cr, x, gripper_bottom);
246 cairo_stroke(cr);
247
248 x -= 2;
249 cairo_set_source_rgb(cr, 248 / 256.0, 248 / 256.0, 248 / 256.0);
250 cairo_move_to(cr, x, gripper_top);
251 cairo_line_to(cr, x, gripper_bottom);
252 cairo_stroke(cr);
253 x += 3;
254 cairo_move_to(cr, x, gripper_top);
255 cairo_line_to(cr, x, gripper_bottom);
256 cairo_stroke(cr);
257
258 cairo_destroy(cr);
259
260 gdk_window_set_back_pixmap(widget->window, background, FALSE);
261 gdk_window_invalidate_rect(widget->window, NULL, TRUE);
262
263 return FALSE;
264 }
265
266 gboolean DisconnectWindowLinux::OnButtonPress(GtkWidget* widget,
267 GdkEventButton* event) {
268 gtk_window_begin_move_drag(GTK_WINDOW(disconnect_window_),
269 event->button,
270 event->x_root,
271 event->y_root,
272 event->time);
273 return FALSE;
274 }
275
127 DisconnectWindow* DisconnectWindow::Create() { 276 DisconnectWindow* DisconnectWindow::Create() {
128 return new DisconnectWindowLinux; 277 return new DisconnectWindowLinux;
129 } 278 }
130 279
131 } // namespace remoting 280 } // namespace remoting
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698