Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace hide/show Passphrase icon control, with checkbox control #1430

Merged
merged 9 commits into from
May 19, 2022
6 changes: 5 additions & 1 deletion securedrop_client/gui/auth/dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from securedrop_client.gui.auth.sign_in import LoginErrorBar, SignInButton
from securedrop_client.gui.auth.use_offline import LoginOfflineLink
from securedrop_client.gui.base import PasswordEdit
from securedrop_client.gui.base.checkbox import SDCheckBox
from securedrop_client.logic import Controller
from securedrop_client.resources import load_image

Expand Down Expand Up @@ -96,6 +97,9 @@ def __init__(self, parent: QWidget) -> None:
self.password_label = QLabel(_("Passphrase"))
self.password_field = PasswordEdit(self)

self.check = SDCheckBox()
self.check.checkbox.stateChanged.connect(self.password_field.on_toggle_password_Action)

self.tfa_label = QLabel(_("Two-Factor Code"))
self.tfa_field = QLineEdit()

Expand All @@ -117,7 +121,7 @@ def __init__(self, parent: QWidget) -> None:
form_layout.addWidget(QWidget(self))
form_layout.addWidget(self.password_label)
form_layout.addWidget(self.password_field)
form_layout.addWidget(QWidget(self))
form_layout.addWidget(self.check, alignment=Qt.AlignRight)
form_layout.addWidget(self.tfa_label)
form_layout.addWidget(self.tfa_field)
form_layout.addWidget(buttons)
Expand Down
18 changes: 18 additions & 0 deletions securedrop_client/gui/base/checkbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ShowPassphrase_widget QFrame{
padding: 5px 3px 3px 5px;
}
#ShowPassphrase_widget QLabel{
font-family: 'Source Sans Pro';
font-weight: 500;
font-size: 12px;
line-height: 15px;
padding: 0px 0px 0px 3px;
}

#ShowPassphrase_widget QFrame:hover {
background-color: rgba(230, 253, 255, 0.2);
}

#ShowPassphrase_widget QLabel:hover {
background-color: none;
}
51 changes: 51 additions & 0 deletions securedrop_client/gui/base/checkbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
SecureDrop customized passphrase checkbox control

A checkbox control created to toggle with hiding and showing PasswordEdit passphrases.
Consists of a QCheckBox and a QLabel positioned horizontally within a QFrame.
Present in the Sign-in and Export Dialog.
"""
from gettext import gettext as _

from pkg_resources import resource_string
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QCursor, QFont, QMouseEvent
from PyQt5.QtWidgets import QCheckBox, QFrame, QHBoxLayout, QLabel, QWidget


class SDCheckBox(QWidget):
clicked = pyqtSignal()
CHECKBOX_CSS = resource_string(__name__, "checkbox.css").decode("utf-8")
PASSPHRASE_LABEL_SPACING = 1

def __init__(self) -> None:
super().__init__()
self.setObjectName("ShowPassphrase_widget")
self.setStyleSheet(self.CHECKBOX_CSS)

font = QFont()
font.setLetterSpacing(QFont.AbsoluteSpacing, self.PASSPHRASE_LABEL_SPACING)

self.layout = QHBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.setLayout(self.layout)

self.frame = QFrame()
self.frame.setLayout(QHBoxLayout())
self.frame.layout().setContentsMargins(0, 0, 0, 0)
self.frame.layout().setSpacing(0)

self.checkbox = QCheckBox()
self.label = QLabel(_("Show Passphrase"))
self.label.setFont(font)

self.layout.addWidget(self.frame)
self.frame.layout().addWidget(self.checkbox)
self.frame.layout().addWidget(self.label)
self.frame.setCursor(QCursor(Qt.PointingHandCursor))

self.clicked.connect(self.checkbox.click)

def mousePressEvent(self, e: QMouseEvent) -> None:
self.clicked.emit()
gonzalo-bulnes marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 0 additions & 9 deletions securedrop_client/gui/base/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
"""
from PyQt5.QtWidgets import QDialog, QLineEdit

from securedrop_client.resources import load_icon


class PasswordEdit(QLineEdit):
"""
Expand All @@ -30,20 +28,13 @@ def __init__(self, parent: QDialog) -> None:
self.parent = parent
super().__init__(self.parent)

self.visibleIcon = load_icon("eye_visible.svg")
self.hiddenIcon = load_icon("eye_hidden.svg")

self.setEchoMode(QLineEdit.Password)
self.togglepasswordAction = self.addAction(self.hiddenIcon, QLineEdit.TrailingPosition)
self.togglepasswordAction.triggered.connect(self.on_toggle_password_Action)
self.password_shown = False

def on_toggle_password_Action(self) -> None:
if not self.password_shown:
self.setEchoMode(QLineEdit.Normal)
self.password_shown = True
self.togglepasswordAction.setIcon(self.visibleIcon)
else:
self.setEchoMode(QLineEdit.Password)
self.password_shown = False
self.togglepasswordAction.setIcon(self.hiddenIcon)
6 changes: 6 additions & 0 deletions securedrop_client/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
SvgPushButton,
SvgToggleButton,
)
from securedrop_client.gui.base.checkbox import SDCheckBox
from securedrop_client.gui.conversation import DeleteConversationDialog
from securedrop_client.gui.source import DeleteSourceDialog
from securedrop_client.logic import Controller
Expand Down Expand Up @@ -2683,8 +2684,13 @@ def __init__(self, controller: Controller, file_uuid: str, file_name: str) -> No
effect.setBlurRadius(4)
effect.setColor(QColor("#aaa"))
self.passphrase_field.setGraphicsEffect(effect)

check = SDCheckBox()
check.checkbox.stateChanged.connect(self.passphrase_field.on_toggle_password_Action)

passphrase_form_layout.addWidget(passphrase_label)
passphrase_form_layout.addWidget(self.passphrase_field)
passphrase_form_layout.addWidget(check, alignment=Qt.AlignRight)
self.body_layout.addWidget(self.passphrase_form)
self.passphrase_form.hide()

Expand Down
3 changes: 3 additions & 0 deletions securedrop_client/locale/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ msgstr ""
msgid "USE OFFLINE"
msgstr ""

msgid "Show Passphrase"
msgstr ""

msgid "CANCEL"
msgstr ""

Expand Down
8 changes: 5 additions & 3 deletions tests/gui/base/test_inputs.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from PyQt5.QtWidgets import QApplication, QLineEdit
from PyQt5.QtWidgets import QApplication, QCheckBox, QLineEdit

from securedrop_client.gui.base import PasswordEdit

app = QApplication([])


def test_PasswordEdit(mocker):
checkbox = QCheckBox()
passwordline = PasswordEdit(None)
passwordline.togglepasswordAction.trigger()

passwordline.on_toggle_password_Action()
checkbox.isChecked()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does checkbox.isChecked() do anything in this context? Was it meant to be an assertion, or maybe a debug statement?

assert passwordline.echoMode() == QLineEdit.Normal
passwordline.togglepasswordAction.trigger()
passwordline.on_toggle_password_Action()
assert passwordline.echoMode() == QLineEdit.Password
15 changes: 15 additions & 0 deletions tests/gui/base/test_sdcheckbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from PyQt5.QtTest import QSignalSpy
from PyQt5.QtWidgets import QApplication

from securedrop_client.gui.base.checkbox import SDCheckBox

app = QApplication([])


def test_SDCheckBox():
checkbox_area = SDCheckBox()
signal_emissions = QSignalSpy(checkbox_area.clicked)

checkbox_area.mousePressEvent(None)

assert len(signal_emissions) == 1
6 changes: 3 additions & 3 deletions tests/integration/test_styles_sdclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ def test_styles_for_login_dialog(mocker, main_window):
form = login_dialog.layout().itemAt(2).widget()
form_children_qlabel = form.findChildren(QLabel)
for c in form_children_qlabel:
assert "Montserrat" == c.font().family()
assert "Montserrat" == c.font().family() or "Source Sans Pro" == c.font().family()
assert QFont.DemiBold - 1 == c.font().weight()
assert 13 == c.font().pixelSize()
assert 13 == c.font().pixelSize() or 12 == c.font().pixelSize()
assert "#ffffff" == c.palette().color(QPalette.Foreground).name()
form_children_qlineedit = form.findChildren(QLineEdit)
for c in form_children_qlineedit:
Expand Down Expand Up @@ -550,7 +550,7 @@ def test_styles_for_export_dialog(export_dialog):

passphrase_children_qlabel = export_dialog.passphrase_form.findChildren(QLabel)
for c in passphrase_children_qlabel:
assert "Montserrat" == c.font().family()
assert "Montserrat" == c.font().family() or "Source Sans Pro" == c.font().family()
assert QFont.DemiBold - 1 == c.font().weight()
assert 12 == c.font().pixelSize()
assert "#2a319d" == c.palette().color(QPalette.Foreground).name()
Expand Down