import clutter from themeMgr import ThemeMgr from ui_elements.Spinner import Spinner import pygtk import gtk import pango import copy class GlossMgr: theme_dir = "themes/" def __init__ (self, stage): self.stage = stage self.menus = [] self.menuHistory = [] #A list that contains menus in the order that they've been viewed self.currentMenu = None self.debug = False #Debug flag self.uiMsg = message(stage) self.themeMgr = ThemeMgr(self) #Set the menu interface element = self.themeMgr.search_docs("menu", "main").childNodes interface_name = self.themeMgr.find_child_value(element, "interface") self.set_menu_interface(interface_name) #Set a default menu transition self.set_menu_transition("slide") #The background is a bit messy due to the depth issues :( background = self.themeMgr.get_texture("background", None, None) background.set_size(self.stage.get_width(), self.stage.get_height()) background.show() self.stage.add(background) #Setup the selector bar self.selector_bar = MenuSelector(self) self.selector_bar.show_all() self.stage.add(self.selector_bar) self.currentPlugin = None def addMenu(self, newMenu): self.menus.append(newMenu) #If this is the first menu, make it the active one if self.currentMenu == None: self.currentMenu = newMenu self.menuHistory.append(newMenu) self.currentMenu.show_all() self.currentMenu.show() #This is a bit hacky, but we set the selector bar size based on the font size tmpLabel = clutter.Label() tmpLabel.set_text("AAA") tmpLabel.set_font_name(self.currentMenu.font) #Selector bar height is 20% larger than the labels self.selector_bar.set_height( int(tmpLabel.get_height()*self.selector_bar.height_percent) ) self.selector_bar.set_menu(self.currentMenu) tmpLabel = None def get_selector_bar(self): return self.selector_bar def get_stage(self): return self.stage def get_themeMgr(self): return self.themeMgr def on_key_press_event (self, stage, event): #Firstly check whether any messages are currently displayed if self.uiMsg.active: self.uiMsg.on_key_press_event(stage, event) return #Secondly, checking whether we are in the process of running a plugin (And that the key isn't escape) if (not self.currentPlugin == None) and (not event.keyval == clutter.keysyms.Escape): #If it is, simply pass the event details along to the plugin self.currentPlugin.on_key_press_event(stage, event) return None # If none of these things, the menu needs to do something if event.keyval == clutter.keysyms.Up: #Up button pressed self.currentMenu.input_queue.input(event) #self.currentMenu.selectPrevious() if event.keyval == clutter.keysyms.Down: #Down button pressed self.currentMenu.input_queue.input(event) #self.currentMenu.selectNext() if event.keyval == clutter.keysyms.q: self.stage.show_cursor() clutter.main_quit() if event.keyval == clutter.keysyms.Return: # Need to decide what action to take # Options are: # 1) Switch to a new menu # 2) Launch a module action = self.currentMenu.get_current_item().getAction() if action.__class__.__name__ == "Interface": # Check whether we're a pointing to an interface self.transition.do_transition(self.currentMenu, action) self.menuHistory.append(action) else: #We have a plugin and need to start it self.currentPlugin = action if action is None: self.display_msg("Error", "Could not start " + self.currentMenu.get_current_item().get_text()) else: #hide any unnecesary actors self.currentMenu.hide() #self.stage.remove(self.currentMenu.getItemGroup()) #And begin the plugin action.begin( self ) # This is tres bodge if event.keyval == clutter.keysyms.Escape: #If there's a plugin running then end it if not self.currentPlugin == None: #Plugins on_key_press_event should return true if the plugin is finishing if self.currentPlugin.on_key_press_event(stage, event): self.kill_plugin() #If there's no plugin running, go back one in the menu list (Providing we're not already at the first item. else: if len(self.menuHistory)>1: self.transition.do_transition(self.menuHistory.pop(), self.menuHistory[-1]) self.currentMenu = self.menuHistory[-1] #print event.hardware_keycode #Kills any currently running plugin/s def kill_plugin(self): if not self.currentPlugin is None: self.currentPlugin.stop() self.currentPlugin = None timeline_stop = clutter.Timeline(10,30) alpha = clutter.Alpha(timeline_stop, clutter.ramp_inc_func) self.stop_behaviour = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=alpha) self.currentMenu.set_opacity(0) self.currentMenu.show() self.stop_behaviour.apply(self.currentMenu) timeline_stop.start() def get_current_menu(self): return self.currentMenu def go_up_x_levels(self, num_levels): for i in range(1, num_levels): if len(self.menuHistory)>1: self.transition_fade_zoom(self.menuHistory.pop(), self.menuHistory[-1]) self.currentMenu = self.menuHistory[-1] def display_msg(self, title, msg): self.uiMsg.display_msg(title, msg) def set_menu_interface(self, interface_name): #Setup the menu interface interface_path = "interfaces/" + interface_name + "/" + interface_name self.interface = __import__(interface_path) def set_menu_transition(self, transition_name): #Setup the menu transition transition_path = "transitions/menus/" + transition_name self.transition = __import__(transition_path).Transition(self) def create_menu(self): return self.interface.Interface(self) class MenuSelector(clutter.Texture): x_offset = -50 height_percent = 1 position_0 = None blank = True def __init__ (self, glossMgr): clutter.Texture.__init__ (self) self.glossMgr = glossMgr glossMgr.themeMgr.get_texture("selector_bar", glossMgr.stage, self) self.set_position(0, self.get_y()) if not self.get_pixbuf() is None: self.x_offset = int(glossMgr.themeMgr.get_value("texture", "selector_bar", "position.x")) self.height_percent = float(glossMgr.themeMgr.get_value("texture", "selector_bar", "height_percent")) / float(100) self.blank = False else: self.position_0 = (0, 0) #This is a utility function that gets the coordinates of an that has been scaled def get_true_abs_position(self, selectee): #This whole clone label thing is a HORRIBLE hack but is there to compensate for the movement caused by scaling using GRAVITY_WEST #Essentially a clone of the selectee is made and scaled to its final position to retrieve the final abs_position coords cloneLabel = clutter.Label() cloneLabel.set_text(selectee.get_text()) cloneLabel.set_font_name(selectee.get_font_name()) (scale_x, scale_y) = selectee.get_scale() cloneLabel.set_anchor_point_from_gravity(clutter.GRAVITY_WEST) cloneLabel.set_scale(scale_x, scale_y) selectee.get_parent().add(cloneLabel) cloneLabel.set_position(selectee.get_x(), selectee.get_y()) #Now that all the cloning is done, find out what the scale is to become and set it on the clone scale = selectee.currentZoom cloneLabel.set_scale(scale, scale) return cloneLabel.get_abs_position() def selectItem(self, selectee, timeline): #Now get the end position of the clone (x, y) = self.get_true_abs_position(selectee) #print (x, y) #Do some minor adjustments for centering etc x = x + self.x_offset y = y - int( (self.get_height()-selectee.get_height())/2 ) #Yet another little hack, but this stores the position of the first element if self.position_0 is None: self.position_0 = (x, y) #Move the bar self.move_to(x, y, timeline) def move_to(self, x, y, timeline): knots = (\ (self.get_x(), self.get_y()),\ (x, y)\ ) self.alpha = clutter.Alpha(timeline, clutter.ramp_inc_func) self.behaviour = clutter.BehaviourPath(self.alpha, knots) self.behaviour.apply(self) def set_menu(self, menu): self.menu = menu def set_spinner(self, state): #Make sure we're not blank first if self.blank: return self.timeline = clutter.Timeline(25, 25) self.alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func) self.behaviour = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=self.alpha) if state: self.spinner = Spinner() height = self.get_height() - int(self.get_height() * 0.11) #Height has to be even otherwise spinner rotates on a slightly off axis if (height % 2) == 1: height = height -1 width = height self.spinner.set_size(width, height) (x, y) = self.get_abs_position() x = x + self.get_width() - int(self.get_width() * 0.13) y = y + int(self.get_height() * 0.03) self.spinner.set_position(x, y) self.spinner.set_opacity(0) self.spinner.show() self.glossMgr.get_stage().add(self.spinner) self.behaviour = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=self.alpha) self.spinner.start() else: self.behaviour = clutter.BehaviourOpacity(opacity_start=255, opacity_end=0, alpha=self.alpha) self.timeline.connect('completed', self.spinner_end_event) self.behaviour.apply(self.spinner) self.timeline.start() def spinner_end_event(self, data): self.glossMgr.get_stage().remove(self.spinner) self.spinner = None def get_x_offset(self): return self.x_offset class message(): theme_dir = "themes" font = "Lucida Grande " message_font_size = 30 detail_font_size = 22 def __init__(self, stage): self.stage = stage self.active = False self.msgQueue = [] self.backdrop = clutter.Rectangle() self.backdrop.set_color(clutter.color_parse('Black')) #self.backdrop.set_opacity(240) self.backdrop.set_width(self.stage.get_width()) self.backdrop.set_height(self.stage.get_height()) self.main_group = clutter.Group() pixbuf = gtk.gdk.pixbuf_new_from_file(self.theme_dir + "/splash_box.png") self.box = clutter.Texture() self.box.set_pixbuf(pixbuf) self.box.set_opacity(int(255 * 0.75)) self.box.set_height(int(self.stage.get_height()* 0.3)) self.main_group.add(self.box) self.message = clutter.Label() self.message.set_font_name(self.font + str(self.message_font_size)) self.message.set_color(clutter.color_parse('White')) pos_x = int(self.box.get_width() * 0.10) pos_y = int(self.box.get_height() * 0.10) self.message.set_position(pos_x, pos_y) width = int(self.box.get_width() * 0.80) #Width is 80% of the box, giving 10% gap each side self.message.set_width(width) self.message.set_ellipsize(pango.ELLIPSIZE_END) self.message.set_text("") self.main_group.add(self.message) self.detail = clutter.Label() self.detail.set_font_name(self.font + str(self.detail_font_size)) self.detail.set_color(clutter.color_parse('White')) pos_x = self.message.get_x() pos_y = self.message.get_y() + self.message.get_height() self.detail.set_position(pos_x, pos_y) height = self.box.get_height() - pos_y self.detail.set_height(height) #self.detail.set_ellipsize(pango.ELLIPSIZE_END) self.main_group.add(self.detail) self.detail.set_line_wrap(True) group_x = (self.stage.get_width()/2) - (self.box.get_width()/2) group_y = (self.stage.get_height()/2) - (self.box.get_height()/2) self.main_group.set_position(group_x, group_y) def display_msg(self, title, text): if self.active: self.msgQueue.append((title, text)) return self.message.set_text(title) self.detail.set_text(text) width = int(self.box.get_width() * 0.80) #Width is 80% of the box, giving 10% gap each side self.detail.set_width(width) self.message.set_width(width) self.main_group.set_opacity(0) self.backdrop.set_opacity(0) self.stage.add(self.backdrop) self.stage.add(self.main_group) self.main_group.show_all() self.backdrop.show() self.timeline = clutter.Timeline(10,30) alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func) self.behaviour_group = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=alpha) self.behaviour_backdrop = clutter.BehaviourOpacity(opacity_start=0, opacity_end=180, alpha=alpha) self.behaviour_group.apply(self.main_group) self.behaviour_backdrop.apply(self.backdrop) self.timeline.start() self.active = True def hide_msg(self): self.active = False self.timeline = clutter.Timeline(10,30) self.timeline.connect("completed", self.remove_from_stage) alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func) self.behaviour_group = clutter.BehaviourOpacity(opacity_start=255, opacity_end=0, alpha=alpha) self.behaviour_backdrop = clutter.BehaviourOpacity(opacity_start=180, opacity_end=0, alpha=alpha) self.behaviour_group.apply(self.main_group) self.behaviour_backdrop.apply(self.backdrop) self.timeline.start() def remove_from_stage(self, timeline): self.stage.remove(self.backdrop) self.stage.remove(self.main_group) #if there's messages in the queue, run through them if len(self.msgQueue) > 0: (title, text) = self.msgQueue.pop() self.display_msg(title, text) def on_key_press_event (self, stage, event): self.hide_msg()