Skip to content

Commit

Permalink
Merge pull request #293 from freedomofpress/issue-279-refreshing
Browse files Browse the repository at this point in the history
Top Pane and User Button
  • Loading branch information
redshiftzero authored Apr 12, 2019
2 parents 2e425ce + a94ad8e commit 8f69f01
Show file tree
Hide file tree
Showing 13 changed files with 1,084 additions and 364 deletions.
87 changes: 87 additions & 0 deletions securedrop_client/gui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
Generic custom widgets.
Copyright (C) 2018 The Freedom of the Press Foundation.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton
from PyQt5.QtCore import QSize

from securedrop_client.resources import load_svg, load_icon


class SvgPushButton(QPushButton):
"""
A widget used to display the contents of Scalable Vector Graphics (SVG) files provided for
associated user action modes, see https://doc.qt.io/qt-5/qicon.html#Mode-enum.
Parameters
----------
normal: str
The name of the SVG file to add to the button for QIcon.Normal mode.
disabled: str, optional
The name of the SVG file to add to the button for QIcon.Disabled mode.
active: str, optional
The name of the SVG file to add to the button for QIcon.Active mode.
selected: str, optional
The name of the SVG file to add to the button for QIcon.Selected mode.
svg_size: QSize, optional
The display size of the SVG, defaults to filling the entire size of the widget.
"""

def __init__(self, normal: str, disabled=None, active=None, selected=None, svg_size=None):
super().__init__()

# Set layout
layout = QHBoxLayout(self)
self.setLayout(layout)

# Remove margins and spacing
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)

# Add SVG icon and set its size
self.icon = load_icon(normal=normal, disabled=disabled, active=active, selected=selected)
self.setIcon(self.icon)
self.setIconSize(svg_size) if svg_size else self.setIconSize(QSize())


class SvgLabel(QLabel):
"""
A widget used to display the contents of a Scalable Vector Graphics (SVG) file.
Parameters
----------
filename: str
The name of the SVG file to add to the label.
svg_size: QSize, optional
The display size of the SVG, defaults to filling the entire size of the widget.
"""

def __init__(self, filename: str, svg_size=None):
super().__init__()

# Remove margins and spacing
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.setLayout(layout)

# Add SVG and set its size
self.svg = load_svg(filename)
self.svg.setFixedSize(svg_size) if svg_size else self.svg.setFixedSize(QSize())
layout.addWidget(self.svg)
56 changes: 32 additions & 24 deletions securedrop_client/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from securedrop_client import __version__
from securedrop_client.db import Source
from securedrop_client.gui.widgets import ToolBar, MainView, LoginDialog, StatusBar, \
from securedrop_client.gui.widgets import TopPane, LeftPane, MainView, LoginDialog, \
SourceConversationWrapper
from securedrop_client.resources import load_icon

Expand Down Expand Up @@ -60,22 +60,24 @@ def __init__(self, sdc_home: str):
self.central_widget = QWidget()
central_widget_layout = QVBoxLayout()
central_widget_layout.setContentsMargins(0, 0, 0, 0)
central_widget_layout.setSpacing(0)
self.central_widget.setLayout(central_widget_layout)
self.setCentralWidget(self.central_widget)

self.status_bar = StatusBar()
central_widget_layout.addWidget(self.status_bar)
self.top_pane = TopPane()
central_widget_layout.addWidget(self.top_pane)

self.widget = QWidget()
widget_layout = QHBoxLayout()
widget_layout.setContentsMargins(0, 0, 0, 0)
widget_layout.setSpacing(0)
self.widget.setLayout(widget_layout)

self.tool_bar = ToolBar(self.widget)
self.left_pane = LeftPane()
self.main_view = MainView(self.widget)
self.main_view.source_list.itemSelectionChanged.connect(self.on_source_changed)

widget_layout.addWidget(self.tool_bar, 1)
widget_layout.addWidget(self.left_pane, 1)
widget_layout.addWidget(self.main_view, 8)

central_widget_layout.addWidget(self.widget)
Expand All @@ -96,9 +98,9 @@ def setup(self, controller):
views used in the UI.
"""
self.controller = controller # Reference the Client logic instance.
self.tool_bar.setup(self, controller)
self.status_bar.setup(controller)
self.set_status(_('Started SecureDrop Client. Please sign in.'), 20000)
self.left_pane.setup(self, controller)
self.top_pane.setup(controller)
self.update_activity_status(_('Started SecureDrop Client. Please sign in.'), 20000)

self.login_dialog = LoginDialog(self)
self.main_view.setup(self.controller)
Expand All @@ -119,7 +121,6 @@ def show_login(self):
self.login_dialog.setup(self.controller)
self.login_dialog.reset()
self.login_dialog.exec()
self.status_bar.show_refresh_icon()

def show_login_error(self, error):
"""
Expand All @@ -134,13 +135,6 @@ def hide_login(self):
"""
self.login_dialog.accept()
self.login_dialog = None
self.status_bar.hide_refresh_icon()

def update_error_status(self, error=None):
"""
Show an error message on the sidebar.
"""
self.main_view.update_error_status(error)

def show_sources(self, sources: List[Source]):
"""
Expand All @@ -154,22 +148,23 @@ def show_sync(self, updated_on):
Display a message indicating the data-sync state.
"""
if updated_on:
self.set_status(_('Last refresh: {}').format(updated_on.humanize()))
self.update_activity_status(_('Last refresh: {}').format(updated_on.humanize()))
else:
self.set_status(_('Waiting to refresh...'), 5000)
self.update_activity_status(_('Waiting to refresh...'), 5000)

def set_logged_in_as(self, username):
"""
Update the UI to show user logged in with username.
"""
self.tool_bar.set_logged_in_as(username)
self.left_pane.set_logged_in_as(username)
self.top_pane.enable_refresh()

def logout(self):
"""
Update the UI to show the user is logged out.
"""
self.tool_bar.set_logged_out()
self.status_bar.hide_refresh_icon()
self.left_pane.set_logged_out()
self.top_pane.disable_refresh()

def on_source_changed(self):
"""
Expand Down Expand Up @@ -197,9 +192,22 @@ def show_conversation_for(self, source: Source, is_authenticated: bool):

self.main_view.set_conversation(conversation_container)

def set_status(self, message, duration=0):
def update_activity_status(self, message: str, duration=0):
"""
Display an activity status message to the user. Optionally, supply a duration
(in milliseconds), the default will continuously show the message.
"""
self.top_pane.update_activity_status(message, duration)

def update_error_status(self, message: str, duration=10000):
"""
Display a status message to the user. Optionally, supply a duration
Display an error status message to the user. Optionally, supply a duration
(in milliseconds), the default will continuously show the message.
"""
self.status_bar.show_message(message, duration)
self.top_pane.update_error_status(message, duration)

def clear_error_status(self):
"""
Clear any message currently in the error status bar.
"""
self.top_pane.clear_error_status()
Loading

0 comments on commit 8f69f01

Please sign in to comment.