Mercurial > libervia-web
changeset 395:98cd5387d291
browser_side: new microblogs are not using the rich text editor by default:
- a button allows to switch from raw to rich text, and vice versa
- raw text microblogs are not submitted anymore when the focus is lost, only when <enter> is pressed
author | souliane <souliane@mailoo.org> |
---|---|
date | Mon, 10 Mar 2014 22:39:26 +0100 |
parents | ee61b0765d6c |
children | a71fcc27f231 |
files | browser_side/base_panels.py browser_side/panels.py browser_side/richtext.py public/libervia.css |
diffstat | 4 files changed, 100 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/browser_side/base_panels.py Thu Mar 06 01:06:19 2014 +0100 +++ b/browser_side/base_panels.py Mon Mar 10 22:39:26 2014 +0100 @@ -343,6 +343,20 @@ """ raise NotImplementedError + def setOriginalContent(self, content): + """Use this method with care! Content initialization should normally be + done with self.setContent. This method exists to let you trick the editor, + e.g. for self.modified to return True also when nothing has been modified. + @param content: dict + """ + self._original_content = content + + def getOriginalContent(self): + """ + @return the original content before modification (dict) + """ + return self._original_content + def modified(self, content=None): """Check if the content has been modified. Remark: we don't use the direct comparison because we want to ignore empty elements @@ -424,21 +438,24 @@ class LightTextEditor(BaseTextEditor, HTML, FocusHandler, KeyboardHandler): """Manage a simple text editor whith the HTML 5 "contenteditable" property.""" - def __init__(self, content=None, modifiedCb=None, afterEditCb=None, single_line=False, enhance_display=True): + def __init__(self, content=None, modifiedCb=None, afterEditCb=None, single_line=False, enhance_display=True, listen_focus=None): """ @param content @param modifiedCb @param afterEditCb - @param single_line: set to True to manage a single line editor. In that case - the edition would be terminated when the focus is lost or <enter> key is pressed. + @param single_line: set to True to manage a single line editor. In that + case the edition will be terminated when the <enter> key is pressed. + @param listen_focus: set to True to terminate the edition when the + focus is lost. Leave to None in order to use single_line's value. @param enhance_display: if True, the display text will be enhanced with addURLToText """ HTML.__init__(self) - if single_line: + self.__single_line = single_line + self.__enhance_display = enhance_display + self.__listen_focus = single_line if listen_focus is None else listen_focus + if self.__listen_focus: FocusHandler.__init__(self) KeyboardHandler.__init__(self) - self.__single_line = single_line - self.__enhance_display = enhance_display strproc = lambda text: html_sanitize(html_strip(text)) if self.__single_line else html_strip(text) BaseTextEditor.__init__(self, content, strproc, modifiedCb, afterEditCb) @@ -455,12 +472,12 @@ self.getElement().setAttribute('contenteditable', 'true' if edit else 'false') BaseTextEditor.edit(self, edit) if edit: - if self.__single_line: + if self.__listen_focus: self.addFocusListener(self) self.addKeyboardListener(self) else: self.setDisplayContent() - if self.__single_line: + if self.__listen_focus: if self in self._focusListeners: self.removeFocusListener(self) if self in self._keyboardListeners: @@ -483,8 +500,10 @@ def onKeyPress(self, sender, keycode, modifiers): if not self.__single_line: return - if keycode == KEY_ENTER: - self.setFocus(False) # finish the edition + if keycode == KEY_ENTER: # finish the edition + self.setFocus(False) + if not self.__listen_focus: + self.edit(False) def onLostFocus(self, sender): """Finish the edition when focus is lost"""
--- a/browser_side/panels.py Thu Mar 06 01:06:19 2014 +0100 +++ b/browser_side/panels.py Mon Mar 10 22:39:26 2014 +0100 @@ -388,7 +388,8 @@ 'author', 'updated', 'published', 'comments', 'service', 'node', 'comments_service', 'comments_node']: getter = lambda attr: lambda inst: getattr(inst._base_item, attr) - setattr(MicroblogEntry, attr, property(getter(attr))) + setter = lambda attr: lambda inst, value: setattr(inst._base_item, attr, value) + setattr(MicroblogEntry, attr, property(getter(attr), setter(attr))) SimplePanel.__init__(self) self._blog_panel = blog_panel @@ -409,7 +410,7 @@ entry_avatar.add(self.avatar) self.panel.add(entry_avatar) - self.entry_dialog = SimplePanel() + self.entry_dialog = HorizontalPanel() self.entry_dialog.setStyleName('mb_entry_dialog') self.panel.add(self.entry_dialog) @@ -473,7 +474,7 @@ elif sender == self.delete_label: self._delete() elif sender == self.update_label: - self.bubble.edit(True) + self.edit(True) elif sender == self.comment_label: self._comment() @@ -485,7 +486,7 @@ self._delete(True) return False extra = {'published': str(self.published)} - if self.empty or self.content_xhtml: + if isinstance(self.bubble, richtext.RichTextEditor): # TODO: if the user change his parameters after the message edition started, # the message syntax could be different then the current syntax: pass the # message syntax in extra for the frontend to use it instead of current syntax. @@ -508,12 +509,16 @@ self._blog_panel.refresh() else: # allow to create a new comment self._parent_entry._current_comment = None + try: + self.toggle_syntax_button.removeFromParent() + except TypeError: + pass - def __setBubble(self): + def __setBubble(self, edit=False): """Set the bubble displaying the initial content.""" content = {'text': self.content_xhtml if self.content_xhtml else self.content, 'title': self.title_xhtml if self.title_xhtml else self.title} - if self.empty or self.content_xhtml: # new message and rich text message + if self.content_xhtml: content.update({'syntax': Const.SYNTAX_XHTML}) if self.author != self._blog_panel.host.whoami.bare: options = ['read_only'] @@ -521,10 +526,14 @@ options = [] if self.empty else ['update_msg'] self.bubble = richtext.RichTextEditor(self._blog_panel.host, content, self.__modifiedCb, self.__afterEditCb, options) else: # assume raw text message have no title - self.bubble = LightTextEditor(content, self.__modifiedCb, self.__afterEditCb, True) + self.bubble = LightTextEditor(content, self.__modifiedCb, self.__afterEditCb, single_line=True, listen_focus=False) self.bubble.setStyleName("bubble") + try: + self.toggle_syntax_button.removeFromParent() + except TypeError: + pass self.entry_dialog.add(self.bubble) - self.bubble.edit(False) + self.edit(edit) self.bubble.addEditListener(self.__showWarning) def __showWarning(self, sender, keycode): @@ -571,7 +580,55 @@ return entry._parent_entry = self self._current_comment = entry - entry.bubble.edit(True) + self.edit(True, entry) + + def edit(self, edit, entry=None): + """Toggle the bubble between display and edit mode + @edit: boolean value + @entry: MicroblogEntry instance, or None to use self + """ + if entry is None: + entry = self + try: + entry.toggle_syntax_button.removeFromParent() + except TypeError: + pass + entry.bubble.edit(edit) + if edit: + if isinstance(entry.bubble, richtext.RichTextEditor): + image = '<a class="richTextIcon">A</a>' + title = _('Switch to raw text edition') + else: + image = '<img src="media/icons/tango/actions/32/format-text-italic.png" class="richTextIcon"/>' + title = _('Switch to rich text edition') + entry.toggle_syntax_button = Button(image, entry.toggleContentSyntax) + entry.toggle_syntax_button.setTitle(title) + entry.entry_dialog.add(entry.toggle_syntax_button) + + def toggleContentSyntax(self): + """Toggle the editor between raw and rich text""" + original_content = self.bubble.getOriginalContent() + rich = not isinstance(self.bubble, richtext.RichTextEditor) + + def setBubble(text): + self.content = text + self.content_xhtml = text if rich else '' + self.content_title = self.content_title_xhtml = '' + self.bubble.removeFromParent() + self.__setBubble(True) + self.bubble.setOriginalContent(original_content) + + text = self.bubble.getContent()['text'] + if not text: + setBubble(' ') # something different than empty string is needed to initialize the rich text editor + return + if not rich: + def confirm_cb(answer): + if answer: + self._blog_panel.host.bridge.call('syntaxConvert', setBubble, text, Const.SYNTAX_CURRENT, Const.SYNTAX_TEXT) + dialog.ConfirmDialog(confirm_cb, text=_("Do you really want to lose the title and text formatting?")).show() + else: + self._blog_panel.host.bridge.call('syntaxConvert', setBubble, text, Const.SYNTAX_TEXT, Const.SYNTAX_XHTML) class MicroblogPanel(base_widget.LiberviaWidget): @@ -607,7 +664,7 @@ 'author': self.host.whoami.bare, } entry = self.addEntry(data) - entry.bubble.edit(True) + entry.edit(True) self.new_button = Button("New message", listener=addBox) self.new_button.setStyleName("microblogNewButton") self.vpanel.insert(self.new_button, 0) @@ -890,7 +947,7 @@ def __init__(self, host, status=''): self.host = host modifiedCb = lambda content: self.host.bridge.call('setStatus', None, self.host.status_panel.presence, content['text']) or True - LightTextEditor.__init__(self, {'text': status}, modifiedCb, None, True) + LightTextEditor.__init__(self, {'text': status}, modifiedCb, None, single_line=True) self.edit(False) self.setStyleName('statusPanel') ClickHandler.__init__(self)
--- a/browser_side/richtext.py Thu Mar 06 01:06:19 2014 +0100 +++ b/browser_side/richtext.py Mon Mar 10 22:39:26 2014 +0100 @@ -285,7 +285,7 @@ self.refresh(edit) # not when we are asking for a confirmation BaseTextEditor.edit(self, edit, abort, sync) # after the UI has been refreshed if (edit and abort): - return # self.avortEdition is called by BaseTextEditor.edit + return # self.abortEdition is called by BaseTextEditor.edit self.setWysiwyg(False, init=True) # after BaseTextEditor (it affects self.getContent) if sync: return
--- a/public/libervia.css Thu Mar 06 01:06:19 2014 +0100 +++ b/public/libervia.css Mon Mar 10 22:39:26 2014 +0100 @@ -851,6 +851,7 @@ float: left; min-height: 54px; padding: 5px 20px 5px 20px; + border-collapse: separate; # for the bubble queue since the entry dialog is now a HorizontalPanel } .bubble { @@ -866,6 +867,7 @@ border-style: solid; display: block; border-collapse: separate; + min-height: 15px; # for the bubble queue to be aligned when the bubble is empty } .bubble:after {