551 lines
17 KiB
Python
551 lines
17 KiB
Python
|
|
# coding: utf-8
|
||
|
|
from typing import List, Union
|
||
|
|
from PySide6.QtCore import QSize, Qt, QRectF, Signal, QPoint, QTimer, QEvent, QAbstractItemModel, Property, QModelIndex
|
||
|
|
from PySide6.QtGui import QPainter, QPainterPath, QIcon, QColor, QAction
|
||
|
|
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLineEdit, QToolButton, QTextEdit,
|
||
|
|
QPlainTextEdit, QCompleter, QStyle, QWidget, QTextBrowser)
|
||
|
|
|
||
|
|
|
||
|
|
from ...common.style_sheet import FluentStyleSheet, themeColor
|
||
|
|
from ...common.icon import isDarkTheme, FluentIconBase, drawIcon
|
||
|
|
from ...common.icon import FluentIcon as FIF
|
||
|
|
from ...common.font import setFont
|
||
|
|
from ...common.color import FluentSystemColor, autoFallbackThemeColor
|
||
|
|
from .tool_tip import ToolTipFilter
|
||
|
|
from .menu import LineEditMenu, TextEditMenu, RoundMenu, MenuAnimationType, IndicatorMenuItemDelegate
|
||
|
|
from .scroll_bar import SmoothScrollDelegate
|
||
|
|
|
||
|
|
|
||
|
|
class LineEditButton(QToolButton):
|
||
|
|
""" Line edit button """
|
||
|
|
|
||
|
|
def __init__(self, icon: Union[str, QIcon, FluentIconBase], parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self._icon = icon
|
||
|
|
self._action = None
|
||
|
|
self.isPressed = False
|
||
|
|
self.setFixedSize(31, 23)
|
||
|
|
self.setIconSize(QSize(10, 10))
|
||
|
|
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
||
|
|
self.setObjectName('lineEditButton')
|
||
|
|
FluentStyleSheet.LINE_EDIT.apply(self)
|
||
|
|
|
||
|
|
def setAction(self, action: QAction):
|
||
|
|
self._action = action
|
||
|
|
self._onActionChanged()
|
||
|
|
|
||
|
|
self.clicked.connect(action.trigger)
|
||
|
|
action.toggled.connect(self.setChecked)
|
||
|
|
action.changed.connect(self._onActionChanged)
|
||
|
|
|
||
|
|
self.installEventFilter(ToolTipFilter(self, 700))
|
||
|
|
|
||
|
|
def _onActionChanged(self):
|
||
|
|
action = self.action()
|
||
|
|
self.setIcon(action.icon())
|
||
|
|
self.setToolTip(action.toolTip())
|
||
|
|
self.setEnabled(action.isEnabled())
|
||
|
|
self.setCheckable(action.isCheckable())
|
||
|
|
self.setChecked(action.isChecked())
|
||
|
|
|
||
|
|
def action(self):
|
||
|
|
return self._action
|
||
|
|
|
||
|
|
def setIcon(self, icon: Union[str, FluentIconBase, QIcon]):
|
||
|
|
self._icon = icon
|
||
|
|
self.update()
|
||
|
|
|
||
|
|
def mousePressEvent(self, e):
|
||
|
|
self.isPressed = True
|
||
|
|
super().mousePressEvent(e)
|
||
|
|
|
||
|
|
def mouseReleaseEvent(self, e):
|
||
|
|
self.isPressed = False
|
||
|
|
super().mouseReleaseEvent(e)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
super().paintEvent(e)
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing |
|
||
|
|
QPainter.SmoothPixmapTransform)
|
||
|
|
|
||
|
|
iw, ih = self.iconSize().width(), self.iconSize().height()
|
||
|
|
w, h = self.width(), self.height()
|
||
|
|
rect = QRectF((w - iw)/2, (h - ih)/2, iw, ih)
|
||
|
|
|
||
|
|
if self.isPressed:
|
||
|
|
painter.setOpacity(0.7)
|
||
|
|
|
||
|
|
if isDarkTheme():
|
||
|
|
drawIcon(self._icon, painter, rect)
|
||
|
|
else:
|
||
|
|
drawIcon(self._icon, painter, rect, fill='#656565')
|
||
|
|
|
||
|
|
|
||
|
|
class LineEdit(QLineEdit):
|
||
|
|
""" Line edit """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self._isClearButtonEnabled = False
|
||
|
|
self._completer = None # type: QCompleter
|
||
|
|
self._completerMenu = None # type: CompleterMenu
|
||
|
|
self._isError = False
|
||
|
|
self.lightFocusedBorderColor = QColor()
|
||
|
|
self.darkFocusedBorderColor = QColor()
|
||
|
|
|
||
|
|
self.leftButtons = [] # type: List[LineEditButton]
|
||
|
|
self.rightButtons = [] # type: List[LineEditButton]
|
||
|
|
|
||
|
|
self.setProperty("transparent", True)
|
||
|
|
FluentStyleSheet.LINE_EDIT.apply(self)
|
||
|
|
self.setFixedHeight(33)
|
||
|
|
self.setAttribute(Qt.WA_MacShowFocusRect, False)
|
||
|
|
setFont(self)
|
||
|
|
|
||
|
|
self.hBoxLayout = QHBoxLayout(self)
|
||
|
|
self.clearButton = LineEditButton(FIF.CLOSE, self)
|
||
|
|
|
||
|
|
self.clearButton.setFixedSize(29, 25)
|
||
|
|
self.clearButton.hide()
|
||
|
|
|
||
|
|
self.hBoxLayout.setSpacing(3)
|
||
|
|
self.hBoxLayout.setContentsMargins(4, 4, 4, 4)
|
||
|
|
self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||
|
|
self.hBoxLayout.addWidget(self.clearButton, 0, Qt.AlignRight)
|
||
|
|
|
||
|
|
self.clearButton.clicked.connect(self.clear)
|
||
|
|
self.textChanged.connect(self.__onTextChanged)
|
||
|
|
self.textEdited.connect(self.__onTextEdited)
|
||
|
|
|
||
|
|
def isError(self):
|
||
|
|
return self._isError
|
||
|
|
|
||
|
|
def setError(self, isError: bool):
|
||
|
|
""" set the error status """
|
||
|
|
if isError == self.isError():
|
||
|
|
return
|
||
|
|
|
||
|
|
self._isError = isError
|
||
|
|
self.update()
|
||
|
|
|
||
|
|
def setCustomFocusedBorderColor(self, light, dark):
|
||
|
|
""" set the border color in focused status
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
light, dark: str | QColor | Qt.GlobalColor
|
||
|
|
border color in light/dark theme mode
|
||
|
|
"""
|
||
|
|
self.lightFocusedBorderColor = QColor(light)
|
||
|
|
self.darkFocusedBorderColor = QColor(dark)
|
||
|
|
self.update()
|
||
|
|
|
||
|
|
def focusedBorderColor(self):
|
||
|
|
if self.isError():
|
||
|
|
return FluentSystemColor.CRITICAL_FOREGROUND.color()
|
||
|
|
|
||
|
|
return autoFallbackThemeColor(self.lightFocusedBorderColor, self.darkFocusedBorderColor)
|
||
|
|
|
||
|
|
def setClearButtonEnabled(self, enable: bool):
|
||
|
|
self._isClearButtonEnabled = enable
|
||
|
|
self._adjustTextMargins()
|
||
|
|
|
||
|
|
def isClearButtonEnabled(self) -> bool:
|
||
|
|
return self._isClearButtonEnabled
|
||
|
|
|
||
|
|
def setCompleter(self, completer: QCompleter):
|
||
|
|
self._completer = completer
|
||
|
|
|
||
|
|
def completer(self):
|
||
|
|
return self._completer
|
||
|
|
|
||
|
|
def addAction(self, action: QAction, position=QLineEdit.ActionPosition.TrailingPosition):
|
||
|
|
QWidget.addAction(self, action)
|
||
|
|
|
||
|
|
button = LineEditButton(action.icon())
|
||
|
|
button.setAction(action)
|
||
|
|
button.setFixedWidth(29)
|
||
|
|
|
||
|
|
if position == QLineEdit.ActionPosition.LeadingPosition:
|
||
|
|
self.hBoxLayout.insertWidget(len(self.leftButtons), button, 0, Qt.AlignLeading)
|
||
|
|
if not self.leftButtons:
|
||
|
|
self.hBoxLayout.insertStretch(1, 1)
|
||
|
|
|
||
|
|
self.leftButtons.append(button)
|
||
|
|
else:
|
||
|
|
self.rightButtons.append(button)
|
||
|
|
self.hBoxLayout.addWidget(button, 0, Qt.AlignRight)
|
||
|
|
|
||
|
|
self._adjustTextMargins()
|
||
|
|
|
||
|
|
def addActions(self, actions, position=QLineEdit.ActionPosition.TrailingPosition):
|
||
|
|
for action in actions:
|
||
|
|
self.addAction(action, position)
|
||
|
|
|
||
|
|
def _adjustTextMargins(self):
|
||
|
|
left = len(self.leftButtons) * 30
|
||
|
|
right = len(self.rightButtons) * 30 + 28 * self.isClearButtonEnabled()
|
||
|
|
m = self.textMargins()
|
||
|
|
self.setTextMargins(left, m.top(), right, m.bottom())
|
||
|
|
|
||
|
|
def focusOutEvent(self, e):
|
||
|
|
super().focusOutEvent(e)
|
||
|
|
self.clearButton.hide()
|
||
|
|
|
||
|
|
def focusInEvent(self, e):
|
||
|
|
super().focusInEvent(e)
|
||
|
|
if self.isClearButtonEnabled():
|
||
|
|
self.clearButton.setVisible(bool(self.text()))
|
||
|
|
|
||
|
|
def __onTextChanged(self, text):
|
||
|
|
""" text changed slot """
|
||
|
|
if self.isClearButtonEnabled():
|
||
|
|
self.clearButton.setVisible(bool(text) and self.hasFocus())
|
||
|
|
|
||
|
|
def __onTextEdited(self, text):
|
||
|
|
if not self.completer():
|
||
|
|
return
|
||
|
|
|
||
|
|
if self.text():
|
||
|
|
QTimer.singleShot(50, self._showCompleterMenu)
|
||
|
|
elif self._completerMenu:
|
||
|
|
self._completerMenu.close()
|
||
|
|
|
||
|
|
def setCompleterMenu(self, menu):
|
||
|
|
""" set completer menu
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
menu: CompleterMenu
|
||
|
|
completer menu
|
||
|
|
"""
|
||
|
|
menu.activated.connect(self._completer.activated)
|
||
|
|
menu.indexActivated.connect(lambda idx: self._completer.activated[QModelIndex].emit(idx))
|
||
|
|
self._completerMenu = menu
|
||
|
|
|
||
|
|
def _showCompleterMenu(self):
|
||
|
|
if not self.completer() or not self.text():
|
||
|
|
return
|
||
|
|
|
||
|
|
# create menu
|
||
|
|
if not self._completerMenu:
|
||
|
|
self.setCompleterMenu(CompleterMenu(self))
|
||
|
|
|
||
|
|
# add menu items
|
||
|
|
self.completer().setCompletionPrefix(self.text())
|
||
|
|
changed = self._completerMenu.setCompletion(self.completer().completionModel(), self.completer().completionColumn())
|
||
|
|
self._completerMenu.setMaxVisibleItems(self.completer().maxVisibleItems())
|
||
|
|
|
||
|
|
# show menu
|
||
|
|
if changed:
|
||
|
|
self._completerMenu.popup()
|
||
|
|
|
||
|
|
def contextMenuEvent(self, e):
|
||
|
|
menu = LineEditMenu(self)
|
||
|
|
menu.exec(e.globalPos(), ani=True)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
super().paintEvent(e)
|
||
|
|
if not self.hasFocus():
|
||
|
|
return
|
||
|
|
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
||
|
|
painter.setPen(Qt.NoPen)
|
||
|
|
|
||
|
|
m = self.contentsMargins()
|
||
|
|
path = QPainterPath()
|
||
|
|
w, h = self.width()-m.left()-m.right(), self.height()
|
||
|
|
path.addRoundedRect(QRectF(m.left(), h-10, w, 10), 5, 5)
|
||
|
|
|
||
|
|
rectPath = QPainterPath()
|
||
|
|
rectPath.addRect(m.left(), h-10, w, 8)
|
||
|
|
path = path.subtracted(rectPath)
|
||
|
|
|
||
|
|
painter.fillPath(path, self.focusedBorderColor())
|
||
|
|
|
||
|
|
|
||
|
|
class CompleterMenu(RoundMenu):
|
||
|
|
""" Completer menu """
|
||
|
|
|
||
|
|
activated = Signal(str)
|
||
|
|
indexActivated = Signal(QModelIndex)
|
||
|
|
|
||
|
|
def __init__(self, lineEdit: LineEdit):
|
||
|
|
super().__init__()
|
||
|
|
self.items = []
|
||
|
|
self.indexes = []
|
||
|
|
self.lineEdit = lineEdit
|
||
|
|
|
||
|
|
self.view.setViewportMargins(0, 2, 0, 6)
|
||
|
|
self.view.setObjectName('completerListWidget')
|
||
|
|
self.view.setItemDelegate(IndicatorMenuItemDelegate())
|
||
|
|
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||
|
|
|
||
|
|
self.installEventFilter(self)
|
||
|
|
self.setItemHeight(33)
|
||
|
|
|
||
|
|
def setCompletion(self, model: QAbstractItemModel, column=0):
|
||
|
|
""" set the completion model """
|
||
|
|
items = []
|
||
|
|
self.indexes.clear()
|
||
|
|
for i in range(model.rowCount()):
|
||
|
|
items.append(model.data(model.index(i, column)))
|
||
|
|
self.indexes.append(model.index(i, column))
|
||
|
|
|
||
|
|
if self.items == items and self.isVisible():
|
||
|
|
return False
|
||
|
|
|
||
|
|
self.setItems(items)
|
||
|
|
return True
|
||
|
|
|
||
|
|
def setItems(self, items: List[str]):
|
||
|
|
""" set completion items """
|
||
|
|
self.view.clear()
|
||
|
|
|
||
|
|
self.items = items
|
||
|
|
self.view.addItems(items)
|
||
|
|
|
||
|
|
for i in range(self.view.count()):
|
||
|
|
item = self.view.item(i)
|
||
|
|
item.setSizeHint(QSize(1, self.itemHeight))
|
||
|
|
|
||
|
|
def _onItemClicked(self, item):
|
||
|
|
self._hideMenu(False)
|
||
|
|
self._onCompletionItemSelected(item.text(), self.view.row(item))
|
||
|
|
|
||
|
|
def eventFilter(self, obj, e: QEvent):
|
||
|
|
if e.type() != QEvent.KeyPress:
|
||
|
|
return super().eventFilter(obj, e)
|
||
|
|
|
||
|
|
# redirect input to line edit
|
||
|
|
self.lineEdit.event(e)
|
||
|
|
self.view.event(e)
|
||
|
|
|
||
|
|
if e.key() == Qt.Key_Escape:
|
||
|
|
self.close()
|
||
|
|
if e.key() in [Qt.Key_Enter, Qt.Key_Return] and self.view.currentRow() >= 0:
|
||
|
|
self._onCompletionItemSelected(self.view.currentItem().text(), self.view.currentRow())
|
||
|
|
self.close()
|
||
|
|
|
||
|
|
return super().eventFilter(obj, e)
|
||
|
|
|
||
|
|
def _onCompletionItemSelected(self, text, row):
|
||
|
|
self.lineEdit.setText(text)
|
||
|
|
self.activated.emit(text)
|
||
|
|
|
||
|
|
if 0 <= row < len(self.indexes):
|
||
|
|
self.indexActivated.emit(self.indexes[row])
|
||
|
|
|
||
|
|
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
|
||
|
|
return super().exec(pos, ani, aniType)
|
||
|
|
|
||
|
|
def popup(self):
|
||
|
|
""" show menu """
|
||
|
|
if not self.items:
|
||
|
|
return self.close()
|
||
|
|
|
||
|
|
# adjust menu size
|
||
|
|
p = self.lineEdit
|
||
|
|
if self.view.width() < p.width():
|
||
|
|
self.view.setMinimumWidth(p.width())
|
||
|
|
self.adjustSize()
|
||
|
|
|
||
|
|
# determine the animation type by choosing the maximum height of view
|
||
|
|
x = -self.width()//2 + self.layout().contentsMargins().left() + p.width()//2
|
||
|
|
y = p.height() - self.layout().contentsMargins().top() + 2
|
||
|
|
pd = p.mapToGlobal(QPoint(x, y))
|
||
|
|
hd = self.view.heightForAnimation(pd, MenuAnimationType.FADE_IN_DROP_DOWN)
|
||
|
|
|
||
|
|
pu = p.mapToGlobal(QPoint(x, 7))
|
||
|
|
hu = self.view.heightForAnimation(pu, MenuAnimationType.FADE_IN_PULL_UP)
|
||
|
|
|
||
|
|
if hd >= hu:
|
||
|
|
pos = pd
|
||
|
|
aniType = MenuAnimationType.FADE_IN_DROP_DOWN
|
||
|
|
else:
|
||
|
|
pos = pu
|
||
|
|
aniType = MenuAnimationType.FADE_IN_PULL_UP
|
||
|
|
|
||
|
|
self.view.adjustSize(pos, aniType)
|
||
|
|
|
||
|
|
# update border style
|
||
|
|
self.view.setProperty('dropDown', aniType == MenuAnimationType.FADE_IN_DROP_DOWN)
|
||
|
|
self.view.setStyle(QApplication.style())
|
||
|
|
|
||
|
|
self.adjustSize()
|
||
|
|
self.exec(pos, aniType=aniType)
|
||
|
|
|
||
|
|
# remove the focus of menu
|
||
|
|
self.view.setFocusPolicy(Qt.NoFocus)
|
||
|
|
self.setFocusPolicy(Qt.NoFocus)
|
||
|
|
p.setFocus()
|
||
|
|
|
||
|
|
|
||
|
|
class SearchLineEdit(LineEdit):
|
||
|
|
""" Search line edit """
|
||
|
|
|
||
|
|
searchSignal = Signal(str)
|
||
|
|
clearSignal = Signal()
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.searchButton = LineEditButton(FIF.SEARCH, self)
|
||
|
|
|
||
|
|
self.hBoxLayout.addWidget(self.searchButton, 0, Qt.AlignRight)
|
||
|
|
self.setClearButtonEnabled(True)
|
||
|
|
self.setTextMargins(0, 0, 59, 0)
|
||
|
|
|
||
|
|
self.searchButton.clicked.connect(self.search)
|
||
|
|
self.clearButton.clicked.connect(self.clearSignal)
|
||
|
|
|
||
|
|
def search(self):
|
||
|
|
""" emit search signal """
|
||
|
|
text = self.text().strip()
|
||
|
|
if text:
|
||
|
|
self.searchSignal.emit(text)
|
||
|
|
else:
|
||
|
|
self.clearSignal.emit()
|
||
|
|
|
||
|
|
def setClearButtonEnabled(self, enable: bool):
|
||
|
|
self._isClearButtonEnabled = enable
|
||
|
|
self.setTextMargins(0, 0, 28*enable+30, 0)
|
||
|
|
|
||
|
|
|
||
|
|
class EditLayer(QWidget):
|
||
|
|
""" Edit layer """
|
||
|
|
|
||
|
|
def __init__(self, parent):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.setAttribute(Qt.WA_TransparentForMouseEvents)
|
||
|
|
parent.installEventFilter(self)
|
||
|
|
|
||
|
|
def eventFilter(self, obj, e):
|
||
|
|
if obj is self.parent() and e.type() == QEvent.Resize:
|
||
|
|
self.resize(e.size())
|
||
|
|
|
||
|
|
return super().eventFilter(obj, e)
|
||
|
|
|
||
|
|
def paintEvent(self, e):
|
||
|
|
if not self.parent().hasFocus():
|
||
|
|
return
|
||
|
|
|
||
|
|
painter = QPainter(self)
|
||
|
|
painter.setRenderHints(QPainter.Antialiasing)
|
||
|
|
painter.setPen(Qt.NoPen)
|
||
|
|
|
||
|
|
m = self.contentsMargins()
|
||
|
|
path = QPainterPath()
|
||
|
|
w, h = self.width()-m.left()-m.right(), self.height()
|
||
|
|
path.addRoundedRect(QRectF(m.left(), h-10, w, 10), 5, 5)
|
||
|
|
|
||
|
|
rectPath = QPainterPath()
|
||
|
|
rectPath.addRect(m.left(), h-10, w, 7.5)
|
||
|
|
path = path.subtracted(rectPath)
|
||
|
|
|
||
|
|
painter.fillPath(path, themeColor())
|
||
|
|
|
||
|
|
|
||
|
|
class TextEdit(QTextEdit):
|
||
|
|
""" Text edit """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.layer = EditLayer(self)
|
||
|
|
self.scrollDelegate = SmoothScrollDelegate(self)
|
||
|
|
FluentStyleSheet.LINE_EDIT.apply(self)
|
||
|
|
setFont(self)
|
||
|
|
|
||
|
|
def contextMenuEvent(self, e):
|
||
|
|
menu = TextEditMenu(self)
|
||
|
|
menu.exec(e.globalPos(), ani=True)
|
||
|
|
|
||
|
|
|
||
|
|
class PlainTextEdit(QPlainTextEdit):
|
||
|
|
""" Plain text edit """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent=parent)
|
||
|
|
self.layer = EditLayer(self)
|
||
|
|
self.scrollDelegate = SmoothScrollDelegate(self)
|
||
|
|
FluentStyleSheet.LINE_EDIT.apply(self)
|
||
|
|
setFont(self)
|
||
|
|
|
||
|
|
def contextMenuEvent(self, e):
|
||
|
|
menu = TextEditMenu(self)
|
||
|
|
menu.exec(e.globalPos())
|
||
|
|
|
||
|
|
|
||
|
|
class TextBrowser(QTextBrowser):
|
||
|
|
""" Text browser """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.layer = EditLayer(self)
|
||
|
|
self.scrollDelegate = SmoothScrollDelegate(self)
|
||
|
|
FluentStyleSheet.LINE_EDIT.apply(self)
|
||
|
|
setFont(self)
|
||
|
|
|
||
|
|
def contextMenuEvent(self, e):
|
||
|
|
menu = TextEditMenu(self)
|
||
|
|
menu.exec(e.globalPos())
|
||
|
|
|
||
|
|
|
||
|
|
class PasswordLineEdit(LineEdit):
|
||
|
|
""" Password line edit """
|
||
|
|
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.viewButton = LineEditButton(FIF.VIEW, self)
|
||
|
|
|
||
|
|
self.setEchoMode(QLineEdit.Password)
|
||
|
|
self.setContextMenuPolicy(Qt.NoContextMenu)
|
||
|
|
self.hBoxLayout.addWidget(self.viewButton, 0, Qt.AlignRight)
|
||
|
|
self.setClearButtonEnabled(False)
|
||
|
|
|
||
|
|
self.viewButton.installEventFilter(self)
|
||
|
|
self.viewButton.setIconSize(QSize(13, 13))
|
||
|
|
self.viewButton.setFixedSize(29, 25)
|
||
|
|
|
||
|
|
def setPasswordVisible(self, isVisible: bool):
|
||
|
|
""" set the visibility of password """
|
||
|
|
if isVisible:
|
||
|
|
self.setEchoMode(QLineEdit.Normal)
|
||
|
|
else:
|
||
|
|
self.setEchoMode(QLineEdit.Password)
|
||
|
|
|
||
|
|
def isPasswordVisible(self):
|
||
|
|
return self.echoMode() == QLineEdit.Normal
|
||
|
|
|
||
|
|
def setClearButtonEnabled(self, enable: bool):
|
||
|
|
self._isClearButtonEnabled = enable
|
||
|
|
|
||
|
|
if self.viewButton.isHidden():
|
||
|
|
self.setTextMargins(0, 0, 28*enable, 0)
|
||
|
|
else:
|
||
|
|
self.setTextMargins(0, 0, 28*enable + 30, 0)
|
||
|
|
|
||
|
|
def setViewPasswordButtonVisible(self, isVisible: bool):
|
||
|
|
""" set the visibility of view password button """
|
||
|
|
self.viewButton.setVisible(isVisible)
|
||
|
|
|
||
|
|
def eventFilter(self, obj, e):
|
||
|
|
if obj is not self.viewButton or not self.isEnabled():
|
||
|
|
return super().eventFilter(obj, e)
|
||
|
|
|
||
|
|
if e.type() == QEvent.MouseButtonPress:
|
||
|
|
self.setPasswordVisible(True)
|
||
|
|
elif e.type() == QEvent.MouseButtonRelease:
|
||
|
|
self.setPasswordVisible(False)
|
||
|
|
|
||
|
|
return super().eventFilter(obj, e)
|
||
|
|
|
||
|
|
def inputMethodQuery(self, query: Qt.InputMethodQuery):
|
||
|
|
# Disable IME for PasswordLineEdit
|
||
|
|
if query == Qt.InputMethodQuery.ImEnabled:
|
||
|
|
return False
|
||
|
|
else:
|
||
|
|
return super().inputMethodQuery(query)
|
||
|
|
|
||
|
|
passwordVisible = Property(bool, isPasswordVisible, setPasswordVisible)
|