From aaf9e9e75c5280de0fd7db675cb75f9d5c7dd663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 22 Mar 2019 12:16:36 +0100 Subject: [PATCH] Improve note button * Add resources method to ModelStorage * Support badge on icon * Display unread and total of notes * Add two extra colors for icon issue8122 review70561002 --- CHANGELOG | 3 + tryton/common/common.py | 31 +++++-- tryton/config.py | 2 +- tryton/gui/window/form.py | 89 +++++++++----------- tryton/gui/window/view_form/model/record.py | 36 ++------ tryton/gui/window/view_form/screen/screen.py | 33 ++------ 6 files changed, 81 insertions(+), 113 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 42eaa59629..d82d1ce480 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +* Display unread and total of notes +* Support badge on icon +* Use resources method * Replace Arrow by Image * Remove Fast Tabbing option * Remove usage of focus_chain diff --git a/tryton/common/common.py b/tryton/common/common.py index 52587f8362..a51940084f 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -111,10 +111,12 @@ class IconFactory: cls._tryton_icons.remove((icon['id'], icon['name'])) del cls._name2id[icon['name']] + colors = CONFIG['icon.colors'].split(',') + @classmethod - def get_pixbuf(cls, iconname, size=16, color=None): + def get_pixbuf(cls, iconname, size=16, color=None, badge=None): cls.register_icon(iconname) - if iconname not in cls._pixbufs[size]: + if iconname not in cls._pixbufs[(size, badge)]: if iconname in cls._icons: data = cls._icons[iconname] elif iconname in cls._local_icons: @@ -124,12 +126,24 @@ class IconFactory: else: logger.error("Unknown icon %s" % iconname) return - if color is None: - color = CONFIG['icon.color'] + if not color: + color = cls.colors[0] try: ET.register_namespace('', 'http://www.w3.org/2000/svg') root = ET.fromstring(data) root.attrib['fill'] = color + if badge: + if not isinstance(badge, str): + try: + badge = cls.colors[badge] + except IndexError: + badge = color + ET.SubElement(root, 'circle', { + 'cx': '20', + 'cy': '4', + 'r': '4', + 'fill': badge, + }) data = ET.tostring(root) except ET.ParseError: pass @@ -141,12 +155,13 @@ class IconFactory: Gtk.IconSize.DND: 12, Gtk.IconSize.DIALOG: 48, }.get(size, size) - cls._pixbufs[size][iconname] = data2pixbuf(data, width, height) - return cls._pixbufs[size][iconname] + pixbuf = data2pixbuf(data, width, height) + cls._pixbufs[(size, badge)][iconname] = pixbuf + return cls._pixbufs[(size, badge)][iconname] @classmethod - def get_image(cls, iconname, size=16, color=None): - pixbuf = cls.get_pixbuf(iconname, size, color) + def get_image(cls, iconname, size=16, color=None, badge=None): + pixbuf = cls.get_pixbuf(iconname, size, color, badge) image = Gtk.Image() image.set_from_pixbuf(pixbuf) return image diff --git a/tryton/config.py b/tryton/config.py index 65648fe97f..f702cf36c3 100644 --- a/tryton/config.py +++ b/tryton/config.py @@ -55,7 +55,7 @@ class ConfigManager(object): 'client.limit': 1000, 'client.check_version': True, 'client.bus_timeout': 10 * 60, - 'icon.color': '#3465a4', + 'icon.colors': '#3465a4,#555753,#cc0000', 'image.max_size': 10 ** 6, 'bug.url': 'https://bugs.tryton.org/', 'download.url': 'https://downloads.tryton.org/', diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index 218e508d44..2ab6901a0b 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -53,9 +53,9 @@ class Form(SignalEvent, TabContent): self.screen.signal_connect(self, 'record-modified', lambda *a: GLib.idle_add(self._record_modified, *a)) self.screen.signal_connect(self, 'record-saved', self._record_saved) - self.screen.signal_connect(self, 'attachment-count', - self._attachment_count) - self.screen.signal_connect(self, 'unread-note', self._unread_note) + self.screen.signal_connect( + self, 'resources', + lambda screen, resources: self.update_resources(resources)) if res_id not in (None, False): if isinstance(res_id, int): @@ -105,7 +105,7 @@ class Form(SignalEvent, TabContent): def sig_attach(self, widget=None): def window(widget): return Attachment( - record, lambda: self.update_attachment_count(reload=True)) + record, lambda: self.refresh_resources(reload=True)) def add_file(widget): filenames = common.file_selection(_("Select"), multi=True) @@ -139,61 +139,48 @@ class Form(SignalEvent, TabContent): menu.connect('deactivate', self._popup_menu_hide, button) self.action_popup(button) - def update_attachment_count(self, reload=False): - record = self.screen.current_record - if record: - attachment_count = record.get_attachment_count(reload=reload) - else: - attachment_count = 0 - self._attachment_count(None, attachment_count) - - def _attachment_count(self, widget, signal_data): - label = _('Attachment(%d)') % signal_data - self.buttons['attach'].set_label(label) - if signal_data: - # FIXME - icon = 'tryton-attach' - else: - icon = 'tryton-attach' - image = common.IconFactory.get_image(icon, Gtk.IconSize.LARGE_TOOLBAR) - image.show() - self.buttons['attach'].set_icon_widget(image) - record = self.screen.current_record - self.buttons['attach'].props.sensitive = bool( - record.id >= 0 if record else False) - def sig_note(self, widget=None): record = self.screen.current_record if not record or record.id < 0: return Note(record, - lambda: self.update_unread_note(reload=True)) + lambda: self.refresh_resources(reload=True)) - def update_unread_note(self, reload=False): + def refresh_resources(self, reload=False): record = self.screen.current_record - if record: - unread = record.get_unread_note(reload=reload) - else: - unread = 0 - self._unread_note(None, unread) + self.update_resources( + record.get_resources(reload=reload) if record else None) - def _unread_note(self, widget, signal_data): - label = _('Note(%d)') % signal_data - self.buttons['note'].set_label(label) - if signal_data: - # FIXME - icon = 'tryton-note' - else: - icon = 'tryton-note' - image = common.IconFactory.get_image(icon, Gtk.IconSize.LARGE_TOOLBAR) - image.show() - self.buttons['note'].set_icon_widget(image) + def update_resources(self, resources): + if not resources: + resources = {} record = self.screen.current_record - if not record or record.id < 0: - sensitive = False + sensitive = record.id >= 0 if record else False + + def update(name, label, icon, badge): + button = self.buttons[name] + button.set_label(label) + image = common.IconFactory.get_image( + icon, Gtk.IconSize.LARGE_TOOLBAR, badge=badge) + image.show() + button.set_icon_widget(image) + button.props.sensitive = sensitive + + attachment_count = resources.get('attachment_count', 0) + badge = 1 if attachment_count else None + label = _("Attachment (%s)") % attachment_count + update('attach', label, 'tryton-attach', badge) + + note_count = resources.get('note_count', 0) + note_unread = resources.get('note_unread', 0) + if note_unread: + badge = 2 + elif note_count: + badge = 1 else: - sensitive = True - self.buttons['note'].props.sensitive = sensitive + badge = None + label = _("Note (%d/%d)") % (note_unread, note_count) + update('note', label, 'tryton-note', badge) def sig_switch(self, widget=None): if not self.modified_save(): @@ -498,7 +485,7 @@ class Form(SignalEvent, TabContent): def _record_saved(self, screen, signal_data): self.activate_save() - self.update_attachment_count() + self.refresh_resources() def modified_save(self): self.screen.save_tree_state() @@ -711,7 +698,7 @@ class Form(SignalEvent, TabContent): if not record or record.id < 0: return win_attach = Attachment(record, - lambda: self.update_attachment_count(reload=True)) + lambda: self.refresh_resources(reload=True)) if info == 0: if selection.get_uris(): for uri in selection.get_uris(): diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index db110bbf35..064b53b010 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -28,8 +28,7 @@ class Record(SignalEvent): self.state_attrs = {} self.modified_fields = {} self._timestamp = None - self.attachment_count = -1 - self.unread_note = -1 + self.resources = None self.button_clicks = {} self.next = {} # Used in Group list self.value = {} @@ -602,34 +601,15 @@ class Record(SignalEvent): if context: value.context = self.expr_eval(context) - def get_attachment_count(self, reload=False): - if self.id < 0: - return 0 - if self.attachment_count < 0 or reload: + def get_resources(self, reload=False): + if self.id >= 0 and (not self.resources or reload): try: - self.attachment_count = RPCExecute('model', 'ir.attachment', - 'search_count', [ - ('resource', '=', - '%s,%s' % (self.model_name, self.id)), - ], context=self.get_context()) + self.resources = RPCExecute( + 'model', self.model_name, 'resources', self.id, + context=self.get_context()) except RPCException: - return 0 - return self.attachment_count - - def get_unread_note(self, reload=False): - if self.id < 0: - return 0 - if self.unread_note < 0 or reload: - try: - self.unread_note = RPCExecute('model', 'ir.note', - 'search_count', [ - ('resource', '=', - '%s,%s' % (self.model_name, self.id)), - ('unread', '=', True), - ], context=self.get_context()) - except RPCException: - return 0 - return self.unread_note + pass + return self.resources def get_button_clicks(self, name): if self.id < 0: diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index 17a8e6c8dc..c457b05da0 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -436,35 +436,18 @@ class Screen(SignalEvent): pos = 0 self.signal('record-message', (pos, len(self.group) + self.offset, self.search_count, record and record.id)) - attachment_count = 0 - if record and record.attachment_count > 0: - attachment_count = record.attachment_count - self.signal('attachment-count', attachment_count) - unread_note = 0 - if record and record.unread_note > 0: - unread_note = record.unread_note - self.signal('unread-note', unread_note) - # update attachment-count after 1 second - GLib.timeout_add(1000, self.update_attachment, record) - GLib.timeout_add(1000, self.update_note, record) + self.signal('resources', record.resources if record else None) + # update resources after 1 second + GLib.timeout_add(1000, self._update_resources, record) return True current_record = property(__get_current_record, __set_current_record) - def update_attachment(self, record): - if record != self.current_record: - return False - if record and self.signal_connected('attachment-count'): - attachment_count = record.get_attachment_count() - self.signal('attachment-count', attachment_count) - return False - - def update_note(self, record): - if record != self.current_record: - return False - if record and self.signal_connected('unread-note'): - unread_note = record.get_unread_note() - self.signal('unread-note', unread_note) + def _update_resources(self, record): + if (record + and record == self.current_record + and self.signal_connected('resources')): + self.signal('resources', record.get_resources()) return False def destroy(self):