# HG changeset patch # User Goffi # Date 1280915871 -28800 # Node ID 2fa58703f1b7b3c74c04747c94c88dc8bf10778f # Parent 74aaf230a7c34ef8533fdbd87479b277947812dd Primitivus: notification bar, first draft - popup queue is now managed - notifications can auto-hide when nothing to show - ctrl-n show next notification Primitivus: ctrl-s allow to temporarily hide a popup Primitivus: cards in card_game now answer to mouse click Primitivus: notification is shown when invalid card is played in card_game Primitivus: SelectableText has now methods get_text and set_text diff -r 74aaf230a7c3 -r 2fa58703f1b7 frontends/primitivus/card_game.py --- a/frontends/primitivus/card_game.py Wed Aug 04 17:53:20 2010 +0800 +++ b/frontends/primitivus/card_game.py Wed Aug 04 17:57:51 2010 +0800 @@ -43,6 +43,14 @@ self._emit('click') return key + def mouse_event(self, size, event, button, x, y, focus): + if urwid.is_mouse_press(event) and button == 1: + self.select(not self.__selected) + self._emit('click') + return True + + return False + def select(self, state=True): self.__selected = state attr,txt = self.card.getAttrText() @@ -275,6 +283,7 @@ @param invalid_cards: cards which are invalid""" QuickCardGame.invalidCards(self, phase, played_cards, invalid_cards) self.hand_wid.update(self.hand) + self.parent.host.notify(_('Cards played are invalid !')) self.parent.host.redraw() def cardsPlayed(self, player, cards): diff -r 74aaf230a7c3 -r 2fa58703f1b7 frontends/primitivus/custom_widgets.py --- a/frontends/primitivus/custom_widgets.py Wed Aug 04 17:53:20 2010 +0800 +++ b/frontends/primitivus/custom_widgets.py Wed Aug 04 17:57:51 2010 +0800 @@ -115,6 +115,14 @@ def getValue(self): return self.text + def get_text(self): + """for compatibility with urwid.Text""" + return self.getValue() + + def set_text(self, text): + self.text=unicode(text) + self._invalidate() + def setAttribute(self, name, value): """Change attribut used for rendering widget @param name: one of @@ -358,6 +366,74 @@ ## MISC ## +class NotificationBar(urwid.WidgetWrap): + """Bar used to show misc information to user""" + signals = ['change'] + + def __init__(self): + self.waitNotifs = urwid.Text('') + self.message = ClickableText('', default_attr='notifs') + urwid.connect_signal(self.message, 'click', lambda wid: self.showNext()) + self.columns = urwid.Columns([('fixed',6,self.waitNotifs),self.message]) + urwid.WidgetWrap.__init__(self, urwid.AttrMap(self.columns,'notifs')) + self.notifs = [] + + def __modQueue(self): + """must be called each time the notifications queue is changed""" + self.waitNotifs.set_text("(%i)" % len(self.notifs) if self.notifs else '') + self._emit('change') + + def addPopUp(self, pop_up_widget): + """Add a popup to the waiting queue""" + self.notifs.append(('popup',pop_up_widget)) + self.__modQueue() + + def addMessage(self, message): + "Add a message to the notificatio bar" + if not self.message.get_text(): + self.message.set_text(message) + self._invalidate() + self._emit('change') + else: + self.notifs.append(('message',message)) + self.__modQueue() + + def showNext(self): + """Show next message if any, else delete current message""" + found = None + for notif in self.notifs: + if notif[0] == "message": + found = notif + break + if found: + self.notifs.remove(found) + self.message.set_text(found[1]) + self.__modQueue() + else: + self.message.set_text('') + self._emit('change') + + def getNextPopup(self): + """Return next pop-up and remove it from the queue + @return: pop-up or None if there is no more in the queue""" + ret = None + for notif in self.notifs: + if notif[0] == 'popup': + ret = notif[1] + break + if ret: + self.notifs.remove(notif) + self.__modQueue() + return ret + + def isQueueEmpty(self): + return not bool(self.notifs) + + def canHide(self): + """Return True if there is now important information to show""" + return self.isQueueEmpty() and not self.message.get_text() + + class MenuBox(urwid.WidgetWrap): """Show menu items of a category in a box""" signals = ['click'] diff -r 74aaf230a7c3 -r 2fa58703f1b7 frontends/primitivus/primitivus --- a/frontends/primitivus/primitivus Wed Aug 04 17:53:20 2010 +0800 +++ b/frontends/primitivus/primitivus Wed Aug 04 17:57:51 2010 +0800 @@ -57,6 +57,7 @@ ('selected_menu', 'light gray,bold', 'dark green'), ('menuitem', 'light gray,bold', 'dark red'), ('menuitem_focus', 'light gray,bold', 'dark green'), + ('notifs', 'black,bold', 'yellow'), ('card_neutral', 'dark gray', 'white', 'standout,underline'), ('card_neutral_selected', 'dark gray', 'dark green', 'standout,underline'), ('card_special', 'brown', 'white', 'standout,underline'), @@ -80,7 +81,7 @@ def __init__(self): self.CM = QuickContactManagement() #FIXME: not the best place - QuickApp.__init__(self) #XXX: yes it's an unusual place for the constructor of a parent class, but the init order is important + QuickApp.__init__(self) ## main loop setup ## self.main_widget = ProfileManager(self) @@ -88,6 +89,9 @@ ##misc setup## self.chat_wins=ChatList(self) + self.notBar = custom_widgets.NotificationBar() + urwid.connect_signal(self.notBar,'change',self.onNotification) + self.__saved_overlay = None def debug(self): """convenient method to reset screen and launch pdb""" @@ -111,6 +115,8 @@ self.loop.run() def inputFilter(self, input, raw): + if self.__saved_overlay and input != ['ctrl s']: + return for i in input: if isinstance(i,tuple): if i[0] == 'mouse press': @@ -129,6 +135,19 @@ self.main_widget.header = None except AttributeError: pass + elif input == 'ctrl n': + """User wants to see next notification""" + self.notBar.showNext() + elif input == 'ctrl s': + """User wants to (un)hide overlay window""" + if isinstance(self.loop.widget,urwid.Overlay): + self.__saved_overlay = self.loop.widget + self.loop.widget = self.main_widget + else: + if self.__saved_overlay: + self.loop.widget = self.__saved_overlay + self.__saved_overlay = None + elif input == 'ctrl d' and 'D' in self.bridge.getVersion(): #Debug only for dev versions self.debug() elif input == 'f2': #user wants to (un)hide the contact_list @@ -190,10 +209,10 @@ self.contactList = ContactList(self, self.CM, on_click = self.contactSelected, on_change=lambda w: self.redraw()) #self.center_part = urwid.Columns([('weight',2,self.contactList),('weight',8,Chat('',self))]) self.center_part = urwid.Columns([('weight',2,self.contactList), ('weight',8,urwid.Filler(urwid.Text('')))]) - editBar = custom_widgets.AdvancedEdit('> ') - urwid.connect_signal(editBar,'click',self.onTextEntered) + self.editBar = custom_widgets.AdvancedEdit('> ') + urwid.connect_signal(self.editBar,'click',self.onTextEntered) self.menu_roller = self.__buildMenuRoller() - self.main_widget = custom_widgets.FocusFrame(self.center_part, header=self.menu_roller, footer=editBar, focus_part='footer') + self.main_widget = custom_widgets.FocusFrame(self.center_part, header=self.menu_roller, footer=self.editBar, focus_part='footer') return self.main_widget def plug_profile(self, profile_key='@DEFAULT@'): @@ -201,11 +220,23 @@ QuickApp.plug_profile(self, profile_key) def removePopUp(self, widget=None): + "Remove current pop-up, and if there is other in queue, show it" self.loop.widget = self.main_widget + next_popup = self.notBar.getNextPopup() + if next_popup: + #we still have popup to show, we display it + self.showPopUp(next_popup) def showPopUp(self, pop_up_widget): - display_widget = urwid.Overlay(pop_up_widget, self.main_widget, 'center', ('relative', 40), 'middle', ('relative', 40)) - self.loop.widget = display_widget + "Show a pop-up window if possible, else put it in queue" + if not isinstance(self.loop.widget,urwid.Overlay): + display_widget = urwid.Overlay(pop_up_widget, self.main_widget, 'center', ('relative', 40), 'middle', ('relative', 40)) + self.loop.widget = display_widget + else: + self.notBar.addPopUp(pop_up_widget) + + def notify(self, message): + self.notBar.addMessage(message) def contactSelected(self, contact_list): contact = contact_list.get_contact() @@ -233,6 +264,24 @@ if JID(self.contactList.selected).short != sender.short: self.contactList.putAlert(sender) + def onNotification(self, notBar): + """Called when a new notification has been received""" + if not isinstance(self.main_widget, custom_widgets.FocusFrame): + #if we are not in the main configuration, we ignore the notifications bar + return + if isinstance(self.main_widget.footer,custom_widgets.AdvancedEdit): + if not self.notBar.canHide(): + #the notification bar is not visible and has usefull informations, we show it + pile = urwid.Pile([self.notBar, self.editBar]) + self.main_widget.footer = pile + else: + if not isinstance(self.main_widget.footer, urwid.Pile): + error(_("INTERNAL ERROR: Unexpected class for main widget's footer")) + assert(False) + if self.notBar.canHide(): + #No notification left, we can hide the bar + self.main_widget.footer = self.editBar + def actionResult(self, type, id, data): if not id in self.current_action_ids: debug (_('unknown id, ignoring')) diff -r 74aaf230a7c3 -r 2fa58703f1b7 frontends/quick_frontend/quick_app.py --- a/frontends/quick_frontend/quick_app.py Wed Aug 04 17:53:20 2010 +0800 +++ b/frontends/quick_frontend/quick_app.py Wed Aug 04 17:57:51 2010 +0800 @@ -72,7 +72,7 @@ return profile in self.profiles.keys() def postInit(self): - """Must be called after __init__, do all automatic task (auto plug profile)""" + """Must be called after initialization is done, do all automatic task (auto plug profile)""" if self.options.profile: if not self.bridge.getProfileName(self.options.profile): error(_("Trying to plug an unknown profile (%s)" % self.options.profile))