Package dtk :: Package ui :: Module window

Source Code for Module dtk.ui.window

  1  #! /usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  # Copyright (C) 2011 ~ 2012 Deepin, Inc. 
  5  #               2011 ~ 2012 Wang Yong 
  6  #  
  7  # Author:     Wang Yong <lazycat.manatee@gmail.com> 
  8  # Maintainer: Wang Yong <lazycat.manatee@gmail.com> 
  9  #  
 10  # This program is free software: you can redistribute it and/or modify 
 11  # it under the terms of the GNU General Public License as published by 
 12  # the Free Software Foundation, either version 3 of the License, or 
 13  # any later version. 
 14  #  
 15  # This program is distributed in the hope that it will be useful, 
 16  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18  # GNU General Public License for more details. 
 19  #  
 20  # You should have received a copy of the GNU General Public License 
 21  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 22   
 23  from constant import EDGE_DICT 
 24  from draw import draw_window_shadow, draw_window_frame 
 25  from skin_config import skin_config 
 26  from theme import ui_theme 
 27  import cairo 
 28  import gobject 
 29  import gtk 
 30  from utils import (cairo_state, propagate_expose, set_cursor,  
 31                     resize_window, get_event_root_coords,  
 32                     enable_shadow, alpha_color_hex_to_cairo,  
 33                     is_double_click, move_window) 
 34   
35 -class Window(gtk.Window):
36 """ 37 The Window class is a subclass of gtk.Window. It adds some features that deepin-ui have to gtk.Window. 38 39 @undocumented: get_cursor_type 40 @undocumented: expose_window_background 41 @undocumented: expose_window_shadow 42 @undocumented: expose_window_frame 43 @undocumented: shape_window_frame 44 @undocumented: motion_notify 45 @undocumented: double_click_window 46 @undocumented: monitor_window_state 47 """
48 - def __init__(self, 49 enable_resize=False, 50 shadow_radius=6, 51 window_type=gtk.WINDOW_TOPLEVEL, 52 shadow_visible=True):
53 """ 54 Initialise the Window class. 55 56 @param enable_resize: If True, the window will be set resizable. By default, it's False. 57 @param shadow_radius: The radius of the shadow. By default, it's 6. 58 @param window_type: A flag of type gtk.WindowType, which indicates the type of the window. By default, it's gtk.WINDOW_TOPLEVEL. 59 @param shadow_visible: If True, the shadow is visible. By default, it's True, just disable when your program not allow manipulate colormap, such as mplayer. 60 """ 61 # Init. 62 gtk.Window.__init__(self, window_type) 63 skin_config.wrap_skin_window(self) 64 self.set_decorated(False) 65 self.set_colormap(gtk.gdk.Screen().get_rgba_colormap()) 66 self.add_events(gtk.gdk.ALL_EVENTS_MASK) 67 self.window_shadow = gtk.Alignment() 68 self.window_frame = gtk.VBox() 69 self.shadow_radius = shadow_radius 70 self.frame_radius = 2 71 self.shadow_is_visible = True 72 self.cursor_type = None 73 self.enable_resize = enable_resize 74 self.shadow_visible = shadow_visible 75 76 # Shadow setup. 77 if enable_shadow(self) and self.shadow_visible: 78 self.shadow_padding = self.shadow_radius - self.frame_radius 79 self.window_frame.connect("size-allocate", self.shape_window_frame) 80 self.window_shadow.connect("expose-event", self.expose_window_shadow) 81 else: 82 # Disable shadow when composited is false. 83 self.shadow_padding = 0 84 self.connect("size-allocate", self.shape_window_frame) 85 86 # Init window frame. 87 self.window_shadow.set(0.0, 0.0, 1.0, 1.0) 88 self.window_shadow.set_padding(self.shadow_padding, self.shadow_padding, self.shadow_padding, self.shadow_padding) 89 90 # Connect widgets. 91 self.add(self.window_shadow) 92 self.window_shadow.add(self.window_frame) 93 94 # Handle signal. 95 self.connect_after("expose-event", self.expose_window_background) 96 self.connect_after("size-allocate", lambda w, e: self.queue_draw()) 97 self.connect("motion-notify-event", self.motion_notify) 98 self.connect("button-press-event", self.resize_window) 99 self.connect("window-state-event", self.monitor_window_state) 100 self.window_frame.connect("expose-event", self.expose_window_frame)
101
102 - def show_window(self):
103 """ 104 Show the window. 105 """ 106 self.show_all()
107
108 - def expose_window_background(self, widget, event):
109 """ 110 Internal function to expose the window background. 111 112 @param widget: A window of type Gtk.Widget. 113 @param event: The expose event of type gtk.gdk.Event. 114 @return: Always return True. 115 """ 116 # Init. 117 cr = widget.window.cairo_create() 118 rect = widget.allocation 119 120 # Clear color to transparent window. 121 cr.set_source_rgba(0.0, 0.0, 0.0, 0.0) 122 cr.set_operator(cairo.OPERATOR_SOURCE) 123 cr.paint() 124 125 # Save cairo context. 126 if self.shadow_is_visible: 127 x = rect.x + self.shadow_padding 128 y = rect.y + self.shadow_padding 129 w = rect.width - self.shadow_padding * 2 130 h = rect.height - self.shadow_padding * 2 131 else: 132 x, y, w, h = rect.x, rect.y, rect.width, rect.height 133 134 # Draw background. 135 with cairo_state(cr): 136 cr.rectangle(x + 2, y, w - 4, 1) 137 cr.rectangle(x + 1, y + 1, w - 2, 1) 138 cr.rectangle(x, y + 2, w, h - 4) 139 cr.rectangle(x + 2, y + h - 1, w - 4, 1) 140 cr.rectangle(x + 1, y + h - 2, w - 2, 1) 141 142 cr.clip() 143 144 skin_config.render_background(cr, self, x, y) 145 146 # Draw mask. 147 self.draw_mask(cr, x, y, w, h) 148 149 # Draw corner shadow. 150 with cairo_state(cr): 151 cr.set_source_rgba(*alpha_color_hex_to_cairo(ui_theme.get_alpha_color("window_shadow_corner").get_color_info())) 152 153 cr.rectangle(x, y + 1, 1, 1) # top-left 154 cr.rectangle(x + 1, y, 1, 1) 155 156 cr.rectangle(x + w - 1, y + 1, 1, 1) # top-right 157 cr.rectangle(x + w - 2, y, 1, 1) 158 159 cr.rectangle(x, y + h - 2, 1, 1) # bottom-left 160 cr.rectangle(x + 1, y + h - 1, 1, 1) 161 162 cr.rectangle(x + w - 1, y + h - 2, 1, 1) # bottom-right 163 cr.rectangle(x + w - 2, y + h - 1, 1, 1) 164 165 cr.fill() 166 167 # Draw background corner. 168 with cairo_state(cr): 169 cr.rectangle(x, y + 1, 1, 1) # top-left 170 cr.rectangle(x + 1, y, 1, 1) 171 172 cr.rectangle(x + w - 1, y + 1, 1, 1) # top-right 173 cr.rectangle(x + w - 2, y, 1, 1) 174 175 cr.rectangle(x, y + h - 2, 1, 1) # bottom-left 176 cr.rectangle(x + 1, y + h - 1, 1, 1) 177 178 cr.rectangle(x + w - 1, y + h - 2, 1, 1) # bottom-right 179 cr.rectangle(x + w - 2, y + h - 1, 1, 1) 180 181 cr.clip() 182 183 skin_config.render_background(cr, self, x, y) 184 185 # Propagate expose. 186 propagate_expose(widget, event) 187 188 return True
189
190 - def draw_mask(self, cr, x, y, w, h):
191 ''' 192 Draw mask interface, you should implement this function own. 193 194 @param cr: Cairo context. 195 @param x: X coordinate of draw area. 196 @param y: Y coordinate of draw area. 197 @param w: Width of draw area. 198 @param h: Height of draw area. 199 ''' 200 pass
201
202 - def expose_window_shadow(self, widget, event):
203 """ 204 Interh function to expose the window shadow. 205 206 @param widget: the window of gtk.Widget. 207 @param event: The expose event of type gtk.gdk.Event. 208 """ 209 if self.shadow_is_visible: 210 # Init. 211 cr = widget.window.cairo_create() 212 rect = widget.allocation 213 x, y, w, h = rect.x, rect.y, rect.width, rect.height 214 215 # Draw window shadow. 216 draw_window_shadow(cr, x, y, w, h, self.shadow_radius, self.shadow_padding, ui_theme.get_shadow_color("window_shadow"))
217
218 - def expose_window_frame(self, widget, event):
219 """ 220 Internal function to expose the window frame. 221 222 @param widget: the window of gtk.Widget. 223 @param event: The expose event of type gtk.gdk.Event. 224 """ 225 # Init. 226 cr = widget.window.cairo_create() 227 rect = widget.allocation 228 x, y, w, h = rect.x, rect.y, rect.width, rect.height 229 230 draw_window_frame(cr, x, y, w, h, 231 ui_theme.get_alpha_color("window_frame_outside_1"), 232 ui_theme.get_alpha_color("window_frame_outside_2"), 233 ui_theme.get_alpha_color("window_frame_outside_3"), 234 ui_theme.get_alpha_color("window_frame_inside_1"), 235 ui_theme.get_alpha_color("window_frame_inside_2"), 236 )
237
238 - def shape_window_frame(self, widget, rect):
239 """ 240 Internal function to draw nonrectangular window frame. 241 242 @param widget: A widget of type gtk.Widget. 243 @param rect: The bounding region of the window. 244 """ 245 if widget.window != None and widget.get_has_window() and rect.width > 0 and rect.height > 0: 246 # Init. 247 x, y, w, h = rect.x, rect.y, rect.width, rect.height 248 bitmap = gtk.gdk.Pixmap(None, w, h, 1) 249 cr = bitmap.cairo_create() 250 251 # Clear the bitmap 252 cr.set_source_rgb(0.0, 0.0, 0.0) 253 cr.set_operator(cairo.OPERATOR_CLEAR) 254 cr.paint() 255 256 # Draw our shape into the bitmap using cairo. 257 cr.set_source_rgb(1.0, 1.0, 1.0) 258 cr.set_operator(cairo.OPERATOR_OVER) 259 260 cr.rectangle(x + 1, y, w - 2, 1) 261 cr.rectangle(x, y + 1, w, h - 2) 262 cr.rectangle(x + 1, y + h - 1, w - 2, 1) 263 264 cr.fill() 265 266 # Shape with given mask. 267 widget.shape_combine_mask(bitmap, 0, 0)
268
269 - def hide_shadow(self):
270 """ 271 Hide the window shadow. 272 """ 273 self.shadow_is_visible = False 274 self.window_shadow.set_padding(0, 0, 0, 0)
275
276 - def show_shadow(self):
277 """ 278 Show the window shadow. 279 """ 280 self.shadow_is_visible = True 281 self.window_shadow.set_padding(self.shadow_padding, self.shadow_padding, self.shadow_padding, self.shadow_padding)
282
283 - def min_window(self):
284 """ 285 Minimize the window. Make it iconified. 286 """ 287 self.iconify()
288
289 - def toggle_max_window(self):
290 """ 291 Toggle the window size between maximized size and normal size. 292 """ 293 window_state = self.window.get_state() 294 if window_state == gtk.gdk.WINDOW_STATE_MAXIMIZED: 295 self.unmaximize() 296 else: 297 self.maximize()
298
299 - def toggle_fullscreen_window(self):
300 """ 301 Toggle the window between fullscreen mode and normal size. 302 """ 303 window_state = self.window.get_state() 304 if window_state == gtk.gdk.WINDOW_STATE_FULLSCREEN: 305 self.unfullscreen() 306 else: 307 self.fullscreen()
308
309 - def close_window(self):
310 """ 311 Close the window. Send the destroy signal to the program. 312 313 @return: Always return False. 314 """ 315 # Hide window immediately when user click close button, 316 # user will feeling this software very quick, ;p 317 self.hide_all() 318 319 self.emit("destroy") 320 321 return False
322
323 - def motion_notify(self, widget, event):
324 """ 325 Internal callback for `motion-notify` signal. 326 327 @param widget: A widget of gtk.Widget. 328 @param event: The motion-notify-event of type gtk.gdk.Event 329 """ 330 if self.enable_resize and self.shadow_is_visible: 331 cursor_type = self.get_cursor_type(event) 332 if cursor_type != None: 333 set_cursor(self, self.cursor_type) 334 elif self.cursor_type != None: 335 set_cursor(self, None) 336 337 self.cursor_type = cursor_type
338
339 - def resize_window(self, widget, event):
340 """ 341 Resize the window. 342 343 @param widget: The window of type gtk.Widget. 344 @param event: A signal of type gtk.gdk.Event. 345 """ 346 if self.enable_resize: 347 edge = self.get_edge() 348 if edge != None: 349 resize_window(widget, event, self, edge)
350
352 """ 353 An interface which indicates whether the window could be maximized, you should implement this function you own. 354 355 @return: Always return False. 356 """ 357 return False
358
359 - def monitor_window_state(self, widget, event):
360 """ 361 Internal function to monitor window state, 362 363 add shadow when window at maximized or fullscreen status. Otherwise hide shadow. 364 365 @param widget: The window of type gtk.Widget. 366 @param event: The event of gtk.gdk.Event. 367 """ 368 window_state = self.window.get_state() 369 if window_state in [gtk.gdk.WINDOW_STATE_MAXIMIZED, gtk.gdk.WINDOW_STATE_FULLSCREEN]: 370 self.hide_shadow() 371 372 if self.is_disable_window_maximized(): 373 self.unmaximize() 374 else: 375 self.show_shadow()
376
377 - def add_move_event(self, widget):
378 """ 379 Add move event callback. 380 381 @param widget: A widget of type gtk.Widget. 382 """ 383 widget.connect("button-press-event", lambda w, e: move_window(w, e, self))
384
385 - def add_toggle_event(self, widget):
386 """ 387 Add toggle event callback. 388 389 @param widget: A widget of type gtk.Widget. 390 """ 391 widget.connect("button-press-event", self.double_click_window)
392
393 - def double_click_window(self, widget, event):
394 """ 395 Double click event handler of the window. It will maximize the window. 396 397 @param widget: A widget of type gtk.Widget. 398 @param event: A event of type gtk.gdk.Event. 399 @return: Always return False. 400 """ 401 if is_double_click(event): 402 self.toggle_max_window() 403 404 return False
405
406 - def get_edge(self):
407 """ 408 Get the edge which the cursor is on, according to the cursor type. 409 410 @return: If there is a corresponding cursor type, an instance of gtk.gdk.WindowEdge is returned, else None is returned. 411 """ 412 if EDGE_DICT.has_key(self.cursor_type): 413 return EDGE_DICT[self.cursor_type] 414 else: 415 return None
416
417 - def get_cursor_type(self, event):
418 """ 419 Get the cursor position. 420 421 @param event: An event of type gtk.gdk.Event. 422 @return: If the cursor is on the frame of the window, return the cursor position. Otherwise return None. 423 """ 424 # Get event coordinate. 425 (ex, ey) = get_event_root_coords(event) 426 427 # Get window allocation. 428 rect = self.get_allocation() 429 (wx, wy) = self.get_position() 430 ww = rect.width 431 wh = rect.height 432 433 # Return cursor position. 434 if wx <= ex <= wx + self.shadow_padding: 435 if wy <= ey <= wy + self.shadow_padding * 2: 436 return gtk.gdk.TOP_LEFT_CORNER 437 elif wy + wh - (self.shadow_padding * 2) <= ey <= wy + wh: 438 return gtk.gdk.BOTTOM_LEFT_CORNER 439 elif wy + self.shadow_padding < ey < wy + wh - self.shadow_padding: 440 return gtk.gdk.LEFT_SIDE 441 else: 442 return None 443 elif wx + ww - self.shadow_padding <= ex <= wx + ww: 444 if wy <= ey <= wy + self.shadow_padding * 2: 445 return gtk.gdk.TOP_RIGHT_CORNER 446 elif wy + wh - (self.shadow_padding * 2) <= ey <= wy + wh: 447 return gtk.gdk.BOTTOM_RIGHT_CORNER 448 elif wy + self.shadow_padding < ey < wy + wh - self.shadow_padding: 449 return gtk.gdk.RIGHT_SIDE 450 else: 451 return None 452 elif wx + self.shadow_padding < ex < wx + ww - self.shadow_padding: 453 if wy <= ey <= wy + self.shadow_padding: 454 return gtk.gdk.TOP_SIDE 455 elif wy + wh - self.shadow_padding <= ey <= wy + wh: 456 return gtk.gdk.BOTTOM_SIDE 457 else: 458 return None 459 else: 460 return None
461
462 - def get_shadow_size(self):
463 """ 464 Get the shadow size. 465 466 @return: return the shadow size or (0, 0) 467 """ 468 if enable_shadow(self) and self.shadow_visible: 469 window_state = self.window.get_state() 470 if window_state in [gtk.gdk.WINDOW_STATE_MAXIMIZED, gtk.gdk.WINDOW_STATE_FULLSCREEN]: 471 return (0, 0) 472 else: 473 return (self.shadow_padding, self.shadow_padding) 474 else: 475 return (0, 0)
476 477 gobject.type_register(Window) 478 479 if __name__ == "__main__": 480 import pseudo_skin 481 482 window = Window() 483 window.connect("destroy", lambda w: gtk.main_quit()) 484 window.set_size_request(500, 500) 485 # window.window_frame.add(gtk.Button("Linux Deepin")) 486 window.show_all() 487 488 gtk.main() 489