Skip to content

Commit

Permalink
Merge pull request #1430 from freedomofpress/1351-checkbox-control
Browse files Browse the repository at this point in the history
Replace hide/show Passphrase icon control, with checkbox control
  • Loading branch information
gonzalo-bulnes authored May 19, 2022
2 parents b857c11 + 060656c commit ca67d95
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 16 deletions.
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()
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()
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

0 comments on commit ca67d95

Please sign in to comment.