src/eric7/QScintilla/EditorButtonsWidget.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a widget containing various buttons for accessing
8 editor actions.
9 """
10
11 import contextlib
12
13 from PyQt6.QtCore import pyqtSlot, Qt
14 from PyQt6.QtWidgets import (
15 QWidget, QVBoxLayout, QToolButton, QFrame, QMenu, QSizePolicy, QScrollArea
16 )
17
18 import UI.PixmapCache
19 import Preferences
20
21 from . import MarkupProviders
22
23
24 class EditorButtonsWidget(QWidget):
25 """
26 Class implementing a widget containing various buttons for accessing
27 editor actions.
28 """
29 def __init__(self, editor, parent=None):
30 """
31 Constructor
32
33 @param editor reference to the editor
34 @type Editor
35 @param parent reference to the parent widget
36 @type QWidget
37 """
38 super().__init__(parent)
39
40 margin = 2
41 spacing = 3
42
43 self.__buttonsWidget = QWidget(self)
44
45 self.__layout = QVBoxLayout(self.__buttonsWidget)
46 self.__layout.setContentsMargins(0, 0, 0, 0)
47 self.__layout.setSpacing(spacing)
48
49 self.__provider = None
50
51 self.__editor = editor
52 self.__editor.languageChanged.connect(self.__updateButtonStates)
53 self.__editor.editorSaved.connect(self.__updateButtonStates)
54 self.__editor.editorRenamed.connect(self.__updateButtonStates)
55 self.__editor.selectionChanged.connect(self.__editorSelectionChanged)
56 self.__editor.settingsRead.connect(self.__editorSettingsRead)
57
58 self.__createButtons()
59
60 self.__layout.addStretch()
61
62 self.__outerLayout = QVBoxLayout(self)
63 self.__outerLayout.setContentsMargins(margin, margin, margin, margin)
64 self.__outerLayout.setSpacing(spacing)
65 self.__outerLayout.setAlignment(Qt.AlignmentFlag.AlignHCenter)
66
67 self.__upButton = QToolButton(self)
68 self.__upButton.setArrowType(Qt.ArrowType.UpArrow)
69 self.__upButton.setSizePolicy(
70 QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Minimum)
71 self.__upButton.setAutoRepeat(True)
72
73 self.__scroller = QScrollArea(self)
74 self.__scroller.setWidget(self.__buttonsWidget)
75 self.__scroller.setSizePolicy(
76 QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
77 self.__scroller.setFrameShape(QFrame.Shape.NoFrame)
78 self.__scroller.setVerticalScrollBarPolicy(
79 Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
80 self.__scroller.setHorizontalScrollBarPolicy(
81 Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
82 self.__scroller.setWidgetResizable(False)
83
84 self.__downButton = QToolButton(self)
85 self.__downButton.setArrowType(Qt.ArrowType.DownArrow)
86 self.__downButton.setSizePolicy(
87 QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Minimum)
88 self.__downButton.setAutoRepeat(True)
89
90 self.__outerLayout.addWidget(self.__upButton)
91 self.__outerLayout.addWidget(self.__scroller)
92 self.__outerLayout.addWidget(self.__downButton)
93
94 self.__upButton.clicked.connect(self.__slideUp)
95 self.__downButton.clicked.connect(self.__slideDown)
96
97 self.setMaximumWidth(
98 self.__buttons["bold"].sizeHint().width() + 2 * margin)
99
100 self.__updateButtonStates()
101
102 #######################################################################
103 ## Methods below implement some event handlers
104 #######################################################################
105
106 def show(self):
107 """
108 Public slot to show the widget.
109 """
110 super().show()
111 self.__enableScrollerButtons()
112
113 def resizeEvent(self, evt):
114 """
115 Protected method to handle resize events.
116
117 @param evt reference to the resize event (QResizeEvent)
118 """
119 self.__enableScrollerButtons()
120 super().resizeEvent(evt)
121
122 #######################################################################
123 ## Methods below implement scroller related functions
124 #######################################################################
125
126 def __enableScrollerButtons(self):
127 """
128 Private method to set the enabled state of the scroll buttons.
129 """
130 scrollBar = self.__scroller.verticalScrollBar()
131 self.__upButton.setEnabled(scrollBar.value() > 0)
132 self.__downButton.setEnabled(scrollBar.value() < scrollBar.maximum())
133
134 def __slideUp(self):
135 """
136 Private slot to move the widget upwards, i.e. show contents to the
137 bottom.
138 """
139 self.__slide(True)
140
141 def __slideDown(self):
142 """
143 Private slot to move the widget downwards, i.e. show contents to
144 the top.
145 """
146 self.__slide(False)
147
148 def __slide(self, up):
149 """
150 Private method to move the sliding widget.
151
152 @param up flag indicating to move upwards (boolean)
153 """
154 scrollBar = self.__scroller.verticalScrollBar()
155 stepSize = scrollBar.singleStep()
156 if up:
157 stepSize = -stepSize
158 newValue = scrollBar.value() + stepSize
159 if newValue < 0:
160 newValue = 0
161 elif newValue > scrollBar.maximum():
162 newValue = scrollBar.maximum()
163 scrollBar.setValue(newValue)
164 self.__enableScrollerButtons()
165
166 #######################################################################
167 ## Methods below implement the format button functions
168 #######################################################################
169
170 def __createButtons(self):
171 """
172 Private slot to create the various tool buttons.
173 """
174 self.__buttons = {}
175 self.__separators = []
176 self.__headerMenu = QMenu()
177
178 self.__addButton("bold", "formatTextBold",
179 self.tr("Bold"))
180 self.__addButton("italic", "formatTextItalic",
181 self.tr("Italic"))
182 self.__addButton("strikethrough", "formatTextStrikethrough",
183 self.tr("Strike Through"))
184 self.__addSeparator()
185 self.__addButton("header1", "formatTextHeader1",
186 self.tr("Header 1"))
187 self.__addButton("header2", "formatTextHeader2",
188 self.tr("Header 2"))
189 self.__addButton("header3", "formatTextHeader3",
190 self.tr("Header 3"))
191 button = self.__addButton("header", "formatTextHeader",
192 self.tr("Header"))
193 button.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
194 button.setMenu(self.__headerMenu)
195 self.__addSeparator()
196 self.__addButton("code", "formatTextInlineCode",
197 self.tr("Inline Code"))
198 self.__addButton("codeBlock", "formatTextCodeBlock",
199 self.tr("Code Block"))
200 self.__addButton("quote", "formatTextQuote",
201 self.tr("Quote"))
202 self.__addSeparator()
203 self.__addButton("hyperlink", "formatTextHyperlink",
204 self.tr("Add Hyperlink"))
205 self.__addButton("line", "formatTextHorizontalLine",
206 self.tr("Add Horizontal Line"))
207 self.__addButton("image", "formatTextImage",
208 self.tr("Add Image"))
209 self.__addSeparator()
210 self.__addButton("bulletedList", "formatTextBulletedList",
211 self.tr("Add Bulleted List"))
212 self.__addButton("numberedList", "formatTextNumberedList",
213 self.tr("Add Numbered List"))
214
215 self.__headerMenu.triggered.connect(self.__headerMenuTriggered)
216
217 def __addButton(self, formatName, iconName, toolTip):
218 """
219 Private method to add a format button.
220
221 @param formatName unique name of the format
222 @type str
223 @param iconName name of the icon for the button
224 @type str
225 @param toolTip text for the tool tip
226 @type str
227 @return generated button
228 @rtype QToolButton
229 """
230 button = QToolButton(self.__buttonsWidget)
231 button.setIcon(UI.PixmapCache.getIcon(iconName))
232 button.setToolTip(toolTip)
233 button.clicked.connect(lambda: self.__formatClicked(formatName))
234 self.__layout.addWidget(button)
235 self.__buttons[formatName] = button
236
237 return button
238
239 def __addSeparator(self):
240 """
241 Private method to add a separator line.
242 """
243 line = QFrame(self.__buttonsWidget)
244 line.setLineWidth(2)
245 if isinstance(self.__layout, QVBoxLayout):
246 line.setFrameShape(QFrame.Shape.HLine)
247 else:
248 line.setFrameShape(QFrame.Shape.VLine)
249 line.setFrameShadow(QFrame.Shadow.Sunken)
250
251 self.__layout.addWidget(line)
252 self.__separators.append(line)
253
254 @pyqtSlot()
255 def __updateButtonStates(self):
256 """
257 Private slot to change the button states.
258 """
259 provider = MarkupProviders.getMarkupProvider(self.__editor)
260 if (
261 self.__provider is None or
262 provider.kind() != self.__provider.kind()
263 ):
264 self.__provider = provider
265
266 self.__buttons["bold"].setEnabled(self.__provider.hasBold())
267 self.__buttons["italic"].setEnabled(self.__provider.hasItalic())
268 self.__buttons["strikethrough"].setEnabled(
269 self.__provider.hasStrikethrough())
270
271 headerLevels = self.__provider.headerLevels()
272 self.__buttons["header1"].setEnabled(headerLevels >= 1)
273 self.__buttons["header2"].setEnabled(headerLevels >= 2)
274 self.__buttons["header3"].setEnabled(headerLevels >= 3)
275 self.__buttons["header"].setEnabled(headerLevels > 3)
276 self.__headerMenu.clear()
277 for level in range(1, headerLevels + 1):
278 act = self.__headerMenu.addAction(
279 self.tr("Level {0}").format(level))
280 act.setData("header{0}".format(level))
281
282 self.__buttons["code"].setEnabled(self.__provider.hasCode())
283 self.__buttons["codeBlock"].setEnabled(
284 self.__provider.hasCodeBlock())
285
286 self.__buttons["bulletedList"].setEnabled(
287 self.__provider.hasBulletedList())
288 self.__buttons["numberedList"].setEnabled(
289 self.__provider.hasNumberedList())
290
291 self.__editorSelectionChanged()
292
293 if Preferences.getEditor("HideFormatButtons"):
294 self.setVisible(self.__provider.kind() != "none")
295
296 def __formatClicked(self, formatName):
297 """
298 Private slot to handle a format button being clicked.
299
300 @param formatName format type of the button
301 @type str
302 """
303 if formatName == "bold":
304 self.__provider.bold(self.__editor)
305 elif formatName == "italic":
306 self.__provider.italic(self.__editor)
307 elif formatName == "strikethrough":
308 self.__provider.strikethrough(self.__editor)
309 elif formatName.startswith("header"):
310 with contextlib.suppress(ValueError):
311 level = int(formatName[-1])
312 self.__provider.header(self.__editor, level)
313 elif formatName == "code":
314 self.__provider.code(self.__editor)
315 elif formatName == "codeBlock":
316 self.__provider.codeBlock(self.__editor)
317 elif formatName == "quote":
318 self.__provider.quote(self.__editor)
319 elif formatName == "hyperlink":
320 self.__provider.hyperlink(self.__editor)
321 elif formatName == "line":
322 self.__provider.line(self.__editor)
323 elif formatName == "image":
324 self.__provider.image(self.__editor)
325 elif formatName == "bulletedList":
326 self.__provider.bulletedList(self.__editor)
327 elif formatName == "numberedList":
328 self.__provider.numberedList(self.__editor)
329
330 def __headerMenuTriggered(self, act):
331 """
332 Private method handling the selection of a header menu entry.
333
334 @param act action of the headers menu that was triggered
335 @type QAction
336 """
337 formatName = act.data()
338 self.__formatClicked(formatName)
339
340 def __editorSelectionChanged(self):
341 """
342 Private slot to handle a change of the editor's selection.
343 """
344 hasSelection = self.__editor.hasSelectedText()
345 if self.__provider:
346 self.__buttons["quote"].setEnabled(
347 self.__provider.hasQuote() and (
348 self.__provider.kind() == "html" or hasSelection
349 )
350 )
351 self.__buttons["hyperlink"].setEnabled(
352 self.__provider.hasHyperlink() and not hasSelection)
353 self.__buttons["line"].setEnabled(
354 self.__provider.hasLine() and not hasSelection)
355 self.__buttons["image"].setEnabled(
356 self.__provider.hasImage() and not hasSelection)
357
358 def __editorSettingsRead(self):
359 """
360 Private slot to handle a change of the editor related settings.
361 """
362 if Preferences.getEditor("HideFormatButtons"):
363 if self.__provider is not None:
364 self.setVisible(self.__provider.kind() != "none")
365 else:
366 self.setVisible(True)

eric ide

mercurial