Skip to content

Commit 6aa90a7

Browse files
author
timmyliang
committed
refactor(MMenu): clean up menu code
1 parent 3cb5c71 commit 6aa90a7

File tree

1 file changed

+60
-50
lines changed

1 file changed

+60
-50
lines changed

dayu_widgets/menu.py

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131

3232
@property_mixin
33-
class ScrollableMenuMixin(object):
33+
class ScrollableMenuBase(QtWidgets.QMenu):
3434
"""
3535
https://www.pythonfixing.com/2021/10/fixed-how-to-have-scrollable-context.html
3636
"""
@@ -40,7 +40,7 @@ class ScrollableMenuMixin(object):
4040
ignoreAutoScroll = False
4141

4242
def __init__(self, *args, **kwargs):
43-
super(ScrollableMenuMixin, self).__init__(*args, **kwargs)
43+
super(ScrollableMenuBase, self).__init__(*args, **kwargs)
4444
self._maximumHeight = self.maximumHeight()
4545
self._actionRects = []
4646

@@ -53,15 +53,15 @@ def __init__(self, *args, **kwargs):
5353
self.setMaxItemCount(0)
5454

5555
def _set_max_scroll_count(self, value):
56-
self.setMaxItemCount(value)
56+
self.setMaxItemCount(value * 2.2)
5757

5858
@property
5959
def actionRects(self):
6060
if self.dirty or not self._actionRects:
6161
del self._actionRects[:]
6262
offset = self.offset()
6363
for action in self.actions():
64-
geo = super(ScrollableMenuMixin, self).actionGeometry(action)
64+
geo = super(ScrollableMenuBase, self).actionGeometry(action)
6565
if offset:
6666
geo.moveTop(geo.y() - offset)
6767
self._actionRects.append(geo)
@@ -105,7 +105,10 @@ def scrollHeight(self, style):
105105
return style.pixelMetric(style.PM_MenuScrollerHeight, None, self) * 2
106106

107107
def isScrollable(self):
108-
return self.height() < super(ScrollableMenuMixin, self).sizeHint().height()
108+
return (
109+
self.property("scrollable")
110+
and self.height() < super(ScrollableMenuBase, self).sizeHint().height()
111+
)
109112

110113
def checkScroll(self):
111114
pos = self.mapFromGlobal(QtGui.QCursor.pos())
@@ -174,7 +177,7 @@ def actionAt(self, pos):
174177
# class methods reimplementation
175178

176179
def sizeHint(self):
177-
hint = super(ScrollableMenuMixin, self).sizeHint()
180+
hint = super(ScrollableMenuBase, self).sizeHint()
178181
if hint.height() > self.maximumHeight():
179182
hint.setHeight(self.maximumHeight())
180183
return hint
@@ -188,16 +191,16 @@ def eventFilter(self, source, event):
188191
delta = rect.topLeft() - self.actionGeometry(action).topLeft()
189192
source.move(source.pos() + delta)
190193
return False
191-
return super(ScrollableMenuMixin, self).eventFilter(source, event)
194+
return super(ScrollableMenuBase, self).eventFilter(source, event)
192195

193196
def event(self, event):
194197
if not self.isScrollable():
195-
return super(ScrollableMenuMixin, self).event(event)
198+
return super(ScrollableMenuBase, self).event(event)
196199
if event.type() == event.KeyPress and event.key() in (
197200
QtCore.Qt.Key_Up,
198201
QtCore.Qt.Key_Down,
199202
):
200-
res = super(ScrollableMenuMixin, self).event(event)
203+
res = super(ScrollableMenuBase, self).event(event)
201204
action = self.activeAction()
202205
if action:
203206
self.ensureVisible(action)
@@ -225,16 +228,16 @@ def event(self, event):
225228
action.trigger()
226229
self.close()
227230
return True
228-
return super(ScrollableMenuMixin, self).event(event)
231+
return super(ScrollableMenuBase, self).event(event)
229232

230233
def timerEvent(self, event):
231234
if not self.isScrollable():
232235
# ignore internal timer event for reopening popups
233-
super(ScrollableMenuMixin, self).timerEvent(event)
236+
super(ScrollableMenuBase, self).timerEvent(event)
234237

235238
def mouseMoveEvent(self, event):
236239
if not self.isScrollable():
237-
super(ScrollableMenuMixin, self).mouseMoveEvent(event)
240+
super(ScrollableMenuBase, self).mouseMoveEvent(event)
238241
return
239242

240243
pos = event.pos()
@@ -295,16 +298,16 @@ def showEvent(self, event):
295298
if action.menu():
296299
action.menu().installEventFilter(self)
297300
self.ignoreAutoScroll = False
298-
super(ScrollableMenuMixin, self).showEvent(event)
301+
super(ScrollableMenuBase, self).showEvent(event)
299302

300303
def hideEvent(self, event):
301304
for action in self.actions():
302305
if action.menu():
303306
action.menu().removeEventFilter(self)
304-
super(ScrollableMenuMixin, self).hideEvent(event)
307+
super(ScrollableMenuBase, self).hideEvent(event)
305308

306309
def resizeEvent(self, event):
307-
super(ScrollableMenuMixin, self).resizeEvent(event)
310+
super(ScrollableMenuBase, self).resizeEvent(event)
308311

309312
style = self.style()
310313
l, t, r, b = self.getContentsMargins()
@@ -329,7 +332,7 @@ def resizeEvent(self, event):
329332

330333
def paintEvent(self, event):
331334
if not self.isScrollable():
332-
super(ScrollableMenuMixin, self).paintEvent(event)
335+
super(ScrollableMenuBase, self).paintEvent(event)
333336
return
334337

335338
style = self.style()
@@ -345,7 +348,6 @@ def paintEvent(self, event):
345348
style.drawPrimitive(style.PE_PanelMenu, menuOpt, qp, self)
346349

347350
fw = style.pixelMetric(style.PM_MenuPanelWidth, None, self)
348-
349351
topEdge = self.scrollUpRect.bottom()
350352
bottomEdge = self.scrollDownRect.top()
351353
offset = self.offset()
@@ -432,7 +434,31 @@ def paintEvent(self, event):
432434

433435

434436
@property_mixin
435-
class SearchableMenuMixin(object):
437+
class SearchableMenuBase(ScrollableMenuBase):
438+
def __init__(self, *args, **kwargs):
439+
super(SearchableMenuBase, self).__init__(*args, **kwargs)
440+
self.search_popup = MPopup(self)
441+
self.search_popup.setVisible(False)
442+
self.search_bar = MLineEdit(self)
443+
self.search_label = QtWidgets.QLabel()
444+
445+
self.search_bar.textChanged.connect(self.slot_search_change)
446+
self.search_bar.keyPressEvent = partial(
447+
self.search_key_event, self.search_bar.keyPressEvent
448+
)
449+
self.aboutToHide.connect(lambda: self.search_bar.setText(""))
450+
451+
layout = QtWidgets.QVBoxLayout()
452+
layout.addWidget(self.search_label)
453+
layout.addWidget(self.search_bar)
454+
self.search_popup.setLayout(layout)
455+
456+
self.setProperty("search_placeholder", self.tr("Search Action..."))
457+
self.setProperty("search_label", self.tr("Search Action..."))
458+
459+
self.setProperty("searchable", True)
460+
self.setProperty("search_re", "I")
461+
436462
def search_key_event(self, call, event):
437463
key = event.key()
438464
# NOTES: support menu original key event on search bar
@@ -442,42 +468,26 @@ def search_key_event(self, call, event):
442468
QtCore.Qt.Key_Return,
443469
QtCore.Qt.Key_Enter,
444470
):
445-
super(SearchableMenuMixin, self).keyPressEvent(event)
471+
super(SearchableMenuBase, self).keyPressEvent(event)
446472
elif key == QtCore.Qt.Key_Tab:
447473
self.search_bar.setFocus()
448474
return call(event)
449475

450-
def search(self):
451-
self.setStyleSheet("QMenu{menu-scrollable: 1;}")
452-
self.setProperty("search", True)
453-
self.search_popup = MPopup(self)
454-
layout = QtWidgets.QVBoxLayout()
455-
456-
self.search_bar = MLineEdit(self)
457-
self.search_bar.keyPressEvent = partial(
458-
self.search_key_event, self.search_bar.keyPressEvent
459-
)
460-
self.search_bar.setPlaceholderText(self.tr("Search Action..."))
461-
self.search_bar.textChanged.connect(self.slot_search_change)
462-
self.search_label = QtWidgets.QLabel(self.tr("Search Action..."))
463-
self.search_label.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
464-
layout.addWidget(self.search_label)
465-
layout.addWidget(self.search_bar)
466-
self.search_popup.setLayout(layout)
467-
468-
self.aboutToHide.connect(lambda: self.search_bar.setText(""))
469-
470-
def _set_search(self, value):
471-
value and self.search()
472-
473476
def _set_search_label(self, value):
474-
self.property("search") and self.search_label.setText(value)
477+
self.search_label.setText(value)
475478

476479
def _set_search_placeholder(self, value):
477-
self.property("search") and self.search_bar.setPlaceholderText(value)
480+
self.search_bar.setPlaceholderText(value)
481+
482+
def _set_search_re(self, value):
483+
if not isinstance(value, six.text_type):
484+
raise TypeError("`search_re` property should be a string type")
478485

479486
def slot_search_change(self, text):
480-
search_reg = re.compile(r".*%s.*" % text)
487+
flags = 0
488+
for m in self.property("search_re") or "":
489+
flags |= getattr(re, m.upper(), 0)
490+
search_reg = re.compile(r".*%s.*" % text, flags)
481491
self._update_search(search_reg)
482492

483493
def _update_search(self, search_reg, parent_menu=None):
@@ -498,26 +508,26 @@ def _update_search(self, search_reg, parent_menu=None):
498508

499509
def keyPressEvent(self, event):
500510
key = event.key()
501-
if self.property("search"):
511+
if self.property("searchable"):
502512
# NOTES(timmyliang): 26 character trigger search bar
503513
if 65 <= key <= 90:
504514
char = chr(key)
505515
self.search_bar.setText(char)
506516
self.search_bar.setFocus()
507517
self.search_bar.selectAll()
508-
self.search_popup.show()
509518
width = self.sizeHint().width()
510519
width = width if width >= 50 else 50
511520
offset = QtCore.QPoint(width, 0)
512521
self.search_popup.move(self.pos() + offset)
522+
self.search_popup.show()
513523
elif key == QtCore.Qt.Key_Escape:
514524
self.search_bar.setText("")
515-
self.search_bar.hide()
516-
return super(SearchableMenuMixin, self).keyPressEvent(event)
525+
self.search_popup.hide()
526+
return super(SearchableMenuBase, self).keyPressEvent(event)
517527

518528

519529
@property_mixin
520-
class MMenu(SearchableMenuMixin, ScrollableMenuMixin, QtWidgets.QMenu):
530+
class MMenu(SearchableMenuBase):
521531
sig_value_changed = QtCore.Signal(object)
522532

523533
def __init__(self, exclusive=True, cascader=False, title="", parent=None):

0 commit comments

Comments
 (0)