def start_animation(self, x, y): if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("stop", self.stop_animation) self.timeline.connect("update", self.update_animation, x, y) self.timeline.connect("completed", self.completed_animation, x, y) self.timeline.run()
def favorite_animation(window): # Add install animation. (screen, px, py, modifier_type) = window.get_display().get_pointer() ax, ay = px, py (wx, wy) = window.window.get_origin() offset_bx = 480 offset_by = 5 bx, by = wx + offset_bx, wy + offset_by offset_cx = 10 offset_cy = 10 if ax < bx: cx, cy = wx + offset_bx + offset_cx, wy + offset_by + offset_cy else: cx, cy = wx + offset_bx - offset_cx, wy + offset_by + offset_cy [[a], [b], [c]] = solve_parabola((ax, ay), (bx, by), (cx, cy)) icon_window = IconWindow(utils.get_common_image('heart/heartx2.png')) icon_window.move(ax, ay) icon_window.show_all() timeline = Timeline(500, CURVE_SINE) timeline.connect( "update", lambda source, status: update(source, status, icon_window, (ax, ay), (bx, by), (cx, cy), (a, b, c))) timeline.connect("completed", lambda source: finish(source, icon_window)) timeline.run()
def start_animation(self, target_pixbuf, is_default=False): self.is_default = False self.target_pixbuf = target_pixbuf if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("update", self.update_animation) self.timeline.connect( "completed", lambda source: self.completed_animation( source, target_pixbuf, is_default)) self.timeline.run()
def install_pkg(bus_interface, install_page, pkg_names, window): for install_item in install_page.treeview.visible_items: if install_item.pkg_name in pkg_names and install_item.status != install_item.STATUS_DOWNLOAD_FAILED: pkg_names.remove(install_item.pkg_name) if pkg_names == []: return # Add install animation. (screen, px, py, modifier_type) = window.get_display().get_pointer() ax, ay = px, py (wx, wy) = window.window.get_origin() offset_bx = 430 offset_by = -20 bx, by = wx + offset_bx, wy + offset_by offset_cx = 10 offset_cy = 10 if ax < bx: cx, cy = wx + offset_bx + offset_cx, wy + offset_by + offset_cy else: cx, cy = wx + offset_bx - offset_cx, wy + offset_by + offset_cy [[a], [b], [c]] = solve_parabola((ax, ay), (bx, by), (cx, cy)) icon_window = IconWindow(pkg_names[0]) icon_window.move(ax, ay) icon_window.show_all() timeline = Timeline(500, CURVE_SINE) timeline.connect("update", lambda source, status: update(source, status, icon_window, (ax, ay), (bx, by), (cx, cy), (a, b, c))) timeline.connect("completed", lambda source: finish(source, icon_window, bus_interface, pkg_names)) timeline.run()
def favorite_animation(window): # Add install animation. (screen, px, py, modifier_type) = window.get_display().get_pointer() ax, ay = px, py (wx, wy) = window.window.get_origin() offset_bx = 480 offset_by = 5 bx, by = wx + offset_bx, wy + offset_by offset_cx = 10 offset_cy = 10 if ax < bx: cx, cy = wx + offset_bx + offset_cx, wy + offset_by + offset_cy else: cx, cy = wx + offset_bx - offset_cx, wy + offset_by + offset_cy [[a], [b], [c]] = solve_parabola((ax, ay), (bx, by), (cx, cy)) icon_window = IconWindow(utils.get_common_image('heart/heartx2.png')) icon_window.move(ax, ay) icon_window.show_all() timeline = Timeline(500, CURVE_SINE) timeline.connect("update", lambda source, status: update(source, status, icon_window, (ax, ay), (bx, by), (cx, cy), (a, b, c))) timeline.connect("completed", lambda source: finish(source, icon_window)) timeline.run()
def move_down(self, send_obj): print "send_level", send_obj.level print "self_level", self.level if self.level == 2: (move_down_height) = send_obj.window_height self.move_up_timeline = Timeline(self.animation_time, CURVE_SINE) self.move_up_timeline.connect("update", self.update_move_down_animation, move_down_height) self.move_up_timeline.run() self.level = 1
def start_animation(self, widget): if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("update", self.update_animation) self.timeline.connect("completed", lambda source: self.completed_animation(source, widget)) self.timeline.run()
def start_animation(self, target_pixbuf, is_default=False): self.is_default = False self.target_pixbuf = target_pixbuf if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("update", self.update_animation) self.timeline.connect("completed", lambda source: self.completed_animation(source, target_pixbuf, is_default)) self.timeline.run()
def start_animation(self, index, tab_start_x): if not self.in_animiation: self.in_animiation = True source_tab_x = tab_start_x + self.tab_index * self.tab_width target_tab_x = tab_start_x + index * self.tab_width timeline = Timeline(self.tab_animation_time, CURVE_SINE) timeline.connect('update', lambda source, status: self.update_animation(source, status, source_tab_x, (target_tab_x - source_tab_x))) timeline.connect("completed", lambda source: self.completed_animation(source, index)) timeline.run() self.emit("tab_switch_start", index)
def start_animation(self, animiation_time, target_index=None): if target_index == None: if self.active_index >= self.slide_number - 1: target_index = 0 else: target_index = self.active_index + 1 if not self.in_animiation: self.in_animiation = True self.target_index = target_index timeline = Timeline(animiation_time, CURVE_SINE) timeline.connect("update", self.update_animation) timeline.connect( "completed", lambda source: self.completed_animation(source, target_index)) timeline.run() return True
def start_animation(self, index, tab_start_x): if not self.in_animiation: self.in_animiation = True source_tab_x = tab_start_x + self.tab_index * self.tab_width target_tab_x = tab_start_x + index * self.tab_width timeline = Timeline(self.tab_animation_time, CURVE_SINE) timeline.connect( 'update', lambda source, status: self.update_animation( source, status, source_tab_x, (target_tab_x - source_tab_x))) timeline.connect( "completed", lambda source: self.completed_animation(source, index)) timeline.run() self.emit("tab_switch_start", self.items[index])
def start_animation(self, animiation_time, target_index=None): if target_index == None: if self.active_index >= self.slide_number - 1: target_index = 0 else: target_index = self.active_index + 1 if not self.in_animiation: self.in_animiation = True self.target_index = target_index timeline = Timeline(animiation_time, CURVE_SINE) timeline.connect("update", self.update_animation) timeline.connect("completed", lambda source: self.completed_animation(source, target_index)) timeline.run() return True
def start_animation(self, animation_time, index=None): # Update ticker with active index if option index is None. if index == None: if self.active_index >= self.image_number - 1: index = 0 else: index = self.active_index + 1 if not self.in_animiation: self.in_animiation = True self.target_index = index timeline = Timeline(animation_time, CURVE_SINE) timeline.connect('update', self.update_animation) timeline.connect( "completed", lambda source: self.completed_animation(source, index)) timeline.run() return True
def start_animation(self, animation_time, index=None): # Update ticker with active index if option index is None. if index == None: if self.active_index >= self.image_number - 1: index = 0 else: index = self.active_index + 1 if not self.in_animiation: self.in_animiation = True self.target_index = index timeline = Timeline(animation_time, CURVE_SINE) timeline.connect('update', self.update_animation) timeline.connect("completed", lambda source: self.completed_animation(source, index)) timeline.run() return True
def start_move_up_animation(self, height): timeline = Timeline(ANIMATION_TIME, CURVE_SINE) timeline.connect("update", self.update_move_up_animation, height) timeline.connect("completed", self.on_move_up_completed) timeline.run()
def start_destroy_animation(self): timeline = Timeline(self.animation_time, CURVE_SINE) timeline.connect("update", self.update_destory_animation) timeline.connect("completed", self.destroy_animation_complete) timeline.run() return False
class Bubble(gtk.Window): ''' class docs ''' def __init__(self, notification, height, create_time): ''' init docs ''' gtk.Window.__init__(self, gtk.WINDOW_POPUP) self.notification = notification self.create_time = create_time self.init_size(height) self.init_pixbuf() self.init_action_dict() self.set_colormap(gtk.gdk.Screen().get_rgba_colormap() or gtk.gdk.Screen().get_rgb_colormap()) self.set_keep_above(True) self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK) self.connect("expose-event", self.on_expose_event) self.connect("button-press-event", self.on_button_press_event) self.connect("motion-notify-event", self.on_motion_notify_event) self.animation_time = 200 self.level = 0 self.win_x, self.win_y = self._get_position() self.pointer_hand_rectangles = [] self.move(self.win_x, self.win_y) self.set_opacity(0) self.show_all() self.timeout_id = gobject.timeout_add(EXPIRE_TIMEOUT, self.start_destroy_animation) event_manager.connect("manual-cancelled", self.move_down) def init_size(self, height): ''' docs ''' self.window_width = 302 self.window_height = height self.close_pixbuf = app_theme.get_pixbuf("close.png").get_pixbuf() self.close_rect = gtk.gdk.Rectangle(self.window_width - self.close_pixbuf.get_width() - CLOSE_OFFSET_WIDTH, CLOSE_OFFSET_HEIGHT, self.close_pixbuf.get_width(), self.close_pixbuf.get_height()) self.set_size_request(self.window_width, self.window_height) self.action_button_areas = [] @property def source_cmd(self): actions = self.notification.actions if "default" in actions: try: cmd = actions[actions.index("default") + 1] return cmd except Exception: pass return self.notification.app_name def get_icon_pixbuf(self): hints = self.notification.hints pixbuf = None image_data = hints.get("image-data", None) or hints.get("image_data", None) if image_data: pixbuf = icon_manager.pixbuf_from_dbus(image_data, ICON_SIZE) if pixbuf: return pixbuf image_path = hints.get("image-path", None) or hints.get("image_path", None) if image_path: try: pixbuf = icon_manager.pixbuf_from_path(image_path, ICON_SIZE[0], ICON_SIZE[1]) except: pixbuf = None if pixbuf: return pixbuf app_icon = self.notification.app_icon if app_icon: pixbuf = icon_manager.pixbuf_from_icon_name(app_icon, ICON_SIZE[0]) if pixbuf: return pixbuf else: pixbuf = icon_manager.pixbuf_from_path(app_icon, ICON_SIZE[0], ICON_SIZE[1]) if pixbuf: return pixbuf return self.default_icon def init_pixbuf(self): pixbuf_file_name = "mini.png" if self.window_height == 87 else "max.png" self.bg_pixbuf = app_theme.get_pixbuf(pixbuf_file_name).get_pixbuf() self.default_icon = app_theme.get_pixbuf("icon.png").get_pixbuf() self.notification_icon = self.get_icon_pixbuf() def on_expose_event(self, widget, event): cr = widget.window.cairo_create() rect = widget.allocation if self.is_composited(): cr.rectangle(*rect) cr.set_source_rgba(0, 0, 0, 0) cr.set_operator(cairo.OPERATOR_SOURCE) cr.fill() else: cr.rectangle(rect.x, rect.y, rect.width, rect.height) cr.set_operator(cairo.OPERATOR_SOURCE) cr.set_source_rgb(0.9, 0.9, 0.9) cr.fill() cr.set_operator(cairo.OPERATOR_OVER) self.draw_notification(cr, rect) propagate_expose(widget, event) return True def _render_action(self, cr, rect, text): with cairo_disable_antialias(cr): cr.rectangle(*rect) cr.set_source_rgb(1, 1, 1) cr.set_line_width(2) cr.stroke_preserve() cr.set_source_rgb(*color_hex_to_cairo(ACTION_BUTTON_COLOR_BG)) cr.fill() draw_text(cr, text, rect.x, rect.y, rect.width, rect.height, text_color="#ffffff", alignment=pango.ALIGN_CENTER) def init_action_dict(self): self.action_dict = {} actions = self.notification.actions if len(actions) % 2 == 0: for index, action in enumerate(actions): if index % 2 == 0: self.action_dict[action] = actions[index + 1] if "default" in self.action_dict: self.action_dict.pop("default") def draw_notification(self, cr, rect): draw_pixbuf(cr, self.bg_pixbuf, rect.x, rect.y) icon_x = rect.x + self.close_rect.x icon_y = rect.y + self.close_rect.y draw_pixbuf(cr, self.close_pixbuf, icon_x, icon_y) # Draw app icon. icon_x = APP_ICON_X + (APP_ICON_WIDTH - self.notification_icon.get_width()) / 2 icon_y = rect.y + (rect.height - self.notification_icon.get_height()) / 2 draw_pixbuf(cr, self.notification_icon, icon_x, icon_y) # Draw summary. width, _height = get_content_size(self.notification.summary) draw_text(cr, "<b>%s</b>" % self.notification.summary, rect.x + TEXT_X, rect.y + SUMMARY_TEXT_Y, TEXT_WIDTH - self.close_pixbuf.get_width(), _height, text_color="#FFFFFF", text_size=10) #Draw body body_text_y = SUMMARY_TEXT_Y + _height + TEXT_PADDING_Y render_hyperlink_support_text(self, cr, self.notification.body, rect.x + TEXT_X, rect.y + body_text_y, TEXT_WIDTH - self.close_pixbuf.get_width(), BODY_TEXT_HEIGHT, wrap_width=TEXT_WIDTH - self.close_pixbuf.get_width() + 5, text_color="#FFFFFF", text_size=10, vertical_alignment=TEXT_ALIGN_TOP, clip_line_count=2) # Draw action for index, key in enumerate(self.action_dict): if key != "default" and index < 2: temp_rect = gtk.gdk.Rectangle( rect.x + TEXT_X + index * (ACTION_BUTTON_SPACING + ACTION_BUTTON_WIDTH), rect.y + body_text_y + BODY_TEXT_HEIGHT + ACTION_BUTTON_PADDING_Y, ACTION_BUTTON_WIDTH, ACTION_BUTTON_HEIGHT) self.action_button_areas.append(temp_rect) self._render_action(cr, temp_rect, self.action_dict[key]) def on_motion_notify_event(self, widget, event): for rect in self.pointer_hand_rectangles: if is_in_rect((event.x, event.y), rect): widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1)) return else: widget.window.set_cursor(None) for rect in self.action_button_areas: if is_in_rect((event.x, event.y), rect): widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1)) return else: widget.window.set_cursor(None) def on_button_press_event(self, widget, event): if is_in_rect((event.x, event.y), (self.close_rect.x, self.close_rect.y, self.close_rect.width, self.close_rect.height)): gobject.source_remove(self.timeout_id) self.destroy() event_manager.emit("manual-cancelled", self) return for index, rect in enumerate(self.pointer_hand_rectangles): if is_in_rect((event.x, event.y), rect): action = self.notification["hints"]["x-deepin-hyperlinks"][index] if action.has_key("href"): webbrowser.open_new_tab(action.get("href")) return for index, rect in enumerate(self.action_button_areas): if is_in_rect((event.x, event.y), rect): for i, action_key in enumerate(self.action_dict): if i == index: event_manager.emit("action-invoked", (self.notification.id, action_key)) gobject.source_remove(self.timeout_id) self.destroy() event_manager.emit("manual-cancelled", self) return rect = widget.allocation if is_in_rect((event.x, event.y), (rect.x, rect.y, APP_ICON_WIDTH, self.window_height)): self.open_source_software() def open_source_software(self): run_command(self.source_cmd) def _get_position(self): screen_w, screen_h = get_screen_size() win_x = screen_w - self.window_width - WINDOW_OFFSET_WIDTH win_y = screen_h - self.window_height - DOCK_HEIGHT - WINDOW_OFFSET_HEIGHT self.last_y = win_y + self.window_height return win_x, win_y def start_destroy_animation(self): timeline = Timeline(self.animation_time, CURVE_SINE) timeline.connect("update", self.update_destory_animation) timeline.connect("completed", self.destroy_animation_complete) timeline.run() return False def update_destory_animation(self, source, status): if status != 1: self.set_opacity(1.0-status) else: self.destroy() def destroy_animation_complete(self, source): event_manager.emit("bubble-destroy", self) def move_up_by(self, move_up_height, update_height=False): self.win_y = int(self.last_y - move_up_height) self.move(self.win_x, self.win_y) if update_height: self.last_y = self.win_y def move_down_by(self, move_down_height, update_height=False): self.win_y = int(self.last_y + move_down_height) self.move(self.win_x, self.win_y) if update_height: self.last_y = self.win_y def move_down(self, send_obj): print "send_level", send_obj.level print "self_level", self.level if self.level == 2: (move_down_height) = send_obj.window_height self.move_up_timeline = Timeline(self.animation_time, CURVE_SINE) self.move_up_timeline.connect("update", self.update_move_down_animation, move_down_height) self.move_up_timeline.run() self.level = 1 def update_move_down_animation(self, source, status, move_down_height): self.move_down_by(move_down_height * status, not bool(status - 1))
class CoverButton(gtk.Button): def __init__(self): super(CoverButton, self).__init__() self.cover_side_pixbuf = app_theme.get_pixbuf("cover/side.png").get_pixbuf() self.set_size_request(self.cover_side_pixbuf.get_width(), self.cover_side_pixbuf.get_height()) self.webcast_dpixbuf = app_theme.get_pixbuf("cover/webcast.png") self.local_dpixbuf = app_theme.get_pixbuf("cover/default_cover.png") self.connect("expose-event", self.expose_button_cb) Dispatcher.connect("album-changed", self.on_album_changed) self.current_song = None self.next_cover_to_download = None self.condition = threading.Condition() self.thread = threading.Thread(target=self.func_thread) self.thread.setDaemon(True) self.thread.start() # animation params. self.active_alpha = 1.0 self.target_alpha = 0.0 self.in_animation = False self.animation_time = 1500 self.active_pixbuf = self.get_default_pixbuf() self.target_pixbuf = None self.is_default = True def start_animation(self, target_pixbuf, is_default=False): self.is_default = False self.target_pixbuf = target_pixbuf if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("update", self.update_animation) self.timeline.connect("completed", lambda source: self.completed_animation(source, target_pixbuf, is_default)) self.timeline.run() def update_animation(self, source, status): self.active_alpha = 1.0 - status self.target_alpha = status self.queue_draw() def completed_animation(self, source, target_pixbuf, is_default): self.active_pixbuf = target_pixbuf self.active_alpha = 1.0 self.target_alpha = 0.0 self.in_animation = False self.is_default = is_default self.queue_draw() def expose_button_cb(self, widget, event): cr = widget.window.cairo_create() rect = widget.allocation # Draw cover side. if self.is_default: self.active_pixbuf = self.get_default_pixbuf(self.current_song) draw_pixbuf(cr, self.cover_side_pixbuf, rect.x, rect.y) draw_pixbuf(cr, self.active_pixbuf, rect.x + 3, rect.y + 3, self.active_alpha) if self.target_pixbuf: draw_pixbuf(cr, self.target_pixbuf, rect.x + 3, rect.y + 3, self.target_alpha) return True def func_thread(self): while True: self.condition.acquire() while not self.next_cover_to_download: self.condition.wait() next_cover_to_download = self.next_cover_to_download self.next_cover_to_download = None self.condition.release() self.set_current_cover(True, next_cover_to_download) def update_default_cover(self, song): self.start_animation(self.get_default_pixbuf(song), is_default=True) def get_default_pixbuf(self, song=None): if not song: song = Player.song if not song: pixbuf = self.local_dpixbuf.get_pixbuf() return utils.get_optimum_pixbuf(pixbuf, COVER_SIZE["x"], COVER_SIZE["y"]) if song.get_type() == "webcast": pixbuf = self.webcast_dpixbuf.get_pixbuf() else: pixbuf = self.local_dpixbuf.get_pixbuf() return utils.get_optimum_pixbuf(pixbuf, COVER_SIZE["x"], COVER_SIZE["y"]) def init_default_cover(self): pixbuf = self.get_default_pixbuf() self.start_animation(pixbuf, is_default=True) def on_album_changed(self, obj, song): if song == self.current_song: self.update_cover(None, song) def update_cover(self, widget, song): self.current_song = song if self.current_song is not None: if not self.set_current_cover(False): self.condition.acquire() self.next_cover_to_download = self.current_song self.condition.notify() self.condition.release() def set_current_cover(self, try_web=True, force_song=None): if not force_song: force_song = self.current_song filename = CoverManager.get_cover(force_song, try_web) if Player.song != force_song: return False if not filename: if not try_web: if force_song.get_type() == "webcast": pixbuf = utils.get_optimum_pixbuf(self.webcast_dpixbuf.get_pixbuf(), COVER_SIZE["x"], COVER_SIZE["y"]) self.start_animation(pixbuf, is_default=True) return True else: pixbuf = utils.get_optimum_pixbuf(self.local_dpixbuf.get_pixbuf(), COVER_SIZE["x"], COVER_SIZE["y"]) self.start_animation(pixbuf, is_default=True) return False return False else: try: pixbuf = get_optimum_pixbuf_from_file(filename, COVER_SIZE["x"], COVER_SIZE["y"]) except gobject.GError: return False else: self.start_animation(pixbuf, is_default=False) return True
class MiniWindow(Window): def __init__(self): Window.__init__(self, window_type=gtk.WINDOW_POPUP, shape_frame_function=self.shape_mini_frame, expose_frame_function=self.expose_mini_frame) self.set_property("skip-pager-hint", True) self.set_property("skip-taskbar-hint", True) self.body_box = gtk.VBox() self.control_box = gtk.HBox() self.action_box = gtk.HBox() self.event_box = gtk.HBox() self.info_box = gtk.HBox() # Build info box self.logo_padding_left = 10 self.logo_padding_right = 10 logo_box = self.create_logo_box() logo_box_align = set_widget_gravity(logo_box, (0.5, 0.5, 0, 0), (0, 0, self.logo_padding_left, self.logo_padding_right)) self.playinfo = PlayInfo(239, None, 0, "#000000") self.info_box.pack_start(logo_box_align, False, False) self.info_box.pack_start(self.playinfo, False, False) # Build control box self.lyrics_button = self.create_lyrics_button() self.signal_auto = False if config.getboolean("lyrics", "status"): self.lyrics_button.set_active(True) self.signal_auto = True self.lyrics_padding_left = 10 self.lyrics_padding_right = 8 lyrics_button_align = set_widget_gravity(self.lyrics_button, (0.5, 0.5, 0, 0), (0, 0, self.lyrics_padding_left, self.lyrics_padding_right)) self.previous_button = self.create_button("previous") self.next_button = self.create_button("next") self.playpause_button = self.create_playpause_button() # swap played status handler self.__id_signal_play = self.playpause_button.connect("toggled", self.on_player_playpause) Player.connect("played", self.__swap_play_status, True) Player.connect("paused", self.__swap_play_status, False) Player.connect("stopped", self.__swap_play_status, False) Player.connect("play-end", self.__swap_play_status, False) self.volume_slider = VolumeSlider(auto_hide=False, mini_mode=True) self.volume_padding_left = 8 volume_slider_align = set_widget_gravity(self.volume_slider, (0.5, 0.5, 0, 0), (0, 0, self.volume_padding_left, 0)) self.action_box.pack_start(lyrics_button_align, False, False) self.action_box.pack_start(set_widget_vcenter(self.previous_button), False, False) self.action_box.pack_start(set_widget_vcenter(self.playpause_button), False, True) self.action_box.pack_start(set_widget_vcenter(self.next_button), False, False) self.action_box.pack_start(volume_slider_align, False, False) # Build event box. quell_button = QuellButton() quell_button.connect("clicked", self.on_quell_button_clicked) min_button = MinButton() min_button.connect("clicked", lambda w: self.min_window()) close_button = CloseButton() close_button.connect("clicked", self.try_to_quit) self.event_box.pack_start(quell_button, False, False) self.event_box.pack_start(min_button, False, False) self.event_box.pack_start(close_button, False, False) event_box_align = set_widget_gravity(self.event_box, paddings=(0, 0, 8, 0)) self.control_box.pack_start(self.action_box, False, True) self.control_box.pack_end(event_box_align, False, True) self.connect("configure-event", self.on_configure_event) self.connect("enter-notify-event", self.on_enter_notify_event) self.connect("leave-notify-event", self.on_leave_notify_event) self.connect("button-press-event", self.on_button_press_event) self.connect("motion-notify-event", self.on_motion_notify_event) self.connect("button-release-event", self.on_button_release_event) Dispatcher.connect("close-lyrics", lambda w : self.lyrics_button.set_active(False)) Dispatcher.connect("show-lyrics", lambda w: self.lyrics_button.set_active(True)) if config.get("mini", "x") == "-1": self.set_position(gtk.WIN_POS_CENTER) else: self.move(int(config.get("mini","x")),int(config.get("mini","y"))) # pixbufs self.info_pixbuf = None self.control_pixbuf = None self.body_box.add(self.info_box) self.window_frame.add(self.body_box) self.set_size_request(305, 40) # animation params. self.active_alpha = 1.0 self.target_alpha = 0.0 self.in_animation = False self.animation_time = 1000 self.animation_timeout_id = None self.draw_animation = False self.active_draw_func = None self.target_draw_func = None # drag params. self.drag_move = False self.old_x = self.old_y = self.mouse_x = self.mouse_y = 0 def on_quell_button_clicked(self, widget): main_window = get_main_window() main_window.change_app_mode("normal") switch_tab(self.body_box, self.info_box) def try_to_quit(self, widget): self.hide_all() main_window = get_main_window() main_window.quit() def create_lyrics_button(self): toggle_button = ToggleButton( app_theme.get_pixbuf("mini/lyrics_inactive_normal.png"), app_theme.get_pixbuf("mini/lyrics_active_normal.png"), app_theme.get_pixbuf("mini/lyrics_inactive_hover.png"), app_theme.get_pixbuf("mini/lyrics_active_hover.png"), app_theme.get_pixbuf("mini/lyrics_inactive_press.png"), app_theme.get_pixbuf("mini/lyrics_active_press.png"), ) toggle_button.connect("toggled", self.change_lyrics_status) return toggle_button def change_lyrics_status(self, widget): if self.signal_auto: if widget.get_active(): Dispatcher.show_lyrics() else: Dispatcher.close_lyrics() def create_button(self, name, tip_msg=None): button = ImageButton( app_theme.get_pixbuf("mini/%s_normal.png" % name), app_theme.get_pixbuf("mini/%s_hover.png" % name), app_theme.get_pixbuf("mini/%s_press.png" % name), ) button.connect("clicked", self.player_control, name) if tip_msg: Tooltip.text(button, tip_msg) return button def create_logo_box(self): return ImageBox(app_theme.get_pixbuf("mini/logo.png")) def player_control(self, button, name): if name == "next": getattr(Player, name)(True) else: getattr(Player, name)() def is_in_window(self): root_window = gtk.gdk.get_default_root_window() r_x, r_y = root_window.get_pointer()[:2] o_x, o_y = self.get_position() rect = self.allocation rect.x = o_x + 1 rect.y = o_y + 1 rect.width -= 2 rect.height -= 2 return is_in_rect((r_x, r_y), rect) def adjust_move_coordinate(self, widget, x, y): x = max(x, 0) y = max(y, 0) screen = widget.get_screen() w, h = widget.get_size() screen_w, screen_h = screen.get_width(), screen.get_height() if x + w > screen_w: x = screen_w - w if y + h > screen_h: y = screen_h - h return (int(x), int(y)) def create_playpause_button(self): play_normal_pixbuf = app_theme.get_pixbuf("mini/play_normal.png") pause_normal_pixbuf = app_theme.get_pixbuf("mini/pause_normal.png") play_hover_pixbuf = app_theme.get_pixbuf("mini/play_hover.png") pause_hover_pixbuf = app_theme.get_pixbuf("mini/pause_hover.png") play_press_pixbuf = app_theme.get_pixbuf("mini/play_press.png") pause_press_pixbuf = app_theme.get_pixbuf("mini/pause_press.png") playpause_button = ToggleButton(play_normal_pixbuf, pause_normal_pixbuf, play_hover_pixbuf, pause_hover_pixbuf, play_press_pixbuf, pause_press_pixbuf) return playpause_button def on_player_playpause(self, widget): if Player.song: Player.playpause() def __swap_play_status(self, obj, active): self.playpause_button.handler_block(self.__id_signal_play) self.playpause_button.set_active(active) self.playpause_button.handler_unblock(self.__id_signal_play) def on_configure_event(self, widget, event): if widget.get_property("visible"): config.set("mini","x","%d" % event.x) config.set("mini","y","%d" % event.y) def on_enter_notify_event(self, widget, event): if self.drag_move: return childs = self.body_box.get_children() if len(childs) > 0: child = childs[0] if child != self.control_box: self.draw_animation = True container_remove_all(self.body_box) self.active_draw_func = self.draw_info self.target_draw_func = self.draw_control self.start_animation(self.control_box) else: self.draw_animation = True container_remove_all(self.body_box) self.active_draw_func = self.draw_info self.target_draw_func = self.draw_control self.start_animation(self.control_box) def on_leave_notify_event(self, widget, event): if self.drag_move: return if not self.is_in_window(): self.draw_animation = True container_remove_all(self.body_box) self.active_draw_func = self.draw_control self.target_draw_func = self.draw_info self.start_animation(self.info_box) def on_button_press_event(self, widget, event): if event.button == 1: self.old_x, self.old_y = widget.get_position() self.mouse_x, self.mouse_y = event.x_root, event.y_root self.drag_move = True def on_motion_notify_event(self, widget, event): if self.drag_move: x = int(self.old_x + (event.x_root - self.mouse_x)) y = int(self.old_y + (event.y_root - self.mouse_y)) widget.move(*self.adjust_move_coordinate(widget, x, y)) def on_button_release_event(self, widget, event): self.drag_move = False def toggle_visible(self, bring_to_front=False): if self.get_property("visible"): if self.is_active(): if not bring_to_front: self.hide_all() else: self.present() else: self.show_from_tray() def toggle_window(self): if self.get_property("visible"): self.hide_to_tray() else: self.show_from_tray() def show_from_tray(self): if config.get("mini", "x") != "-1": self.move(int(config.get("mini", "x")), int(config.get("mini", "y"))) self.show_all() def hide_to_tray(self): self.hide_all() def get_widget_pixbuf(self, widget=None): if widget is None: widget = self drawable = widget.window x, y, width, height = widget.allocation pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) pixbuf.get_from_drawable(drawable, drawable.get_colormap(), x, y, 0, 0, width, height) return pixbuf def shape_mini_frame(self, widget, event): pass def draw_info(self, cr, allocation, alpha): rect = gtk.gdk.Rectangle(allocation.x, allocation.y, allocation.width, allocation.height) cr.push_group() # Draw logo. rect.x += self.logo_padding_left logo_pixbuf = app_theme.get_pixbuf("mini/logo.png").get_pixbuf() icon_y = rect.y + (rect.height - logo_pixbuf.get_height()) / 2 draw_pixbuf(cr, logo_pixbuf, rect.x, icon_y) # Draw playinfo. left_width = logo_pixbuf.get_width() + self.logo_padding_right rect.x += left_width rect.width -= left_width * 2 self.playinfo.draw_content(cr, rect) # set source to paint with alpha. cr.pop_group_to_source() cr.paint_with_alpha(alpha) def draw_control(self, cr, allocation, alpha): rect = gtk.gdk.Rectangle(allocation.x, allocation.y, allocation.width, allocation.height) cr.push_group() # Draw lyrics button rect.x += self.lyrics_padding_left enable_lyrics = config.getboolean('lyrics', 'status') if enable_lyrics: lyrics_pixbuf = app_theme.get_pixbuf('mini/lyrics_active_normal.png').get_pixbuf() else: lyrics_pixbuf = app_theme.get_pixbuf('mini/lyrics_inactive_normal.png').get_pixbuf() icon_y = rect.y + (rect.height - lyrics_pixbuf.get_height()) / 2 draw_pixbuf(cr, lyrics_pixbuf, rect.x, icon_y) # Draw previous button. rect.x += lyrics_pixbuf.get_width() + self.lyrics_padding_right previous_pixbuf = app_theme.get_pixbuf('mini/previous_normal.png').get_pixbuf() icon_y = rect.y + (rect.height - previous_pixbuf.get_height()) / 2 draw_pixbuf(cr, previous_pixbuf, rect.x, icon_y) # Draw playpause button. rect.x += previous_pixbuf.get_width() is_played = config.getboolean('player', 'play') if is_played: playpause_pixbuf = app_theme.get_pixbuf('mini/pause_normal.png').get_pixbuf() else: playpause_pixbuf = app_theme.get_pixbuf('mini/play_normal.png').get_pixbuf() icon_y = rect.y + (rect.height - playpause_pixbuf.get_height()) / 2 draw_pixbuf(cr, playpause_pixbuf, rect.x, icon_y) # Draw next button. rect.x += playpause_pixbuf.get_width() next_pixbuf = app_theme.get_pixbuf('mini/next_normal.png').get_pixbuf() icon_y = rect.y + (rect.height - next_pixbuf.get_height()) / 2 draw_pixbuf(cr, next_pixbuf, rect.x, icon_y) # Draw volume button. (v_w, v_h,) = self.volume_slider.volume_button.get_size() v_y = rect.y + (rect.height - v_h) / 2 rect.x += self.volume_padding_left + next_pixbuf.get_width() volume_button_rect = gtk.gdk.Rectangle(rect.x, v_y, v_w, v_h) self.volume_slider.volume_button.draw_volume(cr, volume_button_rect) # Draw event box, draw close button. close_pixbuf = ui_theme.get_pixbuf('button/window_close_normal.png').get_pixbuf() event_box_x = rect.width - close_pixbuf.get_width() + 4 draw_pixbuf(cr, close_pixbuf, event_box_x, rect.y) # Draw min button. min_pixbuf = ui_theme.get_pixbuf('button/window_min_normal.png').get_pixbuf() event_box_x -= min_pixbuf.get_width() draw_pixbuf(cr, min_pixbuf, event_box_x, rect.y) # draw quell button. quell_pixbuf = app_theme.get_pixbuf('mode/quell_normal.png').get_pixbuf() event_box_x -= quell_pixbuf.get_width() draw_pixbuf(cr, quell_pixbuf, event_box_x, rect.y) cr.pop_group_to_source() cr.paint_with_alpha(alpha) def expose_mini_frame(self, widget, event): cr = widget.window.cairo_create() rect = widget.allocation cr.set_source_rgba(1,1,1, 0.8) cr.rectangle(rect.x, rect.y, rect.width, rect.height) cr.fill() if self.draw_animation: if self.active_draw_func: self.active_draw_func(cr, rect, self.active_alpha) if self.target_draw_func: self.target_draw_func(cr, rect, self.target_alpha) def start_animation(self, widget): if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("update", self.update_animation) self.timeline.connect("completed", lambda source: self.completed_animation(source, widget)) self.timeline.run() def update_animation(self, source, status): self.active_alpha = 1.0 - status self.target_alpha = status self.queue_draw() def completed_animation(self, source, widget): self.draw_animation = False self.active_alpha = 1.0 self.target_alpha = 0.0 self.in_animation = False self.queue_draw() switch_tab(self.body_box, widget) def sync_volume(self): self.volume_slider.load_volume_config()
class CoverButton(gtk.Button): def __init__(self): super(CoverButton, self).__init__() self.cover_side_pixbuf = app_theme.get_pixbuf( "cover/side.png").get_pixbuf() self.set_size_request(self.cover_side_pixbuf.get_width(), self.cover_side_pixbuf.get_height()) self.webcast_dpixbuf = app_theme.get_pixbuf("cover/webcast.png") self.local_dpixbuf = app_theme.get_pixbuf("cover/default_cover.png") self.connect("expose-event", self.expose_button_cb) Dispatcher.connect("album-changed", self.on_album_changed) self.current_song = None self.next_cover_to_download = None self.condition = threading.Condition() self.thread = threading.Thread(target=self.func_thread) self.thread.setDaemon(True) self.thread.start() # animation params. self.active_alpha = 1.0 self.target_alpha = 0.0 self.in_animation = False self.animation_time = 1500 self.active_pixbuf = self.get_default_pixbuf() self.target_pixbuf = None self.is_default = True def start_animation(self, target_pixbuf, is_default=False): self.is_default = False self.target_pixbuf = target_pixbuf if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("update", self.update_animation) self.timeline.connect( "completed", lambda source: self.completed_animation( source, target_pixbuf, is_default)) self.timeline.run() def update_animation(self, source, status): self.active_alpha = 1.0 - status self.target_alpha = status self.queue_draw() def completed_animation(self, source, target_pixbuf, is_default): self.active_pixbuf = target_pixbuf self.active_alpha = 1.0 self.target_alpha = 0.0 self.in_animation = False self.is_default = is_default self.queue_draw() def expose_button_cb(self, widget, event): cr = widget.window.cairo_create() rect = widget.allocation # Draw cover side. if self.is_default: self.active_pixbuf = self.get_default_pixbuf(self.current_song) draw_pixbuf(cr, self.cover_side_pixbuf, rect.x, rect.y) draw_pixbuf(cr, self.active_pixbuf, rect.x + 3, rect.y + 3, self.active_alpha) if self.target_pixbuf: draw_pixbuf(cr, self.target_pixbuf, rect.x + 3, rect.y + 3, self.target_alpha) return True def func_thread(self): while True: self.condition.acquire() while not self.next_cover_to_download: self.condition.wait() next_cover_to_download = self.next_cover_to_download self.next_cover_to_download = None self.condition.release() self.set_current_cover(True, next_cover_to_download) def update_default_cover(self, song): self.start_animation(self.get_default_pixbuf(song), is_default=True) def get_default_pixbuf(self, song=None): if not song: song = Player.song if not song: pixbuf = self.local_dpixbuf.get_pixbuf() return utils.get_optimum_pixbuf(pixbuf, COVER_SIZE["x"], COVER_SIZE["y"]) if song.get_type() == "webcast": pixbuf = self.webcast_dpixbuf.get_pixbuf() else: pixbuf = self.local_dpixbuf.get_pixbuf() return utils.get_optimum_pixbuf(pixbuf, COVER_SIZE["x"], COVER_SIZE["y"]) def init_default_cover(self): pixbuf = self.get_default_pixbuf() self.start_animation(pixbuf, is_default=True) def on_album_changed(self, obj, song): if song == self.current_song: self.update_cover(None, song) def update_cover(self, widget, song): self.current_song = song if self.current_song is not None: if not self.set_current_cover(False): self.condition.acquire() self.next_cover_to_download = self.current_song self.condition.notify() self.condition.release() def set_current_cover(self, try_web=True, force_song=None): if not force_song: force_song = self.current_song filename = CoverManager.get_cover(force_song, try_web) if Player.song != force_song: return False if not filename: if not try_web: if force_song.get_type() == "webcast": pixbuf = utils.get_optimum_pixbuf( self.webcast_dpixbuf.get_pixbuf(), COVER_SIZE["x"], COVER_SIZE["y"]) self.start_animation(pixbuf, is_default=True) return True else: pixbuf = utils.get_optimum_pixbuf( self.local_dpixbuf.get_pixbuf(), COVER_SIZE["x"], COVER_SIZE["y"]) self.start_animation(pixbuf, is_default=True) return False return False else: try: pixbuf = get_optimum_pixbuf_from_file(filename, COVER_SIZE["x"], COVER_SIZE["y"]) except gobject.GError: return False else: self.start_animation(pixbuf, is_default=False) return True
class SongNotify(gtk.Window): def __init__(self, song=None): gtk.Window.__init__(self, # shadow_visible=False, # shadow_radius=0, # shape_frame_function=self.shape_panel_frame, # expose_frame_function=self.expose_panel_frame, # window_type=gtk.WINDOW_POPUP, ) self.set_can_focus(False) self.set_accept_focus(False) self.set_keep_above(True) self.set_skip_taskbar_hint(True) self.set_skip_pager_hint(True) self.set_decorated(False) self.set_app_paintable(True) self.set_colormap(gtk.gdk.Screen().get_rgba_colormap() or gtk.gdk.Screen().get_rga_colormap()) self.connect("expose-event", self.on_panel_expose_event) # self.connect("size-allocate", self.on_size_allocate) self.song = song self.title_text_size = 9 self.other_text_size = 8 self.text_padding_y = 5 self.other_text_padding_y = 2 self.text_padding_x = 5 self.cover_padding_x = 7 self.cover_up_offset = 12 self.cover_bottom_offset = 10 self.other_up_offset = 8 self.line_height = 15 self.cover_width = self.cover_height = BROWSER_COVER_SIZE["x"] self.default_width = 222 self.default_height = 118 self.set_size_request(self.default_width, self.default_height) self.other_text_color = app_theme.get_color("labelText").get_color() # animation params. self.width_percent = 1.0 self.in_animation = False self.animation_time = 500 self.animation_timeout_id = None self.last_x = None self.last_y = None self.move_y = None def shape_panel_frame(self, widget, event): pass def expose_panel_frame(self, widget, event): pass def on_size_allocate(self, widget, rect): x, y, w, h = rect.x, rect.y, rect.width, rect.height bitmap = gtk.gdk.Pixmap(None, w, h, 1) cr = bitmap.cairo_create() cr.set_source_rgb(0.0, 0.0, 0.0) cr.set_operator(cairo.OPERATOR_CLEAR) cr.paint() cr.set_operator(cairo.OPERATOR_OVER) with cairo_disable_antialias(cr): draw_round_rectangle(cr, rect.x, rect.y, rect.width, rect.height, 4) cr.fill() widget.shape_combine_mask(bitmap, 0, 0) def on_panel_expose_event(self, widget, event): cr = widget.window.cairo_create() rect = widget.allocation # Clear color to transparent window. if self.is_composited(): cr.rectangle(*rect) cr.set_source_rgba(1, 1, 1, 0.95) cr.set_operator(cairo.OPERATOR_SOURCE) cr.paint() else: cr.rectangle(rect.x, rect.y, rect.width, rect.height) cr.set_operator(cairo.OPERATOR_SOURCE) cr.set_source_rgb(0.9, 0.9, 0.9) cr.fill() cr = widget.window.cairo_create() rect = widget.allocation with cairo_disable_antialias(cr): cr.rectangle(rect.x + 1, rect.y + 1, rect.width - 1, rect.height - 1) cr.set_line_width(1) cr.set_source_rgb(*color_hex_to_cairo("#D6D6D6")) cr.stroke() if not self.song: return True dash_y = rect.y + self.cover_height + self.cover_up_offset + self.cover_bottom_offset + 1 dash_x = rect.x dash_width = rect.width cover_pixbuf = self.get_song_cover() rect.x += self.cover_padding_x rect.width -= self.cover_padding_x * 2 # rect.y += (rect.height - self.cover_height) / 2 rect.y += self.cover_up_offset draw_pixbuf(cr, cover_pixbuf, rect.x, rect.y) rect.x += self.cover_width + self.text_padding_x rect.width -= (self.cover_width + self.text_padding_x) title = utils.xmlescape(self.song.get_str("title")) artist = utils.xmlescape(self.song.get_str("artist")) album = utils.xmlescape(self.song.get_str("album")) _width, _height = get_content_size(title, text_size=self.title_text_size) if not artist and not album: _height = self.cover_height draw_text(cr, title, rect.x, rect.y, rect.width, _height, text_color=self.other_text_color, text_size=self.title_text_size) if artist: rect.y += _height + self.text_padding_y _width, _height = get_content_size(artist, text_size=self.other_text_size) draw_text(cr, artist, rect.x, rect.y, rect.width, _height, text_size=self.other_text_size, text_color=self.other_text_color) if album: rect.y += _height + self.text_padding_y _width, _height = get_content_size(artist, text_size=self.other_text_size) draw_text(cr, album, rect.x, rect.y, rect.width, _height, text_size=self.other_text_size, text_color=self.other_text_color) # draw dash with cairo_disable_antialias(cr): cr.set_source_rgb(*color_hex_to_cairo("#D6D6D6")) cr.set_line_width(1) cr.move_to(dash_x, dash_y) cr.rel_line_to(dash_width, 0) # cr.set_dash([2.0, 2.0]) cr.stroke() other_width = (dash_width - self.cover_padding_x * 2) / 2 - self.cover_padding_x / 2 other_left_x = dash_x + self.cover_padding_x other_right_x = dash_x + dash_width / 2 + self.cover_padding_x / 2 # draw size and format other_y = dash_y + self.other_up_offset song_size = "%s: %s" % (_("Size"), utils.xmlescape(self.song.get_str("#size"))) song_format = "%s: %s" % (_("Type"), utils.get_song_attr(self.song)) _width, _height = get_content_size(song_size, text_size=self.other_text_size) draw_text(cr, song_size, other_left_x, other_y, other_width, _height, text_size=self.other_text_size, text_color=self.other_text_color ) draw_text(cr, song_format, other_right_x, other_y, other_width, _height, text_size=self.other_text_size, text_color=self.other_text_color ) # draw playcount and duration. other_y += _height + self.other_text_padding_y playcount = self.song.get_str("#playcount") if playcount == "Never" or not playcount: playcount = "0" song_playcount = "%s: %s" % (_("Playcount"), playcount) duration = "%s: %s" % (_("Duration"), self.song.get_str("#duration")) draw_text(cr, song_playcount, other_left_x, other_y, other_width, _height, text_size=self.other_text_size, text_color=self.other_text_color ) draw_text(cr, duration, other_right_x, other_y, other_width, _height, text_size=self.other_text_size, text_color=self.other_text_color ) return True def get_song_cover(self): return CoverManager.get_pixbuf_from_song(self.song, BROWSER_COVER_SIZE["x"], BROWSER_COVER_SIZE["y"], try_web=False, optimum=True) def show(self, x, y): self.show_all() if self.last_x is None or self.last_x != x: self.last_x = x self.last_y = y self.move(x, y) else: self.start_animation(x, y) def start_animation(self, x, y): if not self.in_animation: self.in_animation = False try: self.timeline.stop() except: pass self.timeline = Timeline(self.animation_time, CURVE_SINE) self.timeline.connect("stop", self.stop_animation) self.timeline.connect("update", self.update_animation, x, y) self.timeline.connect("completed", self.completed_animation, x, y) self.timeline.run() def stop_animation(self, source): if self.move_y is not None: self.last_y = self.move_y def update_animation(self, source, status, x, y): height = y - self.last_y new_height = status * height self.move_y = int(self.last_y + new_height) self.move(x, self.move_y) def completed_animation(self, source, x, y): self.last_x = x self.last_y = y def hide_notify(self): self.last_x = None self.hide_all() def update_song(self, song): self.song = song self.queue_draw()