Krita Source Code Documentation
Loading...
Searching...
No Matches
widget.py
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4#
5
6import re
7import sys
8import os
9
10# I put the rope package into a ZIP-file to save space
11# and to keep everything clear
12path = os.path.dirname(os.path.abspath(__file__))
13sys.path.insert(0, os.path.join(path, "rope.zip"))
14
15
16from rope.base.project import get_no_project
17from rope.contrib.codeassist import code_assist
18
19try:
20 from PyQt6.QtCore import QCoreApplication, QLine, Qt
21 from PyQt6.QtGui import (QBrush, QColor, QFont, QKeyEvent, QTextBlockUserData,
22 QTextCursor, QPainter, QPalette, QPen)
23 from PyQt6.QtWidgets import (QApplication, QFrame, QHBoxLayout, QMessageBox,
24 QPlainTextEdit, QVBoxLayout, QWidget)
25except:
26 from PyQt5.QtCore import QCoreApplication, QLine, Qt
27 from PyQt5.QtGui import (QBrush, QColor, QFont, QKeyEvent, QTextBlockUserData,
28 QTextCursor, QPainter, QPalette, QPen)
29 from PyQt5.QtWidgets import (QApplication, QFrame, QHBoxLayout, QMessageBox,
30 QPlainTextEdit, QVBoxLayout, QWidget)
31
32from indenter import PythonCodeIndenter
33from assist import AutoComplete, CallTip
34
35from highlighter import PythonHighlighter, QtQmlHighlighter
36
37
38class EditorBlockData(QTextBlockUserData):
39
40 def __init__(self):
41 QTextBlockUserData.__init__(self)
42
43
44class RopeEditorWrapper(object):
45
46 def __init__(self, editview):
47 self.editview = editview
48
49 def length(self):
50 return self.editview.length()
51
52 def line_editor(self):
53 return self
54
55 def _get_block(self, line_no=None):
56 cursor = self.editview.textCursor()
57 row = cursor.blockNumber()
58 if line_no == None:
59 line_no = row
60 block = cursor.block()
61 while row > line_no:
62 block = block.previous()
63 row -= 1
64 while row < line_no:
65 block = block.next()
66 row += 1
67 return block
68
69 def get_line(self, line_no=None):
70 return unicode(self._get_block(line_no).text())
71
72 def indent_line(self, line_no, indent_length):
73 block = self._get_block(line_no)
74 cursor = QTextCursor(block)
75 cursor.joinPreviousEditBlock()
76 cursor.movePosition(QTextCursor.MoveOperation.StartOfBlock, QTextCursor.MoveMode.MoveAnchor)
77 if indent_length < 0:
78 for i in range(-indent_length):
79 cursor.deleteChar()
80 else:
81 cursor.insertText(" " * indent_length)
82 if indent_length:
83 cursor.movePosition(
84 QTextCursor.MoveOperation.StartOfBlock, QTextCursor.MoveMode.MoveAnchor)
85 line = unicode(cursor.block().text())
86 if len(line) and line[0] == " ":
87 cursor.movePosition(
88 QTextCursor.MoveOperation.NextWord, QTextCursor.MoveMode.MoveAnchor)
89 self.editview.setTextCursor(cursor)
90 cursor.endEditBlock()
91
92
93class EditorView(QPlainTextEdit):
94
95 def __init__(self, parent=None, text=None,
96 EditorHighlighterClass=PythonHighlighter,
97 indenter=PythonCodeIndenter):
98 QPlainTextEdit.__init__(self, parent)
99 self.setFrameStyle(QFrame.Shape.NoFrame)
100 self.setTabStopWidth(4)
101 self.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
102 font = QFont()
103 font.setFamily("lucidasanstypewriter")
104 font.setFixedPitch(True)
105 font.setPointSize(10)
106 self.setFont(font)
107 self.highlighter = EditorHighlighterClass(self)
108 if text:
109 self.setPlainText(text)
110 self.frame_style = self.frameStyle()
111 self.draw_line = True
112 self.print_width = self.fontMetrics().horizontalAdvance("x" * 78)
113 self.line_pen = QPen(QColor("lightgrey"))
114 self.last_row = self.last_col = -1
115 self.last_block = None
116 self.highlight_line = True
117 self.highlight_color = self.palette().highlight().color().light(175)
118 self.highlight_brush = QBrush(QColor(self.highlight_color))
121 # True if you want to catch Emacs keys in actions
122 self.disable_shortcuts = False
123
124 self.prj = get_no_project()
125 self.prj.root = None
126 self.calltip = CallTip(self)
128
129 def closeEvent(self, event):
130 self.calltip.close()
131 self.autocomplete.close()
132
133 def isModified(self):
134 return self.document().isModified()
135
136 def setModified(self, flag):
137 self.document().setModified(flag)
138
139 def length(self):
140 return self.document().blockCount()
141
142 def goto(self, line_no):
143 cursor = self.textCursor()
144 block = cursor.block()
145 row = cursor.blockNumber()
146 while row > line_no:
147 block = block.previous()
148 row -= 1
149 while row < line_no:
150 block = block.next()
151 row += 1
152 cursor = QTextCursor(block)
153 self.setTextCursor(cursor)
154
156 cursor = self.textCursor()
157 cursor.setPosition(0)
158 self.setTextCursor(cursor)
159
161 cursor = self.textCursor()
162 block = cursor.block()
163 while block.isValid():
164 last_block = block
165 block = block.next()
166 cursor.setPosition(last_block.position())
167 cursor.movePosition(
168 QTextCursor.MoveOperation.EndOfBlock, QTextCursor.MoveMode.MoveAnchor)
169 self.setTextCursor(cursor)
170
172 cursor = self.textCursor()
173 cursor.movePosition(
174 QTextCursor.MoveOperation.StartOfBlock, QTextCursor.MoveMode.MoveAnchor)
175 self.setTextCursor(cursor)
176
178 cursor = self.textCursor()
179 cursor.movePosition(
180 QTextCursor.MoveOperation.EndOfBlock, QTextCursor.MoveMode.MoveAnchor)
181 self.setTextCursor(cursor)
182
183 def highline(self, cursor):
184 self.viewport().update()
185
187 cursor = self.textCursor()
188 row, col = cursor.blockNumber(), cursor.columnNumber()
189 if self.last_row != row:
190 self.last_row = row
191 if self.highlight_line:
192 self.highline(cursor)
193 if col != self.last_col:
194 self.last_col = col
195 self.cursorPositionChanged.emit(row, col)
196
197 def _create_line(self):
198 x = self.print_width
199 self.line = QLine(x, 0, x, self.height())
200
201 def resizeEvent(self, event):
202 self._create_line()
203 QPlainTextEdit.resizeEvent(self, event)
204
205 def paintEvent(self, event):
206 painter = QPainter(self.viewport())
207 if self.highlight_line:
208 r = self.cursorRect()
209 r.setX(0)
210 r.setWidth(self.viewport().width())
211 painter.fillRect(r, self.highlight_brush)
212 if self.draw_line:
213 painter.setPen(self.line_pen)
214 painter.drawLine(self.line)
215 painter.end()
216 QPlainTextEdit.paintEvent(self, event)
217
218 def setDocument(self, document):
219 QPlainTextEdit.setDocument(self, document)
220 self.highlighter.setDocument(document)
221
222 def indent(self):
223 self.indenter.correct_indentation(self.textCursor().blockNumber())
224
225 def tab_pressed(self):
226 self.indent()
227
228 def dedent(self):
229 self.indenter.deindent(self.textCursor().blockNumber())
230
232 self.dedent()
233 return True
234
236 cursor = self.textCursor()
237 text = unicode(cursor.block().text())
238 col = cursor.columnNumber()
239 if col > 0 and text[:col].strip() == "":
240 self.indenter.deindent(self.textCursor().blockNumber())
241 return True
242
244 try:
245 items = code_assist(self.prj,
246 unicode(self.toPlainText()),
247 self.textCursor().position())
248 except Exception as e:
249 items = []
250 if items:
251 self.autocomplete.setItems(items)
252 self.autocomplete.show()
253
255 self.indenter.entering_new_line(self.textCursor().blockNumber())
256
257 def keyPressEvent(self, event):
258 if self.autocomplete.active:
259 if self.autocomplete.keyPressEvent(event):
260 return
261 elif self.calltip.active:
262 if self.calltip.keyPressEvent(event):
263 return
264
265 m = event.modifiers()
266 k = event.key()
267 t = event.text()
268 # Disable some shortcuts
269 if self.disable_shortcuts and \
270 m & Qt.KeyboardModifier.ControlModifier and k in [Qt.Key.Key_A, Qt.Key.Key_R,
271 Qt.Key.Key_C, Qt.Key.Key_K,
272 Qt.Key.Key_X, Qt.Key.Key_V,
273 Qt.Key.Key_Y, Qt.Key.Key_Z]:
274 new_ev = QKeyEvent(event.type(), k, m, t)
275 event.ignore()
276 QCoreApplication.postEvent(self.parent(), new_ev)
277 return
278 elif k == Qt.Key.Key_Tab:
279 if self.tab_pressed():
280 return
281 elif k == Qt.Key.Key_Backtab:
282 if self.backtab_pressed():
283 return
284 elif k == Qt.Key.Key_Backspace:
285 if self.backspace_pressed():
286 return
287 elif k == Qt.Key.Key_Period or \
288 (k == Qt.Key.Key_Space and event.modifiers() == Qt.KeyboardModifier.ControlModifier):
289 QPlainTextEdit.keyPressEvent(self, event)
291 return
292 elif k in [Qt.Key.Key_ParenLeft, Qt.Key.Key_BraceLeft, Qt.Key.Key_BracketLeft]:
293 QPlainTextEdit.keyPressEvent(self, event)
294 self.paren_opened(k)
295 return
296 QPlainTextEdit.keyPressEvent(self, event)
297 if k == Qt.Key.Key_Return or k == Qt.Key.Key_Enter:
299
300 def paren_opened(self, key):
301 close_char = {
302 Qt.Key.Key_ParenLeft: ")",
303 Qt.Key.Key_BraceLeft: " }",
304 Qt.Key.Key_BracketLeft: "]"
305 }
306 cursor = self.textCursor()
307 cursor.insertText(close_char[key])
308 cursor.setPosition(cursor.position() - 1)
309 self.setTextCursor(cursor)
310
311
312class EditorSidebar(QWidget):
313
314 def __init__(self, editor):
315 QWidget.__init__(self, editor)
316 self.editor = editor
317 self.view = editor.view
318 self.doc = editor.view.document
319 self.fm = self.fontMetrics()
321
322 self.setAutoFillBackground(True)
323 # bg = editor.view.palette().base().color()
324 # pal = QPalette()
325 # pal.setColor(self.backgroundRole(), bg)
326 # self.setPalette(pal)
327 self.setBackgroundRole(QPalette.ColorRole.Base)
328
329 self.doc().documentLayout().update.connect(self.update)
330 self.view.verticalScrollBar().valueChanged.connect(self.update)
331 self.first_row = self.last_row = self.rows = 0
332 width = 10
333 if self.show_line_numbers:
334 width += self.fm.width("00000")
335 self.setFixedWidth(width)
336
337 def paintEvent(self, event):
338 QWidget.paintEvent(self, event)
339 p = QPainter(self)
340 view = self.view
341 first = view.firstVisibleBlock()
342 first_row = first.blockNumber()
343 block = first
344 row = first_row
345 y = view.contentOffset().y()
346 pageBottom = max(
347 view.height(),
348 view.verticalScrollBar().value() + view.viewport().height())
349 fm = self.fm
350 w = self.width() - 8
351 while block.isValid():
352 txt = str(row).rjust(5)
353 y = view.blockBoundingGeometry(block).y()
354 if y >= pageBottom:
355 break
356 x = w - fm.width(txt)
357 p.drawText(x, y, txt)
358 row += 1
359 block = block.next()
360 p.end()
361
362
363class EditorWidget(QFrame):
364
365 def __init__(self, parent=None, text=None,
366 EditorSidebarClass=EditorSidebar,
367 EditorViewClass=EditorView):
368 QFrame.__init__(self, parent)
369 self.view = EditorViewClass(self, text)
370 self.sidebar = EditorSidebarClass(self)
371 self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Sunken)
372 self.setLineWidth(2)
373 self.vlayout = QVBoxLayout()
374 self.vlayout.setSpacing(0)
375 self.setLayout(self.vlayout)
376 self.hlayout = QHBoxLayout()
377 self.vlayout.addLayout(self.hlayout)
378 self.hlayout.addWidget(self.sidebar)
379 self.hlayout.addWidget(self.view)
380 self.vlayout.setContentsMargins(2, 2, 2, 2)
381
382 def setPlainText(self, text):
383 self.view.document().setPlainText(text)
384 self.view.setModified(False)
385
386 def isModified(self):
387 return self.view.document().isModified()
388
389 def toPlainText(self):
390 return unicode(self.view.document().toPlainText())
391
392 def setModified(self, flag):
393 self.view.document().setModified(flag)
394
395
397 pass
398
399
400class QtQmlEditorWidget(QPlainTextEdit):
401
402 def __init__(self, parent):
403 QPlainTextEdit.__init__(self, parent)
405
406
407class SaveDialog(QMessageBox):
408
409 def __init__(self, msg):
410 QMessageBox.__init__(self)
411 self.setWindowTitle("Save")
412 self.setText(msg)
413 self.setStandardButtons(QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Discard | QMessageBox.StandardButton.Cancel)
414 self.setDefaultButton(QMessageBox.StandardButton.Save)
415
416
417if __name__ == "__main__":
418 if __file__ == "<stdin>":
419 __file__ = "./widget.py"
420 import sys
421 app = QApplication(sys.argv)
422 src = open(__file__).read()
423 edit = EditorWidget(text=src)
424 edit.resize(640, 480)
425 edit.show()
426 app.exec()
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
setDocument(self, document)
Definition widget.py:218
__init__(self, parent=None, text=None, EditorHighlighterClass=PythonHighlighter, indenter=PythonCodeIndenter)
Definition widget.py:97
__init__(self, parent=None, text=None, EditorSidebarClass=EditorSidebar, EditorViewClass=EditorView)
Definition widget.py:367
indent_line(self, line_no, indent_length)
Definition widget.py:72