eric6/QScintilla/EditorButtonsWidget.py

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

eric ide

mercurial