Differential D9419 Diff 25974 plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/pythoneditor.py
Changeset View
Changeset View
Standalone View
Standalone View
plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/editor/pythoneditor.py
1 | from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSlot | 1 | from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSlot | ||
---|---|---|---|---|---|
2 | from PyQt5.QtWidgets import QPlainTextEdit, QTextEdit, QLabel | 2 | from PyQt5.QtWidgets import QPlainTextEdit, QTextEdit, QLabel | ||
3 | from PyQt5.QtGui import QIcon, QColor, QPainter, QTextFormat, QFont, QFontInfo, QTextCursor, QPalette | 3 | from PyQt5.QtGui import QIcon, QColor, QPainter, QTextFormat, QFont, QTextCursor | ||
4 | from scripter.ui_scripter.editor import linenumberarea, debugarea | 4 | from scripter.ui_scripter.editor import linenumberarea, debugarea | ||
5 | from scripter import resources_rc | 5 | | ||
6 | | ||||
7 | INDENT_WIDTH = 4 # size in spaces of indent in editor window. | ||||
8 | # ideally make this a setting sometime? | ||||
6 | 9 | | |||
7 | 10 | | |||
8 | class CodeEditor(QPlainTextEdit): | 11 | class CodeEditor(QPlainTextEdit): | ||
9 | 12 | | |||
10 | 13 | | |||
11 | DEBUG_AREA_WIDTH = 20 | 14 | DEBUG_AREA_WIDTH = 20 | ||
12 | 15 | | |||
13 | def __init__(self, scripter, parent=None): | 16 | def __init__(self, scripter, parent=None): | ||
Show All 11 Lines | |||||
25 | 28 | | |||
26 | self.updateMarginsWidth() | 29 | self.updateMarginsWidth() | ||
27 | self.highlightCurrentLine() | 30 | self.highlightCurrentLine() | ||
28 | self.font = "Monospace" | 31 | self.font = "Monospace" | ||
29 | self._stepped = False | 32 | self._stepped = False | ||
30 | self.debugArrow = QIcon(':/icons/debug_arrow.svg') | 33 | self.debugArrow = QIcon(':/icons/debug_arrow.svg') | ||
31 | self.setCornerWidget(QLabel(str())) | 34 | self.setCornerWidget(QLabel(str())) | ||
32 | self._documentChanged = False | 35 | self._documentChanged = False | ||
36 | self.indent_width = INDENT_WIDTH # maybe one day connect this to a setting | ||||
33 | 37 | | |||
34 | self.undoAvailable.connect(self.setDocumentModified) | 38 | self.undoAvailable.connect(self.setDocumentModified) | ||
35 | 39 | | |||
36 | 40 | | |||
37 | def debugAreaWidth(self): | 41 | def debugAreaWidth(self): | ||
38 | return self.DEBUG_AREA_WIDTH | 42 | return self.DEBUG_AREA_WIDTH | ||
39 | 43 | | |||
40 | def lineNumberAreaWidth(self): | 44 | def lineNumberAreaWidth(self): | ||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Line(s) | 148 | def wheelEvent(self, e): | |||
147 | if e.modifiers() == Qt.ControlModifier: | 151 | if e.modifiers() == Qt.ControlModifier: | ||
148 | delta = e.angleDelta().y() | 152 | delta = e.angleDelta().y() | ||
149 | if delta < 0: | 153 | if delta < 0: | ||
150 | self.zoomOut() | 154 | self.zoomOut() | ||
151 | elif delta > 0: | 155 | elif delta > 0: | ||
152 | self.zoomIn() | 156 | self.zoomIn() | ||
153 | else: | 157 | else: | ||
154 | super(CodeEditor, self).wheelEvent(e) | 158 | super(CodeEditor, self).wheelEvent(e) | ||
159 | | ||||
155 | def keyPressEvent(self, e): | 160 | def keyPressEvent(self, e): | ||
156 | if (e.key() == Qt.Key_Tab): | 161 | if (e.key() == Qt.Key_Tab): | ||
157 | self.textCursor().insertText(" ") | 162 | self.indent() | ||
163 | elif e.key() == Qt.Key_Backtab: | ||||
164 | self.dedent() | ||||
158 | else: | 165 | else: | ||
159 | super(CodeEditor, self).keyPressEvent(e) | 166 | super(CodeEditor, self).keyPressEvent(e) | ||
167 | | ||||
168 | def isEmptyBlock(self, blockNumber): | ||||
169 | """ test whether block with number blockNumber contains any non-whitespace | ||||
170 | If only whitespace: return true, else return false""" | ||||
171 | | ||||
172 | # get block text | ||||
173 | cursor = self.textCursor() | ||||
174 | cursor.movePosition(QTextCursor.Start) | ||||
175 | cursor.movePosition(QTextCursor.NextBlock, n=blockNumber) | ||||
176 | cursor.movePosition(QTextCursor.StartOfLine) | ||||
177 | cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) | ||||
178 | text = cursor.selectedText() | ||||
179 | if text.strip() == "": | ||||
180 | return True | ||||
181 | else: | ||||
182 | return False | ||||
183 | | ||||
184 | | ||||
185 | def indent(self): | ||||
186 | # tab key has been pressed. Indent current line or selected block by self.indent_width | ||||
187 | | ||||
188 | cursor = self.textCursor() | ||||
189 | # is there a selection? | ||||
190 | | ||||
191 | selectionStart = cursor.selectionStart() | ||||
192 | selectionEnd = cursor.selectionEnd() | ||||
193 | | ||||
194 | if selectionStart == selectionEnd and cursor.atBlockEnd(): | ||||
195 | # ie no selection and don't insert in the middle of text | ||||
196 | # something smarter might skip whitespace and add a tab in front of | ||||
197 | # the next non whitespace character | ||||
198 | cursor.insertText(" "*self.indent_width) | ||||
199 | return | ||||
200 | | ||||
201 | cursor.setPosition(selectionStart) | ||||
202 | startBlock = cursor.blockNumber() | ||||
203 | cursor.setPosition(selectionEnd) | ||||
204 | endBlock = cursor.blockNumber() | ||||
205 | | ||||
206 | cursor.movePosition(QTextCursor.Start) | ||||
207 | cursor.movePosition(QTextCursor.NextBlock,n=startBlock) | ||||
208 | | ||||
209 | for i in range(0, endBlock-startBlock+1): | ||||
210 | if not self.isEmptyBlock(startBlock+i): # Don't insert whitespace on empty lines | ||||
211 | cursor.movePosition(QTextCursor.StartOfLine) | ||||
212 | cursor.insertText(" "*self.indent_width) | ||||
213 | | ||||
214 | cursor.movePosition(QTextCursor.NextBlock) | ||||
215 | | ||||
216 | # QT maintains separate cursors, so don't need to track or reset user's cursor | ||||
217 | | ||||
218 | | ||||
219 | def dedentBlock(self, blockNumber): | ||||
220 | # dedent the line at blockNumber | ||||
221 | cursor = self.textCursor() | ||||
222 | cursor.movePosition(QTextCursor.Start) | ||||
223 | cursor.movePosition(QTextCursor.NextBlock, n=blockNumber) | ||||
224 | | ||||
225 | for i in range(self.indent_width): | ||||
226 | cursor.movePosition(QTextCursor.StartOfLine) | ||||
227 | cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) | ||||
228 | if cursor.selectedText() == " ": # need to test each char | ||||
229 | cursor.removeSelectedText() | ||||
230 | else: | ||||
231 | break # stop deleting! | ||||
232 | | ||||
233 | return | ||||
234 | | ||||
235 | def dedent(self): | ||||
236 | cursor = self.textCursor() | ||||
237 | selectionStart = cursor.selectionStart() | ||||
238 | selectionEnd = cursor.selectionEnd() | ||||
239 | | ||||
240 | cursor.setPosition(selectionStart) | ||||
241 | startBlock = cursor.blockNumber() | ||||
242 | cursor.setPosition(selectionEnd) | ||||
243 | endBlock = cursor.blockNumber() | ||||
244 | | ||||
245 | if endBlock < startBlock: | ||||
246 | startBlock, endBlock = endBlock, startBlock | ||||
247 | | ||||
248 | for blockNumber in range(startBlock, endBlock+1): | ||||
249 | self.dedentBlock(blockNumber) | ||||
160 | 250 | | |||
251 | | ||||
161 | @property | 252 | @property | ||
162 | def font(self): | 253 | def font(self): | ||
163 | return self._font | 254 | return self._font | ||
164 | 255 | | |||
165 | @font.setter | 256 | @font.setter | ||
166 | def font(self, font = "Monospace"): | 257 | def font(self, font = "Monospace"): | ||
167 | self._font = font | 258 | self._font = font | ||
168 | self.setFont(QFont(font, self.fontInfo().pointSize())) | 259 | self.setFont(QFont(font, self.fontInfo().pointSize())) | ||
Show All 13 Lines |