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

Elide source designation at lower window widths #1145

Merged
merged 2 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions securedrop_client/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ def get_elided_text(self, full_text: str) -> str:
full_text = full_text.split("\n", 1)[0]

fm = self.fontMetrics()
filename_width = fm.horizontalAdvance(full_text)
if filename_width > self.max_length:
px_width = fm.horizontalAdvance(full_text)
if px_width > self.max_length:
elided_text = ""
for c in full_text:
if fm.horizontalAdvance(elided_text) > self.max_length:
Expand Down
34 changes: 31 additions & 3 deletions securedrop_client/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3133,8 +3133,25 @@ def setText(self, text):
self.placeholder.hide()
super(ReplyTextEdit, self).setPlainText(text)

def resizeEvent(self, event):
Copy link
Contributor

Choose a reason for hiding this comment

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

hooking into the resize event works as expected

# Adjust available source label width to elide text when necessary
self.placeholder.update_label_width(event.size().width())
super().resizeEvent(event)


class ReplyTextEditPlaceholder(QWidget):

# These values are used to determine the width that can be taken up by
# the source designation as the widget is initialized or the window is
# resized.
INITIAL_MAX_WIDTH = 150
RESERVED_WIDTH = 250

# We allocate a fixed with to the source designation because its text is
# dynamically resized, which otherwise causes Qt's layout engine to
# incorrectly reposition it
FIXED_LABEL_WIDTH = 800

def __init__(self, source_name):
super().__init__()

Expand All @@ -3147,14 +3164,18 @@ def __init__(self, source_name):
# Signed in
compose_a_reply_to = QLabel(_("Compose a reply to "))
compose_a_reply_to.setObjectName("ReplyTextEditPlaceholder_text")
source_name = SecureQLabel(source_name, wordwrap=False)
source_name.setObjectName("ReplyTextEditPlaceholder_bold_blue")
self.source_name = source_name
self.source_name_label = SecureQLabel(
source_name, wordwrap=False, max_length=self.INITIAL_MAX_WIDTH
Copy link
Contributor

Choose a reason for hiding this comment

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

yup, setting max_length here will turn on elided text

)
self.source_name_label.setObjectName("ReplyTextEditPlaceholder_bold_blue")
self.source_name_label.setFixedWidth(self.FIXED_LABEL_WIDTH)
self.signed_in = QWidget()
signed_in_layout = QHBoxLayout()
signed_in_layout.setSpacing(0)
self.signed_in.setLayout(signed_in_layout)
signed_in_layout.addWidget(compose_a_reply_to)
signed_in_layout.addWidget(source_name)
signed_in_layout.addWidget(self.source_name_label)
self.signed_in.hide()

# Awaiting key
Expand Down Expand Up @@ -3203,6 +3224,13 @@ def show_signed_out(self):
self.signed_in.hide()
self.signed_out.show()

def update_label_width(self, width):
if width > self.RESERVED_WIDTH:
# Ensure source designations are elided with "..." if needed per
# current container size
self.source_name_label.max_length = width - self.RESERVED_WIDTH
self.source_name_label.setText(self.source_name)


class DeleteSourceAction(QAction):
"""Use this action to delete the source record."""
Expand Down
32 changes: 32 additions & 0 deletions tests/gui/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
PrintDialog,
ReplyBoxWidget,
ReplyTextEdit,
ReplyTextEditPlaceholder,
ReplyWidget,
SecureQLabel,
SourceConversationWrapper,
Expand Down Expand Up @@ -4362,6 +4363,37 @@ def test_ReplyBox_set_logged_in_no_public_key(mocker):
assert rb.text_edit.isEnabled() is False


def test_ReplyBox_resize_adjusts_label_width(mocker):
"""
If the reply widget is resized, the source designation's maximum width is
updated, and text is elided if necessary.
"""
source = factory.Source()
source.journalist_designation = "omniscient hippopotamus"
controller = mocker.MagicMock()
rb = ReplyBoxWidget(source, controller)
rb.set_logged_in()

# The widget must be displayed for the resize event to be triggered.
rb.show()

# We wrap the update_label_width method so we can verify that it has been
# called while preserving its behavior.
with patch.object(
ReplyTextEditPlaceholder,
"update_label_width",
wraps=rb.text_edit.placeholder.update_label_width,
) as wrapped_update:
rb.resize(1000, rb.height())
wrapped_update.assert_called_with(rb.text_edit.width() - 2)
assert rb.text_edit.placeholder.source_name_label.elided is False
rb.resize(500, rb.height())
wrapped_update.assert_called_with(rb.text_edit.width() - 2)
assert rb.text_edit.placeholder.source_name_label.elided is True

rb.hide()


def test_update_conversation_maintains_old_items(mocker, session):
"""
Calling update_conversation maintains old item state / position if there's
Expand Down