Finished implementing a format button bar and provider classes for various markup languages.

Tue, 10 Jan 2017 19:15:17 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 10 Jan 2017 19:15:17 +0100
changeset 5411
a163fbbf2bea
parent 5409
c6f4a6f0d051
child 5412
db5a520f69d3

Finished implementing a format button bar and provider classes for various markup languages.

QScintilla/EditorButtonsWidget.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/HtmlProvider.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/MarkdownProvider.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/MarkupBase.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/RestructuredTextProvider.py file | annotate | diff | comparison | revisions
--- 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()

eric ide

mercurial