Tue, 10 Jan 2017 19:15:17 +0100
Finished implementing a format button bar and provider classes for various markup languages.
--- a/QScintilla/EditorButtonsWidget.py Tue Jan 10 15:35:08 2017 +0100 +++ b/QScintilla/EditorButtonsWidget.py Tue Jan 10 19:15:17 2017 +0100 @@ -10,8 +10,9 @@ from __future__ import unicode_literals -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtWidgets import QWidget, QVBoxLayout, QToolButton, QFrame, QMenu +from PyQt5.QtCore import pyqtSlot, Qt +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QToolButton, QFrame, QMenu, \ + QSizePolicy, QScrollArea import UI.PixmapCache @@ -36,8 +37,11 @@ margin = 2 spacing = 3 - self.__layout = QVBoxLayout(self) - self.__layout.setContentsMargins(margin, margin, margin, margin) + + self.__buttonsWidget = QWidget(self) + + self.__layout = QVBoxLayout(self.__buttonsWidget) + self.__layout.setContentsMargins(0, 0, 0, 0) self.__layout.setSpacing(spacing) self.__provider = None @@ -51,10 +55,112 @@ self.__createButtons() self.__layout.addStretch() + + self.__outerLayout = QVBoxLayout(self) + self.__outerLayout.setContentsMargins(margin, margin, margin, margin) + self.__outerLayout.setSpacing(spacing) + self.__outerLayout.setAlignment(Qt.AlignHCenter) + + self.__upButton = QToolButton(self) + self.__upButton.setArrowType(Qt.UpArrow) + self.__upButton.setSizePolicy( + QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) + self.__upButton.setAutoRepeat(True) + + self.__scroller = QScrollArea(self) + self.__scroller.setWidget(self.__buttonsWidget) + self.__scroller.setSizePolicy( + QSizePolicy.Minimum, QSizePolicy.Expanding) + self.__scroller.setFrameShape(QFrame.NoFrame) + self.__scroller.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.__scroller.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.__scroller.setWidgetResizable(False) + + self.__downButton = QToolButton(self) + self.__downButton.setArrowType(Qt.DownArrow) + self.__downButton.setSizePolicy( + QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) + self.__downButton.setAutoRepeat(True) + + self.__outerLayout.addWidget(self.__upButton) + self.__outerLayout.addWidget(self.__scroller) + self.__outerLayout.addWidget(self.__downButton) + + self.__upButton.clicked.connect(self.__slideUp) + self.__downButton.clicked.connect(self.__slideDown) + self.setMaximumWidth( self.__buttons["bold"].sizeHint().width() + 2 * margin) self.__updateButtonStates() + + ####################################################################### + ## Methods below implement some event handlers + ####################################################################### + + def show(self): + """ + Public slot to show the widget. + """ + super(EditorButtonsWidget, self).show() + self.__enableScrollerButtons() + + def resizeEvent(self, evt): + """ + Protected method to handle resize events. + + @param evt reference to the resize event (QResizeEvent) + """ + self.__enableScrollerButtons() + super(EditorButtonsWidget, self).resizeEvent(evt) + + ####################################################################### + ## Methods below implement scroller related functions + ####################################################################### + + def __enableScrollerButtons(self): + """ + Private method to set the enabled state of the scroll buttons. + """ + scrollBar = self.__scroller.verticalScrollBar() + self.__upButton.setEnabled(scrollBar.value() > 0) + self.__downButton.setEnabled(scrollBar.value() < scrollBar.maximum()) + + def __slideUp(self): + """ + Private slot to move the widget upwards, i.e. show contents to the + bottom. + """ + self.__slide(True) + + def __slideDown(self): + """ + Private slot to move the widget downwards, i.e. show contents to + the top. + """ + self.__slide(False) + + def __slide(self, up): + """ + Private method to move the sliding widget. + + @param up flag indicating to move upwards (boolean) + """ + scrollBar = self.__scroller.verticalScrollBar() + stepSize = scrollBar.singleStep() + if up: + stepSize = -stepSize + newValue = scrollBar.value() + stepSize + if newValue < 0: + newValue = 0 + elif newValue > scrollBar.maximum(): + newValue = scrollBar.maximum() + scrollBar.setValue(newValue) + self.__enableScrollerButtons() + + ####################################################################### + ## Methods below implement the format button functions + ####################################################################### def __createButtons(self): """ @@ -116,7 +222,7 @@ @return generated button @rtype QToolButton """ - button = QToolButton(self) + button = QToolButton(self.__buttonsWidget) button.setIcon(UI.PixmapCache.getIcon(iconName)) button.setToolTip(toolTip) button.clicked.connect(lambda: self.__formatClicked(format)) @@ -129,7 +235,7 @@ """ Private method to add a separator line. """ - line = QFrame(self) + line = QFrame(self.__buttonsWidget) line.setLineWidth(2) if isinstance(self.__layout, QVBoxLayout): line.setFrameShape(QFrame.HLine) @@ -170,6 +276,11 @@ self.__buttons["codeBlock"].setEnabled( self.__provider.hasCodeBlock()) + self.__buttons["bulletedList"].setEnabled( + self.__provider.hasBulletedList()) + self.__buttons["numberedList"].setEnabled( + self.__provider.hasNumberedList()) + self.__editorSelectionChanged() # TODO: make this configurable @@ -206,6 +317,10 @@ self.__provider.line(self.__editor) elif format == "image": self.__provider.image(self.__editor) + elif format == "bulletedList": + self.__provider.bulletedList(self.__editor) + elif format == "numberedList": + self.__provider.numberedList(self.__editor) def __headerMenuTriggered(self, act): """
--- a/QScintilla/MarkupProviders/HtmlProvider.py Tue Jan 10 15:35:08 2017 +0100 +++ b/QScintilla/MarkupProviders/HtmlProvider.py Tue Jan 10 19:15:17 2017 +0100 @@ -9,7 +9,8 @@ from __future__ import unicode_literals -from PyQt5.QtWidgets import QDialog +from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import QDialog, QInputDialog from .MarkupBase import MarkupBase @@ -316,3 +317,94 @@ cline, cindex = editor.getCursorPosition() editor.setCursorPosition(cline, cindex + len(markup)) editor.endUndoAction() + + def hasBulletedList(self): + """ + Public method to indicate the availability of bulleted list markup. + + @return flag indicating the availability of bulleted list markup + @rtype bool + """ + return True + + def bulletedList(self, editor): + """ + Public method to generate bulleted list text. + + @param editor reference to the editor to work on + @type Editor + """ + self.__makeList(editor, "ul") + + def hasNumberedList(self): + """ + Public method to indicate the availability of numbered list markup. + + @return flag indicating the availability of numbered list markup + @rtype bool + """ + return True + + def numberedList(self, editor): + """ + Public method to generate numbered list text. + + @param editor reference to the editor to work on + @type Editor + """ + self.__makeList(editor, "ol") + + def __makeList(self, editor, listType): + """ + Private method to generate the desired list markup. + + @param editor reference to the editor to work on + @type Editor + @param listType type of the desired list (should be ul or ol) + @type str + """ + if editor is None: + return + + lineSeparator = editor.getLineSeparator() + editor.beginUndoAction() + if editor.hasSelectedText(): + startLine, startIndex, endLine, endIndex = \ + editor.getSelection() + if endIndex == 0: + endLine -= 1 + for line in range(startLine, endLine + 1): + editor.insertAt("</li>", line, len(editor.text(line).rstrip())) + editor.insertAt(" <li>", line, 0) + if line == editor.lines() - 1: + editor.insertAt(lineSeparator, line, 1000) + editor.insertAt("</{1}>{0}".format(lineSeparator, listType), + endLine + 1, 0) + editor.insertAt("<{1}>{0}".format(lineSeparator, listType), + startLine, 0) + editor.setCursorPosition(endLine + 3, 0) + else: + listElements, ok = QInputDialog.getInt( + None, + QCoreApplication.translate( + "HtmlProvider", "Create List"), + QCoreApplication.translate( + "HtmlProvider", "Enter desired number of list elements:"), + 0, 0, 99, 1) + if ok: + if listElements == 0: + listElements = 1 + cline, cindex = editor.getCursorPosition() + listBody = \ + listElements * " <li></li>{0}".format(lineSeparator) + markup = "<{1}>{0}{2}</{1}>{0}".format( + lineSeparator, listType, listBody) + if cindex == 0: + editor.insertAt(markup, cline, cindex) + editor.setCursorPosition(cline + 1, 6) + else: + if cline == editor.lines() - 1: + editor.insertAt(lineSeparator, cline, 1000) + editor.insertAt(markup, cline + 1, 0) + editor.setCursorPosition(cline + 2, 6) + editor.endUndoAction()
--- a/QScintilla/MarkupProviders/MarkdownProvider.py Tue Jan 10 15:35:08 2017 +0100 +++ b/QScintilla/MarkupProviders/MarkdownProvider.py Tue Jan 10 19:15:17 2017 +0100 @@ -9,7 +9,8 @@ from __future__ import unicode_literals -from PyQt5.QtWidgets import QDialog +from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import QDialog, QInputDialog from .MarkupBase import MarkupBase @@ -315,3 +316,90 @@ cline, cindex = editor.getCursorPosition() editor.setCursorPosition(cline, cindex + len(markup)) editor.endUndoAction() + + def hasBulletedList(self): + """ + Public method to indicate the availability of bulleted list markup. + + @return flag indicating the availability of bulleted list markup + @rtype bool + """ + return True + + def bulletedList(self, editor): + """ + Public method to generate bulleted list text. + + @param editor reference to the editor to work on + @type Editor + """ + self.__makeList(editor, False) + + def hasNumberedList(self): + """ + Public method to indicate the availability of numbered list markup. + + @return flag indicating the availability of numbered list markup + @rtype bool + """ + return True + + def numberedList(self, editor): + """ + Public method to generate numbered list text. + + @param editor reference to the editor to work on + @type Editor + """ + self.__makeList(editor, True) + + def __makeList(self, editor, numberedList): + """ + Private method to generate the desired list markup. + + @param editor reference to the editor to work on + @type Editor + @param numberedList flag indicating the generation of a numbered list + @type bool + """ + if editor is None: + return + + if numberedList: + markup = " 1. " + else: + markup = " * " + lineSeparator = editor.getLineSeparator() + editor.beginUndoAction() + if editor.hasSelectedText(): + startLine, startIndex, endLine, endIndex = \ + editor.getSelection() + if endIndex == 0: + endLine -= 1 + for line in range(startLine, endLine + 1): + editor.insertAt(markup, line, 0) + editor.setCursorPosition(endLine + 1, 0) + else: + listElements, ok = QInputDialog.getInt( + None, + QCoreApplication.translate( + "MarkdownProvider", "Create List"), + QCoreApplication.translate( + "MarkdownProvider", + "Enter desired number of list elements:"), + 0, 0, 99, 1) + if ok: + if listElements == 0: + listElements = 1 + cline, cindex = editor.getCursorPosition() + listBody = \ + listElements * "{1}{0}".format(lineSeparator, markup) + if cindex == 0: + editor.insertAt(listBody, cline, cindex) + editor.setCursorPosition(cline, len(markup)) + else: + if cline == editor.lines() - 1: + editor.insertAt(lineSeparator, cline, 1000) + editor.insertAt(listBody, cline + 1, 0) + editor.setCursorPosition(cline + 1, len(markup)) + editor.endUndoAction()
--- a/QScintilla/MarkupProviders/MarkupBase.py Tue Jan 10 15:35:08 2017 +0100 +++ b/QScintilla/MarkupProviders/MarkupBase.py Tue Jan 10 19:15:17 2017 +0100 @@ -214,3 +214,39 @@ @type Editor """ pass + + def hasBulletedList(self): + """ + Public method to indicate the availability of bulleted list markup. + + @return flag indicating the availability of bulleted list markup + @rtype bool + """ + return False + + def bulletedList(self, editor): + """ + Public method to generate bulleted list text. + + @param editor reference to the editor to work on + @type Editor + """ + pass + + def hasNumberedList(self): + """ + Public method to indicate the availability of numbered list markup. + + @return flag indicating the availability of numbered list markup + @rtype bool + """ + return False + + def numberedList(self, editor): + """ + Public method to generate numbered list text. + + @param editor reference to the editor to work on + @type Editor + """ + pass
--- a/QScintilla/MarkupProviders/RestructuredTextProvider.py Tue Jan 10 15:35:08 2017 +0100 +++ b/QScintilla/MarkupProviders/RestructuredTextProvider.py Tue Jan 10 19:15:17 2017 +0100 @@ -9,7 +9,8 @@ from __future__ import unicode_literals -from PyQt5.QtWidgets import QDialog +from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import QDialog, QInputDialog from .MarkupBase import MarkupBase @@ -344,3 +345,90 @@ cline, cindex = editor.getCursorPosition() editor.setCursorPosition(cline + lines, 0) editor.endUndoAction() + + def hasBulletedList(self): + """ + Public method to indicate the availability of bulleted list markup. + + @return flag indicating the availability of bulleted list markup + @rtype bool + """ + return True + + def bulletedList(self, editor): + """ + Public method to generate bulleted list text. + + @param editor reference to the editor to work on + @type Editor + """ + self.__makeList(editor, False) + + def hasNumberedList(self): + """ + Public method to indicate the availability of numbered list markup. + + @return flag indicating the availability of numbered list markup + @rtype bool + """ + return True + + def numberedList(self, editor): + """ + Public method to generate numbered list text. + + @param editor reference to the editor to work on + @type Editor + """ + self.__makeList(editor, True) + + def __makeList(self, editor, numberedList): + """ + Private method to generate the desired list markup. + + @param editor reference to the editor to work on + @type Editor + @param numberedList flag indicating the generation of a numbered list + @type bool + """ + if editor is None: + return + + if numberedList: + markup = " #. " + else: + markup = " * " + lineSeparator = editor.getLineSeparator() + editor.beginUndoAction() + if editor.hasSelectedText(): + startLine, startIndex, endLine, endIndex = \ + editor.getSelection() + if endIndex == 0: + endLine -= 1 + for line in range(startLine, endLine + 1): + editor.insertAt(markup, line, 0) + editor.setCursorPosition(endLine + 1, 0) + else: + listElements, ok = QInputDialog.getInt( + None, + QCoreApplication.translate( + "RestructuredTextProvider", "Create List"), + QCoreApplication.translate( + "RestructuredTextProvider", + "Enter desired number of list elements:"), + 0, 0, 99, 1) + if ok: + if listElements == 0: + listElements = 1 + cline, cindex = editor.getCursorPosition() + listBody = \ + listElements * "{1}{0}".format(lineSeparator, markup) + if cindex == 0: + editor.insertAt(listBody, cline, cindex) + editor.setCursorPosition(cline, len(markup)) + else: + if cline == editor.lines() - 1: + editor.insertAt(lineSeparator, cline, 1000) + editor.insertAt(listBody, cline + 1, 0) + editor.setCursorPosition(cline + 1, len(markup)) + editor.endUndoAction()