class MNewTag(QWidget): sig_add_tag = Signal(str) def __init__(self, text='New Tag', parent=None): super(MNewTag, self).__init__(parent) self.setAttribute(Qt.WA_StyledBackground) self._add_button = MToolButton().tiny().svg( 'add_line.svg').text_beside_icon() self._add_button.setText(text) self._add_button.clicked.connect(self._slot_show_edit) self._line_edit = MLineEdit().tiny() self._line_edit.returnPressed.connect(self._slot_return_pressed) self._line_edit.setVisible(False) self._main_lay = QGridLayout() self._main_lay.setContentsMargins(3, 3, 3, 3) self._main_lay.addWidget(self._add_button, 0, 0) self._main_lay.addWidget(self._line_edit, 0, 0) self.setLayout(self._main_lay) def set_completer(self, completer): self._line_edit.setCompleter(completer) def _slot_show_edit(self): self._line_edit.setVisible(True) self._add_button.setVisible(False) self._line_edit.setFocus(Qt.MouseFocusReason) def _slot_return_pressed(self): self._line_edit.setVisible(False) self._add_button.setVisible(True) if self._line_edit.text(): self.sig_add_tag.emit(self._line_edit.text()) self._line_edit.clear() def focusOutEvent(self, *args, **kwargs): self._line_edit.setVisible(False) self._add_button.setVisible(True) return super(MNewTag, self).focusOutEvent(*args, **kwargs)
class SearchableMenuBase(ScrollableMenuBase): def __init__(self, *args, **kwargs): super(SearchableMenuBase, self).__init__(*args, **kwargs) self.search_popup = MPopup(self) self.search_popup.setVisible(False) self.search_bar = MLineEdit(self) self.search_label = QtWidgets.QLabel() self.search_bar.textChanged.connect(self.slot_search_change) self.search_bar.keyPressEvent = partial( self.search_key_event, self.search_bar.keyPressEvent ) self.aboutToHide.connect(lambda: self.search_bar.setText("")) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.search_label) layout.addWidget(self.search_bar) self.search_popup.setLayout(layout) self.setProperty("search_placeholder", self.tr("Search Action...")) self.setProperty("search_label", self.tr("Search Action...")) self.setProperty("searchable", True) self.setProperty("search_re", "I") def search_key_event(self, call, event): key = event.key() # NOTES: support menu original key event on search bar if key in ( QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, ): super(SearchableMenuBase, self).keyPressEvent(event) elif key == QtCore.Qt.Key_Tab: self.search_bar.setFocus() return call(event) def _set_search_label(self, value): self.search_label.setText(value) def _set_search_placeholder(self, value): self.search_bar.setPlaceholderText(value) def _set_search_re(self, value): if not isinstance(value, six.text_type): raise TypeError("`search_re` property should be a string type") def slot_search_change(self, text): flags = 0 for m in self.property("search_re") or "": flags |= getattr(re, m.upper(), 0) search_reg = re.compile(r".*%s.*" % text, flags) self._update_search(search_reg) def _update_search(self, search_reg, parent_menu=None): actions = parent_menu.actions() if parent_menu else self.actions() vis_list = [] for action in actions: menu = action.menu() if not menu: is_match = bool(re.match(search_reg, action.text())) action.setVisible(is_match) is_match and vis_list.append(action) else: is_match = bool(re.match(search_reg, menu.title())) self._update_search("" if is_match else search_reg, menu) if parent_menu: parent_menu.menuAction().setVisible(bool(vis_list) or not search_reg) def keyPressEvent(self, event): key = event.key() if self.property("searchable"): # NOTES(timmyliang): 26 character trigger search bar if 65 <= key <= 90: char = chr(key) self.search_bar.setText(char) self.search_bar.setFocus() self.search_bar.selectAll() width = self.sizeHint().width() width = width if width >= 50 else 50 offset = QtCore.QPoint(width, 0) self.search_popup.move(self.pos() + offset) self.search_popup.show() elif key == QtCore.Qt.Key_Escape: self.search_bar.setText("") self.search_popup.hide() return super(SearchableMenuBase, self).keyPressEvent(event)
class MNewTag(QWidget): """New Tag input component.""" sig_add_tag = Signal(str) def __init__(self, text="New Tag", parent=None): super(MNewTag, self).__init__(parent) self.setAttribute(Qt.WA_StyledBackground) self._add_button = MToolButton().text_beside_icon().small().svg( "add_line.svg") self._add_button.setText(text) self._add_button.clicked.connect(self._slot_show_edit) self._line_edit = MLineEdit().small() self._line_edit.returnPressed.connect(self._slot_return_pressed) self._line_edit.setVisible(False) self._line_edit.installEventFilter(self) self._main_lay = QGridLayout() self._main_lay.setContentsMargins(3, 3, 3, 3) self._main_lay.addWidget(self._add_button, 0, 0) self._main_lay.addWidget(self._line_edit, 0, 0) self.setLayout(self._main_lay) scale_x, _ = get_scale_factor() style = QssTemplate(""" MNewTag{ border: @border@unit dashed @border_color; } MNewTag MToolButton:hover{ border:none; } """) self.setStyleSheet( style.substitute( border_color=utils.fade_color(dayu_theme.secondary_text_color, "35%"), unit=dayu_theme.unit, border=1 * scale_x, )) def set_completer(self, completer): """Set the input completer""" self._line_edit.setCompleter(completer) def _slot_show_edit(self): self._line_edit.setVisible(True) self._add_button.setVisible(False) self._line_edit.setFocus(Qt.MouseFocusReason) def _slot_return_pressed(self): self._line_edit.setVisible(False) self._add_button.setVisible(True) if self._line_edit.text(): self.sig_add_tag.emit(self._line_edit.text()) self._line_edit.clear() self.update() def focusOutEvent(self, *args, **kwargs): """Override focusOutEvent to change the edit mode to button mode.""" self._line_edit.setVisible(False) self._add_button.setVisible(True) return super(MNewTag, self).focusOutEvent(*args, **kwargs) def eventFilter(self, widget, event): if widget is self._line_edit: if event.type() == QEvent.Type.KeyPress and event.key( ) == Qt.Key_Escape: self._line_edit.setVisible(False) self._add_button.setVisible(True) return super(MNewTag, self).eventFilter(widget, event)