def enter_notify_menu_item(self, widget): """ Internal callback for `enter-notify-event` signal. """ # Reset all items in same menu. for menu_item in self.get_menu_items_callback(): if menu_item != self and menu_item.submenu_active: menu_item.submenu_active = False menu_item.item_box.queue_draw() (item_icons, item_content, item_node) = self.item[0:3] if isinstance(item_node, Menu): if widget.state != gtk.STATE_INSENSITIVE: menu_window = self.item_box.get_toplevel() (menu_window_x, menu_window_y) = get_widget_root_coordinate(menu_window, WIDGET_POS_RIGHT_CENTER) (item_x, item_y) = get_widget_root_coordinate(self.item_box) self.show_submenu_callback( item_node, ( menu_window_x - menu_window.shadow_radius, item_y - widget.get_allocation().height - menu_window.shadow_radius, ), self.item_box.allocation.height + menu_window.shadow_radius, ) self.submenu_active = True else: self.hide_submenu_callback()
def enter_notify_menu_item(self, widget): ''' Internal callback for `enter-notify-event` signal. ''' # Reset all items in same menu. for menu_item in self.get_menu_items_callback(): if menu_item != self and menu_item.submenu_active: menu_item.submenu_active = False menu_item.item_box.queue_draw() (item_icons, item_content, item_node) = self.item[0:3] if isinstance(item_node, Menu): if widget.state != gtk.STATE_INSENSITIVE: menu_window = self.item_box.get_toplevel() (menu_window_x, menu_window_y) = get_widget_root_coordinate( menu_window, WIDGET_POS_RIGHT_CENTER) (item_x, item_y) = get_widget_root_coordinate(self.item_box) self.show_submenu_callback( item_node, (menu_window_x - menu_window.shadow_radius, item_y - widget.get_allocation().height - menu_window.shadow_radius), self.item_box.allocation.height + menu_window.shadow_radius) self.submenu_active = True else: self.hide_submenu_callback()
def click_drop_button(self, *args): '''Click drop button.''' if self.droplist.get_visible(): self.droplist.hide() else: (align_x, align_y) = get_widget_root_coordinate(self.align, WIDGET_POS_BOTTOM_LEFT) self.droplist.show( (align_x - 1, align_y - 1), (0, -self.height + 1)) self.droplist.item_select_index = self.select_index self.droplist.active_item() self.droplist.scroll_page_to_select_item() self.queue_draw()
class Menu(Window): ''' Menu. @undocumented: realize_menu @undocumented: hide_menu @undocumented: get_menu_item_at_coordinate @undocumented: get_menu_items @undocumented: init_menu @undocumented: get_submenus @undocumented: get_menu_icon_info @undocumented: adjust_menu_position @undocumented: show_submenu @undocumented: hide_submenu @undocumented: get_root_menu ''' def __init__(self, items, is_root_menu=False, select_scale=False, x_align=ALIGN_START, y_align=ALIGN_START, font_size=DEFAULT_FONT_SIZE, padding_x=3, padding_y=3, item_padding_x=6, item_padding_y=3, shadow_visible=True, menu_min_width=130, menu_item_select_color=None): ''' Initialize Menu class. @param items: A list of item, item format: ((item_normal_dpixbuf, item_hover_dpixbuf, item_disable_dpixbuf), item_name, item_node). @param is_root_menu: Default is False for submenu, you should set it as True if you build root menu. @param select_scale: Default is False, it will use parant's width if it set True. @param x_align: Horizontal alignment value. @param y_align: Vertical alignment value. @param font_size: Menu font size, default is DEFAULT_FONT_SIZE @param padding_x: Horizontal padding value, default is 3 pixel. @param padding_y: Vertical padding value, default is 3 pixel. @param item_padding_x: Horizontal item padding value, default is 6 pixel. @param item_padding_y: Vertical item padding value, default is 3 pixel. @param shadow_visible: Whether show window shadow, default is True. @param menu_min_width: Minimum width of menu. ''' global root_menus # Init. Window.__init__(self, shadow_visible=shadow_visible, window_type=gtk.WINDOW_POPUP, shadow_radius=6) self.set_can_focus(True) # can focus to response key-press signal self.draw_mask = self.draw_menu_mask self.is_root_menu = is_root_menu self.select_scale = select_scale self.x_align = x_align self.y_align = y_align self.submenu = None self.root_menu = None self.offset_x = 0 # use for handle extreme situaiton, such as, menu show at side of screen self.offset_y = 0 self.padding_x = padding_x self.padding_y = padding_y self.item_padding_x = item_padding_x self.item_padding_y = item_padding_y self.menu_min_width = menu_min_width self.menu_item_select_color = menu_item_select_color # Init menu window. self.set_skip_pager_hint(True) self.set_skip_taskbar_hint(True) self.set_keep_above(True) # Add menu item. self.item_box = gtk.VBox() self.item_align = gtk.Alignment() self.item_align.set_padding(padding_y, padding_y, padding_x, padding_x) self.item_align.add(self.item_box) self.window_frame.add(self.item_align) self.menu_items = [] self.font_size = font_size if items: self.add_menu_items(items) self.connect("show", self.init_menu) self.connect("hide", self.hide_menu) self.connect("realize", self.realize_menu) def set_mutual_icons(self, index, icons): # deepin-media-player useing. # other no use. for item in self.menu_items: item.set_item_icons(None) # self.menu_items[index].set_item_icons(icons) def add_menu_items(self, items): (icon_width, icon_height, have_submenu, submenu_width, submenu_height) = self.get_menu_icon_info(items) for item in items: menu_item = MenuItem(item, self.font_size, self.select_scale, self.show_submenu, self.hide_submenu, self.get_root_menu, self.get_menu_items, icon_width, icon_height, have_submenu, submenu_width, submenu_height, self.padding_x, self.padding_y, self.item_padding_x, self.item_padding_y, self.menu_min_width, self.menu_item_select_color) self.menu_items.append(menu_item) self.item_box.pack_start(menu_item.item_box, False, False) def clear_menus(self): self.menu_items = [] self.item_align.remove(self.item_box) self.item_box = gtk.VBox() self.item_align.add(self.item_box) self.resize(1, 1) def hide_menu(self, widget): ''' Internal callback for `hide` signal. ''' # Avoid menu (popup window) show at (0, 0) when next show. self.move(-1000000, -1000000) def realize_menu(self, widget): ''' Internal callback for `realize` signal. ''' # Avoid menu (popup window) show at (0, 0) first. self.move(-1000000, -1000000) # Never draw background. self.window.set_back_pixmap(None, False) def draw_menu_mask(self, cr, x, y, w, h): ''' Draw mask interface. @param cr: Cairo context. @param x: X coordinate of draw area. @param y: Y coordinate of draw area. @param w: Width of draw area. @param h: Height of draw area. ''' # Draw background. cr.set_source_rgba(*alpha_color_hex_to_cairo( ui_theme.get_alpha_color("menu_mask").get_color_info())) cr.rectangle(x, y, w, h) cr.fill() # Draw left side. draw_hlinear(cr, x + 1, y + 1, 16 + self.padding_x + self.padding_x * 2, h - 2, ui_theme.get_shadow_color("menu_side").get_color_info()) def get_menu_item_at_coordinate(self, (x, y)): ''' Internal function to get menu item at coordinate, return None if haven't any menu item at given coordinate. ''' match_menu_item = None for menu_item in self.menu_items: item_rect = menu_item.item_box.get_allocation() (item_x, item_y) = get_widget_root_coordinate(menu_item.item_box, WIDGET_POS_TOP_LEFT) if is_in_rect((x, y), (item_x, item_y, item_rect.width, item_rect.height)): match_menu_item = menu_item break return match_menu_item
class Droplist(gtk.Window): ''' Droplist. @undocumented: create_item @undocumented: expose_item_align @undocumented: droplist_key_press @undocumented: droplist_key_release @undocumented: expose_droplist_frame @undocumented: init_droplist @undocumented: adjust_droplist_position ''' __gsignals__ = { "item-selected": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ( str, gobject.TYPE_PYOBJECT, int, )), "key-release": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ( str, gobject.TYPE_PYOBJECT, int, )), } def __init__(self, items, x_align=ALIGN_START, y_align=ALIGN_START, font_size=DEFAULT_FONT_SIZE, opacity=1.0, padding_x=0, padding_y=0, item_padding_left=6, item_padding_right=32, item_padding_y=3, max_width=None, fixed_width=None, max_height=None): ''' Initialize Droplist class. @param items: A list of item, item format: (item_content, item_value). @param x_align: Horticultural alignment. @param y_align: Vertical alignment. @param font_size: Font size of droplist, default is DEFAULT_FONT_SIZE @param opacity: Opacity of droplist window, default is 1.0. @param padding_x: Padding x, default is 0. @param padding_y: Padding y, default is 0. @param item_padding_left: Padding at left of item, default is 6. @param item_padding_right: Padding at right of item, default is 32. @param item_padding_y: Padding of item vertically, default is 3. @param max_width: Maximum width of droplist, default is None. @param fixed_width: Fixed width of droplist, default is None. @param max_height: Maximum height of droplist, default is None. ''' # Init. gtk.Window.__init__(self, gtk.WINDOW_POPUP) self.items = items self.set_can_focus(True) # can focus to response key-press signal self.add_events(gtk.gdk.ALL_EVENTS_MASK) self.set_decorated(False) self.set_colormap(gtk.gdk.Screen().get_rgba_colormap()) global root_droplists self.font_size = font_size self.x_align = x_align self.y_align = y_align self.subdroplist = None self.root_droplist = None self.offset_x = 0 # use for handle extreme situaiton, such as, droplist show at side of screen self.offset_y = 0 self.padding_x = padding_x self.padding_y = padding_y self.item_padding_left = item_padding_left self.item_padding_right = item_padding_right self.item_padding_y = item_padding_y self.max_width = max_width self.max_height = max_height self.fixed_width = fixed_width self.item_select_index = 0 # Init droplist window. self.set_opacity(opacity) self.set_skip_taskbar_hint(True) self.set_keep_above(True) self.connect_after("show", self.init_droplist) self.droplist_frame = gtk.Alignment() self.droplist_frame.set(0.5, 0.5, 1.0, 1.0) self.droplist_frame.set_padding(1, 1, 1, 1) # Add droplist item. self.item_box = gtk.VBox() self.item_align = gtk.Alignment() self.item_align.set_padding(padding_y, padding_y, padding_x, padding_x) self.item_align.add(self.item_box) self.item_scrolled_window = DroplistScrolledWindow(0, 0) self.item_scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self.add(self.droplist_frame) self.droplist_frame.add(self.item_scrolled_window) self.item_scrolled_window.add_child(self.item_align) self.droplist_items = [] if items: for (index, item) in enumerate(items): droplist_item = self.create_item(index, item) self.droplist_items.append(droplist_item) self.item_box.pack_start(droplist_item.item_box, False, False) self.connect_after("show", lambda w: self.adjust_droplist_position()) self.droplist_frame.connect("expose-event", self.expose_droplist_frame) self.item_align.connect("expose-event", self.expose_item_align) self.connect("key-press-event", self.droplist_key_press) self.connect("key-release-event", self.droplist_key_release) self.keymap = { "Home": self.select_first_item, "End": self.select_last_item, "Page_Up": self.scroll_page_up, "Page_Down": self.scroll_page_down, "Return": self.press_select_item, "Up": self.select_prev_item, "Down": self.select_next_item, "Escape": self.hide } self.select_first_item() self.grab_focus() def create_item(self, index, item): return DroplistItem(self, index, item, self.font_size, self.padding_x, self.padding_y, self.item_padding_left, self.item_padding_right, self.item_padding_y, self.max_width, self.fixed_width) def get_droplist_width(self): ''' Get droplist width. @return: Return width of droplist. ''' if self.fixed_width != None: return self.padding_x * 2 + self.fixed_width else: item_content_width = max( map( lambda item: get_content_size(item.item[0], self.font_size) [0], filter(lambda item: isinstance(item.item_box, gtk.Button), self.droplist_items))) if self.max_width != None: return self.padding_x * 2 + min( self.max_width, self.item_padding_left + self.item_padding_right + int(item_content_width)) else: return self.padding_x * 2 + self.item_padding_left + self.item_padding_right + int( item_content_width) def expose_item_align(self, widget, event): ''' Internal function to handle `expose-event` signal. @param widget: Droplist widget. @param event: Expose event. ''' # Init. cr = widget.window.cairo_create() rect = widget.allocation x, y, w, h = rect.x, rect.y, rect.width, rect.height # Draw background. cr.set_source_rgba(*alpha_color_hex_to_cairo( ui_theme.get_alpha_color("droplist_mask").get_color_info())) cr.rectangle(x, y, w, h) cr.fill() def get_first_index(self): ''' Get index of first item. @return: Return index of first item, or return None if haven't item in droplist. ''' item_indexs = filter( lambda (index, item): isinstance(item.item_box, gtk.Button), enumerate(self.droplist_items)) if len(item_indexs) > 0: return item_indexs[0][0] else: return None def get_last_index(self): ''' Get index of last item. @return: Return index of last item, or return None if haven't item in droplist. ''' item_indexs = filter( lambda (index, item): isinstance(item.item_box, gtk.Button), enumerate(self.droplist_items)) if len(item_indexs) > 0: return item_indexs[-1][0] else: return None def get_prev_index(self): ''' Get index of previous item. @return: Return index of previous item, or return None if haven't item in droplist. ''' item_indexs = filter( lambda (index, item): isinstance(item.item_box, gtk.Button), enumerate(self.droplist_items)) if len(item_indexs) > 0: index_list = map(lambda (index, item): index, item_indexs) if self.item_select_index in index_list: current_index = index_list.index(self.item_select_index) if current_index > 0: return index_list[current_index - 1] else: return self.item_select_index else: return None else: return None def get_next_index(self): ''' Get index of next item. @return: Return index of next item, or return None if haven't item in droplist. ''' item_indexs = filter( lambda (index, item): isinstance(item.item_box, gtk.Button), enumerate(self.droplist_items)) if len(item_indexs) > 0: index_list = map(lambda (index, item): index, item_indexs) if self.item_select_index in index_list: current_index = index_list.index(self.item_select_index) if current_index < len(index_list) - 1: return index_list[current_index + 1] else: return self.item_select_index else: return None else: return None def get_select_item_rect(self, item_index=None): ''' Get item rectangle with given index. @param item_index: If item_index is None, use select index. @return: Return (x, y, w, h) rectangle for match item. ''' if item_index == None: item_index = self.item_select_index item_offset_y = sum( map(lambda item: item.item_box_height, self.droplist_items)[0:item_index]) item_rect = self.droplist_items[item_index].item_box.get_allocation() return (0, item_offset_y, item_rect.width, item_rect.height) def active_item(self, item_index=None): ''' Select item with given index. @param item_index: If item_index is None, use select index. ''' global droplist_active_item if item_index == None: item_index = self.item_select_index if droplist_active_item: droplist_active_item.item_box.set_state(gtk.STATE_NORMAL) item = self.droplist_items[item_index] item.item_box.set_state(gtk.STATE_PRELIGHT) droplist_active_item = item def select_first_item(self): ''' Select first item. ''' if len(self.droplist_items) > 0: first_index = self.get_first_index() if first_index != None: self.item_select_index = first_index self.active_item() # Scroll to top. vadjust = self.item_scrolled_window.get_vadjustment() vadjust.set_value(vadjust.get_lower()) def select_last_item(self): ''' Select last item. ''' if len(self.droplist_items) > 0: last_index = self.get_last_index() if last_index != None: self.item_select_index = last_index self.active_item() # Scroll to bottom. vadjust = self.item_scrolled_window.get_vadjustment() vadjust.set_value(vadjust.get_upper() - vadjust.get_page_size()) def select_prev_item(self): ''' Select previous item. ''' if len(self.droplist_items) > 0: prev_index = self.get_prev_index() if prev_index != None: global droplist_active_item if droplist_active_item: if self.item_select_index > 0: self.item_select_index = prev_index self.active_item() # Make item in visible area. (item_x, item_y, item_width, item_height) = self.get_select_item_rect() vadjust = self.item_scrolled_window.get_vadjustment() if item_y < vadjust.get_value(): vadjust.set_value(item_y) else: self.select_first_item() def select_next_item(self): ''' Select next item. ''' if len(self.droplist_items) > 0: next_index = self.get_next_index() if next_index != None: global droplist_active_item if droplist_active_item: if self.item_select_index < len(self.droplist_items) - 1: self.item_select_index = next_index self.active_item() # Make item in visible area. (item_x, item_y, item_width, item_height) = self.get_select_item_rect() vadjust = self.item_scrolled_window.get_vadjustment() if self.padding_y + item_y + item_height > vadjust.get_value( ) + vadjust.get_page_size(): vadjust.set_value(self.padding_y * 2 + item_y + item_height - vadjust.get_page_size()) else: self.select_first_item() def scroll_page_to_select_item(self): ''' Scroll page to select item. ''' (item_x, item_y, item_width, item_height) = self.get_select_item_rect() vadjust = self.item_scrolled_window.get_vadjustment() vadjust.set_value( min(max(vadjust.get_lower(), item_y - self.padding_y * 2), vadjust.get_upper() - vadjust.get_page_size())) def scroll_page_up(self): ''' Scroll page up. ''' if len(self.droplist_items) > 0: # Scroll page up. vadjust = self.item_scrolled_window.get_vadjustment() vadjust.set_value( max(vadjust.get_lower(), vadjust.get_value() - vadjust.get_page_size())) # Select nearest item. item_infos = map( lambda (index, item): (index, self.get_select_item_rect(index)), enumerate(self.droplist_items)) for (index, (item_x, item_y, item_width, item_height)) in item_infos: if item_y + self.padding_y > vadjust.get_value(): self.item_select_index = index self.active_item() break def scroll_page_down(self): ''' Scroll page down. ''' if len(self.droplist_items) > 0: # Scroll page up. vadjust = self.item_scrolled_window.get_vadjustment() vadjust.set_value( min(vadjust.get_upper() - vadjust.get_page_size(), vadjust.get_value() + vadjust.get_page_size())) # Select nearest item. item_infos = map( lambda (index, item): (index, self.get_select_item_rect(index)), enumerate(self.droplist_items)) item_infos.reverse() for (index, (item_x, item_y, item_width, item_height)) in item_infos: if item_y + item_height + self.padding_y < vadjust.get_value( ) + vadjust.get_page_size(): self.item_select_index = index self.active_item() break def press_select_item(self): ''' Press select item. ''' if len(self.droplist_items) > 0: if 0 <= self.item_select_index < len(self.droplist_items): self.droplist_items[ self.item_select_index].wrap_droplist_clicked_action() def droplist_key_press(self, widget, event): ''' Internal function for `key-press-event` signal. @param widget: Droplist widget. @param event: Key press event. ''' key_name = get_keyevent_name(event) if self.keymap.has_key(key_name): self.keymap[key_name]() return True def droplist_key_release(self, widget, event): ''' Internal function for `key-release-event` signal. @param widget: Droplist widget. @param event: Key release event. ''' self.emit("key-release", self.items[self.item_select_index][0], self.items[self.item_select_index][1], self.item_select_index) def expose_droplist_frame(self, widget, event): ''' Callback for `expose-event` siangl of droplist frame. @param widget: Droplist widget. @param event: Expose event. ''' cr = widget.window.cairo_create() rect = widget.allocation with cairo_disable_antialias(cr): cr.set_line_width(1) cr.set_source_rgb(*color_hex_to_cairo( ui_theme.get_color("droplist_frame").get_color())) cr.rectangle(rect.x, rect.y, rect.width, rect.height) cr.fill() def get_droplist_item_at_coordinate(self, (x, y)): ''' Get droplist item at coordinate, return None if haven't any droplist item at given coordinate. @param x: X coordiante. @param y: Y coordiante. @return: Return match item with given coordinate, return None if haven't any item match coordinate. ''' match_droplist_item = None item_heights = map(lambda item: item.item_box_height, self.droplist_items) item_offsets = map(lambda (index, height): sum(item_heights[0:index]), enumerate(item_heights)) vadjust = self.item_scrolled_window.get_vadjustment() (scrolled_window_x, scrolled_window_y) = get_widget_root_coordinate( self.item_scrolled_window, WIDGET_POS_TOP_LEFT) for (index, droplist_item) in enumerate(self.droplist_items): item_rect = droplist_item.item_box.get_allocation() if is_in_rect( (x, y), (scrolled_window_x, scrolled_window_y + item_offsets[index] - (vadjust.get_value() - vadjust.get_lower()), item_rect.width, item_rect.height)): match_droplist_item = droplist_item break return match_droplist_item