diff --git a/desktop/onionshare/connection_tab.py b/desktop/onionshare/connection_tab.py index 0f268a68..1cf3c376 100644 --- a/desktop/onionshare/connection_tab.py +++ b/desktop/onionshare/connection_tab.py @@ -156,7 +156,7 @@ class AutoConnectTab(QtWidgets.QWidget): self.curr_settings.save() def open_tor_settings(self): - self.parent.open_tor_settings_tab(from_autoconnect=True) + self.parent.open_settings_tab(from_autoconnect=True, active_tab="tor") def first_launch_widget_connect_clicked(self): """ diff --git a/desktop/onionshare/gui_common.py b/desktop/onionshare/gui_common.py index 4cf0f010..c717fd22 100644 --- a/desktop/onionshare/gui_common.py +++ b/desktop/onionshare/gui_common.py @@ -121,6 +121,15 @@ class GuiCommon: font-weight: bold; font-size: 20px; }""", + "settings_subtab_bar": """ + QTabBar::tab { + background: transparent; + } + QTabBar::tab:selected { + border-bottom: 3px solid; + border-color: #4E064F; + padding: 3px + }""", "mode_new_tab_button": """ QPushButton { font-weight: bold; diff --git a/desktop/onionshare/main_window.py b/desktop/onionshare/main_window.py index a7dfe761..d73f3370 100644 --- a/desktop/onionshare/main_window.py +++ b/desktop/onionshare/main_window.py @@ -108,24 +108,6 @@ class MainWindow(QtWidgets.QMainWindow): ) self.status_bar.addPermanentWidget(self.status_bar.server_status_indicator) - # Tor settings button - self.tor_settings_button = QtWidgets.QPushButton() - self.tor_settings_button.setDefault(False) - self.tor_settings_button.setFixedSize(40, 50) - self.tor_settings_button.setIcon( - QtGui.QIcon( - GuiCommon.get_resource_path( - "images/{}_tor_settings.png".format(self.common.gui.color_mode) - ) - ) - ) - self.tor_settings_button.clicked.connect(self.open_tor_settings) - self.tor_settings_button.setStyleSheet(self.common.gui.css["settings_button"]) - self.status_bar.addPermanentWidget(self.tor_settings_button) - - if os.environ.get("ONIONSHARE_HIDE_TOR_SETTINGS") == "1": - self.tor_settings_button.hide() - # Settings button self.settings_button = QtWidgets.QPushButton() self.settings_button.setDefault(False) @@ -242,20 +224,22 @@ class MainWindow(QtWidgets.QMainWindow): """ Open the TorSettingsTab """ - self.common.log("MainWindow", "open_tor_settings") + self._open_settings(active_tab="tor") + + def open_settings(self): + """ + Open the general SettingsTab + """ + self._open_settings(active_tab="general") + + def _open_settings(self, active_tab): + self.common.log("MainWindow", f"open settings with active tab: {active_tab}") from_autoconnect = False for tab_id in self.tabs.tabs: if type(self.tabs.tabs[tab_id]) is AutoConnectTab: from_autoconnect = True break - self.tabs.open_tor_settings_tab(from_autoconnect) - - def open_settings(self): - """ - Open the SettingsTab - """ - self.common.log("MainWindow", "open_settings") - self.tabs.open_settings_tab() + self.tabs.open_settings_tab(from_autoconnect, active_tab=active_tab) def settings_have_changed(self): self.common.log("OnionShareGui", "settings_have_changed") diff --git a/desktop/onionshare/resources/locale/en.json b/desktop/onionshare/resources/locale/en.json index 8b4a6301..9627b4d9 100644 --- a/desktop/onionshare/resources/locale/en.json +++ b/desktop/onionshare/resources/locale/en.json @@ -60,6 +60,7 @@ "gui_autoconnect_circumventing_censorship_got_bridges": "Got bridges! Trying to reconnect to Tor", "gui_autoconnect_could_not_connect_to_tor_api": "Could not connect to the Tor API. Make sure you are connected to the internet before trying again.", "gui_settings_window_title": "Settings", + "gui_general_settings_window_title": "General", "gui_settings_autoupdate_label": "Check for new version", "gui_settings_autoupdate_option": "Notify me when a new version is available", "gui_settings_autoupdate_timestamp": "Last checked: {}", diff --git a/desktop/onionshare/settings_parent_tab.py b/desktop/onionshare/settings_parent_tab.py new file mode 100644 index 00000000..2ade0e05 --- /dev/null +++ b/desktop/onionshare/settings_parent_tab.py @@ -0,0 +1,72 @@ +from PySide2 import QtCore, QtWidgets, QtGui + +from onionshare_cli.mode_settings import ModeSettings + +from . import strings +from .tab import Tab +from .threads import EventHandlerThread +from .gui_common import GuiCommon +from .tor_settings_tab import TorSettingsTab +from .settings_tab import SettingsTab +from .connection_tab import AutoConnectTab + + +class SettingsParentTab(QtWidgets.QTabWidget): + """ + The settings tab widget containing the tor settings + and app settings subtabs + """ + + bring_to_front = QtCore.Signal() + close_this_tab = QtCore.Signal() + + def __init__( + self, common, tab_id, parent=None, active_tab="general", from_autoconnect=False + ): + super(SettingsParentTab, self).__init__() + self.parent = parent + self.common = common + self.common.log("SettingsParentTab", "__init__") + + # Keep track of tabs in a dictionary that maps tab_id to tab. + # Each tab has a unique, auto-incremented id (tab_id). This is different than the + # tab's index, which changes as tabs are re-arranged. + self.tabs = { + "general": 0, + "tor": 1, + } + self.tab_id = tab_id + self.current_tab_id = self.tabs[active_tab] + + # Use a custom tab bar + tab_bar = TabBar(self.common) + self.setTabBar(tab_bar) + settings_tab = SettingsTab(self.common, self.tabs["general"], parent=self) + self.tor_settings_tab = TorSettingsTab( + self.common, + self.tabs["tor"], + self.parent.are_tabs_active(), + self.parent.status_bar, + parent=self, + from_autoconnect=from_autoconnect, + ) + self.addTab(settings_tab, strings._("gui_general_settings_window_title")) + self.addTab(self.tor_settings_tab, strings._("gui_tor_settings_window_title")) + + # Set up the tab widget + self.setMovable(False) + self.setTabsClosable(False) + self.setUsesScrollButtons(False) + self.setCurrentIndex(self.current_tab_id) + + +class TabBar(QtWidgets.QTabBar): + """ + A custom tab bar + """ + + move_new_tab_button = QtCore.Signal() + + def __init__(self, common): + super(TabBar, self).__init__() + self.setStyleSheet(common.gui.css["settings_subtab_bar"]) diff --git a/desktop/onionshare/settings_tab.py b/desktop/onionshare/settings_tab.py index 58843a0d..cf2261b6 100644 --- a/desktop/onionshare/settings_tab.py +++ b/desktop/onionshare/settings_tab.py @@ -33,9 +33,7 @@ class SettingsTab(QtWidgets.QWidget): Settings dialog. """ - close_this_tab = QtCore.Signal() - - def __init__(self, common, tab_id): + def __init__(self, common, tab_id, parent=None): super(SettingsTab, self).__init__() self.common = common @@ -43,6 +41,7 @@ class SettingsTab(QtWidgets.QWidget): self.system = platform.system() self.tab_id = tab_id + self.parent = parent # Automatic updates options @@ -283,7 +282,7 @@ class SettingsTab(QtWidgets.QWidget): # Save the new settings settings.save() - self.close_this_tab.emit() + self.parent.close_this_tab.emit() def help_clicked(self): """ diff --git a/desktop/onionshare/tab_widget.py b/desktop/onionshare/tab_widget.py index 4892b15f..3c1a1703 100644 --- a/desktop/onionshare/tab_widget.py +++ b/desktop/onionshare/tab_widget.py @@ -26,8 +26,7 @@ from . import strings from .tab import Tab from .threads import EventHandlerThread from .gui_common import GuiCommon -from .tor_settings_tab import TorSettingsTab -from .settings_tab import SettingsTab +from .settings_parent_tab import SettingsParentTab from .connection_tab import AutoConnectTab @@ -98,8 +97,7 @@ class TabWidget(QtWidgets.QTabWidget): # Clean up each tab for tab_id in self.tabs: if not ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab + type(self.tabs[tab_id]) is SettingsParentTab or type(self.tabs[tab_id]) is AutoConnectTab ): self.tabs[tab_id].cleanup() @@ -139,8 +137,7 @@ class TabWidget(QtWidgets.QTabWidget): # If it's Settings or Tor Settings, ignore if ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab + type(self.tabs[tab_id]) is SettingsParentTab or type(self.tabs[tab_id]) is AutoConnectTab ): # Blank the server status indicator @@ -230,48 +227,31 @@ class TabWidget(QtWidgets.QTabWidget): index = self.addTab(connection_tab, strings._("gui_autoconnect_start")) self.setCurrentIndex(index) - def open_settings_tab(self): + def open_settings_tab(self, from_autoconnect=False, active_tab="general"): self.common.log("TabWidget", "open_settings_tab") # See if a settings tab is already open, and if so switch to it for tab_id in self.tabs: - if type(self.tabs[tab_id]) is SettingsTab: + if type(self.tabs[tab_id]) is SettingsParentTab: self.setCurrentIndex(self.indexOf(self.tabs[tab_id])) return - settings_tab = SettingsTab(self.common, self.current_tab_id) + settings_tab = SettingsParentTab( + self.common, + self.current_tab_id, + active_tab=active_tab, + parent=self, + from_autoconnect=from_autoconnect, + ) settings_tab.close_this_tab.connect(self.close_settings_tab) + self.tor_settings_tab = settings_tab.tor_settings_tab + self.tor_settings_tab.tor_is_connected.connect(self.tor_is_connected) + self.tor_settings_tab.tor_is_disconnected.connect(self.tor_is_disconnected) self.tabs[self.current_tab_id] = settings_tab self.current_tab_id += 1 index = self.addTab(settings_tab, strings._("gui_settings_window_title")) self.setCurrentIndex(index) - def open_tor_settings_tab(self, from_autoconnect=False): - self.common.log("TabWidget", "open_tor_settings_tab") - - # See if a settings tab is already open, and if so switch to it - for tab_id in self.tabs: - if type(self.tabs[tab_id]) is TorSettingsTab: - self.setCurrentIndex(self.indexOf(self.tabs[tab_id])) - return - - self.tor_settings_tab = TorSettingsTab( - self.common, - self.current_tab_id, - self.are_tabs_active(), - self.status_bar, - from_autoconnect, - ) - self.tor_settings_tab.close_this_tab.connect(self.close_tor_settings_tab) - self.tor_settings_tab.tor_is_connected.connect(self.tor_is_connected) - self.tor_settings_tab.tor_is_disconnected.connect(self.tor_is_disconnected) - self.tabs[self.current_tab_id] = self.tor_settings_tab - self.current_tab_id += 1 - index = self.addTab( - self.tor_settings_tab, strings._("gui_tor_settings_window_title") - ) - self.setCurrentIndex(index) - def change_title(self, tab_id, title): shortened_title = title if len(shortened_title) > 11: @@ -315,8 +295,7 @@ class TabWidget(QtWidgets.QTabWidget): persistent_tabs = [] for tab_id in self.tabs: if not ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab + type(self.tabs[tab_id]) is SettingsParentTab or type(self.tabs[tab_id]) is AutoConnectTab ): tab = self.widget(self.indexOf(self.tabs[tab_id])) @@ -333,13 +312,12 @@ class TabWidget(QtWidgets.QTabWidget): tab_id = tab.tab_id if ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab + type(self.tabs[tab_id]) is SettingsParentTab or type(self.tabs[tab_id]) is AutoConnectTab ): self.common.log("TabWidget", "closing a settings tab") - if type(self.tabs[tab_id]) is TorSettingsTab: + if type(self.tabs[tab_id]) is SettingsParentTab: self.tor_settings_tab = None # Remove the tab @@ -382,14 +360,6 @@ class TabWidget(QtWidgets.QTabWidget): def close_settings_tab(self): self.common.log("TabWidget", "close_settings_tab") - for tab_id in self.tabs: - if type(self.tabs[tab_id]) is SettingsTab: - index = self.indexOf(self.tabs[tab_id]) - self.close_tab(index) - return - - def close_tor_settings_tab(self): - self.common.log("TabWidget", "close_tor_settings_tab") for tab_id in list(self.tabs): if type(self.tabs[tab_id]) is AutoConnectTab: # If we are being returned to the AutoConnectTab, but @@ -399,7 +369,7 @@ class TabWidget(QtWidgets.QTabWidget): if self.common.gui.onion.is_authenticated(): self.common.log( "TabWidget", - "close_tor_settings_tab", + "close_settings_tab", "Tor is connected and we can auto-connect, so closing the tab", ) index = self.indexOf(self.tabs[tab_id]) @@ -408,13 +378,13 @@ class TabWidget(QtWidgets.QTabWidget): self.tabs[tab_id].reload_settings() self.common.log( "TabWidget", - "close_tor_settings_tab", + "close_settings_tab", "Reloading settings in case they changed in the TorSettingsTab. Not auto-connecting", ) break # List of indices may have changed due to the above, so we loop over it again as another copy for tab_id in list(self.tabs): - if type(self.tabs[tab_id]) is TorSettingsTab: + if type(self.tabs[tab_id]) is SettingsParentTab: index = self.indexOf(self.tabs[tab_id]) self.close_tab(index) return @@ -425,8 +395,7 @@ class TabWidget(QtWidgets.QTabWidget): """ for tab_id in self.tabs: if not ( - type(self.tabs[tab_id]) is SettingsTab - or type(self.tabs[tab_id]) is TorSettingsTab + type(self.tabs[tab_id]) is SettingsParentTab or type(self.tabs[tab_id]) is AutoConnectTab ): mode = self.tabs[tab_id].get_mode() @@ -449,29 +418,23 @@ class TabWidget(QtWidgets.QTabWidget): def tor_is_connected(self): for tab_id in self.tabs: - if type(self.tabs[tab_id]) is SettingsTab: - self.tabs[tab_id].tor_is_connected() - else: - if not ( - type(self.tabs[tab_id]) is TorSettingsTab - or type(self.tabs[tab_id]) is AutoConnectTab - ): - mode = self.tabs[tab_id].get_mode() - if mode: - mode.tor_connection_started() + if not ( + type(self.tabs[tab_id]) is SettingsParentTab + or type(self.tabs[tab_id]) is AutoConnectTab + ): + mode = self.tabs[tab_id].get_mode() + if mode: + mode.tor_connection_started() def tor_is_disconnected(self): for tab_id in self.tabs: - if type(self.tabs[tab_id]) is SettingsTab: - self.tabs[tab_id].tor_is_disconnected() - else: - if not ( - type(self.tabs[tab_id]) is TorSettingsTab - or type(self.tabs[tab_id]) is AutoConnectTab - ): - mode = self.tabs[tab_id].get_mode() - if mode: - mode.tor_connection_stopped() + if not ( + type(self.tabs[tab_id]) is SettingsParentTab + or type(self.tabs[tab_id]) is AutoConnectTab + ): + mode = self.tabs[tab_id].get_mode() + if mode: + mode.tor_connection_stopped() class TabBar(QtWidgets.QTabBar): diff --git a/desktop/onionshare/tor_settings_tab.py b/desktop/onionshare/tor_settings_tab.py index 9c9ea593..0e72c3b8 100644 --- a/desktop/onionshare/tor_settings_tab.py +++ b/desktop/onionshare/tor_settings_tab.py @@ -43,7 +43,13 @@ class TorSettingsTab(QtWidgets.QWidget): tor_is_disconnected = QtCore.Signal() def __init__( - self, common, tab_id, are_tabs_active, status_bar, from_autoconnect=False + self, + common, + tab_id, + are_tabs_active, + status_bar, + from_autoconnect=False, + parent=None, ): super(TorSettingsTab, self).__init__() @@ -55,6 +61,7 @@ class TorSettingsTab(QtWidgets.QWidget): self.system = platform.system() self.tab_id = tab_id + self.parent = parent self.from_autoconnect = from_autoconnect # Connection type: either automatic, control port, or socket file @@ -746,9 +753,9 @@ class TorSettingsTab(QtWidgets.QWidget): self.tor_con.show() self.tor_con.start(settings) else: - self.close_this_tab.emit() + self.parent.close_this_tab.emit() else: - self.close_this_tab.emit() + self.parent.close_this_tab.emit() def tor_con_success(self): """ @@ -779,7 +786,7 @@ class TorSettingsTab(QtWidgets.QWidget): # Tell the tabs that Tor is connected self.tor_is_connected.emit() # Close the tab - self.close_this_tab.emit() + self.parent.close_this_tab.emit() self.tor_con_type = None