diff --git a/src/maestral_cocoa/bandwidth.py b/src/maestral_cocoa/bandwidth.py new file mode 100644 index 00000000..d3d5d171 --- /dev/null +++ b/src/maestral_cocoa/bandwidth.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- + +from __future__ import annotations + + +# external imports +import toga +from maestral.daemon import MaestralProxy + +# local imports +from .bandwidth_gui import BandwidthGui +from .private.widgets import Window + + +class BandwidthDialog(BandwidthGui): + def __init__(self, mdbx: MaestralProxy, app: toga.App): + super().__init__(app=app, is_dialog=True) + + self.mdbx = mdbx + self.dialog_buttons.on_press = self.on_dialog_pressed + + self.radio_button_unlimited_down.on_change = self.on_limit_downloads_toggled + self.radio_button_limited_down.on_change = self.on_limit_downloads_toggled + self.radio_button_unlimited_up.on_change = self.on_limit_uploads_toggled + self.radio_button_limited_up.on_change = self.on_limit_uploads_toggled + + def refresh_gui(self) -> None: + if self.mdbx.bandwidth_limit_down == 0: + self.radio_button_unlimited_down.value = True + self.number_input_limit_down.enabled = False + else: + self.radio_button_limited_down.value = True + self.number_input_limit_down.value = self.mdbx.bandwidth_limit_down + self.number_input_limit_down.enabled = True + + if self.mdbx.bandwidth_limit_up == 0: + self.radio_button_unlimited_up.value = True + self.number_input_limit_up.enabled = False + else: + self.radio_button_limited_up.value = True + self.number_input_limit_up.value = self.mdbx.bandwidth_limit_up + self.number_input_limit_up.enabled = True + + def update_settings(self): + if self.radio_button_unlimited_down.value is True: + self.mdbx.bandwidth_limit_down = 0.0 + else: + self.mdbx.bandwidth_limit_down = float(self.number_input_limit_down.value) + + if self.radio_button_unlimited_up.value is True: + self.mdbx.bandwidth_limit_up = 0.0 + else: + self.mdbx.bandwidth_limit_up = float(self.number_input_limit_up.value) + + async def on_limit_downloads_toggled(self, widget: toga.Selection) -> None: + if widget is self.radio_button_unlimited_down: + self.number_input_limit_down.enabled = False + elif widget is self.radio_button_limited_down: + self.number_input_limit_down.enabled = True + else: + raise RuntimeError(f"Unexpected widget {widget}") + + async def on_limit_uploads_toggled(self, widget: toga.Selection) -> None: + if widget is self.radio_button_unlimited_up: + self.number_input_limit_up.enabled = False + elif widget is self.radio_button_limited_up: + self.number_input_limit_up.enabled = True + else: + raise RuntimeError(f"Unexpected widget {widget}") + + async def on_dialog_pressed(self, btn_name: str) -> None: + try: + if btn_name == "Update": + self.update_settings() + finally: + self.close() + + def show_as_sheet(self, window: Window) -> None: + self.refresh_gui() + super().show_as_sheet(window) + + def show(self) -> None: + self.refresh_gui() + super().show() diff --git a/src/maestral_cocoa/bandwidth_gui.py b/src/maestral_cocoa/bandwidth_gui.py new file mode 100644 index 00000000..8546321e --- /dev/null +++ b/src/maestral_cocoa/bandwidth_gui.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- + +# external imports +import toga +from toga.style.pack import Pack +from toga.constants import COLUMN, TOP, RIGHT, LEFT + +# local imports +from .private.widgets import Label, RadioButton, Window, DialogButtons + + +class BandwidthGui(Window): + + COLUMN_WIDTH_LEFT = 100 + COLUMN_WIDTH_RIGHT = 250 + + ELEMENT_PADDING = 10 + COLUMN_PADDING = 10 + + def __init__(self, **kwargs) -> None: + super().__init__(title="Bandwidth Settings", size=(400, 250), **kwargs) + + self._label_download_rate = Label( + "Download rate:", + style=Pack(text_align=RIGHT, width=BandwidthGui.COLUMN_WIDTH_LEFT), + ) + + self.radio_button_unlimited_down = RadioButton( + "Don't limit", group=RadioButton.Group.A + ) + self.radio_button_limited_down = RadioButton( + "Limit to:", group=RadioButton.Group.A + ) + self.number_input_limit_down = toga.NumberInput( + value=1, + min_value=0.005, + style=Pack(padding_left=BandwidthGui.COLUMN_PADDING, width=50), + ) + self._unit_label_down = toga.Label( + "MB/s", + style=Pack(padding_left=BandwidthGui.COLUMN_PADDING, width=50), + ) + + self._label_upload_rate = Label( + "Upload rate:", + style=Pack(text_align=RIGHT, width=BandwidthGui.COLUMN_WIDTH_LEFT), + ) + + self.radio_button_unlimited_up = RadioButton( + "Don't limit", group=RadioButton.Group.B + ) + self.radio_button_limited_up = RadioButton( + "Limit to:", group=RadioButton.Group.B + ) + self.number_input_limit_up = toga.NumberInput( + value=1, + min_value=0.005, + style=Pack(padding_left=BandwidthGui.COLUMN_PADDING, width=50), + ) + self._unit_label_up = toga.Label( + "MB/s", + style=Pack(padding_left=BandwidthGui.COLUMN_PADDING, width=50), + ) + + self.dialog_buttons = DialogButtons( + labels=["Update", "Cancel"], + style=Pack(padding=(20, 20, 20, 20), flex=1), + ) + + children = [ + toga.Box( + children=[ + self._label_download_rate, + toga.Box( + children=[ + self.radio_button_unlimited_down, + toga.Box( + children=[ + self.radio_button_limited_down, + self.number_input_limit_down, + self._unit_label_down, + ], + ), + ], + style=Pack( + alignment=TOP, + direction=COLUMN, + padding_left=BandwidthGui.COLUMN_PADDING, + ), + ), + ], + ), + toga.Box( + children=[ + self._label_upload_rate, + toga.Box( + children=[ + self.radio_button_unlimited_up, + toga.Box( + children=[ + self.radio_button_limited_up, + self.number_input_limit_up, + self._unit_label_up, + ], + ), + ], + style=Pack( + alignment=TOP, + direction=COLUMN, + padding_left=BandwidthGui.COLUMN_PADDING, + ), + ), + ], + style=Pack(padding_top=BandwidthGui.ELEMENT_PADDING), + ), + self.dialog_buttons, + ] + + main_box = toga.Box( + children=children, + style=Pack( + direction=COLUMN, + padding=30, + width=BandwidthGui.COLUMN_WIDTH_LEFT + BandwidthGui.COLUMN_WIDTH_RIGHT, + flex=1, + alignment=LEFT, + ), + ) + + self.content = main_box diff --git a/src/maestral_cocoa/selective_sync.py b/src/maestral_cocoa/selective_sync.py index c777f4b4..b0102dcc 100644 --- a/src/maestral_cocoa/selective_sync.py +++ b/src/maestral_cocoa/selective_sync.py @@ -431,9 +431,7 @@ def update_items(self) -> None: self.mdbx.excluded_items = list(excluded_paths) async def on_dialog_pressed(self, btn_name: str) -> None: - if btn_name == "Update": - try: self.update_items() except BusyError as err: diff --git a/src/maestral_cocoa/settings.py b/src/maestral_cocoa/settings.py index 8ffc6665..8e86a153 100644 --- a/src/maestral_cocoa/settings.py +++ b/src/maestral_cocoa/settings.py @@ -26,6 +26,7 @@ from .private.widgets import FileSelectionButton, Switch, apply_round_clipping from .settings_gui import SettingsGui from .selective_sync import SelectiveSyncDialog +from .bandwidth import BandwidthDialog from .resources import FACEHOLDER_PATH from .autostart import AutoStart from .constants import FROZEN @@ -61,15 +62,9 @@ def __init__(self, mdbx: MaestralProxy, app: MaestralGui) -> None: self.btn_unlink.on_press = self.on_unlink_pressed self.btn_select_folders.on_press = self.on_folder_selection_pressed + self.btn_bandwidth.on_press = self.on_bandwidth_pressed self.combobox_dbx_location.on_select = self.on_dbx_location_selected - self.radio_button_unlimited_down.on_change = self.on_limit_downloads_toggled - self.radio_button_limited_down.on_change = self.on_limit_downloads_toggled - self.radio_button_unlimited_up.on_change = self.on_limit_uploads_toggled - self.radio_button_limited_up.on_change = self.on_limit_uploads_toggled - self.number_input_limit_down.on_change = self.on_download_limit_number_changed - self.number_input_limit_up.on_change = self.on_upload_limit_number_changed - self.combobox_update_interval.on_select = self.on_update_interval_selected self.checkbox_autostart.on_change = self.on_autostart_clicked self.checkbox_notifications.on_change = self.on_notifications_clicked @@ -121,6 +116,9 @@ async def on_dbx_location_selected(self, widget: FileSelectionButton) -> None: def on_folder_selection_pressed(self, widget: Any) -> None: SelectiveSyncDialog(mdbx=self.mdbx, app=self.app).show_as_sheet(self) + def on_bandwidth_pressed(self, widget: Any) -> None: + BandwidthDialog(mdbx=self.mdbx, app=self.app).show_as_sheet(self) + async def on_unlink_pressed(self, widget: Any) -> None: should_unlink = await self.confirm_dialog( title="Unlink your Dropbox account?", @@ -139,42 +137,6 @@ async def on_unlink_pressed(self, widget: Any) -> None: ) await self.app.exit_and_stop_daemon() - async def on_limit_downloads_toggled(self, widget: toga.Selection) -> None: - if widget is self.radio_button_unlimited_down: - unlimited = widget.value is True - elif widget is self.radio_button_limited_down: - unlimited = widget.value is False - else: - raise RuntimeError(f"Unexpected widget {widget}") - - if unlimited: - self.mdbx.bandwidth_limit_down = 0.0 - self.number_input_limit_down.enabled = False - else: - self.mdbx.bandwidth_limit_down = float(self.number_input_limit_down.value) - self.number_input_limit_down.enabled = True - - async def on_limit_uploads_toggled(self, widget: toga.Selection) -> None: - if widget is self.radio_button_unlimited_up: - unlimited = widget.value is True - elif widget is self.radio_button_limited_up: - unlimited = widget.value is False - else: - raise RuntimeError(f"Unexpected widget {widget}") - - if unlimited: - self.mdbx.bandwidth_limit_up = 0.0 - self.number_input_limit_up.enabled = False - else: - self.mdbx.bandwidth_limit_up = float(self.number_input_limit_up.value) - self.number_input_limit_up.enabled = True - - async def on_download_limit_number_changed(self, widget: toga.NumberInput) -> None: - self.mdbx.bandwidth_limit_down = float(widget.value) - - async def on_upload_limit_number_changed(self, widget: toga.NumberInput) -> None: - self.mdbx.bandwidth_limit_up = float(widget.value) - async def on_update_interval_selected(self, widget: toga.Selection) -> None: interval = self._update_interval_mapping[str(widget.value)] self.app.updater.update_check_interval = interval @@ -265,23 +227,6 @@ def refresh_gui(self) -> None: # populate sync section self.combobox_dbx_location.current_selection = self.mdbx.dropbox_path - # populate bandwidth section - if self.mdbx.bandwidth_limit_down == 0: - self.radio_button_unlimited_down.value = True - self.number_input_limit_down.enabled = False - else: - self.radio_button_limited_down.value = True - self.number_input_limit_down.value = self.mdbx.bandwidth_limit_down - self.number_input_limit_down.enabled = True - - if self.mdbx.bandwidth_limit_up == 0: - self.radio_button_unlimited_up.value = True - self.number_input_limit_up.enabled = False - else: - self.radio_button_limited_up.value = True - self.number_input_limit_up.value = self.mdbx.bandwidth_limit_up - self.number_input_limit_up.enabled = True - # populate app section self.checkbox_autostart.state = ON if self.autostart.enabled else OFF self.checkbox_notifications.state = ( diff --git a/src/maestral_cocoa/settings_gui.py b/src/maestral_cocoa/settings_gui.py index d5bb4c1f..aa5f1262 100644 --- a/src/maestral_cocoa/settings_gui.py +++ b/src/maestral_cocoa/settings_gui.py @@ -16,7 +16,6 @@ Label, LinkLabel, Switch, - RadioButton, FileSelectionButton, Window, apply_round_clipping, @@ -118,7 +117,7 @@ def __init__(self, **kwargs) -> None: style=Pack(direction=ROW), ) - # ==== folder settings section ================================================= + # ==== sync settings section =================================================== self._label_select_folders = Label( "Selective sync:", @@ -144,119 +143,41 @@ def __init__(self, **kwargs) -> None: ), ) - folder_settings_box = toga.Box( + self._label_bandwidth = Label( + "Bandwidth limits:", + style=Pack(text_align=RIGHT, width=SettingsGui.COLUMN_WIDTH_LEFT), + ) + self.btn_bandwidth = toga.Button( + "Change limits...", + style=Pack( + padding_left=SettingsGui.COLUMN_PADDING, width=SettingsGui.BUTTON_WIDTH + ), + ) + + sync_settings_box = toga.Box( children=[ toga.Box( children=[self._label_select_folders, self.btn_select_folders], style=Pack( - alignment=CENTER, padding_bottom=SettingsGui.ELEMENT_PADDING + alignment=CENTER, + padding_bottom=SettingsGui.ELEMENT_PADDING, ), ), toga.Box( children=[self._label_dbx_location, self.combobox_dbx_location], + style=Pack( + alignment=CENTER, + padding_bottom=SettingsGui.ELEMENT_PADDING, + ), + ), + toga.Box( + children=[self._label_bandwidth, self.btn_bandwidth], style=Pack(alignment=CENTER), ), ], style=Pack(direction=COLUMN), ) - # ==== bandwidth settings section ============================================== - - self._label_download_rate = Label( - "Download rate:", - style=Pack(text_align=RIGHT, width=SettingsGui.COLUMN_WIDTH_LEFT), - ) - - self.radio_button_unlimited_down = RadioButton( - "Don't limit", group=RadioButton.Group.A - ) - self.radio_button_limited_down = RadioButton( - "Limit to:", group=RadioButton.Group.A - ) - self.number_input_limit_down = toga.NumberInput( - value=1, - min_value=0.005, - style=Pack(padding_left=SettingsGui.COLUMN_PADDING, width=50), - ) - self._unit_label_down = toga.Label( - "MB/s", - style=Pack(padding_left=SettingsGui.COLUMN_PADDING, width=50), - ) - - self._label_upload_rate = Label( - "Upload rate:", - style=Pack(text_align=RIGHT, width=SettingsGui.COLUMN_WIDTH_LEFT), - ) - - self.radio_button_unlimited_up = RadioButton( - "Don't limit", group=RadioButton.Group.B - ) - self.radio_button_limited_up = RadioButton( - "Limit to:", group=RadioButton.Group.B - ) - self.number_input_limit_up = toga.NumberInput( - value=1, - min_value=0.005, - style=Pack(padding_left=SettingsGui.COLUMN_PADDING, width=50), - ) - self._unit_label_up = toga.Label( - "MB/s", - style=Pack(padding_left=SettingsGui.COLUMN_PADDING, width=50), - ) - - children = [ - toga.Box( - children=[ - self._label_download_rate, - toga.Box( - children=[ - self.radio_button_unlimited_down, - toga.Box( - children=[ - self.radio_button_limited_down, - self.number_input_limit_down, - self._unit_label_down, - ], - ), - ], - style=Pack( - alignment=TOP, - direction=COLUMN, - padding_left=SettingsGui.COLUMN_PADDING, - ), - ), - ], - ), - toga.Box( - children=[ - self._label_upload_rate, - toga.Box( - children=[ - self.radio_button_unlimited_up, - toga.Box( - children=[ - self.radio_button_limited_up, - self.number_input_limit_up, - self._unit_label_up, - ], - ), - ], - style=Pack( - alignment=TOP, - direction=COLUMN, - padding_left=SettingsGui.COLUMN_PADDING, - ), - ), - ], - style=Pack(padding_top=SettingsGui.ELEMENT_PADDING), - ), - ] - - bandwidth_settings_box = toga.Box( - children=children, - style=Pack(direction=COLUMN), - ) - # ==== system settings section ================================================= self._label_update_interval = Label( @@ -417,9 +338,7 @@ def __init__(self, **kwargs) -> None: children=[ account_info_box, toga.Divider(style=Pack(padding=SettingsGui.SECTION_PADDING)), - folder_settings_box, - toga.Divider(style=Pack(padding=SettingsGui.SECTION_PADDING)), - bandwidth_settings_box, + sync_settings_box, toga.Divider(style=Pack(padding=SettingsGui.SECTION_PADDING)), system_settings_box, toga.Divider(style=Pack(padding=SettingsGui.SECTION_PADDING)),